Sat Apr 26 2014 22:01:28

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: 412324 $")
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/strings.h"
00123 #include "asterisk/smdi.h"
00124 #include "asterisk/astobj2.h"
00125 #include "asterisk/event.h"
00126 #include "asterisk/taskprocessor.h"
00127 #include "asterisk/test.h"
00128 
00129 #ifdef ODBC_STORAGE
00130 #include "asterisk/res_odbc.h"
00131 #endif
00132 
00133 #ifdef IMAP_STORAGE
00134 #include "asterisk/threadstorage.h"
00135 #endif
00136 
00137 /*** DOCUMENTATION
00138    <application name="VoiceMail" language="en_US">
00139       <synopsis>
00140          Leave a Voicemail message.
00141       </synopsis>
00142       <syntax>
00143          <parameter name="mailboxs" argsep="&amp;" required="true">
00144             <argument name="mailbox1" argsep="@" required="true">
00145                <argument name="mailbox" required="true" />
00146                <argument name="context" />
00147             </argument>
00148             <argument name="mailbox2" argsep="@" multiple="true">
00149                <argument name="mailbox" required="true" />
00150                <argument name="context" />
00151             </argument>
00152          </parameter>
00153          <parameter name="options">
00154             <optionlist>
00155                <option name="b">
00156                   <para>Play the <literal>busy</literal> greeting to the calling party.</para>
00157                </option>
00158                <option name="d">
00159                   <argument name="c" />
00160                   <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
00161                   if played during the greeting. Context defaults to the current context.</para>
00162                </option>
00163                <option name="g">
00164                   <argument name="#" required="true" />
00165                   <para>Use the specified amount of gain when recording the voicemail
00166                   message. The units are whole-number decibels (dB). Only works on supported
00167                   technologies, which is DAHDI only.</para>
00168                </option>
00169                <option name="s">
00170                   <para>Skip the playback of instructions for leaving a message to the
00171                   calling party.</para>
00172                </option>
00173                <option name="u">
00174                   <para>Play the <literal>unavailable</literal> greeting.</para>
00175                </option>
00176                <option name="U">
00177                   <para>Mark message as <literal>URGENT</literal>.</para>
00178                </option>
00179                <option name="P">
00180                   <para>Mark message as <literal>PRIORITY</literal>.</para>
00181                </option>
00182             </optionlist>
00183          </parameter>
00184       </syntax>
00185       <description>
00186          <para>This application allows the calling party to leave a message for the specified
00187          list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
00188          the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
00189          exist.</para>
00190          <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
00191          <enumlist>
00192             <enum name="0">
00193                <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
00194             </enum>
00195             <enum name="*">
00196                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00197             </enum>
00198          </enumlist>
00199          <para>This application will set the following channel variable upon completion:</para>
00200          <variablelist>
00201             <variable name="VMSTATUS">
00202                <para>This indicates the status of the execution of the VoiceMail application.</para>
00203                <value name="SUCCESS" />
00204                <value name="USEREXIT" />
00205                <value name="FAILED" />
00206             </variable>
00207          </variablelist>
00208       </description>
00209       <see-also>
00210          <ref type="application">VoiceMailMain</ref>
00211       </see-also>
00212    </application>
00213    <application name="VoiceMailMain" language="en_US">
00214       <synopsis>
00215          Check Voicemail messages.
00216       </synopsis>
00217       <syntax>
00218          <parameter name="mailbox" required="true" argsep="@">
00219             <argument name="mailbox" />
00220             <argument name="context" />
00221          </parameter>
00222          <parameter name="options">
00223             <optionlist>
00224                <option name="p">
00225                   <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
00226                   the mailbox that is entered by the caller.</para>
00227                </option>
00228                <option name="g">
00229                   <argument name="#" required="true" />
00230                   <para>Use the specified amount of gain when recording a voicemail message.
00231                   The units are whole-number decibels (dB).</para>
00232                </option>
00233                <option name="s">
00234                   <para>Skip checking the passcode for the mailbox.</para>
00235                </option>
00236                <option name="a">
00237                   <argument name="folder" required="true" />
00238                   <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
00239                   Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
00240                   <enumlist>
00241                      <enum name="0"><para>INBOX</para></enum>
00242                      <enum name="1"><para>Old</para></enum>
00243                      <enum name="2"><para>Work</para></enum>
00244                      <enum name="3"><para>Family</para></enum>
00245                      <enum name="4"><para>Friends</para></enum>
00246                      <enum name="5"><para>Cust1</para></enum>
00247                      <enum name="6"><para>Cust2</para></enum>
00248                      <enum name="7"><para>Cust3</para></enum>
00249                      <enum name="8"><para>Cust4</para></enum>
00250                      <enum name="9"><para>Cust5</para></enum>
00251                   </enumlist>
00252                </option>
00253             </optionlist>
00254          </parameter>
00255       </syntax>
00256       <description>
00257          <para>This application allows the calling party to check voicemail messages. A specific
00258          <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
00259          may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
00260          be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
00261          <literal>default</literal> context will be used.</para>
00262          <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
00263          or Password, and the extension exists:</para>
00264          <enumlist>
00265             <enum name="*">
00266                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00267             </enum>
00268          </enumlist>
00269       </description>
00270       <see-also>
00271          <ref type="application">VoiceMail</ref>
00272       </see-also>
00273    </application>
00274    <application name="MailboxExists" language="en_US">
00275       <synopsis>
00276          Check to see if Voicemail mailbox exists.
00277       </synopsis>
00278       <syntax>
00279          <parameter name="mailbox" required="true" argsep="@">
00280             <argument name="mailbox" required="true" />
00281             <argument name="context" />
00282          </parameter>
00283          <parameter name="options">
00284             <para>None options.</para>
00285          </parameter>
00286       </syntax>
00287       <description>
00288          <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
00289          <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
00290          <replaceable>context</replaceable> is specified, the <literal>default</literal> context
00291          will be used.</para>
00292          <para>This application will set the following channel variable upon completion:</para>
00293          <variablelist>
00294             <variable name="VMBOXEXISTSSTATUS">
00295                <para>This will contain the status of the execution of the MailboxExists application.
00296                Possible values include:</para>
00297                <value name="SUCCESS" />
00298                <value name="FAILED" />
00299             </variable>
00300          </variablelist>
00301       </description>
00302       <see-also>
00303          <ref type="function">VM_INFO</ref>
00304       </see-also>
00305    </application>
00306    <application name="VMAuthenticate" language="en_US">
00307       <synopsis>
00308          Authenticate with Voicemail passwords.
00309       </synopsis>
00310       <syntax>
00311          <parameter name="mailbox" required="true" argsep="@">
00312             <argument name="mailbox" />
00313             <argument name="context" />
00314          </parameter>
00315          <parameter name="options">
00316             <optionlist>
00317                <option name="s">
00318                   <para>Skip playing the initial prompts.</para>
00319                </option>
00320             </optionlist>
00321          </parameter>
00322       </syntax>
00323       <description>
00324          <para>This application behaves the same way as the Authenticate application, but the passwords
00325          are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
00326          specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
00327          is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
00328          mailbox.</para>
00329          <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
00330          or Password, and the extension exists:</para>
00331          <enumlist>
00332             <enum name="*">
00333                <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
00334             </enum>
00335          </enumlist>
00336       </description>
00337    </application>
00338    <application name="VoiceMailPlayMsg" language="en_US">
00339       <synopsis>
00340          Play a single voice mail msg from a mailbox by msg id.
00341       </synopsis>
00342       <syntax>
00343          <parameter name="mailbox" required="true" argsep="@">
00344             <argument name="mailbox" />
00345             <argument name="context" />
00346          </parameter>
00347          <parameter name="msg_id" required="true">
00348             <para>The msg id of the msg to play back. </para>
00349          </parameter>
00350       </syntax>
00351       <description>
00352          <para>This application sets the following channel variable upon completion:</para>
00353          <variablelist>
00354             <variable name="VOICEMAIL_PLAYBACKSTATUS">
00355                <para>The status of the playback attempt as a text string.</para>
00356                <value name="SUCCESS"/>
00357                <value name="FAILED"/>
00358             </variable>
00359          </variablelist>
00360       </description>
00361    </application>
00362    <application name="VMSayName" language="en_US">
00363       <synopsis>
00364          Play the name of a voicemail user
00365       </synopsis>
00366       <syntax>
00367          <parameter name="mailbox" required="true" argsep="@">
00368             <argument name="mailbox" />
00369             <argument name="context" />
00370          </parameter>
00371       </syntax>
00372       <description>
00373          <para>This application will say the recorded name of the voicemail user specified as the
00374          argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
00375       </description>
00376    </application>
00377    <function name="MAILBOX_EXISTS" language="en_US">
00378       <synopsis>
00379          Tell if a mailbox is configured.
00380       </synopsis>
00381       <syntax argsep="@">
00382          <parameter name="mailbox" required="true" />
00383          <parameter name="context" />
00384       </syntax>
00385       <description>
00386          <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
00387          <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
00388          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00389          context.</para>
00390       </description>
00391       <see-also>
00392          <ref type="function">VM_INFO</ref>
00393       </see-also>
00394    </function>
00395    <function name="VM_INFO" language="en_US">
00396       <synopsis>
00397          Returns the selected attribute from a mailbox.
00398       </synopsis>
00399       <syntax argsep=",">
00400          <parameter name="mailbox" argsep="@" required="true">
00401             <argument name="mailbox" required="true" />
00402             <argument name="context" />
00403          </parameter>
00404          <parameter name="attribute" required="true">
00405             <optionlist>
00406                <option name="count">
00407                   <para>Count of messages in specified <replaceable>folder</replaceable>.
00408                   If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
00409                </option>
00410                <option name="email">
00411                   <para>E-mail address associated with the mailbox.</para>
00412                </option>
00413                <option name="exists">
00414                   <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
00415                </option>
00416                <option name="fullname">
00417                   <para>Full name associated with the mailbox.</para>
00418                </option>
00419                <option name="language">
00420                   <para>Mailbox language if overridden, otherwise the language of the channel.</para>
00421                </option>
00422                <option name="locale">
00423                   <para>Mailbox locale if overridden, otherwise global locale.</para>
00424                </option>
00425                <option name="pager">
00426                   <para>Pager e-mail address associated with the mailbox.</para>
00427                </option>
00428                <option name="password">
00429                   <para>Mailbox access password.</para>
00430                </option>
00431                <option name="tz">
00432                   <para>Mailbox timezone if overridden, otherwise global timezone</para>
00433                </option>
00434             </optionlist>
00435          </parameter>
00436          <parameter name="folder" required="false">
00437             <para>If not specified, <literal>INBOX</literal> is assumed.</para>
00438          </parameter>
00439       </syntax>
00440       <description>
00441          <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
00442          If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
00443          context. Where the <replaceable>folder</replaceable> can be specified, common folders
00444          include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
00445          <literal>Family</literal> and <literal>Friends</literal>.</para>
00446       </description>
00447    </function>
00448    <manager name="VoicemailUsersList" language="en_US">
00449       <synopsis>
00450          List All Voicemail User Information.
00451       </synopsis>
00452       <syntax>
00453          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00454       </syntax>
00455       <description>
00456       </description>
00457    </manager>
00458  ***/
00459 
00460 #ifdef IMAP_STORAGE
00461 static char imapserver[48];
00462 static char imapport[8];
00463 static char imapflags[128];
00464 static char imapfolder[64];
00465 static char imapparentfolder[64] = "\0";
00466 static char greetingfolder[64];
00467 static char authuser[32];
00468 static char authpassword[42];
00469 static int imapversion = 1;
00470 
00471 static int expungeonhangup = 1;
00472 static int imapgreetings = 0;
00473 static char delimiter = '\0';
00474 
00475 struct vm_state;
00476 struct ast_vm_user;
00477 
00478 AST_THREADSTORAGE(ts_vmstate);
00479 
00480 /* Forward declarations for IMAP */
00481 static int init_mailstream(struct vm_state *vms, int box);
00482 static void write_file(char *filename, char *buffer, unsigned long len);
00483 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00484 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00485 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00486 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00487 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00488 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00489 static void vmstate_insert(struct vm_state *vms);
00490 static void vmstate_delete(struct vm_state *vms);
00491 static void set_update(MAILSTREAM * stream);
00492 static void init_vm_state(struct vm_state *vms);
00493 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00494 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
00495 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00496 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00497 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, const char *msg_id);
00498 static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
00499 static void update_messages_by_imapuser(const char *user, unsigned long number);
00500 static int vm_delete(char *file);
00501 
00502 static int imap_remove_file (char *dir, int msgnum);
00503 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00504 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00505 static void check_quota(struct vm_state *vms, char *mailbox);
00506 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00507 struct vmstate {
00508    struct vm_state *vms;
00509    AST_LIST_ENTRY(vmstate) list;
00510 };
00511 
00512 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00513 
00514 #endif
00515 
00516 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00517 
00518 #define COMMAND_TIMEOUT 5000
00519 /* Don't modify these here; set your umask at runtime instead */
00520 #define  VOICEMAIL_DIR_MODE   0777
00521 #define  VOICEMAIL_FILE_MODE  0666
00522 #define  CHUNKSIZE   65536
00523 
00524 #define VOICEMAIL_CONFIG "voicemail.conf"
00525 #define ASTERISK_USERNAME "asterisk"
00526 
00527 /* Define fast-forward, pause, restart, and reverse keys
00528  * while listening to a voicemail message - these are
00529  * strings, not characters */
00530 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00531 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00532 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00533 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00534 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00535 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00536 
00537 /* Default mail command to mail voicemail. Change it with the
00538  * mailcmd= command in voicemail.conf */
00539 #define SENDMAIL "/usr/sbin/sendmail -t"
00540 
00541 #define INTRO "vm-intro"
00542 
00543 #define MAXMSG 100
00544 #define MAXMSGLIMIT 9999
00545 
00546 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00547 
00548 #define BASELINELEN 72
00549 #define BASEMAXINLINE 256
00550 #ifdef IMAP_STORAGE
00551 #define ENDL "\r\n"
00552 #else
00553 #define ENDL "\n"
00554 #endif
00555 
00556 #define MAX_DATETIME_FORMAT   512
00557 #define MAX_NUM_CID_CONTEXTS 10
00558 
00559 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00560 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00561 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00562 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00563 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00564 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00565 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00566 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00567 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00568 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00569 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00570 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00571 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00572 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00573 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00574 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00575 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00576 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00577 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00578 #define ERROR_LOCK_PATH  -100
00579 #define OPERATOR_EXIT     300
00580 
00581 enum vm_box {
00582    NEW_FOLDER,
00583    OLD_FOLDER,
00584    WORK_FOLDER,
00585    FAMILY_FOLDER,
00586    FRIENDS_FOLDER,
00587    GREETINGS_FOLDER
00588 };
00589 
00590 enum vm_option_flags {
00591    OPT_SILENT =           (1 << 0),
00592    OPT_BUSY_GREETING =    (1 << 1),
00593    OPT_UNAVAIL_GREETING = (1 << 2),
00594    OPT_RECORDGAIN =       (1 << 3),
00595    OPT_PREPEND_MAILBOX =  (1 << 4),
00596    OPT_AUTOPLAY =         (1 << 6),
00597    OPT_DTMFEXIT =         (1 << 7),
00598    OPT_MESSAGE_Urgent =   (1 << 8),
00599    OPT_MESSAGE_PRIORITY = (1 << 9)
00600 };
00601 
00602 enum vm_option_args {
00603    OPT_ARG_RECORDGAIN = 0,
00604    OPT_ARG_PLAYFOLDER = 1,
00605    OPT_ARG_DTMFEXIT   = 2,
00606    /* This *must* be the last value in this enum! */
00607    OPT_ARG_ARRAY_SIZE = 3,
00608 };
00609 
00610 enum vm_passwordlocation {
00611    OPT_PWLOC_VOICEMAILCONF = 0,
00612    OPT_PWLOC_SPOOLDIR      = 1,
00613    OPT_PWLOC_USERSCONF     = 2,
00614 };
00615 
00616 AST_APP_OPTIONS(vm_app_options, {
00617    AST_APP_OPTION('s', OPT_SILENT),
00618    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00619    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00620    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00621    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00622    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00623    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00624    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00625    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00626 });
00627 
00628 static const char * const mailbox_folders[] = {
00629 #ifdef IMAP_STORAGE
00630    imapfolder,
00631 #else
00632    "INBOX",
00633 #endif
00634    "Old",
00635    "Work",
00636    "Family",
00637    "Friends",
00638    "Cust1",
00639    "Cust2",
00640    "Cust3",
00641    "Cust4",
00642    "Cust5",
00643    "Deleted",
00644    "Urgent",
00645 };
00646 
00647 static int load_config(int reload);
00648 #ifdef TEST_FRAMEWORK
00649 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00650 #endif
00651 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
00652 
00653 /*! \page vmlang Voicemail Language Syntaxes Supported
00654 
00655    \par Syntaxes supported, not really language codes.
00656    \arg \b en    - English
00657    \arg \b de    - German
00658    \arg \b es    - Spanish
00659    \arg \b fr    - French
00660    \arg \b it    - Italian
00661    \arg \b nl    - Dutch
00662    \arg \b pt    - Portuguese
00663    \arg \b pt_BR - Portuguese (Brazil)
00664    \arg \b gr    - Greek
00665    \arg \b no    - Norwegian
00666    \arg \b se    - Swedish
00667    \arg \b tw    - Chinese (Taiwan)
00668    \arg \b ua - Ukrainian
00669 
00670 German requires the following additional soundfile:
00671 \arg \b 1F  einE (feminine)
00672 
00673 Spanish requires the following additional soundfile:
00674 \arg \b 1M      un (masculine)
00675 
00676 Dutch, Portuguese & Spanish require the following additional soundfiles:
00677 \arg \b vm-INBOXs singular of 'new'
00678 \arg \b vm-Olds      singular of 'old/heard/read'
00679 
00680 NB these are plural:
00681 \arg \b vm-INBOX  nieuwe (nl)
00682 \arg \b vm-Old    oude (nl)
00683 
00684 Polish uses:
00685 \arg \b vm-new-a  'new', feminine singular accusative
00686 \arg \b vm-new-e  'new', feminine plural accusative
00687 \arg \b vm-new-ych   'new', feminine plural genitive
00688 \arg \b vm-old-a  'old', feminine singular accusative
00689 \arg \b vm-old-e  'old', feminine plural accusative
00690 \arg \b vm-old-ych   'old', feminine plural genitive
00691 \arg \b digits/1-a   'one', not always same as 'digits/1'
00692 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00693 
00694 Swedish uses:
00695 \arg \b vm-nytt      singular of 'new'
00696 \arg \b vm-nya    plural of 'new'
00697 \arg \b vm-gammalt   singular of 'old'
00698 \arg \b vm-gamla  plural of 'old'
00699 \arg \b digits/ett   'one', not always same as 'digits/1'
00700 
00701 Norwegian uses:
00702 \arg \b vm-ny     singular of 'new'
00703 \arg \b vm-nye    plural of 'new'
00704 \arg \b vm-gammel singular of 'old'
00705 \arg \b vm-gamle  plural of 'old'
00706 
00707 Dutch also uses:
00708 \arg \b nl-om     'at'?
00709 
00710 Spanish also uses:
00711 \arg \b vm-youhaveno
00712 
00713 Italian requires the following additional soundfile:
00714 
00715 For vm_intro_it:
00716 \arg \b vm-nuovo  new
00717 \arg \b vm-nuovi  new plural
00718 \arg \b vm-vecchio   old
00719 \arg \b vm-vecchi old plural
00720 
00721 Chinese (Taiwan) requires the following additional soundfile:
00722 \arg \b vm-tong      A class-word for call (tong1)
00723 \arg \b vm-ri     A class-word for day (ri4)
00724 \arg \b vm-you    You (ni3)
00725 \arg \b vm-haveno   Have no (mei2 you3)
00726 \arg \b vm-have     Have (you3)
00727 \arg \b vm-listen   To listen (yao4 ting1)
00728 
00729 
00730 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00731 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00732 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00733 
00734 */
00735 
00736 struct baseio {
00737    int iocp;
00738    int iolen;
00739    int linelength;
00740    int ateof;
00741    unsigned char iobuf[BASEMAXINLINE];
00742 };
00743 
00744 /*! Structure for linked list of users 
00745  * Use ast_vm_user_destroy() to free one of these structures. */
00746 struct ast_vm_user {
00747    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00748    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00749    char password[80];               /*!< Secret pin code, numbers only */
00750    char fullname[80];               /*!< Full name, for directory app */
00751    char email[80];                  /*!< E-mail address */
00752    char *emailsubject;              /*!< E-mail subject */
00753    char *emailbody;                 /*!< E-mail body */
00754    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00755    char serveremail[80];            /*!< From: Mail address */
00756    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00757    char zonetag[80];                /*!< Time zone */
00758    char locale[20];                 /*!< The locale (for presentation of date/time) */
00759    char callback[80];
00760    char dialout[80];
00761    char uniqueid[80];               /*!< Unique integer identifier */
00762    char exit[80];
00763    char attachfmt[20];              /*!< Attachment format */
00764    unsigned int flags;              /*!< VM_ flags */ 
00765    int saydurationm;
00766    int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
00767    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00768    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00769    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00770    int passwordlocation;            /*!< Storage location of the password */
00771 #ifdef IMAP_STORAGE
00772    char imapserver[48];             /*!< IMAP server address */
00773    char imapport[8];                /*!< IMAP server port */
00774    char imapflags[128];             /*!< IMAP optional flags */
00775    char imapuser[80];               /*!< IMAP server login */
00776    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00777    char imapfolder[64];             /*!< IMAP voicemail folder */
00778    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00779    int imapversion;                 /*!< If configuration changes, use the new values */
00780 #endif
00781    double volgain;                  /*!< Volume gain for voicemails sent via email */
00782    AST_LIST_ENTRY(ast_vm_user) list;
00783 };
00784 
00785 /*! Voicemail time zones */
00786 struct vm_zone {
00787    AST_LIST_ENTRY(vm_zone) list;
00788    char name[80];
00789    char timezone[80];
00790    char msg_format[512];
00791 };
00792 
00793 #define VMSTATE_MAX_MSG_ARRAY 256
00794 
00795 /*! Voicemail mailbox state */
00796 struct vm_state {
00797    char curbox[80];
00798    char username[80];
00799    char context[80];
00800    char curdir[PATH_MAX];
00801    char vmbox[PATH_MAX];
00802    char fn[PATH_MAX];
00803    char intro[PATH_MAX];
00804    int *deleted;
00805    int *heard;
00806    int dh_arraysize; /* used for deleted / heard allocation */
00807    int curmsg;
00808    int lastmsg;
00809    int newmessages;
00810    int oldmessages;
00811    int urgentmessages;
00812    int starting;
00813    int repeats;
00814 #ifdef IMAP_STORAGE
00815    ast_mutex_t lock;
00816    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00817    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00818    MAILSTREAM *mailstream;
00819    int vmArrayIndex;
00820    char imapuser[80];                   /*!< IMAP server login */
00821    char imapfolder[64];                 /*!< IMAP voicemail folder */
00822    char imapserver[48];                 /*!< IMAP server address */
00823    char imapport[8];                    /*!< IMAP server port */
00824    char imapflags[128];                 /*!< IMAP optional flags */
00825    int imapversion;
00826    int interactive;
00827    char introfn[PATH_MAX];              /*!< Name of prepended file */
00828    unsigned int quota_limit;
00829    unsigned int quota_usage;
00830    struct vm_state *persist_vms;
00831 #endif
00832 };
00833 
00834 #ifdef ODBC_STORAGE
00835 static char odbc_database[80];
00836 static char odbc_table[80];
00837 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00838 #define DISPOSE(a,b) remove_file(a,b)
00839 #define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
00840 #define EXISTS(a,b,c,d) (message_exists(a,b))
00841 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00842 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00843 #define DELETE(a,b,c,d) (delete_file(a,b))
00844 #define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
00845 #else
00846 #ifdef IMAP_STORAGE
00847 #define DISPOSE(a,b) (imap_remove_file(a,b))
00848 #define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
00849 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00850 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00851 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00852 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00853 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00854 #define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
00855 #else
00856 #define RETRIEVE(a,b,c,d)
00857 #define DISPOSE(a,b)
00858 #define STORE(a,b,c,d,e,f,g,h,i,j,k)
00859 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00860 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00861 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00862 #define DELETE(a,b,c,d) (vm_delete(c))
00863 #define UPDATE_MSG_ID(a, b, c, d, e, f)
00864 #endif
00865 #endif
00866 
00867 static char VM_SPOOL_DIR[PATH_MAX];
00868 
00869 static char ext_pass_cmd[128];
00870 static char ext_pass_check_cmd[128];
00871 
00872 static int my_umask;
00873 
00874 #define PWDCHANGE_INTERNAL (1 << 1)
00875 #define PWDCHANGE_EXTERNAL (1 << 2)
00876 static int pwdchange = PWDCHANGE_INTERNAL;
00877 
00878 #ifdef ODBC_STORAGE
00879 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00880 #else
00881 # ifdef IMAP_STORAGE
00882 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00883 # else
00884 # define tdesc "Comedian Mail (Voicemail System)"
00885 # endif
00886 #endif
00887 
00888 static char userscontext[AST_MAX_EXTENSION] = "default";
00889 
00890 static char *addesc = "Comedian Mail";
00891 
00892 /* Leave a message */
00893 static char *app = "VoiceMail";
00894 
00895 /* Check mail, control, etc */
00896 static char *app2 = "VoiceMailMain";
00897 
00898 static char *app3 = "MailboxExists";
00899 static char *app4 = "VMAuthenticate";
00900 
00901 static char *playmsg_app = "VoiceMailPlayMsg";
00902 
00903 static char *sayname_app = "VMSayName";
00904 
00905 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00906 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00907 static char zonetag[80];
00908 static char locale[20];
00909 static int maxsilence;
00910 static int maxmsg;
00911 static int maxdeletedmsg;
00912 static int silencethreshold = 128;
00913 static char serveremail[80];
00914 static char mailcmd[160];  /* Configurable mail cmd */
00915 static char externnotify[160]; 
00916 static struct ast_smdi_interface *smdi_iface = NULL;
00917 static char vmfmts[80];
00918 static double volgain;
00919 static int vmminsecs;
00920 static int vmmaxsecs;
00921 static int maxgreet;
00922 static int skipms;
00923 static int maxlogins;
00924 static int minpassword;
00925 static int passwordlocation;
00926 
00927 /*! Poll mailboxes for changes since there is something external to
00928  *  app_voicemail that may change them. */
00929 static unsigned int poll_mailboxes;
00930 
00931 /*! Polling frequency */
00932 static unsigned int poll_freq;
00933 /*! By default, poll every 30 seconds */
00934 #define DEFAULT_POLL_FREQ 30
00935 
00936 AST_MUTEX_DEFINE_STATIC(poll_lock);
00937 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00938 static pthread_t poll_thread = AST_PTHREADT_NULL;
00939 static unsigned char poll_thread_run;
00940 
00941 /*! Subscription to ... MWI event subscriptions */
00942 static struct ast_event_sub *mwi_sub_sub;
00943 /*! Subscription to ... MWI event un-subscriptions */
00944 static struct ast_event_sub *mwi_unsub_sub;
00945 
00946 /*!
00947  * \brief An MWI subscription
00948  *
00949  * This is so we can keep track of which mailboxes are subscribed to.
00950  * This way, we know which mailboxes to poll when the pollmailboxes
00951  * option is being used.
00952  */
00953 struct mwi_sub {
00954    AST_RWLIST_ENTRY(mwi_sub) entry;
00955    int old_urgent;
00956    int old_new;
00957    int old_old;
00958    uint32_t uniqueid;
00959    char mailbox[1];
00960 };
00961 
00962 struct mwi_sub_task {
00963    const char *mailbox;
00964    const char *context;
00965    uint32_t uniqueid;
00966 };
00967 
00968 static struct ast_taskprocessor *mwi_subscription_tps;
00969 
00970 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00971 
00972 /* custom audio control prompts for voicemail playback */
00973 static char listen_control_forward_key[12];
00974 static char listen_control_reverse_key[12];
00975 static char listen_control_pause_key[12];
00976 static char listen_control_restart_key[12];
00977 static char listen_control_stop_key[12];
00978 
00979 /* custom password sounds */
00980 static char vm_password[80] = "vm-password";
00981 static char vm_newpassword[80] = "vm-newpassword";
00982 static char vm_passchanged[80] = "vm-passchanged";
00983 static char vm_reenterpassword[80] = "vm-reenterpassword";
00984 static char vm_mismatch[80] = "vm-mismatch";
00985 static char vm_invalid_password[80] = "vm-invalid-password";
00986 static char vm_pls_try_again[80] = "vm-pls-try-again";
00987 
00988 /*
00989  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
00990  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
00991  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
00992  * app.c's __ast_play_and_record function
00993  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
00994  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
00995  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
00996  * more effort than either of the other two.
00997  */
00998 static char vm_prepend_timeout[80] = "vm-then-pound";
00999 
01000 static struct ast_flags globalflags = {0};
01001 
01002 static int saydurationminfo;
01003 
01004 static char dialcontext[AST_MAX_CONTEXT] = "";
01005 static char callcontext[AST_MAX_CONTEXT] = "";
01006 static char exitcontext[AST_MAX_CONTEXT] = "";
01007 
01008 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
01009 
01010 
01011 static char *emailbody = NULL;
01012 static char *emailsubject = NULL;
01013 static char *pagerbody = NULL;
01014 static char *pagersubject = NULL;
01015 static char fromstring[100];
01016 static char pagerfromstring[100];
01017 static char charset[32] = "ISO-8859-1";
01018 
01019 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
01020 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
01021 static int adsiver = 1;
01022 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
01023 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
01024 
01025 /* Forward declarations - generic */
01026 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
01027 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
01028 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);
01029 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
01030 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
01031          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
01032          signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id);
01033 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
01034 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
01035 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);
01036 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, const char *msg_id);
01037 static void apply_options(struct ast_vm_user *vmu, const char *options);
01038 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);
01039 static int is_valid_dtmf(const char *key);
01040 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
01041 static int write_password_to_file(const char *secretfn, const char *password);
01042 static const char *substitute_escapes(const char *value);
01043 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
01044 /*!
01045  * Place a message in the indicated folder
01046  *
01047  * \param vmu Voicemail user
01048  * \param vms Current voicemail state for the user
01049  * \param msg The message number to save
01050  * \param box The folder into which the message should be saved
01051  * \param[out] newmsg The new message number of the saved message
01052  * \param move Tells whether to copy or to move the message
01053  *
01054  * \note the "move" parameter is only honored for IMAP voicemail presently
01055  * \retval 0 Success
01056  * \revval other Failure
01057  */
01058 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
01059 
01060 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD);
01061 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
01062 
01063 static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old);
01064 static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder);
01065 static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
01066 static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb);
01067 
01068 #ifdef TEST_FRAMEWORK
01069 static int vm_test_destroy_user(const char *context, const char *mailbox);
01070 static int vm_test_create_user(const char *context, const char *mailbox);
01071 #endif
01072 
01073 struct ao2_container *inprocess_container;
01074 
01075 struct inprocess {
01076    int count;
01077    char *context;
01078    char mailbox[0];
01079 };
01080 
01081 static int inprocess_hash_fn(const void *obj, const int flags)
01082 {
01083    const struct inprocess *i = obj;
01084    return atoi(i->mailbox);
01085 }
01086 
01087 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
01088 {
01089    struct inprocess *i = obj, *j = arg;
01090    if (strcmp(i->mailbox, j->mailbox)) {
01091       return 0;
01092    }
01093    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
01094 }
01095 
01096 static int inprocess_count(const char *context, const char *mailbox, int delta)
01097 {
01098    struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
01099    arg->context = arg->mailbox + strlen(mailbox) + 1;
01100    strcpy(arg->mailbox, mailbox); /* SAFE */
01101    strcpy(arg->context, context); /* SAFE */
01102    ao2_lock(inprocess_container);
01103    if ((i = ao2_find(inprocess_container, arg, 0))) {
01104       int ret = ast_atomic_fetchadd_int(&i->count, delta);
01105       ao2_unlock(inprocess_container);
01106       ao2_ref(i, -1);
01107       return ret;
01108    }
01109    if (delta < 0) {
01110       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
01111    }
01112    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
01113       ao2_unlock(inprocess_container);
01114       return 0;
01115    }
01116    i->context = i->mailbox + strlen(mailbox) + 1;
01117    strcpy(i->mailbox, mailbox); /* SAFE */
01118    strcpy(i->context, context); /* SAFE */
01119    i->count = delta;
01120    ao2_link(inprocess_container, i);
01121    ao2_unlock(inprocess_container);
01122    ao2_ref(i, -1);
01123    return 0;
01124 }
01125 
01126 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
01127 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
01128 #endif
01129 
01130 /*!
01131  * \brief Strips control and non 7-bit clean characters from input string.
01132  *
01133  * \note To map control and none 7-bit characters to a 7-bit clean characters
01134  *  please use ast_str_encode_mine().
01135  */
01136 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
01137 {
01138    char *bufptr = buf;
01139    for (; *input; input++) {
01140       if (*input < 32) {
01141          continue;
01142       }
01143       *bufptr++ = *input;
01144       if (bufptr == buf + buflen - 1) {
01145          break;
01146       }
01147    }
01148    *bufptr = '\0';
01149    return buf;
01150 }
01151 
01152 
01153 /*!
01154  * \brief Sets default voicemail system options to a voicemail user.
01155  *
01156  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
01157  * - all the globalflags
01158  * - the saydurationminfo
01159  * - the callcontext
01160  * - the dialcontext
01161  * - the exitcontext
01162  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
01163  * - volume gain.
01164  * - emailsubject, emailbody set to NULL
01165  */
01166 static void populate_defaults(struct ast_vm_user *vmu)
01167 {
01168    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
01169    vmu->passwordlocation = passwordlocation;
01170    if (saydurationminfo) {
01171       vmu->saydurationm = saydurationminfo;
01172    }
01173    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
01174    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
01175    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
01176    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
01177    ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
01178    if (vmminsecs) {
01179       vmu->minsecs = vmminsecs;
01180    }
01181    if (vmmaxsecs) {
01182       vmu->maxsecs = vmmaxsecs;
01183    }
01184    if (maxmsg) {
01185       vmu->maxmsg = maxmsg;
01186    }
01187    if (maxdeletedmsg) {
01188       vmu->maxdeletedmsg = maxdeletedmsg;
01189    }
01190    vmu->volgain = volgain;
01191    ast_free(vmu->emailsubject);
01192    vmu->emailsubject = NULL;
01193    ast_free(vmu->emailbody);
01194    vmu->emailbody = NULL;
01195 #ifdef IMAP_STORAGE
01196    ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
01197    ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
01198    ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
01199    ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
01200 #endif
01201 }
01202 
01203 /*!
01204  * \brief Sets a a specific property value.
01205  * \param vmu The voicemail user object to work with.
01206  * \param var The name of the property to be set.
01207  * \param value The value to be set to the property.
01208  * 
01209  * The property name must be one of the understood properties. See the source for details.
01210  */
01211 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
01212 {
01213    int x;
01214    if (!strcasecmp(var, "attach")) {
01215       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
01216    } else if (!strcasecmp(var, "attachfmt")) {
01217       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
01218    } else if (!strcasecmp(var, "serveremail")) {
01219       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
01220    } else if (!strcasecmp(var, "emailbody")) {
01221       ast_free(vmu->emailbody);
01222       vmu->emailbody = ast_strdup(substitute_escapes(value));
01223    } else if (!strcasecmp(var, "emailsubject")) {
01224       ast_free(vmu->emailsubject);
01225       vmu->emailsubject = ast_strdup(substitute_escapes(value));
01226    } else if (!strcasecmp(var, "language")) {
01227       ast_copy_string(vmu->language, value, sizeof(vmu->language));
01228    } else if (!strcasecmp(var, "tz")) {
01229       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
01230    } else if (!strcasecmp(var, "locale")) {
01231       ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
01232 #ifdef IMAP_STORAGE
01233    } else if (!strcasecmp(var, "imapuser")) {
01234       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
01235       vmu->imapversion = imapversion;
01236    } else if (!strcasecmp(var, "imapserver")) {
01237       ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
01238       vmu->imapversion = imapversion;
01239    } else if (!strcasecmp(var, "imapport")) {
01240       ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
01241       vmu->imapversion = imapversion;
01242    } else if (!strcasecmp(var, "imapflags")) {
01243       ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
01244       vmu->imapversion = imapversion;
01245    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
01246       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
01247       vmu->imapversion = imapversion;
01248    } else if (!strcasecmp(var, "imapfolder")) {
01249       ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
01250       vmu->imapversion = imapversion;
01251    } else if (!strcasecmp(var, "imapvmshareid")) {
01252       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
01253       vmu->imapversion = imapversion;
01254 #endif
01255    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
01256       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
01257    } else if (!strcasecmp(var, "saycid")){
01258       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
01259    } else if (!strcasecmp(var, "sendvoicemail")){
01260       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
01261    } else if (!strcasecmp(var, "review")){
01262       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
01263    } else if (!strcasecmp(var, "tempgreetwarn")){
01264       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
01265    } else if (!strcasecmp(var, "messagewrap")){
01266       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
01267    } else if (!strcasecmp(var, "operator")) {
01268       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
01269    } else if (!strcasecmp(var, "envelope")){
01270       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
01271    } else if (!strcasecmp(var, "moveheard")){
01272       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
01273    } else if (!strcasecmp(var, "sayduration")){
01274       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
01275    } else if (!strcasecmp(var, "saydurationm")){
01276       if (sscanf(value, "%30d", &x) == 1) {
01277          vmu->saydurationm = x;
01278       } else {
01279          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
01280       }
01281    } else if (!strcasecmp(var, "forcename")){
01282       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
01283    } else if (!strcasecmp(var, "forcegreetings")){
01284       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
01285    } else if (!strcasecmp(var, "callback")) {
01286       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
01287    } else if (!strcasecmp(var, "dialout")) {
01288       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
01289    } else if (!strcasecmp(var, "exitcontext")) {
01290       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
01291    } else if (!strcasecmp(var, "minsecs")) {
01292       if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
01293          vmu->minsecs = x;
01294       } else {
01295          ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
01296          vmu->minsecs = vmminsecs;
01297       }
01298    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
01299       vmu->maxsecs = atoi(value);
01300       if (vmu->maxsecs <= 0) {
01301          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
01302          vmu->maxsecs = vmmaxsecs;
01303       } else {
01304          vmu->maxsecs = atoi(value);
01305       }
01306       if (!strcasecmp(var, "maxmessage"))
01307          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
01308    } else if (!strcasecmp(var, "maxmsg")) {
01309       vmu->maxmsg = atoi(value);
01310       /* Accept maxmsg=0 (Greetings only voicemail) */
01311       if (vmu->maxmsg < 0) {
01312          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
01313          vmu->maxmsg = MAXMSG;
01314       } else if (vmu->maxmsg > MAXMSGLIMIT) {
01315          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
01316          vmu->maxmsg = MAXMSGLIMIT;
01317       }
01318    } else if (!strcasecmp(var, "nextaftercmd")) {
01319       ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
01320    } else if (!strcasecmp(var, "backupdeleted")) {
01321       if (sscanf(value, "%30d", &x) == 1)
01322          vmu->maxdeletedmsg = x;
01323       else if (ast_true(value))
01324          vmu->maxdeletedmsg = MAXMSG;
01325       else
01326          vmu->maxdeletedmsg = 0;
01327 
01328       if (vmu->maxdeletedmsg < 0) {
01329          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
01330          vmu->maxdeletedmsg = MAXMSG;
01331       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
01332          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
01333          vmu->maxdeletedmsg = MAXMSGLIMIT;
01334       }
01335    } else if (!strcasecmp(var, "volgain")) {
01336       sscanf(value, "%30lf", &vmu->volgain);
01337    } else if (!strcasecmp(var, "passwordlocation")) {
01338       if (!strcasecmp(value, "spooldir")) {
01339          vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
01340       } else {
01341          vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
01342       }
01343    } else if (!strcasecmp(var, "options")) {
01344       apply_options(vmu, value);
01345    }
01346 }
01347 
01348 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
01349 {
01350    int fds[2], pid = 0;
01351 
01352    memset(buf, 0, len);
01353 
01354    if (pipe(fds)) {
01355       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
01356    } else {
01357       /* good to go*/
01358       pid = ast_safe_fork(0);
01359 
01360       if (pid < 0) {
01361          /* ok maybe not */
01362          close(fds[0]);
01363          close(fds[1]);
01364          snprintf(buf, len, "FAILURE: Fork failed");
01365       } else if (pid) {
01366          /* parent */
01367          close(fds[1]);
01368          if (read(fds[0], buf, len) < 0) {
01369             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
01370          }
01371          close(fds[0]);
01372       } else {
01373          /*  child */
01374          AST_DECLARE_APP_ARGS(arg,
01375             AST_APP_ARG(v)[20];
01376          );
01377          char *mycmd = ast_strdupa(command);
01378 
01379          close(fds[0]);
01380          dup2(fds[1], STDOUT_FILENO);
01381          close(fds[1]);
01382          ast_close_fds_above_n(STDOUT_FILENO);
01383 
01384          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
01385 
01386          execv(arg.v[0], arg.v); 
01387          printf("FAILURE: %s", strerror(errno));
01388          _exit(0);
01389       }
01390    }
01391    return buf;
01392 }
01393 
01394 /*!
01395  * \brief Check that password meets minimum required length
01396  * \param vmu The voicemail user to change the password for.
01397  * \param password The password string to check
01398  *
01399  * \return zero on ok, 1 on not ok.
01400  */
01401 static int check_password(struct ast_vm_user *vmu, char *password)
01402 {
01403    /* check minimum length */
01404    if (strlen(password) < minpassword)
01405       return 1;
01406    /* check that password does not contain '*' character */
01407    if (!ast_strlen_zero(password) && password[0] == '*')
01408       return 1;
01409    if (!ast_strlen_zero(ext_pass_check_cmd)) {
01410       char cmd[255], buf[255];
01411 
01412       ast_debug(1, "Verify password policies for %s\n", password);
01413 
01414       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01415       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01416          ast_debug(5, "Result: %s\n", buf);
01417          if (!strncasecmp(buf, "VALID", 5)) {
01418             ast_debug(3, "Passed password check: '%s'\n", buf);
01419             return 0;
01420          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01421             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01422             return 0;
01423          } else {
01424             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01425             return 1;
01426          }
01427       }
01428    }
01429    return 0;
01430 }
01431 
01432 /*! 
01433  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01434  * \param vmu The voicemail user to change the password for.
01435  * \param password The new value to be set to the password for this user.
01436  * 
01437  * This only works if there is a realtime engine configured.
01438  * This is called from the (top level) vm_change_password.
01439  *
01440  * \return zero on success, -1 on error.
01441  */
01442 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01443 {
01444    int res = -1;
01445    if (!strcmp(vmu->password, password)) {
01446       /* No change (but an update would return 0 rows updated, so we opt out here) */
01447       return 0;
01448    }
01449 
01450    if (strlen(password) > 10) {
01451       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01452    }
01453    if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
01454       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
01455       ast_copy_string(vmu->password, password, sizeof(vmu->password));
01456       res = 0;
01457    }
01458    return res;
01459 }
01460 
01461 /*!
01462  * \brief Destructively Parse options and apply.
01463  */
01464 static void apply_options(struct ast_vm_user *vmu, const char *options)
01465 {  
01466    char *stringp;
01467    char *s;
01468    char *var, *value;
01469    stringp = ast_strdupa(options);
01470    while ((s = strsep(&stringp, "|"))) {
01471       value = s;
01472       if ((var = strsep(&value, "=")) && value) {
01473          apply_option(vmu, var, value);
01474       }
01475    }  
01476 }
01477 
01478 /*!
01479  * \brief Loads the options specific to a voicemail user.
01480  * 
01481  * This is called when a vm_user structure is being set up, such as from load_options.
01482  */
01483 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01484 {
01485    for (; var; var = var->next) {
01486       if (!strcasecmp(var->name, "vmsecret")) {
01487          ast_copy_string(retval->password, var->value, sizeof(retval->password));
01488       } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
01489          if (ast_strlen_zero(retval->password)) {
01490             if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
01491                ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
01492                   "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
01493             } else {
01494                ast_copy_string(retval->password, var->value, sizeof(retval->password));
01495             }
01496          }
01497       } else if (!strcasecmp(var->name, "uniqueid")) {
01498          ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
01499       } else if (!strcasecmp(var->name, "pager")) {
01500          ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
01501       } else if (!strcasecmp(var->name, "email")) {
01502          ast_copy_string(retval->email, var->value, sizeof(retval->email));
01503       } else if (!strcasecmp(var->name, "fullname")) {
01504          ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
01505       } else if (!strcasecmp(var->name, "context")) {
01506          ast_copy_string(retval->context, var->value, sizeof(retval->context));
01507       } else if (!strcasecmp(var->name, "emailsubject")) {
01508          ast_free(retval->emailsubject);
01509          retval->emailsubject = ast_strdup(substitute_escapes(var->value));
01510       } else if (!strcasecmp(var->name, "emailbody")) {
01511          ast_free(retval->emailbody);
01512          retval->emailbody = ast_strdup(substitute_escapes(var->value));
01513 #ifdef IMAP_STORAGE
01514       } else if (!strcasecmp(var->name, "imapuser")) {
01515          ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
01516          retval->imapversion = imapversion;
01517       } else if (!strcasecmp(var->name, "imapserver")) {
01518          ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
01519          retval->imapversion = imapversion;
01520       } else if (!strcasecmp(var->name, "imapport")) {
01521          ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
01522          retval->imapversion = imapversion;
01523       } else if (!strcasecmp(var->name, "imapflags")) {
01524          ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
01525          retval->imapversion = imapversion;
01526       } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
01527          ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
01528          retval->imapversion = imapversion;
01529       } else if (!strcasecmp(var->name, "imapfolder")) {
01530          ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
01531          retval->imapversion = imapversion;
01532       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01533          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01534          retval->imapversion = imapversion;
01535 #endif
01536       } else
01537          apply_option(retval, var->name, var->value);
01538    }
01539 }
01540 
01541 /*!
01542  * \brief Determines if a DTMF key entered is valid.
01543  * \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.
01544  *
01545  * Tests the character entered against the set of valid DTMF characters. 
01546  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01547  */
01548 static int is_valid_dtmf(const char *key)
01549 {
01550    int i;
01551    char *local_key = ast_strdupa(key);
01552 
01553    for (i = 0; i < strlen(key); ++i) {
01554       if (!strchr(VALID_DTMF, *local_key)) {
01555          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01556          return 0;
01557       }
01558       local_key++;
01559    }
01560    return 1;
01561 }
01562 
01563 /*!
01564  * \brief Finds a voicemail user from the realtime engine.
01565  * \param ivm
01566  * \param context
01567  * \param mailbox
01568  *
01569  * 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.
01570  *
01571  * \return The ast_vm_user structure for the user that was found.
01572  */
01573 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01574 {
01575    struct ast_variable *var;
01576    struct ast_vm_user *retval;
01577 
01578    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01579       if (ivm) {
01580          memset(retval, 0, sizeof(*retval));
01581       }
01582       populate_defaults(retval);
01583       if (!ivm) {
01584          ast_set_flag(retval, VM_ALLOCED);
01585       }
01586       if (mailbox) {
01587          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01588       }
01589       if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
01590          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01591       } else {
01592          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01593       }
01594       if (var) {
01595          apply_options_full(retval, var);
01596          ast_variables_destroy(var);
01597       } else { 
01598          if (!ivm) 
01599             ast_free(retval);
01600          retval = NULL;
01601       }  
01602    } 
01603    return retval;
01604 }
01605 
01606 /*!
01607  * \brief Finds a voicemail user from the users file or the realtime engine.
01608  * \param ivm
01609  * \param context
01610  * \param mailbox
01611  * 
01612  * \return The ast_vm_user structure for the user that was found.
01613  */
01614 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01615 {
01616    /* This function could be made to generate one from a database, too */
01617    struct ast_vm_user *vmu = NULL, *cur;
01618    AST_LIST_LOCK(&users);
01619 
01620    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01621       context = "default";
01622 
01623    AST_LIST_TRAVERSE(&users, cur, list) {
01624 #ifdef IMAP_STORAGE
01625       if (cur->imapversion != imapversion) {
01626          continue;
01627       }
01628 #endif
01629       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01630          break;
01631       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01632          break;
01633    }
01634    if (cur) {
01635       /* Make a copy, so that on a reload, we have no race */
01636       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01637          *vmu = *cur;
01638          if (!ivm) {
01639             vmu->emailbody = ast_strdup(cur->emailbody);
01640             vmu->emailsubject = ast_strdup(cur->emailsubject);
01641          }
01642          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01643          AST_LIST_NEXT(vmu, list) = NULL;
01644       }
01645    } else
01646       vmu = find_user_realtime(ivm, context, mailbox);
01647    AST_LIST_UNLOCK(&users);
01648    return vmu;
01649 }
01650 
01651 /*!
01652  * \brief Resets a user password to a specified password.
01653  * \param context
01654  * \param mailbox
01655  * \param newpass
01656  *
01657  * This does the actual change password work, called by the vm_change_password() function.
01658  *
01659  * \return zero on success, -1 on error.
01660  */
01661 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01662 {
01663    /* This function could be made to generate one from a database, too */
01664    struct ast_vm_user *cur;
01665    int res = -1;
01666    AST_LIST_LOCK(&users);
01667    AST_LIST_TRAVERSE(&users, cur, list) {
01668       if ((!context || !strcasecmp(context, cur->context)) &&
01669          (!strcasecmp(mailbox, cur->mailbox)))
01670             break;
01671    }
01672    if (cur) {
01673       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01674       res = 0;
01675    }
01676    AST_LIST_UNLOCK(&users);
01677    return res;
01678 }
01679 
01680 /*!
01681  * \brief Check if configuration file is valid
01682  */
01683 static inline int valid_config(const struct ast_config *cfg)
01684 {
01685    return cfg && cfg != CONFIG_STATUS_FILEINVALID;
01686 }
01687 
01688 /*! 
01689  * \brief The handler for the change password option.
01690  * \param vmu The voicemail user to work with.
01691  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01692  * 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.
01693  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01694  */
01695 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01696 {
01697    struct ast_config   *cfg = NULL;
01698    struct ast_variable *var = NULL;
01699    struct ast_category *cat = NULL;
01700    char *category = NULL, *value = NULL, *new = NULL;
01701    const char *tmp = NULL;
01702    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01703    char secretfn[PATH_MAX] = "";
01704    int found = 0;
01705 
01706    if (!change_password_realtime(vmu, newpassword))
01707       return;
01708 
01709    /* check if we should store the secret in the spool directory next to the messages */
01710    switch (vmu->passwordlocation) {
01711    case OPT_PWLOC_SPOOLDIR:
01712       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
01713       if (write_password_to_file(secretfn, newpassword) == 0) {
01714          ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
01715          ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
01716          reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01717          ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01718          break;
01719       } else {
01720          ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
01721       }
01722       /* Fall-through */
01723    case OPT_PWLOC_VOICEMAILCONF:
01724       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
01725          while ((category = ast_category_browse(cfg, category))) {
01726             if (!strcasecmp(category, vmu->context)) {
01727                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01728                   ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01729                   break;
01730                }
01731                value = strstr(tmp, ",");
01732                if (!value) {
01733                   new = ast_alloca(strlen(newpassword)+1);
01734                   sprintf(new, "%s", newpassword);
01735                } else {
01736                   new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
01737                   sprintf(new, "%s%s", newpassword, value);
01738                }
01739                if (!(cat = ast_category_get(cfg, category))) {
01740                   ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01741                   break;
01742                }
01743                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01744                found = 1;
01745             }
01746          }
01747          /* save the results */
01748          if (found) {
01749             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
01750             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01751             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01752             ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01753             ast_config_destroy(cfg);
01754             break;
01755          }
01756 
01757          ast_config_destroy(cfg);
01758       }
01759       /* Fall-through */
01760    case OPT_PWLOC_USERSCONF:
01761       /* check users.conf and update the password stored for the mailbox */
01762       /* if no vmsecret entry exists create one. */
01763       if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
01764          ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01765          for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
01766             ast_debug(4, "users.conf: %s\n", category);
01767             if (!strcasecmp(category, vmu->mailbox)) {
01768                if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
01769                   ast_debug(3, "looks like we need to make vmsecret!\n");
01770                   var = ast_variable_new("vmsecret", newpassword, "");
01771                } else {
01772                   var = NULL;
01773                }
01774                new = ast_alloca(strlen(newpassword) + 1);
01775                sprintf(new, "%s", newpassword);
01776                if (!(cat = ast_category_get(cfg, category))) {
01777                   ast_debug(4, "failed to get category!\n");
01778                   ast_free(var);
01779                   break;
01780                }
01781                if (!var) {
01782                   ast_variable_update(cat, "vmsecret", new, NULL, 0);
01783                } else {
01784                   ast_variable_append(cat, var);
01785                }
01786                found = 1;
01787                break;
01788             }
01789          }
01790          /* save the results and clean things up */
01791          if (found) {
01792             ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
01793             reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01794             ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01795             ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
01796          }
01797 
01798          ast_config_destroy(cfg);
01799       }
01800    }
01801 }
01802 
01803 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01804 {
01805    char buf[255];
01806    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01807    ast_debug(1, "External password: %s\n",buf);
01808    if (!ast_safe_system(buf)) {
01809       ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
01810       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01811       /* Reset the password in memory, too */
01812       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01813    }
01814 }
01815 
01816 /*! 
01817  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01818  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01819  * \param len The length of the path string that was written out.
01820  * \param context
01821  * \param ext 
01822  * \param folder 
01823  * 
01824  * The path is constructed as 
01825  *    VM_SPOOL_DIRcontext/ext/folder
01826  *
01827  * \return zero on success, -1 on error.
01828  */
01829 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01830 {
01831    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01832 }
01833 
01834 /*! 
01835  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01836  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01837  * \param len The length of the path string that was written out.
01838  * \param dir 
01839  * \param num 
01840  * 
01841  * The path is constructed as 
01842  *    VM_SPOOL_DIRcontext/ext/folder
01843  *
01844  * \return zero on success, -1 on error.
01845  */
01846 static int make_file(char *dest, const int len, const char *dir, const int num)
01847 {
01848    return snprintf(dest, len, "%s/msg%04d", dir, num);
01849 }
01850 
01851 /* same as mkstemp, but return a FILE * */
01852 static FILE *vm_mkftemp(char *template)
01853 {
01854    FILE *p = NULL;
01855    int pfd = mkstemp(template);
01856    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01857    if (pfd > -1) {
01858       p = fdopen(pfd, "w+");
01859       if (!p) {
01860          close(pfd);
01861          pfd = -1;
01862       }
01863    }
01864    return p;
01865 }
01866 
01867 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01868  * \param dest    String. base directory.
01869  * \param len     Length of dest.
01870  * \param context String. Ignored if is null or empty string.
01871  * \param ext     String. Ignored if is null or empty string.
01872  * \param folder  String. Ignored if is null or empty string. 
01873  * \return -1 on failure, 0 on success.
01874  */
01875 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01876 {
01877    mode_t   mode = VOICEMAIL_DIR_MODE;
01878    int res;
01879 
01880    make_dir(dest, len, context, ext, folder);
01881    if ((res = ast_mkdir(dest, mode))) {
01882       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01883       return -1;
01884    }
01885    return 0;
01886 }
01887 
01888 static const char *mbox(struct ast_vm_user *vmu, int id)
01889 {
01890 #ifdef IMAP_STORAGE
01891    if (vmu && id == 0) {
01892       return vmu->imapfolder;
01893    }
01894 #endif
01895    return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
01896 }
01897 
01898 static const char *vm_index_to_foldername(int id)
01899 {
01900    return mbox(NULL, id);
01901 }
01902 
01903 
01904 static int get_folder_by_name(const char *name)
01905 {
01906    size_t i;
01907 
01908    for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
01909       if (strcasecmp(name, mailbox_folders[i]) == 0) {
01910          return i;
01911       }
01912    }
01913 
01914    return -1;
01915 }
01916 
01917 static void free_user(struct ast_vm_user *vmu)
01918 {
01919    if (ast_test_flag(vmu, VM_ALLOCED)) {
01920 
01921       ast_free(vmu->emailbody);
01922       vmu->emailbody = NULL;
01923 
01924       ast_free(vmu->emailsubject);
01925       vmu->emailsubject = NULL;
01926 
01927       ast_free(vmu);
01928    }
01929 }
01930 
01931 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01932 
01933    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01934 
01935    /* remove old allocation */
01936    if (vms->deleted) {
01937       ast_free(vms->deleted);
01938       vms->deleted = NULL;
01939    }
01940    if (vms->heard) {
01941       ast_free(vms->heard);
01942       vms->heard = NULL;
01943    }
01944    vms->dh_arraysize = 0;
01945 
01946    if (arraysize > 0) {
01947       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01948          return -1;
01949       }
01950       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01951          ast_free(vms->deleted);
01952          vms->deleted = NULL;
01953          return -1;
01954       }
01955       vms->dh_arraysize = arraysize;
01956    }
01957 
01958    return 0;
01959 }
01960 
01961 /* All IMAP-specific functions should go in this block. This
01962  * keeps them from being spread out all over the code */
01963 #ifdef IMAP_STORAGE
01964 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01965 {
01966    char arg[10];
01967    struct vm_state *vms;
01968    unsigned long messageNum;
01969 
01970    /* If greetings aren't stored in IMAP, just delete the file */
01971    if (msgnum < 0 && !imapgreetings) {
01972       ast_filedelete(file, NULL);
01973       return;
01974    }
01975 
01976    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01977       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);
01978       return;
01979    }
01980 
01981    if (msgnum < 0) {
01982       imap_delete_old_greeting(file, vms);
01983       return;
01984    }
01985 
01986    /* find real message number based on msgnum */
01987    /* this may be an index into vms->msgArray based on the msgnum. */
01988    messageNum = vms->msgArray[msgnum];
01989    if (messageNum == 0) {
01990       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01991       return;
01992    }
01993    ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01994    /* delete message */
01995    snprintf (arg, sizeof(arg), "%lu", messageNum);
01996    ast_mutex_lock(&vms->lock);
01997    mail_setflag (vms->mailstream, arg, "\\DELETED");
01998    mail_expunge(vms->mailstream);
01999    ast_mutex_unlock(&vms->lock);
02000 }
02001 
02002 static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder)
02003 {
02004    struct ast_channel *chan;
02005    char *cid;
02006    char *cid_name;
02007    char *cid_num;
02008    struct vm_state *vms;
02009    const char *duration_str;
02010    int duration = 0;
02011 
02012    /*
02013     * First, get things initially set up. If any of this fails, then
02014     * back out before doing anything substantial
02015     */
02016    vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
02017    if (!vms) {
02018       return;
02019    }
02020 
02021    if (open_mailbox(vms, vmu, folder)) {
02022       return;
02023    }
02024 
02025    chan = ast_dummy_channel_alloc();
02026    if (!chan) {
02027       close_mailbox(vms, vmu);
02028       return;
02029    }
02030 
02031    /*
02032     * We need to make sure the new message we save has the same
02033     * callerid, flag, and duration as the original message
02034     */
02035    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
02036 
02037    if (!ast_strlen_zero(cid)) {
02038       ast_callerid_parse(cid, &cid_name, &cid_num);
02039       ast_party_caller_init(ast_channel_caller(chan));
02040       if (!ast_strlen_zero(cid_name)) {
02041          ast_channel_caller(chan)->id.name.valid = 1;
02042          ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
02043       }
02044       if (!ast_strlen_zero(cid_num)) {
02045          ast_channel_caller(chan)->id.number.valid = 1;
02046          ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
02047       }
02048    }
02049 
02050    duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
02051 
02052    if (!ast_strlen_zero(duration_str)) {
02053       sscanf(duration_str, "%30d", &duration);
02054    }
02055 
02056    /*
02057     * IMAP messages cannot be altered once delivered. So we have to delete the
02058     * current message and then re-add it with the updated message ID.
02059     *
02060     * Furthermore, there currently is no atomic way to create a new message and to
02061     * store it in an arbitrary folder. So we have to save it to the INBOX and then
02062     * move to the appropriate folder.
02063     */
02064    if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
02065          duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
02066       if (folder != NEW_FOLDER) {
02067          save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
02068       }
02069       vm_imap_delete(dir, msgnum, vmu);
02070    }
02071    close_mailbox(vms, vmu);
02072    ast_channel_unref(chan);
02073 }
02074 
02075 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
02076 {
02077    struct vm_state *vms_p;
02078    char *file, *filename;
02079    char *attachment;
02080    int i;
02081    BODY *body;
02082 
02083    /* This function is only used for retrieval of IMAP greetings
02084     * regular messages are not retrieved this way, nor are greetings
02085     * if they are stored locally*/
02086    if (msgnum > -1 || !imapgreetings) {
02087       return 0;
02088    } else {
02089       file = strrchr(ast_strdupa(dir), '/');
02090       if (file)
02091          *file++ = '\0';
02092       else {
02093          ast_debug(1, "Failed to procure file name from directory passed.\n");
02094          return -1;
02095       }
02096    }
02097 
02098    /* check if someone is accessing this box right now... */
02099    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
02100       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
02101       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
02102       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
02103       * that's all we need to do.
02104       */
02105       if (!(vms_p = create_vm_state_from_user(vmu))) {
02106          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
02107          return -1;
02108       }
02109    }
02110 
02111    /* Greetings will never have a prepended message */
02112    *vms_p->introfn = '\0';
02113 
02114    ast_mutex_lock(&vms_p->lock);
02115    if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
02116       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
02117       ast_mutex_unlock(&vms_p->lock);
02118       return -1;
02119    }
02120 
02121    /*XXX Yuck, this could probably be done a lot better */
02122    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
02123       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
02124       /* We have the body, now we extract the file name of the first attachment. */
02125       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
02126          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
02127       } else {
02128          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
02129          ast_mutex_unlock(&vms_p->lock);
02130          return -1;
02131       }
02132       filename = strsep(&attachment, ".");
02133       if (!strcmp(filename, file)) {
02134          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
02135          vms_p->msgArray[vms_p->curmsg] = i + 1;
02136          save_body(body, vms_p, "2", attachment, 0);
02137          ast_mutex_unlock(&vms_p->lock);
02138          return 0;
02139       }
02140    }
02141    ast_mutex_unlock(&vms_p->lock);
02142 
02143    return -1;
02144 }
02145 
02146 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
02147 {
02148    BODY *body;
02149    char *header_content;
02150    char *attachedfilefmt;
02151    char buf[80];
02152    struct vm_state *vms;
02153    char text_file[PATH_MAX];
02154    FILE *text_file_ptr;
02155    int res = 0;
02156    struct ast_vm_user *vmu;
02157 
02158    if (!(vmu = find_user(NULL, context, mailbox))) {
02159       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
02160       return -1;
02161    }
02162    
02163    if (msgnum < 0) {
02164       if (imapgreetings) {
02165          res = imap_retrieve_greeting(dir, msgnum, vmu);
02166          goto exit;
02167       } else {
02168          res = 0;
02169          goto exit;
02170       }
02171    }
02172 
02173    /* Before anything can happen, we need a vm_state so that we can
02174     * actually access the imap server through the vms->mailstream
02175     */
02176    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
02177       /* This should not happen. If it does, then I guess we'd
02178        * need to create the vm_state, extract which mailbox to
02179        * open, and then set up the msgArray so that the correct
02180        * IMAP message could be accessed. If I have seen correctly
02181        * though, the vms should be obtainable from the vmstates list
02182        * and should have its msgArray properly set up.
02183        */
02184       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
02185       res = -1;
02186       goto exit;
02187    }
02188 
02189    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
02190    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
02191 
02192    /* Don't try to retrieve a message from IMAP if it already is on the file system */
02193    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
02194       res = 0;
02195       goto exit;
02196    }
02197 
02198    ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
02199    if (vms->msgArray[msgnum] == 0) {
02200       ast_log(LOG_WARNING, "Trying to access unknown message\n");
02201       res = -1;
02202       goto exit;
02203    }
02204 
02205    /* This will only work for new messages... */
02206    ast_mutex_lock(&vms->lock);
02207    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
02208    ast_mutex_unlock(&vms->lock);
02209    /* empty string means no valid header */
02210    if (ast_strlen_zero(header_content)) {
02211       ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
02212       res = -1;
02213       goto exit;
02214    }
02215 
02216    ast_mutex_lock(&vms->lock);
02217    mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
02218    ast_mutex_unlock(&vms->lock);
02219 
02220    /* We have the body, now we extract the file name of the first attachment. */
02221    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
02222       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
02223    } else {
02224       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
02225       res = -1;
02226       goto exit;
02227    }
02228    
02229    /* Find the format of the attached file */
02230 
02231    strsep(&attachedfilefmt, ".");
02232    if (!attachedfilefmt) {
02233       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
02234       res = -1;
02235       goto exit;
02236    }
02237    
02238    save_body(body, vms, "2", attachedfilefmt, 0);
02239    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
02240       *vms->introfn = '\0';
02241    }
02242 
02243    /* Get info from headers!! */
02244    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
02245 
02246    if (!(text_file_ptr = fopen(text_file, "w"))) {
02247       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
02248    }
02249 
02250    fprintf(text_file_ptr, "%s\n", "[message]");
02251 
02252    if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
02253       fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
02254    }
02255    if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
02256       fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
02257    }
02258    if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
02259       fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
02260    }
02261    if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
02262       fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
02263    }
02264    if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
02265       fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
02266    }
02267    if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
02268       fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
02269    }
02270    if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
02271       fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
02272    }
02273    if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
02274       fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
02275    }
02276    fclose(text_file_ptr);
02277 
02278 exit:
02279    free_user(vmu);
02280    return res;
02281 }
02282 
02283 static int folder_int(const char *folder)
02284 {
02285    /*assume a NULL folder means INBOX*/
02286    if (!folder) {
02287       return 0;
02288    }
02289    if (!strcasecmp(folder, imapfolder)) {
02290       return 0;
02291    } else if (!strcasecmp(folder, "Old")) {
02292       return 1;
02293    } else if (!strcasecmp(folder, "Work")) {
02294       return 2;
02295    } else if (!strcasecmp(folder, "Family")) {
02296       return 3;
02297    } else if (!strcasecmp(folder, "Friends")) {
02298       return 4;
02299    } else if (!strcasecmp(folder, "Cust1")) {
02300       return 5;
02301    } else if (!strcasecmp(folder, "Cust2")) {
02302       return 6;
02303    } else if (!strcasecmp(folder, "Cust3")) {
02304       return 7;
02305    } else if (!strcasecmp(folder, "Cust4")) {
02306       return 8;
02307    } else if (!strcasecmp(folder, "Cust5")) {
02308       return 9;
02309    } else if (!strcasecmp(folder, "Urgent")) {
02310       return 11;
02311    } else { /*assume they meant INBOX if folder is not found otherwise*/
02312       return 0;
02313    }
02314 }
02315 
02316 static int __messagecount(const char *context, const char *mailbox, const char *folder)
02317 {
02318    SEARCHPGM *pgm;
02319    SEARCHHEADER *hdr;
02320 
02321    struct ast_vm_user *vmu, vmus;
02322    struct vm_state *vms_p;
02323    int ret = 0;
02324    int fold = folder_int(folder);
02325    int urgent = 0;
02326    
02327    /* If URGENT, then look at INBOX */
02328    if (fold == 11) {
02329       fold = NEW_FOLDER;
02330       urgent = 1;
02331    }
02332 
02333    if (ast_strlen_zero(mailbox))
02334       return 0;
02335 
02336    /* We have to get the user before we can open the stream! */
02337    vmu = find_user(&vmus, context, mailbox);
02338    if (!vmu) {
02339       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
02340       return -1;
02341    } else {
02342       /* No IMAP account available */
02343       if (vmu->imapuser[0] == '\0') {
02344          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02345          return -1;
02346       }
02347    }
02348 
02349    /* No IMAP account available */
02350    if (vmu->imapuser[0] == '\0') {
02351       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
02352       free_user(vmu);
02353       return -1;
02354    }
02355 
02356    /* check if someone is accessing this box right now... */
02357    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
02358    if (!vms_p) {
02359       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
02360    }
02361    if (vms_p) {
02362       ast_debug(3, "Returning before search - user is logged in\n");
02363       if (fold == 0) { /* INBOX */
02364          return urgent ? vms_p->urgentmessages : vms_p->newmessages;
02365       }
02366       if (fold == 1) { /* Old messages */
02367          return vms_p->oldmessages;
02368       }
02369    }
02370 
02371    /* add one if not there... */
02372    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
02373    if (!vms_p) {
02374       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
02375    }
02376 
02377    if (!vms_p) {
02378       vms_p = create_vm_state_from_user(vmu);
02379    }
02380    ret = init_mailstream(vms_p, fold);
02381    if (!vms_p->mailstream) {
02382       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
02383       return -1;
02384    }
02385    if (ret == 0) {
02386       ast_mutex_lock(&vms_p->lock);
02387       pgm = mail_newsearchpgm ();
02388       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
02389       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
02390       pgm->header = hdr;
02391       if (fold != OLD_FOLDER) {
02392          pgm->unseen = 1;
02393          pgm->seen = 0;
02394       }
02395       /* In the special case where fold is 1 (old messages) we have to do things a bit
02396        * differently. Old messages are stored in the INBOX but are marked as "seen"
02397        */
02398       else {
02399          pgm->unseen = 0;
02400          pgm->seen = 1;
02401       }
02402       /* look for urgent messages */
02403       if (fold == NEW_FOLDER) {
02404          if (urgent) {
02405             pgm->flagged = 1;
02406             pgm->unflagged = 0;
02407          } else {
02408             pgm->flagged = 0;
02409             pgm->unflagged = 1;
02410          }
02411       }
02412       pgm->undeleted = 1;
02413       pgm->deleted = 0;
02414 
02415       vms_p->vmArrayIndex = 0;
02416       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
02417       if (fold == 0 && urgent == 0)
02418          vms_p->newmessages = vms_p->vmArrayIndex;
02419       if (fold == 1)
02420          vms_p->oldmessages = vms_p->vmArrayIndex;
02421       if (fold == 0 && urgent == 1)
02422          vms_p->urgentmessages = vms_p->vmArrayIndex;
02423       /*Freeing the searchpgm also frees the searchhdr*/
02424       mail_free_searchpgm(&pgm);
02425       ast_mutex_unlock(&vms_p->lock);
02426       vms_p->updated = 0;
02427       return vms_p->vmArrayIndex;
02428    } else {
02429       ast_mutex_lock(&vms_p->lock);
02430       mail_ping(vms_p->mailstream);
02431       ast_mutex_unlock(&vms_p->lock);
02432    }
02433    return 0;
02434 }
02435 
02436 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
02437 {
02438    /* Check if mailbox is full */
02439    check_quota(vms, vmu->imapfolder);
02440    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
02441       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
02442       if (chan) {
02443          ast_play_and_wait(chan, "vm-mailboxfull");
02444       }
02445       return -1;
02446    }
02447 
02448    /* Check if we have exceeded maxmsg */
02449    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));
02450    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
02451       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
02452       if (chan) {
02453          ast_play_and_wait(chan, "vm-mailboxfull");
02454          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02455       }
02456       return -1;
02457    }
02458 
02459    return 0;
02460 }
02461 
02462 /*!
02463  * \brief Gets the number of messages that exist in a mailbox folder.
02464  * \param context
02465  * \param mailbox
02466  * \param folder
02467  * 
02468  * This method is used when IMAP backend is used.
02469  * \return The number of messages in this mailbox folder (zero or more).
02470  */
02471 static int messagecount(const char *context, const char *mailbox, const char *folder)
02472 {
02473    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
02474       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
02475    } else {
02476       return __messagecount(context, mailbox, folder);
02477    }
02478 }
02479 
02480 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, const char *msg_id)
02481 {
02482    char *myserveremail = serveremail;
02483    char fn[PATH_MAX];
02484    char introfn[PATH_MAX];
02485    char mailbox[256];
02486    char *stringp;
02487    FILE *p = NULL;
02488    char tmp[80] = "/tmp/astmail-XXXXXX";
02489    long len;
02490    void *buf;
02491    int tempcopy = 0;
02492    STRING str;
02493    int ret; /* for better error checking */
02494    char *imap_flags = NIL;
02495    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
02496    int box = NEW_FOLDER;
02497 
02498    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
02499    if (msgnum < 0) {
02500       if(!imapgreetings) {
02501          return 0;
02502       } else {
02503          box = GREETINGS_FOLDER;
02504       }
02505    }
02506 
02507    if (imap_check_limits(chan, vms, vmu, msgcount)) {
02508       return -1;
02509    }
02510 
02511    /* Set urgent flag for IMAP message */
02512    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
02513       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
02514       imap_flags = "\\FLAGGED";
02515    }
02516 
02517    /* Attach only the first format */
02518    fmt = ast_strdupa(fmt);
02519    stringp = fmt;
02520    strsep(&stringp, "|");
02521 
02522    if (!ast_strlen_zero(vmu->serveremail))
02523       myserveremail = vmu->serveremail;
02524 
02525    if (msgnum > -1)
02526       make_file(fn, sizeof(fn), dir, msgnum);
02527    else
02528       ast_copy_string (fn, dir, sizeof(fn));
02529 
02530    snprintf(introfn, sizeof(introfn), "%sintro", fn);
02531    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
02532       *introfn = '\0';
02533    }
02534 
02535    if (ast_strlen_zero(vmu->email)) {
02536       /* We need the vmu->email to be set when we call make_email_file, but
02537        * if we keep it set, a duplicate e-mail will be created. So at the end
02538        * of this function, we will revert back to an empty string if tempcopy
02539        * is 1.
02540        */
02541       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
02542       tempcopy = 1;
02543    }
02544 
02545    if (!strcmp(fmt, "wav49"))
02546       fmt = "WAV";
02547    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
02548 
02549    /* Make a temporary file instead of piping directly to sendmail, in case the mail
02550       command hangs. */
02551    if (!(p = vm_mkftemp(tmp))) {
02552       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
02553       if (tempcopy)
02554          *(vmu->email) = '\0';
02555       return -1;
02556    }
02557 
02558    if (msgnum < 0 && imapgreetings) {
02559       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
02560          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
02561          return -1;
02562       }
02563       imap_delete_old_greeting(fn, vms);
02564    }
02565 
02566    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
02567       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
02568       S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
02569       fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
02570    /* read mail file to memory */
02571    len = ftell(p);
02572    rewind(p);
02573    if (!(buf = ast_malloc(len + 1))) {
02574       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
02575       fclose(p);
02576       if (tempcopy)
02577          *(vmu->email) = '\0';
02578       return -1;
02579    }
02580    if (fread(buf, len, 1, p) < len) {
02581       if (ferror(p)) {
02582          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
02583          return -1;
02584       }
02585    }
02586    ((char *) buf)[len] = '\0';
02587    INIT(&str, mail_string, buf, len);
02588    ret = init_mailstream(vms, box);
02589    if (ret == 0) {
02590       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
02591       ast_mutex_lock(&vms->lock);
02592       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
02593          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
02594       ast_mutex_unlock(&vms->lock);
02595       fclose(p);
02596       unlink(tmp);
02597       ast_free(buf);
02598    } else {
02599       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
02600       fclose(p);
02601       unlink(tmp);
02602       ast_free(buf);
02603       return -1;
02604    }
02605    ast_debug(3, "%s stored\n", fn);
02606 
02607    if (tempcopy)
02608       *(vmu->email) = '\0';
02609    inprocess_count(vmu->mailbox, vmu->context, -1);
02610    return 0;
02611 
02612 }
02613 
02614 /*!
02615  * \brief Gets the number of messages that exist in the inbox folder.
02616  * \param mailbox_context
02617  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
02618  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
02619  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
02620  * 
02621  * This method is used when IMAP backend is used.
02622  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
02623  *
02624  * \return zero on success, -1 on error.
02625  */
02626 
02627 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
02628 {
02629    char tmp[PATH_MAX] = "";
02630    char *mailboxnc;
02631    char *context;
02632    char *mb;
02633    char *cur;
02634    if (newmsgs)
02635       *newmsgs = 0;
02636    if (oldmsgs)
02637       *oldmsgs = 0;
02638    if (urgentmsgs)
02639       *urgentmsgs = 0;
02640 
02641    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
02642    /* If no mailbox, return immediately */
02643    if (ast_strlen_zero(mailbox_context))
02644       return 0;
02645 
02646    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02647    context = strchr(tmp, '@');
02648    if (strchr(mailbox_context, ',')) {
02649       int tmpnew, tmpold, tmpurgent;
02650       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
02651       mb = tmp;
02652       while ((cur = strsep(&mb, ", "))) {
02653          if (!ast_strlen_zero(cur)) {
02654             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02655                return -1;
02656             else {
02657                if (newmsgs)
02658                   *newmsgs += tmpnew; 
02659                if (oldmsgs)
02660                   *oldmsgs += tmpold;
02661                if (urgentmsgs)
02662                   *urgentmsgs += tmpurgent;
02663             }
02664          }
02665       }
02666       return 0;
02667    }
02668    if (context) {
02669       *context = '\0';
02670       mailboxnc = tmp;
02671       context++;
02672    } else {
02673       context = "default";
02674       mailboxnc = (char *) mailbox_context;
02675    }
02676 
02677    if (newmsgs) {
02678       struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
02679       if (!vmu) {
02680          ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
02681          return -1;
02682       }
02683       if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
02684          free_user(vmu);
02685          return -1;
02686       }
02687       free_user(vmu);
02688    }
02689    if (oldmsgs) {
02690       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02691          return -1;
02692       }
02693    }
02694    if (urgentmsgs) {
02695       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02696          return -1;
02697       }
02698    }
02699    return 0;
02700 }
02701 
02702 /** 
02703  * \brief Determines if the given folder has messages.
02704  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02705  * \param folder the folder to look in
02706  *
02707  * This function is used when the mailbox is stored in an IMAP back end.
02708  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02709  * \return 1 if the folder has one or more messages. zero otherwise.
02710  */
02711 
02712 static int has_voicemail(const char *mailbox, const char *folder)
02713 {
02714    char tmp[256], *tmp2, *box, *context;
02715    ast_copy_string(tmp, mailbox, sizeof(tmp));
02716    tmp2 = tmp;
02717    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02718       while ((box = strsep(&tmp2, ",&"))) {
02719          if (!ast_strlen_zero(box)) {
02720             if (has_voicemail(box, folder)) {
02721                return 1;
02722             }
02723          }
02724       }
02725    }
02726    if ((context = strchr(tmp, '@'))) {
02727       *context++ = '\0';
02728    } else {
02729       context = "default";
02730    }
02731    return __messagecount(context, tmp, folder) ? 1 : 0;
02732 }
02733 
02734 /*!
02735  * \brief Copies a message from one mailbox to another.
02736  * \param chan
02737  * \param vmu
02738  * \param imbox
02739  * \param msgnum
02740  * \param duration
02741  * \param recip
02742  * \param fmt
02743  * \param dir
02744  *
02745  * This works with IMAP storage based mailboxes.
02746  *
02747  * \return zero on success, -1 on error.
02748  */
02749 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, const char *dest_folder)
02750 {
02751    struct vm_state *sendvms = NULL;
02752    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02753    if (msgnum >= recip->maxmsg) {
02754       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02755       return -1;
02756    }
02757    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02758       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02759       return -1;
02760    }
02761    if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
02762       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02763       return -1;
02764    }
02765    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02766    ast_mutex_lock(&sendvms->lock);
02767    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
02768       ast_mutex_unlock(&sendvms->lock);
02769       return 0;
02770    }
02771    ast_mutex_unlock(&sendvms->lock);
02772    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02773    return -1;
02774 }
02775 
02776 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02777 {
02778    char tmp[256], *t = tmp;
02779    size_t left = sizeof(tmp);
02780    
02781    if (box == OLD_FOLDER) {
02782       ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
02783    } else {
02784       ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
02785    }
02786 
02787    if (box == NEW_FOLDER) {
02788       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02789    } else {
02790       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
02791    }
02792 
02793    /* Build up server information */
02794    ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
02795 
02796    /* Add authentication user if present */
02797    if (!ast_strlen_zero(authuser))
02798       ast_build_string(&t, &left, "/authuser=%s", authuser);
02799 
02800    /* Add flags if present */
02801    if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
02802       ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
02803    }
02804 
02805    /* End with username */
02806 #if 1
02807    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02808 #else
02809    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02810 #endif
02811    if (box == NEW_FOLDER || box == OLD_FOLDER)
02812       snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
02813    else if (box == GREETINGS_FOLDER)
02814       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02815    else {   /* Other folders such as Friends, Family, etc... */
02816       if (!ast_strlen_zero(imapparentfolder)) {
02817          /* imapparentfolder would typically be set to INBOX */
02818          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
02819       } else {
02820          snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
02821       }
02822    }
02823 }
02824 
02825 static int init_mailstream(struct vm_state *vms, int box)
02826 {
02827    MAILSTREAM *stream = NIL;
02828    long debug;
02829    char tmp[256];
02830 
02831    if (!vms) {
02832       ast_log(LOG_ERROR, "vm_state is NULL!\n");
02833       return -1;
02834    }
02835    ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
02836    if (vms->mailstream == NIL || !vms->mailstream) {
02837       ast_debug(1, "mailstream not set.\n");
02838    } else {
02839       stream = vms->mailstream;
02840    }
02841    /* debug = T;  user wants protocol telemetry? */
02842    debug = NIL;  /* NO protocol telemetry? */
02843 
02844    if (delimiter == '\0') {      /* did not probe the server yet */
02845       char *cp;
02846 #ifdef USE_SYSTEM_IMAP
02847 #include <imap/linkage.c>
02848 #elif defined(USE_SYSTEM_CCLIENT)
02849 #include <c-client/linkage.c>
02850 #else
02851 #include "linkage.c"
02852 #endif
02853       /* Connect to INBOX first to get folders delimiter */
02854       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02855       ast_mutex_lock(&vms->lock);
02856       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02857       ast_mutex_unlock(&vms->lock);
02858       if (stream == NIL) {
02859          ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02860          return -1;
02861       }
02862       get_mailbox_delimiter(vms, stream);
02863       /* update delimiter in imapfolder */
02864       for (cp = vms->imapfolder; *cp; cp++)
02865          if (*cp == '/')
02866             *cp = delimiter;
02867    }
02868    /* Now connect to the target folder */
02869    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02870    ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
02871    ast_mutex_lock(&vms->lock);
02872    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02873    ast_mutex_unlock(&vms->lock);
02874    if (vms->mailstream == NIL) {
02875       return -1;
02876    } else {
02877       return 0;
02878    }
02879 }
02880 
02881 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02882 {
02883    SEARCHPGM *pgm;
02884    SEARCHHEADER *hdr;
02885    int urgent = 0;
02886 
02887    /* If Urgent, then look at INBOX */
02888    if (box == 11) {
02889       box = NEW_FOLDER;
02890       urgent = 1;
02891    }
02892 
02893    ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
02894    ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
02895    ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
02896    ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
02897    ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
02898    vms->imapversion = vmu->imapversion;
02899    ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
02900 
02901    if (init_mailstream(vms, box) || !vms->mailstream) {
02902       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02903       return -1;
02904    }
02905 
02906    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02907 
02908    /* Check Quota */
02909    if  (box == 0)  {
02910       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
02911       check_quota(vms, (char *) mbox(vmu, box));
02912    }
02913 
02914    ast_mutex_lock(&vms->lock);
02915    pgm = mail_newsearchpgm();
02916 
02917    /* Check IMAP folder for Asterisk messages only... */
02918    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02919    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02920    pgm->header = hdr;
02921    pgm->deleted = 0;
02922    pgm->undeleted = 1;
02923 
02924    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02925    if (box == NEW_FOLDER && urgent == 1) {
02926       pgm->unseen = 1;
02927       pgm->seen = 0;
02928       pgm->flagged = 1;
02929       pgm->unflagged = 0;
02930    } else if (box == NEW_FOLDER && urgent == 0) {
02931       pgm->unseen = 1;
02932       pgm->seen = 0;
02933       pgm->flagged = 0;
02934       pgm->unflagged = 1;
02935    } else if (box == OLD_FOLDER) {
02936       pgm->seen = 1;
02937       pgm->unseen = 0;
02938    }
02939 
02940    ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
02941 
02942    vms->vmArrayIndex = 0;
02943    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02944    vms->lastmsg = vms->vmArrayIndex - 1;
02945    mail_free_searchpgm(&pgm);
02946    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
02947     * ensure to allocate enough space to account for all of them. Warn if old messages
02948     * have not been checked first as that is required.
02949     */
02950    if (box == 0 && !vms->dh_arraysize) {
02951       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
02952    }
02953    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
02954       ast_mutex_unlock(&vms->lock);
02955       return -1;
02956    }
02957 
02958    ast_mutex_unlock(&vms->lock);
02959    return 0;
02960 }
02961 
02962 static void write_file(char *filename, char *buffer, unsigned long len)
02963 {
02964    FILE *output;
02965 
02966    output = fopen (filename, "w");
02967    if (fwrite(buffer, len, 1, output) != 1) {
02968       if (ferror(output)) {
02969          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02970       }
02971    }
02972    fclose (output);
02973 }
02974 
02975 static void update_messages_by_imapuser(const char *user, unsigned long number)
02976 {
02977    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02978 
02979    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02980       return;
02981    }
02982 
02983    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02984    vms->msgArray[vms->vmArrayIndex++] = number;
02985 }
02986 
02987 void mm_searched(MAILSTREAM *stream, unsigned long number)
02988 {
02989    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02990 
02991    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02992       return;
02993 
02994    update_messages_by_imapuser(user, number);
02995 }
02996 
02997 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02998 {
02999    struct ast_variable *var;
03000    struct ast_vm_user *vmu;
03001 
03002    vmu = ast_calloc(1, sizeof *vmu);
03003    if (!vmu)
03004       return NULL;
03005 
03006    populate_defaults(vmu);
03007    ast_set_flag(vmu, VM_ALLOCED);
03008 
03009    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
03010    if (var) {
03011       apply_options_full(vmu, var);
03012       ast_variables_destroy(var);
03013       return vmu;
03014    } else {
03015       ast_free(vmu);
03016       return NULL;
03017    }
03018 }
03019 
03020 /* Interfaces to C-client */
03021 
03022 void mm_exists(MAILSTREAM * stream, unsigned long number)
03023 {
03024    /* mail_ping will callback here if new mail! */
03025    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
03026    if (number == 0) return;
03027    set_update(stream);
03028 }
03029 
03030 
03031 void mm_expunged(MAILSTREAM * stream, unsigned long number)
03032 {
03033    /* mail_ping will callback here if expunged mail! */
03034    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
03035    if (number == 0) return;
03036    set_update(stream);
03037 }
03038 
03039 
03040 void mm_flags(MAILSTREAM * stream, unsigned long number)
03041 {
03042    /* mail_ping will callback here if read mail! */
03043    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
03044    if (number == 0) return;
03045    set_update(stream);
03046 }
03047 
03048 
03049 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
03050 {
03051    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
03052    mm_log (string, errflg);
03053 }
03054 
03055 
03056 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
03057 {
03058    if (delimiter == '\0') {
03059       delimiter = delim;
03060    }
03061 
03062    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
03063    if (attributes & LATT_NOINFERIORS)
03064       ast_debug(5, "no inferiors\n");
03065    if (attributes & LATT_NOSELECT)
03066       ast_debug(5, "no select\n");
03067    if (attributes & LATT_MARKED)
03068       ast_debug(5, "marked\n");
03069    if (attributes & LATT_UNMARKED)
03070       ast_debug(5, "unmarked\n");
03071 }
03072 
03073 
03074 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
03075 {
03076    ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
03077    if (attributes & LATT_NOINFERIORS)
03078       ast_debug(5, "no inferiors\n");
03079    if (attributes & LATT_NOSELECT)
03080       ast_debug(5, "no select\n");
03081    if (attributes & LATT_MARKED)
03082       ast_debug(5, "marked\n");
03083    if (attributes & LATT_UNMARKED)
03084       ast_debug(5, "unmarked\n");
03085 }
03086 
03087 
03088 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
03089 {
03090    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
03091    if (status->flags & SA_MESSAGES)
03092       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
03093    if (status->flags & SA_RECENT)
03094       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
03095    if (status->flags & SA_UNSEEN)
03096       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
03097    if (status->flags & SA_UIDVALIDITY)
03098       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
03099    if (status->flags & SA_UIDNEXT)
03100       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
03101    ast_log(AST_LOG_NOTICE, "\n");
03102 }
03103 
03104 
03105 void mm_log(char *string, long errflg)
03106 {
03107    switch ((short) errflg) {
03108       case NIL:
03109          ast_debug(1, "IMAP Info: %s\n", string);
03110          break;
03111       case PARSE:
03112       case WARN:
03113          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
03114          break;
03115       case ERROR:
03116          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
03117          break;
03118    }
03119 }
03120 
03121 
03122 void mm_dlog(char *string)
03123 {
03124    ast_log(AST_LOG_NOTICE, "%s\n", string);
03125 }
03126 
03127 
03128 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
03129 {
03130    struct ast_vm_user *vmu;
03131 
03132    ast_debug(4, "Entering callback mm_login\n");
03133 
03134    ast_copy_string(user, mb->user, MAILTMPLEN);
03135 
03136    /* We should only do this when necessary */
03137    if (!ast_strlen_zero(authpassword)) {
03138       ast_copy_string(pwd, authpassword, MAILTMPLEN);
03139    } else {
03140       AST_LIST_TRAVERSE(&users, vmu, list) {
03141          if (!strcasecmp(mb->user, vmu->imapuser)) {
03142             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
03143             break;
03144          }
03145       }
03146       if (!vmu) {
03147          if ((vmu = find_user_realtime_imapuser(mb->user))) {
03148             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
03149             free_user(vmu);
03150          }
03151       }
03152    }
03153 }
03154 
03155 
03156 void mm_critical(MAILSTREAM * stream)
03157 {
03158 }
03159 
03160 
03161 void mm_nocritical(MAILSTREAM * stream)
03162 {
03163 }
03164 
03165 
03166 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
03167 {
03168    kill (getpid (), SIGSTOP);
03169    return NIL;
03170 }
03171 
03172 
03173 void mm_fatal(char *string)
03174 {
03175    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
03176 }
03177 
03178 /* C-client callback to handle quota */
03179 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
03180 {
03181    struct vm_state *vms;
03182    char *mailbox = stream->mailbox, *user;
03183    char buf[1024] = "";
03184    unsigned long usage = 0, limit = 0;
03185 
03186    while (pquota) {
03187       usage = pquota->usage;
03188       limit = pquota->limit;
03189       pquota = pquota->next;
03190    }
03191 
03192    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)))) {
03193       ast_log(AST_LOG_ERROR, "No state found.\n");
03194       return;
03195    }
03196 
03197    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
03198 
03199    vms->quota_usage = usage;
03200    vms->quota_limit = limit;
03201 }
03202 
03203 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
03204 {
03205    char *start, *eol_pnt;
03206    int taglen;
03207 
03208    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
03209       return NULL;
03210 
03211    taglen = strlen(tag) + 1;
03212    if (taglen < 1)
03213       return NULL;
03214 
03215    if (!(start = strstr(header, tag)))
03216       return NULL;
03217 
03218    /* Since we can be called multiple times we should clear our buffer */
03219    memset(buf, 0, len);
03220 
03221    ast_copy_string(buf, start+taglen, len);
03222    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
03223       *eol_pnt = '\0';
03224    return buf;
03225 }
03226 
03227 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
03228 {
03229    char *start, *eol_pnt, *quote;
03230 
03231    if (ast_strlen_zero(mailbox))
03232       return NULL;
03233 
03234    if (!(start = strstr(mailbox, "/user=")))
03235       return NULL;
03236 
03237    ast_copy_string(buf, start+6, len);
03238 
03239    if (!(quote = strchr(buf, '"'))) {
03240       if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
03241          *eol_pnt = '\0';
03242       }
03243       return buf;
03244    } else {
03245       if ((eol_pnt = strchr(quote + 1, '"'))) {
03246          *eol_pnt = '\0';
03247       }
03248       return quote + 1;
03249    }
03250 }
03251 
03252 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
03253 {
03254    struct vm_state *vms_p;
03255 
03256    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03257    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
03258       return vms_p;
03259    }
03260    ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
03261    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
03262       return NULL;
03263    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
03264    ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
03265    ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
03266    ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
03267    ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
03268    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
03269    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
03270    vms_p->mailstream = NIL; /* save for access from interactive entry point */
03271    vms_p->imapversion = vmu->imapversion;
03272    ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
03273    vms_p->updated = 1;
03274    /* set mailbox to INBOX! */
03275    ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
03276    init_vm_state(vms_p);
03277    vmstate_insert(vms_p);
03278    return vms_p;
03279 }
03280 
03281 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
03282 {
03283    struct vmstate *vlist = NULL;
03284 
03285    if (interactive) {
03286       struct vm_state *vms;
03287       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03288       vms = pthread_getspecific(ts_vmstate.key);
03289       return vms;
03290    }
03291 
03292    AST_LIST_LOCK(&vmstates);
03293    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03294       if (!vlist->vms) {
03295          ast_debug(3, "error: vms is NULL for %s\n", user);
03296          continue;
03297       }
03298       if (vlist->vms->imapversion != imapversion) {
03299          continue;
03300       }
03301       if (!vlist->vms->imapuser) {
03302          ast_debug(3, "error: imapuser is NULL for %s\n", user);
03303          continue;
03304       }
03305 
03306       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
03307          AST_LIST_UNLOCK(&vmstates);
03308          return vlist->vms;
03309       }
03310    }
03311    AST_LIST_UNLOCK(&vmstates);
03312 
03313    ast_debug(3, "%s not found in vmstates\n", user);
03314 
03315    return NULL;
03316 }
03317 
03318 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
03319 {
03320 
03321    struct vmstate *vlist = NULL;
03322    const char *local_context = S_OR(context, "default");
03323 
03324    if (interactive) {
03325       struct vm_state *vms;
03326       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
03327       vms = pthread_getspecific(ts_vmstate.key);
03328       return vms;
03329    }
03330 
03331    AST_LIST_LOCK(&vmstates);
03332    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
03333       if (!vlist->vms) {
03334          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
03335          continue;
03336       }
03337       if (vlist->vms->imapversion != imapversion) {
03338          continue;
03339       }
03340       if (!vlist->vms->username || !vlist->vms->context) {
03341          ast_debug(3, "error: username is NULL for %s\n", mailbox);
03342          continue;
03343       }
03344 
03345       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);
03346 
03347       if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
03348          ast_debug(3, "Found it!\n");
03349          AST_LIST_UNLOCK(&vmstates);
03350          return vlist->vms;
03351       }
03352    }
03353    AST_LIST_UNLOCK(&vmstates);
03354 
03355    ast_debug(3, "%s not found in vmstates\n", mailbox);
03356 
03357    return NULL;
03358 }
03359 
03360 static void vmstate_insert(struct vm_state *vms)
03361 {
03362    struct vmstate *v;
03363    struct vm_state *altvms;
03364 
03365    /* If interactive, it probably already exists, and we should
03366       use the one we already have since it is more up to date.
03367       We can compare the username to find the duplicate */
03368    if (vms->interactive == 1) {
03369       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
03370       if (altvms) {
03371          ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03372          vms->newmessages = altvms->newmessages;
03373          vms->oldmessages = altvms->oldmessages;
03374          vms->vmArrayIndex = altvms->vmArrayIndex;
03375          vms->lastmsg = altvms->lastmsg;
03376          vms->curmsg = altvms->curmsg;
03377          /* get a pointer to the persistent store */
03378          vms->persist_vms = altvms;
03379          /* Reuse the mailstream? */
03380 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
03381          vms->mailstream = altvms->mailstream;
03382 #else
03383          vms->mailstream = NIL;
03384 #endif
03385       }
03386       return;
03387    }
03388 
03389    if (!(v = ast_calloc(1, sizeof(*v))))
03390       return;
03391 
03392    v->vms = vms;
03393 
03394    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03395 
03396    AST_LIST_LOCK(&vmstates);
03397    AST_LIST_INSERT_TAIL(&vmstates, v, list);
03398    AST_LIST_UNLOCK(&vmstates);
03399 }
03400 
03401 static void vmstate_delete(struct vm_state *vms)
03402 {
03403    struct vmstate *vc = NULL;
03404    struct vm_state *altvms = NULL;
03405 
03406    /* If interactive, we should copy pertinent info
03407       back to the persistent state (to make update immediate) */
03408    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
03409       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
03410       altvms->newmessages = vms->newmessages;
03411       altvms->oldmessages = vms->oldmessages;
03412       altvms->updated = 1;
03413       vms->mailstream = mail_close(vms->mailstream);
03414 
03415       /* Interactive states are not stored within the persistent list */
03416       return;
03417    }
03418 
03419    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03420 
03421    AST_LIST_LOCK(&vmstates);
03422    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
03423       if (vc->vms == vms) {
03424          AST_LIST_REMOVE_CURRENT(list);
03425          break;
03426       }
03427    }
03428    AST_LIST_TRAVERSE_SAFE_END
03429    AST_LIST_UNLOCK(&vmstates);
03430    
03431    if (vc) {
03432       ast_mutex_destroy(&vc->vms->lock);
03433       ast_free(vc);
03434    }
03435    else
03436       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
03437 }
03438 
03439 static void set_update(MAILSTREAM * stream)
03440 {
03441    struct vm_state *vms;
03442    char *mailbox = stream->mailbox, *user;
03443    char buf[1024] = "";
03444 
03445    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
03446       if (user && option_debug > 2)
03447          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
03448       return;
03449    }
03450 
03451    ast_debug(3, "User %s mailbox set for update.\n", user);
03452 
03453    vms->updated = 1; /* Set updated flag since mailbox changed */
03454 }
03455 
03456 static void init_vm_state(struct vm_state *vms)
03457 {
03458    int x;
03459    vms->vmArrayIndex = 0;
03460    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
03461       vms->msgArray[x] = 0;
03462    }
03463    ast_mutex_init(&vms->lock);
03464 }
03465 
03466 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
03467 {
03468    char *body_content;
03469    char *body_decoded;
03470    char *fn = is_intro ? vms->introfn : vms->fn;
03471    unsigned long len;
03472    unsigned long newlen;
03473    char filename[256];
03474 
03475    if (!body || body == NIL)
03476       return -1;
03477 
03478    ast_mutex_lock(&vms->lock);
03479    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
03480    ast_mutex_unlock(&vms->lock);
03481    if (body_content != NIL) {
03482       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
03483       /* ast_debug(1, body_content); */
03484       body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
03485       /* If the body of the file is empty, return an error */
03486       if (!newlen) {
03487          return -1;
03488       }
03489       write_file(filename, (char *) body_decoded, newlen);
03490    } else {
03491       ast_debug(5, "Body of message is NULL.\n");
03492       return -1;
03493    }
03494    return 0;
03495 }
03496 
03497 /*! 
03498  * \brief Get delimiter via mm_list callback 
03499  * \param vms     The voicemail state object
03500  * \param stream
03501  *
03502  * Determines the delimiter character that is used by the underlying IMAP based mail store.
03503  */
03504 /* MUTEX should already be held */
03505 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
03506    char tmp[50];
03507    snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
03508    mail_list(stream, tmp, "*");
03509 }
03510 
03511 /*! 
03512  * \brief Check Quota for user 
03513  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
03514  * \param mailbox the mailbox to check the quota for.
03515  *
03516  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
03517  */
03518 static void check_quota(struct vm_state *vms, char *mailbox) {
03519    ast_mutex_lock(&vms->lock);
03520    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
03521    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
03522    if (vms && vms->mailstream != NULL) {
03523       imap_getquotaroot(vms->mailstream, mailbox);
03524    } else {
03525       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
03526    }
03527    ast_mutex_unlock(&vms->lock);
03528 }
03529 
03530 #endif /* IMAP_STORAGE */
03531 
03532 /*! \brief Lock file path
03533  * only return failure if ast_lock_path returns 'timeout',
03534  * not if the path does not exist or any other reason
03535  */
03536 static int vm_lock_path(const char *path)
03537 {
03538    switch (ast_lock_path(path)) {
03539    case AST_LOCK_TIMEOUT:
03540       return -1;
03541    default:
03542       return 0;
03543    }
03544 }
03545 
03546 #define MSG_ID_LEN 256
03547 
03548 /* Used to attach a unique identifier to an msg_id */
03549 static int msg_id_incrementor;
03550 
03551 /*!
03552  * \brief Sets the destination string to a uniquely identifying msg_id string
03553  * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
03554  */
03555 static void generate_msg_id(char *dst);
03556 
03557 #ifdef ODBC_STORAGE
03558 struct generic_prepare_struct {
03559    char *sql;
03560    int argc;
03561    char **argv;
03562 };
03563 
03564 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
03565 {
03566    struct generic_prepare_struct *gps = data;
03567    int res, i;
03568    SQLHSTMT stmt;
03569 
03570    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03571    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03572       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03573       return NULL;
03574    }
03575    res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
03576    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03577       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
03578       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03579       return NULL;
03580    }
03581    for (i = 0; i < gps->argc; i++)
03582       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
03583 
03584    return stmt;
03585 }
03586 
03587 static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
03588 {
03589    SQLHSTMT stmt;
03590    char sql[PATH_MAX];
03591    struct odbc_obj *obj;
03592    char msg_num_str[20];
03593    char *argv[] = { msg_id, dir, msg_num_str };
03594    struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
03595 
03596    obj = ast_odbc_request_obj(odbc_database, 0);
03597    if (!obj) {
03598       ast_log(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
03599       return;
03600    }
03601 
03602    snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
03603    snprintf(sql, sizeof(sql), "UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?", odbc_table);
03604    stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03605    if (!stmt) {
03606       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03607    } else {
03608       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03609    }
03610    ast_odbc_release_obj(obj);
03611    return;
03612 }
03613 
03614 /*!
03615  * \brief Retrieves a file from an ODBC data store.
03616  * \param dir the path to the file to be retreived.
03617  * \param msgnum the message number, such as within a mailbox folder.
03618  * 
03619  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
03620  * 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.
03621  *
03622  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
03623  * The output is the message information file with the name msgnum and the extension .txt
03624  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
03625  * 
03626  * \return 0 on success, -1 on error.
03627  */
03628 static int retrieve_file(char *dir, int msgnum)
03629 {
03630    int x = 0;
03631    int res;
03632    int fd = -1;
03633    size_t fdlen = 0;
03634    void *fdm = MAP_FAILED;
03635    SQLSMALLINT colcount = 0;
03636    SQLHSTMT stmt;
03637    char sql[PATH_MAX];
03638    char fmt[80]="";
03639    char *c;
03640    char coltitle[256];
03641    SQLSMALLINT collen;
03642    SQLSMALLINT datatype;
03643    SQLSMALLINT decimaldigits;
03644    SQLSMALLINT nullable;
03645    SQLULEN colsize;
03646    SQLLEN colsize2;
03647    FILE *f = NULL;
03648    char rowdata[80];
03649    char fn[PATH_MAX];
03650    char full_fn[PATH_MAX];
03651    char msgnums[80];
03652    char *argv[] = { dir, msgnums };
03653    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03654 
03655    struct odbc_obj *obj;
03656    obj = ast_odbc_request_obj(odbc_database, 0);
03657    if (obj) {
03658       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03659       c = strchr(fmt, '|');
03660       if (c)
03661          *c = '\0';
03662       if (!strcasecmp(fmt, "wav49"))
03663          strcpy(fmt, "WAV");
03664       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03665       if (msgnum > -1)
03666          make_file(fn, sizeof(fn), dir, msgnum);
03667       else
03668          ast_copy_string(fn, dir, sizeof(fn));
03669 
03670       /* Create the information file */
03671       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03672       
03673       if (!(f = fopen(full_fn, "w+"))) {
03674          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
03675          goto yuck;
03676       }
03677       
03678       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03679       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03680       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03681       if (!stmt) {
03682          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03683          ast_odbc_release_obj(obj);
03684          goto yuck;
03685       }
03686       res = SQLFetch(stmt);
03687       if (res == SQL_NO_DATA) {
03688          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03689          ast_odbc_release_obj(obj);
03690          goto yuck;
03691       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03692          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03693          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03694          ast_odbc_release_obj(obj);
03695          goto yuck;
03696       }
03697       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
03698       if (fd < 0) {
03699          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
03700          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03701          ast_odbc_release_obj(obj);
03702          goto yuck;
03703       }
03704       res = SQLNumResultCols(stmt, &colcount);
03705       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
03706          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
03707          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03708          ast_odbc_release_obj(obj);
03709          goto yuck;
03710       }
03711       if (f) 
03712          fprintf(f, "[message]\n");
03713       for (x = 0; x < colcount; x++) {
03714          rowdata[0] = '\0';
03715          colsize = 0;
03716          collen = sizeof(coltitle);
03717          res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
03718                   &datatype, &colsize, &decimaldigits, &nullable);
03719          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03720             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
03721             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03722             ast_odbc_release_obj(obj);
03723             goto yuck;
03724          }
03725          if (!strcasecmp(coltitle, "recording")) {
03726             off_t offset;
03727             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
03728             fdlen = colsize2;
03729             if (fd > -1) {
03730                char tmp[1]="";
03731                lseek(fd, fdlen - 1, SEEK_SET);
03732                if (write(fd, tmp, 1) != 1) {
03733                   close(fd);
03734                   fd = -1;
03735                   continue;
03736                }
03737                /* Read out in small chunks */
03738                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03739                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03740                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03741                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03742                      ast_odbc_release_obj(obj);
03743                      goto yuck;
03744                   } else {
03745                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03746                      munmap(fdm, CHUNKSIZE);
03747                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03748                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03749                         unlink(full_fn);
03750                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03751                         ast_odbc_release_obj(obj);
03752                         goto yuck;
03753                      }
03754                   }
03755                }
03756                if (truncate(full_fn, fdlen) < 0) {
03757                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03758                }
03759             }
03760          } else {
03761             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03762             if ((res == SQL_NULL_DATA) && (!strcasecmp(coltitle, "msg_id"))) {
03763                char msg_id[MSG_ID_LEN];
03764                generate_msg_id(msg_id);
03765                snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
03766                odbc_update_msg_id(dir, msgnum, msg_id);
03767             } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03768                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
03769                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03770                ast_odbc_release_obj(obj);
03771                goto yuck;
03772             }
03773             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03774                fprintf(f, "%s=%s\n", coltitle, rowdata);
03775          }
03776       }
03777       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03778       ast_odbc_release_obj(obj);
03779    } else
03780       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03781 yuck:
03782    if (f)
03783       fclose(f);
03784    if (fd > -1)
03785       close(fd);
03786    return x - 1;
03787 }
03788 
03789 /*!
03790  * \brief Determines the highest message number in use for a given user and mailbox folder.
03791  * \param vmu 
03792  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03793  *
03794  * This method is used when mailboxes are stored in an ODBC back end.
03795  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03796  *
03797  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
03798 
03799  */
03800 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03801 {
03802    int x = 0;
03803    int res;
03804    SQLHSTMT stmt;
03805    char sql[PATH_MAX];
03806    char rowdata[20];
03807    char *argv[] = { dir };
03808    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03809 
03810    struct odbc_obj *obj;
03811    obj = ast_odbc_request_obj(odbc_database, 0);
03812    if (obj) {
03813       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
03814 
03815       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03816       if (!stmt) {
03817          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03818          ast_odbc_release_obj(obj);
03819          goto yuck;
03820       }
03821       res = SQLFetch(stmt);
03822       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03823          if (res == SQL_NO_DATA) {
03824             ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
03825          } else {
03826             ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03827          }
03828 
03829          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03830          ast_odbc_release_obj(obj);
03831          goto yuck;
03832       }
03833       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03834       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03835          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03836          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03837          ast_odbc_release_obj(obj);
03838          goto yuck;
03839       }
03840       if (sscanf(rowdata, "%30d", &x) != 1)
03841          ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
03842       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03843       ast_odbc_release_obj(obj);
03844       return x;
03845    } else
03846       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03847 yuck:
03848    return x - 1;
03849 }
03850 
03851 /*!
03852  * \brief Determines if the specified message exists.
03853  * \param dir the folder the mailbox folder to look for messages. 
03854  * \param msgnum the message index to query for.
03855  *
03856  * This method is used when mailboxes are stored in an ODBC back end.
03857  *
03858  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03859  */
03860 static int message_exists(char *dir, int msgnum)
03861 {
03862    int x = 0;
03863    int res;
03864    SQLHSTMT stmt;
03865    char sql[PATH_MAX];
03866    char rowdata[20];
03867    char msgnums[20];
03868    char *argv[] = { dir, msgnums };
03869    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03870 
03871    struct odbc_obj *obj;
03872    obj = ast_odbc_request_obj(odbc_database, 0);
03873    if (obj) {
03874       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03875       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03876       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03877       if (!stmt) {
03878          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03879          ast_odbc_release_obj(obj);
03880          goto yuck;
03881       }
03882       res = SQLFetch(stmt);
03883       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03884          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03885          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03886          ast_odbc_release_obj(obj);
03887          goto yuck;
03888       }
03889       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03890       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03891          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03892          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03893          ast_odbc_release_obj(obj);
03894          goto yuck;
03895       }
03896       if (sscanf(rowdata, "%30d", &x) != 1)
03897          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03898       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03899       ast_odbc_release_obj(obj);
03900    } else
03901       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03902 yuck:
03903    return x;
03904 }
03905 
03906 /*!
03907  * \brief returns the number of messages found.
03908  * \param vmu
03909  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03910  *
03911  * This method is used when mailboxes are stored in an ODBC back end.
03912  *
03913  * \return The count of messages being zero or more, less than zero on error.
03914  */
03915 static int count_messages(struct ast_vm_user *vmu, char *dir)
03916 {
03917    int x = 0;
03918    int res;
03919    SQLHSTMT stmt;
03920    char sql[PATH_MAX];
03921    char rowdata[20];
03922    char *argv[] = { dir };
03923    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03924 
03925    struct odbc_obj *obj;
03926    obj = ast_odbc_request_obj(odbc_database, 0);
03927    if (obj) {
03928       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
03929       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03930       if (!stmt) {
03931          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03932          ast_odbc_release_obj(obj);
03933          goto yuck;
03934       }
03935       res = SQLFetch(stmt);
03936       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03937          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03938          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03939          ast_odbc_release_obj(obj);
03940          goto yuck;
03941       }
03942       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03943       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03944          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03945          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03946          ast_odbc_release_obj(obj);
03947          goto yuck;
03948       }
03949       if (sscanf(rowdata, "%30d", &x) != 1)
03950          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03951       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03952       ast_odbc_release_obj(obj);
03953       return x;
03954    } else
03955       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03956 yuck:
03957    return x - 1;
03958 
03959 }
03960 
03961 /*!
03962  * \brief Deletes a message from the mailbox folder.
03963  * \param sdir The mailbox folder to work in.
03964  * \param smsg The message index to be deleted.
03965  *
03966  * This method is used when mailboxes are stored in an ODBC back end.
03967  * The specified message is directly deleted from the database 'voicemessages' table.
03968  * 
03969  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03970  */
03971 static void delete_file(const char *sdir, int smsg)
03972 {
03973    SQLHSTMT stmt;
03974    char sql[PATH_MAX];
03975    char msgnums[20];
03976    char *argv[] = { NULL, msgnums };
03977    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03978    struct odbc_obj *obj;
03979 
03980    argv[0] = ast_strdupa(sdir);
03981 
03982    obj = ast_odbc_request_obj(odbc_database, 0);
03983    if (obj) {
03984       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03985       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
03986       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03987       if (!stmt)
03988          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03989       else
03990          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03991       ast_odbc_release_obj(obj);
03992    } else
03993       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03994    return;  
03995 }
03996 
03997 /*!
03998  * \brief Copies a voicemail from one mailbox to another.
03999  * \param sdir the folder for which to look for the message to be copied.
04000  * \param smsg the index of the message to be copied.
04001  * \param ddir the destination folder to copy the message into.
04002  * \param dmsg the index to be used for the copied message.
04003  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
04004  * \param dmailboxcontext The context for the destination user.
04005  *
04006  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
04007  */
04008 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
04009 {
04010    SQLHSTMT stmt;
04011    char sql[512];
04012    char msgnums[20];
04013    char msgnumd[20];
04014    char msg_id[MSG_ID_LEN];
04015    struct odbc_obj *obj;
04016    char *argv[] = { ddir, msgnumd, msg_id, dmailboxuser, dmailboxcontext, sdir, msgnums };
04017    struct generic_prepare_struct gps = { .sql = sql, .argc = 7, .argv = argv };
04018 
04019    generate_msg_id(msg_id);
04020    delete_file(ddir, dmsg);
04021    obj = ast_odbc_request_obj(odbc_database, 0);
04022    if (obj) {
04023       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
04024       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
04025       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, 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);
04026       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04027       if (!stmt)
04028          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
04029       else
04030          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04031       ast_odbc_release_obj(obj);
04032    } else
04033       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04034    return;  
04035 }
04036 
04037 struct insert_data {
04038    char *sql;
04039    const char *dir;
04040    const char *msgnums;
04041    void *data;
04042    SQLLEN datalen;
04043    SQLLEN indlen;
04044    const char *context;
04045    const char *macrocontext;
04046    const char *callerid;
04047    const char *origtime;
04048    const char *duration;
04049    const char *mailboxuser;
04050    const char *mailboxcontext;
04051    const char *category;
04052    const char *flag;
04053    const char *msg_id;
04054 };
04055 
04056 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
04057 {
04058    struct insert_data *data = vdata;
04059    int res;
04060    SQLHSTMT stmt;
04061 
04062    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
04063    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04064       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
04065       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04066       return NULL;
04067    }
04068 
04069    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
04070    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
04071    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
04072    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
04073    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
04074    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
04075    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
04076    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
04077    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
04078    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
04079    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
04080    SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
04081    if (!ast_strlen_zero(data->category)) {
04082       SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
04083    }
04084    res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
04085    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04086       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
04087       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04088       return NULL;
04089    }
04090 
04091    return stmt;
04092 }
04093 
04094 /*!
04095  * \brief Stores a voicemail into the database.
04096  * \param dir the folder the mailbox folder to store the message.
04097  * \param mailboxuser the user owning the mailbox folder.
04098  * \param mailboxcontext
04099  * \param msgnum the message index for the message to be stored.
04100  *
04101  * This method is used when mailboxes are stored in an ODBC back end.
04102  * The message sound file and information file is looked up on the file system. 
04103  * A SQL query is invoked to store the message into the (MySQL) database.
04104  *
04105  * \return the zero on success -1 on error.
04106  */
04107 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
04108 {
04109    int res = 0;
04110    int fd = -1;
04111    void *fdm = MAP_FAILED;
04112    off_t fdlen = -1;
04113    SQLHSTMT stmt;
04114    char sql[PATH_MAX];
04115    char msgnums[20];
04116    char fn[PATH_MAX];
04117    char full_fn[PATH_MAX];
04118    char fmt[80]="";
04119    char *c;
04120    struct ast_config *cfg = NULL;
04121    struct odbc_obj *obj;
04122    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
04123       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
04124    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04125 
04126    delete_file(dir, msgnum);
04127    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
04128       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04129       return -1;
04130    }
04131 
04132    do {
04133       ast_copy_string(fmt, vmfmts, sizeof(fmt));
04134       c = strchr(fmt, '|');
04135       if (c)
04136          *c = '\0';
04137       if (!strcasecmp(fmt, "wav49"))
04138          strcpy(fmt, "WAV");
04139       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
04140       if (msgnum > -1)
04141          make_file(fn, sizeof(fn), dir, msgnum);
04142       else
04143          ast_copy_string(fn, dir, sizeof(fn));
04144       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
04145       cfg = ast_config_load(full_fn, config_flags);
04146       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
04147       fd = open(full_fn, O_RDWR);
04148       if (fd < 0) {
04149          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
04150          res = -1;
04151          break;
04152       }
04153       if (valid_config(cfg)) {
04154          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
04155             idata.context = "";
04156          }
04157          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
04158             idata.macrocontext = "";
04159          }
04160          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
04161             idata.callerid = "";
04162          }
04163          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
04164             idata.origtime = "";
04165          }
04166          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
04167             idata.duration = "";
04168          }
04169          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
04170             idata.category = "";
04171          }
04172          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
04173             idata.flag = "";
04174          }
04175          if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
04176             idata.msg_id = "";
04177          }
04178       }
04179       fdlen = lseek(fd, 0, SEEK_END);
04180       if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
04181          ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
04182          res = -1;
04183          break;
04184       }
04185       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
04186       if (fdm == MAP_FAILED) {
04187          ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
04188          res = -1;
04189          break;
04190       } 
04191       idata.data = fdm;
04192       idata.datalen = idata.indlen = fdlen;
04193 
04194       if (!ast_strlen_zero(idata.category)) 
04195          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
04196       else
04197          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
04198 
04199       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
04200          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04201       } else {
04202          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04203          res = -1;
04204       }
04205    } while (0);
04206    if (obj) {
04207       ast_odbc_release_obj(obj);
04208    }
04209    if (valid_config(cfg))
04210       ast_config_destroy(cfg);
04211    if (fdm != MAP_FAILED)
04212       munmap(fdm, fdlen);
04213    if (fd > -1)
04214       close(fd);
04215    return res;
04216 }
04217 
04218 /*!
04219  * \brief Renames a message in a mailbox folder.
04220  * \param sdir The folder of the message to be renamed.
04221  * \param smsg The index of the message to be renamed.
04222  * \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.
04223  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
04224  * \param ddir The destination folder for the message to be renamed into
04225  * \param dmsg The destination message for the message to be renamed.
04226  *
04227  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
04228  * 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.
04229  * 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.
04230  */
04231 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
04232 {
04233    SQLHSTMT stmt;
04234    char sql[PATH_MAX];
04235    char msgnums[20];
04236    char msgnumd[20];
04237    struct odbc_obj *obj;
04238    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
04239    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
04240 
04241    delete_file(ddir, dmsg);
04242    obj = ast_odbc_request_obj(odbc_database, 0);
04243    if (obj) {
04244       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
04245       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
04246       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
04247       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04248       if (!stmt)
04249          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04250       else
04251          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04252       ast_odbc_release_obj(obj);
04253    } else
04254       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04255    return;  
04256 }
04257 
04258 /*!
04259  * \brief Removes a voicemail message file.
04260  * \param dir the path to the message file.
04261  * \param msgnum the unique number for the message within the mailbox.
04262  *
04263  * Removes the message content file and the information file.
04264  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
04265  * Typical use is to clean up after a RETRIEVE operation. 
04266  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
04267  * \return zero on success, -1 on error.
04268  */
04269 static int remove_file(char *dir, int msgnum)
04270 {
04271    char fn[PATH_MAX];
04272    char full_fn[PATH_MAX];
04273    char msgnums[80];
04274    
04275    if (msgnum > -1) {
04276       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
04277       make_file(fn, sizeof(fn), dir, msgnum);
04278    } else
04279       ast_copy_string(fn, dir, sizeof(fn));
04280    ast_filedelete(fn, NULL);  
04281    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
04282    unlink(full_fn);
04283    return 0;
04284 }
04285 #else
04286 #ifndef IMAP_STORAGE
04287 /*!
04288  * \brief Find all .txt files - even if they are not in sequence from 0000.
04289  * \param vmu
04290  * \param dir
04291  *
04292  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04293  *
04294  * \return the count of messages, zero or more.
04295  */
04296 static int count_messages(struct ast_vm_user *vmu, char *dir)
04297 {
04298 
04299    int vmcount = 0;
04300    DIR *vmdir = NULL;
04301    struct dirent *vment = NULL;
04302 
04303    if (vm_lock_path(dir))
04304       return ERROR_LOCK_PATH;
04305 
04306    if ((vmdir = opendir(dir))) {
04307       while ((vment = readdir(vmdir))) {
04308          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
04309             vmcount++;
04310          }
04311       }
04312       closedir(vmdir);
04313    }
04314    ast_unlock_path(dir);
04315    
04316    return vmcount;
04317 }
04318 
04319 /*!
04320  * \brief Renames a message in a mailbox folder.
04321  * \param sfn The path to the mailbox information and data file to be renamed.
04322  * \param dfn The path for where the message data and information files will be renamed to.
04323  *
04324  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04325  */
04326 static void rename_file(char *sfn, char *dfn)
04327 {
04328    char stxt[PATH_MAX];
04329    char dtxt[PATH_MAX];
04330    ast_filerename(sfn, dfn, NULL);
04331    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
04332    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
04333    if (ast_check_realtime("voicemail_data")) {
04334       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
04335    }
04336    rename(stxt, dtxt);
04337 }
04338 
04339 /*! 
04340  * \brief Determines the highest message number in use for a given user and mailbox folder.
04341  * \param vmu 
04342  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
04343  *
04344  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
04345  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
04346  *
04347  * \note Should always be called with a lock already set on dir.
04348  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
04349  */
04350 static int last_message_index(struct ast_vm_user *vmu, char *dir)
04351 {
04352    int x;
04353    unsigned char map[MAXMSGLIMIT] = "";
04354    DIR *msgdir;
04355    struct dirent *msgdirent;
04356    int msgdirint;
04357    char extension[4];
04358    int stopcount = 0;
04359 
04360    /* Reading the entire directory into a file map scales better than
04361     * doing a stat repeatedly on a predicted sequence.  I suspect this
04362     * is partially due to stat(2) internally doing a readdir(2) itself to
04363     * find each file. */
04364    if (!(msgdir = opendir(dir))) {
04365       return -1;
04366    }
04367 
04368    while ((msgdirent = readdir(msgdir))) {
04369       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
04370          map[msgdirint] = 1;
04371          stopcount++;
04372          ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
04373       }
04374    }
04375    closedir(msgdir);
04376 
04377    for (x = 0; x < vmu->maxmsg; x++) {
04378       if (map[x] == 1) {
04379          stopcount--;
04380       } else if (map[x] == 0 && !stopcount) {
04381          break;
04382       }
04383    }
04384 
04385    return x - 1;
04386 }
04387 
04388 #endif /* #ifndef IMAP_STORAGE */
04389 #endif /* #else of #ifdef ODBC_STORAGE */
04390 #ifndef IMAP_STORAGE
04391 /*!
04392  * \brief Utility function to copy a file.
04393  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
04394  * \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.
04395  *
04396  * 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.
04397  * The copy operation copies up to 4096 bytes at once.
04398  *
04399  * \return zero on success, -1 on error.
04400  */
04401 static int copy(char *infile, char *outfile)
04402 {
04403    int ifd;
04404    int ofd;
04405    int res;
04406    int len;
04407    char buf[4096];
04408 
04409 #ifdef HARDLINK_WHEN_POSSIBLE
04410    /* Hard link if possible; saves disk space & is faster */
04411    if (link(infile, outfile)) {
04412 #endif
04413       if ((ifd = open(infile, O_RDONLY)) < 0) {
04414          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
04415          return -1;
04416       }
04417       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
04418          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
04419          close(ifd);
04420          return -1;
04421       }
04422       do {
04423          len = read(ifd, buf, sizeof(buf));
04424          if (len < 0) {
04425             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
04426             close(ifd);
04427             close(ofd);
04428             unlink(outfile);
04429          } else if (len) {
04430             res = write(ofd, buf, len);
04431             if (errno == ENOMEM || errno == ENOSPC || res != len) {
04432                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
04433                close(ifd);
04434                close(ofd);
04435                unlink(outfile);
04436             }
04437          }
04438       } while (len);
04439       close(ifd);
04440       close(ofd);
04441       return 0;
04442 #ifdef HARDLINK_WHEN_POSSIBLE
04443    } else {
04444       /* Hard link succeeded */
04445       return 0;
04446    }
04447 #endif
04448 }
04449 
04450 /*!
04451  * \brief Copies a voicemail information (envelope) file.
04452  * \param frompath
04453  * \param topath 
04454  *
04455  * Every voicemail has the data (.wav) file, and the information file.
04456  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
04457  * This is used by the COPY macro when not using IMAP storage.
04458  */
04459 static void copy_plain_file(char *frompath, char *topath)
04460 {
04461    char frompath2[PATH_MAX], topath2[PATH_MAX];
04462    struct ast_variable *tmp,*var = NULL;
04463    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
04464    ast_filecopy(frompath, topath, NULL);
04465    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
04466    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
04467    if (ast_check_realtime("voicemail_data")) {
04468       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
04469       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
04470       for (tmp = var; tmp; tmp = tmp->next) {
04471          if (!strcasecmp(tmp->name, "origmailbox")) {
04472             origmailbox = tmp->value;
04473          } else if (!strcasecmp(tmp->name, "context")) {
04474             context = tmp->value;
04475          } else if (!strcasecmp(tmp->name, "macrocontext")) {
04476             macrocontext = tmp->value;
04477          } else if (!strcasecmp(tmp->name, "exten")) {
04478             exten = tmp->value;
04479          } else if (!strcasecmp(tmp->name, "priority")) {
04480             priority = tmp->value;
04481          } else if (!strcasecmp(tmp->name, "callerchan")) {
04482             callerchan = tmp->value;
04483          } else if (!strcasecmp(tmp->name, "callerid")) {
04484             callerid = tmp->value;
04485          } else if (!strcasecmp(tmp->name, "origdate")) {
04486             origdate = tmp->value;
04487          } else if (!strcasecmp(tmp->name, "origtime")) {
04488             origtime = tmp->value;
04489          } else if (!strcasecmp(tmp->name, "category")) {
04490             category = tmp->value;
04491          } else if (!strcasecmp(tmp->name, "duration")) {
04492             duration = tmp->value;
04493          }
04494       }
04495       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);
04496    }
04497    copy(frompath2, topath2);
04498    ast_variables_destroy(var);
04499 }
04500 #endif
04501 
04502 /*! 
04503  * \brief Removes the voicemail sound and information file.
04504  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
04505  *
04506  * This is used by the DELETE macro when voicemails are stored on the file system.
04507  *
04508  * \return zero on success, -1 on error.
04509  */
04510 static int vm_delete(char *file)
04511 {
04512    char *txt;
04513    int txtsize = 0;
04514 
04515    txtsize = (strlen(file) + 5)*sizeof(char);
04516    txt = ast_alloca(txtsize);
04517    /* Sprintf here would safe because we alloca'd exactly the right length,
04518     * but trying to eliminate all sprintf's anyhow
04519     */
04520    if (ast_check_realtime("voicemail_data")) {
04521       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
04522    }
04523    snprintf(txt, txtsize, "%s.txt", file);
04524    unlink(txt);
04525    return ast_filedelete(file, NULL);
04526 }
04527 
04528 /*!
04529  * \brief utility used by inchar(), for base_encode()
04530  */
04531 static int inbuf(struct baseio *bio, FILE *fi)
04532 {
04533    int l;
04534 
04535    if (bio->ateof)
04536       return 0;
04537 
04538    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
04539       if (ferror(fi))
04540          return -1;
04541 
04542       bio->ateof = 1;
04543       return 0;
04544    }
04545 
04546    bio->iolen = l;
04547    bio->iocp = 0;
04548 
04549    return 1;
04550 }
04551 
04552 /*!
04553  * \brief utility used by base_encode()
04554  */
04555 static int inchar(struct baseio *bio, FILE *fi)
04556 {
04557    if (bio->iocp>=bio->iolen) {
04558       if (!inbuf(bio, fi))
04559          return EOF;
04560    }
04561 
04562    return bio->iobuf[bio->iocp++];
04563 }
04564 
04565 /*!
04566  * \brief utility used by base_encode()
04567  */
04568 static int ochar(struct baseio *bio, int c, FILE *so)
04569 {
04570    if (bio->linelength >= BASELINELEN) {
04571       if (fputs(ENDL, so) == EOF) {
04572          return -1;
04573       }
04574 
04575       bio->linelength = 0;
04576    }
04577 
04578    if (putc(((unsigned char) c), so) == EOF) {
04579       return -1;
04580    }
04581 
04582    bio->linelength++;
04583 
04584    return 1;
04585 }
04586 
04587 /*!
04588  * \brief Performs a base 64 encode algorithm on the contents of a File
04589  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
04590  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
04591  *
04592  * 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 ?
04593  *
04594  * \return zero on success, -1 on error.
04595  */
04596 static int base_encode(char *filename, FILE *so)
04597 {
04598    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
04599       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
04600       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
04601       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
04602    int i, hiteof = 0;
04603    FILE *fi;
04604    struct baseio bio;
04605 
04606    memset(&bio, 0, sizeof(bio));
04607    bio.iocp = BASEMAXINLINE;
04608 
04609    if (!(fi = fopen(filename, "rb"))) {
04610       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
04611       return -1;
04612    }
04613 
04614    while (!hiteof){
04615       unsigned char igroup[3], ogroup[4];
04616       int c, n;
04617 
04618       memset(igroup, 0, sizeof(igroup));
04619 
04620       for (n = 0; n < 3; n++) {
04621          if ((c = inchar(&bio, fi)) == EOF) {
04622             hiteof = 1;
04623             break;
04624          }
04625 
04626          igroup[n] = (unsigned char) c;
04627       }
04628 
04629       if (n > 0) {
04630          ogroup[0]= dtable[igroup[0] >> 2];
04631          ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
04632          ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
04633          ogroup[3]= dtable[igroup[2] & 0x3F];
04634 
04635          if (n < 3) {
04636             ogroup[3] = '=';
04637 
04638             if (n < 2)
04639                ogroup[2] = '=';
04640          }
04641 
04642          for (i = 0; i < 4; i++)
04643             ochar(&bio, ogroup[i], so);
04644       }
04645    }
04646 
04647    fclose(fi);
04648    
04649    if (fputs(ENDL, so) == EOF) {
04650       return 0;
04651    }
04652 
04653    return 1;
04654 }
04655 
04656 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)
04657 {
04658    char callerid[256];
04659    char num[12];
04660    char fromdir[256], fromfile[256];
04661    struct ast_config *msg_cfg;
04662    const char *origcallerid, *origtime;
04663    char origcidname[80], origcidnum[80], origdate[80];
04664    int inttime;
04665    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04666 
04667    /* Prepare variables for substitution in email body and subject */
04668    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
04669    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
04670    snprintf(num, sizeof(num), "%d", msgnum);
04671    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
04672    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
04673    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
04674    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
04675       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
04676    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
04677    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
04678    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
04679    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
04680    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
04681 
04682    /* Retrieve info from VM attribute file */
04683    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04684    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
04685    if (strlen(fromfile) < sizeof(fromfile) - 5) {
04686       strcat(fromfile, ".txt");
04687    }
04688    if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
04689       ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
04690       return;
04691    }
04692 
04693    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04694       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
04695       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
04696       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
04697       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
04698    }
04699 
04700    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
04701       struct timeval tv = { inttime, };
04702       struct ast_tm tm;
04703       ast_localtime(&tv, &tm, NULL);
04704       ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04705       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
04706    }
04707    ast_config_destroy(msg_cfg);
04708 }
04709 
04710 /*!
04711  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
04712  * \param from The string to work with.
04713  * \param buf The buffer into which to write the modified quoted string.
04714  * \param maxlen Always zero, but see \see ast_str
04715  * 
04716  * \return The destination string with quotes wrapped on it (the to field).
04717  */
04718 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
04719 {
04720    const char *ptr;
04721 
04722    /* We're only ever passing 0 to maxlen, so short output isn't possible */
04723    ast_str_set(buf, maxlen, "\"");
04724    for (ptr = from; *ptr; ptr++) {
04725       if (*ptr == '"' || *ptr == '\\') {
04726          ast_str_append(buf, maxlen, "\\%c", *ptr);
04727       } else {
04728          ast_str_append(buf, maxlen, "%c", *ptr);
04729       }
04730    }
04731    ast_str_append(buf, maxlen, "\"");
04732 
04733    return ast_str_buffer(*buf);
04734 }
04735 
04736 /*! \brief
04737  * fill in *tm for current time according to the proper timezone, if any.
04738  * \return tm so it can be used as a function argument.
04739  */
04740 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
04741 {
04742    const struct vm_zone *z = NULL;
04743    struct timeval t = ast_tvnow();
04744 
04745    /* Does this user have a timezone specified? */
04746    if (!ast_strlen_zero(vmu->zonetag)) {
04747       /* Find the zone in the list */
04748       AST_LIST_LOCK(&zones);
04749       AST_LIST_TRAVERSE(&zones, z, list) {
04750          if (!strcmp(z->name, vmu->zonetag))
04751             break;
04752       }
04753       AST_LIST_UNLOCK(&zones);
04754    }
04755    ast_localtime(&t, tm, z ? z->timezone : NULL);
04756    return tm;
04757 }
04758 
04759 /*!\brief Check if the string would need encoding within the MIME standard, to
04760  * avoid confusing certain mail software that expects messages to be 7-bit
04761  * clean.
04762  */
04763 static int check_mime(const char *str)
04764 {
04765    for (; *str; str++) {
04766       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
04767          return 1;
04768       }
04769    }
04770    return 0;
04771 }
04772 
04773 /*!\brief Encode a string according to the MIME rules for encoding strings
04774  * that are not 7-bit clean or contain control characters.
04775  *
04776  * Additionally, if the encoded string would exceed the MIME limit of 76
04777  * characters per line, then the encoding will be broken up into multiple
04778  * sections, separated by a space character, in order to facilitate
04779  * breaking up the associated header across multiple lines.
04780  *
04781  * \param end An expandable buffer for holding the result
04782  * \param maxlen Always zero, but see \see ast_str
04783  * \param start A string to be encoded
04784  * \param preamble The length of the first line already used for this string,
04785  * to ensure that each line maintains a maximum length of 76 chars.
04786  * \param postamble the length of any additional characters appended to the
04787  * line, used to ensure proper field wrapping.
04788  * \retval The encoded string.
04789  */
04790 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
04791 {
04792    struct ast_str *tmp = ast_str_alloca(80);
04793    int first_section = 1;
04794 
04795    ast_str_reset(*end);
04796    ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04797    for (; *start; start++) {
04798       int need_encoding = 0;
04799       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
04800          need_encoding = 1;
04801       }
04802       if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
04803          (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
04804          (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
04805          (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
04806          /* Start new line */
04807          ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
04808          ast_str_set(&tmp, -1, "=?%s?Q?", charset);
04809          first_section = 0;
04810       }
04811       if (need_encoding && *start == ' ') {
04812          ast_str_append(&tmp, -1, "_");
04813       } else if (need_encoding) {
04814          ast_str_append(&tmp, -1, "=%hhX", *start);
04815       } else {
04816          ast_str_append(&tmp, -1, "%c", *start);
04817       }
04818    }
04819    ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
04820    return ast_str_buffer(*end);
04821 }
04822 
04823 /*!
04824  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04825  * \param p The output file to generate the email contents into.
04826  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04827  * \param vmu The voicemail user who is sending the voicemail.
04828  * \param msgnum The message index in the mailbox folder.
04829  * \param context 
04830  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04831  * \param fromfolder
04832  * \param cidnum The caller ID number.
04833  * \param cidname The caller ID name.
04834  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04835  * \param attach2 
04836  * \param format The message sound file format. i.e. .wav
04837  * \param duration The time of the message content, in seconds.
04838  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04839  * \param chan
04840  * \param category
04841  * \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.
04842  * \param flag
04843  *
04844  * 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.
04845  */
04846 static void make_email_file(FILE *p,
04847       char *srcemail,
04848       struct ast_vm_user *vmu,
04849       int msgnum,
04850       char *context,
04851       char *mailbox,
04852       const char *fromfolder,
04853       char *cidnum,
04854       char *cidname,
04855       char *attach,
04856       char *attach2,
04857       char *format,
04858       int duration,
04859       int attach_user_voicemail,
04860       struct ast_channel *chan,
04861       const char *category,
04862       int imap,
04863       const char *flag,
04864       const char *msg_id)
04865 {
04866    char date[256];
04867    char host[MAXHOSTNAMELEN] = "";
04868    char who[256];
04869    char bound[256];
04870    char dur[256];
04871    struct ast_tm tm;
04872    char enc_cidnum[256] = "", enc_cidname[256] = "";
04873    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
04874    char *greeting_attachment; 
04875    char filename[256];
04876 
04877    if (!str1 || !str2) {
04878       ast_free(str1);
04879       ast_free(str2);
04880       return;
04881    }
04882 
04883    if (cidnum) {
04884       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04885    }
04886    if (cidname) {
04887       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04888    }
04889    gethostname(host, sizeof(host) - 1);
04890 
04891    if (strchr(srcemail, '@')) {
04892       ast_copy_string(who, srcemail, sizeof(who));
04893    } else {
04894       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04895    }
04896 
04897    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04898    if (greeting_attachment) {
04899       *greeting_attachment++ = '\0';
04900    }
04901 
04902    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04903    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04904    fprintf(p, "Date: %s" ENDL, date);
04905 
04906    /* Set date format for voicemail mail */
04907    ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
04908 
04909    if (!ast_strlen_zero(fromstring)) {
04910       struct ast_channel *ast;
04911       if ((ast = ast_dummy_channel_alloc())) {
04912          char *ptr;
04913          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
04914          ast_str_substitute_variables(&str1, 0, ast, fromstring);
04915 
04916          if (check_mime(ast_str_buffer(str1))) {
04917             int first_line = 1;
04918             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
04919             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04920                *ptr = '\0';
04921                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
04922                first_line = 0;
04923                /* Substring is smaller, so this will never grow */
04924                ast_str_set(&str2, 0, "%s", ptr + 1);
04925             }
04926             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
04927          } else {
04928             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
04929          }
04930          ast = ast_channel_unref(ast);
04931       } else {
04932          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04933       }
04934    } else {
04935       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04936    }
04937 
04938    if (check_mime(vmu->fullname)) {
04939       int first_line = 1;
04940       char *ptr;
04941       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
04942       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04943          *ptr = '\0';
04944          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
04945          first_line = 0;
04946          /* Substring is smaller, so this will never grow */
04947          ast_str_set(&str2, 0, "%s", ptr + 1);
04948       }
04949       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
04950    } else {
04951       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
04952    }
04953 
04954    if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
04955       char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
04956       struct ast_channel *ast;
04957       if ((ast = ast_dummy_channel_alloc())) {
04958          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
04959          ast_str_substitute_variables(&str1, 0, ast, e_subj);
04960          if (check_mime(ast_str_buffer(str1))) {
04961             int first_line = 1;
04962             char *ptr;
04963             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
04964             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
04965                *ptr = '\0';
04966                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04967                first_line = 0;
04968                /* Substring is smaller, so this will never grow */
04969                ast_str_set(&str2, 0, "%s", ptr + 1);
04970             }
04971             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
04972          } else {
04973             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
04974          }
04975          ast = ast_channel_unref(ast);
04976       } else {
04977          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04978       }
04979    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04980       if (ast_strlen_zero(flag)) {
04981          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04982       } else {
04983          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04984       }
04985    } else {
04986       if (ast_strlen_zero(flag)) {
04987          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04988       } else {
04989          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04990       }
04991    }
04992 
04993    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
04994       (unsigned int) ast_random(), mailbox, (int) getpid(), host);
04995    if (imap) {
04996       /* additional information needed for IMAP searching */
04997       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04998       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04999       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
05000       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
05001 #ifdef IMAP_STORAGE
05002       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
05003 #else
05004       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
05005 #endif
05006       /* flag added for Urgent */
05007       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
05008       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
05009       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan ? ast_channel_name(chan) : "");
05010       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
05011       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
05012       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
05013       if (!ast_strlen_zero(category)) {
05014          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
05015       } else {
05016          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
05017       }
05018       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
05019       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
05020       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
05021       fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
05022    }
05023    if (!ast_strlen_zero(cidnum)) {
05024       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
05025    }
05026    if (!ast_strlen_zero(cidname)) {
05027       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
05028    }
05029    fprintf(p, "MIME-Version: 1.0" ENDL);
05030    if (attach_user_voicemail) {
05031       /* Something unique. */
05032       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
05033          (int) getpid(), (unsigned int) ast_random());
05034 
05035       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
05036       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
05037       fprintf(p, "--%s" ENDL, bound);
05038    }
05039    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
05040    if (emailbody || vmu->emailbody) {
05041       char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
05042       struct ast_channel *ast;
05043       if ((ast = ast_dummy_channel_alloc())) {
05044          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05045          ast_str_substitute_variables(&str1, 0, ast, e_body);
05046 #ifdef IMAP_STORAGE
05047             {
05048                /* Convert body to native line terminators for IMAP backend */
05049                char *line = ast_str_buffer(str1), *next;
05050                do {
05051                   /* Terminate line before outputting it to the file */
05052                   if ((next = strchr(line, '\n'))) {
05053                      *next++ = '\0';
05054                   }
05055                   fprintf(p, "%s" ENDL, line);
05056                   line = next;
05057                } while (!ast_strlen_zero(line));
05058             }
05059 #else
05060          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05061 #endif
05062          ast = ast_channel_unref(ast);
05063       } else {
05064          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05065       }
05066    } else if (msgnum > -1) {
05067       if (strcmp(vmu->mailbox, mailbox)) {
05068          /* Forwarded type */
05069          struct ast_config *msg_cfg;
05070          const char *v;
05071          int inttime;
05072          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
05073          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
05074          /* Retrieve info from VM attribute file */
05075          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
05076          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
05077          if (strlen(fromfile) < sizeof(fromfile) - 5) {
05078             strcat(fromfile, ".txt");
05079          }
05080          if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
05081             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
05082                ast_copy_string(origcallerid, v, sizeof(origcallerid));
05083             }
05084 
05085             /* You might be tempted to do origdate, except that a) it's in the wrong
05086              * format, and b) it's missing for IMAP recordings. */
05087             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
05088                struct timeval tv = { inttime, };
05089                struct ast_tm tm;
05090                ast_localtime(&tv, &tm, NULL);
05091                ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
05092             }
05093             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
05094                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
05095                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
05096                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
05097                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
05098                date, origcallerid, origdate);
05099             ast_config_destroy(msg_cfg);
05100          } else {
05101             goto plain_message;
05102          }
05103       } else {
05104 plain_message:
05105          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
05106             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
05107             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
05108             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
05109             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
05110       }
05111    } else {
05112       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
05113             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
05114    }
05115 
05116    if (imap || attach_user_voicemail) {
05117       if (!ast_strlen_zero(attach2)) {
05118          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
05119          ast_debug(5, "creating second attachment filename %s\n", filename);
05120          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
05121          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
05122          ast_debug(5, "creating attachment filename %s\n", filename);
05123          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
05124       } else {
05125          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
05126          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
05127          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
05128       }
05129    }
05130    ast_free(str1);
05131    ast_free(str2);
05132 }
05133 
05134 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)
05135 {
05136    char tmpdir[256], newtmp[256];
05137    char fname[256];
05138    char tmpcmd[256];
05139    int tmpfd = -1;
05140    int soxstatus = 0;
05141 
05142    /* Eww. We want formats to tell us their own MIME type */
05143    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
05144 
05145    if (vmu->volgain < -.001 || vmu->volgain > .001) {
05146       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
05147       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
05148       tmpfd = mkstemp(newtmp);
05149       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
05150       ast_debug(3, "newtmp: %s\n", newtmp);
05151       if (tmpfd > -1) {
05152          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
05153          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
05154             attach = newtmp;
05155             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
05156          } else {
05157             ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
05158                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
05159             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
05160          }
05161       }
05162    }
05163    fprintf(p, "--%s" ENDL, bound);
05164    if (msgnum > -1)
05165       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
05166    else
05167       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
05168    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
05169    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
05170    if (msgnum > -1)
05171       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
05172    else
05173       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
05174    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
05175    base_encode(fname, p);
05176    if (last)
05177       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
05178    if (tmpfd > -1) {
05179       if (soxstatus == 0) {
05180          unlink(fname);
05181       }
05182       close(tmpfd);
05183       unlink(newtmp);
05184    }
05185    return 0;
05186 }
05187 
05188 static int sendmail(char *srcemail,
05189       struct ast_vm_user *vmu,
05190       int msgnum,
05191       char *context,
05192       char *mailbox,
05193       const char *fromfolder,
05194       char *cidnum,
05195       char *cidname,
05196       char *attach,
05197       char *attach2,
05198       char *format,
05199       int duration,
05200       int attach_user_voicemail,
05201       struct ast_channel *chan,
05202       const char *category,
05203       const char *flag,
05204       const char *msg_id)
05205 {
05206    FILE *p = NULL;
05207    char tmp[80] = "/tmp/astmail-XXXXXX";
05208    char tmp2[256];
05209    char *stringp;
05210 
05211    if (vmu && ast_strlen_zero(vmu->email)) {
05212       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
05213       return(0);
05214    }
05215 
05216    /* Mail only the first format */
05217    format = ast_strdupa(format);
05218    stringp = format;
05219    strsep(&stringp, "|");
05220 
05221    if (!strcmp(format, "wav49"))
05222       format = "WAV";
05223    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));
05224    /* Make a temporary file instead of piping directly to sendmail, in case the mail
05225       command hangs */
05226    if ((p = vm_mkftemp(tmp)) == NULL) {
05227       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
05228       return -1;
05229    } else {
05230       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag, msg_id);
05231       fclose(p);
05232       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05233       ast_safe_system(tmp2);
05234       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
05235    }
05236    return 0;
05237 }
05238 
05239 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)
05240 {
05241    char enc_cidnum[256], enc_cidname[256];
05242    char date[256];
05243    char host[MAXHOSTNAMELEN] = "";
05244    char who[256];
05245    char dur[PATH_MAX];
05246    char tmp[80] = "/tmp/astmail-XXXXXX";
05247    char tmp2[PATH_MAX];
05248    struct ast_tm tm;
05249    FILE *p;
05250    struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
05251 
05252    if (!str1 || !str2) {
05253       ast_free(str1);
05254       ast_free(str2);
05255       return -1;
05256    }
05257 
05258    if (cidnum) {
05259       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
05260    }
05261    if (cidname) {
05262       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
05263    }
05264 
05265    if ((p = vm_mkftemp(tmp)) == NULL) {
05266       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
05267       ast_free(str1);
05268       ast_free(str2);
05269       return -1;
05270    }
05271    gethostname(host, sizeof(host)-1);
05272    if (strchr(srcemail, '@')) {
05273       ast_copy_string(who, srcemail, sizeof(who));
05274    } else {
05275       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
05276    }
05277    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
05278    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
05279    fprintf(p, "Date: %s\n", date);
05280 
05281    /* Reformat for custom pager format */
05282    ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
05283 
05284    if (!ast_strlen_zero(pagerfromstring)) {
05285       struct ast_channel *ast;
05286       if ((ast = ast_dummy_channel_alloc())) {
05287          char *ptr;
05288          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
05289          ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
05290 
05291          if (check_mime(ast_str_buffer(str1))) {
05292             int first_line = 1;
05293             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
05294             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05295                *ptr = '\0';
05296                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
05297                first_line = 0;
05298                /* Substring is smaller, so this will never grow */
05299                ast_str_set(&str2, 0, "%s", ptr + 1);
05300             }
05301             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
05302          } else {
05303             fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
05304          }
05305          ast = ast_channel_unref(ast);
05306       } else {
05307          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05308       }
05309    } else {
05310       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
05311    }
05312 
05313    if (check_mime(vmu->fullname)) {
05314       int first_line = 1;
05315       char *ptr;
05316       ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
05317       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05318          *ptr = '\0';
05319          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
05320          first_line = 0;
05321          /* Substring is smaller, so this will never grow */
05322          ast_str_set(&str2, 0, "%s", ptr + 1);
05323       }
05324       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
05325    } else {
05326       fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
05327    }
05328 
05329    if (!ast_strlen_zero(pagersubject)) {
05330       struct ast_channel *ast;
05331       if ((ast = ast_dummy_channel_alloc())) {
05332          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05333          ast_str_substitute_variables(&str1, 0, ast, pagersubject);
05334          if (check_mime(ast_str_buffer(str1))) {
05335             int first_line = 1;
05336             char *ptr;
05337             ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
05338             while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
05339                *ptr = '\0';
05340                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05341                first_line = 0;
05342                /* Substring is smaller, so this will never grow */
05343                ast_str_set(&str2, 0, "%s", ptr + 1);
05344             }
05345             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
05346          } else {
05347             fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
05348          }
05349          ast = ast_channel_unref(ast);
05350       } else {
05351          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05352       }
05353    } else {
05354       if (ast_strlen_zero(flag)) {
05355          fprintf(p, "Subject: New VM\n\n");
05356       } else {
05357          fprintf(p, "Subject: New %s VM\n\n", flag);
05358       }
05359    }
05360 
05361    if (pagerbody) {
05362       struct ast_channel *ast;
05363       if ((ast = ast_dummy_channel_alloc())) {
05364          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
05365          ast_str_substitute_variables(&str1, 0, ast, pagerbody);
05366          fprintf(p, "%s" ENDL, ast_str_buffer(str1));
05367          ast = ast_channel_unref(ast);
05368       } else {
05369          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
05370       }
05371    } else {
05372       fprintf(p, "New %s long %s msg in box %s\n"
05373             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
05374    }
05375 
05376    fclose(p);
05377    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
05378    ast_safe_system(tmp2);
05379    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
05380    ast_free(str1);
05381    ast_free(str2);
05382    return 0;
05383 }
05384 
05385 /*!
05386  * \brief Gets the current date and time, as formatted string.
05387  * \param s The buffer to hold the output formatted date.
05388  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
05389  * 
05390  * The date format string used is "%a %b %e %r UTC %Y".
05391  * 
05392  * \return zero on success, -1 on error.
05393  */
05394 static int get_date(char *s, int len)
05395 {
05396    struct ast_tm tm;
05397    struct timeval t = ast_tvnow();
05398 
05399    ast_localtime(&t, &tm, "UTC");
05400 
05401    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
05402 }
05403 
05404 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
05405 {
05406    int res;
05407    char fn[PATH_MAX];
05408    char dest[PATH_MAX];
05409 
05410    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
05411 
05412    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
05413       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
05414       return -1;
05415    }
05416 
05417    RETRIEVE(fn, -1, ext, context);
05418    if (ast_fileexists(fn, NULL, NULL) > 0) {
05419       res = ast_stream_and_wait(chan, fn, ecodes);
05420       if (res) {
05421          DISPOSE(fn, -1);
05422          return res;
05423       }
05424    } else {
05425       /* Dispose just in case */
05426       DISPOSE(fn, -1);
05427       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
05428       if (res)
05429          return res;
05430       res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
05431       if (res)
05432          return res;
05433    }
05434    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
05435    return res;
05436 }
05437 
05438 static void free_zone(struct vm_zone *z)
05439 {
05440    ast_free(z);
05441 }
05442 
05443 #ifdef ODBC_STORAGE
05444 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05445 {
05446    int x = -1;
05447    int res;
05448    SQLHSTMT stmt = NULL;
05449    char sql[PATH_MAX];
05450    char rowdata[20];
05451    char tmp[PATH_MAX] = "";
05452    struct odbc_obj *obj = NULL;
05453    char *context;
05454    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05455 
05456    if (newmsgs)
05457       *newmsgs = 0;
05458    if (oldmsgs)
05459       *oldmsgs = 0;
05460    if (urgentmsgs)
05461       *urgentmsgs = 0;
05462 
05463    /* If no mailbox, return immediately */
05464    if (ast_strlen_zero(mailbox))
05465       return 0;
05466 
05467    ast_copy_string(tmp, mailbox, sizeof(tmp));
05468 
05469    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
05470       int u, n, o;
05471       char *next, *remaining = tmp;
05472       while ((next = strsep(&remaining, " ,"))) {
05473          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
05474             return -1;
05475          }
05476          if (urgentmsgs) {
05477             *urgentmsgs += u;
05478          }
05479          if (newmsgs) {
05480             *newmsgs += n;
05481          }
05482          if (oldmsgs) {
05483             *oldmsgs += o;
05484          }
05485       }
05486       return 0;
05487    }
05488 
05489    context = strchr(tmp, '@');
05490    if (context) {
05491       *context = '\0';
05492       context++;
05493    } else
05494       context = "default";
05495 
05496    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
05497       do {
05498          if (newmsgs) {
05499             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
05500             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05501                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05502                break;
05503             }
05504             res = SQLFetch(stmt);
05505             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05506                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05507                break;
05508             }
05509             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05510             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05511                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05512                break;
05513             }
05514             *newmsgs = atoi(rowdata);
05515             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05516          }
05517 
05518          if (oldmsgs) {
05519             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
05520             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05521                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05522                break;
05523             }
05524             res = SQLFetch(stmt);
05525             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05526                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05527                break;
05528             }
05529             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05530             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05531                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05532                break;
05533             }
05534             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
05535             *oldmsgs = atoi(rowdata);
05536          }
05537 
05538          if (urgentmsgs) {
05539             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
05540             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
05541                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05542                break;
05543             }
05544             res = SQLFetch(stmt);
05545             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05546                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05547                break;
05548             }
05549             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05550             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05551                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05552                break;
05553             }
05554             *urgentmsgs = atoi(rowdata);
05555          }
05556 
05557          x = 0;
05558       } while (0);
05559    } else {
05560       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05561    }
05562 
05563    if (stmt) {
05564       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05565    }
05566    if (obj) {
05567       ast_odbc_release_obj(obj);
05568    }
05569    return x;
05570 }
05571 
05572 /*!
05573  * \brief Gets the number of messages that exist in a mailbox folder.
05574  * \param context
05575  * \param mailbox
05576  * \param folder
05577  * 
05578  * This method is used when ODBC backend is used.
05579  * \return The number of messages in this mailbox folder (zero or more).
05580  */
05581 static int messagecount(const char *context, const char *mailbox, const char *folder)
05582 {
05583    struct odbc_obj *obj = NULL;
05584    int nummsgs = 0;
05585    int res;
05586    SQLHSTMT stmt = NULL;
05587    char sql[PATH_MAX];
05588    char rowdata[20];
05589    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
05590 
05591    /* If no mailbox, return immediately */
05592    if (ast_strlen_zero(mailbox)) {
05593       return 0;
05594    }
05595 
05596    if (ast_strlen_zero(folder)) {
05597       folder = "INBOX";
05598    }
05599 
05600    obj = ast_odbc_request_obj(odbc_database, 0);
05601    if (obj) {
05602       if (!strcmp(folder, "INBOX")) {
05603          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);
05604       } else {
05605          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
05606       }
05607       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
05608       if (!stmt) {
05609          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
05610          goto yuck;
05611       }
05612       res = SQLFetch(stmt);
05613       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05614          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
05615          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05616          goto yuck;
05617       }
05618       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
05619       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
05620          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
05621          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05622          goto yuck;
05623       }
05624       nummsgs = atoi(rowdata);
05625       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
05626    } else
05627       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
05628 
05629 yuck:
05630    if (obj)
05631       ast_odbc_release_obj(obj);
05632    return nummsgs;
05633 }
05634 
05635 /** 
05636  * \brief Determines if the given folder has messages.
05637  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05638  * 
05639  * This function is used when the mailbox is stored in an ODBC back end.
05640  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05641  * \return 1 if the folder has one or more messages. zero otherwise.
05642  */
05643 static int has_voicemail(const char *mailbox, const char *folder)
05644 {
05645    char tmp[256], *tmp2 = tmp, *box, *context;
05646    ast_copy_string(tmp, mailbox, sizeof(tmp));
05647    while ((context = box = strsep(&tmp2, ",&"))) {
05648       strsep(&context, "@");
05649       if (ast_strlen_zero(context))
05650          context = "default";
05651       if (messagecount(context, box, folder))
05652          return 1;
05653    }
05654    return 0;
05655 }
05656 #endif
05657 #ifndef IMAP_STORAGE
05658 /*! 
05659  * \brief Copies a message from one mailbox to another.
05660  * \param chan
05661  * \param vmu
05662  * \param imbox
05663  * \param msgnum
05664  * \param duration
05665  * \param recip
05666  * \param fmt
05667  * \param dir
05668  * \param flag
05669  *
05670  * This is only used by file storage based mailboxes.
05671  *
05672  * \return zero on success, -1 on error.
05673  */
05674 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, const char *dest_folder)
05675 {
05676    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
05677    const char *frombox = mbox(vmu, imbox);
05678    const char *userfolder;
05679    int recipmsgnum;
05680    int res = 0;
05681 
05682    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
05683 
05684    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
05685       userfolder = "Urgent";
05686    } else if (!ast_strlen_zero(dest_folder)) {
05687       userfolder = dest_folder;
05688    } else {
05689       userfolder = "INBOX";
05690    }
05691 
05692    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05693 
05694    if (!dir)
05695       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
05696    else
05697       ast_copy_string(fromdir, dir, sizeof(fromdir));
05698 
05699    make_file(frompath, sizeof(frompath), fromdir, msgnum);
05700    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
05701 
05702    if (vm_lock_path(todir))
05703       return ERROR_LOCK_PATH;
05704 
05705    recipmsgnum = last_message_index(recip, todir) + 1;
05706    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
05707       make_file(topath, sizeof(topath), todir, recipmsgnum);
05708 #ifndef ODBC_STORAGE
05709       if (EXISTS(fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "")) {
05710          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
05711       } else {
05712 #endif
05713          /* If we are prepending a message for ODBC, then the message already
05714           * exists in the database, but we want to force copying from the
05715           * filesystem (since only the FS contains the prepend). */
05716          copy_plain_file(frompath, topath);
05717          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
05718          vm_delete(topath);
05719 #ifndef ODBC_STORAGE
05720       }
05721 #endif
05722    } else {
05723       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
05724       res = -1;
05725    }
05726    ast_unlock_path(todir);
05727    if (chan) {
05728       struct ast_party_caller *caller = ast_channel_caller(chan);
05729       notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
05730          S_COR(caller->id.number.valid, caller->id.number.str, NULL),
05731          S_COR(caller->id.name.valid, caller->id.name.str, NULL),
05732          flag);
05733    }
05734 
05735    return res;
05736 }
05737 #endif
05738 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
05739 
05740 static int messagecount(const char *context, const char *mailbox, const char *folder)
05741 {
05742    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
05743 }
05744 
05745 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
05746 {
05747    DIR *dir;
05748    struct dirent *de;
05749    char fn[256];
05750    int ret = 0;
05751 
05752    /* If no mailbox, return immediately */
05753    if (ast_strlen_zero(mailbox))
05754       return 0;
05755 
05756    if (ast_strlen_zero(folder))
05757       folder = "INBOX";
05758    if (ast_strlen_zero(context))
05759       context = "default";
05760 
05761    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
05762 
05763    if (!(dir = opendir(fn)))
05764       return 0;
05765 
05766    while ((de = readdir(dir))) {
05767       if (!strncasecmp(de->d_name, "msg", 3)) {
05768          if (shortcircuit) {
05769             ret = 1;
05770             break;
05771          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
05772             ret++;
05773          }
05774       }
05775    }
05776 
05777    closedir(dir);
05778 
05779    return ret;
05780 }
05781 
05782 /** 
05783  * \brief Determines if the given folder has messages.
05784  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
05785  * \param folder the folder to look in
05786  *
05787  * This function is used when the mailbox is stored in a filesystem back end.
05788  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
05789  * \return 1 if the folder has one or more messages. zero otherwise.
05790  */
05791 static int has_voicemail(const char *mailbox, const char *folder)
05792 {
05793    char tmp[256], *tmp2 = tmp, *box, *context;
05794    ast_copy_string(tmp, mailbox, sizeof(tmp));
05795    if (ast_strlen_zero(folder)) {
05796       folder = "INBOX";
05797    }
05798    while ((box = strsep(&tmp2, ",&"))) {
05799       if ((context = strchr(box, '@')))
05800          *context++ = '\0';
05801       else
05802          context = "default";
05803       if (__has_voicemail(context, box, folder, 1))
05804          return 1;
05805       /* If we are checking INBOX, we should check Urgent as well */
05806       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
05807          return 1;
05808       }
05809    }
05810    return 0;
05811 }
05812 
05813 
05814 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
05815 {
05816    char tmp[256];
05817    char *context;
05818 
05819    /* If no mailbox, return immediately */
05820    if (ast_strlen_zero(mailbox))
05821       return 0;
05822 
05823    if (newmsgs)
05824       *newmsgs = 0;
05825    if (oldmsgs)
05826       *oldmsgs = 0;
05827    if (urgentmsgs)
05828       *urgentmsgs = 0;
05829 
05830    if (strchr(mailbox, ',')) {
05831       int tmpnew, tmpold, tmpurgent;
05832       char *mb, *cur;
05833 
05834       ast_copy_string(tmp, mailbox, sizeof(tmp));
05835       mb = tmp;
05836       while ((cur = strsep(&mb, ", "))) {
05837          if (!ast_strlen_zero(cur)) {
05838             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
05839                return -1;
05840             else {
05841                if (newmsgs)
05842                   *newmsgs += tmpnew; 
05843                if (oldmsgs)
05844                   *oldmsgs += tmpold;
05845                if (urgentmsgs)
05846                   *urgentmsgs += tmpurgent;
05847             }
05848          }
05849       }
05850       return 0;
05851    }
05852 
05853    ast_copy_string(tmp, mailbox, sizeof(tmp));
05854    
05855    if ((context = strchr(tmp, '@')))
05856       *context++ = '\0';
05857    else
05858       context = "default";
05859 
05860    if (newmsgs)
05861       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
05862    if (oldmsgs)
05863       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
05864    if (urgentmsgs)
05865       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
05866 
05867    return 0;
05868 }
05869 
05870 #endif
05871 
05872 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
05873 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
05874 {
05875    int urgentmsgs = 0;
05876    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
05877    if (newmsgs) {
05878       *newmsgs += urgentmsgs;
05879    }
05880    return res;
05881 }
05882 
05883 static void run_externnotify(char *context, char *extension, const char *flag)
05884 {
05885    char arguments[255];
05886    char ext_context[256] = "";
05887    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
05888    struct ast_smdi_mwi_message *mwi_msg;
05889 
05890    if (!ast_strlen_zero(context))
05891       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
05892    else
05893       ast_copy_string(ext_context, extension, sizeof(ext_context));
05894 
05895    if (smdi_iface) {
05896       if (ast_app_has_voicemail(ext_context, NULL)) 
05897          ast_smdi_mwi_set(smdi_iface, extension);
05898       else
05899          ast_smdi_mwi_unset(smdi_iface, extension);
05900 
05901       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
05902          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
05903          if (!strncmp(mwi_msg->cause, "INV", 3))
05904             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
05905          else if (!strncmp(mwi_msg->cause, "BLK", 3))
05906             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
05907          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
05908          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
05909       } else {
05910          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
05911       }
05912    }
05913 
05914    if (!ast_strlen_zero(externnotify)) {
05915       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
05916          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
05917       } else {
05918          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
05919             externnotify, S_OR(context, "\"\""),
05920             extension, newvoicemails,
05921             oldvoicemails, urgentvoicemails);
05922          ast_debug(1, "Executing %s\n", arguments);
05923          ast_safe_system(arguments);
05924       }
05925    }
05926 }
05927 
05928 /*!
05929  * \brief Variables used for saving a voicemail.
05930  *
05931  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
05932  */
05933 struct leave_vm_options {
05934    unsigned int flags;
05935    signed char record_gain;
05936    char *exitcontext;
05937 };
05938 
05939 static void generate_msg_id(char *dst)
05940 {
05941    /* msg id is time of msg_id generation plus an incrementing value
05942     * called each time a new msg_id is generated. This should achieve uniqueness,
05943     * but only in single system solutions.
05944     */
05945    int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
05946    snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
05947 }
05948 
05949 /*!
05950  * \internal
05951  * \brief Creates a voicemail based on a specified file to a mailbox.
05952  * \param recdata A vm_recording_data containing filename and voicemail txt info.
05953  * \retval -1 failure
05954  * \retval 0 success
05955  *
05956  * This is installed to the app.h voicemail functions and accommodates all voicemail
05957  * storage methods. It should probably be broken out along with leave_voicemail at
05958  * some point in the future.
05959  *
05960  * This function currently only works for a single recipient and only uses the format
05961  * specified in recording_ext.
05962  */
05963 static int msg_create_from_file(struct ast_vm_recording_data *recdata)
05964 {
05965    /* voicemail recipient structure */
05966    struct ast_vm_user *recipient; /* points to svm once it's been created */
05967    struct ast_vm_user svm; /* struct storing the voicemail recipient */
05968 
05969    /* File paths */
05970    char tmpdir[PATH_MAX]; /* directory temp files are stored in */
05971    char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
05972    char desttxtfile[PATH_MAX]; /* final destination for txt file */
05973    char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
05974    char dir[PATH_MAX]; /* destination for tmp files on completion */
05975    char destination[PATH_MAX]; /* destination with msgXXXX.  Basically <dir>/msgXXXX */
05976 
05977    /* stuff that only seems to be needed for IMAP */
05978    #ifdef IMAP_STORAGE
05979    struct vm_state *vms = NULL;
05980    char ext_context[256] = "";
05981    char *fmt = ast_strdupa(recdata->recording_ext);
05982    int newmsgs = 0;
05983    int oldmsgs = 0;
05984    #endif
05985 
05986    /* miscellaneous operational variables */
05987    int res = 0; /* Used to store error codes from functions */
05988    int txtdes /* File descriptor for the text file used to write the voicemail info */;
05989    FILE *txt; /* FILE pointer to text file used to write the voicemail info */
05990    char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
05991    int msgnum; /* the 4 digit number designated to the voicemail */
05992    int duration = 0; /* Length of the audio being recorded in seconds */
05993    struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
05994 
05995    /* We aren't currently doing anything with category, since it comes from a channel variable and
05996     * this function doesn't use channels, but this function could add that as an argument later. */
05997    const char *category = NULL; /* pointless for now */
05998    char msg_id[MSG_ID_LEN];
05999 
06000    /* Start by checking to see if the file actually exists... */
06001    if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
06002       ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
06003       return -1;
06004    }
06005 
06006    if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
06007       ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
06008       return -1;
06009    }
06010 
06011    /* determine duration in seconds */
06012    if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
06013       if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
06014          long framelength = ast_tellstream(recording_fs);
06015          struct ast_format result = {0,};
06016          /* XXX This use of ast_getformatbyname seems incorrect here. The file extension does not necessarily correspond
06017           * to the name of the format. For instance, if "raw" were passed in, I don't think ast_getformatbyname would
06018           * find the slinear format
06019           */
06020          ast_getformatbyname(recdata->recording_ext, &result);
06021          duration = (int) (framelength / ast_format_rate(&result));
06022       }
06023    }
06024 
06025    /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
06026    if (duration < recipient->minsecs) {
06027       ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
06028                "minmessage of recipient\n", recdata->mailbox, recdata->context);
06029       return -1;
06030    }
06031 
06032    /* Note that this number must be dropped back to a net sum of zero before returning from this function */
06033 
06034    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
06035       ast_log(LOG_ERROR, "Failed to make directory.\n");
06036    }
06037 
06038    snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
06039    txtdes = mkstemp(tmptxtfile);
06040    if (txtdes < 0) {
06041       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
06042       /* Something screwed up.  Abort. */
06043       ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
06044       free_user(recipient);
06045       return -1;
06046    }
06047 
06048    /* Store information */
06049    txt = fdopen(txtdes, "w+");
06050    if (txt) {
06051       generate_msg_id(msg_id);
06052       get_date(date, sizeof(date));
06053       fprintf(txt,
06054          ";\n"
06055          "; Message Information file\n"
06056          ";\n"
06057          "[message]\n"
06058          "origmailbox=%s\n"
06059          "context=%s\n"
06060          "macrocontext=%s\n"
06061          "exten=%s\n"
06062          "rdnis=Unknown\n"
06063          "priority=%d\n"
06064          "callerchan=%s\n"
06065          "callerid=%s\n"
06066          "origdate=%s\n"
06067          "origtime=%ld\n"
06068          "category=%s\n"
06069          "msg_id=%s\n"
06070          "flag=\n" /* flags not supported in copy from file yet */
06071          "duration=%d\n", /* Don't have any reliable way to get duration of file. */
06072 
06073          recdata->mailbox,
06074          S_OR(recdata->call_context, ""),
06075          S_OR(recdata->call_macrocontext, ""),
06076          S_OR(recdata->call_extension, ""),
06077          recdata->call_priority,
06078          S_OR(recdata->call_callerchan, "Unknown"),
06079          S_OR(recdata->call_callerid, "Unknown"),
06080          date, (long) time(NULL),
06081          S_OR(category, ""),
06082          msg_id,
06083          duration);
06084 
06085       /* Since we are recording from a file, we shouldn't need to do anything else with
06086        * this txt file */
06087       fclose(txt);
06088 
06089    } else {
06090       ast_log(LOG_WARNING, "Error opening text file for output\n");
06091       if (ast_check_realtime("voicemail_data")) {
06092          ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06093       }
06094       free_user(recipient);
06095       return -1;
06096    }
06097 
06098    /* At this point, the actual creation of a voicemail message should be finished.
06099     * Now we just need to copy the files being recorded into the receiving folder. */
06100 
06101    create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
06102 
06103 #ifdef IMAP_STORAGE
06104    /* make recipient info into an inboxcount friendly string */
06105    snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
06106 
06107    /* Is ext a mailbox? */
06108    /* must open stream for this user to get info! */
06109    res = inboxcount(ext_context, &newmsgs, &oldmsgs);
06110    if (res < 0) {
06111       ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
06112       free_user(recipient);
06113       unlink(tmptxtfile);
06114       return -1;
06115    }
06116    if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
06117    /* It is possible under certain circumstances that inboxcount did not
06118     * create a vm_state when it was needed. This is a catchall which will
06119     * rarely be used.
06120     */
06121       if (!(vms = create_vm_state_from_user(recipient))) {
06122          ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
06123          free_user(recipient);
06124          unlink(tmptxtfile);
06125          return -1;
06126       }
06127    }
06128    vms->newmessages++;
06129 
06130    /* here is a big difference! We add one to it later */
06131    msgnum = newmsgs + oldmsgs;
06132    ast_debug(3, "Messagecount set to %d\n", msgnum);
06133    snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
06134 
06135    /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
06136     * Note that imap_check_limits raises inprocess_count if successful */
06137    if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
06138       ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
06139       inprocess_count(recipient->mailbox, recipient->context, -1);
06140       free_user(recipient);
06141       unlink(tmptxtfile);
06142       return -1;
06143    }
06144 
06145 #else
06146 
06147    /* Check to see if the mailbox is full for ODBC/File storage */
06148    ast_debug(3, "mailbox = %d : inprocess = %d\n", count_messages(recipient, dir),
06149       inprocess_count(recipient->mailbox, recipient->context, 0));
06150    if (count_messages(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
06151       ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
06152       inprocess_count(recipient->mailbox, recipient->context, -1);
06153       free_user(recipient);
06154       unlink(tmptxtfile);
06155       return -1;
06156    }
06157 
06158    msgnum = last_message_index(recipient, dir) + 1;
06159 #endif
06160 
06161    /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
06162     * We need to unlock it before we return. */
06163    if (vm_lock_path(dir)) {
06164       ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06165       /* Delete files */
06166       ast_filedelete(tmptxtfile, NULL);
06167       unlink(tmptxtfile);
06168       free_user(recipient);
06169       return -1;
06170    }
06171 
06172    make_file(destination, sizeof(destination), dir, msgnum);
06173 
06174    make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
06175 
06176    if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
06177       ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
06178 
06179       inprocess_count(recipient->mailbox, recipient->context, -1);
06180       ast_unlock_path(dir);
06181       free_user(recipient);
06182       unlink(tmptxtfile);
06183       return -1;
06184    }
06185 
06186    /* Alright, try to copy to the destination folder now. */
06187    if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
06188       ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
06189       inprocess_count(recipient->mailbox, recipient->context, -1);
06190       ast_unlock_path(dir);
06191       free_user(recipient);
06192       unlink(tmptxtfile);
06193       return -1;
06194    }
06195 
06196    snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
06197    rename(tmptxtfile, desttxtfile);
06198 
06199    if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
06200       ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
06201    }
06202 
06203 
06204    ast_unlock_path(dir);
06205    inprocess_count(recipient->mailbox, recipient->context, -1);
06206 
06207    /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
06208     * to do both with one line and is also safe to use with file storage mode. Also, if we are using ODBC, now is a good
06209     * time to create the voicemail database entry. */
06210    if (ast_fileexists(destination, NULL, NULL) > 0) {
06211       if (ast_check_realtime("voicemail_data")) {
06212          get_date(date, sizeof(date));
06213          ast_store_realtime("voicemail_data",
06214             "origmailbox", recdata->mailbox,
06215             "context", S_OR(recdata->context, ""),
06216             "macrocontext", S_OR(recdata->call_macrocontext, ""),
06217             "exten", S_OR(recdata->call_extension, ""),
06218             "priority", recdata->call_priority,
06219             "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
06220             "callerid", S_OR(recdata->call_callerid, "Unknown"),
06221             "origdate", date,
06222             "origtime", time(NULL),
06223             "category", S_OR(category, ""),
06224             "filename", tmptxtfile,
06225             "duration", duration,
06226             SENTINEL);
06227       }
06228 
06229       STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
06230    }
06231 
06232    free_user(recipient);
06233    unlink(tmptxtfile);
06234    return 0;
06235 }
06236 
06237 /*!
06238  * \brief Prompts the user and records a voicemail to a mailbox.
06239  * \param chan
06240  * \param ext
06241  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
06242  * 
06243  * 
06244  * 
06245  * \return zero on success, -1 on error.
06246  */
06247 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
06248 {
06249 #ifdef IMAP_STORAGE
06250    int newmsgs, oldmsgs;
06251 #else
06252    char urgdir[PATH_MAX];
06253 #endif
06254    char txtfile[PATH_MAX];
06255    char tmptxtfile[PATH_MAX];
06256    struct vm_state *vms = NULL;
06257    char callerid[256];
06258    FILE *txt;
06259    char date[256];
06260    int txtdes;
06261    int res = 0;
06262    int msgnum;
06263    int duration = 0;
06264    int sound_duration = 0;
06265    int ausemacro = 0;
06266    int ousemacro = 0;
06267    int ouseexten = 0;
06268    char tmpdur[16];
06269    char priority[16];
06270    char origtime[16];
06271    char dir[PATH_MAX];
06272    char tmpdir[PATH_MAX];
06273    char fn[PATH_MAX];
06274    char prefile[PATH_MAX] = "";
06275    char tempfile[PATH_MAX] = "";
06276    char ext_context[256] = "";
06277    char fmt[80];
06278    char *context;
06279    char ecodes[17] = "#";
06280    struct ast_str *tmp = ast_str_create(16);
06281    char *tmpptr;
06282    struct ast_vm_user *vmu;
06283    struct ast_vm_user svm;
06284    const char *category = NULL;
06285    const char *code;
06286    const char *alldtmf = "0123456789ABCD*#";
06287    char flag[80];
06288 
06289    if (!tmp) {
06290       return -1;
06291    }
06292 
06293    ast_str_set(&tmp, 0, "%s", ext);
06294    ext = ast_str_buffer(tmp);
06295    if ((context = strchr(ext, '@'))) {
06296       *context++ = '\0';
06297       tmpptr = strchr(context, '&');
06298    } else {
06299       tmpptr = strchr(ext, '&');
06300    }
06301 
06302    if (tmpptr)
06303       *tmpptr++ = '\0';
06304 
06305    ast_channel_lock(chan);
06306    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06307       category = ast_strdupa(category);
06308    }
06309    ast_channel_unlock(chan);
06310 
06311    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
06312       ast_copy_string(flag, "Urgent", sizeof(flag));
06313    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
06314       ast_copy_string(flag, "PRIORITY", sizeof(flag));
06315    } else {
06316       flag[0] = '\0';
06317    }
06318 
06319    ast_debug(3, "Before find_user\n");
06320    if (!(vmu = find_user(&svm, context, ext))) {
06321       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
06322       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06323       ast_free(tmp);
06324       return res;
06325    }
06326    /* Setup pre-file if appropriate */
06327    if (strcmp(vmu->context, "default"))
06328       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
06329    else
06330       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
06331 
06332    /* Set the path to the prefile. Will be one of 
06333       VM_SPOOL_DIRcontext/ext/busy
06334       VM_SPOOL_DIRcontext/ext/unavail
06335       Depending on the flag set in options.
06336    */
06337    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
06338       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
06339    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
06340       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
06341    }
06342    /* Set the path to the tmpfile as
06343       VM_SPOOL_DIR/context/ext/temp
06344       and attempt to create the folder structure.
06345    */
06346    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
06347    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
06348       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
06349       ast_free(tmp);
06350       return -1;
06351    }
06352    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
06353    if (ast_fileexists(tempfile, NULL, NULL) > 0)
06354       ast_copy_string(prefile, tempfile, sizeof(prefile));
06355 
06356    DISPOSE(tempfile, -1);
06357    /* It's easier just to try to make it than to check for its existence */
06358 #ifndef IMAP_STORAGE
06359    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
06360 #else
06361    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
06362    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
06363       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
06364    }
06365 #endif
06366 
06367    /* Check current or macro-calling context for special extensions */
06368    if (ast_test_flag(vmu, VM_OPERATOR)) {
06369       if (!ast_strlen_zero(vmu->exit)) {
06370          if (ast_exists_extension(chan, vmu->exit, "o", 1,
06371             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06372             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
06373             ouseexten = 1;
06374          }
06375       } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
06376          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06377          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
06378          ouseexten = 1;
06379       } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
06380          && ast_exists_extension(chan, ast_channel_macrocontext(chan), "o", 1,
06381             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06382          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
06383          ousemacro = 1;
06384       }
06385    }
06386 
06387    if (!ast_strlen_zero(vmu->exit)) {
06388       if (ast_exists_extension(chan, vmu->exit, "a", 1,
06389          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06390          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
06391       }
06392    } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
06393       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06394       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
06395    } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
06396       && ast_exists_extension(chan, ast_channel_macrocontext(chan), "a", 1,
06397          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06398       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
06399       ausemacro = 1;
06400    }
06401 
06402    if (ast_test_flag(options, OPT_DTMFEXIT)) {
06403       for (code = alldtmf; *code; code++) {
06404          char e[2] = "";
06405          e[0] = *code;
06406          if (strchr(ecodes, e[0]) == NULL
06407             && ast_canmatch_extension(chan,
06408                (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
06409                e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
06410             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
06411          }
06412       }
06413    }
06414 
06415    /* Play the beginning intro if desired */
06416    if (!ast_strlen_zero(prefile)) {
06417 #ifdef ODBC_STORAGE
06418       int success = 
06419 #endif
06420          RETRIEVE(prefile, -1, ext, context);
06421       if (ast_fileexists(prefile, NULL, NULL) > 0) {
06422          if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) 
06423             res = ast_waitstream(chan, ecodes);
06424 #ifdef ODBC_STORAGE
06425          if (success == -1) {
06426             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
06427             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
06428             store_file(prefile, vmu->mailbox, vmu->context, -1);
06429          }
06430 #endif
06431       } else {
06432          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
06433          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
06434       }
06435       DISPOSE(prefile, -1);
06436       if (res < 0) {
06437          ast_debug(1, "Hang up during prefile playback\n");
06438          free_user(vmu);
06439          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06440          ast_free(tmp);
06441          return -1;
06442       }
06443    }
06444    if (res == '#') {
06445       /* On a '#' we skip the instructions */
06446       ast_set_flag(options, OPT_SILENT);
06447       res = 0;
06448    }
06449    /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
06450    if (vmu->maxmsg == 0) {
06451       ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
06452       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06453       goto leave_vm_out;
06454    }
06455    if (!res && !ast_test_flag(options, OPT_SILENT)) {
06456       res = ast_stream_and_wait(chan, INTRO, ecodes);
06457       if (res == '#') {
06458          ast_set_flag(options, OPT_SILENT);
06459          res = 0;
06460       }
06461    }
06462    if (res > 0)
06463       ast_stopstream(chan);
06464    /* Check for a '*' here in case the caller wants to escape from voicemail to something
06465     other than the operator -- an automated attendant or mailbox login for example */
06466    if (res == '*') {
06467       ast_channel_exten_set(chan, "a");
06468       if (!ast_strlen_zero(vmu->exit)) {
06469          ast_channel_context_set(chan, vmu->exit);
06470       } else if (ausemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
06471          ast_channel_context_set(chan, ast_channel_macrocontext(chan));
06472       }
06473       ast_channel_priority_set(chan, 0);
06474       free_user(vmu);
06475       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
06476       ast_free(tmp);
06477       return 0;
06478    }
06479 
06480    /* Check for a '0' here */
06481    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
06482    transfer:
06483       if (ouseexten || ousemacro) {
06484          ast_channel_exten_set(chan, "o");
06485          if (!ast_strlen_zero(vmu->exit)) {
06486             ast_channel_context_set(chan, vmu->exit);
06487          } else if (ousemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
06488             ast_channel_context_set(chan, ast_channel_macrocontext(chan));
06489          }
06490          ast_play_and_wait(chan, "transfer");
06491          ast_channel_priority_set(chan, 0);
06492          free_user(vmu);
06493          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
06494       }
06495       ast_free(tmp);
06496       return OPERATOR_EXIT;
06497    }
06498 
06499    /* Allow all other digits to exit Voicemail and return to the dialplan */
06500    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
06501       if (!ast_strlen_zero(options->exitcontext)) {
06502          ast_channel_context_set(chan, options->exitcontext);
06503       }
06504       free_user(vmu);
06505       ast_free(tmp);
06506       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
06507       return res;
06508    }
06509 
06510    if (res < 0) {
06511       free_user(vmu);
06512       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06513       ast_free(tmp);
06514       return -1;
06515    }
06516    /* The meat of recording the message...  All the announcements and beeps have been played*/
06517    ast_copy_string(fmt, vmfmts, sizeof(fmt));
06518    if (!ast_strlen_zero(fmt)) {
06519       char msg_id[MSG_ID_LEN] = "";
06520       msgnum = 0;
06521 
06522 #ifdef IMAP_STORAGE
06523       /* Is ext a mailbox? */
06524       /* must open stream for this user to get info! */
06525       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
06526       if (res < 0) {
06527          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
06528          ast_free(tmp);
06529          return -1;
06530       }
06531       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
06532       /* It is possible under certain circumstances that inboxcount did not
06533        * create a vm_state when it was needed. This is a catchall which will
06534        * rarely be used.
06535        */
06536          if (!(vms = create_vm_state_from_user(vmu))) {
06537             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
06538             ast_free(tmp);
06539             return -1;
06540          }
06541       }
06542       vms->newmessages++;
06543 
06544       /* here is a big difference! We add one to it later */
06545       msgnum = newmsgs + oldmsgs;
06546       ast_debug(3, "Messagecount set to %d\n", msgnum);
06547       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
06548       /* set variable for compatibility */
06549       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06550 
06551       if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
06552          goto leave_vm_out;
06553       }
06554 #else
06555       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
06556          res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
06557          if (!res)
06558             res = ast_waitstream(chan, "");
06559          ast_log(AST_LOG_WARNING, "No more messages possible\n");
06560          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06561          inprocess_count(vmu->mailbox, vmu->context, -1);
06562          goto leave_vm_out;
06563       }
06564 
06565 #endif
06566       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
06567       txtdes = mkstemp(tmptxtfile);
06568       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
06569       if (txtdes < 0) {
06570          res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
06571          if (!res)
06572             res = ast_waitstream(chan, "");
06573          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
06574          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06575          inprocess_count(vmu->mailbox, vmu->context, -1);
06576          goto leave_vm_out;
06577       }
06578 
06579       /* Now play the beep once we have the message number for our next message. */
06580       if (res >= 0) {
06581          /* Unless we're *really* silent, try to send the beep */
06582          res = ast_stream_and_wait(chan, "beep", "");
06583       }
06584             
06585       /* Store information in real-time storage */
06586       if (ast_check_realtime("voicemail_data")) {
06587          snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
06588          snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
06589          get_date(date, sizeof(date));
06590          ast_callerid_merge(callerid, sizeof(callerid),
06591             S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
06592             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
06593             "Unknown");
06594          ast_store_realtime("voicemail_data",
06595             "origmailbox", ext,
06596             "context", ast_channel_context(chan),
06597             "macrocontext", ast_channel_macrocontext(chan),
06598             "exten", ast_channel_exten(chan),
06599             "priority", priority,
06600             "callerchan", ast_channel_name(chan),
06601             "callerid", callerid,
06602             "origdate", date,
06603             "origtime", origtime,
06604             "category", S_OR(category, ""),
06605             "filename", tmptxtfile,
06606             SENTINEL);
06607       }
06608 
06609       /* Store information */
06610       txt = fdopen(txtdes, "w+");
06611       if (txt) {
06612          generate_msg_id(msg_id);
06613          get_date(date, sizeof(date));
06614          ast_callerid_merge(callerid, sizeof(callerid),
06615             S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
06616             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
06617             "Unknown");
06618          fprintf(txt, 
06619             ";\n"
06620             "; Message Information file\n"
06621             ";\n"
06622             "[message]\n"
06623             "origmailbox=%s\n"
06624             "context=%s\n"
06625             "macrocontext=%s\n"
06626             "exten=%s\n"
06627             "rdnis=%s\n"
06628             "priority=%d\n"
06629             "callerchan=%s\n"
06630             "callerid=%s\n"
06631             "origdate=%s\n"
06632             "origtime=%ld\n"
06633             "category=%s\n"
06634             "msg_id=%s\n",
06635             ext,
06636             ast_channel_context(chan),
06637             ast_channel_macrocontext(chan), 
06638             ast_channel_exten(chan),
06639             S_COR(ast_channel_redirecting(chan)->from.number.valid,
06640                ast_channel_redirecting(chan)->from.number.str, "unknown"),
06641             ast_channel_priority(chan),
06642             ast_channel_name(chan),
06643             callerid,
06644             date, (long) time(NULL),
06645             category ? category : "",
06646             msg_id);
06647       } else {
06648          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
06649          inprocess_count(vmu->mailbox, vmu->context, -1);
06650          if (ast_check_realtime("voicemail_data")) {
06651             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06652          }
06653          res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
06654          goto leave_vm_out;
06655       }
06656       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id);
06657 
06658       if (txt) {
06659          fprintf(txt, "flag=%s\n", flag);
06660          if (sound_duration < vmu->minsecs) {
06661             fclose(txt);
06662             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
06663             ast_filedelete(tmptxtfile, NULL);
06664             unlink(tmptxtfile);
06665             if (ast_check_realtime("voicemail_data")) {
06666                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06667             }
06668             inprocess_count(vmu->mailbox, vmu->context, -1);
06669          } else {
06670             fprintf(txt, "duration=%d\n", duration);
06671             fclose(txt);
06672             if (vm_lock_path(dir)) {
06673                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
06674                /* Delete files */
06675                ast_filedelete(tmptxtfile, NULL);
06676                unlink(tmptxtfile);
06677                inprocess_count(vmu->mailbox, vmu->context, -1);
06678             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
06679                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
06680                unlink(tmptxtfile);
06681                ast_unlock_path(dir);
06682                inprocess_count(vmu->mailbox, vmu->context, -1);
06683                if (ast_check_realtime("voicemail_data")) {
06684                   ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
06685                }
06686             } else {
06687 #ifndef IMAP_STORAGE
06688                msgnum = last_message_index(vmu, dir) + 1;
06689 #endif
06690                make_file(fn, sizeof(fn), dir, msgnum);
06691 
06692                /* assign a variable with the name of the voicemail file */ 
06693 #ifndef IMAP_STORAGE
06694                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
06695 #else
06696                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
06697 #endif
06698 
06699                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
06700                ast_filerename(tmptxtfile, fn, NULL);
06701                rename(tmptxtfile, txtfile);
06702                inprocess_count(vmu->mailbox, vmu->context, -1);
06703 
06704                /* Properly set permissions on voicemail text descriptor file.
06705                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
06706                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
06707                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
06708 
06709                ast_unlock_path(dir);
06710                if (ast_check_realtime("voicemail_data")) {
06711                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
06712                   ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
06713                }
06714                /* We must store the file first, before copying the message, because
06715                 * ODBC storage does the entire copy with SQL.
06716                 */
06717                if (ast_fileexists(fn, NULL, NULL) > 0) {
06718                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
06719                }
06720 
06721                /* Are there to be more recipients of this message? */
06722                while (tmpptr) {
06723                   struct ast_vm_user recipu, *recip;
06724                   char *exten, *cntx;
06725 
06726                   exten = strsep(&tmpptr, "&");
06727                   cntx = strchr(exten, '@');
06728                   if (cntx) {
06729                      *cntx = '\0';
06730                      cntx++;
06731                   }
06732                   if ((recip = find_user(&recipu, cntx, exten))) {
06733                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
06734                      free_user(recip);
06735                   }
06736                }
06737 #ifndef IMAP_STORAGE
06738                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
06739                   /* Move the message from INBOX to Urgent folder if this is urgent! */
06740                   char sfn[PATH_MAX];
06741                   char dfn[PATH_MAX];
06742                   int x;
06743                   /* It's easier just to try to make it than to check for its existence */
06744                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
06745                   x = last_message_index(vmu, urgdir) + 1;
06746                   make_file(sfn, sizeof(sfn), dir, msgnum);
06747                   make_file(dfn, sizeof(dfn), urgdir, x);
06748                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
06749                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
06750                   /* Notification must happen for this new message in Urgent folder, not INBOX */
06751                   ast_copy_string(fn, dfn, sizeof(fn));
06752                   msgnum = x;
06753                }
06754 #endif
06755                /* Notification needs to happen after the copy, though. */
06756                if (ast_fileexists(fn, NULL, NULL)) {
06757 #ifdef IMAP_STORAGE
06758                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
06759                      S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
06760                      S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
06761                      flag);
06762 #else
06763                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
06764                      S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
06765                      S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
06766                      flag);
06767 #endif
06768                }
06769 
06770                /* Disposal needs to happen after the optional move and copy */
06771                if (ast_fileexists(fn, NULL, NULL)) {
06772                   DISPOSE(dir, msgnum);
06773                }
06774             }
06775          }
06776       } else {
06777          inprocess_count(vmu->mailbox, vmu->context, -1);
06778       }
06779       if (res == '0') {
06780          goto transfer;
06781       } else if (res > 0 && res != 't')
06782          res = 0;
06783 
06784       if (sound_duration < vmu->minsecs)
06785          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
06786          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
06787       else
06788          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
06789    } else
06790       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
06791 leave_vm_out:
06792    free_user(vmu);
06793 
06794 #ifdef IMAP_STORAGE
06795    /* expunge message - use UID Expunge if supported on IMAP server*/
06796    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
06797    if (expungeonhangup == 1) {
06798       ast_mutex_lock(&vms->lock);
06799 #ifdef HAVE_IMAP_TK2006
06800       if (LEVELUIDPLUS (vms->mailstream)) {
06801          mail_expunge_full(vms->mailstream, NIL, EX_UID);
06802       } else 
06803 #endif
06804          mail_expunge(vms->mailstream);
06805       ast_mutex_unlock(&vms->lock);
06806    }
06807 #endif
06808 
06809    ast_free(tmp);
06810    return res;
06811 }
06812 
06813 #if !defined(IMAP_STORAGE)
06814 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
06815 {
06816    /* we know the actual number of messages, so stop process when number is hit */
06817 
06818    int x, dest;
06819    char sfn[PATH_MAX];
06820    char dfn[PATH_MAX];
06821 
06822    if (vm_lock_path(dir)) {
06823       return ERROR_LOCK_PATH;
06824    }
06825 
06826    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
06827       make_file(sfn, sizeof(sfn), dir, x);
06828       if (EXISTS(dir, x, sfn, NULL)) {
06829 
06830          if (x != dest) {
06831             make_file(dfn, sizeof(dfn), dir, dest);
06832             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
06833          }
06834 
06835          dest++;
06836       }
06837    }
06838    ast_unlock_path(dir);
06839 
06840    return dest;
06841 }
06842 #endif
06843 
06844 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
06845 {
06846    int d;
06847    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
06848    return d;
06849 }
06850 
06851 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
06852 {
06853 #ifdef IMAP_STORAGE
06854    /* we must use mbox(x) folder names, and copy the message there */
06855    /* simple. huh? */
06856    char sequence[10];
06857    char mailbox[256];
06858    int res;
06859 
06860    /* get the real IMAP message number for this message */
06861    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
06862 
06863    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
06864    ast_mutex_lock(&vms->lock);
06865    /* if save to Old folder, put in INBOX as read */
06866    if (box == OLD_FOLDER) {
06867       mail_setflag(vms->mailstream, sequence, "\\Seen");
06868       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
06869    } else if (box == NEW_FOLDER) {
06870       mail_setflag(vms->mailstream, sequence, "\\Unseen");
06871       mail_clearflag(vms->mailstream, sequence, "\\Seen");
06872    }
06873    if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
06874       ast_mutex_unlock(&vms->lock);
06875       return 0;
06876    }
06877    /* Create the folder if it don't exist */
06878    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
06879    ast_debug(5, "Checking if folder exists: %s\n", mailbox);
06880    if (mail_create(vms->mailstream, mailbox) == NIL)
06881       ast_debug(5, "Folder exists.\n");
06882    else
06883       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
06884    if (move) {
06885       res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
06886    } else {
06887       res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
06888    }
06889    ast_mutex_unlock(&vms->lock);
06890    return res;
06891 #else
06892    char *dir = vms->curdir;
06893    char *username = vms->username;
06894    char *context = vmu->context;
06895    char sfn[PATH_MAX];
06896    char dfn[PATH_MAX];
06897    char ddir[PATH_MAX];
06898    const char *dbox = mbox(vmu, box);
06899    int x, i;
06900    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
06901 
06902    if (vm_lock_path(ddir))
06903       return ERROR_LOCK_PATH;
06904 
06905    x = last_message_index(vmu, ddir) + 1;
06906 
06907    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
06908       x--;
06909       for (i = 1; i <= x; i++) {
06910          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
06911          make_file(sfn, sizeof(sfn), ddir, i);
06912          make_file(dfn, sizeof(dfn), ddir, i - 1);
06913          if (EXISTS(ddir, i, sfn, NULL)) {
06914             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
06915          } else
06916             break;
06917       }
06918    } else {
06919       if (x >= vmu->maxmsg) {
06920          ast_unlock_path(ddir);
06921          return -1;
06922       }
06923    }
06924    make_file(sfn, sizeof(sfn), dir, msg);
06925    make_file(dfn, sizeof(dfn), ddir, x);
06926    if (strcmp(sfn, dfn)) {
06927       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
06928    }
06929    ast_unlock_path(ddir);
06930 
06931    if (newmsg) {
06932       *newmsg = x;
06933    }
06934    return 0;
06935 #endif
06936 }
06937 
06938 static int adsi_logo(unsigned char *buf)
06939 {
06940    int bytes = 0;
06941    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
06942    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
06943    return bytes;
06944 }
06945 
06946 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
06947 {
06948    unsigned char buf[256];
06949    int bytes = 0;
06950    int x;
06951    char num[5];
06952 
06953    *useadsi = 0;
06954    bytes += ast_adsi_data_mode(buf + bytes);
06955    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06956 
06957    bytes = 0;
06958    bytes += adsi_logo(buf);
06959    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06960 #ifdef DISPLAY
06961    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
06962 #endif
06963    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06964    bytes += ast_adsi_data_mode(buf + bytes);
06965    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06966 
06967    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
06968       bytes = 0;
06969       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
06970       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
06971       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06972       bytes += ast_adsi_voice_mode(buf + bytes, 0);
06973       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06974       return 0;
06975    }
06976 
06977 #ifdef DISPLAY
06978    /* Add a dot */
06979    bytes = 0;
06980    bytes += ast_adsi_logo(buf);
06981    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
06982    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
06983    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06984    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06985 #endif
06986    bytes = 0;
06987    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
06988    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
06989    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
06990    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
06991    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
06992    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
06993    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
06994 
06995 #ifdef DISPLAY
06996    /* Add another dot */
06997    bytes = 0;
06998    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
06999    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07000 
07001    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07002    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07003 #endif
07004 
07005    bytes = 0;
07006    /* These buttons we load but don't use yet */
07007    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
07008    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
07009    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
07010    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
07011    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
07012    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
07013    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
07014 
07015 #ifdef DISPLAY
07016    /* Add another dot */
07017    bytes = 0;
07018    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
07019    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07020    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07021 #endif
07022 
07023    bytes = 0;
07024    for (x = 0; x < 5; x++) {
07025       snprintf(num, sizeof(num), "%d", x);
07026       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
07027    }
07028    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
07029    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
07030 
07031 #ifdef DISPLAY
07032    /* Add another dot */
07033    bytes = 0;
07034    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
07035    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07036    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07037 #endif
07038 
07039    if (ast_adsi_end_download(chan)) {
07040       bytes = 0;
07041       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
07042       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
07043       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07044       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07045       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07046       return 0;
07047    }
07048    bytes = 0;
07049    bytes += ast_adsi_download_disconnect(buf + bytes);
07050    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07051    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
07052 
07053    ast_debug(1, "Done downloading scripts...\n");
07054 
07055 #ifdef DISPLAY
07056    /* Add last dot */
07057    bytes = 0;
07058    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
07059    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07060 #endif
07061    ast_debug(1, "Restarting session...\n");
07062 
07063    bytes = 0;
07064    /* Load the session now */
07065    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
07066       *useadsi = 1;
07067       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
07068    } else
07069       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
07070 
07071    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07072    return 0;
07073 }
07074 
07075 static void adsi_begin(struct ast_channel *chan, int *useadsi)
07076 {
07077    int x;
07078    if (!ast_adsi_available(chan))
07079       return;
07080    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
07081    if (x < 0)
07082       return;
07083    if (!x) {
07084       if (adsi_load_vmail(chan, useadsi)) {
07085          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
07086          return;
07087       }
07088    } else
07089       *useadsi = 1;
07090 }
07091 
07092 static void adsi_login(struct ast_channel *chan)
07093 {
07094    unsigned char buf[256];
07095    int bytes = 0;
07096    unsigned char keys[8];
07097    int x;
07098    if (!ast_adsi_available(chan))
07099       return;
07100 
07101    for (x = 0; x < 8; x++)
07102       keys[x] = 0;
07103    /* Set one key for next */
07104    keys[3] = ADSI_KEY_APPS + 3;
07105 
07106    bytes += adsi_logo(buf + bytes);
07107    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
07108    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
07109    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07110    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
07111    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
07112    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
07113    bytes += ast_adsi_set_keys(buf + bytes, keys);
07114    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07115    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07116 }
07117 
07118 static void adsi_password(struct ast_channel *chan)
07119 {
07120    unsigned char buf[256];
07121    int bytes = 0;
07122    unsigned char keys[8];
07123    int x;
07124    if (!ast_adsi_available(chan))
07125       return;
07126 
07127    for (x = 0; x < 8; x++)
07128       keys[x] = 0;
07129    /* Set one key for next */
07130    keys[3] = ADSI_KEY_APPS + 3;
07131 
07132    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07133    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
07134    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
07135    bytes += ast_adsi_set_keys(buf + bytes, keys);
07136    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07137    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07138 }
07139 
07140 static void adsi_folders(struct ast_channel *chan, int start, char *label)
07141 {
07142    unsigned char buf[256];
07143    int bytes = 0;
07144    unsigned char keys[8];
07145    int x, y;
07146 
07147    if (!ast_adsi_available(chan))
07148       return;
07149 
07150    for (x = 0; x < 5; x++) {
07151       y = ADSI_KEY_APPS + 12 + start + x;
07152       if (y > ADSI_KEY_APPS + 12 + 4)
07153          y = 0;
07154       keys[x] = ADSI_KEY_SKT | y;
07155    }
07156    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
07157    keys[6] = 0;
07158    keys[7] = 0;
07159 
07160    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
07161    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
07162    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07163    bytes += ast_adsi_set_keys(buf + bytes, keys);
07164    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07165 
07166    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07167 }
07168 
07169 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
07170 {
07171    int bytes = 0;
07172    unsigned char buf[256]; 
07173    char buf1[256], buf2[256];
07174    char fn2[PATH_MAX];
07175 
07176    char cid[256] = "";
07177    char *val;
07178    char *name, *num;
07179    char datetime[21] = "";
07180    FILE *f;
07181 
07182    unsigned char keys[8];
07183 
07184    int x;
07185 
07186    if (!ast_adsi_available(chan))
07187       return;
07188 
07189    /* Retrieve important info */
07190    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
07191    f = fopen(fn2, "r");
07192    if (f) {
07193       while (!feof(f)) {   
07194          if (!fgets((char *) buf, sizeof(buf), f)) {
07195             continue;
07196          }
07197          if (!feof(f)) {
07198             char *stringp = NULL;
07199             stringp = (char *) buf;
07200             strsep(&stringp, "=");
07201             val = strsep(&stringp, "=");
07202             if (!ast_strlen_zero(val)) {
07203                if (!strcmp((char *) buf, "callerid"))
07204                   ast_copy_string(cid, val, sizeof(cid));
07205                if (!strcmp((char *) buf, "origdate"))
07206                   ast_copy_string(datetime, val, sizeof(datetime));
07207             }
07208          }
07209       }
07210       fclose(f);
07211    }
07212    /* New meaning for keys */
07213    for (x = 0; x < 5; x++)
07214       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
07215    keys[6] = 0x0;
07216    keys[7] = 0x0;
07217 
07218    if (!vms->curmsg) {
07219       /* No prev key, provide "Folder" instead */
07220       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
07221    }
07222    if (vms->curmsg >= vms->lastmsg) {
07223       /* If last message ... */
07224       if (vms->curmsg) {
07225          /* but not only message, provide "Folder" instead */
07226          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
07227          bytes += ast_adsi_voice_mode(buf + bytes, 0);
07228 
07229       } else {
07230          /* Otherwise if only message, leave blank */
07231          keys[3] = 1;
07232       }
07233    }
07234 
07235    if (!ast_strlen_zero(cid)) {
07236       ast_callerid_parse(cid, &name, &num);
07237       if (!name)
07238          name = num;
07239    } else {
07240       name = "Unknown Caller";
07241    }
07242 
07243    /* If deleted, show "undeleted" */
07244 #ifdef IMAP_STORAGE
07245    ast_mutex_lock(&vms->lock);
07246 #endif
07247    if (vms->deleted[vms->curmsg]) {
07248       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
07249    }
07250 #ifdef IMAP_STORAGE
07251    ast_mutex_unlock(&vms->lock);
07252 #endif
07253 
07254    /* Except "Exit" */
07255    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
07256    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
07257       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
07258    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
07259 
07260    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
07261    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
07262    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
07263    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
07264    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07265    bytes += ast_adsi_set_keys(buf + bytes, keys);
07266    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07267 
07268    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07269 }
07270 
07271 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
07272 {
07273    int bytes = 0;
07274    unsigned char buf[256];
07275    unsigned char keys[8];
07276 
07277    int x;
07278 
07279    if (!ast_adsi_available(chan))
07280       return;
07281 
07282    /* New meaning for keys */
07283    for (x = 0; x < 5; x++)
07284       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
07285 
07286    keys[6] = 0x0;
07287    keys[7] = 0x0;
07288 
07289    if (!vms->curmsg) {
07290       /* No prev key, provide "Folder" instead */
07291       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
07292    }
07293    if (vms->curmsg >= vms->lastmsg) {
07294       /* If last message ... */
07295       if (vms->curmsg) {
07296          /* but not only message, provide "Folder" instead */
07297          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
07298       } else {
07299          /* Otherwise if only message, leave blank */
07300          keys[3] = 1;
07301       }
07302    }
07303 
07304    /* If deleted, show "undeleted" */
07305 #ifdef IMAP_STORAGE
07306    ast_mutex_lock(&vms->lock);
07307 #endif
07308    if (vms->deleted[vms->curmsg]) {
07309       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
07310    }
07311 #ifdef IMAP_STORAGE
07312    ast_mutex_unlock(&vms->lock);
07313 #endif
07314 
07315    /* Except "Exit" */
07316    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
07317    bytes += ast_adsi_set_keys(buf + bytes, keys);
07318    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07319 
07320    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07321 }
07322 
07323 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
07324 {
07325    unsigned char buf[256] = "";
07326    char buf1[256] = "", buf2[256] = "";
07327    int bytes = 0;
07328    unsigned char keys[8];
07329    int x;
07330 
07331    char *newm = (vms->newmessages == 1) ? "message" : "messages";
07332    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
07333    if (!ast_adsi_available(chan))
07334       return;
07335    if (vms->newmessages) {
07336       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
07337       if (vms->oldmessages) {
07338          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
07339          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
07340       } else {
07341          snprintf(buf2, sizeof(buf2), "%s.", newm);
07342       }
07343    } else if (vms->oldmessages) {
07344       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
07345       snprintf(buf2, sizeof(buf2), "%s.", oldm);
07346    } else {
07347       strcpy(buf1, "You have no messages.");
07348       buf2[0] = ' ';
07349       buf2[1] = '\0';
07350    }
07351    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
07352    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
07353    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07354 
07355    for (x = 0; x < 6; x++)
07356       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
07357    keys[6] = 0;
07358    keys[7] = 0;
07359 
07360    /* Don't let them listen if there are none */
07361    if (vms->lastmsg < 0)
07362       keys[0] = 1;
07363    bytes += ast_adsi_set_keys(buf + bytes, keys);
07364 
07365    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07366 
07367    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07368 }
07369 
07370 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
07371 {
07372    unsigned char buf[256] = "";
07373    char buf1[256] = "", buf2[256] = "";
07374    int bytes = 0;
07375    unsigned char keys[8];
07376    int x;
07377 
07378    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
07379 
07380    if (!ast_adsi_available(chan))
07381       return;
07382 
07383    /* Original command keys */
07384    for (x = 0; x < 6; x++)
07385       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
07386 
07387    keys[6] = 0;
07388    keys[7] = 0;
07389 
07390    if ((vms->lastmsg + 1) < 1)
07391       keys[0] = 0;
07392 
07393    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
07394       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
07395 
07396    if (vms->lastmsg + 1)
07397       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
07398    else
07399       strcpy(buf2, "no messages.");
07400    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
07401    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
07402    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
07403    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07404    bytes += ast_adsi_set_keys(buf + bytes, keys);
07405 
07406    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07407 
07408    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07409    
07410 }
07411 
07412 /*
07413 static void adsi_clear(struct ast_channel *chan)
07414 {
07415    char buf[256];
07416    int bytes=0;
07417    if (!ast_adsi_available(chan))
07418       return;
07419    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07420    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07421 
07422    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07423 }
07424 */
07425 
07426 static void adsi_goodbye(struct ast_channel *chan)
07427 {
07428    unsigned char buf[256];
07429    int bytes = 0;
07430 
07431    if (!ast_adsi_available(chan))
07432       return;
07433    bytes += adsi_logo(buf + bytes);
07434    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
07435    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
07436    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07437    bytes += ast_adsi_voice_mode(buf + bytes, 0);
07438 
07439    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07440 }
07441 
07442 /*!\brief get_folder: Folder menu
07443  * Plays "press 1 for INBOX messages" etc.
07444  * Should possibly be internationalized
07445  */
07446 static int get_folder(struct ast_channel *chan, int start)
07447 {
07448    int x;
07449    int d;
07450    char fn[PATH_MAX];
07451    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
07452    if (d)
07453       return d;
07454    for (x = start; x < 5; x++) { /* For all folders */
07455       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
07456          return d;
07457       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
07458       if (d)
07459          return d;
07460       snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));  /* Folder name */
07461 
07462       /* The inbox folder can have its name changed under certain conditions
07463        * so this checks if the sound file exists for the inbox folder name and
07464        * if it doesn't, plays the default name instead. */
07465       if (x == 0) {
07466          if (ast_fileexists(fn, NULL, NULL)) {
07467             d = vm_play_folder_name(chan, fn);
07468          } else {
07469             ast_verb(1, "failed to find %s\n", fn);
07470             d = vm_play_folder_name(chan, "vm-INBOX");
07471          }
07472       } else {
07473          ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
07474          d = vm_play_folder_name(chan, fn);
07475       }
07476 
07477       if (d)
07478          return d;
07479       d = ast_waitfordigit(chan, 500);
07480       if (d)
07481          return d;
07482    }
07483 
07484    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
07485    if (d)
07486       return d;
07487    d = ast_waitfordigit(chan, 4000);
07488    return d;
07489 }
07490 
07491 /*!
07492  * \brief plays a prompt and waits for a keypress.
07493  * \param chan
07494  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
07495  * \param start Does not appear to be used at this time.
07496  *
07497  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
07498  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
07499  * prompting for the number inputs that correspond to the available folders.
07500  * 
07501  * \return zero on success, or -1 on error.
07502  */
07503 static int get_folder2(struct ast_channel *chan, char *fn, int start)
07504 {
07505    int res = 0;
07506    int loops = 0;
07507 
07508    res = ast_play_and_wait(chan, fn);  /* Folder name */
07509    while (((res < '0') || (res > '9')) &&
07510          (res != '#') && (res >= 0) &&
07511          loops < 4) {
07512       res = get_folder(chan, 0);
07513       loops++;
07514    }
07515    if (loops == 4) { /* give up */
07516       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
07517       return '#';
07518    }
07519    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
07520    return res;
07521 }
07522 
07523 /*!
07524  * \brief presents the option to prepend to an existing message when forwarding it.
07525  * \param chan
07526  * \param vmu
07527  * \param curdir
07528  * \param curmsg
07529  * \param vm_fmts
07530  * \param context
07531  * \param record_gain
07532  * \param duration
07533  * \param vms
07534  * \param flag 
07535  *
07536  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
07537  *
07538  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
07539  * \return zero on success, -1 on error.
07540  */
07541 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
07542          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
07543 {
07544    int cmd = 0;
07545    int retries = 0, prepend_duration = 0, already_recorded = 0;
07546    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07547    char textfile[PATH_MAX];
07548    struct ast_config *msg_cfg;
07549    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07550 #ifndef IMAP_STORAGE
07551    signed char zero_gain = 0;
07552 #else
07553    const char *msg_id = NULL;
07554 #endif
07555    const char *duration_str;
07556 
07557    /* Must always populate duration correctly */
07558    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
07559    strcpy(textfile, msgfile);
07560    strcpy(backup, msgfile);
07561    strcpy(backup_textfile, msgfile);
07562    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
07563    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
07564    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
07565 
07566    if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
07567       *duration = atoi(duration_str);
07568    } else {
07569       *duration = 0;
07570    }
07571 
07572    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
07573       if (cmd)
07574          retries = 0;
07575       switch (cmd) {
07576       case '1': 
07577 
07578 #ifdef IMAP_STORAGE
07579          /* Record new intro file */
07580          if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
07581             msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
07582          }
07583          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
07584          strncat(vms->introfn, "intro", sizeof(vms->introfn));
07585          ast_play_and_wait(chan, INTRO);
07586          ast_play_and_wait(chan, "beep");
07587          cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id);
07588          if (cmd == -1) {
07589             break;
07590          }
07591          cmd = 't';
07592 #else
07593 
07594          /* prepend a message to the current message, update the metadata and return */
07595 
07596          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
07597          strcpy(textfile, msgfile);
07598          strncat(textfile, ".txt", sizeof(textfile) - 1);
07599          *duration = 0;
07600 
07601          /* if we can't read the message metadata, stop now */
07602          if (!valid_config(msg_cfg)) {
07603             cmd = 0;
07604             break;
07605          }
07606 
07607          /* Back up the original file, so we can retry the prepend and restore it after forward. */
07608 #ifndef IMAP_STORAGE
07609          if (already_recorded) {
07610             ast_filecopy(backup, msgfile, NULL);
07611             copy(backup_textfile, textfile);
07612          }
07613          else {
07614             ast_filecopy(msgfile, backup, NULL);
07615             copy(textfile, backup_textfile);
07616          }
07617 #endif
07618          already_recorded = 1;
07619 
07620          if (record_gain)
07621             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
07622 
07623          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
07624 
07625          if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
07626             ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
07627             ast_stream_and_wait(chan, vm_prepend_timeout, "");
07628             ast_filerename(backup, msgfile, NULL);
07629          }
07630 
07631          if (record_gain)
07632             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
07633 
07634          
07635          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
07636             *duration = atoi(duration_str);
07637 
07638          if (prepend_duration) {
07639             struct ast_category *msg_cat;
07640             /* need enough space for a maximum-length message duration */
07641             char duration_buf[12];
07642 
07643             *duration += prepend_duration;
07644             msg_cat = ast_category_get(msg_cfg, "message");
07645             snprintf(duration_buf, 11, "%ld", *duration);
07646             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
07647                ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
07648             }
07649          }
07650 
07651 #endif
07652          break;
07653       case '2': 
07654          /* NULL out introfile so we know there is no intro! */
07655 #ifdef IMAP_STORAGE
07656          *vms->introfn = '\0';
07657 #endif
07658          cmd = 't';
07659          break;
07660       case '*':
07661          cmd = '*';
07662          break;
07663       default: 
07664          /* If time_out and return to menu, reset already_recorded */
07665          already_recorded = 0;
07666 
07667          cmd = ast_play_and_wait(chan, "vm-forwardoptions");
07668             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
07669          if (!cmd) {
07670             cmd = ast_play_and_wait(chan, "vm-starmain");
07671             /* "press star to return to the main menu" */
07672          }
07673          if (!cmd) {
07674             cmd = ast_waitfordigit(chan, 6000);
07675          }
07676          if (!cmd) {
07677             retries++;
07678          }
07679          if (retries > 3) {
07680             cmd = '*'; /* Let's cancel this beast */
07681          }
07682          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07683       }
07684    }
07685 
07686    if (valid_config(msg_cfg))
07687       ast_config_destroy(msg_cfg);
07688    if (prepend_duration)
07689       *duration = prepend_duration;
07690 
07691    if (already_recorded && cmd == -1) {
07692       /* restore original message if prepention cancelled */
07693       ast_filerename(backup, msgfile, NULL);
07694       rename(backup_textfile, textfile);
07695    }
07696 
07697    if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
07698       cmd = 0;
07699    return cmd;
07700 }
07701 
07702 static void queue_mwi_event(const char *box, int urgent, int new, int old)
07703 {
07704    struct ast_event *event;
07705    char *mailbox, *context;
07706 
07707    /* Strip off @default */
07708    context = mailbox = ast_strdupa(box);
07709    strsep(&context, "@");
07710    if (ast_strlen_zero(context))
07711       context = "default";
07712 
07713    if (!(event = ast_event_new(AST_EVENT_MWI,
07714          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
07715          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
07716          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
07717          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
07718          AST_EVENT_IE_END))) {
07719       return;
07720    }
07721 
07722    ast_event_queue_and_cache(event);
07723 }
07724 
07725 /*!
07726  * \brief Sends email notification that a user has a new voicemail waiting for them.
07727  * \param chan
07728  * \param vmu
07729  * \param vms
07730  * \param msgnum
07731  * \param duration
07732  * \param fmt
07733  * \param cidnum The Caller ID phone number value.
07734  * \param cidname The Caller ID name value.
07735  * \param flag
07736  *
07737  * \return zero on success, -1 on error.
07738  */
07739 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)
07740 {
07741    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
07742    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
07743    const char *category;
07744    char *myserveremail = serveremail;
07745 
07746    ast_channel_lock(chan);
07747    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
07748       category = ast_strdupa(category);
07749    }
07750    ast_channel_unlock(chan);
07751 
07752 #ifndef IMAP_STORAGE
07753    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
07754 #else
07755    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
07756 #endif
07757    make_file(fn, sizeof(fn), todir, msgnum);
07758    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
07759 
07760    if (!ast_strlen_zero(vmu->attachfmt)) {
07761       if (strstr(fmt, vmu->attachfmt))
07762          fmt = vmu->attachfmt;
07763       else
07764          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);
07765    }
07766 
07767    /* Attach only the first format */
07768    fmt = ast_strdupa(fmt);
07769    stringp = fmt;
07770    strsep(&stringp, "|");
07771 
07772    if (!ast_strlen_zero(vmu->serveremail))
07773       myserveremail = vmu->serveremail;
07774 
07775    if (!ast_strlen_zero(vmu->email)) {
07776       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
07777       char *msg_id = NULL;
07778 #ifdef IMAP_STORAGE
07779       struct ast_config *msg_cfg;
07780       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
07781       char filename[PATH_MAX];
07782 
07783       snprintf(filename, sizeof(filename), "%s.txt", fn);
07784       msg_cfg = ast_config_load(filename, config_flags);
07785       if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
07786          msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
07787          ast_config_destroy(msg_cfg);
07788       }
07789 #endif
07790 
07791       if (attach_user_voicemail)
07792          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
07793 
07794       /* XXX possible imap issue, should category be NULL XXX */
07795       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag, msg_id);
07796 
07797       if (attach_user_voicemail)
07798          DISPOSE(todir, msgnum);
07799    }
07800 
07801    if (!ast_strlen_zero(vmu->pager)) {
07802       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
07803    }
07804 
07805    if (ast_test_flag(vmu, VM_DELETE))
07806       DELETE(todir, msgnum, fn, vmu);
07807 
07808    /* Leave voicemail for someone */
07809    if (ast_app_has_voicemail(ext_context, NULL)) 
07810       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
07811 
07812    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
07813 
07814    /*** DOCUMENTATION
07815       <managerEventInstance>
07816          <synopsis>Raised when a new message has been left in a voicemail mailbox.</synopsis>
07817          <syntax>
07818             <parameter name="Mailbox">
07819                <para>The mailbox with the new message, specified as <emphasis>mailbox</emphasis>@<emphasis>context</emphasis></para>
07820             </parameter>
07821             <parameter name="Waiting">
07822                <para>Whether or not the mailbox has access to a voicemail application.</para>
07823             </parameter>
07824             <parameter name="New">
07825                <para>The number of new messages.</para>
07826             </parameter>
07827             <parameter name="Old">
07828                <para>The number of old messages.</para>
07829             </parameter>
07830          </syntax>
07831       </managerEventInstance>
07832    ***/
07833    ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting",
07834          "Mailbox: %s@%s\r\n"
07835          "Waiting: %d\r\n"
07836          "New: %d\r\n"
07837          "Old: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
07838    run_externnotify(vmu->context, vmu->mailbox, flag);
07839 
07840 #ifdef IMAP_STORAGE
07841    vm_delete(fn);  /* Delete the file, but not the IMAP message */
07842    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
07843       vm_imap_delete(NULL, vms->curmsg, vmu);
07844       vms->newmessages--;  /* Fix new message count */
07845    }
07846 #endif
07847 
07848    return 0;
07849 }
07850 
07851 /*!
07852  * \brief Sends a voicemail message to a mailbox recipient.
07853  * \param chan
07854  * \param context
07855  * \param vms
07856  * \param sender
07857  * \param fmt
07858  * \param is_new_message Used to indicate the mode for which this method was invoked. 
07859  *             Will be 0 when called to forward an existing message (option 8)
07860  *             Will be 1 when called to leave a message (option 3->5)
07861  * \param record_gain 
07862  * \param urgent
07863  *
07864  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
07865  * 
07866  * When in the leave message mode (is_new_message == 1):
07867  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
07868  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
07869  *
07870  * When in the forward message mode (is_new_message == 0):
07871  *   - retreives the current message to be forwarded
07872  *   - copies the original message to a temporary file, so updates to the envelope can be done.
07873  *   - determines the target mailbox and folders
07874  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
07875  *
07876  * \return zero on success, -1 on error.
07877  */
07878 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)
07879 {
07880 #ifdef IMAP_STORAGE
07881    int todircount = 0;
07882    struct vm_state *dstvms;
07883 #endif
07884    char username[70]="";
07885    char fn[PATH_MAX]; /* for playback of name greeting */
07886    char ecodes[16] = "#";
07887    int res = 0, cmd = 0;
07888    struct ast_vm_user *receiver = NULL, *vmtmp;
07889    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
07890    char *stringp;
07891    const char *s;
07892    int saved_messages = 0;
07893    int valid_extensions = 0;
07894    char *dir;
07895    int curmsg;
07896    char urgent_str[7] = "";
07897    int prompt_played = 0;
07898 #ifndef IMAP_STORAGE
07899    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
07900 #endif
07901    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
07902       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
07903    }
07904 
07905    if (vms == NULL) return -1;
07906    dir = vms->curdir;
07907    curmsg = vms->curmsg;
07908 
07909    ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
07910    while (!res && !valid_extensions) {
07911       int use_directory = 0;
07912       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
07913          int done = 0;
07914          int retries = 0;
07915          cmd = 0;
07916          while ((cmd >= 0) && !done ){
07917             if (cmd)
07918                retries = 0;
07919             switch (cmd) {
07920             case '1': 
07921                use_directory = 0;
07922                done = 1;
07923                break;
07924             case '2': 
07925                use_directory = 1;
07926                done = 1;
07927                break;
07928             case '*': 
07929                cmd = 't';
07930                done = 1;
07931                break;
07932             default: 
07933                /* Press 1 to enter an extension press 2 to use the directory */
07934                cmd = ast_play_and_wait(chan, "vm-forward");
07935                if (!cmd) {
07936                   cmd = ast_waitfordigit(chan, 3000);
07937                }
07938                if (!cmd) {
07939                   retries++;
07940                }
07941                if (retries > 3) {
07942                   cmd = 't';
07943                   done = 1;
07944                }
07945                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
07946             }
07947          }
07948          if (cmd < 0 || cmd == 't')
07949             break;
07950       }
07951 
07952       if (use_directory) {
07953          /* use app_directory */
07954 
07955          struct ast_app* directory_app;
07956 
07957          directory_app = pbx_findapp("Directory");
07958          if (directory_app) {
07959             char vmcontext[256];
07960             char *old_context;
07961             char *old_exten;
07962             int old_priority;
07963             /* make backup copies */
07964             old_context = ast_strdupa(ast_channel_context(chan));
07965             old_exten = ast_strdupa(ast_channel_exten(chan));
07966             old_priority = ast_channel_priority(chan);
07967 
07968             /* call the the Directory, changes the channel */
07969             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
07970             res = pbx_exec(chan, directory_app, vmcontext);
07971 
07972             ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
07973 
07974             /* restore the old context, exten, and priority */
07975             ast_channel_context_set(chan, old_context);
07976             ast_channel_exten_set(chan, old_exten);
07977             ast_channel_priority_set(chan, old_priority);
07978          } else {
07979             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
07980             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
07981          }
07982       } else {
07983          /* Ask for an extension */
07984          ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
07985          res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
07986          prompt_played++;
07987          if (res || prompt_played > 4)
07988             break;
07989          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
07990             break;
07991       }
07992 
07993       /* start all over if no username */
07994       if (ast_strlen_zero(username))
07995          continue;
07996       stringp = username;
07997       s = strsep(&stringp, "*");
07998       /* start optimistic */
07999       valid_extensions = 1;
08000       while (s) {
08001          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
08002             int oldmsgs;
08003             int newmsgs;
08004             int capacity;
08005             if (inboxcount(s, &newmsgs, &oldmsgs)) {
08006                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
08007                /* Shouldn't happen, but allow trying another extension if it does */
08008                res = ast_play_and_wait(chan, "pbx-invalid");
08009                valid_extensions = 0;
08010                break;
08011             }
08012             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
08013             if ((newmsgs + oldmsgs) >= capacity) {
08014                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
08015                res = ast_play_and_wait(chan, "vm-mailboxfull");
08016                valid_extensions = 0;
08017                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
08018                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
08019                   free_user(vmtmp);
08020                }
08021                inprocess_count(receiver->mailbox, receiver->context, -1);
08022                break;
08023             }
08024             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
08025          } else {
08026             /* XXX Optimization for the future.  When we encounter a single bad extension,
08027              * bailing out on all of the extensions may not be the way to go.  We should
08028              * probably just bail on that single extension, then allow the user to enter
08029              * several more. XXX
08030              */
08031             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
08032                free_user(receiver);
08033             }
08034             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
08035             /* "I am sorry, that's not a valid extension.  Please try again." */
08036             res = ast_play_and_wait(chan, "pbx-invalid");
08037             valid_extensions = 0;
08038             break;
08039          }
08040 
08041          /* play name if available, else play extension number */
08042          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
08043          RETRIEVE(fn, -1, s, receiver->context);
08044          if (ast_fileexists(fn, NULL, NULL) > 0) {
08045             res = ast_stream_and_wait(chan, fn, ecodes);
08046             if (res) {
08047                DISPOSE(fn, -1);
08048                return res;
08049             }
08050          } else {
08051             res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
08052          }
08053          DISPOSE(fn, -1);
08054 
08055          s = strsep(&stringp, "*");
08056       }
08057       /* break from the loop of reading the extensions */
08058       if (valid_extensions)
08059          break;
08060    }
08061    /* check if we're clear to proceed */
08062    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
08063       return res;
08064    if (is_new_message == 1) {
08065       struct leave_vm_options leave_options;
08066       char mailbox[AST_MAX_EXTENSION * 2 + 2];
08067       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
08068 
08069       /* Send VoiceMail */
08070       memset(&leave_options, 0, sizeof(leave_options));
08071       leave_options.record_gain = record_gain;
08072       cmd = leave_voicemail(chan, mailbox, &leave_options);
08073    } else {
08074       /* Forward VoiceMail */
08075       long duration = 0;
08076       struct vm_state vmstmp;
08077       int copy_msg_result = 0;
08078 #ifdef IMAP_STORAGE
08079       char filename[PATH_MAX];
08080       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
08081       const char *msg_id = NULL;
08082       struct ast_config *msg_cfg;
08083 #endif
08084       memcpy(&vmstmp, vms, sizeof(vmstmp));
08085 
08086       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
08087 #ifdef IMAP_STORAGE
08088       make_file(filename, sizeof(filename), dir, curmsg);
08089       strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
08090       msg_cfg = ast_config_load(filename, config_flags);
08091       if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
08092          msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
08093          ast_config_destroy(msg_cfg);
08094       }
08095 #endif
08096 
08097       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
08098       if (!cmd) {
08099          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
08100 #ifdef IMAP_STORAGE
08101             int attach_user_voicemail;
08102             char *myserveremail = serveremail;
08103             
08104             /* get destination mailbox */
08105             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
08106             if (!dstvms) {
08107                dstvms = create_vm_state_from_user(vmtmp);
08108             }
08109             if (dstvms) {
08110                init_mailstream(dstvms, 0);
08111                if (!dstvms->mailstream) {
08112                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
08113                } else {
08114                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
08115                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
08116                }
08117             } else {
08118                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
08119             }
08120             if (!ast_strlen_zero(vmtmp->serveremail))
08121                myserveremail = vmtmp->serveremail;
08122             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
08123             /* NULL category for IMAP storage */
08124             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
08125                dstvms->curbox,
08126                S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
08127                S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
08128                vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
08129                NULL, urgent_str, msg_id);
08130 #else
08131             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
08132 #endif
08133             saved_messages++;
08134             AST_LIST_REMOVE_CURRENT(list);
08135             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
08136             free_user(vmtmp);
08137             if (res)
08138                break;
08139          }
08140          AST_LIST_TRAVERSE_SAFE_END;
08141          if (saved_messages > 0 && !copy_msg_result) {
08142             /* give confirmation that the message was saved */
08143             /* commented out since we can't forward batches yet
08144             if (saved_messages == 1)
08145                res = ast_play_and_wait(chan, "vm-message");
08146             else
08147                res = ast_play_and_wait(chan, "vm-messages");
08148             if (!res)
08149                res = ast_play_and_wait(chan, "vm-saved"); */
08150 #ifdef IMAP_STORAGE
08151             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
08152             if (ast_strlen_zero(vmstmp.introfn))
08153 #endif
08154             res = ast_play_and_wait(chan, "vm-msgsaved");
08155          }
08156 #ifndef IMAP_STORAGE
08157          else {
08158             /* with IMAP, mailbox full warning played by imap_check_limits */
08159             res = ast_play_and_wait(chan, "vm-mailboxfull");
08160          }
08161          /* Restore original message without prepended message if backup exists */
08162          make_file(msgfile, sizeof(msgfile), dir, curmsg);
08163          strcpy(textfile, msgfile);
08164          strcpy(backup, msgfile);
08165          strcpy(backup_textfile, msgfile);
08166          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
08167          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
08168          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
08169          if (ast_fileexists(backup, NULL, NULL) > 0) {
08170             ast_filerename(backup, msgfile, NULL);
08171             rename(backup_textfile, textfile);
08172          }
08173 #endif
08174       }
08175       DISPOSE(dir, curmsg);
08176 #ifndef IMAP_STORAGE
08177       if (cmd) { /* assuming hangup, cleanup backup file */
08178          make_file(msgfile, sizeof(msgfile), dir, curmsg);
08179          strcpy(textfile, msgfile);
08180          strcpy(backup_textfile, msgfile);
08181          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
08182          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
08183          rename(backup_textfile, textfile);
08184       }
08185 #endif
08186    }
08187 
08188    /* If anything failed above, we still have this list to free */
08189    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
08190       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
08191       free_user(vmtmp);
08192    }
08193    return res ? res : cmd;
08194 }
08195 
08196 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
08197 {
08198    int res;
08199    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
08200       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
08201    return res;
08202 }
08203 
08204 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
08205 {
08206    ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
08207    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);
08208 }
08209 
08210 static int play_message_category(struct ast_channel *chan, const char *category)
08211 {
08212    int res = 0;
08213 
08214    if (!ast_strlen_zero(category))
08215       res = ast_play_and_wait(chan, category);
08216 
08217    if (res) {
08218       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
08219       res = 0;
08220    }
08221 
08222    return res;
08223 }
08224 
08225 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
08226 {
08227    int res = 0;
08228    struct vm_zone *the_zone = NULL;
08229    time_t t;
08230 
08231    if (ast_get_time_t(origtime, &t, 0, NULL)) {
08232       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
08233       return 0;
08234    }
08235 
08236    /* Does this user have a timezone specified? */
08237    if (!ast_strlen_zero(vmu->zonetag)) {
08238       /* Find the zone in the list */
08239       struct vm_zone *z;
08240       AST_LIST_LOCK(&zones);
08241       AST_LIST_TRAVERSE(&zones, z, list) {
08242          if (!strcmp(z->name, vmu->zonetag)) {
08243             the_zone = z;
08244             break;
08245          }
08246       }
08247       AST_LIST_UNLOCK(&zones);
08248    }
08249 
08250 /* No internal variable parsing for now, so we'll comment it out for the time being */
08251 #if 0
08252    /* Set the DIFF_* variables */
08253    ast_localtime(&t, &time_now, NULL);
08254    tv_now = ast_tvnow();
08255    ast_localtime(&tv_now, &time_then, NULL);
08256 
08257    /* Day difference */
08258    if (time_now.tm_year == time_then.tm_year)
08259       snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
08260    else
08261       snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
08262    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
08263 
08264    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
08265 #endif
08266    if (the_zone) {
08267       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
08268    } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) {     /* GERMAN syntax */
08269       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
08270    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {     /* GREEK syntax */
08271       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q  H 'digits/kai' M ", NULL);
08272    } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {     /* ITALIAN syntax */
08273       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);
08274    } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) {     /* DUTCH syntax */
08275       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
08276    } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) {     /* NORWEGIAN syntax */
08277       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
08278    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {     /* POLISH syntax */
08279       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
08280    } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
08281       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);
08282    } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) {     /* SWEDISH syntax */
08283       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
08284    } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) {     /* CHINESE (Taiwan) syntax */
08285       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
08286    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {     /* VIETNAMESE syntax */
08287       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);
08288    } else {
08289       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
08290    }
08291 #if 0
08292    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
08293 #endif
08294    return res;
08295 }
08296 
08297 
08298 
08299 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
08300 {
08301    int res = 0;
08302    int i;
08303    char *callerid, *name;
08304    char prefile[PATH_MAX] = "";
08305 
08306    /* If voicemail cid is not enabled, or we didn't get cid or context from
08307     * the attribute file, leave now.
08308     *
08309     * TODO Still need to change this so that if this function is called by the
08310     * message envelope (and someone is explicitly requesting to hear the CID),
08311     * it does not check to see if CID is enabled in the config file.
08312     */
08313    if ((cid == NULL)||(context == NULL))
08314       return res;
08315 
08316    /* Strip off caller ID number from name */
08317    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
08318    ast_callerid_parse(cid, &name, &callerid);
08319    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
08320       /* Check for internal contexts and only */
08321       /* say extension when the call didn't come from an internal context in the list */
08322       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
08323          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
08324          if ((strcmp(cidinternalcontexts[i], context) == 0))
08325             break;
08326       }
08327       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
08328          if (!res) {
08329             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
08330             if (!ast_strlen_zero(prefile)) {
08331                /* See if we can find a recorded name for this callerid
08332                 * and if found, use that instead of saying number. */
08333                if (ast_fileexists(prefile, NULL, NULL) > 0) {
08334                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
08335                   if (!callback)
08336                      res = wait_file2(chan, vms, "vm-from");
08337                   res = ast_stream_and_wait(chan, prefile, "");
08338                } else {
08339                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
08340                   /* Say "from extension" as one saying to sound smoother */
08341                   if (!callback)
08342                      res = wait_file2(chan, vms, "vm-from-extension");
08343                   res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
08344                }
08345             }
08346          }
08347       } else if (!res) {
08348          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
08349          /* If there is a recording for this numeric callerid then play that */
08350          if (!callback) {
08351             /* See if we can find a recorded name for this person instead of their extension number */
08352             snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
08353             if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
08354                ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
08355                wait_file2(chan, vms, "vm-from");
08356                res = ast_stream_and_wait(chan, prefile, "");
08357                ast_verb(3, "Played recorded name result '%d'\n", res);
08358             } else {
08359                /* Since this is all nicely figured out, why not say "from phone number" in this case" */
08360                wait_file2(chan, vms, "vm-from-phonenumber");
08361                res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
08362             }
08363          } else {
08364             res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
08365          }
08366       }
08367    } else {
08368       /* Number unknown */
08369       ast_debug(1, "VM-CID: From an unknown number\n");
08370       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
08371       res = wait_file2(chan, vms, "vm-unknown-caller");
08372    }
08373    return res;
08374 }
08375 
08376 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
08377 {
08378    int res = 0;
08379    int durationm;
08380    int durations;
08381    /* Verify that we have a duration for the message */
08382    if (duration == NULL)
08383       return res;
08384 
08385    /* Convert from seconds to minutes */
08386    durations = atoi(duration);
08387    durationm = (durations / 60);
08388 
08389    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
08390 
08391    if ((!res) && (durationm >= minduration)) {
08392       res = wait_file2(chan, vms, "vm-duration");
08393 
08394       /* POLISH syntax */
08395       if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
08396          div_t num = div(durationm, 10);
08397 
08398          if (durationm == 1) {
08399             res = ast_play_and_wait(chan, "digits/1z");
08400             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
08401          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
08402             if (num.rem == 2) {
08403                if (!num.quot) {
08404                   res = ast_play_and_wait(chan, "digits/2-ie");
08405                } else {
08406                   res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
08407                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
08408                }
08409             } else {
08410                res = say_and_wait(chan, durationm, ast_channel_language(chan));
08411             }
08412             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
08413          } else {
08414             res = say_and_wait(chan, durationm, ast_channel_language(chan));
08415             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
08416          }
08417       /* DEFAULT syntax */
08418       } else {
08419          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
08420          res = wait_file2(chan, vms, "vm-minutes");
08421       }
08422    }
08423    return res;
08424 }
08425 
08426 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08427 {
08428    int res = 0;
08429    char filename[256], *cid;
08430    const char *origtime, *context, *category, *duration, *flag;
08431    struct ast_config *msg_cfg;
08432    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
08433 
08434    vms->starting = 0;
08435    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
08436    adsi_message(chan, vms);
08437    if (!vms->curmsg) {
08438       res = wait_file2(chan, vms, "vm-first");  /* "First" */
08439    } else if (vms->curmsg == vms->lastmsg) {
08440       res = wait_file2(chan, vms, "vm-last");      /* "last" */
08441    }
08442 
08443    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
08444    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
08445    msg_cfg = ast_config_load(filename, config_flags);
08446    if (!valid_config(msg_cfg)) {
08447       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
08448       return 0;
08449    }
08450    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
08451 
08452    /* Play the word urgent if we are listening to urgent messages */
08453    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
08454       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
08455    }
08456 
08457    if (!res) {
08458       /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
08459       /* POLISH syntax */
08460       if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
08461          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
08462             int ten, one;
08463             char nextmsg[256];
08464             ten = (vms->curmsg + 1) / 10;
08465             one = (vms->curmsg + 1) % 10;
08466 
08467             if (vms->curmsg < 20) {
08468                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
08469                res = wait_file2(chan, vms, nextmsg);
08470             } else {
08471                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
08472                res = wait_file2(chan, vms, nextmsg);
08473                if (one > 0) {
08474                   if (!res) {
08475                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
08476                      res = wait_file2(chan, vms, nextmsg);
08477                   }
08478                }
08479             }
08480          }
08481          if (!res)
08482             res = wait_file2(chan, vms, "vm-message");
08483       /* HEBREW syntax */
08484       } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
08485          if (!vms->curmsg) {
08486             res = wait_file2(chan, vms, "vm-message");
08487             res = wait_file2(chan, vms, "vm-first");
08488          } else if (vms->curmsg == vms->lastmsg) {
08489             res = wait_file2(chan, vms, "vm-message");
08490             res = wait_file2(chan, vms, "vm-last");
08491          } else {
08492             res = wait_file2(chan, vms, "vm-message");
08493             res = wait_file2(chan, vms, "vm-number");
08494             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08495          }
08496       /* VIETNAMESE syntax */
08497       } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
08498          if (!vms->curmsg) {
08499             res = wait_file2(chan, vms, "vm-message");
08500             res = wait_file2(chan, vms, "vm-first");
08501          } else if (vms->curmsg == vms->lastmsg) {
08502             res = wait_file2(chan, vms, "vm-message");
08503             res = wait_file2(chan, vms, "vm-last");
08504          } else {
08505             res = wait_file2(chan, vms, "vm-message");
08506             res = wait_file2(chan, vms, "vm-number");
08507             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
08508          }
08509       } else {
08510          if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
08511             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
08512          } else { /* DEFAULT syntax */
08513             res = wait_file2(chan, vms, "vm-message");
08514          }
08515          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
08516             if (!res) {
08517                ast_test_suite_event_notify("PLAYBACK", "Message: message number");
08518                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
08519             }
08520          }
08521       }
08522    }
08523 
08524    if (!valid_config(msg_cfg)) {
08525       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
08526       return 0;
08527    }
08528 
08529    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
08530       ast_log(AST_LOG_WARNING, "No origtime?!\n");
08531       DISPOSE(vms->curdir, vms->curmsg);
08532       ast_config_destroy(msg_cfg);
08533       return 0;
08534    }
08535 
08536    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
08537    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
08538    category = ast_variable_retrieve(msg_cfg, "message", "category");
08539 
08540    context = ast_variable_retrieve(msg_cfg, "message", "context");
08541    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
08542       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
08543    if (!res) {
08544       res = play_message_category(chan, category);
08545    }
08546    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
08547       res = play_message_datetime(chan, vmu, origtime, filename);
08548    }
08549    if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
08550       res = play_message_callerid(chan, vms, cid, context, 0, 0);
08551    }
08552    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
08553       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
08554    }
08555    /* Allow pressing '1' to skip envelope / callerid */
08556    if (res == '1') {
08557       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
08558       res = 0;
08559    }
08560    ast_config_destroy(msg_cfg);
08561 
08562    if (!res) {
08563       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
08564 #ifdef IMAP_STORAGE
08565       ast_mutex_lock(&vms->lock);
08566 #endif
08567       vms->heard[vms->curmsg] = 1;
08568 #ifdef IMAP_STORAGE
08569       ast_mutex_unlock(&vms->lock);
08570       /*IMAP storage stores any prepended message from a forward
08571        * as a separate file from the rest of the message
08572        */
08573       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
08574          wait_file(chan, vms, vms->introfn);
08575       }
08576 #endif
08577       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
08578          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
08579          res = 0;
08580       }
08581       ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
08582    }
08583    DISPOSE(vms->curdir, vms->curmsg);
08584    return res;
08585 }
08586 
08587 #ifdef IMAP_STORAGE
08588 static int imap_remove_file(char *dir, int msgnum)
08589 {
08590    char fn[PATH_MAX];
08591    char full_fn[PATH_MAX];
08592    char intro[PATH_MAX] = {0,};
08593    
08594    if (msgnum > -1) {
08595       make_file(fn, sizeof(fn), dir, msgnum);
08596       snprintf(intro, sizeof(intro), "%sintro", fn);
08597    } else
08598       ast_copy_string(fn, dir, sizeof(fn));
08599    
08600    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
08601       ast_filedelete(fn, NULL);
08602       if (!ast_strlen_zero(intro)) {
08603          ast_filedelete(intro, NULL);
08604       }
08605       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
08606       unlink(full_fn);
08607    }
08608    return 0;
08609 }
08610 
08611 
08612 
08613 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
08614 {
08615    char *file, *filename;
08616    char *attachment;
08617    char arg[10];
08618    int i;
08619    BODY* body;
08620 
08621    file = strrchr(ast_strdupa(dir), '/');
08622    if (file) {
08623       *file++ = '\0';
08624    } else {
08625       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
08626       return -1;
08627    }
08628 
08629    ast_mutex_lock(&vms->lock);
08630    for (i = 0; i < vms->mailstream->nmsgs; i++) {
08631       mail_fetchstructure(vms->mailstream, i + 1, &body);
08632       /* We have the body, now we extract the file name of the first attachment. */
08633       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
08634          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
08635       } else {
08636          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
08637          ast_mutex_unlock(&vms->lock);
08638          return -1;
08639       }
08640       filename = strsep(&attachment, ".");
08641       if (!strcmp(filename, file)) {
08642          sprintf(arg, "%d", i + 1);
08643          mail_setflag(vms->mailstream, arg, "\\DELETED");
08644       }
08645    }
08646    mail_expunge(vms->mailstream);
08647    ast_mutex_unlock(&vms->lock);
08648    return 0;
08649 }
08650 
08651 #elif !defined(IMAP_STORAGE)
08652 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
08653 {
08654    int count_msg, last_msg;
08655 
08656    ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
08657 
08658    /* Rename the member vmbox HERE so that we don't try to return before
08659     * we know what's going on.
08660     */
08661    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
08662 
08663    /* Faster to make the directory than to check if it exists. */
08664    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
08665 
08666    /* traverses directory using readdir (or select query for ODBC) */
08667    count_msg = count_messages(vmu, vms->curdir);
08668    if (count_msg < 0) {
08669       return count_msg;
08670    } else {
08671       vms->lastmsg = count_msg - 1;
08672    }
08673 
08674    if (vm_allocate_dh(vms, vmu, count_msg)) {
08675       return -1;
08676    }
08677 
08678    /*
08679    The following test is needed in case sequencing gets messed up.
08680    There appears to be more than one way to mess up sequence, so
08681    we will not try to find all of the root causes--just fix it when
08682    detected.
08683    */
08684 
08685    if (vm_lock_path(vms->curdir)) {
08686       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
08687       return ERROR_LOCK_PATH;
08688    }
08689 
08690    /* for local storage, checks directory for messages up to maxmsg limit */
08691    last_msg = last_message_index(vmu, vms->curdir);
08692    ast_unlock_path(vms->curdir);
08693 
08694    if (last_msg < -1) {
08695       return last_msg;
08696    } else if (vms->lastmsg != last_msg) {
08697       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);
08698       resequence_mailbox(vmu, vms->curdir, count_msg);
08699    }
08700 
08701    return 0;
08702 }
08703 #endif
08704 
08705 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
08706 {
08707    int x = 0;
08708    int last_msg_idx = 0;
08709 
08710 #ifndef IMAP_STORAGE
08711    int res = 0, nummsg;
08712    char fn2[PATH_MAX];
08713 #endif
08714 
08715    if (vms->lastmsg <= -1) {
08716       goto done;
08717    }
08718 
08719    vms->curmsg = -1;
08720 #ifndef IMAP_STORAGE
08721    /* Get the deleted messages fixed */
08722    if (vm_lock_path(vms->curdir)) {
08723       return ERROR_LOCK_PATH;
08724    }
08725 
08726    /* update count as message may have arrived while we've got mailbox open */
08727    last_msg_idx = last_message_index(vmu, vms->curdir);
08728    if (last_msg_idx != vms->lastmsg) {
08729       ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
08730    }
08731 
08732    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
08733    for (x = 0; x < last_msg_idx + 1; x++) {
08734       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
08735          /* Save this message.  It's not in INBOX or hasn't been heard */
08736          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08737          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
08738             break;
08739          }
08740          vms->curmsg++;
08741          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
08742          if (strcmp(vms->fn, fn2)) {
08743             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
08744          }
08745       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
08746          /* Move to old folder before deleting */
08747          res = save_to_folder(vmu, vms, x, 1, NULL, 0);
08748          if (res == ERROR_LOCK_PATH) {
08749             /* If save failed do not delete the message */
08750             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
08751             vms->deleted[x] = 0;
08752             vms->heard[x] = 0;
08753             --x;
08754          }
08755       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
08756          /* Move to deleted folder */
08757          res = save_to_folder(vmu, vms, x, 10, NULL, 0);
08758          if (res == ERROR_LOCK_PATH) {
08759             /* If save failed do not delete the message */
08760             vms->deleted[x] = 0;
08761             vms->heard[x] = 0;
08762             --x;
08763          }
08764       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
08765          /* If realtime storage enabled - we should explicitly delete this message,
08766          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
08767          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08768          if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08769             DELETE(vms->curdir, x, vms->fn, vmu);
08770          }
08771       }
08772    }
08773 
08774    /* Delete ALL remaining messages */
08775    nummsg = x - 1;
08776    for (x = vms->curmsg + 1; x <= nummsg; x++) {
08777       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
08778       if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
08779          DELETE(vms->curdir, x, vms->fn, vmu);
08780       }
08781    }
08782    ast_unlock_path(vms->curdir);
08783 #else /* defined(IMAP_STORAGE) */
08784    ast_mutex_lock(&vms->lock);
08785    if (vms->deleted) {
08786       /* Since we now expunge after each delete, deleting in reverse order
08787        * ensures that no reordering occurs between each step. */
08788       last_msg_idx = vms->dh_arraysize;
08789       for (x = last_msg_idx - 1; x >= 0; x--) {
08790          if (vms->deleted[x]) {
08791             ast_debug(3, "IMAP delete of %d\n", x);
08792             DELETE(vms->curdir, x, vms->fn, vmu);
08793          }
08794       }
08795    }
08796 #endif
08797 
08798 done:
08799    if (vms->deleted) {
08800       ast_free(vms->deleted);
08801       vms->deleted = NULL;
08802    }
08803    if (vms->heard) {
08804       ast_free(vms->heard);
08805       vms->heard = NULL;
08806    }
08807    vms->dh_arraysize = 0;
08808 #ifdef IMAP_STORAGE
08809    ast_mutex_unlock(&vms->lock);
08810 #endif
08811 
08812    return 0;
08813 }
08814 
08815 /* In Greek even though we CAN use a syntax like "friends messages"
08816  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
08817  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
08818  * syntax for the above three categories which is more elegant.
08819  */
08820 
08821 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
08822 {
08823    int cmd;
08824    char *buf;
08825 
08826    buf = ast_alloca(strlen(box) + 2);
08827    strcpy(buf, box);
08828    strcat(buf, "s");
08829 
08830    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
08831       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
08832       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08833    } else {
08834       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
08835       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
08836    }
08837 }
08838 
08839 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
08840 {
08841    int cmd;
08842 
08843    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
08844       if (!strcasecmp(box, "vm-INBOX"))
08845          cmd = ast_play_and_wait(chan, "vm-new-e");
08846       else
08847          cmd = ast_play_and_wait(chan, "vm-old-e");
08848       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08849    } else {
08850       cmd = ast_play_and_wait(chan, "vm-messages");
08851       return cmd ? cmd : ast_play_and_wait(chan, box);
08852    }
08853 }
08854 
08855 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
08856 {
08857    int cmd;
08858 
08859    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
08860       cmd = ast_play_and_wait(chan, "vm-messages");
08861       return cmd ? cmd : ast_play_and_wait(chan, box);
08862    } else {
08863       cmd = ast_play_and_wait(chan, box);
08864       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
08865    }
08866 }
08867 
08868 static int vm_play_folder_name(struct ast_channel *chan, char *box)
08869 {
08870    int cmd;
08871 
08872    if (  !strncasecmp(ast_channel_language(chan), "it", 2) ||
08873         !strncasecmp(ast_channel_language(chan), "es", 2) ||
08874         !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
08875       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
08876       return cmd ? cmd : ast_play_and_wait(chan, box);
08877    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
08878       return vm_play_folder_name_gr(chan, box);
08879    } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* Hebrew syntax */
08880       return ast_play_and_wait(chan, box);
08881    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
08882       return vm_play_folder_name_pl(chan, box);
08883    } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) {  /* Ukrainian syntax */
08884       return vm_play_folder_name_ua(chan, box);
08885    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
08886       return ast_play_and_wait(chan, box);
08887    } else {  /* Default English */
08888       cmd = ast_play_and_wait(chan, box);
08889       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
08890    }
08891 }
08892 
08893 /* GREEK SYNTAX
08894    In greek the plural for old/new is
08895    different so we need the following files
08896    We also need vm-denExeteMynhmata because
08897    this syntax is different.
08898 
08899    -> vm-Olds.wav : "Palia"
08900    -> vm-INBOXs.wav : "Nea"
08901    -> vm-denExeteMynhmata : "den exete mynhmata"
08902 */
08903 
08904 
08905 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
08906 {
08907    int res = 0;
08908 
08909    if (vms->newmessages) {
08910       res = ast_play_and_wait(chan, "vm-youhave");
08911       if (!res) 
08912          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
08913       if (!res) {
08914          if ((vms->newmessages == 1)) {
08915             res = ast_play_and_wait(chan, "vm-INBOX");
08916             if (!res)
08917                res = ast_play_and_wait(chan, "vm-message");
08918          } else {
08919             res = ast_play_and_wait(chan, "vm-INBOXs");
08920             if (!res)
08921                res = ast_play_and_wait(chan, "vm-messages");
08922          }
08923       }
08924    } else if (vms->oldmessages){
08925       res = ast_play_and_wait(chan, "vm-youhave");
08926       if (!res)
08927          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
08928       if ((vms->oldmessages == 1)){
08929          res = ast_play_and_wait(chan, "vm-Old");
08930          if (!res)
08931             res = ast_play_and_wait(chan, "vm-message");
08932       } else {
08933          res = ast_play_and_wait(chan, "vm-Olds");
08934          if (!res)
08935             res = ast_play_and_wait(chan, "vm-messages");
08936       }
08937    } else if (!vms->oldmessages && !vms->newmessages) 
08938       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
08939    return res;
08940 }
08941 
08942 /* Version of vm_intro() designed to work for many languages.
08943  *
08944  * It is hoped that this function can prevent the proliferation of 
08945  * language-specific vm_intro() functions and in time replace the language-
08946  * specific functions which already exist.  An examination of the language-
08947  * specific functions revealed that they all corrected the same deficiencies
08948  * in vm_intro_en() (which was the default function). Namely:
08949  *
08950  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
08951  *     wording of the voicemail greeting hides this problem.  For example,
08952  *     vm-INBOX contains only the word "new".  This means that both of these
08953  *     sequences produce valid utterances:
08954  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
08955  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
08956  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
08957  *     in many languages) the first utterance becomes "you have 1 the new message".
08958  *  2) The function contains hardcoded rules for pluralizing the word "message".
08959  *     These rules are correct for English, but not for many other languages.
08960  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
08961  *     required in many languages.
08962  *  4) The gender of the word for "message" is not specified. This is a problem
08963  *     because in many languages the gender of the number in phrases such
08964  *     as "you have one new message" must match the gender of the word
08965  *     meaning "message".
08966  *
08967  * Fixing these problems for each new language has meant duplication of effort.
08968  * This new function solves the problems in the following general ways:
08969  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
08970  *     and vm-Old respectively for those languages where it makes sense.
08971  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
08972  *     on vm-message.
08973  *  3) Call ast_say_counted_adjective() to put the proper gender and number
08974  *     prefix on vm-new and vm-old (none for English).
08975  *  4) Pass the gender of the language's word for "message" as an agument to
08976  *     this function which is can in turn pass on to the functions which 
08977  *     say numbers and put endings on nounds and adjectives.
08978  *
08979  * All languages require these messages:
08980  *  vm-youhave    "You have..."
08981  *  vm-and     "and"
08982  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
08983  *
08984  * To use it for English, you will need these additional sound files:
08985  *  vm-new     "new"
08986  *  vm-message    "message", singular
08987  *  vm-messages      "messages", plural
08988  *
08989  * If you use it for Russian and other slavic languages, you will need these additional sound files:
08990  *
08991  *  vm-newn    "novoye" (singular, neuter)
08992  *  vm-newx    "novikh" (counting plural form, genative plural)
08993  *  vm-message    "sobsheniye" (singular form)
08994  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
08995  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
08996  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
08997  *  digits/2n     "dva" (neuter singular)
08998  */
08999 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
09000 {
09001    int res;
09002    int lastnum = 0;
09003 
09004    res = ast_play_and_wait(chan, "vm-youhave");
09005 
09006    if (!res && vms->newmessages) {
09007       lastnum = vms->newmessages;
09008 
09009       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
09010          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
09011       }
09012 
09013       if (!res && vms->oldmessages) {
09014          res = ast_play_and_wait(chan, "vm-and");
09015       }
09016    }
09017 
09018    if (!res && vms->oldmessages) {
09019       lastnum = vms->oldmessages;
09020 
09021       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
09022          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
09023       }
09024    }
09025 
09026    if (!res) {
09027       if (lastnum == 0) {
09028          res = ast_play_and_wait(chan, "vm-no");
09029       }
09030       if (!res) {
09031          res = ast_say_counted_noun(chan, lastnum, "vm-message");
09032       }
09033    }
09034 
09035    return res;
09036 }
09037 
09038 /* Default Hebrew syntax */
09039 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
09040 {
09041    int res = 0;
09042 
09043    /* Introduce messages they have */
09044    if (!res) {
09045       if ((vms->newmessages) || (vms->oldmessages)) {
09046          res = ast_play_and_wait(chan, "vm-youhave");
09047       }
09048       /*
09049        * The word "shtei" refers to the number 2 in hebrew when performing a count
09050        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
09051        * an element, this is one of them.
09052        */
09053       if (vms->newmessages) {
09054          if (!res) {
09055             if (vms->newmessages == 1) {
09056                res = ast_play_and_wait(chan, "vm-INBOX1");
09057             } else {
09058                if (vms->newmessages == 2) {
09059                   res = ast_play_and_wait(chan, "vm-shtei");
09060                } else {
09061                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
09062                }
09063                res = ast_play_and_wait(chan, "vm-INBOX");
09064             }
09065          }
09066          if (vms->oldmessages && !res) {
09067             res = ast_play_and_wait(chan, "vm-and");
09068             if (vms->oldmessages == 1) {
09069                res = ast_play_and_wait(chan, "vm-Old1");
09070             } else {
09071                if (vms->oldmessages == 2) {
09072                   res = ast_play_and_wait(chan, "vm-shtei");
09073                } else {
09074                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
09075                }
09076                res = ast_play_and_wait(chan, "vm-Old");
09077             }
09078          }
09079       }
09080       if (!res && vms->oldmessages && !vms->newmessages) {
09081          if (!res) {
09082             if (vms->oldmessages == 1) {
09083                res = ast_play_and_wait(chan, "vm-Old1");
09084             } else {
09085                if (vms->oldmessages == 2) {
09086                   res = ast_play_and_wait(chan, "vm-shtei");
09087                } else {
09088                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");            
09089                }
09090                res = ast_play_and_wait(chan, "vm-Old");
09091             }
09092          }
09093       }
09094       if (!res) {
09095          if (!vms->oldmessages && !vms->newmessages) {
09096             if (!res) {
09097                res = ast_play_and_wait(chan, "vm-nomessages");
09098             }
09099          }
09100       }
09101    }
09102    return res;
09103 }
09104    
09105 /* Default English syntax */
09106 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
09107 {
09108    int res;
09109 
09110    /* Introduce messages they have */
09111    res = ast_play_and_wait(chan, "vm-youhave");
09112    if (!res) {
09113       if (vms->urgentmessages) {
09114          res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
09115          if (!res)
09116             res = ast_play_and_wait(chan, "vm-Urgent");
09117          if ((vms->oldmessages || vms->newmessages) && !res) {
09118             res = ast_play_and_wait(chan, "vm-and");
09119          } else if (!res) {
09120             if ((vms->urgentmessages == 1))
09121                res = ast_play_and_wait(chan, "vm-message");
09122             else
09123                res = ast_play_and_wait(chan, "vm-messages");
09124          }
09125       }
09126       if (vms->newmessages) {
09127          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09128          if (!res)
09129             res = ast_play_and_wait(chan, "vm-INBOX");
09130          if (vms->oldmessages && !res)
09131             res = ast_play_and_wait(chan, "vm-and");
09132          else if (!res) {
09133             if ((vms->newmessages == 1))
09134                res = ast_play_and_wait(chan, "vm-message");
09135             else
09136                res = ast_play_and_wait(chan, "vm-messages");
09137          }
09138             
09139       }
09140       if (!res && vms->oldmessages) {
09141          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09142          if (!res)
09143             res = ast_play_and_wait(chan, "vm-Old");
09144          if (!res) {
09145             if (vms->oldmessages == 1)
09146                res = ast_play_and_wait(chan, "vm-message");
09147             else
09148                res = ast_play_and_wait(chan, "vm-messages");
09149          }
09150       }
09151       if (!res) {
09152          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
09153             res = ast_play_and_wait(chan, "vm-no");
09154             if (!res)
09155                res = ast_play_and_wait(chan, "vm-messages");
09156          }
09157       }
09158    }
09159    return res;
09160 }
09161 
09162 /* ITALIAN syntax */
09163 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
09164 {
09165    /* Introduce messages they have */
09166    int res;
09167    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
09168       res = ast_play_and_wait(chan, "vm-no") ||
09169          ast_play_and_wait(chan, "vm-message");
09170    else
09171       res = ast_play_and_wait(chan, "vm-youhave");
09172    if (!res && vms->newmessages) {
09173       res = (vms->newmessages == 1) ?
09174          ast_play_and_wait(chan, "digits/un") ||
09175          ast_play_and_wait(chan, "vm-nuovo") ||
09176          ast_play_and_wait(chan, "vm-message") :
09177          /* 2 or more new messages */
09178          say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
09179          ast_play_and_wait(chan, "vm-nuovi") ||
09180          ast_play_and_wait(chan, "vm-messages");
09181       if (!res && vms->oldmessages)
09182          res = ast_play_and_wait(chan, "vm-and");
09183    }
09184    if (!res && vms->oldmessages) {
09185       res = (vms->oldmessages == 1) ?
09186          ast_play_and_wait(chan, "digits/un") ||
09187          ast_play_and_wait(chan, "vm-vecchio") ||
09188          ast_play_and_wait(chan, "vm-message") :
09189          /* 2 or more old messages */
09190          say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
09191          ast_play_and_wait(chan, "vm-vecchi") ||
09192          ast_play_and_wait(chan, "vm-messages");
09193    }
09194    return res;
09195 }
09196 
09197 /* POLISH syntax */
09198 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
09199 {
09200    /* Introduce messages they have */
09201    int res;
09202    div_t num;
09203 
09204    if (!vms->oldmessages && !vms->newmessages) {
09205       res = ast_play_and_wait(chan, "vm-no");
09206       res = res ? res : ast_play_and_wait(chan, "vm-messages");
09207       return res;
09208    } else {
09209       res = ast_play_and_wait(chan, "vm-youhave");
09210    }
09211 
09212    if (vms->newmessages) {
09213       num = div(vms->newmessages, 10);
09214       if (vms->newmessages == 1) {
09215          res = ast_play_and_wait(chan, "digits/1-a");
09216          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
09217          res = res ? res : ast_play_and_wait(chan, "vm-message");
09218       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
09219          if (num.rem == 2) {
09220             if (!num.quot) {
09221                res = ast_play_and_wait(chan, "digits/2-ie");
09222             } else {
09223                res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
09224                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
09225             }
09226          } else {
09227             res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09228          }
09229          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
09230          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09231       } else {
09232          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09233          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
09234          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09235       }
09236       if (!res && vms->oldmessages)
09237          res = ast_play_and_wait(chan, "vm-and");
09238    }
09239    if (!res && vms->oldmessages) {
09240       num = div(vms->oldmessages, 10);
09241       if (vms->oldmessages == 1) {
09242          res = ast_play_and_wait(chan, "digits/1-a");
09243          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
09244          res = res ? res : ast_play_and_wait(chan, "vm-message");
09245       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
09246          if (num.rem == 2) {
09247             if (!num.quot) {
09248                res = ast_play_and_wait(chan, "digits/2-ie");
09249             } else {
09250                res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
09251                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
09252             }
09253          } else {
09254             res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09255          }
09256          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
09257          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09258       } else {
09259          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09260          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
09261          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09262       }
09263    }
09264 
09265    return res;
09266 }
09267 
09268 /* SWEDISH syntax */
09269 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
09270 {
09271    /* Introduce messages they have */
09272    int res;
09273 
09274    res = ast_play_and_wait(chan, "vm-youhave");
09275    if (res)
09276       return res;
09277 
09278    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09279       res = ast_play_and_wait(chan, "vm-no");
09280       res = res ? res : ast_play_and_wait(chan, "vm-messages");
09281       return res;
09282    }
09283 
09284    if (vms->newmessages) {
09285       if ((vms->newmessages == 1)) {
09286          res = ast_play_and_wait(chan, "digits/ett");
09287          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
09288          res = res ? res : ast_play_and_wait(chan, "vm-message");
09289       } else {
09290          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09291          res = res ? res : ast_play_and_wait(chan, "vm-nya");
09292          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09293       }
09294       if (!res && vms->oldmessages)
09295          res = ast_play_and_wait(chan, "vm-and");
09296    }
09297    if (!res && vms->oldmessages) {
09298       if (vms->oldmessages == 1) {
09299          res = ast_play_and_wait(chan, "digits/ett");
09300          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
09301          res = res ? res : ast_play_and_wait(chan, "vm-message");
09302       } else {
09303          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09304          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
09305          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09306       }
09307    }
09308 
09309    return res;
09310 }
09311 
09312 /* NORWEGIAN syntax */
09313 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
09314 {
09315    /* Introduce messages they have */
09316    int res;
09317 
09318    res = ast_play_and_wait(chan, "vm-youhave");
09319    if (res)
09320       return res;
09321 
09322    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09323       res = ast_play_and_wait(chan, "vm-no");
09324       res = res ? res : ast_play_and_wait(chan, "vm-messages");
09325       return res;
09326    }
09327 
09328    if (vms->newmessages) {
09329       if ((vms->newmessages == 1)) {
09330          res = ast_play_and_wait(chan, "digits/1");
09331          res = res ? res : ast_play_and_wait(chan, "vm-ny");
09332          res = res ? res : ast_play_and_wait(chan, "vm-message");
09333       } else {
09334          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09335          res = res ? res : ast_play_and_wait(chan, "vm-nye");
09336          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09337       }
09338       if (!res && vms->oldmessages)
09339          res = ast_play_and_wait(chan, "vm-and");
09340    }
09341    if (!res && vms->oldmessages) {
09342       if (vms->oldmessages == 1) {
09343          res = ast_play_and_wait(chan, "digits/1");
09344          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
09345          res = res ? res : ast_play_and_wait(chan, "vm-message");
09346       } else {
09347          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09348          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
09349          res = res ? res : ast_play_and_wait(chan, "vm-messages");
09350       }
09351    }
09352 
09353    return res;
09354 }
09355 
09356 /* GERMAN syntax */
09357 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
09358 {
09359    /* Introduce messages they have */
09360    int res;
09361    res = ast_play_and_wait(chan, "vm-youhave");
09362    if (!res) {
09363       if (vms->newmessages) {
09364          if ((vms->newmessages == 1))
09365             res = ast_play_and_wait(chan, "digits/1F");
09366          else
09367             res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09368          if (!res)
09369             res = ast_play_and_wait(chan, "vm-INBOX");
09370          if (vms->oldmessages && !res)
09371             res = ast_play_and_wait(chan, "vm-and");
09372          else if (!res) {
09373             if ((vms->newmessages == 1))
09374                res = ast_play_and_wait(chan, "vm-message");
09375             else
09376                res = ast_play_and_wait(chan, "vm-messages");
09377          }
09378             
09379       }
09380       if (!res && vms->oldmessages) {
09381          if (vms->oldmessages == 1)
09382             res = ast_play_and_wait(chan, "digits/1F");
09383          else
09384             res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09385          if (!res)
09386             res = ast_play_and_wait(chan, "vm-Old");
09387          if (!res) {
09388             if (vms->oldmessages == 1)
09389                res = ast_play_and_wait(chan, "vm-message");
09390             else
09391                res = ast_play_and_wait(chan, "vm-messages");
09392          }
09393       }
09394       if (!res) {
09395          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09396             res = ast_play_and_wait(chan, "vm-no");
09397             if (!res)
09398                res = ast_play_and_wait(chan, "vm-messages");
09399          }
09400       }
09401    }
09402    return res;
09403 }
09404 
09405 /* SPANISH syntax */
09406 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
09407 {
09408    /* Introduce messages they have */
09409    int res;
09410    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09411       res = ast_play_and_wait(chan, "vm-youhaveno");
09412       if (!res)
09413          res = ast_play_and_wait(chan, "vm-messages");
09414    } else {
09415       res = ast_play_and_wait(chan, "vm-youhave");
09416    }
09417    if (!res) {
09418       if (vms->newmessages) {
09419          if (!res) {
09420             if ((vms->newmessages == 1)) {
09421                res = ast_play_and_wait(chan, "digits/1M");
09422                if (!res)
09423                   res = ast_play_and_wait(chan, "vm-message");
09424                if (!res)
09425                   res = ast_play_and_wait(chan, "vm-INBOXs");
09426             } else {
09427                res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09428                if (!res)
09429                   res = ast_play_and_wait(chan, "vm-messages");
09430                if (!res)
09431                   res = ast_play_and_wait(chan, "vm-INBOX");
09432             }
09433          }
09434          if (vms->oldmessages && !res)
09435             res = ast_play_and_wait(chan, "vm-and");
09436       }
09437       if (vms->oldmessages) {
09438          if (!res) {
09439             if (vms->oldmessages == 1) {
09440                res = ast_play_and_wait(chan, "digits/1M");
09441                if (!res)
09442                   res = ast_play_and_wait(chan, "vm-message");
09443                if (!res)
09444                   res = ast_play_and_wait(chan, "vm-Olds");
09445             } else {
09446                res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09447                if (!res)
09448                   res = ast_play_and_wait(chan, "vm-messages");
09449                if (!res)
09450                   res = ast_play_and_wait(chan, "vm-Old");
09451             }
09452          }
09453       }
09454    }
09455 return res;
09456 }
09457 
09458 /* BRAZILIAN PORTUGUESE syntax */
09459 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
09460    /* Introduce messages they have */
09461    int res;
09462    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09463       res = ast_play_and_wait(chan, "vm-nomessages");
09464       return res;
09465    } else {
09466       res = ast_play_and_wait(chan, "vm-youhave");
09467    }
09468    if (vms->newmessages) {
09469       if (!res)
09470          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
09471       if ((vms->newmessages == 1)) {
09472          if (!res)
09473             res = ast_play_and_wait(chan, "vm-message");
09474          if (!res)
09475             res = ast_play_and_wait(chan, "vm-INBOXs");
09476       } else {
09477          if (!res)
09478             res = ast_play_and_wait(chan, "vm-messages");
09479          if (!res)
09480             res = ast_play_and_wait(chan, "vm-INBOX");
09481       }
09482       if (vms->oldmessages && !res)
09483          res = ast_play_and_wait(chan, "vm-and");
09484    }
09485    if (vms->oldmessages) {
09486       if (!res)
09487          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
09488       if (vms->oldmessages == 1) {
09489          if (!res)
09490             res = ast_play_and_wait(chan, "vm-message");
09491          if (!res)
09492             res = ast_play_and_wait(chan, "vm-Olds");
09493       } else {
09494          if (!res)
09495             res = ast_play_and_wait(chan, "vm-messages");
09496          if (!res)
09497             res = ast_play_and_wait(chan, "vm-Old");
09498       }
09499    }
09500    return res;
09501 }
09502 
09503 /* FRENCH syntax */
09504 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
09505 {
09506    /* Introduce messages they have */
09507    int res;
09508    res = ast_play_and_wait(chan, "vm-youhave");
09509    if (!res) {
09510       if (vms->newmessages) {
09511          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09512          if (!res)
09513             res = ast_play_and_wait(chan, "vm-INBOX");
09514          if (vms->oldmessages && !res)
09515             res = ast_play_and_wait(chan, "vm-and");
09516          else if (!res) {
09517             if ((vms->newmessages == 1))
09518                res = ast_play_and_wait(chan, "vm-message");
09519             else
09520                res = ast_play_and_wait(chan, "vm-messages");
09521          }
09522             
09523       }
09524       if (!res && vms->oldmessages) {
09525          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09526          if (!res)
09527             res = ast_play_and_wait(chan, "vm-Old");
09528          if (!res) {
09529             if (vms->oldmessages == 1)
09530                res = ast_play_and_wait(chan, "vm-message");
09531             else
09532                res = ast_play_and_wait(chan, "vm-messages");
09533          }
09534       }
09535       if (!res) {
09536          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09537             res = ast_play_and_wait(chan, "vm-no");
09538             if (!res)
09539                res = ast_play_and_wait(chan, "vm-messages");
09540          }
09541       }
09542    }
09543    return res;
09544 }
09545 
09546 /* DUTCH syntax */
09547 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
09548 {
09549    /* Introduce messages they have */
09550    int res;
09551    res = ast_play_and_wait(chan, "vm-youhave");
09552    if (!res) {
09553       if (vms->newmessages) {
09554          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09555          if (!res) {
09556             if (vms->newmessages == 1)
09557                res = ast_play_and_wait(chan, "vm-INBOXs");
09558             else
09559                res = ast_play_and_wait(chan, "vm-INBOX");
09560          }
09561          if (vms->oldmessages && !res)
09562             res = ast_play_and_wait(chan, "vm-and");
09563          else if (!res) {
09564             if ((vms->newmessages == 1))
09565                res = ast_play_and_wait(chan, "vm-message");
09566             else
09567                res = ast_play_and_wait(chan, "vm-messages");
09568          }
09569             
09570       }
09571       if (!res && vms->oldmessages) {
09572          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09573          if (!res) {
09574             if (vms->oldmessages == 1)
09575                res = ast_play_and_wait(chan, "vm-Olds");
09576             else
09577                res = ast_play_and_wait(chan, "vm-Old");
09578          }
09579          if (!res) {
09580             if (vms->oldmessages == 1)
09581                res = ast_play_and_wait(chan, "vm-message");
09582             else
09583                res = ast_play_and_wait(chan, "vm-messages");
09584          }
09585       }
09586       if (!res) {
09587          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09588             res = ast_play_and_wait(chan, "vm-no");
09589             if (!res)
09590                res = ast_play_and_wait(chan, "vm-messages");
09591          }
09592       }
09593    }
09594    return res;
09595 }
09596 
09597 /* PORTUGUESE syntax */
09598 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
09599 {
09600    /* Introduce messages they have */
09601    int res;
09602    res = ast_play_and_wait(chan, "vm-youhave");
09603    if (!res) {
09604       if (vms->newmessages) {
09605          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
09606          if (!res) {
09607             if ((vms->newmessages == 1)) {
09608                res = ast_play_and_wait(chan, "vm-message");
09609                if (!res)
09610                   res = ast_play_and_wait(chan, "vm-INBOXs");
09611             } else {
09612                res = ast_play_and_wait(chan, "vm-messages");
09613                if (!res)
09614                   res = ast_play_and_wait(chan, "vm-INBOX");
09615             }
09616          }
09617          if (vms->oldmessages && !res)
09618             res = ast_play_and_wait(chan, "vm-and");
09619       }
09620       if (!res && vms->oldmessages) {
09621          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
09622          if (!res) {
09623             if (vms->oldmessages == 1) {
09624                res = ast_play_and_wait(chan, "vm-message");
09625                if (!res)
09626                   res = ast_play_and_wait(chan, "vm-Olds");
09627             } else {
09628                res = ast_play_and_wait(chan, "vm-messages");
09629                if (!res)
09630                   res = ast_play_and_wait(chan, "vm-Old");
09631             }
09632          }
09633       }
09634       if (!res) {
09635          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09636             res = ast_play_and_wait(chan, "vm-no");
09637             if (!res)
09638                res = ast_play_and_wait(chan, "vm-messages");
09639          }
09640       }
09641    }
09642    return res;
09643 }
09644 
09645 
09646 /* CZECH syntax */
09647 /* in czech there must be declension of word new and message
09648  * czech        : english        : czech      : english
09649  * --------------------------------------------------------
09650  * vm-youhave   : you have 
09651  * vm-novou     : one new        : vm-zpravu  : message
09652  * vm-nove      : 2-4 new        : vm-zpravy  : messages
09653  * vm-novych    : 5-infinite new : vm-zprav   : messages
09654  * vm-starou   : one old
09655  * vm-stare     : 2-4 old 
09656  * vm-starych   : 5-infinite old
09657  * jednu        : one   - falling 4. 
09658  * vm-no        : no  ( no messages )
09659  */
09660 
09661 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
09662 {
09663    int res;
09664    res = ast_play_and_wait(chan, "vm-youhave");
09665    if (!res) {
09666       if (vms->newmessages) {
09667          if (vms->newmessages == 1) {
09668             res = ast_play_and_wait(chan, "digits/jednu");
09669          } else {
09670             res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09671          }
09672          if (!res) {
09673             if ((vms->newmessages == 1))
09674                res = ast_play_and_wait(chan, "vm-novou");
09675             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
09676                res = ast_play_and_wait(chan, "vm-nove");
09677             if (vms->newmessages > 4)
09678                res = ast_play_and_wait(chan, "vm-novych");
09679          }
09680          if (vms->oldmessages && !res)
09681             res = ast_play_and_wait(chan, "vm-and");
09682          else if (!res) {
09683             if ((vms->newmessages == 1))
09684                res = ast_play_and_wait(chan, "vm-zpravu");
09685             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
09686                res = ast_play_and_wait(chan, "vm-zpravy");
09687             if (vms->newmessages > 4)
09688                res = ast_play_and_wait(chan, "vm-zprav");
09689          }
09690       }
09691       if (!res && vms->oldmessages) {
09692          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09693          if (!res) {
09694             if ((vms->oldmessages == 1))
09695                res = ast_play_and_wait(chan, "vm-starou");
09696             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09697                res = ast_play_and_wait(chan, "vm-stare");
09698             if (vms->oldmessages > 4)
09699                res = ast_play_and_wait(chan, "vm-starych");
09700          }
09701          if (!res) {
09702             if ((vms->oldmessages == 1))
09703                res = ast_play_and_wait(chan, "vm-zpravu");
09704             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
09705                res = ast_play_and_wait(chan, "vm-zpravy");
09706             if (vms->oldmessages > 4)
09707                res = ast_play_and_wait(chan, "vm-zprav");
09708          }
09709       }
09710       if (!res) {
09711          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
09712             res = ast_play_and_wait(chan, "vm-no");
09713             if (!res)
09714                res = ast_play_and_wait(chan, "vm-zpravy");
09715          }
09716       }
09717    }
09718    return res;
09719 }
09720 
09721 /* CHINESE (Taiwan) syntax */
09722 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
09723 {
09724    int res;
09725    /* Introduce messages they have */
09726    res = ast_play_and_wait(chan, "vm-you");
09727 
09728    if (!res && vms->newmessages) {
09729       res = ast_play_and_wait(chan, "vm-have");
09730       if (!res)
09731          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09732       if (!res)
09733          res = ast_play_and_wait(chan, "vm-tong");
09734       if (!res)
09735          res = ast_play_and_wait(chan, "vm-INBOX");
09736       if (vms->oldmessages && !res)
09737          res = ast_play_and_wait(chan, "vm-and");
09738       else if (!res) 
09739          res = ast_play_and_wait(chan, "vm-messages");
09740    }
09741    if (!res && vms->oldmessages) {
09742       res = ast_play_and_wait(chan, "vm-have");
09743       if (!res)
09744          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09745       if (!res)
09746          res = ast_play_and_wait(chan, "vm-tong");
09747       if (!res)
09748          res = ast_play_and_wait(chan, "vm-Old");
09749       if (!res)
09750          res = ast_play_and_wait(chan, "vm-messages");
09751    }
09752    if (!res && !vms->oldmessages && !vms->newmessages) {
09753       res = ast_play_and_wait(chan, "vm-haveno");
09754       if (!res)
09755          res = ast_play_and_wait(chan, "vm-messages");
09756    }
09757    return res;
09758 }
09759 
09760 /* Vietnamese syntax */
09761 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
09762 {
09763    int res;
09764 
09765    /* Introduce messages they have */
09766    res = ast_play_and_wait(chan, "vm-youhave");
09767    if (!res) {
09768       if (vms->newmessages) {
09769          res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
09770          if (!res)
09771             res = ast_play_and_wait(chan, "vm-INBOX");
09772          if (vms->oldmessages && !res)
09773             res = ast_play_and_wait(chan, "vm-and");
09774       }
09775       if (!res && vms->oldmessages) {
09776          res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
09777          if (!res)
09778             res = ast_play_and_wait(chan, "vm-Old");        
09779       }
09780       if (!res) {
09781          if (!vms->oldmessages && !vms->newmessages) {
09782             res = ast_play_and_wait(chan, "vm-no");
09783             if (!res)
09784                res = ast_play_and_wait(chan, "vm-message");
09785          }
09786       }
09787    }
09788    return res;
09789 }
09790 
09791 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
09792 {
09793    char prefile[256];
09794    
09795    /* Notify the user that the temp greeting is set and give them the option to remove it */
09796    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
09797    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
09798       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
09799       if (ast_fileexists(prefile, NULL, NULL) > 0) {
09800          ast_play_and_wait(chan, "vm-tempgreetactive");
09801       }
09802       DISPOSE(prefile, -1);
09803    }
09804 
09805    /* Play voicemail intro - syntax is different for different languages */
09806    if (0) {
09807       return 0;
09808    } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) {  /* CZECH syntax */
09809       return vm_intro_cs(chan, vms);
09810    } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) {  /* deprecated CZECH syntax */
09811       static int deprecation_warning = 0;
09812       if (deprecation_warning++ % 10 == 0) {
09813          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
09814       }
09815       return vm_intro_cs(chan, vms);
09816    } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) {  /* GERMAN syntax */
09817       return vm_intro_de(chan, vms);
09818    } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) {  /* SPANISH syntax */
09819       return vm_intro_es(chan, vms);
09820    } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) {  /* FRENCH syntax */
09821       return vm_intro_fr(chan, vms);
09822    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {  /* GREEK syntax */
09823       return vm_intro_gr(chan, vms);
09824    } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* HEBREW syntax */
09825       return vm_intro_he(chan, vms);
09826    } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {  /* ITALIAN syntax */
09827       return vm_intro_it(chan, vms);
09828    } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) {  /* DUTCH syntax */
09829       return vm_intro_nl(chan, vms);
09830    } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) {  /* NORWEGIAN syntax */
09831       return vm_intro_no(chan, vms);
09832    } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {  /* POLISH syntax */
09833       return vm_intro_pl(chan, vms);
09834    } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
09835       return vm_intro_pt_BR(chan, vms);
09836    } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) {  /* PORTUGUESE syntax */
09837       return vm_intro_pt(chan, vms);
09838    } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) {  /* RUSSIAN syntax */
09839       return vm_intro_multilang(chan, vms, "n");
09840    } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) {  /* SWEDISH syntax */
09841       return vm_intro_se(chan, vms);
09842    } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) {  /* UKRAINIAN syntax */
09843       return vm_intro_multilang(chan, vms, "n");
09844    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
09845       return vm_intro_vi(chan, vms);
09846    } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
09847       return vm_intro_zh(chan, vms);
09848    } else {                                             /* Default to ENGLISH */
09849       return vm_intro_en(chan, vms);
09850    }
09851 }
09852 
09853 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09854 {
09855    int res = 0;
09856    /* Play instructions and wait for new command */
09857    while (!res) {
09858       if (vms->starting) {
09859          if (vms->lastmsg > -1) {
09860             if (skipadvanced)
09861                res = ast_play_and_wait(chan, "vm-onefor-full");
09862             else
09863                res = ast_play_and_wait(chan, "vm-onefor");
09864             if (!res)
09865                res = vm_play_folder_name(chan, vms->vmbox);
09866          }
09867          if (!res) {
09868             if (skipadvanced)
09869                res = ast_play_and_wait(chan, "vm-opts-full");
09870             else
09871                res = ast_play_and_wait(chan, "vm-opts");
09872          }
09873       } else {
09874          /* Added for additional help */
09875          if (skipadvanced) {
09876             res = ast_play_and_wait(chan, "vm-onefor-full");
09877             if (!res)
09878                res = vm_play_folder_name(chan, vms->vmbox);
09879             res = ast_play_and_wait(chan, "vm-opts-full");
09880          }
09881          /* Logic:
09882           * If the current message is not the first OR
09883           * if we're listening to the first new message and there are
09884           * also urgent messages, then prompt for navigation to the
09885           * previous message
09886           */
09887          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
09888             res = ast_play_and_wait(chan, "vm-prev");
09889          }
09890          if (!res && !skipadvanced)
09891             res = ast_play_and_wait(chan, "vm-advopts");
09892          if (!res)
09893             res = ast_play_and_wait(chan, "vm-repeat");
09894          /* Logic:
09895           * If we're not listening to the last message OR
09896           * we're listening to the last urgent message and there are
09897           * also new non-urgent messages, then prompt for navigation
09898           * to the next message
09899           */
09900          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
09901             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
09902             res = ast_play_and_wait(chan, "vm-next");
09903          }
09904          if (!res) {
09905             int curmsg_deleted;
09906 #ifdef IMAP_STORAGE
09907             ast_mutex_lock(&vms->lock);
09908 #endif
09909             curmsg_deleted = vms->deleted[vms->curmsg];
09910 #ifdef IMAP_STORAGE
09911             ast_mutex_unlock(&vms->lock);
09912 #endif
09913             if (!curmsg_deleted) {
09914                res = ast_play_and_wait(chan, "vm-delete");
09915             } else {
09916                res = ast_play_and_wait(chan, "vm-undelete");
09917             }
09918             if (!res) {
09919                res = ast_play_and_wait(chan, "vm-toforward");
09920             }
09921             if (!res) {
09922                res = ast_play_and_wait(chan, "vm-savemessage");
09923             }
09924          }
09925       }
09926       if (!res) {
09927          res = ast_play_and_wait(chan, "vm-helpexit");
09928       }
09929       if (!res)
09930          res = ast_waitfordigit(chan, 6000);
09931       if (!res) {
09932          vms->repeats++;
09933          if (vms->repeats > 2) {
09934             res = 't';
09935          }
09936       }
09937    }
09938    return res;
09939 }
09940 
09941 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
09942 {
09943    int res = 0;
09944    /* Play instructions and wait for new command */
09945    while (!res) {
09946       if (vms->lastmsg > -1) {
09947          res = ast_play_and_wait(chan, "vm-listen");
09948          if (!res)
09949             res = vm_play_folder_name(chan, vms->vmbox);
09950          if (!res)
09951             res = ast_play_and_wait(chan, "press");
09952          if (!res)
09953             res = ast_play_and_wait(chan, "digits/1");
09954       }
09955       if (!res)
09956          res = ast_play_and_wait(chan, "vm-opts");
09957       if (!res) {
09958          vms->starting = 0;
09959          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09960       }
09961    }
09962    return res;
09963 }
09964 
09965 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
09966 {
09967    if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
09968       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
09969    } else {             /* Default to ENGLISH */
09970       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
09971    }
09972 }
09973 
09974 
09975 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
09976 {
09977    int cmd = 0;
09978    int duration = 0;
09979    int tries = 0;
09980    char newpassword[80] = "";
09981    char newpassword2[80] = "";
09982    char prefile[PATH_MAX] = "";
09983    unsigned char buf[256];
09984    int bytes = 0;
09985 
09986    ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
09987    if (ast_adsi_available(chan)) {
09988       bytes += adsi_logo(buf + bytes);
09989       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
09990       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
09991       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
09992       bytes += ast_adsi_voice_mode(buf + bytes, 0);
09993       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
09994    }
09995 
09996    /* If forcename is set, have the user record their name */
09997    if (ast_test_flag(vmu, VM_FORCENAME)) {
09998       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
09999       if (ast_fileexists(prefile, NULL, NULL) < 1) {
10000          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10001          if (cmd < 0 || cmd == 't' || cmd == '#')
10002             return cmd;
10003       }
10004    }
10005 
10006    /* If forcegreetings is set, have the user record their greetings */
10007    if (ast_test_flag(vmu, VM_FORCEGREET)) {
10008       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
10009       if (ast_fileexists(prefile, NULL, NULL) < 1) {
10010          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10011          if (cmd < 0 || cmd == 't' || cmd == '#')
10012             return cmd;
10013       }
10014 
10015       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
10016       if (ast_fileexists(prefile, NULL, NULL) < 1) {
10017          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10018          if (cmd < 0 || cmd == 't' || cmd == '#')
10019             return cmd;
10020       }
10021    }
10022 
10023    /*
10024     * Change the password last since new users will be able to skip over any steps this one comes before
10025     * by hanging up and calling back to voicemail main since the password is used to verify new user status.
10026     */
10027    for (;;) {
10028       newpassword[1] = '\0';
10029       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
10030       if (cmd == '#')
10031          newpassword[0] = '\0';
10032       if (cmd < 0 || cmd == 't' || cmd == '#')
10033          return cmd;
10034       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
10035       if (cmd < 0 || cmd == 't' || cmd == '#')
10036          return cmd;
10037       cmd = check_password(vmu, newpassword); /* perform password validation */
10038       if (cmd != 0) {
10039          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
10040          cmd = ast_play_and_wait(chan, vm_invalid_password);
10041       } else {
10042          newpassword2[1] = '\0';
10043          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
10044          if (cmd == '#')
10045             newpassword2[0] = '\0';
10046          if (cmd < 0 || cmd == 't' || cmd == '#')
10047             return cmd;
10048          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
10049          if (cmd < 0 || cmd == 't' || cmd == '#')
10050             return cmd;
10051          if (!strcmp(newpassword, newpassword2))
10052             break;
10053          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
10054          cmd = ast_play_and_wait(chan, vm_mismatch);
10055       }
10056       if (++tries == 3)
10057          return -1;
10058       if (cmd != 0) {
10059          cmd = ast_play_and_wait(chan, vm_pls_try_again);
10060       }
10061    }
10062    if (pwdchange & PWDCHANGE_INTERNAL)
10063       vm_change_password(vmu, newpassword);
10064    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
10065       vm_change_password_shell(vmu, newpassword);
10066 
10067    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
10068    cmd = ast_play_and_wait(chan, vm_passchanged);
10069 
10070    return cmd;
10071 }
10072 
10073 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
10074 {
10075    int cmd = 0;
10076    int retries = 0;
10077    int duration = 0;
10078    char newpassword[80] = "";
10079    char newpassword2[80] = "";
10080    char prefile[PATH_MAX] = "";
10081    unsigned char buf[256];
10082    int bytes = 0;
10083 
10084    ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
10085    if (ast_adsi_available(chan)) {
10086       bytes += adsi_logo(buf + bytes);
10087       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
10088       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
10089       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
10090       bytes += ast_adsi_voice_mode(buf + bytes, 0);
10091       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
10092    }
10093    while ((cmd >= 0) && (cmd != 't')) {
10094       if (cmd)
10095          retries = 0;
10096       switch (cmd) {
10097       case '1': /* Record your unavailable message */
10098          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
10099          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10100          break;
10101       case '2':  /* Record your busy message */
10102          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
10103          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10104          break;
10105       case '3': /* Record greeting */
10106          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
10107          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10108          break;
10109       case '4':  /* manage the temporary greeting */
10110          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
10111          break;
10112       case '5': /* change password */
10113          if (vmu->password[0] == '-') {
10114             cmd = ast_play_and_wait(chan, "vm-no");
10115             break;
10116          }
10117          newpassword[1] = '\0';
10118          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
10119          if (cmd == '#')
10120             newpassword[0] = '\0';
10121          else {
10122             if (cmd < 0)
10123                break;
10124             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
10125                break;
10126             }
10127          }
10128          cmd = check_password(vmu, newpassword); /* perform password validation */
10129          if (cmd != 0) {
10130             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
10131             cmd = ast_play_and_wait(chan, vm_invalid_password);
10132             if (!cmd) {
10133                cmd = ast_play_and_wait(chan, vm_pls_try_again);
10134             }
10135             break;
10136          }
10137          newpassword2[1] = '\0';
10138          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
10139          if (cmd == '#')
10140             newpassword2[0] = '\0';
10141          else {
10142             if (cmd < 0)
10143                break;
10144 
10145             if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
10146                break;
10147             }
10148          }
10149          if (strcmp(newpassword, newpassword2)) {
10150             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
10151             cmd = ast_play_and_wait(chan, vm_mismatch);
10152             if (!cmd) {
10153                cmd = ast_play_and_wait(chan, vm_pls_try_again);
10154             }
10155             break;
10156          }
10157 
10158          if (pwdchange & PWDCHANGE_INTERNAL) {
10159             vm_change_password(vmu, newpassword);
10160          }
10161          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
10162             vm_change_password_shell(vmu, newpassword);
10163          }
10164 
10165          ast_debug(1, "User %s set password to %s of length %d\n",
10166             vms->username, newpassword, (int) strlen(newpassword));
10167          cmd = ast_play_and_wait(chan, vm_passchanged);
10168          break;
10169       case '*':
10170          cmd = 't';
10171          break;
10172       default:
10173          cmd = 0;
10174          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10175          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10176          if (ast_fileexists(prefile, NULL, NULL)) {
10177             cmd = ast_play_and_wait(chan, "vm-tmpexists");
10178          }
10179          DISPOSE(prefile, -1);
10180          if (!cmd) {
10181             cmd = ast_play_and_wait(chan, "vm-options");
10182          }
10183          if (!cmd) {
10184             cmd = ast_waitfordigit(chan, 6000);
10185          }
10186          if (!cmd) {
10187             retries++;
10188          }
10189          if (retries > 3) {
10190             cmd = 't';
10191          }
10192          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10193       }
10194    }
10195    if (cmd == 't')
10196       cmd = 0;
10197    return cmd;
10198 }
10199 
10200 /*!
10201  * \brief The handler for 'record a temporary greeting'. 
10202  * \param chan
10203  * \param vmu
10204  * \param vms
10205  * \param fmtc
10206  * \param record_gain
10207  *
10208  * This is option 4 from the mailbox options menu.
10209  * This function manages the following promptings:
10210  * 1: play / record / review the temporary greeting. : invokes play_record_review().
10211  * 2: remove (delete) the temporary greeting.
10212  * *: return to the main menu.
10213  *
10214  * \return zero on success, -1 on error.
10215  */
10216 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
10217 {
10218    int cmd = 0;
10219    int retries = 0;
10220    int duration = 0;
10221    char prefile[PATH_MAX] = "";
10222    unsigned char buf[256];
10223    int bytes = 0;
10224 
10225    if (ast_adsi_available(chan)) {
10226       bytes += adsi_logo(buf + bytes);
10227       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
10228       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
10229       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
10230       bytes += ast_adsi_voice_mode(buf + bytes, 0);
10231       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
10232    }
10233 
10234    ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
10235    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10236    while ((cmd >= 0) && (cmd != 't')) {
10237       if (cmd)
10238          retries = 0;
10239       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10240       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
10241          cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10242          if (cmd == -1) {
10243             break;
10244          }
10245          cmd = 't';  
10246       } else {
10247          switch (cmd) {
10248          case '1':
10249             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
10250             break;
10251          case '2':
10252             DELETE(prefile, -1, prefile, vmu);
10253             ast_play_and_wait(chan, "vm-tempremoved");
10254             cmd = 't';  
10255             break;
10256          case '*': 
10257             cmd = 't';
10258             break;
10259          default:
10260             cmd = ast_play_and_wait(chan,
10261                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
10262                   "vm-tempgreeting2" : "vm-tempgreeting");
10263             if (!cmd) {
10264                cmd = ast_waitfordigit(chan, 6000);
10265             }
10266             if (!cmd) {
10267                retries++;
10268             }
10269             if (retries > 3) {
10270                cmd = 't';
10271             }
10272             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10273          }
10274       }
10275       DISPOSE(prefile, -1);
10276    }
10277    if (cmd == 't')
10278       cmd = 0;
10279    return cmd;
10280 }
10281 
10282 /*!
10283  * \brief Greek syntax for 'You have N messages' greeting.
10284  * \param chan
10285  * \param vms
10286  * \param vmu
10287  *
10288  * \return zero on success, -1 on error.
10289  */   
10290 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10291 {
10292    int cmd = 0;
10293 
10294    if (vms->lastmsg > -1) {
10295       cmd = play_message(chan, vmu, vms);
10296    } else {
10297       cmd = ast_play_and_wait(chan, "vm-youhaveno");
10298       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
10299          if (!cmd) {
10300             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
10301             cmd = ast_play_and_wait(chan, vms->fn);
10302          }
10303          if (!cmd)
10304             cmd = ast_play_and_wait(chan, "vm-messages");
10305       } else {
10306          if (!cmd)
10307             cmd = ast_play_and_wait(chan, "vm-messages");
10308          if (!cmd) {
10309             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10310             cmd = ast_play_and_wait(chan, vms->fn);
10311          }
10312       }
10313    } 
10314    return cmd;
10315 }
10316 
10317 /* Hebrew Syntax */
10318 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10319 {
10320    int cmd = 0;
10321 
10322    if (vms->lastmsg > -1) {
10323       cmd = play_message(chan, vmu, vms);
10324    } else {
10325       if (!strcasecmp(vms->fn, "INBOX")) {
10326          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
10327       } else {
10328          cmd = ast_play_and_wait(chan, "vm-nomessages");
10329       }
10330    }
10331    return cmd;
10332 }
10333 
10334 /*! 
10335  * \brief Default English syntax for 'You have N messages' greeting.
10336  * \param chan
10337  * \param vms
10338  * \param vmu
10339  *
10340  * \return zero on success, -1 on error.
10341  */
10342 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10343 {
10344    int cmd = 0;
10345 
10346    if (vms->lastmsg > -1) {
10347       cmd = play_message(chan, vmu, vms);
10348    } else {
10349       cmd = ast_play_and_wait(chan, "vm-youhave");
10350       if (!cmd) 
10351          cmd = ast_play_and_wait(chan, "vm-no");
10352       if (!cmd) {
10353          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10354          cmd = ast_play_and_wait(chan, vms->fn);
10355       }
10356       if (!cmd)
10357          cmd = ast_play_and_wait(chan, "vm-messages");
10358    }
10359    return cmd;
10360 }
10361 
10362 /*! 
10363  *\brief Italian syntax for 'You have N messages' greeting.
10364  * \param chan
10365  * \param vms
10366  * \param vmu
10367  *
10368  * \return zero on success, -1 on error.
10369  */
10370 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10371 {
10372    int cmd;
10373 
10374    if (vms->lastmsg > -1) {
10375       cmd = play_message(chan, vmu, vms);
10376    } else {
10377       cmd = ast_play_and_wait(chan, "vm-no");
10378       if (!cmd)
10379          cmd = ast_play_and_wait(chan, "vm-message");
10380       if (!cmd) {
10381          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10382          cmd = ast_play_and_wait(chan, vms->fn);
10383       }
10384    }
10385    return cmd;
10386 }
10387 
10388 /*! 
10389  * \brief Spanish syntax for 'You have N messages' greeting.
10390  * \param chan
10391  * \param vms
10392  * \param vmu
10393  *
10394  * \return zero on success, -1 on error.
10395  */
10396 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10397 {
10398    int cmd;
10399 
10400    if (vms->lastmsg > -1) {
10401       cmd = play_message(chan, vmu, vms);
10402    } else {
10403       cmd = ast_play_and_wait(chan, "vm-youhaveno");
10404       if (!cmd)
10405          cmd = ast_play_and_wait(chan, "vm-messages");
10406       if (!cmd) {
10407          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10408          cmd = ast_play_and_wait(chan, vms->fn);
10409       }
10410    }
10411    return cmd;
10412 }
10413 
10414 /*! 
10415  * \brief Portuguese syntax for 'You have N messages' greeting.
10416  * \param chan
10417  * \param vms
10418  * \param vmu
10419  *
10420  * \return zero on success, -1 on error.
10421  */
10422 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10423 {
10424    int cmd;
10425 
10426    if (vms->lastmsg > -1) {
10427       cmd = play_message(chan, vmu, vms);
10428    } else {
10429       cmd = ast_play_and_wait(chan, "vm-no");
10430       if (!cmd) {
10431          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10432          cmd = ast_play_and_wait(chan, vms->fn);
10433       }
10434       if (!cmd)
10435          cmd = ast_play_and_wait(chan, "vm-messages");
10436    }
10437    return cmd;
10438 }
10439 
10440 /*! 
10441  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
10442  * \param chan
10443  * \param vms
10444  * \param vmu
10445  *
10446  * \return zero on success, -1 on error.
10447  */
10448 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10449 {
10450    int cmd;
10451 
10452    if (vms->lastmsg > -1) {
10453       cmd = play_message(chan, vmu, vms);
10454    } else {
10455       cmd = ast_play_and_wait(chan, "vm-you");
10456       if (!cmd) 
10457          cmd = ast_play_and_wait(chan, "vm-haveno");
10458       if (!cmd)
10459          cmd = ast_play_and_wait(chan, "vm-messages");
10460       if (!cmd) {
10461          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10462          cmd = ast_play_and_wait(chan, vms->fn);
10463       }
10464    }
10465    return cmd;
10466 }
10467 
10468 /*! 
10469  * \brief Vietnamese syntax for 'You have N messages' greeting.
10470  * \param chan
10471  * \param vms
10472  * \param vmu
10473  *
10474  * \return zero on success, -1 on error.
10475  */
10476 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10477 {
10478    int cmd = 0;
10479 
10480    if (vms->lastmsg > -1) {
10481       cmd = play_message(chan, vmu, vms);
10482    } else {
10483       cmd = ast_play_and_wait(chan, "vm-no");
10484       if (!cmd) {
10485          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10486          cmd = ast_play_and_wait(chan, vms->fn);
10487       }
10488    }
10489    return cmd;
10490 }
10491 
10492 /*!
10493  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
10494  * \param chan The channel for the current user. We read the language property from this.
10495  * \param vms passed into the language-specific vm_browse_messages function.
10496  * \param vmu passed into the language-specific vm_browse_messages function.
10497  * 
10498  * The method to be invoked is determined by the value of language code property in the user's channel.
10499  * The default (when unable to match) is to use english.
10500  *
10501  * \return zero on success, -1 on error.
10502  */
10503 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10504 {
10505    if (!strncasecmp(ast_channel_language(chan), "es", 2)) {         /* SPANISH */
10506       return vm_browse_messages_es(chan, vms, vmu);
10507    } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {  /* GREEK */
10508       return vm_browse_messages_gr(chan, vms, vmu);
10509    } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* HEBREW */
10510       return vm_browse_messages_he(chan, vms, vmu);
10511    } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {  /* ITALIAN */
10512       return vm_browse_messages_it(chan, vms, vmu);
10513    } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) {  /* PORTUGUESE */
10514       return vm_browse_messages_pt(chan, vms, vmu);
10515    } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {  /* VIETNAMESE */
10516       return vm_browse_messages_vi(chan, vms, vmu);
10517    } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) {  /* CHINESE (Taiwan) */
10518       return vm_browse_messages_zh(chan, vms, vmu);
10519    } else {                                             /* Default to English syntax */
10520       return vm_browse_messages_en(chan, vms, vmu);
10521    }
10522 }
10523 
10524 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
10525          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
10526          int skipuser, int max_logins, int silent)
10527 {
10528    int useadsi = 0, valid = 0, logretries = 0;
10529    char password[AST_MAX_EXTENSION]="", *passptr;
10530    struct ast_vm_user vmus, *vmu = NULL;
10531 
10532    /* If ADSI is supported, setup login screen */
10533    adsi_begin(chan, &useadsi);
10534    if (!skipuser && useadsi)
10535       adsi_login(chan);
10536    ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
10537    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", ast_channel_language(chan))) {
10538       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
10539       return -1;
10540    }
10541 
10542    /* Authenticate them and get their mailbox/password */
10543 
10544    while (!valid && (logretries < max_logins)) {
10545       /* Prompt for, and read in the username */
10546       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
10547          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
10548          return -1;
10549       }
10550       if (ast_strlen_zero(mailbox)) {
10551          if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
10552             ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
10553          } else {
10554             ast_verb(3, "Username not entered\n"); 
10555             return -1;
10556          }
10557       } else if (mailbox[0] == '*') {
10558          /* user entered '*' */
10559          ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
10560          if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
10561             S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
10562             return -1;
10563          }
10564          ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
10565          mailbox[0] = '\0';
10566       }
10567 
10568       if (useadsi)
10569          adsi_password(chan);
10570 
10571       if (!ast_strlen_zero(prefix)) {
10572          char fullusername[80] = "";
10573          ast_copy_string(fullusername, prefix, sizeof(fullusername));
10574          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
10575          ast_copy_string(mailbox, fullusername, mailbox_size);
10576       }
10577 
10578       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
10579       vmu = find_user(&vmus, context, mailbox);
10580       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
10581          /* saved password is blank, so don't bother asking */
10582          password[0] = '\0';
10583       } else {
10584          ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
10585          if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
10586             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
10587             return -1;
10588          }
10589          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
10590             ast_log(AST_LOG_WARNING, "Unable to read password\n");
10591             return -1;
10592          } else if (password[0] == '*') {
10593             /* user entered '*' */
10594             ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
10595             if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
10596                S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
10597                mailbox[0] = '*';
10598                return -1;
10599             }
10600             ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
10601             mailbox[0] = '\0';
10602             /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
10603             vmu = NULL;
10604          }
10605       }
10606 
10607       if (vmu) {
10608          passptr = vmu->password;
10609          if (passptr[0] == '-') passptr++;
10610       }
10611       if (vmu && !strcmp(passptr, password))
10612          valid++;
10613       else {
10614          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
10615          if (!ast_strlen_zero(prefix))
10616             mailbox[0] = '\0';
10617       }
10618       logretries++;
10619       if (!valid) {
10620          if (skipuser || logretries >= max_logins) {
10621             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
10622             if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
10623                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
10624                return -1;
10625             }
10626          } else {
10627             ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
10628             if (useadsi)
10629                adsi_login(chan);
10630             if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
10631                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
10632                return -1;
10633             }
10634          }
10635          if (ast_waitstream(chan, "")) /* Channel is hung up */
10636             return -1;
10637       }
10638    }
10639    if (!valid && (logretries >= max_logins)) {
10640       ast_stopstream(chan);
10641       ast_play_and_wait(chan, "vm-goodbye");
10642       return -1;
10643    }
10644    if (vmu && !skipuser) {
10645       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
10646    }
10647    return 0;
10648 }
10649 
10650 static int play_message_by_id_helper(struct ast_channel *chan,
10651    struct ast_vm_user *vmu,
10652    struct vm_state *vms,
10653    const char *msg_id)
10654 {
10655    if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
10656       return -1;
10657    }
10658    /* Found the msg, so play it back */
10659 
10660    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
10661    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
10662 
10663 #ifdef IMAP_STORAGE
10664    /*IMAP storage stores any prepended message from a forward
10665     * as a separate file from the rest of the message
10666     */
10667    if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
10668       wait_file(chan, vms, vms->introfn);
10669    }
10670 #endif
10671    if ((wait_file(chan, vms, vms->fn)) < 0) {
10672       ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
10673    } else {
10674 #ifdef IMAP_STORAGE
10675       ast_mutex_lock(&vms->lock);
10676 #endif
10677       vms->heard[vms->curmsg] = 1;
10678 #ifdef IMAP_STORAGE
10679       ast_mutex_unlock(&vms->lock);
10680 #endif
10681    }
10682 
10683    return 0;
10684 }
10685 
10686 /*!
10687  * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
10688  *
10689  * \retval 0 Success
10690  * \retval -1 Failure
10691  */
10692 static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
10693 {
10694    struct vm_state vms;
10695    struct ast_vm_user *vmu = NULL, vmus;
10696    int res = 0;
10697    int open = 0;
10698    int played = 0;
10699    int i;
10700 
10701    memset(&vmus, 0, sizeof(vmus));
10702    memset(&vms, 0, sizeof(vms));
10703 
10704    if (!(vmu = find_user(&vmus, context, mailbox))) {
10705       goto play_msg_cleanup;
10706    }
10707 
10708    /* Iterate through every folder, find the msg, and play it */
10709    for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
10710       ast_copy_string(vms.username, mailbox, sizeof(vms.username));
10711       vms.lastmsg = -1;
10712 
10713       /* open the mailbox state */
10714       if ((res = open_mailbox(&vms, vmu, i)) < 0) {
10715          ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
10716          res = -1;
10717          goto play_msg_cleanup;
10718       }
10719       open = 1;
10720 
10721       /* play msg if it exists in this mailbox */
10722       if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
10723          played = 1;
10724       }
10725 
10726       /* close mailbox */
10727       if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
10728          res = -1;
10729          goto play_msg_cleanup;
10730       }
10731       open = 0;
10732    }
10733 
10734 play_msg_cleanup:
10735    if (!played) {
10736       res = -1;
10737    }
10738 
10739    if (vmu && open) {
10740       close_mailbox(&vms, vmu);
10741    }
10742 
10743 #ifdef IMAP_STORAGE
10744    if (vmu) {
10745       vmstate_delete(&vms);
10746    }
10747 #endif
10748 
10749    return res;
10750 }
10751 
10752 static int vm_playmsgexec(struct ast_channel *chan, const char *data)
10753 {
10754    char *parse;
10755    char *mailbox = NULL;
10756    char *context = NULL;
10757    int res;
10758 
10759    AST_DECLARE_APP_ARGS(args,
10760       AST_APP_ARG(mailbox);
10761       AST_APP_ARG(msg_id);
10762    );
10763 
10764    if (ast_channel_state(chan) != AST_STATE_UP) {
10765       ast_debug(1, "Before ast_answer\n");
10766       ast_answer(chan);
10767    }
10768 
10769    if (ast_strlen_zero(data)) {
10770       return -1;
10771    }
10772 
10773    parse = ast_strdupa(data);
10774    AST_STANDARD_APP_ARGS(args, parse);
10775 
10776    if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
10777       return -1;
10778    }
10779 
10780    if ((context = strchr(args.mailbox, '@'))) {
10781       *context++ = '\0';
10782    }
10783    mailbox = args.mailbox;
10784 
10785    res = play_message_by_id(chan, mailbox, context, args.msg_id);
10786    pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
10787 
10788    return 0;
10789 }
10790 
10791 static int vm_execmain(struct ast_channel *chan, const char *data)
10792 {
10793    /* XXX This is, admittedly, some pretty horrendous code.  For some
10794       reason it just seemed a lot easier to do with GOTO's.  I feel
10795       like I'm back in my GWBASIC days. XXX */
10796    int res = -1;
10797    int cmd = 0;
10798    int valid = 0;
10799    char prefixstr[80] ="";
10800    char ext_context[256]="";
10801    int box;
10802    int useadsi = 0;
10803    int skipuser = 0;
10804    struct vm_state vms;
10805    struct ast_vm_user *vmu = NULL, vmus;
10806    char *context = NULL;
10807    int silentexit = 0;
10808    struct ast_flags flags = { 0 };
10809    signed char record_gain = 0;
10810    int play_auto = 0;
10811    int play_folder = 0;
10812    int in_urgent = 0;
10813 #ifdef IMAP_STORAGE
10814    int deleted = 0;
10815 #endif
10816 
10817    /* Add the vm_state to the active list and keep it active */
10818    memset(&vms, 0, sizeof(vms));
10819 
10820    vms.lastmsg = -1;
10821 
10822    memset(&vmus, 0, sizeof(vmus));
10823 
10824    ast_test_suite_event_notify("START", "Message: vm_execmain started");
10825    if (ast_channel_state(chan) != AST_STATE_UP) {
10826       ast_debug(1, "Before ast_answer\n");
10827       ast_answer(chan);
10828    }
10829 
10830    if (!ast_strlen_zero(data)) {
10831       char *opts[OPT_ARG_ARRAY_SIZE];
10832       char *parse;
10833       AST_DECLARE_APP_ARGS(args,
10834          AST_APP_ARG(argv0);
10835          AST_APP_ARG(argv1);
10836       );
10837 
10838       parse = ast_strdupa(data);
10839 
10840       AST_STANDARD_APP_ARGS(args, parse);
10841 
10842       if (args.argc == 2) {
10843          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10844             return -1;
10845          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10846             int gain;
10847             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
10848                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10849                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10850                   return -1;
10851                } else {
10852                   record_gain = (signed char) gain;
10853                }
10854             } else {
10855                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
10856             }
10857          }
10858          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
10859             play_auto = 1;
10860             if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
10861                /* See if it is a folder name first */
10862                if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
10863                   if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
10864                      play_folder = -1;
10865                   }
10866                } else {
10867                   play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
10868                }
10869             } else {
10870                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
10871             }
10872             if (play_folder > 9 || play_folder < 0) {
10873                ast_log(AST_LOG_WARNING,
10874                   "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
10875                   opts[OPT_ARG_PLAYFOLDER]);
10876                play_folder = 0;
10877             }
10878          }
10879       } else {
10880          /* old style options parsing */
10881          while (*(args.argv0)) {
10882             if (*(args.argv0) == 's')
10883                ast_set_flag(&flags, OPT_SILENT);
10884             else if (*(args.argv0) == 'p')
10885                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
10886             else 
10887                break;
10888             (args.argv0)++;
10889          }
10890 
10891       }
10892 
10893       valid = ast_test_flag(&flags, OPT_SILENT);
10894 
10895       if ((context = strchr(args.argv0, '@')))
10896          *context++ = '\0';
10897 
10898       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
10899          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
10900       else
10901          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
10902 
10903       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
10904          skipuser++;
10905       else
10906          valid = 0;
10907    }
10908 
10909    if (!valid)
10910       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
10911 
10912    ast_debug(1, "After vm_authenticate\n");
10913 
10914    if (vms.username[0] == '*') {
10915       ast_debug(1, "user pressed * in context '%s'\n", ast_channel_context(chan));
10916 
10917       /* user entered '*' */
10918       if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
10919          ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10920          res = 0; /* prevent hangup */
10921          goto out;
10922       }
10923    }
10924 
10925    if (!res) {
10926       valid = 1;
10927       if (!skipuser)
10928          vmu = &vmus;
10929    } else {
10930       res = 0;
10931    }
10932 
10933    /* If ADSI is supported, setup login screen */
10934    adsi_begin(chan, &useadsi);
10935 
10936    ast_test_suite_assert(valid);
10937    if (!valid) {
10938       goto out;
10939    }
10940    ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10941 
10942 #ifdef IMAP_STORAGE
10943    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10944    pthread_setspecific(ts_vmstate.key, &vms);
10945 
10946    vms.interactive = 1;
10947    vms.updated = 1;
10948    if (vmu)
10949       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10950    vmstate_insert(&vms);
10951    init_vm_state(&vms);
10952 #endif
10953 
10954    /* Set language from config to override channel language */
10955    if (!ast_strlen_zero(vmu->language))
10956       ast_channel_language_set(chan, vmu->language);
10957 
10958    /* Retrieve urgent, old and new message counts */
10959    ast_debug(1, "Before open_mailbox\n");
10960    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10961    if (res < 0)
10962       goto out;
10963    vms.oldmessages = vms.lastmsg + 1;
10964    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10965    /* check INBOX */
10966    res = open_mailbox(&vms, vmu, NEW_FOLDER);
10967    if (res < 0)
10968       goto out;
10969    vms.newmessages = vms.lastmsg + 1;
10970    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10971    /* Start in Urgent */
10972    in_urgent = 1;
10973    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10974    if (res < 0)
10975       goto out;
10976    vms.urgentmessages = vms.lastmsg + 1;
10977    ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10978 
10979    /* Select proper mailbox FIRST!! */
10980    if (play_auto) {
10981       ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10982       if (vms.urgentmessages) {
10983          in_urgent = 1;
10984          res = open_mailbox(&vms, vmu, 11);
10985       } else {
10986          in_urgent = 0;
10987          res = open_mailbox(&vms, vmu, play_folder);
10988       }
10989       if (res < 0)
10990          goto out;
10991 
10992       /* If there are no new messages, inform the user and hangup */
10993       if (vms.lastmsg == -1) {
10994          in_urgent = 0;
10995          cmd = vm_browse_messages(chan, &vms, vmu);
10996          res = 0;
10997          goto out;
10998       }
10999    } else {
11000       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
11001          /* If we only have old messages start here */
11002          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
11003          in_urgent = 0;
11004          play_folder = 1;
11005          if (res < 0)
11006             goto out;
11007       } else if (!vms.urgentmessages && vms.newmessages) {
11008          /* If we have new messages but none are urgent */
11009          in_urgent = 0;
11010          res = open_mailbox(&vms, vmu, NEW_FOLDER);
11011          if (res < 0)
11012             goto out;
11013       }
11014    }
11015 
11016    if (useadsi)
11017       adsi_status(chan, &vms);
11018    res = 0;
11019 
11020    /* Check to see if this is a new user */
11021    if (!strcasecmp(vmu->mailbox, vmu->password) && 
11022       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
11023       if (ast_play_and_wait(chan, "vm-newuser") == -1)
11024          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
11025       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
11026       if ((cmd == 't') || (cmd == '#')) {
11027          /* Timeout */
11028          ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
11029          res = 0;
11030          goto out;
11031       } else if (cmd < 0) {
11032          /* Hangup */
11033          ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
11034          res = -1;
11035          goto out;
11036       }
11037    }
11038 #ifdef IMAP_STORAGE
11039       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
11040       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
11041          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
11042          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
11043       }
11044       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
11045       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
11046          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
11047          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
11048       }
11049 #endif
11050 
11051    ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
11052    if (play_auto) {
11053       cmd = '1';
11054    } else {
11055       cmd = vm_intro(chan, vmu, &vms);
11056    }
11057    ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
11058 
11059    vms.repeats = 0;
11060    vms.starting = 1;
11061    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
11062       /* Run main menu */
11063       switch (cmd) {
11064       case '1': /* First message */
11065          vms.curmsg = 0;
11066          /* Fall through */
11067       case '5': /* Play current message */
11068          ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
11069          cmd = vm_browse_messages(chan, &vms, vmu);
11070          break;
11071       case '2': /* Change folders */
11072          ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
11073          if (useadsi)
11074             adsi_folders(chan, 0, "Change to folder...");
11075 
11076          cmd = get_folder2(chan, "vm-changeto", 0);
11077          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
11078          if (cmd == '#') {
11079             cmd = 0;
11080          } else if (cmd > 0) {
11081             cmd = cmd - '0';
11082             res = close_mailbox(&vms, vmu);
11083             if (res == ERROR_LOCK_PATH)
11084                goto out;
11085             /* If folder is not urgent, set in_urgent to zero! */
11086             if (cmd != 11) in_urgent = 0;
11087             res = open_mailbox(&vms, vmu, cmd);
11088             if (res < 0)
11089                goto out;
11090             play_folder = cmd;
11091             cmd = 0;
11092          }
11093          if (useadsi)
11094             adsi_status2(chan, &vms);
11095 
11096          if (!cmd) {
11097             cmd = vm_play_folder_name(chan, vms.vmbox);
11098          }
11099 
11100          vms.starting = 1;
11101          vms.curmsg = 0;
11102          break;
11103       case '3': /* Advanced options */
11104          ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
11105          cmd = 0;
11106          vms.repeats = 0;
11107          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
11108             switch (cmd) {
11109             case '1': /* Reply */
11110                if (vms.lastmsg > -1 && !vms.starting) {
11111                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
11112                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
11113                      res = cmd;
11114                      goto out;
11115                   }
11116                } else {
11117                   cmd = ast_play_and_wait(chan, "vm-sorry");
11118                }
11119                cmd = 't';
11120                break;
11121             case '2': /* Callback */
11122                if (!vms.starting)
11123                   ast_verb(3, "Callback Requested\n");
11124                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
11125                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
11126                   if (cmd == 9) {
11127                      silentexit = 1;
11128                      goto out;
11129                   } else if (cmd == ERROR_LOCK_PATH) {
11130                      res = cmd;
11131                      goto out;
11132                   }
11133                } else {
11134                   cmd = ast_play_and_wait(chan, "vm-sorry");
11135                }
11136                cmd = 't';
11137                break;
11138             case '3': /* Envelope */
11139                if (vms.lastmsg > -1 && !vms.starting) {
11140                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
11141                   if (cmd == ERROR_LOCK_PATH) {
11142                      res = cmd;
11143                      goto out;
11144                   }
11145                } else {
11146                   cmd = ast_play_and_wait(chan, "vm-sorry");
11147                }
11148                cmd = 't';
11149                break;
11150             case '4': /* Dialout */
11151                if (!ast_strlen_zero(vmu->dialout)) {
11152                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
11153                   if (cmd == 9) {
11154                      silentexit = 1;
11155                      goto out;
11156                   }
11157                } else {
11158                   cmd = ast_play_and_wait(chan, "vm-sorry");
11159                }
11160                cmd = 't';
11161                break;
11162 
11163             case '5': /* Leave VoiceMail */
11164                if (ast_test_flag(vmu, VM_SVMAIL)) {
11165                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
11166                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
11167                      res = cmd;
11168                      goto out;
11169                   }
11170                } else {
11171                   cmd = ast_play_and_wait(chan, "vm-sorry");
11172                }
11173                cmd = 't';
11174                break;
11175 
11176             case '*': /* Return to main menu */
11177                cmd = 't';
11178                break;
11179 
11180             default:
11181                cmd = 0;
11182                if (!vms.starting) {
11183                   cmd = ast_play_and_wait(chan, "vm-toreply");
11184                }
11185                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
11186                   cmd = ast_play_and_wait(chan, "vm-tocallback");
11187                }
11188                if (!cmd && !vms.starting) {
11189                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
11190                }
11191                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
11192                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
11193                }
11194                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
11195                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
11196                }
11197                if (!cmd) {
11198                   cmd = ast_play_and_wait(chan, "vm-starmain");
11199                }
11200                if (!cmd) {
11201                   cmd = ast_waitfordigit(chan, 6000);
11202                }
11203                if (!cmd) {
11204                   vms.repeats++;
11205                }
11206                if (vms.repeats > 3) {
11207                   cmd = 't';
11208                }
11209                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
11210             }
11211          }
11212          if (cmd == 't') {
11213             cmd = 0;
11214             vms.repeats = 0;
11215          }
11216          break;
11217       case '4': /* Go to the previous message */
11218          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
11219          if (vms.curmsg > 0) {
11220             vms.curmsg--;
11221             cmd = play_message(chan, vmu, &vms);
11222          } else {
11223             /* Check if we were listening to new
11224                messages.  If so, go to Urgent messages
11225                instead of saying "no more messages"
11226             */
11227             if (in_urgent == 0 && vms.urgentmessages > 0) {
11228                /* Check for Urgent messages */
11229                in_urgent = 1;
11230                res = close_mailbox(&vms, vmu);
11231                if (res == ERROR_LOCK_PATH)
11232                   goto out;
11233                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
11234                if (res < 0)
11235                   goto out;
11236                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
11237                vms.curmsg = vms.lastmsg;
11238                if (vms.lastmsg < 0) {
11239                   cmd = ast_play_and_wait(chan, "vm-nomore");
11240                }
11241             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
11242                vms.curmsg = vms.lastmsg;
11243                cmd = play_message(chan, vmu, &vms);
11244             } else {
11245                cmd = ast_play_and_wait(chan, "vm-nomore");
11246             }
11247          }
11248          break;
11249       case '6': /* Go to the next message */
11250          ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
11251          if (vms.curmsg < vms.lastmsg) {
11252             vms.curmsg++;
11253             cmd = play_message(chan, vmu, &vms);
11254          } else {
11255             if (in_urgent && vms.newmessages > 0) {
11256                /* Check if we were listening to urgent
11257                 * messages.  If so, go to regular new messages
11258                 * instead of saying "no more messages"
11259                 */
11260                in_urgent = 0;
11261                res = close_mailbox(&vms, vmu);
11262                if (res == ERROR_LOCK_PATH)
11263                   goto out;
11264                res = open_mailbox(&vms, vmu, NEW_FOLDER);
11265                if (res < 0)
11266                   goto out;
11267                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
11268                vms.curmsg = -1;
11269                if (vms.lastmsg < 0) {
11270                   cmd = ast_play_and_wait(chan, "vm-nomore");
11271                }
11272             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
11273                vms.curmsg = 0;
11274                cmd = play_message(chan, vmu, &vms);
11275             } else {
11276                cmd = ast_play_and_wait(chan, "vm-nomore");
11277             }
11278          }
11279          break;
11280       case '7': /* Delete the current message */
11281          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
11282             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
11283             if (useadsi)
11284                adsi_delete(chan, &vms);
11285             if (vms.deleted[vms.curmsg]) {
11286                if (play_folder == 0) {
11287                   if (in_urgent) {
11288                      vms.urgentmessages--;
11289                   } else {
11290                      vms.newmessages--;
11291                   }
11292                }
11293                else if (play_folder == 1)
11294                   vms.oldmessages--;
11295                cmd = ast_play_and_wait(chan, "vm-deleted");
11296             } else {
11297                if (play_folder == 0) {
11298                   if (in_urgent) {
11299                      vms.urgentmessages++;
11300                   } else {
11301                      vms.newmessages++;
11302                   }
11303                }
11304                else if (play_folder == 1)
11305                   vms.oldmessages++;
11306                cmd = ast_play_and_wait(chan, "vm-undeleted");
11307             }
11308             if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
11309                if (vms.curmsg < vms.lastmsg) {
11310                   vms.curmsg++;
11311                   cmd = play_message(chan, vmu, &vms);
11312                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
11313                   vms.curmsg = 0;
11314                   cmd = play_message(chan, vmu, &vms);
11315                } else {
11316                   /* Check if we were listening to urgent
11317                      messages.  If so, go to regular new messages
11318                      instead of saying "no more messages"
11319                   */
11320                   if (in_urgent == 1) {
11321                      /* Check for new messages */
11322                      in_urgent = 0;
11323                      res = close_mailbox(&vms, vmu);
11324                      if (res == ERROR_LOCK_PATH)
11325                         goto out;
11326                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
11327                      if (res < 0)
11328                         goto out;
11329                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
11330                      vms.curmsg = -1;
11331                      if (vms.lastmsg < 0) {
11332                         cmd = ast_play_and_wait(chan, "vm-nomore");
11333                      }
11334                   } else {
11335                      cmd = ast_play_and_wait(chan, "vm-nomore");
11336                   }
11337                }
11338             }
11339          } else /* Delete not valid if we haven't selected a message */
11340             cmd = 0;
11341 #ifdef IMAP_STORAGE
11342          deleted = 1;
11343 #endif
11344          break;
11345 
11346       case '8': /* Forward the current message */
11347          if (vms.lastmsg > -1) {
11348             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
11349             if (cmd == ERROR_LOCK_PATH) {
11350                res = cmd;
11351                goto out;
11352             }
11353          } else {
11354             /* Check if we were listening to urgent
11355                messages.  If so, go to regular new messages
11356                instead of saying "no more messages"
11357             */
11358             if (in_urgent == 1 && vms.newmessages > 0) {
11359                /* Check for new messages */
11360                in_urgent = 0;
11361                res = close_mailbox(&vms, vmu);
11362                if (res == ERROR_LOCK_PATH)
11363                   goto out;
11364                res = open_mailbox(&vms, vmu, NEW_FOLDER);
11365                if (res < 0)
11366                   goto out;
11367                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
11368                vms.curmsg = -1;
11369                if (vms.lastmsg < 0) {
11370                   cmd = ast_play_and_wait(chan, "vm-nomore");
11371                }
11372             } else {
11373                cmd = ast_play_and_wait(chan, "vm-nomore");
11374             }
11375          }
11376          break;
11377       case '9': /* Save message to folder */
11378          ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
11379          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
11380             /* No message selected */
11381             cmd = 0;
11382             break;
11383          }
11384          if (useadsi)
11385             adsi_folders(chan, 1, "Save to folder...");
11386          cmd = get_folder2(chan, "vm-savefolder", 1);
11387          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
11388          box = 0; /* Shut up compiler */
11389          if (cmd == '#') {
11390             cmd = 0;
11391             break;
11392          } else if (cmd > 0) {
11393             box = cmd = cmd - '0';
11394             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd, NULL, 0);
11395             if (cmd == ERROR_LOCK_PATH) {
11396                res = cmd;
11397                goto out;
11398 #ifndef IMAP_STORAGE
11399             } else if (!cmd) {
11400                vms.deleted[vms.curmsg] = 1;
11401 #endif
11402             } else {
11403                vms.deleted[vms.curmsg] = 0;
11404                vms.heard[vms.curmsg] = 0;
11405             }
11406          }
11407          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
11408          if (useadsi)
11409             adsi_message(chan, &vms);
11410          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
11411          if (!cmd) {
11412             cmd = ast_play_and_wait(chan, "vm-message");
11413             if (!cmd) 
11414                cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
11415             if (!cmd)
11416                cmd = ast_play_and_wait(chan, "vm-savedto");
11417             if (!cmd)
11418                cmd = vm_play_folder_name(chan, vms.fn);
11419          } else {
11420             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
11421          }
11422          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
11423             if (vms.curmsg < vms.lastmsg) {
11424                vms.curmsg++;
11425                cmd = play_message(chan, vmu, &vms);
11426             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
11427                vms.curmsg = 0;
11428                cmd = play_message(chan, vmu, &vms);
11429             } else {
11430                /* Check if we were listening to urgent
11431                   messages.  If so, go to regular new messages
11432                   instead of saying "no more messages"
11433                */
11434                if (in_urgent == 1 && vms.newmessages > 0) {
11435                   /* Check for new messages */
11436                   in_urgent = 0;
11437                   res = close_mailbox(&vms, vmu);
11438                   if (res == ERROR_LOCK_PATH)
11439                      goto out;
11440                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
11441                   if (res < 0)
11442                      goto out;
11443                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
11444                   vms.curmsg = -1;
11445                   if (vms.lastmsg < 0) {
11446                      cmd = ast_play_and_wait(chan, "vm-nomore");
11447                   }
11448                } else {
11449                   cmd = ast_play_and_wait(chan, "vm-nomore");
11450                }
11451             }
11452          }
11453          break;
11454       case '*': /* Help */
11455          if (!vms.starting) {
11456             cmd = ast_play_and_wait(chan, "vm-onefor");
11457             if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
11458                cmd = ast_play_and_wait(chan, "vm-for");
11459             }
11460             if (!cmd)
11461                cmd = vm_play_folder_name(chan, vms.vmbox);
11462             if (!cmd)
11463                cmd = ast_play_and_wait(chan, "vm-opts");
11464             if (!cmd)
11465                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
11466          } else
11467             cmd = 0;
11468          break;
11469       case '0': /* Mailbox options */
11470          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
11471          if (useadsi)
11472             adsi_status(chan, &vms);
11473          break;
11474       default: /* Nothing */
11475          ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
11476          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
11477          break;
11478       }
11479    }
11480    if ((cmd == 't') || (cmd == '#')) {
11481       /* Timeout */
11482       res = 0;
11483    } else {
11484       /* Hangup */
11485       res = -1;
11486    }
11487 
11488 out:
11489    if (res > -1) {
11490       ast_stopstream(chan);
11491       adsi_goodbye(chan);
11492       if (valid && res != OPERATOR_EXIT) {
11493          if (silentexit)
11494             res = ast_play_and_wait(chan, "vm-dialout");
11495          else 
11496             res = ast_play_and_wait(chan, "vm-goodbye");
11497       }
11498       if ((valid && res > 0) || res == OPERATOR_EXIT) {
11499          res = 0;
11500       }
11501       if (useadsi)
11502          ast_adsi_unload_session(chan);
11503    }
11504    if (vmu)
11505       close_mailbox(&vms, vmu);
11506    if (valid) {
11507       int new = 0, old = 0, urgent = 0;
11508       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
11509       /*** DOCUMENTATION
11510          <managerEventInstance>
11511             <synopsis>Raised when a user has finished listening to their messages.</synopsis>
11512          </managerEventInstance>
11513       ***/
11514       ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
11515       /* Urgent flag not passwd to externnotify here */
11516       run_externnotify(vmu->context, vmu->mailbox, NULL);
11517       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
11518       queue_mwi_event(ext_context, urgent, new, old);
11519    }
11520 #ifdef IMAP_STORAGE
11521    /* expunge message - use UID Expunge if supported on IMAP server*/
11522    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
11523    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
11524       ast_mutex_lock(&vms.lock);
11525 #ifdef HAVE_IMAP_TK2006
11526       if (LEVELUIDPLUS (vms.mailstream)) {
11527          mail_expunge_full(vms.mailstream, NIL, EX_UID);
11528       } else
11529 #endif
11530          mail_expunge(vms.mailstream);
11531       ast_mutex_unlock(&vms.lock);
11532    }
11533    /*  before we delete the state, we should copy pertinent info
11534     *  back to the persistent model */
11535    if (vmu) {
11536       vmstate_delete(&vms);
11537    }
11538 #endif
11539    if (vmu)
11540       free_user(vmu);
11541 
11542 #ifdef IMAP_STORAGE
11543    pthread_setspecific(ts_vmstate.key, NULL);
11544 #endif
11545    return res;
11546 }
11547 
11548 static int vm_exec(struct ast_channel *chan, const char *data)
11549 {
11550    int res = 0;
11551    char *tmp;
11552    struct leave_vm_options leave_options;
11553    struct ast_flags flags = { 0 };
11554    char *opts[OPT_ARG_ARRAY_SIZE];
11555    AST_DECLARE_APP_ARGS(args,
11556       AST_APP_ARG(argv0);
11557       AST_APP_ARG(argv1);
11558    );
11559    
11560    memset(&leave_options, 0, sizeof(leave_options));
11561 
11562    if (ast_channel_state(chan) != AST_STATE_UP)
11563       ast_answer(chan);
11564 
11565    if (!ast_strlen_zero(data)) {
11566       tmp = ast_strdupa(data);
11567       AST_STANDARD_APP_ARGS(args, tmp);
11568       if (args.argc == 2) {
11569          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
11570             return -1;
11571          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
11572          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
11573             int gain;
11574 
11575             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
11576                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
11577                return -1;
11578             } else {
11579                leave_options.record_gain = (signed char) gain;
11580             }
11581          }
11582          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
11583             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
11584                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
11585          }
11586       }
11587    } else {
11588       char temp[256];
11589       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
11590       if (res < 0)
11591          return res;
11592       if (ast_strlen_zero(temp))
11593          return 0;
11594       args.argv0 = ast_strdupa(temp);
11595    }
11596 
11597    res = leave_voicemail(chan, args.argv0, &leave_options);
11598    if (res == 't') {
11599       ast_play_and_wait(chan, "vm-goodbye");
11600       res = 0;
11601    }
11602 
11603    if (res == OPERATOR_EXIT) {
11604       res = 0;
11605    }
11606 
11607    if (res == ERROR_LOCK_PATH) {
11608       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
11609       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
11610       res = 0;
11611    }
11612 
11613    return res;
11614 }
11615 
11616 static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
11617 {
11618    struct ast_variable *var;
11619    struct ast_category *cat;
11620    generate_msg_id(id);
11621 
11622    var = ast_variable_new("msg_id", id, "");
11623    if (!var) {
11624       return -1;
11625    }
11626 
11627    cat = ast_category_get(msg_cfg, "message");
11628    if (!cat) {
11629       ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
11630       ast_variables_destroy(var);
11631       return -1;
11632    }
11633 
11634    ast_variable_append(cat, var);
11635 
11636    if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
11637       ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
11638       return -1;
11639    }
11640 
11641    UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
11642    return 0;
11643 }
11644 
11645 static struct ast_vm_user *find_or_create(const char *context, const char *box)
11646 {
11647    struct ast_vm_user *vmu;
11648 
11649    if (!ast_strlen_zero(box) && box[0] == '*') {
11650       ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character.  The '*' character,"
11651             "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
11652             "\n\tpredefined extension 'a'.  A mailbox or password beginning with '*' is not valid"
11653             "\n\tand will be ignored.\n", box, context);
11654       return NULL;
11655    }
11656 
11657    AST_LIST_TRAVERSE(&users, vmu, list) {
11658       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
11659          if (strcasecmp(vmu->context, context)) {
11660             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
11661                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
11662                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
11663                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
11664          }
11665          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
11666          return NULL;
11667       }
11668       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
11669          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
11670          return NULL;
11671       }
11672    }
11673    
11674    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
11675       return NULL;
11676    
11677    ast_copy_string(vmu->context, context, sizeof(vmu->context));
11678    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
11679 
11680    AST_LIST_INSERT_TAIL(&users, vmu, list);
11681    
11682    return vmu;
11683 }
11684 
11685 static int append_mailbox(const char *context, const char *box, const char *data)
11686 {
11687    /* Assumes lock is already held */
11688    char *tmp;
11689    char *stringp;
11690    char *s;
11691    struct ast_vm_user *vmu;
11692    char *mailbox_full;
11693    int new = 0, old = 0, urgent = 0;
11694    char secretfn[PATH_MAX] = "";
11695 
11696    tmp = ast_strdupa(data);
11697 
11698    if (!(vmu = find_or_create(context, box)))
11699       return -1;
11700 
11701    populate_defaults(vmu);
11702 
11703    stringp = tmp;
11704    if ((s = strsep(&stringp, ","))) {
11705       if (!ast_strlen_zero(s) && s[0] == '*') {
11706          ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
11707             "\n\tmust be reset in voicemail.conf.\n", box);
11708       }
11709       /* assign password regardless of validity to prevent NULL password from being assigned */
11710       ast_copy_string(vmu->password, s, sizeof(vmu->password));
11711    }
11712    if (stringp && (s = strsep(&stringp, ","))) {
11713       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
11714    }
11715    if (stringp && (s = strsep(&stringp, ","))) {
11716       ast_copy_string(vmu->email, s, sizeof(vmu->email));
11717    }
11718    if (stringp && (s = strsep(&stringp, ","))) {
11719       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
11720    }
11721    if (stringp && (s = strsep(&stringp, ","))) {
11722       apply_options(vmu, s);
11723    }
11724 
11725    switch (vmu->passwordlocation) {
11726    case OPT_PWLOC_SPOOLDIR:
11727       snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
11728       read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
11729    }
11730 
11731    mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
11732    strcpy(mailbox_full, box);
11733    strcat(mailbox_full, "@");
11734    strcat(mailbox_full, context);
11735 
11736    inboxcount2(mailbox_full, &urgent, &new, &old);
11737    queue_mwi_event(mailbox_full, urgent, new, old);
11738 
11739    return 0;
11740 }
11741 
11742 AST_TEST_DEFINE(test_voicemail_vmuser)
11743 {
11744    int res = 0;
11745    struct ast_vm_user *vmu;
11746    /* language parameter seems to only be used for display in manager action */
11747    static const char options_string[] = "attach=yes|attachfmt=wav49|"
11748       "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
11749       "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
11750       "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
11751       "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
11752       "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
11753       "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
11754       "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
11755       "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
11756 #ifdef IMAP_STORAGE
11757    static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
11758       "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
11759 #endif
11760 
11761    switch (cmd) {
11762    case TEST_INIT:
11763       info->name = "vmuser";
11764       info->category = "/apps/app_voicemail/";
11765       info->summary = "Vmuser unit test";
11766       info->description =
11767          "This tests passing all supported parameters to apply_options, the voicemail user config parser";
11768       return AST_TEST_NOT_RUN;
11769    case TEST_EXECUTE:
11770       break;
11771    }
11772 
11773    if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
11774       return AST_TEST_NOT_RUN;
11775    }
11776    populate_defaults(vmu);
11777    ast_set_flag(vmu, VM_ALLOCED);
11778 
11779    apply_options(vmu, options_string);
11780 
11781    if (!ast_test_flag(vmu, VM_ATTACH)) {
11782       ast_test_status_update(test, "Parse failure for attach option\n");
11783       res = 1;
11784    }
11785    if (strcasecmp(vmu->attachfmt, "wav49")) {
11786       ast_test_status_update(test, "Parse failure for attachftm option\n");
11787       res = 1;
11788    }
11789    if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
11790       ast_test_status_update(test, "Parse failure for serveremail option\n");
11791       res = 1;
11792    }
11793    if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
11794       ast_test_status_update(test, "Parse failure for emailsubject option\n");
11795       res = 1;
11796    }
11797    if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
11798       ast_test_status_update(test, "Parse failure for emailbody option\n");
11799       res = 1;
11800    }
11801    if (strcasecmp(vmu->zonetag, "central")) {
11802       ast_test_status_update(test, "Parse failure for tz option\n");
11803       res = 1;
11804    }
11805    if (!ast_test_flag(vmu, VM_DELETE)) {
11806       ast_test_status_update(test, "Parse failure for delete option\n");
11807       res = 1;
11808    }
11809    if (!ast_test_flag(vmu, VM_SAYCID)) {
11810       ast_test_status_update(test, "Parse failure for saycid option\n");
11811       res = 1;
11812    }
11813    if (!ast_test_flag(vmu, VM_SVMAIL)) {
11814       ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
11815       res = 1;
11816    }
11817    if (!ast_test_flag(vmu, VM_REVIEW)) {
11818       ast_test_status_update(test, "Parse failure for review option\n");
11819       res = 1;
11820    }
11821    if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
11822       ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
11823       res = 1;
11824    }
11825    if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
11826       ast_test_status_update(test, "Parse failure for messagewrap option\n");
11827       res = 1;
11828    }
11829    if (!ast_test_flag(vmu, VM_OPERATOR)) {
11830       ast_test_status_update(test, "Parse failure for operator option\n");
11831       res = 1;
11832    }
11833    if (!ast_test_flag(vmu, VM_ENVELOPE)) {
11834       ast_test_status_update(test, "Parse failure for envelope option\n");
11835       res = 1;
11836    }
11837    if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
11838       ast_test_status_update(test, "Parse failure for moveheard option\n");
11839       res = 1;
11840    }
11841    if (!ast_test_flag(vmu, VM_SAYDURATION)) {
11842       ast_test_status_update(test, "Parse failure for sayduration option\n");
11843       res = 1;
11844    }
11845    if (vmu->saydurationm != 5) {
11846       ast_test_status_update(test, "Parse failure for saydurationm option\n");
11847       res = 1;
11848    }
11849    if (!ast_test_flag(vmu, VM_FORCENAME)) {
11850       ast_test_status_update(test, "Parse failure for forcename option\n");
11851       res = 1;
11852    }
11853    if (!ast_test_flag(vmu, VM_FORCEGREET)) {
11854       ast_test_status_update(test, "Parse failure for forcegreetings option\n");
11855       res = 1;
11856    }
11857    if (strcasecmp(vmu->callback, "somecontext")) {
11858       ast_test_status_update(test, "Parse failure for callbacks option\n");
11859       res = 1;
11860    }
11861    if (strcasecmp(vmu->dialout, "somecontext2")) {
11862       ast_test_status_update(test, "Parse failure for dialout option\n");
11863       res = 1;
11864    }
11865    if (strcasecmp(vmu->exit, "somecontext3")) {
11866       ast_test_status_update(test, "Parse failure for exitcontext option\n");
11867       res = 1;
11868    }
11869    if (vmu->minsecs != 10) {
11870       ast_test_status_update(test, "Parse failure for minsecs option\n");
11871       res = 1;
11872    }
11873    if (vmu->maxsecs != 100) {
11874       ast_test_status_update(test, "Parse failure for maxsecs option\n");
11875       res = 1;
11876    }
11877    if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
11878       ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
11879       res = 1;
11880    }
11881    if (vmu->maxdeletedmsg != 50) {
11882       ast_test_status_update(test, "Parse failure for backupdeleted option\n");
11883       res = 1;
11884    }
11885    if (vmu->volgain != 1.3) {
11886       ast_test_status_update(test, "Parse failure for volgain option\n");
11887       res = 1;
11888    }
11889    if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
11890       ast_test_status_update(test, "Parse failure for passwordlocation option\n");
11891       res = 1;
11892    }
11893 #ifdef IMAP_STORAGE
11894    apply_options(vmu, option_string2);
11895 
11896    if (strcasecmp(vmu->imapuser, "imapuser")) {
11897       ast_test_status_update(test, "Parse failure for imapuser option\n");
11898       res = 1;
11899    }
11900    if (strcasecmp(vmu->imappassword, "imappasswd")) {
11901       ast_test_status_update(test, "Parse failure for imappasswd option\n");
11902       res = 1;
11903    }
11904    if (strcasecmp(vmu->imapfolder, "INBOX")) {
11905       ast_test_status_update(test, "Parse failure for imapfolder option\n");
11906       res = 1;
11907    }
11908    if (strcasecmp(vmu->imapvmshareid, "6000")) {
11909       ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
11910       res = 1;
11911    }
11912    if (strcasecmp(vmu->imapserver, "imapserver")) {
11913       ast_test_status_update(test, "Parse failure for imapserver option\n");
11914       res = 1;
11915    }
11916    if (strcasecmp(vmu->imapport, "1234")) {
11917       ast_test_status_update(test, "Parse failure for imapport option\n");
11918       res = 1;
11919    }
11920    if (strcasecmp(vmu->imapflags, "flagged")) {
11921       ast_test_status_update(test, "Parse failure for imapflags option\n");
11922       res = 1;
11923    }
11924 #endif
11925 
11926    free_user(vmu);
11927    return res ? AST_TEST_FAIL : AST_TEST_PASS;
11928 }
11929 
11930 static int vm_box_exists(struct ast_channel *chan, const char *data) 
11931 {
11932    struct ast_vm_user svm;
11933    char *context, *box;
11934    AST_DECLARE_APP_ARGS(args,
11935       AST_APP_ARG(mbox);
11936       AST_APP_ARG(options);
11937    );
11938    static int dep_warning = 0;
11939 
11940    if (ast_strlen_zero(data)) {
11941       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
11942       return -1;
11943    }
11944 
11945    if (!dep_warning) {
11946       dep_warning = 1;
11947       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${VM_INFO(%s,exists)} instead.\n", data);
11948    }
11949 
11950    box = ast_strdupa(data);
11951 
11952    AST_STANDARD_APP_ARGS(args, box);
11953 
11954    if (args.options) {
11955    }
11956 
11957    if ((context = strchr(args.mbox, '@'))) {
11958       *context = '\0';
11959       context++;
11960    }
11961 
11962    if (find_user(&svm, context, args.mbox)) {
11963       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11964    } else
11965       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11966 
11967    return 0;
11968 }
11969 
11970 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11971 {
11972    struct ast_vm_user svm;
11973    AST_DECLARE_APP_ARGS(arg,
11974       AST_APP_ARG(mbox);
11975       AST_APP_ARG(context);
11976    );
11977    static int dep_warning = 0;
11978 
11979    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11980 
11981    if (ast_strlen_zero(arg.mbox)) {
11982       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11983       return -1;
11984    }
11985 
11986    if (!dep_warning) {
11987       dep_warning = 1;
11988       ast_log(AST_LOG_WARNING, "MAILBOX_EXISTS is deprecated.  Please use ${VM_INFO(%s,exists)} instead.\n", args);
11989    }
11990 
11991    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11992    return 0;
11993 }
11994 
11995 static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11996 {
11997    struct ast_vm_user svm;
11998    struct ast_vm_user *vmu = NULL;
11999    char *tmp, *mailbox, *context, *parse;
12000    int res = 0;
12001 
12002    AST_DECLARE_APP_ARGS(arg,
12003       AST_APP_ARG(mailbox_context);
12004       AST_APP_ARG(attribute);
12005       AST_APP_ARG(folder);
12006    );
12007 
12008    buf[0] = '\0';
12009 
12010    if (ast_strlen_zero(args)) {
12011       ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
12012       return -1;
12013    }
12014 
12015    parse = ast_strdupa(args);
12016    AST_STANDARD_APP_ARGS(arg, parse);
12017 
12018    if (ast_strlen_zero(arg.mailbox_context) || ast_strlen_zero(arg.attribute)) {
12019       ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
12020       return -1;
12021    }
12022 
12023    tmp = ast_strdupa(arg.mailbox_context);
12024    mailbox = strsep(&tmp, "@");
12025    context = strsep(&tmp, "");
12026 
12027    if (ast_strlen_zero(context)) {
12028        context = "default";
12029    }
12030 
12031    vmu = find_user(&svm, context, mailbox);
12032 
12033    if (!strncasecmp(arg.attribute, "exists", 5)) {
12034       ast_copy_string(buf, vmu ? "1" : "0", len);
12035       return 0;
12036    }
12037 
12038    if (vmu) {
12039       if (!strncasecmp(arg.attribute, "password", 8)) {
12040          ast_copy_string(buf, vmu->password, len);
12041       } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
12042          ast_copy_string(buf, vmu->fullname, len);
12043       } else if (!strncasecmp(arg.attribute, "email", 5)) {
12044          ast_copy_string(buf, vmu->email, len);
12045       } else if (!strncasecmp(arg.attribute, "pager", 5)) {
12046          ast_copy_string(buf, vmu->pager, len);
12047       } else if (!strncasecmp(arg.attribute, "language", 8)) {
12048          const char *lang = S_OR(vmu->language, chan ?
12049             ast_channel_language(chan) : ast_defaultlanguage);
12050          ast_copy_string(buf, lang, len);
12051       } else if (!strncasecmp(arg.attribute, "locale", 6)) {
12052          ast_copy_string(buf, vmu->locale, len);
12053       } else if (!strncasecmp(arg.attribute, "tz", 2)) {
12054          ast_copy_string(buf, vmu->zonetag, len);
12055       } else if (!strncasecmp(arg.attribute, "count", 5)) {
12056          /* If mbxfolder is empty messagecount will default to INBOX */
12057          res = messagecount(context, mailbox, arg.folder);
12058          if (res < 0) {
12059             ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
12060             return -1;
12061          }
12062          snprintf(buf, len, "%d", res);
12063       } else {
12064          ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
12065          return -1;
12066       }
12067    }
12068 
12069    return 0;
12070 }
12071 
12072 static struct ast_custom_function mailbox_exists_acf = {
12073    .name = "MAILBOX_EXISTS",
12074    .read = acf_mailbox_exists,
12075 };
12076 
12077 static struct ast_custom_function vm_info_acf = {
12078    .name = "VM_INFO",
12079    .read = acf_vm_info,
12080 };
12081 
12082 static int vmauthenticate(struct ast_channel *chan, const char *data)
12083 {
12084    char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
12085    struct ast_vm_user vmus;
12086    char *options = NULL;
12087    int silent = 0, skipuser = 0;
12088    int res = -1;
12089    
12090    if (data) {
12091       s = ast_strdupa(data);
12092       user = strsep(&s, ",");
12093       options = strsep(&s, ",");
12094       if (user) {
12095          s = user;
12096          user = strsep(&s, "@");
12097          context = strsep(&s, "");
12098          if (!ast_strlen_zero(user))
12099             skipuser++;
12100          ast_copy_string(mailbox, user, sizeof(mailbox));
12101       }
12102    }
12103 
12104    if (options) {
12105       silent = (strchr(options, 's')) != NULL;
12106    }
12107 
12108    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
12109       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
12110       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
12111       ast_play_and_wait(chan, "auth-thankyou");
12112       res = 0;
12113    } else if (mailbox[0] == '*') {
12114       /* user entered '*' */
12115       if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12116          res = 0; /* prevent hangup */
12117       }
12118    }
12119 
12120    return res;
12121 }
12122 
12123 static char *show_users_realtime(int fd, const char *context)
12124 {
12125    struct ast_config *cfg;
12126    const char *cat = NULL;
12127 
12128    if (!(cfg = ast_load_realtime_multientry("voicemail", 
12129       "context", context, SENTINEL))) {
12130       return CLI_FAILURE;
12131    }
12132 
12133    ast_cli(fd,
12134       "\n"
12135       "=============================================================\n"
12136       "=== Configured Voicemail Users ==============================\n"
12137       "=============================================================\n"
12138       "===\n");
12139 
12140    while ((cat = ast_category_browse(cfg, cat))) {
12141       struct ast_variable *var = NULL;
12142       ast_cli(fd,
12143          "=== Mailbox ...\n"
12144          "===\n");
12145       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
12146          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
12147       ast_cli(fd,
12148          "===\n"
12149          "=== ---------------------------------------------------------\n"
12150          "===\n");
12151    }
12152 
12153    ast_cli(fd,
12154       "=============================================================\n"
12155       "\n");
12156 
12157    ast_config_destroy(cfg);
12158 
12159    return CLI_SUCCESS;
12160 }
12161 
12162 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
12163 {
12164    int which = 0;
12165    int wordlen;
12166    struct ast_vm_user *vmu;
12167    const char *context = "";
12168 
12169    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
12170    if (pos > 4)
12171       return NULL;
12172    if (pos == 3)
12173       return (state == 0) ? ast_strdup("for") : NULL;
12174    wordlen = strlen(word);
12175    AST_LIST_TRAVERSE(&users, vmu, list) {
12176       if (!strncasecmp(word, vmu->context, wordlen)) {
12177          if (context && strcmp(context, vmu->context) && ++which > state)
12178             return ast_strdup(vmu->context);
12179          /* ignore repeated contexts ? */
12180          context = vmu->context;
12181       }
12182    }
12183    return NULL;
12184 }
12185 
12186 /*! \brief Show a list of voicemail users in the CLI */
12187 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12188 {
12189    struct ast_vm_user *vmu;
12190 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
12191    const char *context = NULL;
12192    int users_counter = 0;
12193 
12194    switch (cmd) {
12195    case CLI_INIT:
12196       e->command = "voicemail show users";
12197       e->usage =
12198          "Usage: voicemail show users [for <context>]\n"
12199          "       Lists all mailboxes currently set up\n";
12200       return NULL;
12201    case CLI_GENERATE:
12202       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
12203    }  
12204 
12205    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
12206       return CLI_SHOWUSAGE;
12207    if (a->argc == 5) {
12208       if (strcmp(a->argv[3],"for"))
12209          return CLI_SHOWUSAGE;
12210       context = a->argv[4];
12211    }
12212 
12213    if (ast_check_realtime("voicemail")) {
12214       if (!context) {
12215          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
12216          return CLI_SHOWUSAGE;
12217       }
12218       return show_users_realtime(a->fd, context);
12219    }
12220 
12221    AST_LIST_LOCK(&users);
12222    if (AST_LIST_EMPTY(&users)) {
12223       ast_cli(a->fd, "There are no voicemail users currently defined\n");
12224       AST_LIST_UNLOCK(&users);
12225       return CLI_FAILURE;
12226    }
12227    if (!context) {
12228       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
12229    } else {
12230       int count = 0;
12231       AST_LIST_TRAVERSE(&users, vmu, list) {
12232          if (!strcmp(context, vmu->context)) {
12233             count++;
12234             break;
12235          }
12236       }
12237       if (count) {
12238          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
12239       } else {
12240          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
12241          AST_LIST_UNLOCK(&users);
12242          return CLI_FAILURE;
12243       }
12244    }
12245    AST_LIST_TRAVERSE(&users, vmu, list) {
12246       int newmsgs = 0, oldmsgs = 0;
12247       char count[12], tmp[256] = "";
12248 
12249       if (!context || !strcmp(context, vmu->context)) {
12250          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
12251          inboxcount(tmp, &newmsgs, &oldmsgs);
12252          snprintf(count, sizeof(count), "%d", newmsgs);
12253          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
12254          users_counter++;
12255       }
12256    }
12257    AST_LIST_UNLOCK(&users);
12258    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
12259    return CLI_SUCCESS;
12260 }
12261 
12262 /*! \brief Show a list of voicemail zones in the CLI */
12263 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12264 {
12265    struct vm_zone *zone;
12266 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
12267    char *res = CLI_SUCCESS;
12268 
12269    switch (cmd) {
12270    case CLI_INIT:
12271       e->command = "voicemail show zones";
12272       e->usage =
12273          "Usage: voicemail show zones\n"
12274          "       Lists zone message formats\n";
12275       return NULL;
12276    case CLI_GENERATE:
12277       return NULL;
12278    }
12279 
12280    if (a->argc != 3)
12281       return CLI_SHOWUSAGE;
12282 
12283    AST_LIST_LOCK(&zones);
12284    if (!AST_LIST_EMPTY(&zones)) {
12285       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
12286       AST_LIST_TRAVERSE(&zones, zone, list) {
12287          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
12288       }
12289    } else {
12290       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
12291       res = CLI_FAILURE;
12292    }
12293    AST_LIST_UNLOCK(&zones);
12294 
12295    return res;
12296 }
12297 
12298 /*! \brief Reload voicemail configuration from the CLI */
12299 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12300 {
12301    switch (cmd) {
12302    case CLI_INIT:
12303       e->command = "voicemail reload";
12304       e->usage =
12305          "Usage: voicemail reload\n"
12306          "       Reload voicemail configuration\n";
12307       return NULL;
12308    case CLI_GENERATE:
12309       return NULL;
12310    }
12311 
12312    if (a->argc != 2)
12313       return CLI_SHOWUSAGE;
12314 
12315    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
12316    load_config(1);
12317    
12318    return CLI_SUCCESS;
12319 }
12320 
12321 static struct ast_cli_entry cli_voicemail[] = {
12322    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
12323    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
12324    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
12325 };
12326 
12327 #ifdef IMAP_STORAGE
12328    #define DATA_EXPORT_VM_USERS(USER)              \
12329       USER(ast_vm_user, context, AST_DATA_STRING)        \
12330       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
12331       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
12332       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
12333       USER(ast_vm_user, email, AST_DATA_STRING)       \
12334       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
12335       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
12336       USER(ast_vm_user, pager, AST_DATA_STRING)       \
12337       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
12338       USER(ast_vm_user, language, AST_DATA_STRING)       \
12339       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
12340       USER(ast_vm_user, callback, AST_DATA_STRING)       \
12341       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
12342       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
12343       USER(ast_vm_user, exit, AST_DATA_STRING)        \
12344       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
12345       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
12346       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
12347       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
12348       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
12349       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
12350       USER(ast_vm_user, imapuser, AST_DATA_STRING)       \
12351       USER(ast_vm_user, imappassword, AST_DATA_STRING)      \
12352       USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)     \
12353       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
12354 #else
12355    #define DATA_EXPORT_VM_USERS(USER)              \
12356       USER(ast_vm_user, context, AST_DATA_STRING)        \
12357       USER(ast_vm_user, mailbox, AST_DATA_STRING)        \
12358       USER(ast_vm_user, password, AST_DATA_PASSWORD)        \
12359       USER(ast_vm_user, fullname, AST_DATA_STRING)       \
12360       USER(ast_vm_user, email, AST_DATA_STRING)       \
12361       USER(ast_vm_user, emailsubject, AST_DATA_STRING)      \
12362       USER(ast_vm_user, emailbody, AST_DATA_STRING)         \
12363       USER(ast_vm_user, pager, AST_DATA_STRING)       \
12364       USER(ast_vm_user, serveremail, AST_DATA_STRING)       \
12365       USER(ast_vm_user, language, AST_DATA_STRING)       \
12366       USER(ast_vm_user, zonetag, AST_DATA_STRING)        \
12367       USER(ast_vm_user, callback, AST_DATA_STRING)       \
12368       USER(ast_vm_user, dialout, AST_DATA_STRING)        \
12369       USER(ast_vm_user, uniqueid, AST_DATA_STRING)       \
12370       USER(ast_vm_user, exit, AST_DATA_STRING)        \
12371       USER(ast_vm_user, attachfmt, AST_DATA_STRING)         \
12372       USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)      \
12373       USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)     \
12374       USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)        \
12375       USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)    \
12376       USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)       \
12377       USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
12378 #endif
12379 
12380 AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
12381 
12382 #define DATA_EXPORT_VM_ZONES(ZONE)        \
12383    ZONE(vm_zone, name, AST_DATA_STRING)      \
12384    ZONE(vm_zone, timezone, AST_DATA_STRING)  \
12385    ZONE(vm_zone, msg_format, AST_DATA_STRING)
12386 
12387 AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
12388 
12389 /*!
12390  * \internal
12391  * \brief Add voicemail user to the data_root.
12392  * \param[in] search The search tree.
12393  * \param[in] data_root The main result node.
12394  * \param[in] user The voicemail user.
12395  */
12396 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
12397     struct ast_data *data_root, struct ast_vm_user *user)
12398 {
12399    struct ast_data *data_user, *data_zone;
12400    struct ast_data *data_state;
12401    struct vm_zone *zone = NULL;
12402    int urgentmsg = 0, newmsg = 0, oldmsg = 0;
12403    char ext_context[256] = "";
12404 
12405    data_user = ast_data_add_node(data_root, "user");
12406    if (!data_user) {
12407       return -1;
12408    }
12409 
12410    ast_data_add_structure(ast_vm_user, data_user, user);
12411 
12412    AST_LIST_LOCK(&zones);
12413    AST_LIST_TRAVERSE(&zones, zone, list) {
12414       if (!strcmp(zone->name, user->zonetag)) {
12415          break;
12416       }
12417    }
12418    AST_LIST_UNLOCK(&zones);
12419 
12420    /* state */
12421    data_state = ast_data_add_node(data_user, "state");
12422    if (!data_state) {
12423       return -1;
12424    }
12425    snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
12426    inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
12427    ast_data_add_int(data_state, "urgentmsg", urgentmsg);
12428    ast_data_add_int(data_state, "newmsg", newmsg);
12429    ast_data_add_int(data_state, "oldmsg", oldmsg);
12430 
12431    if (zone) {
12432       data_zone = ast_data_add_node(data_user, "zone");
12433       ast_data_add_structure(vm_zone, data_zone, zone);
12434    }
12435 
12436    if (!ast_data_search_match(search, data_user)) {
12437       ast_data_remove_node(data_root, data_user);
12438    }
12439 
12440    return 0;
12441 }
12442 
12443 static int vm_users_data_provider_get(const struct ast_data_search *search,
12444    struct ast_data *data_root)
12445 {
12446    struct ast_vm_user *user;
12447 
12448    AST_LIST_LOCK(&users);
12449    AST_LIST_TRAVERSE(&users, user, list) {
12450       vm_users_data_provider_get_helper(search, data_root, user);
12451    }
12452    AST_LIST_UNLOCK(&users);
12453 
12454    return 0;
12455 }
12456 
12457 static const struct ast_data_handler vm_users_data_provider = {
12458    .version = AST_DATA_HANDLER_VERSION,
12459    .get = vm_users_data_provider_get
12460 };
12461 
12462 static const struct ast_data_entry vm_data_providers[] = {
12463    AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
12464 };
12465 
12466 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
12467 {
12468    int new = 0, old = 0, urgent = 0;
12469 
12470    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
12471 
12472    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
12473       mwi_sub->old_urgent = urgent;
12474       mwi_sub->old_new = new;
12475       mwi_sub->old_old = old;
12476       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
12477       run_externnotify(NULL, mwi_sub->mailbox, NULL);
12478    }
12479 }
12480 
12481 static void poll_subscribed_mailboxes(void)
12482 {
12483    struct mwi_sub *mwi_sub;
12484 
12485    AST_RWLIST_RDLOCK(&mwi_subs);
12486    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
12487       if (!ast_strlen_zero(mwi_sub->mailbox)) {
12488          poll_subscribed_mailbox(mwi_sub);
12489       }
12490    }
12491    AST_RWLIST_UNLOCK(&mwi_subs);
12492 }
12493 
12494 static void *mb_poll_thread(void *data)
12495 {
12496    while (poll_thread_run) {
12497       struct timespec ts = { 0, };
12498       struct timeval wait;
12499 
12500       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
12501       ts.tv_sec = wait.tv_sec;
12502       ts.tv_nsec = wait.tv_usec * 1000;
12503 
12504       ast_mutex_lock(&poll_lock);
12505       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
12506       ast_mutex_unlock(&poll_lock);
12507 
12508       if (!poll_thread_run)
12509          break;
12510 
12511       poll_subscribed_mailboxes();
12512    }
12513 
12514    return NULL;
12515 }
12516 
12517 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
12518 {
12519    ast_free(mwi_sub);
12520 }
12521 
12522 static int handle_unsubscribe(void *datap)
12523 {
12524    struct mwi_sub *mwi_sub;
12525    uint32_t *uniqueid = datap;
12526    
12527    AST_RWLIST_WRLOCK(&mwi_subs);
12528    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
12529       if (mwi_sub->uniqueid == *uniqueid) {
12530          AST_LIST_REMOVE_CURRENT(entry);
12531          break;
12532       }
12533    }
12534    AST_RWLIST_TRAVERSE_SAFE_END
12535    AST_RWLIST_UNLOCK(&mwi_subs);
12536 
12537    if (mwi_sub)
12538       mwi_sub_destroy(mwi_sub);
12539 
12540    ast_free(uniqueid);  
12541    return 0;
12542 }
12543 
12544 static int handle_subscribe(void *datap)
12545 {
12546    unsigned int len;
12547    struct mwi_sub *mwi_sub;
12548    struct mwi_sub_task *p = datap;
12549 
12550    len = sizeof(*mwi_sub);
12551    if (!ast_strlen_zero(p->mailbox))
12552       len += strlen(p->mailbox);
12553 
12554    if (!ast_strlen_zero(p->context))
12555       len += strlen(p->context) + 1; /* Allow for seperator */
12556 
12557    if (!(mwi_sub = ast_calloc(1, len)))
12558       return -1;
12559 
12560    mwi_sub->uniqueid = p->uniqueid;
12561    if (!ast_strlen_zero(p->mailbox))
12562       strcpy(mwi_sub->mailbox, p->mailbox);
12563 
12564    if (!ast_strlen_zero(p->context)) {
12565       strcat(mwi_sub->mailbox, "@");
12566       strcat(mwi_sub->mailbox, p->context);
12567    }
12568 
12569    AST_RWLIST_WRLOCK(&mwi_subs);
12570    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
12571    AST_RWLIST_UNLOCK(&mwi_subs);
12572    ast_free((void *) p->mailbox);
12573    ast_free((void *) p->context);
12574    ast_free(p);
12575    poll_subscribed_mailbox(mwi_sub);
12576    return 0;
12577 }
12578 
12579 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
12580 {
12581    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
12582 
12583    if (!uniqueid) {
12584       ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
12585       return;
12586    }
12587 
12588    if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
12589       ast_free(uniqueid);
12590       return;
12591    }
12592 
12593    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI) {
12594       ast_free(uniqueid);
12595       return;
12596    }
12597 
12598    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
12599    *uniqueid = u;
12600    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
12601       ast_free(uniqueid);
12602    }
12603 }
12604 
12605 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
12606 {
12607    struct mwi_sub_task *mwist;
12608    
12609    if (ast_event_get_type(event) != AST_EVENT_SUB)
12610       return;
12611 
12612    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
12613       return;
12614 
12615    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
12616       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
12617       return;
12618    }
12619    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
12620    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
12621    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
12622    
12623    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
12624       ast_free(mwist);
12625    }
12626 }
12627 
12628 static void start_poll_thread(void)
12629 {
12630    int errcode;
12631    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
12632       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
12633       AST_EVENT_IE_END);
12634 
12635    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
12636       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
12637       AST_EVENT_IE_END);
12638 
12639    if (mwi_sub_sub)
12640       ast_event_report_subs(mwi_sub_sub);
12641 
12642    poll_thread_run = 1;
12643 
12644    if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
12645       ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
12646    }
12647 }
12648 
12649 static void stop_poll_thread(void)
12650 {
12651    poll_thread_run = 0;
12652 
12653    if (mwi_sub_sub) {
12654       ast_event_unsubscribe(mwi_sub_sub);
12655       mwi_sub_sub = NULL;
12656    }
12657 
12658    if (mwi_unsub_sub) {
12659       ast_event_unsubscribe(mwi_unsub_sub);
12660       mwi_unsub_sub = NULL;
12661    }
12662 
12663    ast_mutex_lock(&poll_lock);
12664    ast_cond_signal(&poll_cond);
12665    ast_mutex_unlock(&poll_lock);
12666 
12667    pthread_join(poll_thread, NULL);
12668 
12669    poll_thread = AST_PTHREADT_NULL;
12670 }
12671 
12672 /*! \brief Manager list voicemail users command */
12673 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
12674 {
12675    struct ast_vm_user *vmu = NULL;
12676    const char *id = astman_get_header(m, "ActionID");
12677    char actionid[128] = "";
12678 
12679    if (!ast_strlen_zero(id))
12680       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
12681 
12682    AST_LIST_LOCK(&users);
12683 
12684    if (AST_LIST_EMPTY(&users)) {
12685       astman_send_ack(s, m, "There are no voicemail users currently defined.");
12686       AST_LIST_UNLOCK(&users);
12687       return RESULT_SUCCESS;
12688    }
12689    
12690    astman_send_ack(s, m, "Voicemail user list will follow");
12691    
12692    AST_LIST_TRAVERSE(&users, vmu, list) {
12693       char dirname[256];
12694 
12695 #ifdef IMAP_STORAGE
12696       int new, old;
12697       inboxcount(vmu->mailbox, &new, &old);
12698 #endif
12699       
12700       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
12701       astman_append(s,
12702          "%s"
12703          "Event: VoicemailUserEntry\r\n"
12704          "VMContext: %s\r\n"
12705          "VoiceMailbox: %s\r\n"
12706          "Fullname: %s\r\n"
12707          "Email: %s\r\n"
12708          "Pager: %s\r\n"
12709          "ServerEmail: %s\r\n"
12710          "MailCommand: %s\r\n"
12711          "Language: %s\r\n"
12712          "TimeZone: %s\r\n"
12713          "Callback: %s\r\n"
12714          "Dialout: %s\r\n"
12715          "UniqueID: %s\r\n"
12716          "ExitContext: %s\r\n"
12717          "SayDurationMinimum: %d\r\n"
12718          "SayEnvelope: %s\r\n"
12719          "SayCID: %s\r\n"
12720          "AttachMessage: %s\r\n"
12721          "AttachmentFormat: %s\r\n"
12722          "DeleteMessage: %s\r\n"
12723          "VolumeGain: %.2f\r\n"
12724          "CanReview: %s\r\n"
12725          "CallOperator: %s\r\n"
12726          "MaxMessageCount: %d\r\n"
12727          "MaxMessageLength: %d\r\n"
12728          "NewMessageCount: %d\r\n"
12729 #ifdef IMAP_STORAGE
12730          "OldMessageCount: %d\r\n"
12731          "IMAPUser: %s\r\n"
12732          "IMAPServer: %s\r\n"
12733          "IMAPPort: %s\r\n"
12734          "IMAPFlags: %s\r\n"
12735 #endif
12736          "\r\n",
12737          actionid,
12738          vmu->context,
12739          vmu->mailbox,
12740          vmu->fullname,
12741          vmu->email,
12742          vmu->pager,
12743          ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
12744          mailcmd,
12745          vmu->language,
12746          vmu->zonetag,
12747          vmu->callback,
12748          vmu->dialout,
12749          vmu->uniqueid,
12750          vmu->exit,
12751          vmu->saydurationm,
12752          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
12753          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
12754          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
12755          vmu->attachfmt,
12756          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
12757          vmu->volgain,
12758          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
12759          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
12760          vmu->maxmsg,
12761          vmu->maxsecs,
12762 #ifdef IMAP_STORAGE
12763          new, old,
12764          vmu->imapuser,
12765          vmu->imapserver,
12766          vmu->imapport,
12767          vmu->imapflags
12768 #else
12769          count_messages(vmu, dirname)
12770 #endif
12771          );
12772    }     
12773    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
12774 
12775    AST_LIST_UNLOCK(&users);
12776 
12777    return RESULT_SUCCESS;
12778 }
12779 
12780 /*! \brief Free the users structure. */
12781 static void free_vm_users(void) 
12782 {
12783    struct ast_vm_user *current;
12784    AST_LIST_LOCK(&users);
12785    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
12786       ast_set_flag(current, VM_ALLOCED);
12787       free_user(current);
12788    }
12789    AST_LIST_UNLOCK(&users);
12790 }
12791 
12792 /*! \brief Free the zones structure. */
12793 static void free_vm_zones(void)
12794 {
12795    struct vm_zone *zcur;
12796    AST_LIST_LOCK(&zones);
12797    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
12798       free_zone(zcur);
12799    AST_LIST_UNLOCK(&zones);
12800 }
12801 
12802 static const char *substitute_escapes(const char *value)
12803 {
12804    char *current;
12805 
12806    /* Add 16 for fudge factor */
12807    struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
12808 
12809    ast_str_reset(str);
12810    
12811    /* Substitute strings \r, \n, and \t into the appropriate characters */
12812    for (current = (char *) value; *current; current++) {
12813       if (*current == '\\') {
12814          current++;
12815          if (!*current) {
12816             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
12817             break;
12818          }
12819          switch (*current) {
12820          case '\\':
12821             ast_str_append(&str, 0, "\\");
12822             break;
12823          case 'r':
12824             ast_str_append(&str, 0, "\r");
12825             break;
12826          case 'n':
12827 #ifdef IMAP_STORAGE
12828             if (!str->used || str->str[str->used - 1] != '\r') {
12829                ast_str_append(&str, 0, "\r");
12830             }
12831 #endif
12832             ast_str_append(&str, 0, "\n");
12833             break;
12834          case 't':
12835             ast_str_append(&str, 0, "\t");
12836             break;
12837          default:
12838             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
12839             break;
12840          }
12841       } else {
12842          ast_str_append(&str, 0, "%c", *current);
12843       }
12844    }
12845 
12846    return ast_str_buffer(str);
12847 }
12848 
12849 static int load_config(int reload)
12850 {
12851    struct ast_config *cfg, *ucfg;
12852    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
12853    int res;
12854 
12855    ast_unload_realtime("voicemail");
12856    ast_unload_realtime("voicemail_data");
12857 
12858    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
12859       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
12860          return 0;
12861       } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
12862          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
12863          ucfg = NULL;
12864       }
12865       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
12866       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
12867          ast_config_destroy(ucfg);
12868          ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
12869          return 0;
12870       }
12871    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
12872       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
12873       return 0;
12874    } else {
12875       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
12876       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
12877          ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
12878          ucfg = NULL;
12879       }
12880    }
12881 
12882    res = actual_load_config(reload, cfg, ucfg);
12883 
12884    ast_config_destroy(cfg);
12885    ast_config_destroy(ucfg);
12886 
12887    return res;
12888 }
12889 
12890 #ifdef TEST_FRAMEWORK
12891 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
12892 {
12893    ast_unload_realtime("voicemail");
12894    ast_unload_realtime("voicemail_data");
12895    return actual_load_config(reload, cfg, ucfg);
12896 }
12897 #endif
12898 
12899 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
12900 {
12901    struct ast_vm_user *current;
12902    char *cat;
12903    struct ast_variable *var;
12904    const char *val;
12905    char *q, *stringp, *tmp;
12906    int x;
12907    int tmpadsi[4];
12908    char secretfn[PATH_MAX] = "";
12909 
12910 #ifdef IMAP_STORAGE
12911    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
12912 #endif
12913    /* set audio control prompts */
12914    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
12915    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
12916    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
12917    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
12918    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
12919 
12920    /* Free all the users structure */  
12921    free_vm_users();
12922 
12923    /* Free all the zones structure */
12924    free_vm_zones();
12925 
12926    AST_LIST_LOCK(&users);  
12927 
12928    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
12929    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
12930 
12931    if (cfg) {
12932       /* General settings */
12933 
12934       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
12935          val = "default";
12936       ast_copy_string(userscontext, val, sizeof(userscontext));
12937       /* Attach voice message to mail message ? */
12938       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
12939          val = "yes";
12940       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
12941 
12942       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
12943          val = "no";
12944       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
12945 
12946       volgain = 0.0;
12947       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
12948          sscanf(val, "%30lf", &volgain);
12949 
12950 #ifdef ODBC_STORAGE
12951       strcpy(odbc_database, "asterisk");
12952       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
12953          ast_copy_string(odbc_database, val, sizeof(odbc_database));
12954       }
12955       strcpy(odbc_table, "voicemessages");
12956       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
12957          ast_copy_string(odbc_table, val, sizeof(odbc_table));
12958       }
12959 #endif      
12960       /* Mail command */
12961       strcpy(mailcmd, SENDMAIL);
12962       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
12963          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
12964 
12965       maxsilence = 0;
12966       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
12967          maxsilence = atoi(val);
12968          if (maxsilence > 0)
12969             maxsilence *= 1000;
12970       }
12971       
12972       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
12973          maxmsg = MAXMSG;
12974       } else {
12975          maxmsg = atoi(val);
12976          if (maxmsg < 0) {
12977             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
12978             maxmsg = MAXMSG;
12979          } else if (maxmsg > MAXMSGLIMIT) {
12980             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
12981             maxmsg = MAXMSGLIMIT;
12982          }
12983       }
12984 
12985       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
12986          maxdeletedmsg = 0;
12987       } else {
12988          if (sscanf(val, "%30d", &x) == 1)
12989             maxdeletedmsg = x;
12990          else if (ast_true(val))
12991             maxdeletedmsg = MAXMSG;
12992          else
12993             maxdeletedmsg = 0;
12994 
12995          if (maxdeletedmsg < 0) {
12996             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
12997             maxdeletedmsg = MAXMSG;
12998          } else if (maxdeletedmsg > MAXMSGLIMIT) {
12999             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
13000             maxdeletedmsg = MAXMSGLIMIT;
13001          }
13002       }
13003 
13004       /* Load date format config for voicemail mail */
13005       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
13006          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
13007       }
13008 
13009       /* Load date format config for voicemail pager mail */
13010       if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
13011          ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
13012       }
13013 
13014       /* External password changing command */
13015       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
13016          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
13017          pwdchange = PWDCHANGE_EXTERNAL;
13018       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
13019          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
13020          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
13021       }
13022 
13023       /* External password validation command */
13024       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
13025          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
13026          ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
13027       }
13028 
13029 #ifdef IMAP_STORAGE
13030       /* IMAP server address */
13031       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
13032          ast_copy_string(imapserver, val, sizeof(imapserver));
13033       } else {
13034          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
13035       }
13036       /* IMAP server port */
13037       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
13038          ast_copy_string(imapport, val, sizeof(imapport));
13039       } else {
13040          ast_copy_string(imapport, "143", sizeof(imapport));
13041       }
13042       /* IMAP server flags */
13043       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
13044          ast_copy_string(imapflags, val, sizeof(imapflags));
13045       }
13046       /* IMAP server master username */
13047       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
13048          ast_copy_string(authuser, val, sizeof(authuser));
13049       }
13050       /* IMAP server master password */
13051       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
13052          ast_copy_string(authpassword, val, sizeof(authpassword));
13053       }
13054       /* Expunge on exit */
13055       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
13056          if (ast_false(val))
13057             expungeonhangup = 0;
13058          else
13059             expungeonhangup = 1;
13060       } else {
13061          expungeonhangup = 1;
13062       }
13063       /* IMAP voicemail folder */
13064       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
13065          ast_copy_string(imapfolder, val, sizeof(imapfolder));
13066       } else {
13067          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
13068       }
13069       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
13070          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
13071       }
13072       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
13073          imapgreetings = ast_true(val);
13074       } else {
13075          imapgreetings = 0;
13076       }
13077       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
13078          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
13079       } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
13080          /* Also support greetingsfolder as documented in voicemail.conf.sample */
13081          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
13082       } else {
13083          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
13084       }
13085 
13086       /* There is some very unorthodox casting done here. This is due
13087        * to the way c-client handles the argument passed in. It expects a 
13088        * void pointer and casts the pointer directly to a long without
13089        * first dereferencing it. */
13090       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
13091          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
13092       } else {
13093          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
13094       }
13095 
13096       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
13097          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
13098       } else {
13099          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
13100       }
13101 
13102       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
13103          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
13104       } else {
13105          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
13106       }
13107 
13108       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
13109          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
13110       } else {
13111          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
13112       }
13113 
13114       /* Increment configuration version */
13115       imapversion++;
13116 #endif
13117       /* External voicemail notify application */
13118       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
13119          ast_copy_string(externnotify, val, sizeof(externnotify));
13120          ast_debug(1, "found externnotify: %s\n", externnotify);
13121       } else {
13122          externnotify[0] = '\0';
13123       }
13124 
13125       /* SMDI voicemail notification */
13126       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
13127          ast_debug(1, "Enabled SMDI voicemail notification\n");
13128          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
13129             smdi_iface = ast_smdi_interface_find(val);
13130          } else {
13131             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
13132             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
13133          }
13134          if (!smdi_iface) {
13135             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
13136          }
13137       }
13138 
13139       /* Silence treshold */
13140       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
13141       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
13142          silencethreshold = atoi(val);
13143 
13144       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
13145          val = ASTERISK_USERNAME;
13146       ast_copy_string(serveremail, val, sizeof(serveremail));
13147 
13148       vmmaxsecs = 0;
13149       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
13150          if (sscanf(val, "%30d", &x) == 1) {
13151             vmmaxsecs = x;
13152          } else {
13153             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
13154          }
13155       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
13156          static int maxmessage_deprecate = 0;
13157          if (maxmessage_deprecate == 0) {
13158             maxmessage_deprecate = 1;
13159             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
13160          }
13161          if (sscanf(val, "%30d", &x) == 1) {
13162             vmmaxsecs = x;
13163          } else {
13164             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
13165          }
13166       }
13167 
13168       vmminsecs = 0;
13169       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
13170          if (sscanf(val, "%30d", &x) == 1) {
13171             vmminsecs = x;
13172             if (maxsilence / 1000 >= vmminsecs) {
13173                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
13174             }
13175          } else {
13176             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
13177          }
13178       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
13179          static int maxmessage_deprecate = 0;
13180          if (maxmessage_deprecate == 0) {
13181             maxmessage_deprecate = 1;
13182             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
13183          }
13184          if (sscanf(val, "%30d", &x) == 1) {
13185             vmminsecs = x;
13186             if (maxsilence / 1000 >= vmminsecs) {
13187                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
13188             }
13189          } else {
13190             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
13191          }
13192       }
13193 
13194       val = ast_variable_retrieve(cfg, "general", "format");
13195       if (!val) {
13196          val = "wav";   
13197       } else {
13198          tmp = ast_strdupa(val);
13199          val = ast_format_str_reduce(tmp);
13200          if (!val) {
13201             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
13202             val = "wav";
13203          }
13204       }
13205       ast_copy_string(vmfmts, val, sizeof(vmfmts));
13206 
13207       skipms = 3000;
13208       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
13209          if (sscanf(val, "%30d", &x) == 1) {
13210             maxgreet = x;
13211          } else {
13212             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
13213          }
13214       }
13215 
13216       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
13217          if (sscanf(val, "%30d", &x) == 1) {
13218             skipms = x;
13219          } else {
13220             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
13221          }
13222       }
13223 
13224       maxlogins = 3;
13225       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
13226          if (sscanf(val, "%30d", &x) == 1) {
13227             maxlogins = x;
13228          } else {
13229             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
13230          }
13231       }
13232 
13233       minpassword = MINPASSWORD;
13234       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
13235          if (sscanf(val, "%30d", &x) == 1) {
13236             minpassword = x;
13237          } else {
13238             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
13239          }
13240       }
13241 
13242       /* Force new user to record name ? */
13243       if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
13244          val = "no";
13245       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
13246 
13247       /* Force new user to record greetings ? */
13248       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
13249          val = "no";
13250       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
13251 
13252       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
13253          ast_debug(1, "VM_CID Internal context string: %s\n", val);
13254          stringp = ast_strdupa(val);
13255          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
13256             if (!ast_strlen_zero(stringp)) {
13257                q = strsep(&stringp, ",");
13258                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
13259                   q++;
13260                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
13261                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
13262             } else {
13263                cidinternalcontexts[x][0] = '\0';
13264             }
13265          }
13266       }
13267       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
13268          ast_debug(1, "VM Review Option disabled globally\n");
13269          val = "no";
13270       }
13271       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
13272 
13273       /* Temporary greeting reminder */
13274       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
13275          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
13276          val = "no";
13277       } else {
13278          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
13279       }
13280       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
13281       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
13282          ast_debug(1, "VM next message wrap disabled globally\n");
13283          val = "no";
13284       }
13285       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
13286 
13287       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
13288          ast_debug(1, "VM Operator break disabled globally\n");
13289          val = "no";
13290       }
13291       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
13292 
13293       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
13294          ast_debug(1, "VM CID Info before msg disabled globally\n");
13295          val = "no";
13296       }
13297       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
13298 
13299       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
13300          ast_debug(1, "Send Voicemail msg disabled globally\n");
13301          val = "no";
13302       }
13303       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
13304 
13305       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
13306          ast_debug(1, "ENVELOPE before msg enabled globally\n");
13307          val = "yes";
13308       }
13309       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
13310 
13311       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
13312          ast_debug(1, "Move Heard enabled globally\n");
13313          val = "yes";
13314       }
13315       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
13316 
13317       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
13318          ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
13319          val = "no";
13320       }
13321       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
13322 
13323       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
13324          ast_debug(1, "Duration info before msg enabled globally\n");
13325          val = "yes";
13326       }
13327       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
13328 
13329       saydurationminfo = 2;
13330       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
13331          if (sscanf(val, "%30d", &x) == 1) {
13332             saydurationminfo = x;
13333          } else {
13334             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
13335          }
13336       }
13337 
13338       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
13339          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
13340          val = "no";
13341       }
13342       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
13343 
13344       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
13345          ast_copy_string(dialcontext, val, sizeof(dialcontext));
13346          ast_debug(1, "found dialout context: %s\n", dialcontext);
13347       } else {
13348          dialcontext[0] = '\0';
13349       }
13350 
13351       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
13352          ast_copy_string(callcontext, val, sizeof(callcontext));
13353          ast_debug(1, "found callback context: %s\n", callcontext);
13354       } else {
13355          callcontext[0] = '\0';
13356       }
13357 
13358       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
13359          ast_copy_string(exitcontext, val, sizeof(exitcontext));
13360          ast_debug(1, "found operator context: %s\n", exitcontext);
13361       } else {
13362          exitcontext[0] = '\0';
13363       }
13364 
13365       /* load password sounds configuration */
13366       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
13367          ast_copy_string(vm_password, val, sizeof(vm_password));
13368       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
13369          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
13370       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
13371          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
13372       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
13373          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
13374       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
13375          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
13376       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
13377          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
13378       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
13379          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
13380       }
13381       if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
13382          ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
13383       }
13384       /* load configurable audio prompts */
13385       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
13386          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
13387       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
13388          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
13389       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
13390          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
13391       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
13392          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
13393       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
13394          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
13395 
13396       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
13397          val = "no";
13398       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
13399 
13400       if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
13401          val = "voicemail.conf";
13402       }
13403       if (!(strcmp(val, "spooldir"))) {
13404          passwordlocation = OPT_PWLOC_SPOOLDIR;
13405       } else {
13406          passwordlocation = OPT_PWLOC_VOICEMAILCONF;
13407       }
13408 
13409       poll_freq = DEFAULT_POLL_FREQ;
13410       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
13411          if (sscanf(val, "%30u", &poll_freq) != 1) {
13412             poll_freq = DEFAULT_POLL_FREQ;
13413             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
13414          }
13415       }
13416 
13417       poll_mailboxes = 0;
13418       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
13419          poll_mailboxes = ast_true(val);
13420 
13421       memset(fromstring, 0, sizeof(fromstring));
13422       memset(pagerfromstring, 0, sizeof(pagerfromstring));
13423       strcpy(charset, "ISO-8859-1");
13424       if (emailbody) {
13425          ast_free(emailbody);
13426          emailbody = NULL;
13427       }
13428       if (emailsubject) {
13429          ast_free(emailsubject);
13430          emailsubject = NULL;
13431       }
13432       if (pagerbody) {
13433          ast_free(pagerbody);
13434          pagerbody = NULL;
13435       }
13436       if (pagersubject) {
13437          ast_free(pagersubject);
13438          pagersubject = NULL;
13439       }
13440       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
13441          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
13442       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
13443          ast_copy_string(fromstring, val, sizeof(fromstring));
13444       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
13445          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
13446       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
13447          ast_copy_string(charset, val, sizeof(charset));
13448       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
13449          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
13450          for (x = 0; x < 4; x++) {
13451             memcpy(&adsifdn[x], &tmpadsi[x], 1);
13452          }
13453       }
13454       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
13455          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
13456          for (x = 0; x < 4; x++) {
13457             memcpy(&adsisec[x], &tmpadsi[x], 1);
13458          }
13459       }
13460       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
13461          if (atoi(val)) {
13462             adsiver = atoi(val);
13463          }
13464       }
13465       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
13466          ast_copy_string(zonetag, val, sizeof(zonetag));
13467       }
13468       if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
13469          ast_copy_string(locale, val, sizeof(locale));
13470       }
13471       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
13472          emailsubject = ast_strdup(substitute_escapes(val));
13473       }
13474       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
13475          emailbody = ast_strdup(substitute_escapes(val));
13476       }
13477       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
13478          pagersubject = ast_strdup(substitute_escapes(val));
13479       }
13480       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
13481          pagerbody = ast_strdup(substitute_escapes(val));
13482       }
13483 
13484       /* load mailboxes from users.conf */
13485       if (ucfg) { 
13486          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
13487             if (!strcasecmp(cat, "general")) {
13488                continue;
13489             }
13490             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
13491                continue;
13492             if ((current = find_or_create(userscontext, cat))) {
13493                populate_defaults(current);
13494                apply_options_full(current, ast_variable_browse(ucfg, cat));
13495                ast_copy_string(current->context, userscontext, sizeof(current->context));
13496                if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
13497                   current->passwordlocation = OPT_PWLOC_USERSCONF;
13498                }
13499 
13500                switch (current->passwordlocation) {
13501                case OPT_PWLOC_SPOOLDIR:
13502                   snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
13503                   read_password_from_file(secretfn, current->password, sizeof(current->password));
13504                }
13505             }
13506          }
13507       }
13508 
13509       /* load mailboxes from voicemail.conf */
13510       cat = ast_category_browse(cfg, NULL);
13511       while (cat) {
13512          if (strcasecmp(cat, "general")) {
13513             var = ast_variable_browse(cfg, cat);
13514             if (strcasecmp(cat, "zonemessages")) {
13515                /* Process mailboxes in this context */
13516                while (var) {
13517                   append_mailbox(cat, var->name, var->value);
13518                   var = var->next;
13519                }
13520             } else {
13521                /* Timezones in this context */
13522                while (var) {
13523                   struct vm_zone *z;
13524                   if ((z = ast_malloc(sizeof(*z)))) {
13525                      char *msg_format, *tzone;
13526                      msg_format = ast_strdupa(var->value);
13527                      tzone = strsep(&msg_format, "|,");
13528                      if (msg_format) {
13529                         ast_copy_string(z->name, var->name, sizeof(z->name));
13530                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
13531                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
13532                         AST_LIST_LOCK(&zones);
13533                         AST_LIST_INSERT_HEAD(&zones, z, list);
13534                         AST_LIST_UNLOCK(&zones);
13535                      } else {
13536                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
13537                         ast_free(z);
13538                      }
13539                   } else {
13540                      AST_LIST_UNLOCK(&users);
13541                      return -1;
13542                   }
13543                   var = var->next;
13544                }
13545             }
13546          }
13547          cat = ast_category_browse(cfg, cat);
13548       }
13549 
13550       AST_LIST_UNLOCK(&users);
13551 
13552       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
13553          start_poll_thread();
13554       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
13555          stop_poll_thread();;
13556 
13557       return 0;
13558    } else {
13559       AST_LIST_UNLOCK(&users);
13560       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
13561       return 0;
13562    }
13563 }
13564 
13565 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
13566 {
13567    int res = -1;
13568    char dir[PATH_MAX];
13569    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
13570    ast_debug(2, "About to try retrieving name file %s\n", dir);
13571    RETRIEVE(dir, -1, mailbox, context);
13572    if (ast_fileexists(dir, NULL, NULL)) {
13573       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
13574    }
13575    DISPOSE(dir, -1);
13576    return res;
13577 }
13578 
13579 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
13580    struct ast_config *pwconf;
13581    struct ast_flags config_flags = { 0 };
13582 
13583    pwconf = ast_config_load(secretfn, config_flags);
13584    if (valid_config(pwconf)) {
13585       const char *val = ast_variable_retrieve(pwconf, "general", "password");
13586       if (val) {
13587          ast_copy_string(password, val, passwordlen);
13588          ast_config_destroy(pwconf);
13589          return;
13590       }
13591       ast_config_destroy(pwconf);
13592    }
13593    ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
13594 }
13595 
13596 static int write_password_to_file(const char *secretfn, const char *password) {
13597    struct ast_config *conf;
13598    struct ast_category *cat;
13599    struct ast_variable *var;
13600    int res = -1;
13601 
13602    if (!(conf = ast_config_new())) {
13603       ast_log(LOG_ERROR, "Error creating new config structure\n");
13604       return res;
13605    }
13606    if (!(cat = ast_category_new("general", "", 1))) {
13607       ast_log(LOG_ERROR, "Error creating new category structure\n");
13608       ast_config_destroy(conf);
13609       return res;
13610    }
13611    if (!(var = ast_variable_new("password", password, ""))) {
13612       ast_log(LOG_ERROR, "Error creating new variable structure\n");
13613       ast_config_destroy(conf);
13614       ast_category_destroy(cat);
13615       return res;
13616    }
13617    ast_category_append(conf, cat);
13618    ast_variable_append(cat, var);
13619    if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
13620       res = 0;
13621    } else {
13622       ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
13623    }
13624 
13625    ast_config_destroy(conf);
13626    return res;
13627 }
13628 
13629 static int vmsayname_exec(struct ast_channel *chan, const char *data)
13630 {
13631    char *context;
13632    char *args_copy;
13633    int res;
13634 
13635    if (ast_strlen_zero(data)) {
13636       ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
13637       return -1;
13638    }
13639 
13640    args_copy = ast_strdupa(data);
13641    if ((context = strchr(args_copy, '@'))) {
13642       *context++ = '\0';
13643    } else {
13644       context = "default";
13645    }
13646 
13647    if ((res = sayname(chan, args_copy, context)) < 0) {
13648       ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
13649       res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
13650       if (!res) {
13651          res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, ast_channel_language(chan));
13652       }
13653    }
13654 
13655    return res;
13656 }
13657 
13658 #ifdef TEST_FRAMEWORK
13659 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
13660 {
13661    return 0;
13662 }
13663 
13664 static struct ast_frame *fake_read(struct ast_channel *ast)
13665 {
13666    return &ast_null_frame;
13667 }
13668 
13669 AST_TEST_DEFINE(test_voicemail_vmsayname)
13670 {
13671    char dir[PATH_MAX];
13672    char dir2[PATH_MAX];
13673    static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
13674    static const char TEST_EXTENSION[] = "1234";
13675 
13676    struct ast_channel *test_channel1 = NULL;
13677    int res = -1;
13678    struct ast_format_cap *nativeformats;
13679 
13680    static const struct ast_channel_tech fake_tech = {
13681       .write = fake_write,
13682       .read = fake_read,
13683    };
13684 
13685    switch (cmd) {
13686    case TEST_INIT:
13687       info->name = "vmsayname_exec";
13688       info->category = "/apps/app_voicemail/";
13689       info->summary = "Vmsayname unit test";
13690       info->description =
13691          "This tests passing various parameters to vmsayname";
13692       return AST_TEST_NOT_RUN;
13693    case TEST_EXECUTE:
13694       break;
13695    }
13696 
13697    if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
13698         NULL, NULL, 0, 0, "TestChannel1"))) {
13699       goto exit_vmsayname_test;
13700    }
13701 
13702    /* normally this is done in the channel driver */
13703    ast_format_set(ast_channel_writeformat(test_channel1), AST_FORMAT_GSM, 0);
13704    nativeformats = ast_channel_nativeformats(test_channel1);
13705    ast_format_cap_add(nativeformats, ast_channel_writeformat(test_channel1));
13706    ast_format_set(ast_channel_rawwriteformat(test_channel1), AST_FORMAT_GSM, 0);
13707    ast_format_set(ast_channel_readformat(test_channel1), AST_FORMAT_GSM, 0);
13708    ast_format_set(ast_channel_rawreadformat(test_channel1), AST_FORMAT_GSM, 0);
13709    ast_channel_tech_set(test_channel1, &fake_tech);
13710 
13711    ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
13712    snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
13713    if (!(res = vmsayname_exec(test_channel1, dir))) {
13714       snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
13715       if (ast_fileexists(dir, NULL, NULL)) {
13716          ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
13717          res = -1;
13718          goto exit_vmsayname_test;
13719       } else {
13720          /* no greeting already exists as expected, let's create one to fully test sayname */
13721          if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
13722             ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
13723             goto exit_vmsayname_test;
13724          }
13725          snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
13726          snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
13727          /* we're not going to hear the sound anyway, just use a valid gsm audio file */
13728          if ((res = symlink(dir, dir2))) {
13729             ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
13730             goto exit_vmsayname_test;
13731          }
13732          ast_test_status_update(test, "Test playing created mailbox greeting...\n");
13733          snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
13734          res = vmsayname_exec(test_channel1, dir);
13735 
13736          /* TODO: there may be a better way to do this */
13737          unlink(dir2);
13738          snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
13739          rmdir(dir2);
13740          snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
13741          rmdir(dir2);
13742       }
13743    }
13744 
13745 exit_vmsayname_test:
13746 
13747    if (test_channel1) {
13748       ast_hangup(test_channel1);
13749    }
13750 
13751    return res ? AST_TEST_FAIL : AST_TEST_PASS;
13752 }
13753 
13754 AST_TEST_DEFINE(test_voicemail_msgcount)
13755 {
13756    int i, j, res = AST_TEST_PASS, syserr;
13757    struct ast_vm_user *vmu;
13758    struct ast_vm_user svm;
13759    struct vm_state vms;
13760 #ifdef IMAP_STORAGE
13761    struct ast_channel *chan = NULL;
13762 #endif
13763    struct {
13764       char dir[256];
13765       char file[256];
13766       char txtfile[256];
13767    } tmp[3];
13768    char syscmd[256];
13769    const char origweasels[] = "tt-weasels";
13770    const char testcontext[] = "test";
13771    const char testmailbox[] = "00000000";
13772    const char testspec[] = "00000000@test";
13773    FILE *txt;
13774    int new, old, urgent;
13775    const char *folders[3] = { "Old", "Urgent", "INBOX" };
13776    const int folder2mbox[3] = { 1, 11, 0 };
13777    const int expected_results[3][12] = {
13778       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
13779       {          1,            0,         0,      1,         0,      0,       1,          0,       0,      1,         0,      0 },
13780       {          1,            1,         1,      1,         0,      1,       1,          1,       0,      1,         1,      1 },
13781       {          1,            1,         1,      1,         0,      2,       1,          1,       1,      1,         1,      2 },
13782    };
13783 
13784    switch (cmd) {
13785    case TEST_INIT:
13786       info->name = "test_voicemail_msgcount";
13787       info->category = "/apps/app_voicemail/";
13788       info->summary = "Test Voicemail status checks";
13789       info->description =
13790          "Verify that message counts are correct when retrieved through the public API";
13791       return AST_TEST_NOT_RUN;
13792    case TEST_EXECUTE:
13793       break;
13794    }
13795 
13796    /* Make sure the original path was completely empty */
13797    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
13798    if ((syserr = ast_safe_system(syscmd))) {
13799       ast_test_status_update(test, "Unable to clear test directory: %s\n",
13800          syserr > 0 ? strerror(syserr) : "unable to fork()");
13801       return AST_TEST_FAIL;
13802    }
13803 
13804 #ifdef IMAP_STORAGE
13805    if (!(chan = ast_dummy_channel_alloc())) {
13806       ast_test_status_update(test, "Unable to create dummy channel\n");
13807       return AST_TEST_FAIL;
13808    }
13809 #endif
13810 
13811    if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
13812       !(vmu = find_or_create(testcontext, testmailbox))) {
13813       ast_test_status_update(test, "Cannot create vmu structure\n");
13814       ast_unreplace_sigchld();
13815 #ifdef IMAP_STORAGE
13816       chan = ast_channel_unref(chan);
13817 #endif
13818       return AST_TEST_FAIL;
13819    }
13820 
13821    populate_defaults(vmu);
13822    memset(&vms, 0, sizeof(vms));
13823 
13824    /* Create temporary voicemail */
13825    for (i = 0; i < 3; i++) {
13826       create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
13827       make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
13828       snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
13829 
13830       if (ast_fileexists(origweasels, "gsm", "en") > 0) {
13831          snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
13832             VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
13833          if ((syserr = ast_safe_system(syscmd))) {
13834             ast_test_status_update(test, "Unable to create test voicemail: %s\n",
13835                syserr > 0 ? strerror(syserr) : "unable to fork()");
13836             ast_unreplace_sigchld();
13837 #ifdef IMAP_STORAGE
13838             chan = ast_channel_unref(chan);
13839 #endif
13840             return AST_TEST_FAIL;
13841          }
13842       }
13843 
13844       if ((txt = fopen(tmp[i].txtfile, "w+"))) {
13845          fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
13846          fclose(txt);
13847       } else {
13848          ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
13849          res = AST_TEST_FAIL;
13850          break;
13851       }
13852       open_mailbox(&vms, vmu, folder2mbox[i]);
13853       STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
13854 
13855       /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
13856       for (j = 0; j < 3; j++) {
13857          /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
13858          if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
13859             ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
13860                testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
13861             res = AST_TEST_FAIL;
13862          }
13863       }
13864 
13865       new = old = urgent = 0;
13866       if (ast_app_inboxcount(testspec, &new, &old)) {
13867          ast_test_status_update(test, "inboxcount returned failure\n");
13868          res = AST_TEST_FAIL;
13869       } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
13870          ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
13871             testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
13872          res = AST_TEST_FAIL;
13873       }
13874 
13875       new = old = urgent = 0;
13876       if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
13877          ast_test_status_update(test, "inboxcount2 returned failure\n");
13878          res = AST_TEST_FAIL;
13879       } else if (old != expected_results[i][6 + 0] ||
13880             urgent != expected_results[i][6 + 1] ||
13881                new != expected_results[i][6 + 2]    ) {
13882          ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
13883             testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
13884          res = AST_TEST_FAIL;
13885       }
13886 
13887       new = old = urgent = 0;
13888       for (j = 0; j < 3; j++) {
13889          if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
13890             ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
13891                testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
13892             res = AST_TEST_FAIL;
13893          }
13894       }
13895    }
13896 
13897    for (i = 0; i < 3; i++) {
13898       /* This is necessary if the voicemails are stored on an ODBC/IMAP
13899        * server, in which case, the rm below will not affect the
13900        * voicemails. */
13901       DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
13902       DISPOSE(tmp[i].dir, 0);
13903    }
13904 
13905    if (vms.deleted) {
13906       ast_free(vms.deleted);
13907    }
13908    if (vms.heard) {
13909       ast_free(vms.heard);
13910    }
13911 
13912 #ifdef IMAP_STORAGE
13913    chan = ast_channel_unref(chan);
13914 #endif
13915 
13916    /* And remove test directory */
13917    snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
13918    if ((syserr = ast_safe_system(syscmd))) {
13919       ast_test_status_update(test, "Unable to clear test directory: %s\n",
13920          syserr > 0 ? strerror(syserr) : "unable to fork()");
13921    }
13922 
13923    return res;
13924 }
13925 
13926 AST_TEST_DEFINE(test_voicemail_notify_endl)
13927 {
13928    int res = AST_TEST_PASS;
13929    char testcontext[] = "test";
13930    char testmailbox[] = "00000000";
13931    char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
13932    char attach[256], attach2[256];
13933    char buf[256] = ""; /* No line should actually be longer than 80 */
13934    struct ast_channel *chan = NULL;
13935    struct ast_vm_user *vmu, vmus = {
13936       .flags = 0,
13937    };
13938    FILE *file;
13939    struct {
13940       char *name;
13941       enum { INT, FLAGVAL, STATIC, STRPTR } type;
13942       void *location;
13943       union {
13944          int intval;
13945          char *strval;
13946       } u;
13947    } test_items[] = {
13948       { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
13949       { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
13950       { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
13951       { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
13952       { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
13953       { "attach2", STRPTR, attach2, .u.strval = "" },
13954       { "attach", STRPTR, attach, .u.strval = "" },
13955    };
13956    int which;
13957 
13958    switch (cmd) {
13959    case TEST_INIT:
13960       info->name = "test_voicemail_notify_endl";
13961       info->category = "/apps/app_voicemail/";
13962       info->summary = "Test Voicemail notification end-of-line";
13963       info->description =
13964          "Verify that notification emails use a consistent end-of-line character";
13965       return AST_TEST_NOT_RUN;
13966    case TEST_EXECUTE:
13967       break;
13968    }
13969 
13970    snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
13971    snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
13972 
13973    if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
13974       !(vmu = find_or_create(testcontext, testmailbox))) {
13975       ast_test_status_update(test, "Cannot create vmu structure\n");
13976       return AST_TEST_NOT_RUN;
13977    }
13978 
13979    if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
13980       ast_test_status_update(test, "Cannot find vmu structure?!!\n");
13981       return AST_TEST_NOT_RUN;
13982    }
13983 
13984    populate_defaults(vmu);
13985    ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
13986 #ifdef IMAP_STORAGE
13987    /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
13988 #endif
13989 
13990    file = tmpfile();
13991    for (which = 0; which < ARRAY_LEN(test_items); which++) {
13992       /* Kill previous test, if any */
13993       rewind(file);
13994       if (ftruncate(fileno(file), 0)) {
13995          ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
13996          res = AST_TEST_FAIL;
13997          break;
13998       }
13999 
14000       /* Make each change, in order, to the test mailbox */
14001       if (test_items[which].type == INT) {
14002          *((int *) test_items[which].location) = test_items[which].u.intval;
14003       } else if (test_items[which].type == FLAGVAL) {
14004          if (ast_test_flag(vmu, test_items[which].u.intval)) {
14005             ast_clear_flag(vmu, test_items[which].u.intval);
14006          } else {
14007             ast_set_flag(vmu, test_items[which].u.intval);
14008          }
14009       } else if (test_items[which].type == STATIC) {
14010          strcpy(test_items[which].location, test_items[which].u.strval);
14011       } else if (test_items[which].type == STRPTR) {
14012          test_items[which].location = test_items[which].u.strval;
14013       }
14014 
14015       make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
14016       rewind(file);
14017       while (fgets(buf, sizeof(buf), file)) {
14018          if (
14019 #ifdef IMAP_STORAGE
14020          buf[strlen(buf) - 2] != '\r'
14021 #else
14022          buf[strlen(buf) - 2] == '\r'
14023 #endif
14024          || buf[strlen(buf) - 1] != '\n') {
14025             res = AST_TEST_FAIL;
14026          }
14027       }
14028    }
14029    fclose(file);
14030    return res;
14031 }
14032 
14033 AST_TEST_DEFINE(test_voicemail_load_config)
14034 {
14035    int res = AST_TEST_PASS;
14036    struct ast_vm_user *vmu;
14037    struct ast_config *cfg;
14038    char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
14039    int fd;
14040    FILE *file;
14041    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
14042 
14043    switch (cmd) {
14044    case TEST_INIT:
14045       info->name = "test_voicemail_load_config";
14046       info->category = "/apps/app_voicemail/";
14047       info->summary = "Test loading Voicemail config";
14048       info->description =
14049          "Verify that configuration is loaded consistently. "
14050          "This is to test regressions of ASTERISK-18838 where it was noticed that "
14051          "some options were loaded after the mailboxes were instantiated, causing "
14052          "those options not to be set correctly.";
14053       return AST_TEST_NOT_RUN;
14054    case TEST_EXECUTE:
14055       break;
14056    }
14057 
14058    /* build a config file by hand... */
14059    if ((fd = mkstemp(config_filename)) < 0) {
14060       return AST_TEST_FAIL;
14061    }
14062    if (!(file = fdopen(fd, "w"))) {
14063       close(fd);
14064       unlink(config_filename);
14065       return AST_TEST_FAIL;
14066    }
14067    fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
14068    fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
14069    fputs("00000002 => 9999,Mrs. Test\n", file);
14070    fclose(file);
14071 
14072    if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
14073       res = AST_TEST_FAIL;
14074       goto cleanup;
14075    }
14076 
14077    load_config_from_memory(1, cfg, NULL);
14078    ast_config_destroy(cfg);
14079 
14080 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
14081    ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
14082    u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
14083 
14084    AST_LIST_LOCK(&users);
14085    AST_LIST_TRAVERSE(&users, vmu, list) {
14086       if (!strcmp(vmu->mailbox, "00000001")) {
14087          if (0); /* trick to get CHECK to work */
14088          CHECK(vmu, callback, "othercontext")
14089          CHECK(vmu, locale, "nl_NL.UTF-8")
14090          CHECK(vmu, zonetag, "central")
14091       } else if (!strcmp(vmu->mailbox, "00000002")) {
14092          if (0); /* trick to get CHECK to work */
14093          CHECK(vmu, callback, "somecontext")
14094          CHECK(vmu, locale, "de_DE.UTF-8")
14095          CHECK(vmu, zonetag, "european")
14096       }
14097    }
14098    AST_LIST_UNLOCK(&users);
14099 
14100 #undef CHECK
14101 
14102    /* restore config */
14103    load_config(1); /* this might say "Failed to load configuration file." */
14104 
14105 cleanup:
14106    unlink(config_filename);
14107    return res;
14108 }
14109 
14110 AST_TEST_DEFINE(test_voicemail_vm_info)
14111 {
14112    struct ast_vm_user *vmu;
14113    struct ast_channel *chan = NULL;
14114    const char testcontext[] = "test";
14115    const char testmailbox[] = "00000000";
14116    const char vminfo_cmd[] = "VM_INFO";
14117    char vminfo_buf[256], vminfo_args[256];
14118    int res = AST_TEST_PASS;
14119    int test_ret = 0;
14120    int test_counter = 0;
14121 
14122    struct {
14123       char *vminfo_test_args;
14124       char *vminfo_expected;
14125       int vminfo_ret;
14126    } test_items[] = {
14127       { "", "", -1 },            /* Missing argument */
14128       { "00000000@test,badparam", "", -1 },  /* Wrong argument */
14129       { "00000000@test", "", -1 },     /* Missing argument */
14130       { "00000000@test,exists", "1", 0 },
14131       { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
14132       { "00000000@test,email", "vm-info-test@example.net", 0 },
14133       { "11111111@test,email", "", 0 },   /* Invalid mailbox */
14134       { "00000000@test,fullname", "Test Framework Mailbox", 0 },
14135       { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
14136       { "00000000@test,locale", "en_US", 0 },
14137       { "00000000@test,tz", "central", 0 },
14138       { "00000000@test,language", "en", 0 },
14139       { "00000000@test,password", "9876", 0 },
14140    };
14141 
14142    switch (cmd) {
14143       case TEST_INIT:
14144          info->name = "test_voicemail_vm_info";
14145          info->category = "/apps/app_voicemail/";
14146          info->summary = "VM_INFO unit test";
14147          info->description =
14148             "This tests passing various parameters to VM_INFO";
14149          return AST_TEST_NOT_RUN;
14150       case TEST_EXECUTE:
14151          break;
14152    }
14153 
14154    if (!(chan = ast_dummy_channel_alloc())) {
14155       ast_test_status_update(test, "Unable to create dummy channel\n");
14156       return AST_TEST_FAIL;
14157    }
14158 
14159    if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
14160          !(vmu = find_or_create(testcontext, testmailbox))) {
14161       ast_test_status_update(test, "Cannot create vmu structure\n");
14162       chan = ast_channel_unref(chan);
14163       return AST_TEST_FAIL;
14164    }
14165 
14166    populate_defaults(vmu);
14167 
14168    ast_copy_string(vmu->email, "vm-info-test@example.net", sizeof(vmu->email));
14169    ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
14170    ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
14171    ast_copy_string(vmu->language, "en", sizeof(vmu->language));
14172    ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
14173    ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
14174    ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
14175 
14176    for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
14177       ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
14178       test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
14179       if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
14180          ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
14181          res = AST_TEST_FAIL;
14182       }
14183       if (!(test_ret == test_items[test_counter].vminfo_ret)) {
14184          ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
14185          res = AST_TEST_FAIL;
14186       }
14187    }
14188 
14189    chan = ast_channel_unref(chan);
14190    return res;
14191 }
14192 #endif /* defined(TEST_FRAMEWORK) */
14193 
14194 static int reload(void)
14195 {
14196    return load_config(1);
14197 }
14198 
14199 static int unload_module(void)
14200 {
14201    int res;
14202 
14203    res = ast_unregister_application(app);
14204    res |= ast_unregister_application(app2);
14205    res |= ast_unregister_application(app3);
14206    res |= ast_unregister_application(app4);
14207    res |= ast_unregister_application(playmsg_app);
14208    res |= ast_unregister_application(sayname_app);
14209    res |= ast_custom_function_unregister(&mailbox_exists_acf);
14210    res |= ast_custom_function_unregister(&vm_info_acf);
14211    res |= ast_manager_unregister("VoicemailUsersList");
14212    res |= ast_data_unregister(NULL);
14213 #ifdef TEST_FRAMEWORK
14214    res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
14215    res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
14216    res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
14217    res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
14218    res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
14219    res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
14220 #endif
14221    ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
14222    ast_uninstall_vm_functions();
14223 #ifdef TEST_FRAMEWORK
14224    ast_uninstall_vm_test_functions();
14225 #endif
14226    ao2_ref(inprocess_container, -1);
14227 
14228    if (poll_thread != AST_PTHREADT_NULL)
14229       stop_poll_thread();
14230 
14231    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
14232    ast_unload_realtime("voicemail");
14233    ast_unload_realtime("voicemail_data");
14234 
14235    free_vm_users();
14236    free_vm_zones();
14237    return res;
14238 }
14239 
14240 static int load_module(void)
14241 {
14242    int res;
14243    my_umask = umask(0);
14244    umask(my_umask);
14245 
14246    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
14247       return AST_MODULE_LOAD_DECLINE;
14248    }
14249 
14250    /* compute the location of the voicemail spool directory */
14251    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
14252    
14253    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
14254       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
14255    }
14256 
14257    if ((res = load_config(0)))
14258       return res;
14259 
14260    res = ast_register_application_xml(app, vm_exec);
14261    res |= ast_register_application_xml(app2, vm_execmain);
14262    res |= ast_register_application_xml(app3, vm_box_exists);
14263    res |= ast_register_application_xml(app4, vmauthenticate);
14264    res |= ast_register_application_xml(playmsg_app, vm_playmsgexec);
14265    res |= ast_register_application_xml(sayname_app, vmsayname_exec);
14266    res |= ast_custom_function_register(&mailbox_exists_acf);
14267    res |= ast_custom_function_register(&vm_info_acf);
14268    res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
14269 #ifdef TEST_FRAMEWORK
14270    res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
14271    res |= AST_TEST_REGISTER(test_voicemail_msgcount);
14272    res |= AST_TEST_REGISTER(test_voicemail_vmuser);
14273    res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
14274    res |= AST_TEST_REGISTER(test_voicemail_load_config);
14275    res |= AST_TEST_REGISTER(test_voicemail_vm_info);
14276 #endif
14277 
14278    if (res)
14279       return res;
14280 
14281    ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
14282    ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
14283 
14284    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname, msg_create_from_file,
14285              vm_index_to_foldername,
14286              vm_mailbox_snapshot_create, vm_mailbox_snapshot_destroy,
14287              vm_msg_move, vm_msg_remove, vm_msg_forward, vm_msg_play);
14288 
14289 #ifdef TEST_FRAMEWORK
14290    ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
14291 #endif
14292 
14293    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
14294    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
14295 
14296    return res;
14297 }
14298 
14299 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
14300 {
14301    int cmd = 0;
14302    char destination[80] = "";
14303    int retries = 0;
14304 
14305    if (!num) {
14306       ast_verb(3, "Destination number will be entered manually\n");
14307       while (retries < 3 && cmd != 't') {
14308          destination[1] = '\0';
14309          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
14310          if (!cmd)
14311             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
14312          if (!cmd)
14313             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
14314          if (!cmd) {
14315             cmd = ast_waitfordigit(chan, 6000);
14316             if (cmd)
14317                destination[0] = cmd;
14318          }
14319          if (!cmd) {
14320             retries++;
14321          } else {
14322 
14323             if (cmd < 0)
14324                return 0;
14325             if (cmd == '*') {
14326                ast_verb(3, "User hit '*' to cancel outgoing call\n");
14327                return 0;
14328             }
14329             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
14330                retries++;
14331             else
14332                cmd = 't';
14333          }
14334          ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
14335       }
14336       if (retries >= 3) {
14337          return 0;
14338       }
14339       
14340    } else {
14341       ast_verb(3, "Destination number is CID number '%s'\n", num);
14342       ast_copy_string(destination, num, sizeof(destination));
14343    }
14344 
14345    if (!ast_strlen_zero(destination)) {
14346       if (destination[strlen(destination) -1 ] == '*')
14347          return 0; 
14348       ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
14349       ast_channel_exten_set(chan, destination);
14350       ast_channel_context_set(chan, outgoing_context);
14351       ast_channel_priority_set(chan, 0);
14352       return 9;
14353    }
14354    return 0;
14355 }
14356 
14357 /*!
14358  * \brief The advanced options within a message.
14359  * \param chan
14360  * \param vmu 
14361  * \param vms
14362  * \param msg
14363  * \param option
14364  * \param record_gain
14365  *
14366  * Provides handling for the play message envelope, call the person back, or reply to message. 
14367  *
14368  * \return zero on success, -1 on error.
14369  */
14370 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)
14371 {
14372    int res = 0;
14373    char filename[PATH_MAX];
14374    struct ast_config *msg_cfg = NULL;
14375    const char *origtime, *context;
14376    char *name, *num;
14377    int retries = 0;
14378    char *cid;
14379    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
14380 
14381    vms->starting = 0; 
14382 
14383    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
14384 
14385    /* Retrieve info from VM attribute file */
14386    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
14387    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
14388    msg_cfg = ast_config_load(filename, config_flags);
14389    DISPOSE(vms->curdir, vms->curmsg);
14390    if (!valid_config(msg_cfg)) {
14391       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
14392       return 0;
14393    }
14394 
14395    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
14396       ast_config_destroy(msg_cfg);
14397       return 0;
14398    }
14399 
14400    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
14401 
14402    context = ast_variable_retrieve(msg_cfg, "message", "context");
14403    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
14404       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
14405    switch (option) {
14406    case 3: /* Play message envelope */
14407       if (!res) {
14408          res = play_message_datetime(chan, vmu, origtime, filename);
14409       }
14410       if (!res) {
14411          res = play_message_callerid(chan, vms, cid, context, 0, 1);
14412       }
14413 
14414       res = 't';
14415       break;
14416 
14417    case 2:  /* Call back */
14418 
14419       if (ast_strlen_zero(cid))
14420          break;
14421 
14422       ast_callerid_parse(cid, &name, &num);
14423       while ((res > -1) && (res != 't')) {
14424          switch (res) {
14425          case '1':
14426             if (num) {
14427                /* Dial the CID number */
14428                res = dialout(chan, vmu, num, vmu->callback);
14429                if (res) {
14430                   ast_config_destroy(msg_cfg);
14431                   return 9;
14432                }
14433             } else {
14434                res = '2';
14435             }
14436             break;
14437 
14438          case '2':
14439             /* Want to enter a different number, can only do this if there's a dialout context for this user */
14440             if (!ast_strlen_zero(vmu->dialout)) {
14441                res = dialout(chan, vmu, NULL, vmu->dialout);
14442                if (res) {
14443                   ast_config_destroy(msg_cfg);
14444                   return 9;
14445                }
14446             } else {
14447                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
14448                res = ast_play_and_wait(chan, "vm-sorry");
14449             }
14450             ast_config_destroy(msg_cfg);
14451             return res;
14452          case '*':
14453             res = 't';
14454             break;
14455          case '3':
14456          case '4':
14457          case '5':
14458          case '6':
14459          case '7':
14460          case '8':
14461          case '9':
14462          case '0':
14463 
14464             res = ast_play_and_wait(chan, "vm-sorry");
14465             retries++;
14466             break;
14467          default:
14468             if (num) {
14469                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
14470                res = ast_play_and_wait(chan, "vm-num-i-have");
14471                if (!res)
14472                   res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
14473                if (!res)
14474                   res = ast_play_and_wait(chan, "vm-tocallnum");
14475                /* Only prompt for a caller-specified number if there is a dialout context specified */
14476                if (!ast_strlen_zero(vmu->dialout)) {
14477                   if (!res)
14478                      res = ast_play_and_wait(chan, "vm-calldiffnum");
14479                }
14480             } else {
14481                res = ast_play_and_wait(chan, "vm-nonumber");
14482                if (!ast_strlen_zero(vmu->dialout)) {
14483                   if (!res)
14484                      res = ast_play_and_wait(chan, "vm-toenternumber");
14485                }
14486             }
14487             if (!res) {
14488                res = ast_play_and_wait(chan, "vm-star-cancel");
14489             }
14490             if (!res) {
14491                res = ast_waitfordigit(chan, 6000);
14492             }
14493             if (!res) {
14494                retries++;
14495                if (retries > 3) {
14496                   res = 't';
14497                }
14498             }
14499             ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
14500             break; 
14501             
14502          }
14503          if (res == 't')
14504             res = 0;
14505          else if (res == '*')
14506             res = -1;
14507       }
14508       break;
14509       
14510    case 1:  /* Reply */
14511       /* Send reply directly to sender */
14512       if (ast_strlen_zero(cid))
14513          break;
14514 
14515       ast_callerid_parse(cid, &name, &num);
14516       if (!num) {
14517          ast_verb(3, "No CID number available, no reply sent\n");
14518          if (!res)
14519             res = ast_play_and_wait(chan, "vm-nonumber");
14520          ast_config_destroy(msg_cfg);
14521          return res;
14522       } else {
14523          struct ast_vm_user vmu2;
14524          if (find_user(&vmu2, vmu->context, num)) {
14525             struct leave_vm_options leave_options;
14526             char mailbox[AST_MAX_EXTENSION * 2 + 2];
14527             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
14528 
14529             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
14530             
14531             memset(&leave_options, 0, sizeof(leave_options));
14532             leave_options.record_gain = record_gain;
14533             res = leave_voicemail(chan, mailbox, &leave_options);
14534             if (!res)
14535                res = 't';
14536             ast_config_destroy(msg_cfg);
14537             return res;
14538          } else {
14539             /* Sender has no mailbox, can't reply */
14540             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
14541             ast_play_and_wait(chan, "vm-nobox");
14542             res = 't';
14543             ast_config_destroy(msg_cfg);
14544             return res;
14545          }
14546       } 
14547       res = 0;
14548 
14549       break;
14550    }
14551 
14552    ast_config_destroy(msg_cfg);
14553 
14554 #ifndef IMAP_STORAGE
14555    if (!res) {
14556       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
14557       vms->heard[msg] = 1;
14558       res = wait_file(chan, vms, vms->fn);
14559    }
14560 #endif
14561    return res;
14562 }
14563 
14564 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
14565          int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
14566          signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id)
14567 {
14568    /* Record message & let caller review or re-record it, or set options if applicable */
14569    int res = 0;
14570    int cmd = 0;
14571    int max_attempts = 3;
14572    int attempts = 0;
14573    int recorded = 0;
14574    int msg_exists = 0;
14575    signed char zero_gain = 0;
14576    char tempfile[PATH_MAX];
14577    char *acceptdtmf = "#";
14578    char *canceldtmf = "";
14579    int canceleddtmf = 0;
14580 
14581    /* Note that urgent and private are for flagging messages as such in the future */
14582 
14583    /* barf if no pointer passed to store duration in */
14584    if (duration == NULL) {
14585       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
14586       return -1;
14587    }
14588 
14589    if (!outsidecaller)
14590       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
14591    else
14592       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
14593 
14594    cmd = '3';  /* Want to start by recording */
14595 
14596    while ((cmd >= 0) && (cmd != 't')) {
14597       switch (cmd) {
14598       case '1':
14599          if (!msg_exists) {
14600             /* In this case, 1 is to record a message */
14601             cmd = '3';
14602             break;
14603          } else {
14604             /* Otherwise 1 is to save the existing message */
14605             ast_verb(3, "Saving message as is\n");
14606             if (!outsidecaller) 
14607                ast_filerename(tempfile, recordfile, NULL);
14608             ast_stream_and_wait(chan, "vm-msgsaved", "");
14609             if (!outsidecaller) {
14610                /* Saves to IMAP server only if imapgreeting=yes */
14611                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
14612                DISPOSE(recordfile, -1);
14613             }
14614             cmd = 't';
14615             return res;
14616          }
14617       case '2':
14618          /* Review */
14619          ast_verb(3, "Reviewing the message\n");
14620          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
14621          break;
14622       case '3':
14623          msg_exists = 0;
14624          /* Record */
14625          if (recorded == 1) 
14626             ast_verb(3, "Re-recording the message\n");
14627          else  
14628             ast_verb(3, "Recording the message\n");
14629          
14630          if (recorded && outsidecaller) {
14631             cmd = ast_play_and_wait(chan, INTRO);
14632             cmd = ast_play_and_wait(chan, "beep");
14633          }
14634          recorded = 1;
14635          /* 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 */
14636          if (record_gain)
14637             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
14638          if (ast_test_flag(vmu, VM_OPERATOR))
14639             canceldtmf = "0";
14640          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
14641          if (strchr(canceldtmf, cmd)) {
14642          /* need this flag here to distinguish between pressing '0' during message recording or after */
14643             canceleddtmf = 1;
14644          }
14645          if (record_gain)
14646             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
14647          if (cmd == -1) {
14648             /* User has hung up, no options to give */
14649             if (!outsidecaller) {
14650                /* user was recording a greeting and they hung up, so let's delete the recording. */
14651                ast_filedelete(tempfile, NULL);
14652             }     
14653             return cmd;
14654          }
14655          if (cmd == '0') {
14656             break;
14657          } else if (cmd == '*') {
14658             break;
14659 #if 0
14660          } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
14661             /* Message is too short */
14662             ast_verb(3, "Message too short\n");
14663             cmd = ast_play_and_wait(chan, "vm-tooshort");
14664             cmd = ast_filedelete(tempfile, NULL);
14665             break;
14666          } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
14667             /* Message is all silence */
14668             ast_verb(3, "Nothing recorded\n");
14669             cmd = ast_filedelete(tempfile, NULL);
14670             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
14671             if (!cmd)
14672                cmd = ast_play_and_wait(chan, "vm-speakup");
14673             break;
14674 #endif
14675          } else {
14676             /* If all is well, a message exists */
14677             msg_exists = 1;
14678             cmd = 0;
14679          }
14680          break;
14681       case '4':
14682          if (outsidecaller) {  /* only mark vm messages */
14683             /* Mark Urgent */
14684             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
14685                ast_verb(3, "marking message as Urgent\n");
14686                res = ast_play_and_wait(chan, "vm-marked-urgent");
14687                strcpy(flag, "Urgent");
14688             } else if (flag) {
14689                ast_verb(3, "UNmarking message as Urgent\n");
14690                res = ast_play_and_wait(chan, "vm-marked-nonurgent");
14691                strcpy(flag, "");
14692             } else {
14693                ast_play_and_wait(chan, "vm-sorry");
14694             }
14695             cmd = 0;
14696          } else {
14697             cmd = ast_play_and_wait(chan, "vm-sorry");
14698          }
14699          break;
14700       case '5':
14701       case '6':
14702       case '7':
14703       case '8':
14704       case '9':
14705       case '*':
14706       case '#':
14707          cmd = ast_play_and_wait(chan, "vm-sorry");
14708          break;
14709 #if 0 
14710 /*  XXX Commented out for the moment because of the dangers of deleting
14711     a message while recording (can put the message numbers out of sync) */
14712       case '*':
14713          /* Cancel recording, delete message, offer to take another message*/
14714          cmd = ast_play_and_wait(chan, "vm-deleted");
14715          cmd = ast_filedelete(tempfile, NULL);
14716          if (outsidecaller) {
14717             res = vm_exec(chan, NULL);
14718             return res;
14719          }
14720          else
14721             return 1;
14722 #endif
14723       case '0':
14724          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
14725             cmd = ast_play_and_wait(chan, "vm-sorry");
14726             break;
14727          }
14728          if (msg_exists || recorded) {
14729             cmd = ast_play_and_wait(chan, "vm-saveoper");
14730             if (!cmd)
14731                cmd = ast_waitfordigit(chan, 3000);
14732             if (cmd == '1') {
14733                ast_filerename(tempfile, recordfile, NULL);
14734                ast_play_and_wait(chan, "vm-msgsaved");
14735                cmd = '0';
14736             } else if (cmd == '4') {
14737                if (flag) {
14738                   ast_play_and_wait(chan, "vm-marked-urgent");
14739                   strcpy(flag, "Urgent");
14740                }
14741                ast_play_and_wait(chan, "vm-msgsaved");
14742                cmd = '0';
14743             } else {
14744                ast_play_and_wait(chan, "vm-deleted");
14745                DELETE(tempfile, -1, tempfile, vmu);
14746                cmd = '0';
14747             }
14748          }
14749          return cmd;
14750       default:
14751          /* If the caller is an ouside caller, and the review option is enabled,
14752             allow them to review the message, but let the owner of the box review
14753             their OGM's */
14754          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
14755             return cmd;
14756          if (msg_exists) {
14757             cmd = ast_play_and_wait(chan, "vm-review");
14758             if (!cmd && outsidecaller) {
14759                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
14760                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
14761                } else if (flag) {
14762                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
14763                }
14764             }
14765          } else {
14766             cmd = ast_play_and_wait(chan, "vm-torerecord");
14767             if (!cmd)
14768                cmd = ast_waitfordigit(chan, 600);
14769          }
14770          
14771          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
14772             cmd = ast_play_and_wait(chan, "vm-reachoper");
14773             if (!cmd)
14774                cmd = ast_waitfordigit(chan, 600);
14775          }
14776 #if 0
14777          if (!cmd)
14778             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
14779 #endif
14780          if (!cmd)
14781             cmd = ast_waitfordigit(chan, 6000);
14782          if (!cmd) {
14783             attempts++;
14784          }
14785          if (attempts > max_attempts) {
14786             cmd = 't';
14787          }
14788       }
14789    }
14790    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
14791       /* Hang up or timeout, so delete the recording. */
14792       ast_filedelete(tempfile, NULL);
14793    }
14794 
14795    if (cmd != 't' && outsidecaller)
14796       ast_play_and_wait(chan, "vm-goodbye");
14797 
14798    return cmd;
14799 }
14800 
14801 static struct ast_vm_msg_snapshot *vm_msg_snapshot_alloc(void)
14802 {
14803    struct ast_vm_msg_snapshot *msg_snapshot;
14804 
14805    if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
14806       return NULL;
14807    }
14808 
14809    if (ast_string_field_init(msg_snapshot, 512)) {
14810       ast_free(msg_snapshot);
14811       return NULL;
14812    }
14813 
14814    return msg_snapshot;
14815 }
14816 
14817 static struct ast_vm_msg_snapshot *vm_msg_snapshot_destroy(struct ast_vm_msg_snapshot *msg_snapshot)
14818 {
14819    ast_string_field_free_memory(msg_snapshot);
14820    ast_free(msg_snapshot);
14821 
14822    return NULL;
14823 }
14824 
14825 #ifdef TEST_FRAMEWORK
14826 
14827 static int vm_test_destroy_user(const char *context, const char *mailbox)
14828 {
14829    struct ast_vm_user *vmu;
14830 
14831    AST_LIST_LOCK(&users);
14832    AST_LIST_TRAVERSE_SAFE_BEGIN(&users, vmu, list) {
14833       if (!strcmp(context, vmu->context)
14834          && !strcmp(mailbox, vmu->mailbox)) {
14835          AST_LIST_REMOVE_CURRENT(list);
14836          ast_free(vmu);
14837          break;
14838       }
14839    }
14840    AST_LIST_TRAVERSE_SAFE_END
14841    AST_LIST_UNLOCK(&users);
14842    return 0;
14843 }
14844 
14845 static int vm_test_create_user(const char *context, const char *mailbox)
14846 {
14847    struct ast_vm_user *vmu;
14848 
14849    if (!(vmu = find_or_create(context, mailbox))) {
14850       return -1;
14851    }
14852    populate_defaults(vmu);
14853    return 0;
14854 }
14855 
14856 #endif
14857 
14858 /*!
14859  * \brief Create and store off all the msgs in an open mailbox
14860  *
14861  * \note TODO XXX This function should work properly for all
14862  *       voicemail storage options, but is far more expensive for
14863  *       ODBC at the moment.  This is because the RETRIEVE macro
14864  *       not only pulls out the message's meta data file from the
14865  *       database, but also the actual audio for each message, temporarily
14866  *       writing it to the file system.  This is an area that needs
14867  *       to be made more efficient.
14868  */
14869 static int vm_msg_snapshot_create(struct ast_vm_user *vmu,
14870    struct vm_state *vms,
14871    struct ast_vm_mailbox_snapshot *mailbox_snapshot,
14872    int snapshot_index,
14873    int mailbox_index,
14874    int descending,
14875    enum ast_vm_snapshot_sort_val sort_val)
14876 {
14877    struct ast_vm_msg_snapshot *msg_snapshot;
14878    struct ast_vm_msg_snapshot *msg_snapshot_tmp;
14879    struct ast_config *msg_cfg;
14880    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
14881    char filename[PATH_MAX];
14882    const char *value;
14883 
14884    for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
14885       int inserted = 0;
14886       /* Find the msg */
14887       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
14888       snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
14889       RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
14890       msg_cfg = ast_config_load(filename, config_flags);
14891       if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
14892          DISPOSE(vms->curdir, vms->curmsg);
14893          continue;
14894       }
14895 
14896       /* Create the snapshot object */
14897       if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
14898          ast_config_destroy(msg_cfg);
14899          return -1;
14900       }
14901 
14902       /* Fill in the snapshot object */
14903       if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
14904          ast_string_field_set(msg_snapshot, msg_id, value);
14905       } else {
14906          /* Message snapshots *really* should have a
14907           * message ID. Add one to the message config
14908           * if it does not already exist
14909           */
14910          char id[MSG_ID_LEN];
14911          if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
14912                      filename, id, sizeof(id), vmu, mailbox_index))) {
14913             ast_string_field_set(msg_snapshot, msg_id, id);
14914          } else {
14915             ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
14916          }
14917       }
14918       if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
14919          ast_string_field_set(msg_snapshot, callerid, value);
14920       }
14921       if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
14922          ast_string_field_set(msg_snapshot, callerchan, value);
14923       }
14924       if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
14925          ast_string_field_set(msg_snapshot, exten, value);
14926       }
14927       if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
14928          ast_string_field_set(msg_snapshot, origdate, value);
14929       }
14930       if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
14931          ast_string_field_set(msg_snapshot, origtime, value);
14932       }
14933       if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
14934          ast_string_field_set(msg_snapshot, duration, value);
14935       }
14936       if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
14937          ast_string_field_set(msg_snapshot, flag, value);
14938       }
14939       msg_snapshot->msg_number = vms->curmsg;
14940       ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
14941 
14942       /* store msg snapshot in mailbox snapshot */
14943       switch (sort_val) {
14944       default:
14945       case AST_VM_SNAPSHOT_SORT_BY_ID:
14946          if (descending) {
14947             AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
14948          } else {
14949             AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
14950          }
14951          inserted = 1;
14952          break;
14953       case AST_VM_SNAPSHOT_SORT_BY_TIME:
14954          AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
14955             int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
14956             if (descending && val >= 0) {
14957                AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
14958                inserted = 1;
14959                break;
14960             } else if (!descending && val <= 0) {
14961                AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
14962                inserted = 1;
14963                break;
14964             }
14965          }
14966          AST_LIST_TRAVERSE_SAFE_END;
14967          break;
14968       }
14969 
14970       if (!inserted) {
14971          AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
14972       }
14973 
14974       mailbox_snapshot->total_msg_num++;
14975 
14976       /* cleanup configs and msg */
14977       ast_config_destroy(msg_cfg);
14978       DISPOSE(vms->curdir, vms->curmsg);
14979    }
14980 
14981    return 0;
14982 }
14983 
14984 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox,
14985    const char *context,
14986    const char *folder,
14987    int descending,
14988    enum ast_vm_snapshot_sort_val sort_val,
14989    int combine_INBOX_and_OLD)
14990 {
14991    struct ast_vm_mailbox_snapshot *mailbox_snapshot;
14992    struct vm_state vms;
14993    struct ast_vm_user *vmu = NULL, vmus;
14994    int res;
14995    int i;
14996    int this_index_only = -1;
14997    int open = 0;
14998    int inbox_index = get_folder_by_name("INBOX");
14999    int old_index = get_folder_by_name("Old");
15000    int urgent_index = get_folder_by_name("Urgent");
15001 
15002    if (ast_strlen_zero(mailbox)) {
15003       ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
15004       return NULL;
15005    }
15006 
15007    memset(&vmus, 0, sizeof(vmus));
15008 
15009    if (!(ast_strlen_zero(folder))) {
15010       /* find the folder index */
15011       for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
15012          if (!strcasecmp(mailbox_folders[i], folder)) {
15013             this_index_only = i;
15014             break;
15015          }
15016       }
15017       if (this_index_only == -1) {
15018          /* Folder was specified and it did not match any folder in our list */
15019          return NULL;
15020       }
15021    }
15022 
15023    if (!(vmu = find_user(&vmus, context, mailbox))) {
15024       ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
15025       return NULL;
15026    }
15027 
15028    if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
15029       ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
15030       return NULL;
15031    }
15032 
15033    if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
15034       ast_free(mailbox_snapshot);
15035       return NULL;
15036    }
15037 
15038    mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
15039 
15040    for (i = 0; i < mailbox_snapshot->folders; i++) {
15041       int msg_folder_index = i;
15042 
15043       /* We want this message in the snapshot if any of the following:
15044        *   No folder was specified.
15045        *   The specified folder matches the current folder.
15046        *   The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
15047        */
15048       if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
15049          continue;
15050       }
15051 
15052       /* Make sure that Old or Urgent messages are marked as being in INBOX. */
15053       if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
15054          msg_folder_index = inbox_index;
15055       }
15056 
15057       memset(&vms, 0, sizeof(vms));
15058       ast_copy_string(vms.username, mailbox, sizeof(vms.username));
15059       vms.lastmsg = -1;
15060       open = 0;
15061 
15062       /* open the mailbox state */
15063       if ((res = open_mailbox(&vms, vmu, i)) < 0) {
15064          ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
15065          goto snapshot_cleanup;
15066       }
15067       open = 1;
15068 
15069       /* Iterate through each msg, storing off info */
15070       if (vms.lastmsg != -1) {
15071          if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
15072             ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
15073             goto snapshot_cleanup;
15074          }
15075       }
15076 
15077       /* close mailbox */
15078       if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
15079          goto snapshot_cleanup;
15080       }
15081       open = 0;
15082    }
15083 
15084 snapshot_cleanup:
15085    if (vmu && open) {
15086       close_mailbox(&vms, vmu);
15087    }
15088 
15089 #ifdef IMAP_STORAGE
15090    if (vmu) {
15091       vmstate_delete(&vms);
15092    }
15093 #endif
15094 
15095    return mailbox_snapshot;
15096 }
15097 
15098 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
15099 {
15100    int i;
15101    struct ast_vm_msg_snapshot *msg_snapshot;
15102 
15103    for (i = 0; i < mailbox_snapshot->folders; i++) {
15104       while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
15105          msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
15106       }
15107    }
15108    ast_free(mailbox_snapshot->snapshots);
15109    ast_free(mailbox_snapshot);
15110    return NULL;
15111 }
15112 
15113 /*!
15114  * \brief common bounds checking and existence check for Voicemail API functions.
15115  *
15116  * \details
15117  * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
15118  * ensure that data passed in are valid. This ensures that given the
15119  * desired message IDs, they can be found.
15120  *
15121  * \param vms The voicemail state corresponding to an open mailbox
15122  * \param msg_ids An array of message identifiers
15123  * \param num_msgs The number of identifiers in msg_ids
15124  * \param msg_nums [out] The message indexes corresponding to the given
15125  * message IDs
15126  * \pre vms must have open_mailbox() called on it prior to this function.
15127  *
15128  * \retval -1 Failure
15129  * \retval 0 Success
15130  */
15131 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
15132 {
15133    int i;
15134    int res = 0;
15135    for (i = 0; i < num_msgs; ++i) {
15136       const char *msg_id = msg_ids[i];
15137       int found = 0;
15138       for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
15139          const char *other_msg_id;
15140          char filename[PATH_MAX];
15141          struct ast_config *msg_cfg;
15142          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
15143 
15144          make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
15145          snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
15146          RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
15147          msg_cfg = ast_config_load(filename, config_flags);
15148          if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
15149             DISPOSE(vms->curdir, vms->curmsg);
15150             res = -1;
15151             goto done;
15152          }
15153 
15154          other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
15155 
15156          if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
15157             /* Message found. We can get out of this inner loop
15158              * and move on to the next message to find
15159              */
15160             found = 1;
15161             msg_nums[i] = vms->curmsg;
15162             ast_config_destroy(msg_cfg);
15163             DISPOSE(vms->curdir, vms->curmsg);
15164             break;
15165          }
15166          ast_config_destroy(msg_cfg);
15167          DISPOSE(vms->curdir, vms->curmsg);
15168       }
15169       if (!found) {
15170          /* If we can't find one of the message IDs requested, then OH NO! */
15171          res = -1;
15172          goto done;
15173       }
15174    }
15175 
15176 done:
15177    return res;
15178 }
15179 
15180 static void notify_new_state(struct ast_vm_user *vmu)
15181 {
15182    int new = 0, old = 0, urgent = 0;
15183    char ext_context[1024];
15184 
15185    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
15186    run_externnotify(vmu->context, vmu->mailbox, NULL);
15187    ast_app_inboxcount2(ext_context, &urgent, &new, &old);
15188    queue_mwi_event(ext_context, urgent, new, old);
15189 }
15190 
15191 static int vm_msg_forward(const char *from_mailbox,
15192    const char *from_context,
15193    const char *from_folder,
15194    const char *to_mailbox,
15195    const char *to_context,
15196    const char *to_folder,
15197    size_t num_msgs,
15198    const char *msg_ids [],
15199    int delete_old)
15200 {
15201    struct vm_state from_vms;
15202    struct ast_vm_user *vmu = NULL, vmus;
15203    struct ast_vm_user *to_vmu = NULL, to_vmus;
15204    struct ast_config *msg_cfg;
15205    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
15206    char filename[PATH_MAX];
15207    int from_folder_index;
15208    int open = 0;
15209    int res = 0;
15210    int i;
15211    int *msg_nums;
15212 
15213    if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
15214       ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
15215       return -1;
15216    }
15217 
15218    if (!num_msgs) {
15219       ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
15220       return -1;
15221    }
15222 
15223    if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
15224       ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
15225       return -1;
15226    }
15227 
15228    memset(&vmus, 0, sizeof(vmus));
15229    memset(&to_vmus, 0, sizeof(to_vmus));
15230    memset(&from_vms, 0, sizeof(from_vms));
15231 
15232    from_folder_index = get_folder_by_name(from_folder);
15233    if (from_folder_index == -1) {
15234       return -1;
15235    }
15236 
15237    if (get_folder_by_name(to_folder) == -1) {
15238       return -1;
15239    }
15240 
15241    if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
15242       ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
15243       return -1;
15244    }
15245 
15246    if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
15247       ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
15248       return -1;
15249    }
15250 
15251    ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
15252    from_vms.lastmsg = -1;
15253    open = 0;
15254 
15255    /* open the mailbox state */
15256    if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
15257       ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
15258       res = -1;
15259       goto vm_forward_cleanup;
15260    }
15261 
15262    open = 1;
15263 
15264    if ((from_vms.lastmsg + 1) < num_msgs) {
15265       ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
15266       res = -1;
15267       goto vm_forward_cleanup;
15268    }
15269 
15270    msg_nums = ast_alloca(sizeof(int) * num_msgs);
15271 
15272    if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
15273       goto vm_forward_cleanup;
15274    }
15275 
15276    /* Now we actually forward the messages */
15277    for (i = 0; i < num_msgs; i++) {
15278       int cur_msg = msg_nums[i];
15279       int duration = 0;
15280       const char *value;
15281 
15282       make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
15283       snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
15284       RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
15285       msg_cfg = ast_config_load(filename, config_flags);
15286       /* XXX This likely will not fail since we previously ensured that the
15287        * message we are looking for exists. However, there still could be some
15288        * circumstance where this fails, so atomicity is not guaranteed.
15289        */
15290       if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
15291          DISPOSE(from_vms.curdir, cur_msg);
15292          continue;
15293       }
15294       if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
15295          duration = atoi(value);
15296       }
15297 
15298       copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
15299 
15300       if (delete_old) {
15301          from_vms.deleted[cur_msg] = 1;
15302       }
15303       ast_config_destroy(msg_cfg);
15304       DISPOSE(from_vms.curdir, cur_msg);
15305    }
15306 
15307    /* close mailbox */
15308    if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
15309       res = -1;
15310       goto vm_forward_cleanup;
15311    }
15312    open = 0;
15313 
15314 vm_forward_cleanup:
15315    if (vmu && open) {
15316       close_mailbox(&from_vms, vmu);
15317    }
15318 #ifdef IMAP_STORAGE
15319    if (vmu) {
15320       vmstate_delete(&from_vms);
15321    }
15322 #endif
15323 
15324    if (!res) {
15325       notify_new_state(to_vmu);
15326    }
15327 
15328    return res;
15329 }
15330 
15331 static int vm_msg_move(const char *mailbox,
15332    const char *context,
15333    size_t num_msgs,
15334    const char *oldfolder,
15335    const char *old_msg_ids [],
15336    const char *newfolder)
15337 {
15338    struct vm_state vms;
15339    struct ast_vm_user *vmu = NULL, vmus;
15340    int old_folder_index;
15341    int new_folder_index;
15342    int open = 0;
15343    int res = 0;
15344    int i;
15345    int *old_msg_nums;
15346 
15347    if (ast_strlen_zero(mailbox)) {
15348       ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
15349       return -1;
15350    }
15351 
15352    if (!num_msgs) {
15353       ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
15354       return -1;
15355    }
15356 
15357    if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
15358       ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
15359       return -1;
15360    }
15361 
15362    old_folder_index = get_folder_by_name(oldfolder);
15363    new_folder_index = get_folder_by_name(newfolder);
15364 
15365    memset(&vmus, 0, sizeof(vmus));
15366    memset(&vms, 0, sizeof(vms));
15367 
15368    if (old_folder_index == -1 || new_folder_index == -1) {
15369       return -1;
15370    }
15371 
15372    if (!(vmu = find_user(&vmus, context, mailbox))) {
15373       return -1;
15374    }
15375 
15376    ast_copy_string(vms.username, mailbox, sizeof(vms.username));
15377    vms.lastmsg = -1;
15378    open = 0;
15379 
15380    /* open the mailbox state */
15381    if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
15382       ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
15383       res = -1;
15384       goto vm_move_cleanup;
15385    }
15386 
15387    open = 1;
15388 
15389    if ((vms.lastmsg + 1) < num_msgs) {
15390       ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
15391       res = -1;
15392       goto vm_move_cleanup;
15393    }
15394 
15395    old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
15396 
15397    if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
15398       goto vm_move_cleanup;
15399    }
15400 
15401    /* Now actually move the message */
15402    for (i = 0; i < num_msgs; ++i) {
15403       if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
15404          res = -1;
15405          goto vm_move_cleanup;
15406       }
15407       vms.deleted[old_msg_nums[i]] = 1;
15408    }
15409 
15410    /* close mailbox */
15411    if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
15412       res = -1;
15413       goto vm_move_cleanup;
15414    }
15415    open = 0;
15416 
15417 vm_move_cleanup:
15418    if (vmu && open) {
15419       close_mailbox(&vms, vmu);
15420    }
15421 #ifdef IMAP_STORAGE
15422    if (vmu) {
15423       vmstate_delete(&vms);
15424    }
15425 #endif
15426 
15427    if (!res) {
15428       notify_new_state(vmu);
15429    }
15430 
15431    return res;
15432 }
15433 
15434 static int vm_msg_remove(const char *mailbox,
15435    const char *context,
15436    size_t num_msgs,
15437    const char *folder,
15438    const char *msgs[])
15439 {
15440    struct vm_state vms;
15441    struct ast_vm_user *vmu = NULL, vmus;
15442    int folder_index;
15443    int open = 0;
15444    int res = 0;
15445    int i;
15446    int *msg_nums;
15447 
15448    if (ast_strlen_zero(mailbox)) {
15449       ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
15450       return -1;
15451    }
15452 
15453    if (!num_msgs) {
15454       ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
15455       return -1;
15456    }
15457 
15458    if (ast_strlen_zero(folder)) {
15459       ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
15460       return -1;
15461    }
15462 
15463    memset(&vmus, 0, sizeof(vmus));
15464    memset(&vms, 0, sizeof(vms));
15465 
15466    folder_index = get_folder_by_name(folder);
15467    if (folder_index == -1) {
15468       ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
15469       return -1;
15470    }
15471 
15472    if (!(vmu = find_user(&vmus, context, mailbox))) {
15473       ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
15474       return -1;
15475    }
15476 
15477    ast_copy_string(vms.username, mailbox, sizeof(vms.username));
15478    vms.lastmsg = -1;
15479    open = 0;
15480 
15481    /* open the mailbox state */
15482    if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
15483       ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
15484       res = -1;
15485       goto vm_remove_cleanup;
15486    }
15487 
15488    open = 1;
15489 
15490    if ((vms.lastmsg + 1) < num_msgs) {
15491       ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
15492       res = -1;
15493       goto vm_remove_cleanup;
15494    }
15495 
15496    msg_nums = ast_alloca(sizeof(int) * num_msgs);
15497 
15498    if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
15499       goto vm_remove_cleanup;
15500    }
15501 
15502    for (i = 0; i < num_msgs; i++) {
15503       vms.deleted[msg_nums[i]] = 1;
15504    }
15505 
15506    /* close mailbox */
15507    if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
15508       res = -1;
15509       ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
15510       goto vm_remove_cleanup;
15511    }
15512    open = 0;
15513 
15514 vm_remove_cleanup:
15515    if (vmu && open) {
15516       close_mailbox(&vms, vmu);
15517    }
15518 #ifdef IMAP_STORAGE
15519    if (vmu) {
15520       vmstate_delete(&vms);
15521    }
15522 #endif
15523 
15524    if (!res) {
15525       notify_new_state(vmu);
15526    }
15527 
15528    return res;
15529 }
15530 
15531 static int vm_msg_play(struct ast_channel *chan,
15532    const char *mailbox,
15533    const char *context,
15534    const char *folder,
15535    const char *msg_id,
15536    ast_vm_msg_play_cb cb)
15537 {
15538    struct vm_state vms;
15539    struct ast_vm_user *vmu = NULL, vmus;
15540    int res = 0;
15541    int open = 0;
15542    int i;
15543    char filename[PATH_MAX];
15544    struct ast_config *msg_cfg;
15545    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
15546    int duration = 0;
15547    const char *value;
15548 
15549    if (ast_strlen_zero(mailbox)) {
15550       ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
15551       return -1;
15552    }
15553 
15554    if (ast_strlen_zero(folder)) {
15555       ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
15556       return -1;
15557    }
15558 
15559    if (ast_strlen_zero(msg_id)) {
15560       ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
15561       return -1;
15562    }
15563 
15564    memset(&vmus, 0, sizeof(vmus));
15565    memset(&vms, 0, sizeof(vms));
15566 
15567    if (ast_strlen_zero(context)) {
15568       context = "default";
15569    }
15570 
15571    if (!(vmu = find_user(&vmus, context, mailbox))) {
15572       return -1;
15573    }
15574 
15575    i = get_folder_by_name(folder);
15576    ast_copy_string(vms.username, mailbox, sizeof(vms.username));
15577    vms.lastmsg = -1;
15578    if ((res = open_mailbox(&vms, vmu, i)) < 0) {
15579       ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
15580       goto play2_msg_cleanup;
15581    }
15582    open = 1;
15583 
15584    if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
15585       res = -1;
15586       goto play2_msg_cleanup;
15587    }
15588 
15589    /* Find the msg */
15590    make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
15591    snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
15592    RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
15593 
15594    msg_cfg = ast_config_load(filename, config_flags);
15595    if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
15596       DISPOSE(vms.curdir, vms.curmsg);
15597       res = -1;
15598       goto play2_msg_cleanup;
15599    }
15600    if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
15601       duration = atoi(value);
15602    }
15603    ast_config_destroy(msg_cfg);
15604 
15605 #ifdef IMAP_STORAGE
15606    /*IMAP storage stores any prepended message from a forward
15607     * as a separate file from the rest of the message
15608     */
15609    if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
15610       wait_file(chan, &vms, vms.introfn);
15611    }
15612 #endif
15613    if (cb) {
15614       cb(chan, vms.fn, duration);
15615    } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
15616       ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
15617    } else {
15618       res = 0;
15619    }
15620 
15621    vms.heard[vms.curmsg] = 1;
15622 
15623    /* cleanup configs and msg */
15624    DISPOSE(vms.curdir, vms.curmsg);
15625 
15626 play2_msg_cleanup:
15627    if (vmu && open) {
15628       close_mailbox(&vms, vmu);
15629    }
15630 
15631 #ifdef IMAP_STORAGE
15632    if (vmu) {
15633       vmstate_delete(&vms);
15634    }
15635 #endif
15636 
15637    if (!res) {
15638       notify_new_state(vmu);
15639    }
15640 
15641    return res;
15642 }
15643 
15644 /* This is a workaround so that menuselect displays a proper description
15645  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
15646  */
15647 
15648 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
15649       .load = load_module,
15650       .unload = unload_module,
15651       .reload = reload,
15652       .nonoptreq = "res_adsi,res_smdi",
15653       );