Tue Mar 2 17:31:43 2010

Asterisk developer's documentation


app_voicemail.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Comedian Mail - Voicemail System
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * \extref Unixodbc - http://www.unixodbc.org
00026  * \extref A source distribution of University of Washington's IMAP
00027 c-client (http://www.washington.edu/imap/
00028  * 
00029  * \par See also
00030  * \arg \ref Config_vm
00031  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
00032  * \ingroup applications
00033  * \note This module requires res_adsi to load. This needs to be optional
00034  * during compilation.
00035  *
00036  *
00037  *
00038  * \note  This file is now almost impossible to work with, due to all \#ifdefs.
00039  *        Feels like the database code before realtime. Someone - please come up
00040  *        with a plan to clean this up.
00041  */
00042 
00043 /*** MODULEINFO
00044    <depend>res_smdi</depend>
00045  ***/
00046 
00047 /*** MAKEOPTS
00048 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_voicemail.so apps/app_directory.o apps/app_directory.so">
00049    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00050       <conflict>ODBC_STORAGE</conflict>
00051       <conflict>IMAP_STORAGE</conflict>
00052       <defaultenabled>yes</defaultenabled>
00053    </member>
00054    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00055       <depend>unixodbc</depend>
00056       <depend>ltdl</depend>
00057       <conflict>IMAP_STORAGE</conflict>
00058       <conflict>FILE_STORAGE</conflict>
00059       <defaultenabled>no</defaultenabled>
00060    </member>
00061    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00062       <depend>imap_tk</depend>
00063       <conflict>ODBC_STORAGE</conflict>
00064       <conflict>FILE_STORAGE</conflict>
00065       <use>ssl</use>
00066       <defaultenabled>no</defaultenabled>
00067    </member>
00068 </category>
00069  ***/
00070 
00071 #include "asterisk.h"
00072 
00073 #ifdef IMAP_STORAGE
00074 #include <ctype.h>
00075 #include <signal.h>
00076 #include <pwd.h>
00077 #ifdef USE_SYSTEM_IMAP
00078 #include <imap/c-client.h>
00079 #include <imap/imap4r1.h>
00080 #include <imap/linkage.h>
00081 #elif defined (USE_SYSTEM_CCLIENT)
00082 #include <c-client/c-client.h>
00083 #include <c-client/imap4r1.h>
00084 #include <c-client/linkage.h>
00085 #else
00086 #include "c-client.h"
00087 #include "imap4r1.h"
00088 #include "linkage.h"
00089 #endif
00090 #endif
00091 
00092 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 236669 $")
00093 
00094 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00095 #include <sys/time.h>
00096 #include <sys/stat.h>
00097 #include <sys/mman.h>
00098 #include <time.h>
00099 #include <dirent.h>
00100 
00101 #include "asterisk/logger.h"
00102 #include "asterisk/lock.h"
00103 #include "asterisk/file.h"
00104 #include "asterisk/channel.h"
00105 #include "asterisk/pbx.h"
00106 #include "asterisk/config.h"
00107 #include "asterisk/say.h"
00108 #include "asterisk/module.h"
00109 #include "asterisk/adsi.h"
00110 #include "asterisk/app.h"
00111 #include "asterisk/manager.h"
00112 #include "asterisk/dsp.h"
00113 #include "asterisk/localtime.h"
00114 #include "asterisk/cli.h"
00115 #include "asterisk/utils.h"
00116 #include "asterisk/stringfields.h"
00117 #include "asterisk/smdi.h"
00118 #include "asterisk/event.h"
00119 #include "asterisk/taskprocessor.h"
00120 
00121 #ifdef ODBC_STORAGE
00122 #include "asterisk/res_odbc.h"
00123 #endif
00124 
00125 #ifdef IMAP_STORAGE
00126 #include "asterisk/threadstorage.h"
00127 
00128 static char imapserver[48];
00129 static char imapport[8];
00130 static char imapflags[128];
00131 static char imapfolder[64];
00132 static char imapparentfolder[64] = "\0";
00133 static char greetingfolder[64];
00134 static char authuser[32];
00135 static char authpassword[42];
00136 static int imapversion = 1;
00137 
00138 static int expungeonhangup = 1;
00139 static int imapgreetings = 0;
00140 static char delimiter = '\0';
00141 
00142 struct vm_state;
00143 struct ast_vm_user;
00144 
00145 AST_THREADSTORAGE(ts_vmstate);
00146 
00147 /* Forward declarations for IMAP */
00148 static int init_mailstream(struct vm_state *vms, int box);
00149 static void write_file(char *filename, char *buffer, unsigned long len);
00150 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00151 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00152 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00153 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00154 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00155 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00156 static void vmstate_insert(struct vm_state *vms);
00157 static void vmstate_delete(struct vm_state *vms);
00158 static void set_update(MAILSTREAM * stream);
00159 static void init_vm_state(struct vm_state *vms);
00160 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00161 static void get_mailbox_delimiter(MAILSTREAM *stream);
00162 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00163 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00164 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00165 static void update_messages_by_imapuser(const char *user, unsigned long number);
00166 static int vm_delete(char *file);
00167 
00168 static int imap_remove_file (char *dir, int msgnum);
00169 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00170 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00171 static void check_quota(struct vm_state *vms, char *mailbox);
00172 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00173 struct vmstate {
00174    struct vm_state *vms;
00175    AST_LIST_ENTRY(vmstate) list;
00176 };
00177 
00178 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00179 
00180 #endif
00181 
00182 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00183 
00184 #define COMMAND_TIMEOUT 5000
00185 /* Don't modify these here; set your umask at runtime instead */
00186 #define  VOICEMAIL_DIR_MODE   0777
00187 #define  VOICEMAIL_FILE_MODE  0666
00188 #define  CHUNKSIZE   65536
00189 
00190 #define VOICEMAIL_CONFIG "voicemail.conf"
00191 #define ASTERISK_USERNAME "asterisk"
00192 
00193 /* Define fast-forward, pause, restart, and reverse keys
00194    while listening to a voicemail message - these are
00195    strings, not characters */
00196 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00197 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00198 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00199 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00200 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00201 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00202 
00203 /* Default mail command to mail voicemail. Change it with the
00204     mailcmd= command in voicemail.conf */
00205 #define SENDMAIL "/usr/sbin/sendmail -t"
00206 
00207 #define INTRO "vm-intro"
00208 
00209 #define MAXMSG 100
00210 #define MAXMSGLIMIT 9999
00211 
00212 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00213 
00214 #define BASELINELEN 72
00215 #define BASEMAXINLINE 256
00216 #define eol "\r\n"
00217 
00218 #define MAX_DATETIME_FORMAT   512
00219 #define MAX_NUM_CID_CONTEXTS 10
00220 
00221 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00222 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00223 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00224 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00225 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00226 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00227 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00228 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00229 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00230 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00231 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00232 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00233 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00234 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00235 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00236 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00237 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00238 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00239 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00240 #define ERROR_LOCK_PATH  -100
00241 
00242 
00243 enum {
00244    NEW_FOLDER,
00245    OLD_FOLDER,
00246    WORK_FOLDER,
00247    FAMILY_FOLDER,
00248    FRIENDS_FOLDER,
00249    GREETINGS_FOLDER
00250 } vm_box;
00251 
00252 enum {
00253    OPT_SILENT =           (1 << 0),
00254    OPT_BUSY_GREETING =    (1 << 1),
00255    OPT_UNAVAIL_GREETING = (1 << 2),
00256    OPT_RECORDGAIN =       (1 << 3),
00257    OPT_PREPEND_MAILBOX =  (1 << 4),
00258    OPT_AUTOPLAY =         (1 << 6),
00259    OPT_DTMFEXIT =         (1 << 7),
00260    OPT_MESSAGE_Urgent =   (1 << 8),
00261    OPT_MESSAGE_PRIORITY = (1 << 9)
00262 } vm_option_flags;
00263 
00264 enum {
00265    OPT_ARG_RECORDGAIN = 0,
00266    OPT_ARG_PLAYFOLDER = 1,
00267    OPT_ARG_DTMFEXIT   = 2,
00268    /* This *must* be the last value in this enum! */
00269    OPT_ARG_ARRAY_SIZE = 3,
00270 } vm_option_args;
00271 
00272 AST_APP_OPTIONS(vm_app_options, {
00273    AST_APP_OPTION('s', OPT_SILENT),
00274    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00275    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00276    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00277    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00278    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00279    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00280    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00281    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00282 });
00283 
00284 static int load_config(int reload);
00285 
00286 /*! \page vmlang Voicemail Language Syntaxes Supported
00287 
00288    \par Syntaxes supported, not really language codes.
00289    \arg \b en    - English
00290    \arg \b de    - German
00291    \arg \b es    - Spanish
00292    \arg \b fr    - French
00293    \arg \b it    - Italian
00294    \arg \b nl    - Dutch
00295    \arg \b pt    - Portuguese
00296    \arg \b pt_BR - Portuguese (Brazil)
00297    \arg \b gr    - Greek
00298    \arg \b no    - Norwegian
00299    \arg \b se    - Swedish
00300    \arg \b tw    - Chinese (Taiwan)
00301    \arg \b ua - Ukrainian
00302 
00303 German requires the following additional soundfile:
00304 \arg \b 1F  einE (feminine)
00305 
00306 Spanish requires the following additional soundfile:
00307 \arg \b 1M      un (masculine)
00308 
00309 Dutch, Portuguese & Spanish require the following additional soundfiles:
00310 \arg \b vm-INBOXs singular of 'new'
00311 \arg \b vm-Olds      singular of 'old/heard/read'
00312 
00313 NB these are plural:
00314 \arg \b vm-INBOX  nieuwe (nl)
00315 \arg \b vm-Old    oude (nl)
00316 
00317 Polish uses:
00318 \arg \b vm-new-a  'new', feminine singular accusative
00319 \arg \b vm-new-e  'new', feminine plural accusative
00320 \arg \b vm-new-ych   'new', feminine plural genitive
00321 \arg \b vm-old-a  'old', feminine singular accusative
00322 \arg \b vm-old-e  'old', feminine plural accusative
00323 \arg \b vm-old-ych   'old', feminine plural genitive
00324 \arg \b digits/1-a   'one', not always same as 'digits/1'
00325 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00326 
00327 Swedish uses:
00328 \arg \b vm-nytt      singular of 'new'
00329 \arg \b vm-nya    plural of 'new'
00330 \arg \b vm-gammalt   singular of 'old'
00331 \arg \b vm-gamla  plural of 'old'
00332 \arg \b digits/ett   'one', not always same as 'digits/1'
00333 
00334 Norwegian uses:
00335 \arg \b vm-ny     singular of 'new'
00336 \arg \b vm-nye    plural of 'new'
00337 \arg \b vm-gammel singular of 'old'
00338 \arg \b vm-gamle  plural of 'old'
00339 
00340 Dutch also uses:
00341 \arg \b nl-om     'at'?
00342 
00343 Spanish also uses:
00344 \arg \b vm-youhaveno
00345 
00346 Italian requires the following additional soundfile:
00347 
00348 For vm_intro_it:
00349 \arg \b vm-nuovo  new
00350 \arg \b vm-nuovi  new plural
00351 \arg \b vm-vecchio   old
00352 \arg \b vm-vecchi old plural
00353 
00354 Chinese (Taiwan) requires the following additional soundfile:
00355 \arg \b vm-tong      A class-word for call (tong1)
00356 \arg \b vm-ri     A class-word for day (ri4)
00357 \arg \b vm-you    You (ni3)
00358 \arg \b vm-haveno   Have no (mei2 you3)
00359 \arg \b vm-have     Have (you3)
00360 \arg \b vm-listen   To listen (yao4 ting1)
00361 
00362 
00363 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00364 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00365 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00366 
00367 */
00368 
00369 struct baseio {
00370    int iocp;
00371    int iolen;
00372    int linelength;
00373    int ateof;
00374    unsigned char iobuf[BASEMAXINLINE];
00375 };
00376 
00377 /*! Structure for linked list of users 
00378  * Use ast_vm_user_destroy() to free one of these structures. */
00379 struct ast_vm_user {
00380    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00381    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00382    char password[80];               /*!< Secret pin code, numbers only */
00383    char fullname[80];               /*!< Full name, for directory app */
00384    char email[80];                  /*!< E-mail address */
00385    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00386    char serveremail[80];            /*!< From: Mail address */
00387    char mailcmd[160];               /*!< Configurable mail command */
00388    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00389    char zonetag[80];                /*!< Time zone */
00390    char callback[80];
00391    char dialout[80];
00392    char uniqueid[80];               /*!< Unique integer identifier */
00393    char exit[80];
00394    char attachfmt[20];              /*!< Attachment format */
00395    unsigned int flags;              /*!< VM_ flags */ 
00396    int saydurationm;
00397    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00398    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00399    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00400 #ifdef IMAP_STORAGE
00401    char imapuser[80];               /*!< IMAP server login */
00402    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00403    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00404    int imapversion;                 /*!< If configuration changes, use the new values */
00405 #endif
00406    double volgain;                  /*!< Volume gain for voicemails sent via email */
00407    AST_LIST_ENTRY(ast_vm_user) list;
00408 };
00409 
00410 /*! Voicemail time zones */
00411 struct vm_zone {
00412    AST_LIST_ENTRY(vm_zone) list;
00413    char name[80];
00414    char timezone[80];
00415    char msg_format[512];
00416 };
00417 
00418 #define VMSTATE_MAX_MSG_ARRAY 256
00419 
00420 /*! Voicemail mailbox state */
00421 struct vm_state {
00422    char curbox[80];
00423    char username[80];
00424    char context[80];
00425    char curdir[PATH_MAX];
00426    char vmbox[PATH_MAX];
00427    char fn[PATH_MAX];
00428    char intro[PATH_MAX];
00429    int *deleted;
00430    int *heard;
00431    int curmsg;
00432    int lastmsg;
00433    int newmessages;
00434    int oldmessages;
00435    int urgentmessages;
00436    int starting;
00437    int repeats;
00438 #ifdef IMAP_STORAGE
00439    ast_mutex_t lock;
00440    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00441    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00442    MAILSTREAM *mailstream;
00443    int vmArrayIndex;
00444    char imapuser[80];                   /*!< IMAP server login */
00445    int imapversion;
00446    int interactive;
00447    char introfn[PATH_MAX];              /*!< Name of prepended file */
00448    unsigned int quota_limit;
00449    unsigned int quota_usage;
00450    struct vm_state *persist_vms;
00451 #endif
00452 };
00453 
00454 #ifdef ODBC_STORAGE
00455 static char odbc_database[80];
00456 static char odbc_table[80];
00457 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00458 #define DISPOSE(a,b) remove_file(a,b)
00459 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00460 #define EXISTS(a,b,c,d) (message_exists(a,b))
00461 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00462 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00463 #define DELETE(a,b,c,d) (delete_file(a,b))
00464 #else
00465 #ifdef IMAP_STORAGE
00466 #define DISPOSE(a,b) (imap_remove_file(a,b))
00467 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
00468 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00469 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00470 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00471 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00472 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00473 #else
00474 #define RETRIEVE(a,b,c,d)
00475 #define DISPOSE(a,b)
00476 #define STORE(a,b,c,d,e,f,g,h,i,j)
00477 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00478 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00479 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00480 #define DELETE(a,b,c,d) (vm_delete(c))
00481 #endif
00482 #endif
00483 
00484 static char VM_SPOOL_DIR[PATH_MAX];
00485 
00486 static char ext_pass_cmd[128];
00487 static char ext_pass_check_cmd[128];
00488 
00489 static int my_umask;
00490 
00491 #define PWDCHANGE_INTERNAL (1 << 1)
00492 #define PWDCHANGE_EXTERNAL (1 << 2)
00493 static int pwdchange = PWDCHANGE_INTERNAL;
00494 
00495 #ifdef ODBC_STORAGE
00496 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00497 #else
00498 # ifdef IMAP_STORAGE
00499 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00500 # else
00501 # define tdesc "Comedian Mail (Voicemail System)"
00502 # endif
00503 #endif
00504 
00505 static char userscontext[AST_MAX_EXTENSION] = "default";
00506 
00507 static char *addesc = "Comedian Mail";
00508 
00509 static char *synopsis_vm = "Leave a Voicemail message";
00510 
00511 static char *descrip_vm =
00512    "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
00513    "application allows the calling party to leave a message for the specified\n"
00514    "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00515    "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00516    "specified mailbox does not exist.\n"
00517    "  The Voicemail application will exit if any of the following DTMF digits are\n"
00518    "received:\n"
00519    "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00520    "    * - Jump to the 'a' extension in the current dialplan context.\n"
00521    "  This application will set the following channel variable upon completion:\n"
00522    "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00523    "               application. The possible values are:\n"
00524    "               SUCCESS | USEREXIT | FAILED\n\n"
00525    "  Options:\n"
00526    "    b    - Play the 'busy' greeting to the calling party.\n"
00527    "    d([c]) - Accept digits for a new extension in context c, if played during\n"
00528    "             the greeting.  Context defaults to the current context.\n"
00529    "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00530    "           message. The units are whole-number decibels (dB).\n"
00531    "           Only works on supported technologies, which is DAHDI only.\n"
00532    "    s    - Skip the playback of instructions for leaving a message to the\n"
00533    "           calling party.\n"
00534    "    u    - Play the 'unavailable' greeting.\n"
00535    "    U    - Mark message as Urgent.\n"
00536    "    P    - Mark message as PRIORITY.\n";
00537 
00538 static char *synopsis_vmain = "Check Voicemail messages";
00539 
00540 static char *descrip_vmain =
00541    "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
00542    "calling party to check voicemail messages. A specific mailbox, and optional\n"
00543    "corresponding context, may be specified. If a mailbox is not provided, the\n"
00544    "calling party will be prompted to enter one. If a context is not specified,\n"
00545    "the 'default' context will be used.\n\n"
00546    "  Options:\n"
00547    "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00548    "           is entered by the caller.\n"
00549    "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00550    "           message. The units are whole-number decibels (dB).\n"
00551    "    s    - Skip checking the passcode for the mailbox.\n"
00552    "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00553    "           Accepted values are:\n"
00554    "               0 for INBOX\n"
00555    "               1 for Old\n"
00556    "               2 for Work\n"
00557    "               3 for Family\n"
00558    "               4 for Friends\n"
00559    "               5 for Cust1\n"
00560    "               6 for Cust2\n"
00561    "               7 for Cust3\n"
00562    "               8 for Cust4\n"
00563    "               9 for Cust5\n"
00564    "           Defaults to 0 (INBOX).\n";
00565 
00566 static char *synopsis_vm_box_exists =
00567 "Check to see if Voicemail mailbox exists";
00568 
00569 static char *descrip_vm_box_exists =
00570    "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
00571    "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00572    "will be used.\n"
00573    "  This application will set the following channel variable upon completion:\n"
00574    "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00575    "                        MailboxExists application. Possible values include:\n"
00576    "                        SUCCESS | FAILED\n\n"
00577    "  Options: (none)\n";
00578 
00579 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
00580 
00581 static char *descrip_vmauthenticate =
00582    "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
00583    "same way as the Authenticate application, but the passwords are taken from\n"
00584    "voicemail.conf.\n"
00585    "  If the mailbox is specified, only that mailbox's password will be considered\n"
00586    "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00587    "be set with the authenticated mailbox.\n\n"
00588    "  Options:\n"
00589    "    s - Skip playing the initial prompts.\n";
00590 
00591 /* Leave a message */
00592 static char *app = "VoiceMail";
00593 
00594 /* Check mail, control, etc */
00595 static char *app2 = "VoiceMailMain";
00596 
00597 static char *app3 = "MailboxExists";
00598 static char *app4 = "VMAuthenticate";
00599 
00600 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00601 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00602 static char zonetag[80];
00603 static int maxsilence;
00604 static int maxmsg;
00605 static int maxdeletedmsg;
00606 static int silencethreshold = 128;
00607 static char serveremail[80];
00608 static char mailcmd[160];  /* Configurable mail cmd */
00609 static char externnotify[160]; 
00610 static struct ast_smdi_interface *smdi_iface = NULL;
00611 static char vmfmts[80];
00612 static double volgain;
00613 static int vmminsecs;
00614 static int vmmaxsecs;
00615 static int maxgreet;
00616 static int skipms;
00617 static int maxlogins;
00618 static int minpassword;
00619 
00620 /*! Poll mailboxes for changes since there is something external to
00621  *  app_voicemail that may change them. */
00622 static unsigned int poll_mailboxes;
00623 
00624 /*! Polling frequency */
00625 static unsigned int poll_freq;
00626 /*! By default, poll every 30 seconds */
00627 #define DEFAULT_POLL_FREQ 30
00628 
00629 AST_MUTEX_DEFINE_STATIC(poll_lock);
00630 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00631 static pthread_t poll_thread = AST_PTHREADT_NULL;
00632 static unsigned char poll_thread_run;
00633 
00634 /*! Subscription to ... MWI event subscriptions */
00635 static struct ast_event_sub *mwi_sub_sub;
00636 /*! Subscription to ... MWI event un-subscriptions */
00637 static struct ast_event_sub *mwi_unsub_sub;
00638 
00639 /*!
00640  * \brief An MWI subscription
00641  *
00642  * This is so we can keep track of which mailboxes are subscribed to.
00643  * This way, we know which mailboxes to poll when the pollmailboxes
00644  * option is being used.
00645  */
00646 struct mwi_sub {
00647    AST_RWLIST_ENTRY(mwi_sub) entry;
00648    int old_urgent;
00649    int old_new;
00650    int old_old;
00651    uint32_t uniqueid;
00652    char mailbox[1];
00653 };
00654 
00655 struct mwi_sub_task {
00656    const char *mailbox;
00657    const char *context;
00658    uint32_t uniqueid;
00659 };
00660 
00661 static struct ast_taskprocessor *mwi_subscription_tps;
00662 
00663 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00664 
00665 /* custom audio control prompts for voicemail playback */
00666 static char listen_control_forward_key[12];
00667 static char listen_control_reverse_key[12];
00668 static char listen_control_pause_key[12];
00669 static char listen_control_restart_key[12];
00670 static char listen_control_stop_key[12];
00671 
00672 /* custom password sounds */
00673 static char vm_password[80] = "vm-password";
00674 static char vm_newpassword[80] = "vm-newpassword";
00675 static char vm_passchanged[80] = "vm-passchanged";
00676 static char vm_reenterpassword[80] = "vm-reenterpassword";
00677 static char vm_mismatch[80] = "vm-mismatch";
00678 static char vm_invalid_password[80] = "vm-invalid-password";
00679 static char vm_pls_try_again[80] = "vm-pls-try-again";
00680 
00681 static struct ast_flags globalflags = {0};
00682 
00683 static int saydurationminfo;
00684 
00685 static char dialcontext[AST_MAX_CONTEXT] = "";
00686 static char callcontext[AST_MAX_CONTEXT] = "";
00687 static char exitcontext[AST_MAX_CONTEXT] = "";
00688 
00689 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00690 
00691 
00692 static char *emailbody = NULL;
00693 static char *emailsubject = NULL;
00694 static char *pagerbody = NULL;
00695 static char *pagersubject = NULL;
00696 static char fromstring[100];
00697 static char pagerfromstring[100];
00698 static char charset[32] = "ISO-8859-1";
00699 
00700 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00701 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00702 static int adsiver = 1;
00703 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00704 
00705 /* Forward declarations - generic */
00706 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00707 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);
00708 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00709 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00710          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00711          signed char record_gain, struct vm_state *vms, char *flag);
00712 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00713 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00714 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);
00715 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);
00716 static void apply_options(struct ast_vm_user *vmu, const char *options);
00717 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);
00718 static int is_valid_dtmf(const char *key);
00719 
00720 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00721 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00722 #endif
00723 
00724 /*!
00725  * \brief Strips control and non 7-bit clean characters from input string.
00726  *
00727  * \note To map control and none 7-bit characters to a 7-bit clean characters
00728  *  please use ast_str_encode_mine().
00729  */
00730 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00731 {
00732    char *bufptr = buf;
00733    for (; *input; input++) {
00734       if (*input < 32) {
00735          continue;
00736       }
00737       *bufptr++ = *input;
00738       if (bufptr == buf + buflen - 1) {
00739          break;
00740       }
00741    }
00742    *bufptr = '\0';
00743    return buf;
00744 }
00745 
00746 
00747 /*!
00748  * \brief Sets default voicemail system options to a voicemail user.
00749  *
00750  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00751  * - all the globalflags
00752  * - the saydurationminfo
00753  * - the callcontext
00754  * - the dialcontext
00755  * - the exitcontext
00756  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00757  * - volume gain.
00758  */
00759 static void populate_defaults(struct ast_vm_user *vmu)
00760 {
00761    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00762    if (saydurationminfo)
00763       vmu->saydurationm = saydurationminfo;
00764    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00765    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00766    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00767    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00768    if (vmmaxsecs)
00769       vmu->maxsecs = vmmaxsecs;
00770    if (maxmsg)
00771       vmu->maxmsg = maxmsg;
00772    if (maxdeletedmsg)
00773       vmu->maxdeletedmsg = maxdeletedmsg;
00774    vmu->volgain = volgain;
00775 }
00776 
00777 /*!
00778  * \brief Sets a a specific property value.
00779  * \param vmu The voicemail user object to work with.
00780  * \param var The name of the property to be set.
00781  * \param value The value to be set to the property.
00782  * 
00783  * The property name must be one of the understood properties. See the source for details.
00784  */
00785 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00786 {
00787    int x;
00788    if (!strcasecmp(var, "attach")) {
00789       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00790    } else if (!strcasecmp(var, "attachfmt")) {
00791       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00792    } else if (!strcasecmp(var, "serveremail")) {
00793       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00794    } else if (!strcasecmp(var, "language")) {
00795       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00796    } else if (!strcasecmp(var, "tz")) {
00797       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00798 #ifdef IMAP_STORAGE
00799    } else if (!strcasecmp(var, "imapuser")) {
00800       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00801       vmu->imapversion = imapversion;
00802    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00803       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00804       vmu->imapversion = imapversion;
00805    } else if (!strcasecmp(var, "imapvmshareid")) {
00806       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00807       vmu->imapversion = imapversion;
00808 #endif
00809    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00810       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00811    } else if (!strcasecmp(var, "saycid")){
00812       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00813    } else if (!strcasecmp(var,"sendvoicemail")){
00814       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00815    } else if (!strcasecmp(var, "review")){
00816       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00817    } else if (!strcasecmp(var, "tempgreetwarn")){
00818       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00819    } else if (!strcasecmp(var, "messagewrap")){
00820       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00821    } else if (!strcasecmp(var, "operator")) {
00822       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00823    } else if (!strcasecmp(var, "envelope")){
00824       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00825    } else if (!strcasecmp(var, "moveheard")){
00826       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00827    } else if (!strcasecmp(var, "sayduration")){
00828       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00829    } else if (!strcasecmp(var, "saydurationm")){
00830       if (sscanf(value, "%30d", &x) == 1) {
00831          vmu->saydurationm = x;
00832       } else {
00833          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
00834       }
00835    } else if (!strcasecmp(var, "forcename")){
00836       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00837    } else if (!strcasecmp(var, "forcegreetings")){
00838       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00839    } else if (!strcasecmp(var, "callback")) {
00840       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00841    } else if (!strcasecmp(var, "dialout")) {
00842       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00843    } else if (!strcasecmp(var, "exitcontext")) {
00844       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00845    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
00846       vmu->maxsecs = atoi(value);
00847       if (vmu->maxsecs <= 0) {
00848          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
00849          vmu->maxsecs = vmmaxsecs;
00850       } else {
00851          vmu->maxsecs = atoi(value);
00852       }
00853       if (!strcasecmp(var, "maxmessage"))
00854          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
00855    } else if (!strcasecmp(var, "maxmsg")) {
00856       vmu->maxmsg = atoi(value);
00857       if (vmu->maxmsg <= 0) {
00858          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
00859          vmu->maxmsg = MAXMSG;
00860       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00861          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00862          vmu->maxmsg = MAXMSGLIMIT;
00863       }
00864    } else if (!strcasecmp(var, "backupdeleted")) {
00865       if (sscanf(value, "%30d", &x) == 1)
00866          vmu->maxdeletedmsg = x;
00867       else if (ast_true(value))
00868          vmu->maxdeletedmsg = MAXMSG;
00869       else
00870          vmu->maxdeletedmsg = 0;
00871 
00872       if (vmu->maxdeletedmsg < 0) {
00873          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
00874          vmu->maxdeletedmsg = MAXMSG;
00875       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
00876          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
00877          vmu->maxdeletedmsg = MAXMSGLIMIT;
00878       }
00879    } else if (!strcasecmp(var, "volgain")) {
00880       sscanf(value, "%30lf", &vmu->volgain);
00881    } else if (!strcasecmp(var, "options")) {
00882       apply_options(vmu, value);
00883    }
00884 }
00885 
00886 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
00887 {
00888    int fds[2], pid = 0;
00889 
00890    memset(buf, 0, len);
00891 
00892    if (pipe(fds)) {
00893       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
00894    } else {
00895       /* good to go*/
00896       pid = ast_safe_fork(0);
00897 
00898       if (pid < 0) {
00899          /* ok maybe not */
00900          close(fds[0]);
00901          close(fds[1]);
00902          snprintf(buf, len, "FAILURE: Fork failed");
00903       } else if (pid) {
00904          /* parent */
00905          close(fds[1]);
00906          if (read(fds[0], buf, len) < 0) {
00907             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00908          }
00909          close(fds[0]);
00910       } else {
00911          /*  child */
00912          AST_DECLARE_APP_ARGS(arg,
00913             AST_APP_ARG(v)[20];
00914          );
00915          char *mycmd = ast_strdupa(command);
00916 
00917          close(fds[0]);
00918          dup2(fds[1], STDOUT_FILENO);
00919          close(fds[1]);
00920          ast_close_fds_above_n(STDOUT_FILENO);
00921 
00922          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
00923 
00924          execv(arg.v[0], arg.v); 
00925          printf("FAILURE: %s", strerror(errno));
00926          _exit(0);
00927       }
00928    }
00929    return buf;
00930 }
00931 
00932 /*!
00933  * \brief Check that password meets minimum required length
00934  * \param vmu The voicemail user to change the password for.
00935  * \param password The password string to check
00936  *
00937  * \return zero on ok, 1 on not ok.
00938  */
00939 static int check_password(struct ast_vm_user *vmu, char *password)
00940 {
00941    /* check minimum length */
00942    if (strlen(password) < minpassword)
00943       return 1;
00944    if (!ast_strlen_zero(ext_pass_check_cmd)) {
00945       char cmd[255], buf[255];
00946 
00947       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
00948 
00949       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
00950       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
00951          ast_debug(5, "Result: %s\n", buf);
00952          if (!strncasecmp(buf, "VALID", 5)) {
00953             ast_debug(3, "Passed password check: '%s'\n", buf);
00954             return 0;
00955          } else if (!strncasecmp(buf, "FAILURE", 7)) {
00956             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
00957             return 0;
00958          } else {
00959             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
00960             return 1;
00961          }
00962       }
00963    }
00964    return 0;
00965 }
00966 
00967 /*! 
00968  * \brief Performs a change of the voicemail passowrd in the realtime engine.
00969  * \param vmu The voicemail user to change the password for.
00970  * \param password The new value to be set to the password for this user.
00971  * 
00972  * This only works if there is a realtime engine configured.
00973  * This is called from the (top level) vm_change_password.
00974  *
00975  * \return zero on success, -1 on error.
00976  */
00977 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00978 {
00979    int res = -1;
00980    if (!strcmp(vmu->password, password)) {
00981       /* No change (but an update would return 0 rows updated, so we opt out here) */
00982       res = 0;
00983    } else if (!ast_strlen_zero(vmu->uniqueid)) {
00984       if (strlen(password) > 10) {
00985          ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
00986       }
00987       if (ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL) > 0) {
00988          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00989          res = 0;
00990       }
00991    }
00992    return res;
00993 }
00994 
00995 /*!
00996  * \brief Destructively Parse options and apply.
00997  */
00998 static void apply_options(struct ast_vm_user *vmu, const char *options)
00999 {  
01000    char *stringp;
01001    char *s;
01002    char *var, *value;
01003    stringp = ast_strdupa(options);
01004    while ((s = strsep(&stringp, "|"))) {
01005       value = s;
01006       if ((var = strsep(&value, "=")) && value) {
01007          apply_option(vmu, var, value);
01008       }
01009    }  
01010 }
01011 
01012 /*!
01013  * \brief Loads the options specific to a voicemail user.
01014  * 
01015  * This is called when a vm_user structure is being set up, such as from load_options.
01016  */
01017 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01018 {
01019    struct ast_variable *tmp;
01020    tmp = var;
01021    while (tmp) {
01022       if (!strcasecmp(tmp->name, "vmsecret")) {
01023          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
01024       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
01025          if (ast_strlen_zero(retval->password))
01026             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
01027       } else if (!strcasecmp(tmp->name, "uniqueid")) {
01028          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
01029       } else if (!strcasecmp(tmp->name, "pager")) {
01030          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
01031       } else if (!strcasecmp(tmp->name, "email")) {
01032          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
01033       } else if (!strcasecmp(tmp->name, "fullname")) {
01034          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
01035       } else if (!strcasecmp(tmp->name, "context")) {
01036          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
01037 #ifdef IMAP_STORAGE
01038       } else if (!strcasecmp(tmp->name, "imapuser")) {
01039          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
01040          retval->imapversion = imapversion;
01041       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
01042          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
01043          retval->imapversion = imapversion;
01044       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01045          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01046          retval->imapversion = imapversion;
01047 #endif
01048       } else
01049          apply_option(retval, tmp->name, tmp->value);
01050       tmp = tmp->next;
01051    } 
01052 }
01053 
01054 /*!
01055  * \brief Determines if a DTMF key entered is valid.
01056  * \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.
01057  *
01058  * Tests the character entered against the set of valid DTMF characters. 
01059  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01060  */
01061 static int is_valid_dtmf(const char *key)
01062 {
01063    int i;
01064    char *local_key = ast_strdupa(key);
01065 
01066    for (i = 0; i < strlen(key); ++i) {
01067       if (!strchr(VALID_DTMF, *local_key)) {
01068          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01069          return 0;
01070       }
01071       local_key++;
01072    }
01073    return 1;
01074 }
01075 
01076 /*!
01077  * \brief Finds a voicemail user from the realtime engine.
01078  * \param ivm
01079  * \param context
01080  * \param mailbox
01081  *
01082  * 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.
01083  *
01084  * \return The ast_vm_user structure for the user that was found.
01085  */
01086 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01087 {
01088    struct ast_variable *var;
01089    struct ast_vm_user *retval;
01090 
01091    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01092       if (!ivm)
01093          ast_set_flag(retval, VM_ALLOCED);   
01094       else
01095          memset(retval, 0, sizeof(*retval));
01096       if (mailbox) 
01097          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01098       populate_defaults(retval);
01099       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01100          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01101       else
01102          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01103       if (var) {
01104          apply_options_full(retval, var);
01105          ast_variables_destroy(var);
01106       } else { 
01107          if (!ivm) 
01108             ast_free(retval);
01109          retval = NULL;
01110       }  
01111    } 
01112    return retval;
01113 }
01114 
01115 /*!
01116  * \brief Finds a voicemail user from the users file or the realtime engine.
01117  * \param ivm
01118  * \param context
01119  * \param mailbox
01120  * 
01121  * \return The ast_vm_user structure for the user that was found.
01122  */
01123 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01124 {
01125    /* This function could be made to generate one from a database, too */
01126    struct ast_vm_user *vmu=NULL, *cur;
01127    AST_LIST_LOCK(&users);
01128 
01129    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01130       context = "default";
01131 
01132    AST_LIST_TRAVERSE(&users, cur, list) {
01133 #ifdef IMAP_STORAGE
01134       if (cur->imapversion != imapversion) {
01135          continue;
01136       }
01137 #endif
01138       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01139          break;
01140       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01141          break;
01142    }
01143    if (cur) {
01144       /* Make a copy, so that on a reload, we have no race */
01145       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01146          memcpy(vmu, cur, sizeof(*vmu));
01147          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01148          AST_LIST_NEXT(vmu, list) = NULL;
01149       }
01150    } else
01151       vmu = find_user_realtime(ivm, context, mailbox);
01152    AST_LIST_UNLOCK(&users);
01153    return vmu;
01154 }
01155 
01156 /*!
01157  * \brief Resets a user password to a specified password.
01158  * \param context
01159  * \param mailbox
01160  * \param newpass
01161  *
01162  * This does the actual change password work, called by the vm_change_password() function.
01163  *
01164  * \return zero on success, -1 on error.
01165  */
01166 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01167 {
01168    /* This function could be made to generate one from a database, too */
01169    struct ast_vm_user *cur;
01170    int res = -1;
01171    AST_LIST_LOCK(&users);
01172    AST_LIST_TRAVERSE(&users, cur, list) {
01173       if ((!context || !strcasecmp(context, cur->context)) &&
01174          (!strcasecmp(mailbox, cur->mailbox)))
01175             break;
01176    }
01177    if (cur) {
01178       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01179       res = 0;
01180    }
01181    AST_LIST_UNLOCK(&users);
01182    return res;
01183 }
01184 
01185 /*! 
01186  * \brief The handler for the change password option.
01187  * \param vmu The voicemail user to work with.
01188  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01189  * 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.
01190  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01191  */
01192 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01193 {
01194    struct ast_config   *cfg=NULL;
01195    struct ast_variable *var=NULL;
01196    struct ast_category *cat=NULL;
01197    char *category=NULL, *value=NULL, *new=NULL;
01198    const char *tmp=NULL;
01199    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01200    if (!change_password_realtime(vmu, newpassword))
01201       return;
01202 
01203    /* check voicemail.conf */
01204    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
01205       while ((category = ast_category_browse(cfg, category))) {
01206          if (!strcasecmp(category, vmu->context)) {
01207             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01208                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01209                break;
01210             }
01211             value = strstr(tmp,",");
01212             if (!value) {
01213                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01214                break;
01215             }
01216             new = alloca((strlen(value)+strlen(newpassword)+1));
01217             sprintf(new,"%s%s", newpassword, value);
01218             if (!(cat = ast_category_get(cfg, category))) {
01219                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01220                break;
01221             }
01222             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01223          }
01224       }
01225       /* save the results */
01226       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01227       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01228       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01229    }
01230    category = NULL;
01231    var = NULL;
01232    /* check users.conf and update the password stored for the mailbox*/
01233    /* if no vmsecret entry exists create one. */
01234    if ((cfg = ast_config_load("users.conf", config_flags))) {
01235       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01236       while ((category = ast_category_browse(cfg, category))) {
01237          ast_debug(4, "users.conf: %s\n", category);
01238          if (!strcasecmp(category, vmu->mailbox)) {
01239             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01240                ast_debug(3, "looks like we need to make vmsecret!\n");
01241                var = ast_variable_new("vmsecret", newpassword, "");
01242             } 
01243             new = alloca(strlen(newpassword)+1);
01244             sprintf(new, "%s", newpassword);
01245             if (!(cat = ast_category_get(cfg, category))) {
01246                ast_debug(4, "failed to get category!\n");
01247                break;
01248             }
01249             if (!var)      
01250                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01251             else
01252                ast_variable_append(cat, var);
01253          }
01254       }
01255       /* save the results and clean things up */
01256       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01257       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01258       config_text_file_save("users.conf", cfg, "AppVoicemail");
01259    }
01260 }
01261 
01262 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01263 {
01264    char buf[255];
01265    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01266    if (!ast_safe_system(buf)) {
01267       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01268       /* Reset the password in memory, too */
01269       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01270    }
01271 }
01272 
01273 /*! 
01274  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01275  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01276  * \param len The length of the path string that was written out.
01277  * 
01278  * The path is constructed as 
01279  *    VM_SPOOL_DIRcontext/ext/folder
01280  *
01281  * \return zero on success, -1 on error.
01282  */
01283 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01284 {
01285    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01286 }
01287 
01288 /*! 
01289  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01290  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01291  * \param len The length of the path string that was written out.
01292  * 
01293  * The path is constructed as 
01294  *    VM_SPOOL_DIRcontext/ext/folder
01295  *
01296  * \return zero on success, -1 on error.
01297  */
01298 static int make_file(char *dest, const int len, const char *dir, const int num)
01299 {
01300    return snprintf(dest, len, "%s/msg%04d", dir, num);
01301 }
01302 
01303 /* same as mkstemp, but return a FILE * */
01304 static FILE *vm_mkftemp(char *template)
01305 {
01306    FILE *p = NULL;
01307    int pfd = mkstemp(template);
01308    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01309    if (pfd > -1) {
01310       p = fdopen(pfd, "w+");
01311       if (!p) {
01312          close(pfd);
01313          pfd = -1;
01314       }
01315    }
01316    return p;
01317 }
01318 
01319 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01320  * \param dest    String. base directory.
01321  * \param len     Length of dest.
01322  * \param context String. Ignored if is null or empty string.
01323  * \param ext     String. Ignored if is null or empty string.
01324  * \param folder  String. Ignored if is null or empty string. 
01325  * \return -1 on failure, 0 on success.
01326  */
01327 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01328 {
01329    mode_t   mode = VOICEMAIL_DIR_MODE;
01330    int res;
01331 
01332    make_dir(dest, len, context, ext, folder);
01333    if ((res = ast_mkdir(dest, mode))) {
01334       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01335       return -1;
01336    }
01337    return 0;
01338 }
01339 
01340 static const char *mbox(int id)
01341 {
01342    static const char *msgs[] = {
01343 #ifdef IMAP_STORAGE
01344       imapfolder,
01345 #else
01346       "INBOX",
01347 #endif
01348       "Old",
01349       "Work",
01350       "Family",
01351       "Friends",
01352       "Cust1",
01353       "Cust2",
01354       "Cust3",
01355       "Cust4",
01356       "Cust5",
01357       "Deleted",
01358       "Urgent"
01359    };
01360    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
01361 }
01362 
01363 static void free_user(struct ast_vm_user *vmu)
01364 {
01365    if (ast_test_flag(vmu, VM_ALLOCED))
01366       ast_free(vmu);
01367 }
01368 
01369 /* All IMAP-specific functions should go in this block. This
01370  * keeps them from being spread out all over the code */
01371 #ifdef IMAP_STORAGE
01372 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01373 {
01374    char arg[10];
01375    struct vm_state *vms;
01376    unsigned long messageNum;
01377 
01378    /* If greetings aren't stored in IMAP, just delete the file */
01379    if (msgnum < 0 && !imapgreetings) {
01380       ast_filedelete(file, NULL);
01381       return;
01382    }
01383 
01384    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01385       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);
01386       return;
01387    }
01388 
01389    /* find real message number based on msgnum */
01390    /* this may be an index into vms->msgArray based on the msgnum. */
01391    messageNum = vms->msgArray[msgnum];
01392    if (messageNum == 0) {
01393       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01394       return;
01395    }
01396    if (option_debug > 2)
01397       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01398    /* delete message */
01399    snprintf (arg, sizeof(arg), "%lu",messageNum);
01400    ast_mutex_lock(&vms->lock);
01401    mail_setflag (vms->mailstream,arg,"\\DELETED");
01402    mail_expunge(vms->mailstream);
01403    ast_mutex_unlock(&vms->lock);
01404 }
01405 
01406 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01407 {
01408    struct vm_state *vms_p;
01409    char *file, *filename;
01410    char *attachment;
01411    int ret = 0, i;
01412    BODY *body;
01413 
01414    /* This function is only used for retrieval of IMAP greetings
01415     * regular messages are not retrieved this way, nor are greetings
01416     * if they are stored locally*/
01417    if (msgnum > -1 || !imapgreetings) {
01418       return 0;
01419    } else {
01420       file = strrchr(ast_strdupa(dir), '/');
01421       if (file)
01422          *file++ = '\0';
01423       else {
01424          ast_debug (1, "Failed to procure file name from directory passed.\n");
01425          return -1;
01426       }
01427    }
01428 
01429    /* check if someone is accessing this box right now... */
01430    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01431       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01432       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01433       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01434       * that's all we need to do.
01435       */
01436       if (!(vms_p = create_vm_state_from_user(vmu))) {
01437          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01438          return -1;
01439       }
01440    }
01441    
01442    /* Greetings will never have a prepended message */
01443    *vms_p->introfn = '\0';
01444 
01445    ast_mutex_lock(&vms_p->lock);
01446    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01447    if (!vms_p->mailstream) {
01448       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01449       ast_mutex_unlock(&vms_p->lock);
01450       return -1;
01451    }
01452 
01453    /*XXX Yuck, this could probably be done a lot better */
01454    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01455       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01456       /* We have the body, now we extract the file name of the first attachment. */
01457       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01458          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01459       } else {
01460          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01461          ast_mutex_unlock(&vms_p->lock);
01462          return -1;
01463       }
01464       filename = strsep(&attachment, ".");
01465       if (!strcmp(filename, file)) {
01466          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01467          vms_p->msgArray[vms_p->curmsg] = i + 1;
01468          save_body(body, vms_p, "2", attachment, 0);
01469          ast_mutex_unlock(&vms_p->lock);
01470          return 0;
01471       }
01472    }
01473    ast_mutex_unlock(&vms_p->lock);
01474 
01475    return -1;
01476 }
01477 
01478 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01479 {
01480    BODY *body;
01481    char *header_content;
01482    char *attachedfilefmt;
01483    char buf[80];
01484    struct vm_state *vms;
01485    char text_file[PATH_MAX];
01486    FILE *text_file_ptr;
01487    int res = 0;
01488    struct ast_vm_user *vmu;
01489 
01490    if (!(vmu = find_user(NULL, context, mailbox))) {
01491       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01492       return -1;
01493    }
01494    
01495    if (msgnum < 0) {
01496       if (imapgreetings) {
01497          res = imap_retrieve_greeting(dir, msgnum, vmu);
01498          goto exit;
01499       } else {
01500          res = 0;
01501          goto exit;
01502       }
01503    }
01504 
01505    /* Before anything can happen, we need a vm_state so that we can
01506     * actually access the imap server through the vms->mailstream
01507     */
01508    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01509       /* This should not happen. If it does, then I guess we'd
01510        * need to create the vm_state, extract which mailbox to
01511        * open, and then set up the msgArray so that the correct
01512        * IMAP message could be accessed. If I have seen correctly
01513        * though, the vms should be obtainable from the vmstates list
01514        * and should have its msgArray properly set up.
01515        */
01516       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01517       res = -1;
01518       goto exit;
01519    }
01520    
01521    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01522    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01523 
01524    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01525    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01526       res = 0;
01527       goto exit;
01528    }
01529 
01530    if (option_debug > 2)
01531       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01532    if (vms->msgArray[msgnum] == 0) {
01533       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01534       res = -1;
01535       goto exit;
01536    }
01537 
01538    /* This will only work for new messages... */
01539    ast_mutex_lock(&vms->lock);
01540    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01541    ast_mutex_unlock(&vms->lock);
01542    /* empty string means no valid header */
01543    if (ast_strlen_zero(header_content)) {
01544       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01545       res = -1;
01546       goto exit;
01547    }
01548 
01549    ast_mutex_lock(&vms->lock);
01550    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01551    ast_mutex_unlock(&vms->lock);
01552 
01553    /* We have the body, now we extract the file name of the first attachment. */
01554    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01555       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01556    } else {
01557       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01558       res = -1;
01559       goto exit;
01560    }
01561    
01562    /* Find the format of the attached file */
01563 
01564    strsep(&attachedfilefmt, ".");
01565    if (!attachedfilefmt) {
01566       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01567       res = -1;
01568       goto exit;
01569    }
01570    
01571    save_body(body, vms, "2", attachedfilefmt, 0);
01572    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01573       *vms->introfn = '\0';
01574    }
01575 
01576    /* Get info from headers!! */
01577    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01578 
01579    if (!(text_file_ptr = fopen(text_file, "w"))) {
01580       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01581    }
01582 
01583    fprintf(text_file_ptr, "%s\n", "[message]");
01584 
01585    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01586    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01587    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01588    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01589    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01590    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01591    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01592    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01593    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01594    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01595    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01596    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01597    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01598    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01599    fclose(text_file_ptr);
01600 
01601 exit:
01602    free_user(vmu);
01603    return res;
01604 }
01605 
01606 static int folder_int(const char *folder)
01607 {
01608    /*assume a NULL folder means INBOX*/
01609    if (!folder)
01610       return 0;
01611 #ifdef IMAP_STORAGE
01612    if (!strcasecmp(folder, imapfolder))
01613 #else
01614    if (!strcasecmp(folder, "INBOX"))
01615 #endif
01616       return 0;
01617    else if (!strcasecmp(folder, "Old"))
01618       return 1;
01619    else if (!strcasecmp(folder, "Work"))
01620       return 2;
01621    else if (!strcasecmp(folder, "Family"))
01622       return 3;
01623    else if (!strcasecmp(folder, "Friends"))
01624       return 4;
01625    else if (!strcasecmp(folder, "Cust1"))
01626       return 5;
01627    else if (!strcasecmp(folder, "Cust2"))
01628       return 6;
01629    else if (!strcasecmp(folder, "Cust3"))
01630       return 7;
01631    else if (!strcasecmp(folder, "Cust4"))
01632       return 8;
01633    else if (!strcasecmp(folder, "Cust5"))
01634       return 9;
01635    else /*assume they meant INBOX if folder is not found otherwise*/
01636       return 0;
01637 }
01638 
01639 /*!
01640  * \brief Gets the number of messages that exist in a mailbox folder.
01641  * \param context
01642  * \param mailbox
01643  * \param folder
01644  * 
01645  * This method is used when IMAP backend is used.
01646  * \return The number of messages in this mailbox folder (zero or more).
01647  */
01648 static int messagecount(const char *context, const char *mailbox, const char *folder)
01649 {
01650    SEARCHPGM *pgm;
01651    SEARCHHEADER *hdr;
01652 
01653    struct ast_vm_user *vmu, vmus;
01654    struct vm_state *vms_p;
01655    int ret = 0;
01656    int fold = folder_int(folder);
01657    int urgent = 0;
01658    
01659    /* If URGENT, then look at INBOX */
01660    if (fold == 11) {
01661       fold = NEW_FOLDER;
01662       urgent = 1;
01663    }
01664 
01665    if (ast_strlen_zero(mailbox))
01666       return 0;
01667 
01668    /* We have to get the user before we can open the stream! */
01669    vmu = find_user(&vmus, context, mailbox);
01670    if (!vmu) {
01671       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01672       return -1;
01673    } else {
01674       /* No IMAP account available */
01675       if (vmu->imapuser[0] == '\0') {
01676          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01677          return -1;
01678       }
01679    }
01680    
01681    /* No IMAP account available */
01682    if (vmu->imapuser[0] == '\0') {
01683       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01684       free_user(vmu);
01685       return -1;
01686    }
01687 
01688    /* check if someone is accessing this box right now... */
01689    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01690    if (!vms_p) {
01691       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01692    }
01693    if (vms_p) {
01694       ast_debug(3, "Returning before search - user is logged in\n");
01695       if (fold == 0) { /* INBOX */
01696          return vms_p->newmessages;
01697       }
01698       if (fold == 1) { /* Old messages */
01699          return vms_p->oldmessages;
01700       }
01701       if (fold == 11) {/*Urgent messages*/
01702          return vms_p->urgentmessages;
01703       }
01704    }
01705 
01706    /* add one if not there... */
01707    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01708    if (!vms_p) {
01709       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01710    }
01711 
01712    if (!vms_p) {
01713       vms_p = create_vm_state_from_user(vmu);
01714    }
01715    ret = init_mailstream(vms_p, fold);
01716    if (!vms_p->mailstream) {
01717       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01718       return -1;
01719    }
01720    if (ret == 0) {
01721       ast_mutex_lock(&vms_p->lock);
01722       pgm = mail_newsearchpgm ();
01723       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01724       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01725       pgm->header = hdr;
01726       if (fold != 1) {
01727          pgm->unseen = 1;
01728          pgm->seen = 0;
01729       }
01730       /* In the special case where fold is 1 (old messages) we have to do things a bit
01731        * differently. Old messages are stored in the INBOX but are marked as "seen"
01732        */
01733       else {
01734          pgm->unseen = 0;
01735          pgm->seen = 1;
01736       }
01737       /* look for urgent messages */
01738       if (urgent) {
01739          pgm->flagged = 1;
01740          pgm->unflagged = 0;
01741       }
01742       pgm->undeleted = 1;
01743       pgm->deleted = 0;
01744 
01745       vms_p->vmArrayIndex = 0;
01746       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01747       if (fold == 0 && urgent == 0)
01748          vms_p->newmessages = vms_p->vmArrayIndex;
01749       if (fold == 1)
01750          vms_p->oldmessages = vms_p->vmArrayIndex;
01751       if (fold == 0 && urgent == 1)
01752          vms_p->urgentmessages = vms_p->vmArrayIndex;
01753       /*Freeing the searchpgm also frees the searchhdr*/
01754       mail_free_searchpgm(&pgm);
01755       ast_mutex_unlock(&vms_p->lock);
01756       vms_p->updated = 0;
01757       return vms_p->vmArrayIndex;
01758    } else {
01759       ast_mutex_lock(&vms_p->lock);
01760       mail_ping(vms_p->mailstream);
01761       ast_mutex_unlock(&vms_p->lock);
01762    }
01763    return 0;
01764 }
01765 
01766 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
01767 {
01768    char *myserveremail = serveremail;
01769    char fn[PATH_MAX];
01770    char introfn[PATH_MAX];
01771    char mailbox[256];
01772    char *stringp;
01773    FILE *p=NULL;
01774    char tmp[80] = "/tmp/astmail-XXXXXX";
01775    long len;
01776    void *buf;
01777    int tempcopy = 0;
01778    STRING str;
01779    int ret; /* for better error checking */
01780    char *imap_flags = NIL;
01781 
01782     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
01783     if (msgnum < 0 && !imapgreetings) {
01784         return 0;
01785     }
01786 
01787    /* Set urgent flag for IMAP message */
01788    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
01789       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
01790       imap_flags="\\FLAGGED";
01791    }
01792    
01793    /* Attach only the first format */
01794    fmt = ast_strdupa(fmt);
01795    stringp = fmt;
01796    strsep(&stringp, "|");
01797 
01798    if (!ast_strlen_zero(vmu->serveremail))
01799       myserveremail = vmu->serveremail;
01800 
01801    if (msgnum > -1)
01802       make_file(fn, sizeof(fn), dir, msgnum);
01803    else
01804       ast_copy_string (fn, dir, sizeof(fn));
01805 
01806    snprintf(introfn, sizeof(introfn), "%sintro", fn);
01807    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
01808       *introfn = '\0';
01809    }
01810    
01811    if (ast_strlen_zero(vmu->email)) {
01812       /* We need the vmu->email to be set when we call make_email_file, but
01813        * if we keep it set, a duplicate e-mail will be created. So at the end
01814        * of this function, we will revert back to an empty string if tempcopy
01815        * is 1.
01816        */
01817       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01818       tempcopy = 1;
01819    }
01820 
01821    if (!strcmp(fmt, "wav49"))
01822       fmt = "WAV";
01823    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
01824 
01825    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01826       command hangs. */
01827    if (!(p = vm_mkftemp(tmp))) {
01828       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01829       if (tempcopy)
01830          *(vmu->email) = '\0';
01831       return -1;
01832    }
01833 
01834    if (msgnum < 0 && imapgreetings) {
01835       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
01836          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
01837          return -1;
01838       }
01839       imap_delete_old_greeting(fn, vms);
01840    }
01841 
01842    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX", S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
01843    /* read mail file to memory */
01844    len = ftell(p);
01845    rewind(p);
01846    if (!(buf = ast_malloc(len + 1))) {
01847       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
01848       fclose(p);
01849       if (tempcopy)
01850          *(vmu->email) = '\0';
01851       return -1;
01852    }
01853    if (fread(buf, len, 1, p) < len) {
01854       if (ferror(p)) {
01855          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
01856          return -1;
01857       }
01858    }
01859    ((char *)buf)[len] = '\0';
01860    INIT(&str, mail_string, buf, len);
01861    ret = init_mailstream(vms, NEW_FOLDER);
01862    if (ret == 0) {
01863       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
01864       ast_mutex_lock(&vms->lock);
01865       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
01866          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01867       ast_mutex_unlock(&vms->lock);
01868       fclose(p);
01869       unlink(tmp);
01870       ast_free(buf);
01871    } else {
01872       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
01873       fclose(p);
01874       unlink(tmp);
01875       ast_free(buf);
01876       return -1;
01877    }
01878    ast_debug(3, "%s stored\n", fn);
01879    
01880    if (tempcopy)
01881       *(vmu->email) = '\0';
01882    
01883    return 0;
01884 
01885 }
01886 
01887 /*!
01888  * \brief Gets the number of messages that exist in the inbox folder.
01889  * \param mailbox_context
01890  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
01891  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
01892  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
01893  * 
01894  * This method is used when IMAP backend is used.
01895  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
01896  *
01897  * \return zero on success, -1 on error.
01898  */
01899 
01900 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
01901 {
01902    char tmp[PATH_MAX] = "";
01903    char *mailboxnc;
01904    char *context;
01905    char *mb;
01906    char *cur;
01907    if (newmsgs)
01908       *newmsgs = 0;
01909    if (oldmsgs)
01910       *oldmsgs = 0;
01911    if (urgentmsgs)
01912       *urgentmsgs = 0;
01913 
01914    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
01915    /* If no mailbox, return immediately */
01916    if (ast_strlen_zero(mailbox_context))
01917       return 0;
01918    
01919    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01920    context = strchr(tmp, '@');
01921    if (strchr(mailbox_context, ',')) {
01922       int tmpnew, tmpold, tmpurgent;
01923       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01924       mb = tmp;
01925       while ((cur = strsep(&mb, ", "))) {
01926          if (!ast_strlen_zero(cur)) {
01927             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01928                return -1;
01929             else {
01930                if (newmsgs)
01931                   *newmsgs += tmpnew; 
01932                if (oldmsgs)
01933                   *oldmsgs += tmpold;
01934                if (urgentmsgs)
01935                   *urgentmsgs += tmpurgent;
01936             }
01937          }
01938       }
01939       return 0;
01940    }
01941    if (context) {
01942       *context = '\0';
01943       mailboxnc = tmp;
01944       context++;
01945    } else {
01946       context = "default";
01947       mailboxnc = (char *)mailbox_context;
01948    }
01949    if (newmsgs) {
01950       if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
01951          return -1;
01952    }
01953    if (oldmsgs) {
01954       if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
01955          return -1;
01956    }
01957    if (urgentmsgs) {
01958       if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
01959          return -1;
01960    }
01961    return 0;
01962 }
01963 
01964 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
01965 {
01966    return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
01967 }
01968 
01969 /** 
01970  * \brief Determines if the given folder has messages.
01971  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
01972  * \param folder the folder to look in
01973  *
01974  * This function is used when the mailbox is stored in an IMAP back end.
01975  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
01976  * \return 1 if the folder has one or more messages. zero otherwise.
01977  */
01978 
01979 static int has_voicemail(const char *mailbox, const char *folder)
01980 {
01981    char tmp[256], *tmp2, *box, *context;
01982    ast_copy_string(tmp, mailbox, sizeof(tmp));
01983    tmp2 = tmp;
01984    if (strchr(tmp2, ',')) {
01985       while ((box = strsep(&tmp2, ","))) {
01986          if (!ast_strlen_zero(box)) {
01987             if (has_voicemail(box, folder))
01988                return 1;
01989          }
01990       }
01991    }
01992    if ((context= strchr(tmp, '@')))
01993       *context++ = '\0';
01994    else
01995       context = "default";
01996    return messagecount(context, tmp, folder) ? 1 : 0;
01997 }
01998 
01999 /*!
02000  * \brief Copies a message from one mailbox to another.
02001  * \param chan
02002  * \param vmu
02003  * \param imbox
02004  * \param msgnum
02005  * \param duration
02006  * \param recip
02007  * \param fmt
02008  * \param dir
02009  *
02010  * This works with IMAP storage based mailboxes.
02011  *
02012  * \return zero on success, -1 on error.
02013  */
02014 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)
02015 {
02016    struct vm_state *sendvms = NULL, *destvms = NULL;
02017    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02018    if (msgnum >= recip->maxmsg) {
02019       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02020       return -1;
02021    }
02022    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02023       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02024       return -1;
02025    }
02026    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02027       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02028       return -1;
02029    }
02030    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02031    ast_mutex_lock(&sendvms->lock);
02032    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02033       ast_mutex_unlock(&sendvms->lock);
02034       return 0;
02035    }
02036    ast_mutex_unlock(&sendvms->lock);
02037    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02038    return -1;
02039 }
02040 
02041 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02042 {
02043    char tmp[256], *t = tmp;
02044    size_t left = sizeof(tmp);
02045    
02046    if (box == OLD_FOLDER) {
02047       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02048    } else {
02049       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02050    }
02051 
02052    if (box == NEW_FOLDER) {
02053       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02054    } else {
02055       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02056    }
02057 
02058    /* Build up server information */
02059    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02060 
02061    /* Add authentication user if present */
02062    if (!ast_strlen_zero(authuser))
02063       ast_build_string(&t, &left, "/authuser=%s", authuser);
02064 
02065    /* Add flags if present */
02066    if (!ast_strlen_zero(imapflags))
02067       ast_build_string(&t, &left, "/%s", imapflags);
02068 
02069    /* End with username */
02070 #if 1
02071    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02072 #else
02073    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02074 #endif
02075    if (box == NEW_FOLDER || box == OLD_FOLDER)
02076       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02077    else if (box == GREETINGS_FOLDER)
02078       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02079    else {   /* Other folders such as Friends, Family, etc... */
02080       if (!ast_strlen_zero(imapparentfolder)) {
02081          /* imapparentfolder would typically be set to INBOX */
02082          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02083       } else {
02084          snprintf(spec, len, "%s%s", tmp, mbox(box));
02085       }
02086    }
02087 }
02088 
02089 static int init_mailstream(struct vm_state *vms, int box)
02090 {
02091    MAILSTREAM *stream = NIL;
02092    long debug;
02093    char tmp[256];
02094    
02095    if (!vms) {
02096       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02097       return -1;
02098    }
02099    if (option_debug > 2)
02100       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02101    if (vms->mailstream == NIL || !vms->mailstream) {
02102       if (option_debug)
02103          ast_log (LOG_DEBUG,"mailstream not set.\n");
02104    } else {
02105       stream = vms->mailstream;
02106    }
02107    /* debug = T;  user wants protocol telemetry? */
02108    debug = NIL;  /* NO protocol telemetry? */
02109 
02110    if (delimiter == '\0') {      /* did not probe the server yet */
02111       char *cp;
02112 #ifdef USE_SYSTEM_IMAP
02113 #include <imap/linkage.c>
02114 #elif defined(USE_SYSTEM_CCLIENT)
02115 #include <c-client/linkage.c>
02116 #else
02117 #include "linkage.c"
02118 #endif
02119       /* Connect to INBOX first to get folders delimiter */
02120       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02121       ast_mutex_lock(&vms->lock);
02122       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02123       ast_mutex_unlock(&vms->lock);
02124       if (stream == NIL) {
02125          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02126          return -1;
02127       }
02128       get_mailbox_delimiter(stream);
02129       /* update delimiter in imapfolder */
02130       for (cp = imapfolder; *cp; cp++)
02131          if (*cp == '/')
02132             *cp = delimiter;
02133    }
02134    /* Now connect to the target folder */
02135    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02136    if (option_debug > 2)
02137       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02138    ast_mutex_lock(&vms->lock);
02139    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02140    ast_mutex_unlock(&vms->lock);
02141    if (vms->mailstream == NIL) {
02142       return -1;
02143    } else {
02144       return 0;
02145    }
02146 }
02147 
02148 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02149 {
02150    SEARCHPGM *pgm;
02151    SEARCHHEADER *hdr;
02152    int ret, urgent = 0;
02153 
02154    /* If Urgent, then look at INBOX */
02155    if (box == 11) {
02156       box = NEW_FOLDER;
02157       urgent = 1;
02158    }
02159 
02160    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02161    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02162    vms->imapversion = vmu->imapversion;
02163 
02164    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02165       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02166       return -1;
02167    }
02168    
02169    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02170    
02171    /* Check Quota */
02172    if  (box == 0)  {
02173       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02174       check_quota(vms,(char *)mbox(box));
02175    }
02176 
02177    ast_mutex_lock(&vms->lock);
02178    pgm = mail_newsearchpgm();
02179 
02180    /* Check IMAP folder for Asterisk messages only... */
02181    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02182    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02183    pgm->header = hdr;
02184    pgm->deleted = 0;
02185    pgm->undeleted = 1;
02186 
02187    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02188    if (box == NEW_FOLDER && urgent == 1) {
02189       pgm->unseen = 1;
02190       pgm->seen = 0;
02191       pgm->flagged = 1;
02192       pgm->unflagged = 0;
02193    } else if (box == NEW_FOLDER && urgent == 0) {
02194       pgm->unseen = 1;
02195       pgm->seen = 0;
02196       pgm->flagged = 0;
02197       pgm->unflagged = 1;
02198    } else if (box == OLD_FOLDER) {
02199       pgm->seen = 1;
02200       pgm->unseen = 0;
02201    }
02202 
02203    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02204 
02205    vms->vmArrayIndex = 0;
02206    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02207    vms->lastmsg = vms->vmArrayIndex - 1;
02208    mail_free_searchpgm(&pgm);
02209 
02210    ast_mutex_unlock(&vms->lock);
02211    return 0;
02212 }
02213 
02214 static void write_file(char *filename, char *buffer, unsigned long len)
02215 {
02216    FILE *output;
02217 
02218    output = fopen (filename, "w");
02219    if (fwrite(buffer, len, 1, output) != 1) {
02220       if (ferror(output)) {
02221          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02222       }
02223    }
02224    fclose (output);
02225 }
02226 
02227 static void update_messages_by_imapuser(const char *user, unsigned long number)
02228 {
02229    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02230 
02231    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02232       return;
02233    }
02234 
02235    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02236    vms->msgArray[vms->vmArrayIndex++] = number;
02237 }
02238 
02239 void mm_searched(MAILSTREAM *stream, unsigned long number)
02240 {
02241    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02242 
02243    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02244       return;
02245 
02246    update_messages_by_imapuser(user, number);
02247 }
02248 
02249 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02250 {
02251    struct ast_variable *var;
02252    struct ast_vm_user *vmu;
02253 
02254    vmu = ast_calloc(1, sizeof *vmu);
02255    if (!vmu)
02256       return NULL;
02257    ast_set_flag(vmu, VM_ALLOCED);
02258    populate_defaults(vmu);
02259 
02260    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02261    if (var) {
02262       apply_options_full(vmu, var);
02263       ast_variables_destroy(var);
02264       return vmu;
02265    } else {
02266       ast_free(vmu);
02267       return NULL;
02268    }
02269 }
02270 
02271 /* Interfaces to C-client */
02272 
02273 void mm_exists(MAILSTREAM * stream, unsigned long number)
02274 {
02275    /* mail_ping will callback here if new mail! */
02276    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02277    if (number == 0) return;
02278    set_update(stream);
02279 }
02280 
02281 
02282 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02283 {
02284    /* mail_ping will callback here if expunged mail! */
02285    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02286    if (number == 0) return;
02287    set_update(stream);
02288 }
02289 
02290 
02291 void mm_flags(MAILSTREAM * stream, unsigned long number)
02292 {
02293    /* mail_ping will callback here if read mail! */
02294    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02295    if (number == 0) return;
02296    set_update(stream);
02297 }
02298 
02299 
02300 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02301 {
02302    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02303    mm_log (string, errflg);
02304 }
02305 
02306 
02307 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02308 {
02309    if (delimiter == '\0') {
02310       delimiter = delim;
02311    }
02312 
02313    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02314    if (attributes & LATT_NOINFERIORS)
02315       ast_debug(5, "no inferiors\n");
02316    if (attributes & LATT_NOSELECT)
02317       ast_debug(5, "no select\n");
02318    if (attributes & LATT_MARKED)
02319       ast_debug(5, "marked\n");
02320    if (attributes & LATT_UNMARKED)
02321       ast_debug(5, "unmarked\n");
02322 }
02323 
02324 
02325 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02326 {
02327    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02328    if (attributes & LATT_NOINFERIORS)
02329       ast_debug(5, "no inferiors\n");
02330    if (attributes & LATT_NOSELECT)
02331       ast_debug(5, "no select\n");
02332    if (attributes & LATT_MARKED)
02333       ast_debug(5, "marked\n");
02334    if (attributes & LATT_UNMARKED)
02335       ast_debug(5, "unmarked\n");
02336 }
02337 
02338 
02339 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02340 {
02341    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02342    if (status->flags & SA_MESSAGES)
02343       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02344    if (status->flags & SA_RECENT)
02345       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02346    if (status->flags & SA_UNSEEN)
02347       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02348    if (status->flags & SA_UIDVALIDITY)
02349       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02350    if (status->flags & SA_UIDNEXT)
02351       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02352    ast_log(AST_LOG_NOTICE, "\n");
02353 }
02354 
02355 
02356 void mm_log(char *string, long errflg)
02357 {
02358    switch ((short) errflg) {
02359       case NIL:
02360          ast_debug(1,"IMAP Info: %s\n", string);
02361          break;
02362       case PARSE:
02363       case WARN:
02364          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02365          break;
02366       case ERROR:
02367          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02368          break;
02369    }
02370 }
02371 
02372 
02373 void mm_dlog(char *string)
02374 {
02375    ast_log(AST_LOG_NOTICE, "%s\n", string);
02376 }
02377 
02378 
02379 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02380 {
02381    struct ast_vm_user *vmu;
02382 
02383    ast_debug(4, "Entering callback mm_login\n");
02384 
02385    ast_copy_string(user, mb->user, MAILTMPLEN);
02386 
02387    /* We should only do this when necessary */
02388    if (!ast_strlen_zero(authpassword)) {
02389       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02390    } else {
02391       AST_LIST_TRAVERSE(&users, vmu, list) {
02392          if (!strcasecmp(mb->user, vmu->imapuser)) {
02393             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02394             break;
02395          }
02396       }
02397       if (!vmu) {
02398          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02399             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02400             free_user(vmu);
02401          }
02402       }
02403    }
02404 }
02405 
02406 
02407 void mm_critical(MAILSTREAM * stream)
02408 {
02409 }
02410 
02411 
02412 void mm_nocritical(MAILSTREAM * stream)
02413 {
02414 }
02415 
02416 
02417 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02418 {
02419    kill (getpid (), SIGSTOP);
02420    return NIL;
02421 }
02422 
02423 
02424 void mm_fatal(char *string)
02425 {
02426    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02427 }
02428 
02429 /* C-client callback to handle quota */
02430 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02431 {
02432    struct vm_state *vms;
02433    char *mailbox = stream->mailbox, *user;
02434    char buf[1024] = "";
02435    unsigned long usage = 0, limit = 0;
02436    
02437    while (pquota) {
02438       usage = pquota->usage;
02439       limit = pquota->limit;
02440       pquota = pquota->next;
02441    }
02442    
02443    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)))) {
02444       ast_log(AST_LOG_ERROR, "No state found.\n");
02445       return;
02446    }
02447 
02448    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02449 
02450    vms->quota_usage = usage;
02451    vms->quota_limit = limit;
02452 }
02453 
02454 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02455 {
02456    char *start, *eol_pnt;
02457    int taglen;
02458 
02459    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02460       return NULL;
02461 
02462    taglen = strlen(tag) + 1;
02463    if (taglen < 1)
02464       return NULL;
02465 
02466    if (!(start = strstr(header, tag)))
02467       return NULL;
02468 
02469    /* Since we can be called multiple times we should clear our buffer */
02470    memset(buf, 0, len);
02471 
02472    ast_copy_string(buf, start+taglen, len);
02473    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02474       *eol_pnt = '\0';
02475    return buf;
02476 }
02477 
02478 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02479 {
02480    char *start, *quote, *eol_pnt;
02481 
02482    if (ast_strlen_zero(mailbox))
02483       return NULL;
02484 
02485    if (!(start = strstr(mailbox, "/user=")))
02486       return NULL;
02487 
02488    ast_copy_string(buf, start+6, len);
02489 
02490    if (!(quote = strchr(buf, '\"'))) {
02491       if (!(eol_pnt = strchr(buf, '/')))
02492          eol_pnt = strchr(buf,'}');
02493       *eol_pnt = '\0';
02494       return buf;
02495    } else {
02496       eol_pnt = strchr(buf+1,'\"');
02497       *eol_pnt = '\0';
02498       return buf+1;
02499    }
02500 }
02501 
02502 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02503 {
02504    struct vm_state *vms_p;
02505 
02506    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02507    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02508       return vms_p;
02509    }
02510    if (option_debug > 4)
02511       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02512    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02513       return NULL;
02514    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02515    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02516    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02517    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02518    vms_p->imapversion = vmu->imapversion;
02519    if (option_debug > 4)
02520       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02521    vms_p->updated = 1;
02522    /* set mailbox to INBOX! */
02523    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02524    init_vm_state(vms_p);
02525    vmstate_insert(vms_p);
02526    return vms_p;
02527 }
02528 
02529 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02530 {
02531    struct vmstate *vlist = NULL;
02532 
02533    if (interactive) {
02534       struct vm_state *vms;
02535       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02536       vms = pthread_getspecific(ts_vmstate.key);
02537       return vms;
02538    }
02539 
02540    AST_LIST_LOCK(&vmstates);
02541    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02542       if (!vlist->vms) {
02543          ast_debug(3, "error: vms is NULL for %s\n", user);
02544          continue;
02545       }
02546       if (vlist->vms->imapversion != imapversion) {
02547          continue;
02548       }
02549       if (!vlist->vms->imapuser) {
02550          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02551          continue;
02552       }
02553 
02554       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02555          AST_LIST_UNLOCK(&vmstates);
02556          return vlist->vms;
02557       }
02558    }
02559    AST_LIST_UNLOCK(&vmstates);
02560 
02561    ast_debug(3, "%s not found in vmstates\n", user);
02562 
02563    return NULL;
02564 }
02565 
02566 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02567 {
02568 
02569    struct vmstate *vlist = NULL;
02570    const char *local_context = S_OR(context, "default");
02571 
02572    if (interactive) {
02573       struct vm_state *vms;
02574       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02575       vms = pthread_getspecific(ts_vmstate.key);
02576       return vms;
02577    }
02578 
02579    AST_LIST_LOCK(&vmstates);
02580    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02581       if (!vlist->vms) {
02582          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02583          continue;
02584       }
02585       if (vlist->vms->imapversion != imapversion) {
02586          continue;
02587       }
02588       if (!vlist->vms->username || !vlist->vms->context) {
02589          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02590          continue;
02591       }
02592 
02593       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);
02594       
02595       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02596          ast_debug(3, "Found it!\n");
02597          AST_LIST_UNLOCK(&vmstates);
02598          return vlist->vms;
02599       }
02600    }
02601    AST_LIST_UNLOCK(&vmstates);
02602 
02603    ast_debug(3, "%s not found in vmstates\n", mailbox);
02604 
02605    return NULL;
02606 }
02607 
02608 static void vmstate_insert(struct vm_state *vms) 
02609 {
02610    struct vmstate *v;
02611    struct vm_state *altvms;
02612 
02613    /* If interactive, it probably already exists, and we should
02614       use the one we already have since it is more up to date.
02615       We can compare the username to find the duplicate */
02616    if (vms->interactive == 1) {
02617       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02618       if (altvms) {  
02619          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02620          vms->newmessages = altvms->newmessages;
02621          vms->oldmessages = altvms->oldmessages;
02622          vms->vmArrayIndex = altvms->vmArrayIndex;
02623          vms->lastmsg = altvms->lastmsg;
02624          vms->curmsg = altvms->curmsg;
02625          /* get a pointer to the persistent store */
02626          vms->persist_vms = altvms;
02627          /* Reuse the mailstream? */
02628 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02629          vms->mailstream = altvms->mailstream;
02630 #else
02631          vms->mailstream = NIL;
02632 #endif
02633       }
02634       return;
02635    }
02636 
02637    if (!(v = ast_calloc(1, sizeof(*v))))
02638       return;
02639    
02640    v->vms = vms;
02641 
02642    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02643 
02644    AST_LIST_LOCK(&vmstates);
02645    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02646    AST_LIST_UNLOCK(&vmstates);
02647 }
02648 
02649 static void vmstate_delete(struct vm_state *vms) 
02650 {
02651    struct vmstate *vc = NULL;
02652    struct vm_state *altvms = NULL;
02653 
02654    /* If interactive, we should copy pertinent info
02655       back to the persistent state (to make update immediate) */
02656    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02657       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02658       altvms->newmessages = vms->newmessages;
02659       altvms->oldmessages = vms->oldmessages;
02660       altvms->updated = 1;
02661       vms->mailstream = mail_close(vms->mailstream);
02662 
02663       /* Interactive states are not stored within the persistent list */
02664       return;
02665    }
02666    
02667    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02668    
02669    AST_LIST_LOCK(&vmstates);
02670    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02671       if (vc->vms == vms) {
02672          AST_LIST_REMOVE_CURRENT(list);
02673          break;
02674       }
02675    }
02676    AST_LIST_TRAVERSE_SAFE_END
02677    AST_LIST_UNLOCK(&vmstates);
02678    
02679    if (vc) {
02680       ast_mutex_destroy(&vc->vms->lock);
02681       ast_free(vc);
02682    }
02683    else
02684       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02685 }
02686 
02687 static void set_update(MAILSTREAM * stream) 
02688 {
02689    struct vm_state *vms;
02690    char *mailbox = stream->mailbox, *user;
02691    char buf[1024] = "";
02692 
02693    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02694       if (user && option_debug > 2)
02695          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02696       return;
02697    }
02698 
02699    ast_debug(3, "User %s mailbox set for update.\n", user);
02700 
02701    vms->updated = 1; /* Set updated flag since mailbox changed */
02702 }
02703 
02704 static void init_vm_state(struct vm_state *vms) 
02705 {
02706    int x;
02707    vms->vmArrayIndex = 0;
02708    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02709       vms->msgArray[x] = 0;
02710    }
02711    ast_mutex_init(&vms->lock);
02712 }
02713 
02714 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02715 {
02716    char *body_content;
02717    char *body_decoded;
02718    char *fn = is_intro ? vms->introfn : vms->fn;
02719    unsigned long len;
02720    unsigned long newlen;
02721    char filename[256];
02722    
02723    if (!body || body == NIL)
02724       return -1;
02725 
02726    ast_mutex_lock(&vms->lock);
02727    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02728    ast_mutex_unlock(&vms->lock);
02729    if (body_content != NIL) {
02730       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02731       /* ast_debug(1,body_content); */
02732       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02733       /* If the body of the file is empty, return an error */
02734       if (!newlen) {
02735          return -1;
02736       }
02737       write_file(filename, (char *) body_decoded, newlen);
02738    } else {
02739       ast_debug(5, "Body of message is NULL.\n");
02740       return -1;
02741    }
02742    return 0;
02743 }
02744 
02745 /*! 
02746  * \brief Get delimiter via mm_list callback 
02747  * \param stream
02748  *
02749  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02750  */
02751 /* MUTEX should already be held */
02752 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02753    char tmp[50];
02754    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02755    mail_list(stream, tmp, "*");
02756 }
02757 
02758 /*! 
02759  * \brief Check Quota for user 
02760  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02761  * \param mailbox the mailbox to check the quota for.
02762  *
02763  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02764  */
02765 static void check_quota(struct vm_state *vms, char *mailbox) {
02766    ast_mutex_lock(&vms->lock);
02767    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02768    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02769    if (vms && vms->mailstream != NULL) {
02770       imap_getquotaroot(vms->mailstream, mailbox);
02771    } else {
02772       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02773    }
02774    ast_mutex_unlock(&vms->lock);
02775 }
02776 
02777 #endif /* IMAP_STORAGE */
02778 
02779 /*! \brief Lock file path
02780     only return failure if ast_lock_path returns 'timeout',
02781    not if the path does not exist or any other reason
02782 */
02783 static int vm_lock_path(const char *path)
02784 {
02785    switch (ast_lock_path(path)) {
02786    case AST_LOCK_TIMEOUT:
02787       return -1;
02788    default:
02789       return 0;
02790    }
02791 }
02792 
02793 
02794 #ifdef ODBC_STORAGE
02795 struct generic_prepare_struct {
02796    char *sql;
02797    int argc;
02798    char **argv;
02799 };
02800 
02801 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02802 {
02803    struct generic_prepare_struct *gps = data;
02804    int res, i;
02805    SQLHSTMT stmt;
02806 
02807    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02808    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02809       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
02810       return NULL;
02811    }
02812    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02813    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02814       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02815       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02816       return NULL;
02817    }
02818    for (i = 0; i < gps->argc; i++)
02819       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02820 
02821    return stmt;
02822 }
02823 
02824 /*!
02825  * \brief Retrieves a file from an ODBC data store.
02826  * \param dir the path to the file to be retreived.
02827  * \param msgnum the message number, such as within a mailbox folder.
02828  * 
02829  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
02830  * 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.
02831  *
02832  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
02833  * The output is the message information file with the name msgnum and the extension .txt
02834  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
02835  * 
02836  * \return 0 on success, -1 on error.
02837  */
02838 static int retrieve_file(char *dir, int msgnum)
02839 {
02840    int x = 0;
02841    int res;
02842    int fd=-1;
02843    size_t fdlen = 0;
02844    void *fdm = MAP_FAILED;
02845    SQLSMALLINT colcount=0;
02846    SQLHSTMT stmt;
02847    char sql[PATH_MAX];
02848    char fmt[80]="";
02849    char *c;
02850    char coltitle[256];
02851    SQLSMALLINT collen;
02852    SQLSMALLINT datatype;
02853    SQLSMALLINT decimaldigits;
02854    SQLSMALLINT nullable;
02855    SQLULEN colsize;
02856    SQLLEN colsize2;
02857    FILE *f=NULL;
02858    char rowdata[80];
02859    char fn[PATH_MAX];
02860    char full_fn[PATH_MAX];
02861    char msgnums[80];
02862    char *argv[] = { dir, msgnums };
02863    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02864 
02865    struct odbc_obj *obj;
02866    obj = ast_odbc_request_obj(odbc_database, 0);
02867    if (obj) {
02868       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02869       c = strchr(fmt, '|');
02870       if (c)
02871          *c = '\0';
02872       if (!strcasecmp(fmt, "wav49"))
02873          strcpy(fmt, "WAV");
02874       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02875       if (msgnum > -1)
02876          make_file(fn, sizeof(fn), dir, msgnum);
02877       else
02878          ast_copy_string(fn, dir, sizeof(fn));
02879 
02880       /* Create the information file */
02881       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02882       
02883       if (!(f = fopen(full_fn, "w+"))) {
02884          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02885          goto yuck;
02886       }
02887       
02888       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02889       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02890       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02891       if (!stmt) {
02892          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02893          ast_odbc_release_obj(obj);
02894          goto yuck;
02895       }
02896       res = SQLFetch(stmt);
02897       if (res == SQL_NO_DATA) {
02898          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02899          ast_odbc_release_obj(obj);
02900          goto yuck;
02901       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02902          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02903          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02904          ast_odbc_release_obj(obj);
02905          goto yuck;
02906       }
02907       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
02908       if (fd < 0) {
02909          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02910          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02911          ast_odbc_release_obj(obj);
02912          goto yuck;
02913       }
02914       res = SQLNumResultCols(stmt, &colcount);
02915       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02916          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02917          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02918          ast_odbc_release_obj(obj);
02919          goto yuck;
02920       }
02921       if (f) 
02922          fprintf(f, "[message]\n");
02923       for (x=0;x<colcount;x++) {
02924          rowdata[0] = '\0';
02925          collen = sizeof(coltitle);
02926          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02927                   &datatype, &colsize, &decimaldigits, &nullable);
02928          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02929             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02930             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02931             ast_odbc_release_obj(obj);
02932             goto yuck;
02933          }
02934          if (!strcasecmp(coltitle, "recording")) {
02935             off_t offset;
02936             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02937             fdlen = colsize2;
02938             if (fd > -1) {
02939                char tmp[1]="";
02940                lseek(fd, fdlen - 1, SEEK_SET);
02941                if (write(fd, tmp, 1) != 1) {
02942                   close(fd);
02943                   fd = -1;
02944                   continue;
02945                }
02946                /* Read out in small chunks */
02947                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
02948                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
02949                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
02950                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02951                      ast_odbc_release_obj(obj);
02952                      goto yuck;
02953                   } else {
02954                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
02955                      munmap(fdm, CHUNKSIZE);
02956                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02957                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02958                         unlink(full_fn);
02959                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02960                         ast_odbc_release_obj(obj);
02961                         goto yuck;
02962                      }
02963                   }
02964                }
02965                if (truncate(full_fn, fdlen) < 0) {
02966                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
02967                }
02968             }
02969          } else {
02970             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02971             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02972                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
02973                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02974                ast_odbc_release_obj(obj);
02975                goto yuck;
02976             }
02977             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
02978                fprintf(f, "%s=%s\n", coltitle, rowdata);
02979          }
02980       }
02981       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02982       ast_odbc_release_obj(obj);
02983    } else
02984       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02985 yuck: 
02986    if (f)
02987       fclose(f);
02988    if (fd > -1)
02989       close(fd);
02990    return x - 1;
02991 }
02992 
02993 /*!
02994  * \brief Determines the highest message number in use for a given user and mailbox folder.
02995  * \param vmu 
02996  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
02997  *
02998  * This method is used when mailboxes are stored in an ODBC back end.
02999  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03000  *
03001  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03002  */
03003 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03004 {
03005    int x = 0;
03006    int res;
03007    SQLHSTMT stmt;
03008    char sql[PATH_MAX];
03009    char rowdata[20];
03010    char *argv[] = { dir };
03011    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03012 
03013    struct odbc_obj *obj;
03014    obj = ast_odbc_request_obj(odbc_database, 0);
03015    if (obj) {
03016       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
03017       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03018       if (!stmt) {
03019          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03020          ast_odbc_release_obj(obj);
03021          goto yuck;
03022       }
03023       res = SQLFetch(stmt);
03024       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03025          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03026          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03027          ast_odbc_release_obj(obj);
03028          goto yuck;
03029       }
03030       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03031       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03032          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03033          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03034          ast_odbc_release_obj(obj);
03035          goto yuck;
03036       }
03037       if (sscanf(rowdata, "%30d", &x) != 1)
03038          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03039       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03040       ast_odbc_release_obj(obj);
03041    } else
03042       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03043 yuck: 
03044    return x - 1;
03045 }
03046 
03047 /*!
03048  * \brief Determines if the specified message exists.
03049  * \param dir the folder the mailbox folder to look for messages. 
03050  * \param msgnum the message index to query for.
03051  *
03052  * This method is used when mailboxes are stored in an ODBC back end.
03053  *
03054  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03055  */
03056 static int message_exists(char *dir, int msgnum)
03057 {
03058    int x = 0;
03059    int res;
03060    SQLHSTMT stmt;
03061    char sql[PATH_MAX];
03062    char rowdata[20];
03063    char msgnums[20];
03064    char *argv[] = { dir, msgnums };
03065    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03066 
03067    struct odbc_obj *obj;
03068    obj = ast_odbc_request_obj(odbc_database, 0);
03069    if (obj) {
03070       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03071       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03072       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03073       if (!stmt) {
03074          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03075          ast_odbc_release_obj(obj);
03076          goto yuck;
03077       }
03078       res = SQLFetch(stmt);
03079       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03080          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03081          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03082          ast_odbc_release_obj(obj);
03083          goto yuck;
03084       }
03085       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03086       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03087          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03088          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03089          ast_odbc_release_obj(obj);
03090          goto yuck;
03091       }
03092       if (sscanf(rowdata, "%30d", &x) != 1)
03093          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03094       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03095       ast_odbc_release_obj(obj);
03096    } else
03097       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03098 yuck: 
03099    return x;
03100 }
03101 
03102 /*!
03103  * \brief returns the one-based count for messages.
03104  * \param vmu
03105  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03106  *
03107  * This method is used when mailboxes are stored in an ODBC back end.
03108  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03109  * one-based messages.
03110  * This method just calls last_message_index and returns +1 of its value.
03111  *
03112  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03113  */
03114 static int count_messages(struct ast_vm_user *vmu, char *dir)
03115 {
03116    return last_message_index(vmu, dir) + 1;
03117 }
03118 
03119 /*!
03120  * \brief Deletes a message from the mailbox folder.
03121  * \param sdir The mailbox folder to work in.
03122  * \param smsg The message index to be deleted.
03123  *
03124  * This method is used when mailboxes are stored in an ODBC back end.
03125  * The specified message is directly deleted from the database 'voicemessages' table.
03126  * 
03127  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03128  */
03129 static void delete_file(char *sdir, int smsg)
03130 {
03131    SQLHSTMT stmt;
03132    char sql[PATH_MAX];
03133    char msgnums[20];
03134    char *argv[] = { sdir, msgnums };
03135    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03136 
03137    struct odbc_obj *obj;
03138    obj = ast_odbc_request_obj(odbc_database, 0);
03139    if (obj) {
03140       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03141       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03142       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03143       if (!stmt)
03144          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03145       else
03146          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03147       ast_odbc_release_obj(obj);
03148    } else
03149       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03150    return;  
03151 }
03152 
03153 /*!
03154  * \brief Copies a voicemail from one mailbox to another.
03155  * \param sdir the folder for which to look for the message to be copied.
03156  * \param smsg the index of the message to be copied.
03157  * \param ddir the destination folder to copy the message into.
03158  * \param dmsg the index to be used for the copied message.
03159  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03160  * \param dmailboxcontext The context for the destination user.
03161  *
03162  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03163  */
03164 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03165 {
03166    SQLHSTMT stmt;
03167    char sql[512];
03168    char msgnums[20];
03169    char msgnumd[20];
03170    struct odbc_obj *obj;
03171    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03172    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03173 
03174    delete_file(ddir, dmsg);
03175    obj = ast_odbc_request_obj(odbc_database, 0);
03176    if (obj) {
03177       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03178       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03179       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
03180       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03181       if (!stmt)
03182          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03183       else
03184          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03185       ast_odbc_release_obj(obj);
03186    } else
03187       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03188    return;  
03189 }
03190 
03191 struct insert_data {
03192    char *sql;
03193    char *dir;
03194    char *msgnums;
03195    void *data;
03196    SQLLEN datalen;
03197    SQLLEN indlen;
03198    const char *context;
03199    const char *macrocontext;
03200    const char *callerid;
03201    const char *origtime;
03202    const char *duration;
03203    char *mailboxuser;
03204    char *mailboxcontext;
03205    const char *category;
03206    const char *flag;
03207 };
03208 
03209 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03210 {
03211    struct insert_data *data = vdata;
03212    int res;
03213    SQLHSTMT stmt;
03214 
03215    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03216    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03217       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03218       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03219       return NULL;
03220    }
03221 
03222    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03223    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03224    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03225    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03226    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03227    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03228    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03229    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03230    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03231    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03232    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03233    if (!ast_strlen_zero(data->category)) {
03234       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03235    }
03236    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03237    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03238       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03239       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03240       return NULL;
03241    }
03242 
03243    return stmt;
03244 }
03245 
03246 /*!
03247  * \brief Stores a voicemail into the database.
03248  * \param dir the folder the mailbox folder to store the message.
03249  * \param mailboxuser the user owning the mailbox folder.
03250  * \param mailboxcontext
03251  * \param msgnum the message index for the message to be stored.
03252  *
03253  * This method is used when mailboxes are stored in an ODBC back end.
03254  * The message sound file and information file is looked up on the file system. 
03255  * A SQL query is invoked to store the message into the (MySQL) database.
03256  *
03257  * \return the zero on success -1 on error.
03258  */
03259 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
03260 {
03261    int res = 0;
03262    int fd = -1;
03263    void *fdm = MAP_FAILED;
03264    size_t fdlen = -1;
03265    SQLHSTMT stmt;
03266    char sql[PATH_MAX];
03267    char msgnums[20];
03268    char fn[PATH_MAX];
03269    char full_fn[PATH_MAX];
03270    char fmt[80]="";
03271    char *c;
03272    struct ast_config *cfg=NULL;
03273    struct odbc_obj *obj;
03274    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03275       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03276    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03277 
03278    delete_file(dir, msgnum);
03279    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03280       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03281       return -1;
03282    }
03283 
03284    do {
03285       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03286       c = strchr(fmt, '|');
03287       if (c)
03288          *c = '\0';
03289       if (!strcasecmp(fmt, "wav49"))
03290          strcpy(fmt, "WAV");
03291       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03292       if (msgnum > -1)
03293          make_file(fn, sizeof(fn), dir, msgnum);
03294       else
03295          ast_copy_string(fn, dir, sizeof(fn));
03296       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03297       cfg = ast_config_load(full_fn, config_flags);
03298       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03299       fd = open(full_fn, O_RDWR);
03300       if (fd < 0) {
03301          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03302          res = -1;
03303          break;
03304       }
03305       if (cfg) {
03306          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03307             idata.context = "";
03308          }
03309          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03310             idata.macrocontext = "";
03311          }
03312          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03313             idata.callerid = "";
03314          }
03315          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03316             idata.origtime = "";
03317          }
03318          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03319             idata.duration = "";
03320          }
03321          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03322             idata.category = "";
03323          }
03324          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03325             idata.flag = "";
03326          }
03327       }
03328       fdlen = lseek(fd, 0, SEEK_END);
03329       lseek(fd, 0, SEEK_SET);
03330       printf("Length is %zd\n", fdlen);
03331       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03332       if (fdm == MAP_FAILED) {
03333          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03334          res = -1;
03335          break;
03336       } 
03337       idata.data = fdm;
03338       idata.datalen = idata.indlen = fdlen;
03339 
03340       if (!ast_strlen_zero(idata.category)) 
03341          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03342       else
03343          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03344 
03345       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03346          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03347       } else {
03348          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03349          res = -1;
03350       }
03351    } while (0);
03352    if (obj) {
03353       ast_odbc_release_obj(obj);
03354    }
03355    if (cfg)
03356       ast_config_destroy(cfg);
03357    if (fdm != MAP_FAILED)
03358       munmap(fdm, fdlen);
03359    if (fd > -1)
03360       close(fd);
03361    return res;
03362 }
03363 
03364 /*!
03365  * \brief Renames a message in a mailbox folder.
03366  * \param sdir The folder of the message to be renamed.
03367  * \param smsg The index of the message to be renamed.
03368  * \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.
03369  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03370  * \param ddir The destination folder for the message to be renamed into
03371  * \param dmsg The destination message for the message to be renamed.
03372  *
03373  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03374  * 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.
03375  * 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.
03376  */
03377 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03378 {
03379    SQLHSTMT stmt;
03380    char sql[PATH_MAX];
03381    char msgnums[20];
03382    char msgnumd[20];
03383    struct odbc_obj *obj;
03384    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03385    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03386 
03387    delete_file(ddir, dmsg);
03388    obj = ast_odbc_request_obj(odbc_database, 0);
03389    if (obj) {
03390       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03391       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03392       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03393       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03394       if (!stmt)
03395          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03396       else
03397          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03398       ast_odbc_release_obj(obj);
03399    } else
03400       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03401    return;  
03402 }
03403 
03404 /*!
03405  * \brief Removes a voicemail message file.
03406  * \param dir the path to the message file.
03407  * \param msgnum the unique number for the message within the mailbox.
03408  *
03409  * Removes the message content file and the information file.
03410  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03411  * Typical use is to clean up after a RETRIEVE operation. 
03412  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03413  * \return zero on success, -1 on error.
03414  */
03415 static int remove_file(char *dir, int msgnum)
03416 {
03417    char fn[PATH_MAX];
03418    char full_fn[PATH_MAX];
03419    char msgnums[80];
03420    
03421    if (msgnum > -1) {
03422       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03423       make_file(fn, sizeof(fn), dir, msgnum);
03424    } else
03425       ast_copy_string(fn, dir, sizeof(fn));
03426    ast_filedelete(fn, NULL);  
03427    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03428    unlink(full_fn);
03429    return 0;
03430 }
03431 #else
03432 #ifndef IMAP_STORAGE
03433 /*!
03434  * \brief Find all .txt files - even if they are not in sequence from 0000.
03435  * \param vmu
03436  * \param dir
03437  *
03438  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03439  *
03440  * \return the count of messages, zero or more.
03441  */
03442 static int count_messages(struct ast_vm_user *vmu, char *dir)
03443 {
03444 
03445    int vmcount = 0;
03446    DIR *vmdir = NULL;
03447    struct dirent *vment = NULL;
03448 
03449    if (vm_lock_path(dir))
03450       return ERROR_LOCK_PATH;
03451 
03452    if ((vmdir = opendir(dir))) {
03453       while ((vment = readdir(vmdir))) {
03454          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03455             vmcount++;
03456          }
03457       }
03458       closedir(vmdir);
03459    }
03460    ast_unlock_path(dir);
03461    
03462    return vmcount;
03463 }
03464 
03465 /*!
03466  * \brief Renames a message in a mailbox folder.
03467  * \param sfn The path to the mailbox information and data file to be renamed.
03468  * \param dfn The path for where the message data and information files will be renamed to.
03469  *
03470  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03471  */
03472 static void rename_file(char *sfn, char *dfn)
03473 {
03474    char stxt[PATH_MAX];
03475    char dtxt[PATH_MAX];
03476    ast_filerename(sfn,dfn,NULL);
03477    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03478    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03479    if (ast_check_realtime("voicemail_data")) {
03480       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03481    }
03482    rename(stxt, dtxt);
03483 }
03484 
03485 /*! 
03486  * \brief Determines the highest message number in use for a given user and mailbox folder.
03487  * \param vmu 
03488  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03489  *
03490  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03491  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03492  *
03493  * \note Should always be called with a lock already set on dir.
03494  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03495  */
03496 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03497 {
03498    int x;
03499    unsigned char map[MAXMSGLIMIT] = "";
03500    DIR *msgdir;
03501    struct dirent *msgdirent;
03502    int msgdirint;
03503 
03504    /* Reading the entire directory into a file map scales better than
03505     * doing a stat repeatedly on a predicted sequence.  I suspect this
03506     * is partially due to stat(2) internally doing a readdir(2) itself to
03507     * find each file. */
03508    if (!(msgdir = opendir(dir))) {
03509       return -1;
03510    }
03511 
03512    while ((msgdirent = readdir(msgdir))) {
03513       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03514          map[msgdirint] = 1;
03515    }
03516    closedir(msgdir);
03517 
03518    for (x = 0; x < vmu->maxmsg; x++) {
03519       if (map[x] == 0)
03520          break;
03521    }
03522 
03523    return x - 1;
03524 }
03525 
03526 #endif /* #ifndef IMAP_STORAGE */
03527 #endif /* #else of #ifdef ODBC_STORAGE */
03528 #ifndef IMAP_STORAGE
03529 /*!
03530  * \brief Utility function to copy a file.
03531  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03532  * \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.
03533  *
03534  * 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.
03535  * The copy operation copies up to 4096 bytes at once.
03536  *
03537  * \return zero on success, -1 on error.
03538  */
03539 static int copy(char *infile, char *outfile)
03540 {
03541    int ifd;
03542    int ofd;
03543    int res;
03544    int len;
03545    char buf[4096];
03546 
03547 #ifdef HARDLINK_WHEN_POSSIBLE
03548    /* Hard link if possible; saves disk space & is faster */
03549    if (link(infile, outfile)) {
03550 #endif
03551       if ((ifd = open(infile, O_RDONLY)) < 0) {
03552          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03553          return -1;
03554       }
03555       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03556          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03557          close(ifd);
03558          return -1;
03559       }
03560       do {
03561          len = read(ifd, buf, sizeof(buf));
03562          if (len < 0) {
03563             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03564             close(ifd);
03565             close(ofd);
03566             unlink(outfile);
03567          }
03568          if (len) {
03569             res = write(ofd, buf, len);
03570             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03571                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03572                close(ifd);
03573                close(ofd);
03574                unlink(outfile);
03575             }
03576          }
03577       } while (len);
03578       close(ifd);
03579       close(ofd);
03580       return 0;
03581 #ifdef HARDLINK_WHEN_POSSIBLE
03582    } else {
03583       /* Hard link succeeded */
03584       return 0;
03585    }
03586 #endif
03587 }
03588 
03589 /*!
03590  * \brief Copies a voicemail information (envelope) file.
03591  * \param frompath
03592  * \param topath 
03593  *
03594  * Every voicemail has the data (.wav) file, and the information file.
03595  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03596  * This is used by the COPY macro when not using IMAP storage.
03597  */
03598 static void copy_plain_file(char *frompath, char *topath)
03599 {
03600    char frompath2[PATH_MAX], topath2[PATH_MAX];
03601    struct ast_variable *tmp,*var = NULL;
03602    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03603    ast_filecopy(frompath, topath, NULL);
03604    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03605    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03606    if (ast_check_realtime("voicemail_data")) {
03607       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03608       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03609       for (tmp = var; tmp; tmp = tmp->next) {
03610          if (!strcasecmp(tmp->name, "origmailbox")) {
03611             origmailbox = tmp->value;
03612          } else if (!strcasecmp(tmp->name, "context")) {
03613             context = tmp->value;
03614          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03615             macrocontext = tmp->value;
03616          } else if (!strcasecmp(tmp->name, "exten")) {
03617             exten = tmp->value;
03618          } else if (!strcasecmp(tmp->name, "priority")) {
03619             priority = tmp->value;
03620          } else if (!strcasecmp(tmp->name, "callerchan")) {
03621             callerchan = tmp->value;
03622          } else if (!strcasecmp(tmp->name, "callerid")) {
03623             callerid = tmp->value;
03624          } else if (!strcasecmp(tmp->name, "origdate")) {
03625             origdate = tmp->value;
03626          } else if (!strcasecmp(tmp->name, "origtime")) {
03627             origtime = tmp->value;
03628          } else if (!strcasecmp(tmp->name, "category")) {
03629             category = tmp->value;
03630          } else if (!strcasecmp(tmp->name, "duration")) {
03631             duration = tmp->value;
03632          }
03633       }
03634       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);
03635    }
03636    copy(frompath2, topath2);
03637    ast_variables_destroy(var);
03638 }
03639 #endif
03640 
03641 /*! 
03642  * \brief Removes the voicemail sound and information file.
03643  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03644  *
03645  * This is used by the DELETE macro when voicemails are stored on the file system.
03646  *
03647  * \return zero on success, -1 on error.
03648  */
03649 static int vm_delete(char *file)
03650 {
03651    char *txt;
03652    int txtsize = 0;
03653 
03654    txtsize = (strlen(file) + 5)*sizeof(char);
03655    txt = alloca(txtsize);
03656    /* Sprintf here would safe because we alloca'd exactly the right length,
03657     * but trying to eliminate all sprintf's anyhow
03658     */
03659    if (ast_check_realtime("voicemail_data")) {
03660       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03661    }
03662    snprintf(txt, txtsize, "%s.txt", file);
03663    unlink(txt);
03664    return ast_filedelete(file, NULL);
03665 }
03666 
03667 /*!
03668  * \brief utility used by inchar(), for base_encode()
03669  */
03670 static int inbuf(struct baseio *bio, FILE *fi)
03671 {
03672    int l;
03673 
03674    if (bio->ateof)
03675       return 0;
03676 
03677    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03678       if (ferror(fi))
03679          return -1;
03680 
03681       bio->ateof = 1;
03682       return 0;
03683    }
03684 
03685    bio->iolen= l;
03686    bio->iocp= 0;
03687 
03688    return 1;
03689 }
03690 
03691 /*!
03692  * \brief utility used by base_encode()
03693  */
03694 static int inchar(struct baseio *bio, FILE *fi)
03695 {
03696    if (bio->iocp>=bio->iolen) {
03697       if (!inbuf(bio, fi))
03698          return EOF;
03699    }
03700 
03701    return bio->iobuf[bio->iocp++];
03702 }
03703 
03704 /*!
03705  * \brief utility used by base_encode()
03706  */
03707 static int ochar(struct baseio *bio, int c, FILE *so)
03708 {
03709    if (bio->linelength >= BASELINELEN) {
03710       if (fputs(eol,so) == EOF)
03711          return -1;
03712 
03713       bio->linelength= 0;
03714    }
03715 
03716    if (putc(((unsigned char)c),so) == EOF)
03717       return -1;
03718 
03719    bio->linelength++;
03720 
03721    return 1;
03722 }
03723 
03724 /*!
03725  * \brief Performs a base 64 encode algorithm on the contents of a File
03726  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03727  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03728  *
03729  * 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 ?
03730  *
03731  * \return zero on success, -1 on error.
03732  */
03733 static int base_encode(char *filename, FILE *so)
03734 {
03735    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03736       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03737       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03738       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03739    int i,hiteof= 0;
03740    FILE *fi;
03741    struct baseio bio;
03742 
03743    memset(&bio, 0, sizeof(bio));
03744    bio.iocp = BASEMAXINLINE;
03745 
03746    if (!(fi = fopen(filename, "rb"))) {
03747       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03748       return -1;
03749    }
03750 
03751    while (!hiteof){
03752       unsigned char igroup[3], ogroup[4];
03753       int c,n;
03754 
03755       igroup[0]= igroup[1]= igroup[2]= 0;
03756 
03757       for (n= 0;n<3;n++) {
03758          if ((c = inchar(&bio, fi)) == EOF) {
03759             hiteof= 1;
03760             break;
03761          }
03762 
03763          igroup[n]= (unsigned char)c;
03764       }
03765 
03766       if (n> 0) {
03767          ogroup[0]= dtable[igroup[0]>>2];
03768          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03769          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03770          ogroup[3]= dtable[igroup[2]&0x3F];
03771 
03772          if (n<3) {
03773             ogroup[3]= '=';
03774 
03775             if (n<2)
03776                ogroup[2]= '=';
03777          }
03778 
03779          for (i= 0;i<4;i++)
03780             ochar(&bio, ogroup[i], so);
03781       }
03782    }
03783 
03784    fclose(fi);
03785    
03786    if (fputs(eol,so)==EOF)
03787       return 0;
03788 
03789    return 1;
03790 }
03791 
03792 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
03793 {
03794    char callerid[256];
03795    char fromdir[256], fromfile[256];
03796    struct ast_config *msg_cfg;
03797    const char *origcallerid, *origtime;
03798    char origcidname[80], origcidnum[80], origdate[80];
03799    int inttime;
03800    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03801 
03802    /* Prepare variables for substitution in email body and subject */
03803    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03804    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03805    snprintf(passdata, passdatasize, "%d", msgnum);
03806    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03807    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03808    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03809    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03810       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03811    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03812    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03813    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03814    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03815    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
03816 
03817    /* Retrieve info from VM attribute file */
03818    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03819    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03820    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03821       strcat(fromfile, ".txt");
03822    }
03823    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
03824       if (option_debug > 0) {
03825          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03826       }
03827       return;
03828    }
03829 
03830    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03831       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03832       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03833       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03834       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03835    }
03836 
03837    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
03838       struct timeval tv = { inttime, };
03839       struct ast_tm tm;
03840       ast_localtime(&tv, &tm, NULL);
03841       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03842       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03843    }
03844    ast_config_destroy(msg_cfg);
03845 }
03846 
03847 /*!
03848  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
03849  * \param from The string to work with.
03850  * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
03851  * 
03852  * \return The destination string with quotes wrapped on it (the to field).
03853  */
03854 static char *quote(const char *from, char *to, size_t len)
03855 {
03856    char *ptr = to;
03857    *ptr++ = '"';
03858    for (; ptr < to + len - 1; from++) {
03859       if (*from == '"')
03860          *ptr++ = '\\';
03861       else if (*from == '\0')
03862          break;
03863       *ptr++ = *from;
03864    }
03865    if (ptr < to + len - 1)
03866       *ptr++ = '"';
03867    *ptr = '\0';
03868    return to;
03869 }
03870 
03871 /*! \brief
03872  * fill in *tm for current time according to the proper timezone, if any.
03873  * Return tm so it can be used as a function argument.
03874  */
03875 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
03876 {
03877    const struct vm_zone *z = NULL;
03878    struct timeval t = ast_tvnow();
03879 
03880    /* Does this user have a timezone specified? */
03881    if (!ast_strlen_zero(vmu->zonetag)) {
03882       /* Find the zone in the list */
03883       AST_LIST_LOCK(&zones);
03884       AST_LIST_TRAVERSE(&zones, z, list) {
03885          if (!strcmp(z->name, vmu->zonetag))
03886             break;
03887       }
03888       AST_LIST_UNLOCK(&zones);
03889    }
03890    ast_localtime(&t, tm, z ? z->timezone : NULL);
03891    return tm;
03892 }
03893 
03894 /*!\brief Check if the string would need encoding within the MIME standard, to
03895  * avoid confusing certain mail software that expects messages to be 7-bit
03896  * clean.
03897  */
03898 static int check_mime(const char *str)
03899 {
03900    for (; *str; str++) {
03901       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03902          return 1;
03903       }
03904    }
03905    return 0;
03906 }
03907 
03908 /*!\brief Encode a string according to the MIME rules for encoding strings
03909  * that are not 7-bit clean or contain control characters.
03910  *
03911  * Additionally, if the encoded string would exceed the MIME limit of 76
03912  * characters per line, then the encoding will be broken up into multiple
03913  * sections, separated by a space character, in order to facilitate
03914  * breaking up the associated header across multiple lines.
03915  *
03916  * \param start A string to be encoded
03917  * \param end An expandable buffer for holding the result
03918  * \param preamble The length of the first line already used for this string,
03919  * to ensure that each line maintains a maximum length of 76 chars.
03920  * \param postamble the length of any additional characters appended to the
03921  * line, used to ensure proper field wrapping.
03922  * \retval The encoded string.
03923  */
03924 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03925 {
03926    char tmp[80];
03927    int first_section = 1;
03928    size_t endlen = 0, tmplen = 0;
03929    *end = '\0';
03930 
03931    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03932    for (; *start; start++) {
03933       int need_encoding = 0;
03934       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03935          need_encoding = 1;
03936       }
03937       if ((first_section && need_encoding && preamble + tmplen > 70) ||
03938          (first_section && !need_encoding && preamble + tmplen > 72) ||
03939          (!first_section && need_encoding && tmplen > 70) ||
03940          (!first_section && !need_encoding && tmplen > 72)) {
03941          /* Start new line */
03942          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
03943          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03944          first_section = 0;
03945       }
03946       if (need_encoding && *start == ' ') {
03947          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
03948       } else if (need_encoding) {
03949          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
03950       } else {
03951          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
03952       }
03953    }
03954    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
03955    return end;
03956 }
03957 
03958 /*!
03959  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
03960  * \param p The output file to generate the email contents into.
03961  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
03962  * \param vmu The voicemail user who is sending the voicemail.
03963  * \param msgnum The message index in the mailbox folder.
03964  * \param context 
03965  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
03966  * \param cidnum The caller ID number.
03967  * \param cidname The caller ID name.
03968  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
03969  * \param format The message sound file format. i.e. .wav
03970  * \param duration The time of the message content, in seconds.
03971  * \param attach_user_voicemail if 1, the sound file is attached to the email.
03972  * \param chan
03973  * \param category
03974  * \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.
03975  *
03976  * 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.
03977  */
03978 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)
03979 {
03980    char date[256];
03981    char host[MAXHOSTNAMELEN] = "";
03982    char who[256];
03983    char bound[256];
03984    char dur[256];
03985    struct ast_tm tm;
03986    char enc_cidnum[256] = "", enc_cidname[256] = "";
03987    char *passdata = NULL, *passdata2;
03988    size_t len_passdata = 0, len_passdata2, tmplen;
03989    char *greeting_attachment; 
03990    char filename[256];
03991 
03992 #ifdef IMAP_STORAGE
03993 #define ENDL "\r\n"
03994 #else
03995 #define ENDL "\n"
03996 #endif
03997 
03998    /* One alloca for multiple fields */
03999    len_passdata2 = strlen(vmu->fullname);
04000    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04001       len_passdata2 = tmplen;
04002    }
04003    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04004       len_passdata2 = tmplen;
04005    }
04006    len_passdata2 = len_passdata2 * 3 + 200;
04007    passdata2 = alloca(len_passdata2);
04008 
04009    if (cidnum) {
04010       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04011    }
04012    if (cidname) {
04013       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04014    }
04015    gethostname(host, sizeof(host) - 1);
04016 
04017    if (strchr(srcemail, '@'))
04018       ast_copy_string(who, srcemail, sizeof(who));
04019    else 
04020       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04021    
04022    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04023    if (greeting_attachment)
04024       *greeting_attachment++ = '\0';
04025 
04026    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04027    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04028    fprintf(p, "Date: %s" ENDL, date);
04029 
04030    /* Set date format for voicemail mail */
04031    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04032 
04033    if (!ast_strlen_zero(fromstring)) {
04034       struct ast_channel *ast;
04035       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04036          char *ptr;
04037          memset(passdata2, 0, len_passdata2);
04038          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04039          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04040          len_passdata = strlen(passdata2) * 3 + 300;
04041          passdata = alloca(len_passdata);
04042          if (check_mime(passdata2)) {
04043             int first_line = 1;
04044             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04045             while ((ptr = strchr(passdata, ' '))) {
04046                *ptr = '\0';
04047                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04048                first_line = 0;
04049                passdata = ptr + 1;
04050             }
04051             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04052          } else {
04053             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04054          }
04055          ast_channel_free(ast);
04056       } else {
04057          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04058       }
04059    } else {
04060       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04061    }
04062 
04063    if (check_mime(vmu->fullname)) {
04064       int first_line = 1;
04065       char *ptr;
04066       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04067       while ((ptr = strchr(passdata2, ' '))) {
04068          *ptr = '\0';
04069          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04070          first_line = 0;
04071          passdata2 = ptr + 1;
04072       }
04073       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04074    } else {
04075       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04076    }
04077    if (!ast_strlen_zero(emailsubject)) {
04078       struct ast_channel *ast;
04079       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04080          int vmlen = strlen(emailsubject) * 3 + 200;
04081          /* Only allocate more space if the previous was not large enough */
04082          if (vmlen > len_passdata) {
04083             passdata = alloca(vmlen);
04084             len_passdata = vmlen;
04085          }
04086 
04087          memset(passdata, 0, len_passdata);
04088          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04089          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
04090          if (check_mime(passdata)) {
04091             int first_line = 1;
04092             char *ptr;
04093             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04094             while ((ptr = strchr(passdata2, ' '))) {
04095                *ptr = '\0';
04096                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04097                first_line = 0;
04098                passdata2 = ptr + 1;
04099             }
04100             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04101          } else {
04102             fprintf(p, "Subject: %s" ENDL, passdata);
04103          }
04104          ast_channel_free(ast);
04105       } else {
04106          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04107       }
04108    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04109       if (ast_strlen_zero(flag)) {
04110          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04111       } else {
04112          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04113       }
04114    } else {
04115       if (ast_strlen_zero(flag)) {
04116          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04117       } else {
04118          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04119       }
04120    }
04121 
04122    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04123    if (imap) {
04124       /* additional information needed for IMAP searching */
04125       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04126       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04127       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04128       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04129 #ifdef IMAP_STORAGE
04130       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04131 #else
04132       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04133 #endif
04134       /* flag added for Urgent */
04135       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04136       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04137       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04138       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04139       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04140       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04141       if (!ast_strlen_zero(category)) {
04142          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04143       } else {
04144          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04145       }
04146       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04147       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04148       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04149    }
04150    if (!ast_strlen_zero(cidnum)) {
04151       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04152    }
04153    if (!ast_strlen_zero(cidname)) {
04154       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04155    }
04156    fprintf(p, "MIME-Version: 1.0" ENDL);
04157    if (attach_user_voicemail) {
04158       /* Something unique. */
04159       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04160 
04161       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04162       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04163       fprintf(p, "--%s" ENDL, bound);
04164    }
04165    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04166    if (emailbody) {
04167       struct ast_channel *ast;
04168       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04169          char *passdata;
04170          int vmlen = strlen(emailbody)*3 + 200;
04171          passdata = alloca(vmlen);
04172          memset(passdata, 0, vmlen);
04173          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04174          pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
04175          fprintf(p, "%s" ENDL, passdata);
04176          ast_channel_free(ast);
04177       } else
04178          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04179    } else if (msgnum > -1) {
04180       if (strcmp(vmu->mailbox, mailbox)) {
04181          /* Forwarded type */
04182          struct ast_config *msg_cfg;
04183          const char *v;
04184          int inttime;
04185          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04186          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04187          /* Retrieve info from VM attribute file */
04188          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04189          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04190          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04191             strcat(fromfile, ".txt");
04192          }
04193          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04194             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04195                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04196             }
04197 
04198             /* You might be tempted to do origdate, except that a) it's in the wrong
04199              * format, and b) it's missing for IMAP recordings. */
04200             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04201                struct timeval tv = { inttime, };
04202                struct ast_tm tm;
04203                ast_localtime(&tv, &tm, NULL);
04204                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04205             }
04206             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04207                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04208                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04209                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04210                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04211                date, origcallerid, origdate);
04212             ast_config_destroy(msg_cfg);
04213          } else {
04214             goto plain_message;
04215          }
04216       } else {
04217 plain_message:
04218          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04219             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04220             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04221             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04222             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04223       }
04224    } else {
04225       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04226             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04227    }
04228 
04229    if (imap || attach_user_voicemail) {
04230       if (!ast_strlen_zero(attach2)) {
04231          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04232          ast_debug(5, "creating second attachment filename %s\n", filename);
04233          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04234          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04235          ast_debug(5, "creating attachment filename %s\n", filename);
04236          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04237       } else {
04238          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04239          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04240          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04241       }
04242    }
04243 }
04244 
04245 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)
04246 {
04247    char tmpdir[256], newtmp[256];
04248    char fname[256];
04249    char tmpcmd[256];
04250    int tmpfd = -1;
04251 
04252    /* Eww. We want formats to tell us their own MIME type */
04253    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04254 
04255    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04256       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04257       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04258       tmpfd = mkstemp(newtmp);
04259       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04260       ast_debug(3, "newtmp: %s\n", newtmp);
04261       if (tmpfd > -1) {
04262          int soxstatus;
04263          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04264          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04265             attach = newtmp;
04266             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04267          } else {
04268             ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04269                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04270             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04271          }
04272       }
04273    }
04274    fprintf(p, "--%s" ENDL, bound);
04275    if (msgnum > -1)
04276       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04277    else
04278       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04279    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04280    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04281    if (msgnum > -1)
04282       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04283    else
04284       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04285    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04286    base_encode(fname, p);
04287    if (last)
04288       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04289    if (tmpfd > -1) {
04290       unlink(fname);
04291       close(tmpfd);
04292       unlink(newtmp);
04293    }
04294    return 0;
04295 }
04296 #undef ENDL
04297 
04298 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
04299 {
04300    FILE *p=NULL;
04301    char tmp[80] = "/tmp/astmail-XXXXXX";
04302    char tmp2[256];
04303 
04304    if (vmu && ast_strlen_zero(vmu->email)) {
04305       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04306       return(0);
04307    }
04308    if (!strcmp(format, "wav49"))
04309       format = "WAV";
04310    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));
04311    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04312       command hangs */
04313    if ((p = vm_mkftemp(tmp)) == NULL) {
04314       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04315       return -1;
04316    } else {
04317       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04318       fclose(p);
04319       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04320       ast_safe_system(tmp2);
04321       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04322    }
04323    return 0;
04324 }
04325 
04326 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)
04327 {
04328    char date[256];
04329    char host[MAXHOSTNAMELEN] = "";
04330    char who[256];
04331    char dur[PATH_MAX];
04332    char tmp[80] = "/tmp/astmail-XXXXXX";
04333    char tmp2[PATH_MAX];
04334    struct ast_tm tm;
04335    FILE *p;
04336 
04337    if ((p = vm_mkftemp(tmp)) == NULL) {
04338       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04339       return -1;
04340    }
04341    gethostname(host, sizeof(host)-1);
04342    if (strchr(srcemail, '@'))
04343       ast_copy_string(who, srcemail, sizeof(who));
04344    else 
04345       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04346    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04347    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04348    fprintf(p, "Date: %s\n", date);
04349 
04350    if (*pagerfromstring) {
04351       struct ast_channel *ast;
04352       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04353          char *passdata;
04354          int vmlen = strlen(fromstring)*3 + 200;
04355          passdata = alloca(vmlen);
04356          memset(passdata, 0, vmlen);
04357          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04358          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04359          fprintf(p, "From: %s <%s>\n", passdata, who);
04360          ast_channel_free(ast);
04361       } else 
04362          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04363    } else
04364       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04365    fprintf(p, "To: %s\n", pager);
04366    if (pagersubject) {
04367       struct ast_channel *ast;
04368       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04369          char *passdata;
04370          int vmlen = strlen(pagersubject) * 3 + 200;
04371          passdata = alloca(vmlen);
04372          memset(passdata, 0, vmlen);
04373          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04374          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04375          fprintf(p, "Subject: %s\n\n", passdata);
04376          ast_channel_free(ast);
04377       } else
04378          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04379    } else {
04380       if (ast_strlen_zero(flag)) {
04381          fprintf(p, "Subject: New VM\n\n");
04382       } else {
04383          fprintf(p, "Subject: New %s VM\n\n", flag);
04384       }
04385    }
04386 
04387    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04388    if (pagerbody) {
04389       struct ast_channel *ast;
04390       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04391          char *passdata;
04392          int vmlen = strlen(pagerbody) * 3 + 200;
04393          passdata = alloca(vmlen);
04394          memset(passdata, 0, vmlen);
04395          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04396          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04397          fprintf(p, "%s\n", passdata);
04398          ast_channel_free(ast);
04399       } else
04400          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04401    } else {
04402       fprintf(p, "New %s long %s msg in box %s\n"
04403             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04404    }
04405    fclose(p);
04406    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04407    ast_safe_system(tmp2);
04408    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04409    return 0;
04410 }
04411 
04412 /*!
04413  * \brief Gets the current date and time, as formatted string.
04414  * \param s The buffer to hold the output formatted date.
04415  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04416  * 
04417  * The date format string used is "%a %b %e %r UTC %Y".
04418  * 
04419  * \return zero on success, -1 on error.
04420  */
04421 static int get_date(char *s, int len)
04422 {
04423    struct ast_tm tm;
04424    struct timeval t = ast_tvnow();
04425    
04426    ast_localtime(&t, &tm, "UTC");
04427 
04428    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04429 }
04430 
04431 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04432 {
04433    int res;
04434    char fn[PATH_MAX];
04435    char dest[PATH_MAX];
04436 
04437    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04438 
04439    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04440       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04441       return -1;
04442    }
04443 
04444    RETRIEVE(fn, -1, ext, context);
04445    if (ast_fileexists(fn, NULL, NULL) > 0) {
04446       res = ast_stream_and_wait(chan, fn, ecodes);
04447       if (res) {
04448          DISPOSE(fn, -1);
04449          return res;
04450       }
04451    } else {
04452       /* Dispose just in case */
04453       DISPOSE(fn, -1);
04454       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04455       if (res)
04456          return res;
04457       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04458       if (res)
04459          return res;
04460    }
04461    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04462    return res;
04463 }
04464 
04465 static void free_zone(struct vm_zone *z)
04466 {
04467    ast_free(z);
04468 }
04469 
04470 #ifdef ODBC_STORAGE
04471 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04472 {
04473    int x = -1;
04474    int res;
04475    SQLHSTMT stmt = NULL;
04476    char sql[PATH_MAX];
04477    char rowdata[20];
04478    char tmp[PATH_MAX] = "";
04479    struct odbc_obj *obj = NULL;
04480    char *context;
04481    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04482 
04483    if (newmsgs)
04484       *newmsgs = 0;
04485    if (oldmsgs)
04486       *oldmsgs = 0;
04487    if (urgentmsgs)
04488       *urgentmsgs = 0;
04489 
04490    /* If no mailbox, return immediately */
04491    if (ast_strlen_zero(mailbox))
04492       return 0;
04493 
04494    ast_copy_string(tmp, mailbox, sizeof(tmp));
04495 
04496    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04497       int u, n, o;
04498       char *next, *remaining = tmp;
04499       while ((next = strsep(&remaining, " ,"))) {
04500          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04501             return -1;
04502          }
04503          if (urgentmsgs) {
04504             *urgentmsgs += u;
04505          }
04506          if (newmsgs) {
04507             *newmsgs += n;
04508          }
04509          if (oldmsgs) {
04510             *oldmsgs += o;
04511          }
04512       }
04513       return 0;
04514    }
04515 
04516    context = strchr(tmp, '@');
04517    if (context) {
04518       *context = '\0';
04519       context++;
04520    } else
04521       context = "default";
04522 
04523    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04524       do {
04525          if (newmsgs) {
04526             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04527             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04528                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04529                break;
04530             }
04531             res = SQLFetch(stmt);
04532             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04533                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04534                break;
04535             }
04536             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04537             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04538                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04539                break;
04540             }
04541             *newmsgs = atoi(rowdata);
04542             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04543          }
04544 
04545          if (oldmsgs) {
04546             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04547             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04548                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04549                break;
04550             }
04551             res = SQLFetch(stmt);
04552             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04553                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04554                break;
04555             }
04556             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04557             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04558                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04559                break;
04560             }
04561             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04562             *oldmsgs = atoi(rowdata);
04563          }
04564 
04565          if (urgentmsgs) {
04566             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04567             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04568                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04569                break;
04570             }
04571             res = SQLFetch(stmt);
04572             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04573                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04574                break;
04575             }
04576             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04577             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04578                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04579                break;
04580             }
04581             *urgentmsgs = atoi(rowdata);
04582          }
04583 
04584          x = 0;
04585       } while (0);
04586    } else {
04587       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04588    }
04589 
04590    if (stmt) {
04591       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04592    }
04593    if (obj) {
04594       ast_odbc_release_obj(obj);
04595    }
04596 
04597    return x;
04598 }
04599 
04600 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04601 {
04602    return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
04603 }
04604 
04605 /*!
04606  * \brief Gets the number of messages that exist in a mailbox folder.
04607  * \param context
04608  * \param mailbox
04609  * \param folder
04610  * 
04611  * This method is used when ODBC backend is used.
04612  * \return The number of messages in this mailbox folder (zero or more).
04613  */
04614 static int messagecount(const char *context, const char *mailbox, const char *folder)
04615 {
04616    struct odbc_obj *obj = NULL;
04617    int nummsgs = 0;
04618    int res;
04619    SQLHSTMT stmt = NULL;
04620    char sql[PATH_MAX];
04621    char rowdata[20];
04622    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04623    if (!folder)
04624       folder = "INBOX";
04625    /* If no mailbox, return immediately */
04626    if (ast_strlen_zero(mailbox))
04627       return 0;
04628 
04629    obj = ast_odbc_request_obj(odbc_database, 0);
04630    if (obj) {
04631       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04632       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04633       if (!stmt) {
04634          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04635          goto yuck;
04636       }
04637       res = SQLFetch(stmt);
04638       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04639          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04640          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04641          goto yuck;
04642       }
04643       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04644       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04645          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04646          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04647          goto yuck;
04648       }
04649       nummsgs = atoi(rowdata);
04650       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04651    } else
04652       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04653 
04654 yuck:
04655    if (obj)
04656       ast_odbc_release_obj(obj);
04657    return nummsgs;
04658 }
04659 
04660 /** 
04661  * \brief Determines if the given folder has messages.
04662  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04663  * 
04664  * This function is used when the mailbox is stored in an ODBC back end.
04665  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04666  * \return 1 if the folder has one or more messages. zero otherwise.
04667  */
04668 static int has_voicemail(const char *mailbox, const char *folder)
04669 {
04670    char tmp[256], *tmp2 = tmp, *box, *context;
04671    ast_copy_string(tmp, mailbox, sizeof(tmp));
04672    while ((context = box = strsep(&tmp2, ","))) {
04673       strsep(&context, "@");
04674       if (ast_strlen_zero(context))
04675          context = "default";
04676       if (messagecount(context, box, folder))
04677          return 1;
04678    }
04679    return 0;
04680 }
04681 #endif
04682 #ifndef IMAP_STORAGE
04683 /*! 
04684  * \brief Copies a message from one mailbox to another.
04685  * \param chan
04686  * \param vmu
04687  * \param imbox
04688  * \param msgnum
04689  * \param duration
04690  * \param recip
04691  * \param fmt
04692  * \param dir
04693  *
04694  * This is only used by file storage based mailboxes.
04695  *
04696  * \return zero on success, -1 on error.
04697  */
04698 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)
04699 {
04700    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04701    const char *frombox = mbox(imbox);
04702    int recipmsgnum;
04703 
04704    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04705 
04706    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04707       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04708    } else {
04709       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04710    }
04711    
04712    if (!dir)
04713       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04714    else
04715       ast_copy_string(fromdir, dir, sizeof(fromdir));
04716 
04717    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04718    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04719 
04720    if (vm_lock_path(todir))
04721       return ERROR_LOCK_PATH;
04722 
04723    recipmsgnum = last_message_index(recip, todir) + 1;
04724    if (recipmsgnum < recip->maxmsg) {
04725       make_file(topath, sizeof(topath), todir, recipmsgnum);
04726       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04727          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04728       } else {
04729          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04730           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04731           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04732           * much worse problem happening and IMAP storage doesn't call this function
04733           */
04734          copy_plain_file(frompath, topath);
04735          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04736          vm_delete(topath);
04737       }
04738    } else {
04739       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04740    }
04741    ast_unlock_path(todir);
04742    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04743    
04744    return 0;
04745 }
04746 #endif
04747 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04748 
04749 static int messagecount(const char *context, const char *mailbox, const char *folder)
04750 {
04751    return __has_voicemail(context, mailbox, folder, 0);
04752 }
04753 
04754 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04755 {
04756    DIR *dir;
04757    struct dirent *de;
04758    char fn[256];
04759    int ret = 0;
04760 
04761    /* If no mailbox, return immediately */
04762    if (ast_strlen_zero(mailbox))
04763       return 0;
04764 
04765    if (ast_strlen_zero(folder))
04766       folder = "INBOX";
04767    if (ast_strlen_zero(context))
04768       context = "default";
04769 
04770    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04771 
04772    if (!(dir = opendir(fn)))
04773       return 0;
04774 
04775    while ((de = readdir(dir))) {
04776       if (!strncasecmp(de->d_name, "msg", 3)) {
04777          if (shortcircuit) {
04778             ret = 1;
04779             break;
04780          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
04781             if (shortcircuit) return 1;
04782             ret++;
04783          }
04784       }
04785    }
04786 
04787    closedir(dir);
04788 
04789    /* If we are checking INBOX, we should check Urgent as well */
04790    if (strcmp(folder, "INBOX") == 0) {
04791       return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
04792    } else {
04793       return ret;
04794    }
04795 }
04796 
04797 /** 
04798  * \brief Determines if the given folder has messages.
04799  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04800  * \param folder the folder to look in
04801  *
04802  * This function is used when the mailbox is stored in a filesystem back end.
04803  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04804  * \return 1 if the folder has one or more messages. zero otherwise.
04805  */
04806 static int has_voicemail(const char *mailbox, const char *folder)
04807 {
04808    char tmp[256], *tmp2 = tmp, *box, *context;
04809    ast_copy_string(tmp, mailbox, sizeof(tmp));
04810    while ((box = strsep(&tmp2, ","))) {
04811       if ((context = strchr(box, '@')))
04812          *context++ = '\0';
04813       else
04814          context = "default";
04815       if (__has_voicemail(context, box, folder, 1))
04816          return 1;
04817    }
04818    return 0;
04819 }
04820 
04821 
04822 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04823 {
04824    char tmp[256];
04825    char *context;
04826 
04827    /* If no mailbox, return immediately */
04828    if (ast_strlen_zero(mailbox))
04829       return 0;
04830 
04831    if (newmsgs)
04832       *newmsgs = 0;
04833    if (oldmsgs)
04834       *oldmsgs = 0;
04835    if (urgentmsgs)
04836       *urgentmsgs = 0;
04837 
04838    if (strchr(mailbox, ',')) {
04839       int tmpnew, tmpold, tmpurgent;
04840       char *mb, *cur;
04841 
04842       ast_copy_string(tmp, mailbox, sizeof(tmp));
04843       mb = tmp;
04844       while ((cur = strsep(&mb, ", "))) {
04845          if (!ast_strlen_zero(cur)) {
04846             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04847                return -1;
04848             else {
04849                if (newmsgs)
04850                   *newmsgs += tmpnew; 
04851                if (oldmsgs)
04852                   *oldmsgs += tmpold;
04853                if (urgentmsgs)
04854                   *urgentmsgs += tmpurgent;
04855             }
04856          }
04857       }
04858       return 0;
04859    }
04860 
04861    ast_copy_string(tmp, mailbox, sizeof(tmp));
04862    
04863    if ((context = strchr(tmp, '@')))
04864       *context++ = '\0';
04865    else
04866       context = "default";
04867 
04868    if (newmsgs)
04869       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04870    if (oldmsgs)
04871       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04872    if (urgentmsgs)
04873       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
04874 
04875    return 0;
04876 }
04877 
04878 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04879 {
04880    return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
04881 }
04882 
04883 #endif
04884 
04885 static void run_externnotify(char *context, char *extension, const char *flag)
04886 {
04887    char arguments[255];
04888    char ext_context[256] = "";
04889    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
04890    struct ast_smdi_mwi_message *mwi_msg;
04891 
04892    if (!ast_strlen_zero(context))
04893       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04894    else
04895       ast_copy_string(ext_context, extension, sizeof(ext_context));
04896 
04897    if (smdi_iface) {
04898       if (ast_app_has_voicemail(ext_context, NULL)) 
04899          ast_smdi_mwi_set(smdi_iface, extension);
04900       else
04901          ast_smdi_mwi_unset(smdi_iface, extension);
04902 
04903       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04904          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04905          if (!strncmp(mwi_msg->cause, "INV", 3))
04906             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04907          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04908             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04909          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04910          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04911       } else {
04912          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
04913       }
04914    }
04915 
04916    if (!ast_strlen_zero(externnotify)) {
04917       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
04918          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04919       } else {
04920          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
04921          ast_debug(1, "Executing %s\n", arguments);
04922          ast_safe_system(arguments);
04923       }
04924    }
04925 }
04926 
04927 /*!
04928  * \brief Variables used for saving a voicemail.
04929  *
04930  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
04931  */
04932 struct leave_vm_options {
04933    unsigned int flags;
04934    signed char record_gain;
04935    char *exitcontext;
04936 };
04937 
04938 /*!
04939  * \brief Prompts the user and records a voicemail to a mailbox.
04940  * \param chan
04941  * \param ext
04942  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
04943  * 
04944  * 
04945  * 
04946  * \return zero on success, -1 on error.
04947  */
04948 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04949 {
04950 #ifdef IMAP_STORAGE
04951    int newmsgs, oldmsgs;
04952 #else
04953    char urgdir[PATH_MAX];
04954 #endif
04955    char txtfile[PATH_MAX];
04956    char tmptxtfile[PATH_MAX];
04957    struct vm_state *vms = NULL;
04958    char callerid[256];
04959    FILE *txt;
04960    char date[256];
04961    int txtdes;
04962    int res = 0;
04963    int msgnum;
04964    int duration = 0;
04965    int ausemacro = 0;
04966    int ousemacro = 0;
04967    int ouseexten = 0;
04968    int rtmsgid = 0;
04969    char tmpid[16];
04970    char tmpdur[16];
04971    char priority[16];
04972    char origtime[16];
04973    char dir[PATH_MAX];
04974    char tmpdir[PATH_MAX];
04975    char fn[PATH_MAX];
04976    char prefile[PATH_MAX] = "";
04977    char tempfile[PATH_MAX] = "";
04978    char ext_context[256] = "";
04979    char fmt[80];
04980    char *context;
04981    char ecodes[17] = "#";
04982    struct ast_str *tmp = ast_str_create(16);
04983    char *tmpptr;
04984    struct ast_vm_user *vmu;
04985    struct ast_vm_user svm;
04986    const char *category = NULL;
04987    const char *code;
04988    const char *alldtmf = "0123456789ABCD*#";
04989    char flag[80];
04990 
04991    ast_str_set(&tmp, 0, "%s", ext);
04992    ext = ast_str_buffer(tmp);
04993    if ((context = strchr(ext, '@'))) {
04994       *context++ = '\0';
04995       tmpptr = strchr(context, '&');
04996    } else {
04997       tmpptr = strchr(ext, '&');
04998    }
04999 
05000    if (tmpptr)
05001       *tmpptr++ = '\0';
05002 
05003    ast_channel_lock(chan);
05004    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05005       category = ast_strdupa(category);
05006    }
05007    ast_channel_unlock(chan);
05008 
05009    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05010       ast_copy_string(flag, "Urgent", sizeof(flag));
05011    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05012       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05013    } else {
05014       flag[0] = '\0';
05015    }
05016 
05017    ast_debug(3, "Before find_user\n");
05018    if (!(vmu = find_user(&svm, context, ext))) {
05019       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05020       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05021       ast_free(tmp);
05022       return res;
05023    }
05024    /* Setup pre-file if appropriate */
05025    if (strcmp(vmu->context, "default"))
05026       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05027    else
05028       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05029 
05030    /* Set the path to the prefile. Will be one of 
05031       VM_SPOOL_DIRcontext/ext/busy
05032       VM_SPOOL_DIRcontext/ext/unavail
05033       Depending on the flag set in options.
05034    */
05035    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05036       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05037    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05038       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05039    }
05040    /* Set the path to the tmpfile as
05041       VM_SPOOL_DIR/context/ext/temp
05042       and attempt to create the folder structure.
05043    */
05044    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05045    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05046       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05047       ast_free(tmp);
05048       return -1;
05049    }
05050    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05051    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05052       ast_copy_string(prefile, tempfile, sizeof(prefile));
05053 
05054    DISPOSE(tempfile, -1);
05055    /* It's easier just to try to make it than to check for its existence */
05056    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05057 
05058    /* Check current or macro-calling context for special extensions */
05059    if (ast_test_flag(vmu, VM_OPERATOR)) {
05060       if (!ast_strlen_zero(vmu->exit)) {
05061          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05062             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05063             ouseexten = 1;
05064          }
05065       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05066          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05067          ouseexten = 1;
05068       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05069          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05070          ousemacro = 1;
05071       }
05072    }
05073 
05074    if (!ast_strlen_zero(vmu->exit)) {
05075       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05076          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05077    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05078       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05079    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05080       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05081       ausemacro = 1;
05082    }
05083 
05084    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05085       for (code = alldtmf; *code; code++) {
05086          char e[2] = "";
05087          e[0] = *code;
05088          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05089             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05090       }
05091    }
05092 
05093    /* Play the beginning intro if desired */
05094    if (!ast_strlen_zero(prefile)) {
05095 #ifdef ODBC_STORAGE
05096       int success = 
05097 #endif
05098          RETRIEVE(prefile, -1, ext, context);
05099       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05100          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05101             res = ast_waitstream(chan, ecodes);
05102 #ifdef ODBC_STORAGE
05103          if (success == -1) {
05104             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05105             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05106             store_file(prefile, vmu->mailbox, vmu->context, -1);
05107          }
05108 #endif
05109       } else {
05110          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05111          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05112       }
05113       DISPOSE(prefile, -1);
05114       if (res < 0) {
05115          ast_debug(1, "Hang up during prefile playback\n");
05116          free_user(vmu);
05117          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05118          ast_free(tmp);
05119          return -1;
05120       }
05121    }
05122    if (res == '#') {
05123       /* On a '#' we skip the instructions */
05124       ast_set_flag(options, OPT_SILENT);
05125       res = 0;
05126    }
05127    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05128       res = ast_stream_and_wait(chan, INTRO, ecodes);
05129       if (res == '#') {
05130          ast_set_flag(options, OPT_SILENT);
05131          res = 0;
05132       }
05133    }
05134    if (res > 0)
05135       ast_stopstream(chan);
05136    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05137     other than the operator -- an automated attendant or mailbox login for example */
05138    if (res == '*') {
05139       chan->exten[0] = 'a';
05140       chan->exten[1] = '\0';
05141       if (!ast_strlen_zero(vmu->exit)) {
05142          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05143       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05144          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05145       }
05146       chan->priority = 0;
05147       free_user(vmu);
05148       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05149       ast_free(tmp);
05150       return 0;
05151    }
05152 
05153    /* Check for a '0' here */
05154    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05155    transfer:
05156       if (ouseexten || ousemacro) {
05157          chan->exten[0] = 'o';
05158          chan->exten[1] = '\0';
05159          if (!ast_strlen_zero(vmu->exit)) {
05160             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05161          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05162             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05163          }
05164          ast_play_and_wait(chan, "transfer");
05165          chan->priority = 0;
05166          free_user(vmu);
05167          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05168       }
05169       ast_free(tmp);
05170       return 0;
05171    }
05172 
05173    /* Allow all other digits to exit Voicemail and return to the dialplan */
05174    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05175       if (!ast_strlen_zero(options->exitcontext))
05176          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05177       free_user(vmu);
05178       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05179       ast_free(tmp);
05180       return res;
05181    }
05182 
05183    if (res < 0) {
05184       free_user(vmu);
05185       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05186       ast_free(tmp);
05187       return -1;
05188    }
05189    /* The meat of recording the message...  All the announcements and beeps have been played*/
05190    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05191    if (!ast_strlen_zero(fmt)) {
05192       msgnum = 0;
05193 
05194 #ifdef IMAP_STORAGE
05195       /* Is ext a mailbox? */
05196       /* must open stream for this user to get info! */
05197       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05198       if (res < 0) {
05199          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05200          ast_free(tmp);
05201          return -1;
05202       }
05203       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05204       /* It is possible under certain circumstances that inboxcount did not
05205        * create a vm_state when it was needed. This is a catchall which will
05206        * rarely be used.
05207        */
05208          if (!(vms = create_vm_state_from_user(vmu))) {
05209             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05210             ast_free(tmp);
05211             return -1;
05212          }
05213       }
05214       vms->newmessages++;
05215       
05216       /* here is a big difference! We add one to it later */
05217       msgnum = newmsgs + oldmsgs;
05218       ast_debug(3, "Messagecount set to %d\n",msgnum);
05219       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05220       /* set variable for compatibility */
05221       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05222 
05223       /* Check if mailbox is full */
05224       check_quota(vms, imapfolder);
05225       if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
05226          ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
05227          ast_play_and_wait(chan, "vm-mailboxfull");
05228          ast_free(tmp);
05229          return -1;
05230       }
05231       
05232       /* Check if we have exceeded maxmsg */
05233       if (msgnum >= vmu->maxmsg) {
05234          ast_log(AST_LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
05235          ast_play_and_wait(chan, "vm-mailboxfull");
05236          ast_free(tmp);
05237          return -1;
05238       }
05239 #else
05240       if (count_messages(vmu, dir) >= vmu->maxmsg) {
05241          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05242          if (!res)
05243             res = ast_waitstream(chan, "");
05244          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05245          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05246          goto leave_vm_out;
05247       }
05248 
05249 #endif
05250       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05251       txtdes = mkstemp(tmptxtfile);
05252       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05253       if (txtdes < 0) {
05254          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05255          if (!res)
05256             res = ast_waitstream(chan, "");
05257          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05258          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05259          goto leave_vm_out;
05260       }
05261 
05262       /* Now play the beep once we have the message number for our next message. */
05263       if (res >= 0) {
05264          /* Unless we're *really* silent, try to send the beep */
05265          res = ast_stream_and_wait(chan, "beep", "");
05266       }
05267             
05268       /* Store information in real-time storage */
05269       if (ast_check_realtime("voicemail_data")) {
05270          snprintf(priority, sizeof(priority), "%d", chan->priority);
05271          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05272          get_date(date, sizeof(date));
05273          rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), SENTINEL);
05274       }
05275 
05276       /* Store information */
05277       txt = fdopen(txtdes, "w+");
05278       if (txt) {
05279          get_date(date, sizeof(date));
05280          fprintf(txt, 
05281             ";\n"
05282             "; Message Information file\n"
05283             ";\n"
05284             "[message]\n"
05285             "origmailbox=%s\n"
05286             "context=%s\n"
05287             "macrocontext=%s\n"
05288             "exten=%s\n"
05289             "priority=%d\n"
05290             "callerchan=%s\n"
05291             "callerid=%s\n"
05292             "origdate=%s\n"
05293             "origtime=%ld\n"
05294             "category=%s\n",
05295             ext,
05296             chan->context,
05297             chan->macrocontext, 
05298             chan->exten,
05299             chan->priority,
05300             chan->name,
05301             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05302             date, (long)time(NULL),
05303             category ? category : "");
05304       } else
05305          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05306       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05307 
05308       if (txt) {
05309          fprintf(txt, "flag=%s\n", flag);
05310          if (duration < vmminsecs) {
05311             fclose(txt);
05312             if (option_verbose > 2) 
05313                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05314             ast_filedelete(tmptxtfile, NULL);
05315             unlink(tmptxtfile);
05316             if (ast_check_realtime("voicemail_data")) {
05317                snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05318                ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05319             }
05320          } else {
05321             fprintf(txt, "duration=%d\n", duration);
05322             fclose(txt);
05323             if (vm_lock_path(dir)) {
05324                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05325                /* Delete files */
05326                ast_filedelete(tmptxtfile, NULL);
05327                unlink(tmptxtfile);
05328             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05329                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05330                unlink(tmptxtfile);
05331                ast_unlock_path(dir);
05332                if (ast_check_realtime("voicemail_data")) {
05333                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05334                   ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05335                }
05336             } else {
05337 #ifndef IMAP_STORAGE
05338                msgnum = last_message_index(vmu, dir) + 1;
05339 #endif
05340                make_file(fn, sizeof(fn), dir, msgnum);
05341 
05342                /* assign a variable with the name of the voicemail file */ 
05343 #ifndef IMAP_STORAGE
05344                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05345 #else
05346                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05347 #endif
05348 
05349                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05350                ast_filerename(tmptxtfile, fn, NULL);
05351                rename(tmptxtfile, txtfile);
05352 
05353                /* Properly set permissions on voicemail text descriptor file.
05354                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05355                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05356                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05357 
05358                ast_unlock_path(dir);
05359                if (ast_check_realtime("voicemail_data")) {
05360                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05361                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05362                   ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
05363                }
05364                /* We must store the file first, before copying the message, because
05365                 * ODBC storage does the entire copy with SQL.
05366                 */
05367                if (ast_fileexists(fn, NULL, NULL) > 0) {
05368                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05369                }
05370 
05371                /* Are there to be more recipients of this message? */
05372                while (tmpptr) {
05373                   struct ast_vm_user recipu, *recip;
05374                   char *exten, *cntx;
05375                
05376                   exten = strsep(&tmpptr, "&");
05377                   cntx = strchr(exten, '@');
05378                   if (cntx) {
05379                      *cntx = '\0';
05380                      cntx++;
05381                   }
05382                   if ((recip = find_user(&recipu, cntx, exten))) {
05383                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05384                      free_user(recip);
05385                   }
05386                }
05387 #ifndef IMAP_STORAGE
05388                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05389                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05390                   char sfn[PATH_MAX];
05391                   char dfn[PATH_MAX];
05392                   int x;
05393                   /* It's easier just to try to make it than to check for its existence */
05394                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05395                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n",sfn,dfn);
05396                   x = last_message_index(vmu, urgdir) + 1;
05397                   make_file(sfn, sizeof(sfn), dir, msgnum);
05398                   make_file(dfn, sizeof(dfn), urgdir, x);
05399                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05400                }
05401 #endif
05402                /* Notification needs to happen after the copy, though. */
05403                if (ast_fileexists(fn, NULL, NULL)) {
05404 #ifdef IMAP_STORAGE
05405                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05406 #else
05407                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05408 #endif
05409                }
05410 
05411                /* Disposal needs to happen after the optional move and copy */
05412                if (ast_fileexists(fn, NULL, NULL)) {
05413                   DISPOSE(dir, msgnum);
05414                }
05415             }
05416          }
05417       }
05418       if (res == '0') {
05419          goto transfer;
05420       } else if (res > 0)
05421          res = 0;
05422 
05423       if (duration < vmminsecs)
05424          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05425          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05426       else
05427          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05428    } else
05429       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05430 leave_vm_out:
05431    free_user(vmu);
05432 
05433 #ifdef IMAP_STORAGE
05434    /* expunge message - use UID Expunge if supported on IMAP server*/
05435    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05436    if (expungeonhangup == 1) {
05437       ast_mutex_lock(&vms->lock);
05438 #ifdef HAVE_IMAP_TK2006
05439       if (LEVELUIDPLUS (vms->mailstream)) {
05440          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05441       } else 
05442 #endif
05443          mail_expunge(vms->mailstream);
05444       ast_mutex_unlock(&vms->lock);
05445    }
05446 #endif
05447 
05448    ast_free(tmp);
05449    return res;
05450 }
05451 
05452 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05453 {
05454    int d;
05455    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05456    return d;
05457 }
05458 
05459 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05460 {
05461 #ifdef IMAP_STORAGE
05462    /* we must use mbox(x) folder names, and copy the message there */
05463    /* simple. huh? */
05464    char sequence[10];
05465    char mailbox[256];
05466    int res;
05467 
05468    /* get the real IMAP message number for this message */
05469    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05470    
05471    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05472    ast_mutex_lock(&vms->lock);
05473    /* if save to Old folder, put in INBOX as read */
05474    if (box == OLD_FOLDER) {
05475       mail_setflag(vms->mailstream, sequence, "\\Seen");
05476       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05477    } else if (box == NEW_FOLDER) {
05478       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05479       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05480    }
05481    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05482       ast_mutex_unlock(&vms->lock);
05483       return 0;
05484    }
05485    /* Create the folder if it don't exist */
05486    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05487    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05488    if (mail_create(vms->mailstream, mailbox) == NIL) 
05489       ast_debug(5, "Folder exists.\n");
05490    else
05491       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05492    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05493    ast_mutex_unlock(&vms->lock);
05494    return res;
05495 #else
05496    char *dir = vms->curdir;
05497    char *username = vms->username;
05498    char *context = vmu->context;
05499    char sfn[PATH_MAX];
05500    char dfn[PATH_MAX];
05501    char ddir[PATH_MAX];
05502    const char *dbox = mbox(box);
05503    int x, i;
05504    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05505 
05506    if (vm_lock_path(ddir))
05507       return ERROR_LOCK_PATH;
05508 
05509    x = last_message_index(vmu, ddir) + 1;
05510 
05511    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05512       x--;
05513       for (i = 1; i <= x; i++) {
05514          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05515          make_file(sfn, sizeof(sfn), ddir, i);
05516          make_file(dfn, sizeof(dfn), ddir, i - 1);
05517          if (EXISTS(ddir, i, sfn, NULL)) {
05518             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05519          } else
05520             break;
05521       }
05522    } else {
05523       if (x >= vmu->maxmsg) {
05524          ast_unlock_path(ddir);
05525          return -1;
05526       }
05527    }
05528    make_file(sfn, sizeof(sfn), dir, msg);
05529    make_file(dfn, sizeof(dfn), ddir, x);
05530    if (strcmp(sfn, dfn)) {
05531       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05532    }
05533    ast_unlock_path(ddir);
05534 #endif
05535    return 0;
05536 }
05537 
05538 static int adsi_logo(unsigned char *buf)
05539 {
05540    int bytes = 0;
05541    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05542    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05543    return bytes;
05544 }
05545 
05546 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05547 {
05548    unsigned char buf[256];
05549    int bytes=0;
05550    int x;
05551    char num[5];
05552 
05553    *useadsi = 0;
05554    bytes += ast_adsi_data_mode(buf + bytes);
05555    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05556 
05557    bytes = 0;
05558    bytes += adsi_logo(buf);
05559    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05560 #ifdef DISPLAY
05561    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05562 #endif
05563    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05564    bytes += ast_adsi_data_mode(buf + bytes);
05565    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05566 
05567    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05568       bytes = 0;
05569       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05570       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05571       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05572       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05573       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05574       return 0;
05575    }
05576 
05577 #ifdef DISPLAY
05578    /* Add a dot */
05579    bytes = 0;
05580    bytes += ast_adsi_logo(buf);
05581    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05582    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05583    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05584    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05585 #endif
05586    bytes = 0;
05587    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05588    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05589    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05590    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05591    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05592    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05593    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05594 
05595 #ifdef DISPLAY
05596    /* Add another dot */
05597    bytes = 0;
05598    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05599    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05600 
05601    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05602    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05603 #endif
05604 
05605    bytes = 0;
05606    /* These buttons we load but don't use yet */
05607    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05608    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05609    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05610    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05611    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05612    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05613    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05614 
05615 #ifdef DISPLAY
05616    /* Add another dot */
05617    bytes = 0;
05618    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05619    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05620    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05621 #endif
05622 
05623    bytes = 0;
05624    for (x=0;x<5;x++) {
05625       snprintf(num, sizeof(num), "%d", x);
05626       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05627    }
05628    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05629    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05630 
05631 #ifdef DISPLAY
05632    /* Add another dot */
05633    bytes = 0;
05634    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05635    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05636    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05637 #endif
05638 
05639    if (ast_adsi_end_download(chan)) {
05640       bytes = 0;
05641       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05642       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05643       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05644       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05645       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05646       return 0;
05647    }
05648    bytes = 0;
05649    bytes += ast_adsi_download_disconnect(buf + bytes);
05650    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05651    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05652 
05653    ast_debug(1, "Done downloading scripts...\n");
05654 
05655 #ifdef DISPLAY
05656    /* Add last dot */
05657    bytes = 0;
05658    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05659    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05660 #endif
05661    ast_debug(1, "Restarting session...\n");
05662 
05663    bytes = 0;
05664    /* Load the session now */
05665    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05666       *useadsi = 1;
05667       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05668    } else
05669       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05670 
05671    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05672    return 0;
05673 }
05674 
05675 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05676 {
05677    int x;
05678    if (!ast_adsi_available(chan))
05679       return;
05680    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05681    if (x < 0)
05682       return;
05683    if (!x) {
05684       if (adsi_load_vmail(chan, useadsi)) {
05685          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05686          return;
05687       }
05688    } else
05689       *useadsi = 1;
05690 }
05691 
05692 static void adsi_login(struct ast_channel *chan)
05693 {
05694    unsigned char buf[256];
05695    int bytes=0;
05696    unsigned char keys[8];
05697    int x;
05698    if (!ast_adsi_available(chan))
05699       return;
05700 
05701    for (x=0;x<8;x++)
05702       keys[x] = 0;
05703    /* Set one key for next */
05704    keys[3] = ADSI_KEY_APPS + 3;
05705 
05706    bytes += adsi_logo(buf + bytes);
05707    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05708    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05709    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05710    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05711    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05712    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05713    bytes += ast_adsi_set_keys(buf + bytes, keys);
05714    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05715    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05716 }
05717 
05718 static void adsi_password(struct ast_channel *chan)
05719 {
05720    unsigned char buf[256];
05721    int bytes=0;
05722    unsigned char keys[8];
05723    int x;
05724    if (!ast_adsi_available(chan))
05725       return;
05726 
05727    for (x=0;x<8;x++)
05728       keys[x] = 0;
05729    /* Set one key for next */
05730    keys[3] = ADSI_KEY_APPS + 3;
05731 
05732    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05733    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05734    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05735    bytes += ast_adsi_set_keys(buf + bytes, keys);
05736    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05737    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05738 }
05739 
05740 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05741 {
05742    unsigned char buf[256];
05743    int bytes=0;
05744    unsigned char keys[8];
05745    int x,y;
05746 
05747    if (!ast_adsi_available(chan))
05748       return;
05749 
05750    for (x=0;x<5;x++) {
05751       y = ADSI_KEY_APPS + 12 + start + x;
05752       if (y > ADSI_KEY_APPS + 12 + 4)
05753          y = 0;
05754       keys[x] = ADSI_KEY_SKT | y;
05755    }
05756    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05757    keys[6] = 0;
05758    keys[7] = 0;
05759 
05760    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05761    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
05762    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05763    bytes += ast_adsi_set_keys(buf + bytes, keys);
05764    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05765 
05766    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05767 }
05768 
05769 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
05770 {
05771    int bytes=0;
05772    unsigned char buf[256]; 
05773    char buf1[256], buf2[256];
05774    char fn2[PATH_MAX];
05775 
05776    char cid[256]="";
05777    char *val;
05778    char *name, *num;
05779    char datetime[21]="";
05780    FILE *f;
05781 
05782    unsigned char keys[8];
05783 
05784    int x;
05785 
05786    if (!ast_adsi_available(chan))
05787       return;
05788 
05789    /* Retrieve important info */
05790    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
05791    f = fopen(fn2, "r");
05792    if (f) {
05793       while (!feof(f)) {   
05794          if (!fgets((char *)buf, sizeof(buf), f)) {
05795             continue;
05796          }
05797          if (!feof(f)) {
05798             char *stringp=NULL;
05799             stringp = (char *)buf;
05800             strsep(&stringp, "=");
05801             val = strsep(&stringp, "=");
05802             if (!ast_strlen_zero(val)) {
05803                if (!strcmp((char *)buf, "callerid"))
05804                   ast_copy_string(cid, val, sizeof(cid));
05805                if (!strcmp((char *)buf, "origdate"))
05806                   ast_copy_string(datetime, val, sizeof(datetime));
05807             }
05808          }
05809       }
05810       fclose(f);
05811    }
05812    /* New meaning for keys */
05813    for (x=0;x<5;x++)
05814       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05815    keys[6] = 0x0;
05816    keys[7] = 0x0;
05817 
05818    if (!vms->curmsg) {
05819       /* No prev key, provide "Folder" instead */
05820       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05821    }
05822    if (vms->curmsg >= vms->lastmsg) {
05823       /* If last message ... */
05824       if (vms->curmsg) {
05825          /* but not only message, provide "Folder" instead */
05826          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05827          bytes += ast_adsi_voice_mode(buf + bytes, 0);
05828 
05829       } else {
05830          /* Otherwise if only message, leave blank */
05831          keys[3] = 1;
05832       }
05833    }
05834 
05835    if (!ast_strlen_zero(cid)) {
05836       ast_callerid_parse(cid, &name, &num);
05837       if (!name)
05838          name = num;
05839    } else
05840       name = "Unknown Caller";
05841 
05842    /* If deleted, show "undeleted" */
05843 
05844    if (vms->deleted[vms->curmsg])
05845       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05846 
05847    /* Except "Exit" */
05848    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05849    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05850       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05851    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05852 
05853    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05854    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05855    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05856    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05857    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05858    bytes += ast_adsi_set_keys(buf + bytes, keys);
05859    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05860 
05861    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05862 }
05863 
05864 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05865 {
05866    int bytes=0;
05867    unsigned char buf[256];
05868    unsigned char keys[8];
05869 
05870    int x;
05871 
05872    if (!ast_adsi_available(chan))
05873       return;
05874 
05875    /* New meaning for keys */
05876    for (x=0;x<5;x++)
05877       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05878 
05879    keys[6] = 0x0;
05880    keys[7] = 0x0;
05881 
05882    if (!vms->curmsg) {
05883       /* No prev key, provide "Folder" instead */
05884       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05885    }
05886    if (vms->curmsg >= vms->lastmsg) {
05887       /* If last message ... */
05888       if (vms->curmsg) {
05889          /* but not only message, provide "Folder" instead */
05890          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05891       } else {
05892          /* Otherwise if only message, leave blank */
05893          keys[3] = 1;
05894       }
05895    }
05896 
05897    /* If deleted, show "undeleted" */
05898    if (vms->deleted[vms->curmsg]) 
05899       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05900 
05901    /* Except "Exit" */
05902    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05903    bytes += ast_adsi_set_keys(buf + bytes, keys);
05904    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05905 
05906    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05907 }
05908 
05909 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05910 {
05911    unsigned char buf[256] = "";
05912    char buf1[256] = "", buf2[256] = "";
05913    int bytes=0;
05914    unsigned char keys[8];
05915    int x;
05916 
05917    char *newm = (vms->newmessages == 1) ? "message" : "messages";
05918    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05919    if (!ast_adsi_available(chan))
05920       return;
05921    if (vms->newmessages) {
05922       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05923       if (vms->oldmessages) {
05924          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05925          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05926       } else {
05927          snprintf(buf2, sizeof(buf2), "%s.", newm);
05928       }
05929    } else if (vms->oldmessages) {
05930       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05931       snprintf(buf2, sizeof(buf2), "%s.", oldm);
05932    } else {
05933       strcpy(buf1, "You have no messages.");
05934       buf2[0] = ' ';
05935       buf2[1] = '\0';
05936    }
05937    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05938    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05939    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05940 
05941    for (x=0;x<6;x++)
05942       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05943    keys[6] = 0;
05944    keys[7] = 0;
05945 
05946    /* Don't let them listen if there are none */
05947    if (vms->lastmsg < 0)
05948       keys[0] = 1;
05949    bytes += ast_adsi_set_keys(buf + bytes, keys);
05950 
05951    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05952 
05953    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05954 }
05955 
05956 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05957 {
05958    unsigned char buf[256] = "";
05959    char buf1[256] = "", buf2[256] = "";
05960    int bytes=0;
05961    unsigned char keys[8];
05962    int x;
05963 
05964    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05965 
05966    if (!ast_adsi_available(chan))
05967       return;
05968 
05969    /* Original command keys */
05970    for (x=0;x<6;x++)
05971       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05972 
05973    keys[6] = 0;
05974    keys[7] = 0;
05975 
05976    if ((vms->lastmsg + 1) < 1)
05977       keys[0] = 0;
05978 
05979    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05980       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05981 
05982    if (vms->lastmsg + 1)
05983       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05984    else
05985       strcpy(buf2, "no messages.");
05986    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05987    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05988    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05989    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05990    bytes += ast_adsi_set_keys(buf + bytes, keys);
05991 
05992    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05993 
05994    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05995    
05996 }
05997 
05998 /*
05999 static void adsi_clear(struct ast_channel *chan)
06000 {
06001    char buf[256];
06002    int bytes=0;
06003    if (!ast_adsi_available(chan))
06004       return;
06005    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06006    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06007 
06008    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06009 }
06010 */
06011 
06012 static void adsi_goodbye(struct ast_channel *chan)
06013 {
06014    unsigned char buf[256];
06015    int bytes=0;
06016 
06017    if (!ast_adsi_available(chan))
06018       return;
06019    bytes += adsi_logo(buf + bytes);
06020    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06021    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06022    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06023    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06024 
06025    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06026 }
06027 
06028 /*!\brief get_folder: Folder menu
06029  * Plays "press 1 for INBOX messages" etc.
06030  * Should possibly be internationalized
06031  */
06032 static int get_folder(struct ast_channel *chan, int start)
06033 {
06034    int x;
06035    int d;
06036    char fn[PATH_MAX];
06037    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06038    if (d)
06039       return d;
06040    for (x = start; x< 5; x++) {  /* For all folders */
06041       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06042          return d;
06043       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06044       if (d)
06045          return d;
06046       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06047       d = vm_play_folder_name(chan, fn);
06048       if (d)
06049          return d;
06050       d = ast_waitfordigit(chan, 500);
06051       if (d)
06052          return d;
06053    }
06054    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06055    if (d)
06056       return d;
06057    d = ast_waitfordigit(chan, 4000);
06058    return d;
06059 }
06060 
06061 /*!
06062  * \brief plays a prompt and waits for a keypress.
06063  * \param chan
06064  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06065  * \param start Does not appear to be used at this time.
06066  *
06067  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06068  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06069  * prompting for the number inputs that correspond to the available folders.
06070  * 
06071  * \return zero on success, or -1 on error.
06072  */
06073 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06074 {
06075    int res = 0;
06076    res = ast_play_and_wait(chan, fn);  /* Folder name */
06077    while (((res < '0') || (res > '9')) &&
06078          (res != '#') && (res >= 0)) {
06079       res = get_folder(chan, 0);
06080    }
06081    return res;
06082 }
06083 
06084 /*!
06085  * \brief presents the option to prepend to an existing message when forwarding it.
06086  * \param chan
06087  * \param vmu
06088  * \param curdir
06089  * \param curmsg
06090  * \param vmfmts
06091  * \param context
06092  * \param record_gain
06093  * \param duration
06094  * \param vms
06095  *
06096  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06097  *
06098  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06099  * \return zero on success, -1 on error.
06100  */
06101 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06102          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06103 {
06104 #ifdef IMAP_STORAGE
06105    int res;
06106 #endif
06107    int cmd = 0;
06108    int retries = 0, prepend_duration = 0, already_recorded = 0;
06109    char msgfile[PATH_MAX], backup[PATH_MAX];
06110    char textfile[PATH_MAX];
06111    struct ast_config *msg_cfg;
06112    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06113 #ifndef IMAP_STORAGE
06114    signed char zero_gain = 0;
06115 #endif
06116    const char *duration_str;
06117 
06118    /* Must always populate duration correctly */
06119    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06120    strcpy(textfile, msgfile);
06121    strcpy(backup, msgfile);
06122    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06123    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06124 
06125    if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06126       *duration = atoi(duration_str);
06127    } else {
06128       *duration = 0;
06129    }
06130 
06131    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06132       if (cmd)
06133          retries = 0;
06134       switch (cmd) {
06135       case '1': 
06136 
06137 #ifdef IMAP_STORAGE
06138          /* Record new intro file */
06139          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06140          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06141          res = ast_play_and_wait(chan, INTRO);
06142          res = ast_play_and_wait(chan, "beep");
06143          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06144          cmd = 't';
06145 #else
06146 
06147          /* prepend a message to the current message, update the metadata and return */
06148 
06149          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06150          strcpy(textfile, msgfile);
06151          strncat(textfile, ".txt", sizeof(textfile) - 1);
06152          *duration = 0;
06153 
06154          /* if we can't read the message metadata, stop now */
06155          if (!msg_cfg) {
06156             cmd = 0;
06157             break;
06158          }
06159          
06160          /* Back up the original file, so we can retry the prepend */
06161          if (already_recorded)
06162             ast_filecopy(backup, msgfile, NULL);
06163          else
06164             ast_filecopy(msgfile, backup, NULL);
06165          already_recorded = 1;
06166 
06167          if (record_gain)
06168             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06169 
06170          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06171          if (record_gain)
06172             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06173 
06174          
06175          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06176             *duration = atoi(duration_str);
06177 
06178          if (prepend_duration) {
06179             struct ast_category *msg_cat;
06180             /* need enough space for a maximum-length message duration */
06181             char duration_buf[12];
06182 
06183             *duration += prepend_duration;
06184             msg_cat = ast_category_get(msg_cfg, "message");
06185             snprintf(duration_buf, 11, "%ld", *duration);
06186             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06187                config_text_file_save(textfile, msg_cfg, "app_voicemail");
06188             }
06189          }
06190 
06191 #endif
06192          break;
06193       case '2': 
06194          /* NULL out introfile so we know there is no intro! */
06195 #ifdef IMAP_STORAGE
06196          *vms->introfn = '\0';
06197 #endif
06198          cmd = 't';
06199          break;
06200       case '*':
06201          cmd = '*';
06202          break;
06203       default: 
06204          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06205             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06206          if (!cmd)
06207             cmd = ast_play_and_wait(chan,"vm-starmain");
06208             /* "press star to return to the main menu" */
06209          if (!cmd)
06210             cmd = ast_waitfordigit(chan,6000);
06211          if (!cmd)
06212             retries++;
06213          if (retries > 3)
06214             cmd = 't';
06215       }
06216    }
06217 
06218    if (msg_cfg)
06219       ast_config_destroy(msg_cfg);
06220    if (already_recorded)
06221       ast_filedelete(backup, NULL);
06222    if (prepend_duration)
06223       *duration = prepend_duration;
06224 
06225    if (cmd == 't' || cmd == 'S')
06226       cmd = 0;
06227    return cmd;
06228 }
06229 
06230 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06231 {
06232    struct ast_event *event;
06233    char *mailbox, *context;
06234 
06235    /* Strip off @default */
06236    context = mailbox = ast_strdupa(box);
06237    strsep(&context, "@");
06238    if (ast_strlen_zero(context))
06239       context = "default";
06240 
06241    if (!(event = ast_event_new(AST_EVENT_MWI,
06242          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06243          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06244          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06245          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06246          AST_EVENT_IE_END))) {
06247       return;
06248    }
06249 
06250    ast_event_queue_and_cache(event);
06251 }
06252 
06253 /*!
06254  * \brief Sends email notification that a user has a new voicemail waiting for them.
06255  * \param chan
06256  * \param vmu
06257  * \param vms
06258  * \param msgnum
06259  * \param duration
06260  * \param fmt
06261  * \param cidnum The Caller ID phone number value.
06262  * \param cidname The Caller ID name value.
06263  *
06264  * \return zero on success, -1 on error.
06265  */
06266 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)
06267 {
06268    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06269    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06270    const char *category;
06271    char *myserveremail = serveremail;
06272 
06273    ast_channel_lock(chan);
06274    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06275       category = ast_strdupa(category);
06276    }
06277    ast_channel_unlock(chan);
06278 
06279    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
06280    make_file(fn, sizeof(fn), todir, msgnum);
06281    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06282 
06283    if (!ast_strlen_zero(vmu->attachfmt)) {
06284       if (strstr(fmt, vmu->attachfmt))
06285          fmt = vmu->attachfmt;
06286       else
06287          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);
06288    }
06289 
06290    /* Attach only the first format */
06291    fmt = ast_strdupa(fmt);
06292    stringp = fmt;
06293    strsep(&stringp, "|");
06294 
06295    if (!ast_strlen_zero(vmu->serveremail))
06296       myserveremail = vmu->serveremail;
06297 
06298    if (!ast_strlen_zero(vmu->email)) {
06299       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06300       if (!attach_user_voicemail)
06301          attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
06302 
06303       if (attach_user_voicemail)
06304          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06305 
06306       /* XXX possible imap issue, should category be NULL XXX */
06307       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06308 
06309       if (attach_user_voicemail)
06310          DISPOSE(todir, msgnum);
06311    }
06312 
06313    if (!ast_strlen_zero(vmu->pager)) {
06314       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06315    }
06316 
06317    if (ast_test_flag(vmu, VM_DELETE))
06318       DELETE(todir, msgnum, fn, vmu);
06319 
06320    /* Leave voicemail for someone */
06321    if (ast_app_has_voicemail(ext_context, NULL)) 
06322       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06323 
06324    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06325 
06326    manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
06327    run_externnotify(vmu->context, vmu->mailbox, flag);
06328 
06329 #ifdef IMAP_STORAGE
06330    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06331    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06332       vm_imap_delete(NULL, vms->curmsg, vmu);
06333       vms->newmessages--;  /* Fix new message count */
06334    }
06335 #endif
06336 
06337    return 0;
06338 }
06339 
06340 /*!
06341  * \brief Sends a voicemail message to a mailbox recipient.
06342  * \param ast_channel
06343  * \param context
06344  * \param vms
06345  * \param sender
06346  * \param fmt
06347  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06348  *             Will be 0 when called to forward an existing message (option 8)
06349  *             Will be 1 when called to leave a message (option 3->5)
06350  * \param record_gain 
06351  *
06352  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06353  * 
06354  * When in the leave message mode (is_new_message == 1):
06355  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06356  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06357  *
06358  * When in the forward message mode (is_new_message == 0):
06359  *   - retreives the current message to be forwarded
06360  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06361  *   - determines the target mailbox and folders
06362  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06363  *
06364  * \return zero on success, -1 on error.
06365  */
06366 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)
06367 {
06368 #ifdef IMAP_STORAGE
06369    int todircount=0;
06370    struct vm_state *dstvms;
06371 #endif
06372    char username[70]="";
06373    char fn[PATH_MAX]; /* for playback of name greeting */
06374    char ecodes[16] = "#";
06375    int res = 0, cmd = 0;
06376    struct ast_vm_user *receiver = NULL, *vmtmp;
06377    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06378    char *stringp;
06379    const char *s;
06380    int saved_messages = 0, found = 0;
06381    int valid_extensions = 0;
06382    char *dir;
06383    int curmsg;
06384    char urgent_str[7] = "";
06385    char tmptxtfile[PATH_MAX];
06386 
06387    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06388       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06389    }
06390 
06391    if (vms == NULL) return -1;
06392    dir = vms->curdir;
06393    curmsg = vms->curmsg;
06394 
06395    tmptxtfile[0] = '\0';
06396    while (!res && !valid_extensions) {
06397       int use_directory = 0;
06398       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06399          int done = 0;
06400          int retries = 0;
06401          cmd=0;
06402          while ((cmd >= 0) && !done ){
06403             if (cmd)
06404                retries = 0;
06405             switch (cmd) {
06406             case '1': 
06407                use_directory = 0;
06408                done = 1;
06409                break;
06410             case '2': 
06411                use_directory = 1;
06412                done=1;
06413                break;
06414             case '*': 
06415                cmd = 't';
06416                done = 1;
06417                break;
06418             default: 
06419                /* Press 1 to enter an extension press 2 to use the directory */
06420                cmd = ast_play_and_wait(chan,"vm-forward");
06421                if (!cmd)
06422                   cmd = ast_waitfordigit(chan,3000);
06423                if (!cmd)
06424                   retries++;
06425                if (retries > 3) {
06426                   cmd = 't';
06427                   done = 1;
06428                }
06429                
06430             }
06431          }
06432          if (cmd < 0 || cmd == 't')
06433             break;
06434       }
06435       
06436       if (use_directory) {
06437          /* use app_directory */
06438          
06439          char old_context[sizeof(chan->context)];
06440          char old_exten[sizeof(chan->exten)];
06441          int old_priority;
06442          struct ast_app* directory_app;
06443 
06444          directory_app = pbx_findapp("Directory");
06445          if (directory_app) {
06446             char vmcontext[256];
06447             /* make backup copies */
06448             memcpy(old_context, chan->context, sizeof(chan->context));
06449             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06450             old_priority = chan->priority;
06451             
06452             /* call the the Directory, changes the channel */
06453             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06454             res = pbx_exec(chan, directory_app, vmcontext);
06455             
06456             ast_copy_string(username, chan->exten, sizeof(username));
06457             
06458             /* restore the old context, exten, and priority */
06459             memcpy(chan->context, old_context, sizeof(chan->context));
06460             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06461             chan->priority = old_priority;
06462          } else {
06463             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06464             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06465          }
06466       } else {
06467          /* Ask for an extension */
06468          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06469          if (res)
06470             break;
06471          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06472             break;
06473       }
06474       
06475       /* start all over if no username */
06476       if (ast_strlen_zero(username))
06477          continue;
06478       stringp = username;
06479       s = strsep(&stringp, "*");
06480       /* start optimistic */
06481       valid_extensions = 1;
06482       while (s) {
06483          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06484             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06485             found++;
06486          } else {
06487             /* XXX Optimization for the future.  When we encounter a single bad extension,
06488              * bailing out on all of the extensions may not be the way to go.  We should
06489              * probably just bail on that single extension, then allow the user to enter
06490              * several more. XXX
06491              */
06492             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06493                free_user(receiver);
06494             }
06495             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06496             valid_extensions = 0;
06497             break;
06498          }
06499 
06500          /* play name if available, else play extension number */
06501          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06502          RETRIEVE(fn, -1, s, receiver->context);
06503          if (ast_fileexists(fn, NULL, NULL) > 0) {
06504             res = ast_stream_and_wait(chan, fn, ecodes);
06505             if (res) {
06506                DISPOSE(fn, -1);
06507                return res;
06508             }
06509          } else {
06510             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06511          }
06512          DISPOSE(fn, -1);
06513 
06514          s = strsep(&stringp, "*");
06515       }
06516       /* break from the loop of reading the extensions */
06517       if (valid_extensions)
06518          break;
06519       /* "I am sorry, that's not a valid extension.  Please try again." */
06520       res = ast_play_and_wait(chan, "pbx-invalid");
06521    }
06522    /* check if we're clear to proceed */
06523    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06524       return res;
06525    if (is_new_message == 1) {
06526       struct leave_vm_options leave_options;
06527       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06528       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06529 
06530       /* Send VoiceMail */
06531       memset(&leave_options, 0, sizeof(leave_options));
06532       leave_options.record_gain = record_gain;
06533       cmd = leave_voicemail(chan, mailbox, &leave_options);
06534    } else {
06535       /* Forward VoiceMail */
06536       long duration = 0;
06537       struct vm_state vmstmp;
06538       memcpy(&vmstmp, vms, sizeof(vmstmp));
06539 
06540       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06541 
06542       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06543       if (!cmd) {
06544          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06545 #ifdef IMAP_STORAGE
06546             int attach_user_voicemail;
06547             char *myserveremail = serveremail;
06548             
06549             /* get destination mailbox */
06550             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06551             if (!dstvms) {
06552                dstvms = create_vm_state_from_user(vmtmp);
06553             }
06554             if (dstvms) {
06555                init_mailstream(dstvms, 0);
06556                if (!dstvms->mailstream) {
06557                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06558                } else {
06559                   STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06560                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06561                }
06562             } else {
06563                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06564             }
06565             if (!ast_strlen_zero(vmtmp->serveremail))
06566                myserveremail = vmtmp->serveremail;
06567             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06568             /* NULL category for IMAP storage */
06569             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, dstvms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan, NULL, urgent_str);
06570 #else
06571             copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06572 #endif
06573             saved_messages++;
06574             AST_LIST_REMOVE_CURRENT(list);
06575             free_user(vmtmp);
06576             if (res)
06577                break;
06578          }
06579          AST_LIST_TRAVERSE_SAFE_END;
06580          if (saved_messages > 0) {
06581             /* give confirmation that the message was saved */
06582             /* commented out since we can't forward batches yet
06583             if (saved_messages == 1)
06584                res = ast_play_and_wait(chan, "vm-message");
06585             else
06586                res = ast_play_and_wait(chan, "vm-messages");
06587             if (!res)
06588                res = ast_play_and_wait(chan, "vm-saved"); */
06589 #ifdef IMAP_STORAGE
06590             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06591             if (ast_strlen_zero(vmstmp.introfn))
06592 #endif
06593             res = ast_play_and_wait(chan, "vm-msgsaved");
06594          }  
06595       }
06596       DISPOSE(dir, curmsg);
06597    }
06598 
06599    /* If anything failed above, we still have this list to free */
06600    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06601       free_user(vmtmp);
06602    return res ? res : cmd;
06603 }
06604 
06605 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06606 {
06607    int res;
06608    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06609       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06610    return res;
06611 }
06612 
06613 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06614 {
06615    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);
06616 }
06617 
06618 static int play_message_category(struct ast_channel *chan, const char *category)
06619 {
06620    int res = 0;
06621 
06622    if (!ast_strlen_zero(category))
06623       res = ast_play_and_wait(chan, category);
06624 
06625    if (res) {
06626       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06627       res = 0;
06628    }
06629 
06630    return res;
06631 }
06632 
06633 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06634 {
06635    int res = 0;
06636    struct vm_zone *the_zone = NULL;
06637    time_t t;
06638 
06639    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06640       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06641       return 0;
06642    }
06643 
06644    /* Does this user have a timezone specified? */
06645    if (!ast_strlen_zero(vmu->zonetag)) {
06646       /* Find the zone in the list */
06647       struct vm_zone *z;
06648       AST_LIST_LOCK(&zones);
06649       AST_LIST_TRAVERSE(&zones, z, list) {
06650          if (!strcmp(z->name, vmu->zonetag)) {
06651             the_zone = z;
06652             break;
06653          }
06654       }
06655       AST_LIST_UNLOCK(&zones);
06656    }
06657 
06658 /* No internal variable parsing for now, so we'll comment it out for the time being */
06659 #if 0
06660    /* Set the DIFF_* variables */
06661    ast_localtime(&t, &time_now, NULL);
06662    tv_now = ast_tvnow();
06663    ast_localtime(&tv_now, &time_then, NULL);
06664 
06665    /* Day difference */
06666    if (time_now.tm_year == time_then.tm_year)
06667       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06668    else
06669       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06670    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06671 
06672    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06673 #endif
06674    if (the_zone) {
06675       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06676    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
06677       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06678    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
06679       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06680    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
06681       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
06682    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
06683       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06684    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
06685       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06686    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
06687       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06688    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
06689       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
06690    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
06691       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06692    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
06693       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
06694    } else {
06695       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06696    }
06697 #if 0
06698    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06699 #endif
06700    return res;
06701 }
06702 
06703 
06704 
06705 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06706 {
06707    int res = 0;
06708    int i;
06709    char *callerid, *name;
06710    char prefile[PATH_MAX] = "";
06711    
06712 
06713    /* If voicemail cid is not enabled, or we didn't get cid or context from
06714     * the attribute file, leave now.
06715     *
06716     * TODO Still need to change this so that if this function is called by the
06717     * message envelope (and someone is explicitly requesting to hear the CID),
06718     * it does not check to see if CID is enabled in the config file.
06719     */
06720    if ((cid == NULL)||(context == NULL))
06721       return res;
06722 
06723    /* Strip off caller ID number from name */
06724    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
06725    ast_callerid_parse(cid, &name, &callerid);
06726    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
06727       /* Check for internal contexts and only */
06728       /* say extension when the call didn't come from an internal context in the list */
06729       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
06730          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
06731          if ((strcmp(cidinternalcontexts[i], context) == 0))
06732             break;
06733       }
06734       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
06735          if (!res) {
06736             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
06737             if (!ast_strlen_zero(prefile)) {
06738             /* See if we can find a recorded name for this person instead of their extension number */
06739                if (ast_fileexists(prefile, NULL, NULL) > 0) {
06740                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
06741                   if (!callback)
06742                      res = wait_file2(chan, vms, "vm-from");
06743                   res = ast_stream_and_wait(chan, prefile, "");
06744                } else {
06745                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
06746                   /* Say "from extension" as one saying to sound smoother */
06747                   if (!callback)
06748                      res = wait_file2(chan, vms, "vm-from-extension");
06749                   res = ast_say_digit_str(chan, callerid, "", chan->language);
06750                }
06751             }
06752          }
06753       } else if (!res) {
06754          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
06755          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
06756          if (!callback)
06757             res = wait_file2(chan, vms, "vm-from-phonenumber");
06758          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
06759       }
06760    } else {
06761       /* Number unknown */
06762       ast_debug(1, "VM-CID: From an unknown number\n");
06763       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
06764       res = wait_file2(chan, vms, "vm-unknown-caller");
06765    }
06766    return res;
06767 }
06768 
06769 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
06770 {
06771    int res = 0;
06772    int durationm;
06773    int durations;
06774    /* Verify that we have a duration for the message */
06775    if (duration == NULL)
06776       return res;
06777 
06778    /* Convert from seconds to minutes */
06779    durations=atoi(duration);
06780    durationm=(durations / 60);
06781 
06782    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
06783 
06784    if ((!res) && (durationm >= minduration)) {
06785       res = wait_file2(chan, vms, "vm-duration");
06786 
06787       /* POLISH syntax */
06788       if (!strncasecmp(chan->language, "pl", 2)) {
06789          div_t num = div(durationm, 10);
06790 
06791          if (durationm == 1) {
06792             res = ast_play_and_wait(chan, "digits/1z");
06793             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
06794          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06795             if (num.rem == 2) {
06796                if (!num.quot) {
06797                   res = ast_play_and_wait(chan, "digits/2-ie");
06798                } else {
06799                   res = say_and_wait(chan, durationm - 2 , chan->language);
06800                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06801                }
06802             } else {
06803                res = say_and_wait(chan, durationm, chan->language);
06804             }
06805             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
06806          } else {
06807             res = say_and_wait(chan, durationm, chan->language);
06808             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
06809          }
06810       /* DEFAULT syntax */
06811       } else {
06812          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
06813          res = wait_file2(chan, vms, "vm-minutes");
06814       }
06815    }
06816    return res;
06817 }
06818 
06819 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
06820 {
06821    int res = 0;
06822    char filename[256], *cid;
06823    const char *origtime, *context, *category, *duration, *flag;
06824    struct ast_config *msg_cfg;
06825    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06826 
06827    vms->starting = 0;
06828    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06829    adsi_message(chan, vms);
06830    if (!vms->curmsg)
06831       res = wait_file2(chan, vms, "vm-first");  /* "First" */
06832    else if (vms->curmsg == vms->lastmsg)
06833       res = wait_file2(chan, vms, "vm-last");      /* "last" */
06834 
06835    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
06836    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
06837    msg_cfg = ast_config_load(filename, config_flags);
06838    if (!msg_cfg) {
06839       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06840       return 0;
06841    }
06842    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
06843 
06844    /* Play the word urgent if we are listening to urgent messages */
06845    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
06846       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
06847    }
06848 
06849    if (!res) {
06850       /* POLISH syntax */
06851       if (!strncasecmp(chan->language, "pl", 2)) {
06852          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06853             int ten, one;
06854             char nextmsg[256];
06855             ten = (vms->curmsg + 1) / 10;
06856             one = (vms->curmsg + 1) % 10;
06857 
06858             if (vms->curmsg < 20) {
06859                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
06860                res = wait_file2(chan, vms, nextmsg);
06861             } else {
06862                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
06863                res = wait_file2(chan, vms, nextmsg);
06864                if (one > 0) {
06865                   if (!res) {
06866                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
06867                      res = wait_file2(chan, vms, nextmsg);
06868                   }
06869                }
06870             }
06871          }
06872          if (!res)
06873             res = wait_file2(chan, vms, "vm-message");
06874       /* HEBREW syntax */
06875       } else if (!strncasecmp(chan->language, "he", 2)) {
06876          if (!vms->curmsg) {
06877             res = wait_file2(chan, vms, "vm-message");
06878             res = wait_file2(chan, vms, "vm-first");
06879          } else if (vms->curmsg == vms->lastmsg) {
06880             res = wait_file2(chan, vms, "vm-message");
06881             res = wait_file2(chan, vms, "vm-last");
06882          } else {
06883             res = wait_file2(chan, vms, "vm-message");
06884             res = wait_file2(chan, vms, "vm-number");
06885             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
06886          }
06887       } else {
06888          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
06889             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
06890          } else { /* DEFAULT syntax */
06891             res = wait_file2(chan, vms, "vm-message");
06892          }
06893          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06894             if (!res) {
06895                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06896             }
06897          }
06898       }
06899    }
06900 
06901    if (!msg_cfg) {
06902       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06903       return 0;
06904    }
06905 
06906    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06907       ast_log(AST_LOG_WARNING, "No origtime?!\n");
06908       DISPOSE(vms->curdir, vms->curmsg);
06909       ast_config_destroy(msg_cfg);
06910       return 0;
06911    }
06912 
06913    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06914    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06915    category = ast_variable_retrieve(msg_cfg, "message", "category");
06916 
06917    context = ast_variable_retrieve(msg_cfg, "message", "context");
06918    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06919       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06920    if (!res) {
06921       res = play_message_category(chan, category);
06922    }
06923    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06924       res = play_message_datetime(chan, vmu, origtime, filename);
06925    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06926       res = play_message_callerid(chan, vms, cid, context, 0);
06927    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06928       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06929    /* Allow pressing '1' to skip envelope / callerid */
06930    if (res == '1')
06931       res = 0;
06932    ast_config_destroy(msg_cfg);
06933 
06934    if (!res) {
06935       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06936       vms->heard[vms->curmsg] = 1;
06937 #ifdef IMAP_STORAGE
06938       /*IMAP storage stores any prepended message from a forward
06939        * as a separate file from the rest of the message
06940        */
06941       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
06942          wait_file(chan, vms, vms->introfn);
06943       }
06944 #endif
06945       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06946          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06947          res = 0;
06948       }
06949    }
06950    DISPOSE(vms->curdir, vms->curmsg);
06951    return res;
06952 }
06953 
06954 #ifdef IMAP_STORAGE
06955 static int imap_remove_file(char *dir, int msgnum)
06956 {
06957    char fn[PATH_MAX];
06958    char full_fn[PATH_MAX];
06959    char intro[PATH_MAX] = {0,};
06960    
06961    if (msgnum > -1) {
06962       make_file(fn, sizeof(fn), dir, msgnum);
06963       snprintf(intro, sizeof(intro), "%sintro", fn);
06964    } else
06965       ast_copy_string(fn, dir, sizeof(fn));
06966    
06967    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
06968       ast_filedelete(fn, NULL);
06969       if (!ast_strlen_zero(intro)) {
06970          ast_filedelete(intro, NULL);
06971       }
06972       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
06973       unlink(full_fn);
06974    }
06975    return 0;
06976 }
06977 
06978 
06979 
06980 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
06981 {
06982    char *file, *filename;
06983    char *attachment;
06984    char arg[10];
06985    int i;
06986    BODY* body;
06987 
06988    
06989    file = strrchr(ast_strdupa(dir), '/');
06990    if (file)
06991       *file++ = '\0';
06992    else {
06993       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
06994       return -1;
06995    }
06996 
06997    ast_mutex_lock(&vms->lock);
06998    for (i = 0; i < vms->mailstream->nmsgs; i++) {
06999       mail_fetchstructure(vms->mailstream, i + 1, &body);
07000       /* We have the body, now we extract the file name of the first attachment. */
07001       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07002          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07003       } else {
07004          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07005          ast_mutex_unlock(&vms->lock);
07006          return -1;
07007       }
07008       filename = strsep(&attachment, ".");
07009       if (!strcmp(filename, file)) {
07010          sprintf (arg,"%d", i+1);
07011          mail_setflag (vms->mailstream,arg,"\\DELETED");
07012       }
07013    }
07014    mail_expunge(vms->mailstream);
07015    ast_mutex_unlock(&vms->lock);
07016    return 0;
07017 }
07018 
07019 #else
07020 #ifndef IMAP_STORAGE
07021 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07022 {
07023    int count_msg, last_msg;
07024 
07025    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07026    
07027    /* Rename the member vmbox HERE so that we don't try to return before
07028     * we know what's going on.
07029     */
07030    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07031    
07032    /* Faster to make the directory than to check if it exists. */
07033    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07034 
07035    count_msg = count_messages(vmu, vms->curdir);
07036    if (count_msg < 0)
07037       return count_msg;
07038    else
07039       vms->lastmsg = count_msg - 1;
07040 
07041    /*
07042    The following test is needed in case sequencing gets messed up.
07043    There appears to be more than one way to mess up sequence, so
07044    we will not try to find all of the root causes--just fix it when
07045    detected.
07046    */
07047 
07048    if (vm_lock_path(vms->curdir)) {
07049       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07050       return -1;
07051    }
07052 
07053    last_msg = last_message_index(vmu, vms->curdir);
07054    ast_unlock_path(vms->curdir);
07055 
07056    if (last_msg < 0) 
07057       return last_msg;
07058 
07059    return 0;
07060 }
07061 #endif
07062 #endif
07063 
07064 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07065 {
07066    int x = 0;
07067 #ifndef IMAP_STORAGE
07068    int res = 0, nummsg;
07069    char fn2[PATH_MAX];
07070 #endif
07071 
07072    if (vms->lastmsg <= -1)
07073       goto done;
07074 
07075    vms->curmsg = -1; 
07076 #ifndef IMAP_STORAGE
07077    /* Get the deleted messages fixed */ 
07078    if (vm_lock_path(vms->curdir))
07079       return ERROR_LOCK_PATH;
07080 
07081    for (x = 0; x < vmu->maxmsg; x++) { 
07082       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
07083          /* Save this message.  It's not in INBOX or hasn't been heard */ 
07084          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
07085          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
07086             break;
07087          vms->curmsg++; 
07088          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg); 
07089          if (strcmp(vms->fn, fn2)) { 
07090             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07091          } 
07092       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
07093          /* Move to old folder before deleting */ 
07094          res = save_to_folder(vmu, vms, x, 1);
07095          if (res == ERROR_LOCK_PATH) {
07096             /* If save failed do not delete the message */
07097             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07098             vms->deleted[x] = 0;
07099             vms->heard[x] = 0;
07100             --x;
07101          }
07102       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07103          /* Move to deleted folder */ 
07104          res = save_to_folder(vmu, vms, x, 10);
07105          if (res == ERROR_LOCK_PATH) {
07106             /* If save failed do not delete the message */
07107             vms->deleted[x] = 0;
07108             vms->heard[x] = 0;
07109             --x;
07110          }
07111       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07112          /* If realtime storage enabled - we should explicitly delete this message,
07113          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07114          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07115          if (EXISTS(vms->curdir, x, vms->fn, NULL))
07116             DELETE(vms->curdir, x, vms->fn, vmu);
07117       }
07118    } 
07119 
07120    /* Delete ALL remaining messages */
07121    nummsg = x - 1;
07122    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07123       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07124       if (EXISTS(vms->curdir, x, vms->fn, NULL))
07125          DELETE(vms->curdir, x, vms->fn, vmu);
07126    }
07127    ast_unlock_path(vms->curdir);
07128 #else
07129    if (vms->deleted) {
07130       for (x=0;x < vmu->maxmsg;x++) { 
07131          if (vms->deleted[x]) { 
07132             ast_debug(3,"IMAP delete of %d\n",x);
07133             DELETE(vms->curdir, x, vms->fn, vmu);
07134          }
07135       }
07136    }
07137 #endif
07138 
07139 done:
07140    if (vms->deleted)
07141       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
07142    if (vms->heard)
07143       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
07144 
07145    return 0;
07146 }
07147 
07148 /* In Greek even though we CAN use a syntax like "friends messages"
07149  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07150  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
07151  * syntax for the above three categories which is more elegant. 
07152  */
07153 
07154 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07155 {
07156    int cmd;
07157    char *buf;
07158 
07159    buf = alloca(strlen(box)+2); 
07160    strcpy(buf, box);
07161    strcat(buf,"s");
07162 
07163    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07164       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07165       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07166    } else {
07167       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07168       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07169    }
07170 }
07171 
07172 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07173 {
07174    int cmd;
07175 
07176    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07177       if (!strcasecmp(box, "vm-INBOX"))
07178          cmd = ast_play_and_wait(chan, "vm-new-e");
07179       else
07180          cmd = ast_play_and_wait(chan, "vm-old-e");
07181       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07182    } else {
07183       cmd = ast_play_and_wait(chan, "vm-messages");
07184       return cmd ? cmd : ast_play_and_wait(chan, box);
07185    }
07186 }
07187 
07188 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07189 {
07190    int cmd;
07191 
07192    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07193       cmd = ast_play_and_wait(chan, "vm-messages");
07194       return cmd ? cmd : ast_play_and_wait(chan, box);
07195    } else {
07196       cmd = ast_play_and_wait(chan, box);
07197       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07198    }
07199 }
07200 
07201 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07202 {
07203    int cmd;
07204 
07205    if (  !strncasecmp(chan->language, "it", 2) ||
07206         !strncasecmp(chan->language, "es", 2) ||
07207         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07208       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07209       return cmd ? cmd : ast_play_and_wait(chan, box);
07210    } else if (!strncasecmp(chan->language, "gr", 2)) {
07211       return vm_play_folder_name_gr(chan, box);
07212    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07213       return ast_play_and_wait(chan, box);
07214    } else if (!strncasecmp(chan->language, "pl", 2)) {
07215       return vm_play_folder_name_pl(chan, box);
07216    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07217       return vm_play_folder_name_ua(chan, box);
07218    } else {  /* Default English */
07219       cmd = ast_play_and_wait(chan, box);
07220       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07221    }
07222 }
07223 
07224 /* GREEK SYNTAX
07225    In greek the plural for old/new is
07226    different so we need the following files
07227    We also need vm-denExeteMynhmata because
07228    this syntax is different.
07229 
07230    -> vm-Olds.wav : "Palia"
07231    -> vm-INBOXs.wav : "Nea"
07232    -> vm-denExeteMynhmata : "den exete mynhmata"
07233 */
07234 
07235 
07236 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07237 {
07238    int res = 0;
07239 
07240    if (vms->newmessages) {
07241       res = ast_play_and_wait(chan, "vm-youhave");
07242       if (!res) 
07243          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07244       if (!res) {
07245          if ((vms->newmessages == 1)) {
07246             res = ast_play_and_wait(chan, "vm-INBOX");
07247             if (!res)
07248                res = ast_play_and_wait(chan, "vm-message");
07249          } else {
07250             res = ast_play_and_wait(chan, "vm-INBOXs");
07251             if (!res)
07252                res = ast_play_and_wait(chan, "vm-messages");
07253          }
07254       }
07255    } else if (vms->oldmessages){
07256       res = ast_play_and_wait(chan, "vm-youhave");
07257       if (!res)
07258          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07259       if ((vms->oldmessages == 1)){
07260          res = ast_play_and_wait(chan, "vm-Old");
07261          if (!res)
07262             res = ast_play_and_wait(chan, "vm-message");
07263       } else {
07264          res = ast_play_and_wait(chan, "vm-Olds");
07265          if (!res)
07266             res = ast_play_and_wait(chan, "vm-messages");
07267       }
07268    } else if (!vms->oldmessages && !vms->newmessages) 
07269       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07270    return res;
07271 }
07272 
07273 /* Version of vm_intro() designed to work for many languages.
07274  *
07275  * It is hoped that this function can prevent the proliferation of 
07276  * language-specific vm_intro() functions and in time replace the language-
07277  * specific functions which already exist.  An examination of the language-
07278  * specific functions revealed that they all corrected the same deficiencies
07279  * in vm_intro_en() (which was the default function). Namely:
07280  *
07281  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07282  *     wording of the voicemail greeting hides this problem.  For example,
07283  *     vm-INBOX contains only the word "new".  This means that both of these
07284  *     sequences produce valid utterances:
07285  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07286  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07287  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07288  *     in many languages) the first utterance becomes "you have 1 the new message".
07289  *  2) The function contains hardcoded rules for pluralizing the word "message".
07290  *     These rules are correct for English, but not for many other languages.
07291  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07292  *     required in many languages.
07293  *  4) The gender of the word for "message" is not specified. This is a problem
07294  *     because in many languages the gender of the number in phrases such
07295  *     as "you have one new message" must match the gender of the word
07296  *     meaning "message".
07297  *
07298  * Fixing these problems for each new language has meant duplication of effort.
07299  * This new function solves the problems in the following general ways:
07300  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07301  *     and vm-Old respectively for those languages where it makes sense.
07302  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07303  *     on vm-message.
07304  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07305  *     prefix on vm-new and vm-old (none for English).
07306  *  4) Pass the gender of the language's word for "message" as an agument to
07307  *     this function which is can in turn pass on to the functions which 
07308  *     say numbers and put endings on nounds and adjectives.
07309  *
07310  * All languages require these messages:
07311  *  vm-youhave    "You have..."
07312  *  vm-and     "and"
07313  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07314  *
07315  * To use it for English, you will need these additional sound files:
07316  *  vm-new     "new"
07317  *  vm-message    "message", singular
07318  *  vm-messages      "messages", plural
07319  *
07320  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07321  *
07322  *  vm-newn    "novoye" (singular, neuter)
07323  *  vm-newx    "novikh" (counting plural form, genative plural)
07324  *  vm-message    "sobsheniye" (singular form)
07325  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07326  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07327  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07328  *  digits/2n     "dva" (neuter singular)
07329  */
07330 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07331 {
07332    int res;
07333    int lastnum = 0;
07334 
07335    res = ast_play_and_wait(chan, "vm-youhave");
07336 
07337    if (!res && vms->newmessages) {
07338       lastnum = vms->newmessages;
07339 
07340       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07341          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07342       }
07343 
07344       if (!res && vms->oldmessages) {
07345          res = ast_play_and_wait(chan, "vm-and");
07346       }
07347    }
07348 
07349    if (!res && vms->oldmessages) {
07350       lastnum = vms->oldmessages;
07351 
07352       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07353          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07354       }
07355    }
07356 
07357    if (!res) {
07358       if (lastnum == 0) {
07359          res = ast_play_and_wait(chan, "vm-no");
07360       }
07361       if (!res) {
07362          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07363       }
07364    }
07365 
07366    return res;
07367 }
07368 
07369 /* Default Hebrew syntax */
07370 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07371 {
07372    int res = 0;
07373 
07374    /* Introduce messages they have */
07375    if (!res) {
07376       if ((vms->newmessages) || (vms->oldmessages)) {
07377          res = ast_play_and_wait(chan, "vm-youhave");
07378       }
07379       /*
07380        * The word "shtei" refers to the number 2 in hebrew when performing a count
07381        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07382        * an element, this is one of them.
07383        */
07384       if (vms->newmessages) {
07385          if (!res) {
07386             if (vms->newmessages == 1) {
07387                res = ast_play_and_wait(chan, "vm-INBOX1");
07388             } else {
07389                if (vms->newmessages == 2) {
07390                   res = ast_play_and_wait(chan, "vm-shtei");
07391                } else {
07392                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07393                }
07394                res = ast_play_and_wait(chan, "vm-INBOX");
07395             }
07396          }
07397          if (vms->oldmessages && !res) {
07398             res = ast_play_and_wait(chan, "vm-and");
07399             if (vms->oldmessages == 1) {
07400                res = ast_play_and_wait(chan, "vm-Old1");
07401             } else {
07402                if (vms->oldmessages == 2) {
07403                   res = ast_play_and_wait(chan, "vm-shtei");
07404                } else {
07405                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07406                }
07407                res = ast_play_and_wait(chan, "vm-Old");
07408             }
07409          }
07410       }
07411       if (!res && vms->oldmessages && !vms->newmessages) {
07412          if (!res) {
07413             if (vms->oldmessages == 1) {
07414                res = ast_play_and_wait(chan, "vm-Old1");
07415             } else {
07416                if (vms->oldmessages == 2) {
07417                   res = ast_play_and_wait(chan, "vm-shtei");
07418                } else {
07419                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07420                }
07421                res = ast_play_and_wait(chan, "vm-Old");
07422             }
07423          }
07424       }
07425       if (!res) {
07426          if (!vms->oldmessages && !vms->newmessages) {
07427             if (!res) {
07428                res = ast_play_and_wait(chan, "vm-nomessages");
07429             }
07430          }
07431       }
07432    }
07433    return res;
07434 }
07435    
07436 /* Default English syntax */
07437 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07438 {
07439    int res;
07440 
07441    /* Introduce messages they have */
07442    res = ast_play_and_wait(chan, "vm-youhave");
07443    if (!res) {
07444       if (vms->urgentmessages) {
07445          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07446          if (!res)
07447             res = ast_play_and_wait(chan, "vm-Urgent");
07448          if ((vms->oldmessages || vms->newmessages) && !res) {
07449             res = ast_play_and_wait(chan, "vm-and");
07450          } else if (!res) {
07451             if ((vms->urgentmessages == 1))
07452                res = ast_play_and_wait(chan, "vm-message");
07453             else
07454                res = ast_play_and_wait(chan, "vm-messages");
07455          }
07456       }
07457       if (vms->newmessages) {
07458          res = say_and_wait(chan, vms->newmessages, chan->language);
07459          if (!res)
07460             res = ast_play_and_wait(chan, "vm-INBOX");
07461          if (vms->oldmessages && !res)
07462             res = ast_play_and_wait(chan, "vm-and");
07463          else if (!res) {
07464             if ((vms->newmessages == 1))
07465                res = ast_play_and_wait(chan, "vm-message");
07466             else
07467                res = ast_play_and_wait(chan, "vm-messages");
07468          }
07469             
07470       }
07471       if (!res && vms->oldmessages) {
07472          res = say_and_wait(chan, vms->oldmessages, chan->language);
07473          if (!res)
07474             res = ast_play_and_wait(chan, "vm-Old");
07475          if (!res) {
07476             if (vms->oldmessages == 1)
07477                res = ast_play_and_wait(chan, "vm-message");
07478             else
07479                res = ast_play_and_wait(chan, "vm-messages");
07480          }
07481       }
07482       if (!res) {
07483          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07484             res = ast_play_and_wait(chan, "vm-no");
07485             if (!res)
07486                res = ast_play_and_wait(chan, "vm-messages");
07487          }
07488       }
07489    }
07490    return res;
07491 }
07492 
07493 /* ITALIAN syntax */
07494 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07495 {
07496    /* Introduce messages they have */
07497    int res;
07498    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07499       res = ast_play_and_wait(chan, "vm-no") ||
07500          ast_play_and_wait(chan, "vm-message");
07501    else
07502       res = ast_play_and_wait(chan, "vm-youhave");
07503    if (!res && vms->newmessages) {
07504       res = (vms->newmessages == 1) ?
07505          ast_play_and_wait(chan, "digits/un") ||
07506          ast_play_and_wait(chan, "vm-nuovo") ||
07507          ast_play_and_wait(chan, "vm-message") :
07508          /* 2 or more new messages */
07509          say_and_wait(chan, vms->newmessages, chan->language) ||
07510          ast_play_and_wait(chan, "vm-nuovi") ||
07511          ast_play_and_wait(chan, "vm-messages");
07512       if (!res && vms->oldmessages)
07513          res = ast_play_and_wait(chan, "vm-and");
07514    }
07515    if (!res && vms->oldmessages) {
07516       res = (vms->oldmessages == 1) ?
07517          ast_play_and_wait(chan, "digits/un") ||
07518          ast_play_and_wait(chan, "vm-vecchio") ||
07519          ast_play_and_wait(chan, "vm-message") :
07520          /* 2 or more old messages */
07521          say_and_wait(chan, vms->oldmessages, chan->language) ||
07522          ast_play_and_wait(chan, "vm-vecchi") ||
07523          ast_play_and_wait(chan, "vm-messages");
07524    }
07525    return res;
07526 }
07527 
07528 /* POLISH syntax */
07529 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07530 {
07531    /* Introduce messages they have */
07532    int res;
07533    div_t num;
07534 
07535    if (!vms->oldmessages && !vms->newmessages) {
07536       res = ast_play_and_wait(chan, "vm-no");
07537       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07538       return res;
07539    } else {
07540       res = ast_play_and_wait(chan, "vm-youhave");
07541    }
07542 
07543    if (vms->newmessages) {
07544       num = div(vms->newmessages, 10);
07545       if (vms->newmessages == 1) {
07546          res = ast_play_and_wait(chan, "digits/1-a");
07547          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07548          res = res ? res : ast_play_and_wait(chan, "vm-message");
07549       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07550          if (num.rem == 2) {
07551             if (!num.quot) {
07552                res = ast_play_and_wait(chan, "digits/2-ie");
07553             } else {
07554                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07555                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07556             }
07557          } else {
07558             res = say_and_wait(chan, vms->newmessages, chan->language);
07559          }
07560          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07561          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07562       } else {
07563          res = say_and_wait(chan, vms->newmessages, chan->language);
07564          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07565          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07566       }
07567       if (!res && vms->oldmessages)
07568          res = ast_play_and_wait(chan, "vm-and");
07569    }
07570    if (!res && vms->oldmessages) {
07571       num = div(vms->oldmessages, 10);
07572       if (vms->oldmessages == 1) {
07573          res = ast_play_and_wait(chan, "digits/1-a");
07574          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07575          res = res ? res : ast_play_and_wait(chan, "vm-message");
07576       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07577          if (num.rem == 2) {
07578             if (!num.quot) {
07579                res = ast_play_and_wait(chan, "digits/2-ie");
07580             } else {
07581                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07582                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07583             }
07584          } else {
07585             res = say_and_wait(chan, vms->oldmessages, chan->language);
07586          }
07587          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07588          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07589       } else {
07590          res = say_and_wait(chan, vms->oldmessages, chan->language);
07591          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07592          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07593       }
07594    }
07595 
07596    return res;
07597 }
07598 
07599 /* SWEDISH syntax */
07600 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07601 {
07602    /* Introduce messages they have */
07603    int res;
07604 
07605    res = ast_play_and_wait(chan, "vm-youhave");
07606    if (res)
07607       return res;
07608 
07609    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07610       res = ast_play_and_wait(chan, "vm-no");
07611       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07612       return res;
07613    }
07614 
07615    if (vms->newmessages) {
07616       if ((vms->newmessages == 1)) {
07617          res = ast_play_and_wait(chan, "digits/ett");
07618          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07619          res = res ? res : ast_play_and_wait(chan, "vm-message");
07620       } else {
07621          res = say_and_wait(chan, vms->newmessages, chan->language);
07622          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07623          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07624       }
07625       if (!res && vms->oldmessages)
07626          res = ast_play_and_wait(chan, "vm-and");
07627    }
07628    if (!res && vms->oldmessages) {
07629       if (vms->oldmessages == 1) {
07630          res = ast_play_and_wait(chan, "digits/ett");
07631          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07632          res = res ? res : ast_play_and_wait(chan, "vm-message");
07633       } else {
07634          res = say_and_wait(chan, vms->oldmessages, chan->language);
07635          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07636          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07637       }
07638    }
07639 
07640    return res;
07641 }
07642 
07643 /* NORWEGIAN syntax */
07644 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07645 {
07646    /* Introduce messages they have */
07647    int res;
07648 
07649    res = ast_play_and_wait(chan, "vm-youhave");
07650    if (res)
07651       return res;
07652 
07653    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07654       res = ast_play_and_wait(chan, "vm-no");
07655       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07656       return res;
07657    }
07658 
07659    if (vms->newmessages) {
07660       if ((vms->newmessages == 1)) {
07661          res = ast_play_and_wait(chan, "digits/1");
07662          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07663          res = res ? res : ast_play_and_wait(chan, "vm-message");
07664       } else {
07665          res = say_and_wait(chan, vms->newmessages, chan->language);
07666          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07667          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07668       }
07669       if (!res && vms->oldmessages)
07670          res = ast_play_and_wait(chan, "vm-and");
07671    }
07672    if (!res && vms->oldmessages) {
07673       if (vms->oldmessages == 1) {
07674          res = ast_play_and_wait(chan, "digits/1");
07675          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07676          res = res ? res : ast_play_and_wait(chan, "vm-message");
07677       } else {
07678          res = say_and_wait(chan, vms->oldmessages, chan->language);
07679          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07680          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07681       }
07682    }
07683 
07684    return res;
07685 }
07686 
07687 /* GERMAN syntax */
07688 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07689 {
07690    /* Introduce messages they have */
07691    int res;
07692    res = ast_play_and_wait(chan, "vm-youhave");
07693    if (!res) {
07694       if (vms->newmessages) {
07695          if ((vms->newmessages == 1))
07696             res = ast_play_and_wait(chan, "digits/1F");
07697          else
07698             res = say_and_wait(chan, vms->newmessages, chan->language);
07699          if (!res)
07700             res = ast_play_and_wait(chan, "vm-INBOX");
07701          if (vms->oldmessages && !res)
07702             res = ast_play_and_wait(chan, "vm-and");
07703          else if (!res) {
07704             if ((vms->newmessages == 1))
07705                res = ast_play_and_wait(chan, "vm-message");
07706             else
07707                res = ast_play_and_wait(chan, "vm-messages");
07708          }
07709             
07710       }
07711       if (!res && vms->oldmessages) {
07712          if (vms->oldmessages == 1)
07713             res = ast_play_and_wait(chan, "digits/1F");
07714          else
07715             res = say_and_wait(chan, vms->oldmessages, chan->language);
07716          if (!res)
07717             res = ast_play_and_wait(chan, "vm-Old");
07718          if (!res) {
07719             if (vms->oldmessages == 1)
07720                res = ast_play_and_wait(chan, "vm-message");
07721             else
07722                res = ast_play_and_wait(chan, "vm-messages");
07723          }
07724       }
07725       if (!res) {
07726          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07727             res = ast_play_and_wait(chan, "vm-no");
07728             if (!res)
07729                res = ast_play_and_wait(chan, "vm-messages");
07730          }
07731       }
07732    }
07733    return res;
07734 }
07735 
07736 /* SPANISH syntax */
07737 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
07738 {
07739    /* Introduce messages they have */
07740    int res;
07741    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07742       res = ast_play_and_wait(chan, "vm-youhaveno");
07743       if (!res)
07744          res = ast_play_and_wait(chan, "vm-messages");
07745    } else {
07746       res = ast_play_and_wait(chan, "vm-youhave");
07747    }
07748    if (!res) {
07749       if (vms->newmessages) {
07750          if (!res) {
07751             if ((vms->newmessages == 1)) {
07752                res = ast_play_and_wait(chan, "digits/1M");
07753                if (!res)
07754                   res = ast_play_and_wait(chan, "vm-message");
07755                if (!res)
07756                   res = ast_play_and_wait(chan, "vm-INBOXs");
07757             } else {
07758                res = say_and_wait(chan, vms->newmessages, chan->language);
07759                if (!res)
07760                   res = ast_play_and_wait(chan, "vm-messages");
07761                if (!res)
07762                   res = ast_play_and_wait(chan, "vm-INBOX");
07763             }
07764          }
07765          if (vms->oldmessages && !res)
07766             res = ast_play_and_wait(chan, "vm-and");
07767       }
07768       if (vms->oldmessages) {
07769          if (!res) {
07770             if (vms->oldmessages == 1) {
07771                res = ast_play_and_wait(chan, "digits/1M");
07772                if (!res)
07773                   res = ast_play_and_wait(chan, "vm-message");
07774                if (!res)
07775                   res = ast_play_and_wait(chan, "vm-Olds");
07776             } else {
07777                res = say_and_wait(chan, vms->oldmessages, chan->language);
07778                if (!res)
07779                   res = ast_play_and_wait(chan, "vm-messages");
07780                if (!res)
07781                   res = ast_play_and_wait(chan, "vm-Old");
07782             }
07783          }
07784       }
07785    }
07786 return res;
07787 }
07788 
07789 /* BRAZILIAN PORTUGUESE syntax */
07790 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
07791    /* Introduce messages they have */
07792    int res;
07793    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07794       res = ast_play_and_wait(chan, "vm-nomessages");
07795       return res;
07796    } else {
07797       res = ast_play_and_wait(chan, "vm-youhave");
07798    }
07799    if (vms->newmessages) {
07800       if (!res)
07801          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07802       if ((vms->newmessages == 1)) {
07803          if (!res)
07804             res = ast_play_and_wait(chan, "vm-message");
07805          if (!res)
07806             res = ast_play_and_wait(chan, "vm-INBOXs");
07807       } else {
07808          if (!res)
07809             res = ast_play_and_wait(chan, "vm-messages");
07810          if (!res)
07811             res = ast_play_and_wait(chan, "vm-INBOX");
07812       }
07813       if (vms->oldmessages && !res)
07814          res = ast_play_and_wait(chan, "vm-and");
07815    }
07816    if (vms->oldmessages) {
07817       if (!res)
07818          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07819       if (vms->oldmessages == 1) {
07820          if (!res)
07821             res = ast_play_and_wait(chan, "vm-message");
07822          if (!res)
07823             res = ast_play_and_wait(chan, "vm-Olds");
07824       } else {
07825          if (!res)
07826             res = ast_play_and_wait(chan, "vm-messages");
07827          if (!res)
07828             res = ast_play_and_wait(chan, "vm-Old");
07829       }
07830    }
07831    return res;
07832 }
07833 
07834 /* FRENCH syntax */
07835 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
07836 {
07837    /* Introduce messages they have */
07838    int res;
07839    res = ast_play_and_wait(chan, "vm-youhave");
07840    if (!res) {
07841       if (vms->newmessages) {
07842          res = say_and_wait(chan, vms->newmessages, chan->language);
07843          if (!res)
07844             res = ast_play_and_wait(chan, "vm-INBOX");
07845          if (vms->oldmessages && !res)
07846             res = ast_play_and_wait(chan, "vm-and");
07847          else if (!res) {
07848             if ((vms->newmessages == 1))
07849                res = ast_play_and_wait(chan, "vm-message");
07850             else
07851                res = ast_play_and_wait(chan, "vm-messages");
07852          }
07853             
07854       }
07855       if (!res && vms->oldmessages) {
07856          res = say_and_wait(chan, vms->oldmessages, chan->language);
07857          if (!res)
07858             res = ast_play_and_wait(chan, "vm-Old");
07859          if (!res) {
07860             if (vms->oldmessages == 1)
07861                res = ast_play_and_wait(chan, "vm-message");
07862             else
07863                res = ast_play_and_wait(chan, "vm-messages");
07864          }
07865       }
07866       if (!res) {
07867          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07868             res = ast_play_and_wait(chan, "vm-no");
07869             if (!res)
07870                res = ast_play_and_wait(chan, "vm-messages");
07871          }
07872       }
07873    }
07874    return res;
07875 }
07876 
07877 /* DUTCH syntax */
07878 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
07879 {
07880    /* Introduce messages they have */
07881    int res;
07882    res = ast_play_and_wait(chan, "vm-youhave");
07883    if (!res) {
07884       if (vms->newmessages) {
07885          res = say_and_wait(chan, vms->newmessages, chan->language);
07886          if (!res) {
07887             if (vms->newmessages == 1)
07888                res = ast_play_and_wait(chan, "vm-INBOXs");
07889             else
07890                res = ast_play_and_wait(chan, "vm-INBOX");
07891          }
07892          if (vms->oldmessages && !res)
07893             res = ast_play_and_wait(chan, "vm-and");
07894          else if (!res) {
07895             if ((vms->newmessages == 1))
07896                res = ast_play_and_wait(chan, "vm-message");
07897             else
07898                res = ast_play_and_wait(chan, "vm-messages");
07899          }
07900             
07901       }
07902       if (!res && vms->oldmessages) {
07903          res = say_and_wait(chan, vms->oldmessages, chan->language);
07904          if (!res) {
07905             if (vms->oldmessages == 1)
07906                res = ast_play_and_wait(chan, "vm-Olds");
07907             else
07908                res = ast_play_and_wait(chan, "vm-Old");
07909          }
07910          if (!res) {
07911             if (vms->oldmessages == 1)
07912                res = ast_play_and_wait(chan, "vm-message");
07913             else
07914                res = ast_play_and_wait(chan, "vm-messages");
07915          }
07916       }
07917       if (!res) {
07918          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07919             res = ast_play_and_wait(chan, "vm-no");
07920             if (!res)
07921                res = ast_play_and_wait(chan, "vm-messages");
07922          }
07923       }
07924    }
07925    return res;
07926 }
07927 
07928 /* PORTUGUESE syntax */
07929 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
07930 {
07931    /* Introduce messages they have */
07932    int res;
07933    res = ast_play_and_wait(chan, "vm-youhave");
07934    if (!res) {
07935       if (vms->newmessages) {
07936          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07937          if (!res) {
07938             if ((vms->newmessages == 1)) {
07939                res = ast_play_and_wait(chan, "vm-message");
07940                if (!res)
07941                   res = ast_play_and_wait(chan, "vm-INBOXs");
07942             } else {
07943                res = ast_play_and_wait(chan, "vm-messages");
07944                if (!res)
07945                   res = ast_play_and_wait(chan, "vm-INBOX");
07946             }
07947          }
07948          if (vms->oldmessages && !res)
07949             res = ast_play_and_wait(chan, "vm-and");
07950       }
07951       if (!res && vms->oldmessages) {
07952          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07953          if (!res) {
07954             if (vms->oldmessages == 1) {
07955                res = ast_play_and_wait(chan, "vm-message");
07956                if (!res)
07957                   res = ast_play_and_wait(chan, "vm-Olds");
07958             } else {
07959                res = ast_play_and_wait(chan, "vm-messages");
07960                if (!res)
07961                   res = ast_play_and_wait(chan, "vm-Old");
07962             }
07963          }
07964       }
07965       if (!res) {
07966          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07967             res = ast_play_and_wait(chan, "vm-no");
07968             if (!res)
07969                res = ast_play_and_wait(chan, "vm-messages");
07970          }
07971       }
07972    }
07973    return res;
07974 }
07975 
07976 
07977 /* CZECH syntax */
07978 /* in czech there must be declension of word new and message
07979  * czech        : english        : czech      : english
07980  * --------------------------------------------------------
07981  * vm-youhave   : you have 
07982  * vm-novou     : one new        : vm-zpravu  : message
07983  * vm-nove      : 2-4 new        : vm-zpravy  : messages
07984  * vm-novych    : 5-infinite new : vm-zprav   : messages
07985  * vm-starou   : one old
07986  * vm-stare     : 2-4 old 
07987  * vm-starych   : 5-infinite old
07988  * jednu        : one   - falling 4. 
07989  * vm-no        : no  ( no messages )
07990  */
07991 
07992 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
07993 {
07994    int res;
07995    res = ast_play_and_wait(chan, "vm-youhave");
07996    if (!res) {
07997       if (vms->newmessages) {
07998          if (vms->newmessages == 1) {
07999             res = ast_play_and_wait(chan, "digits/jednu");
08000          } else {
08001             res = say_and_wait(chan, vms->newmessages, chan->language);
08002          }
08003          if (!res) {
08004             if ((vms->newmessages == 1))
08005                res = ast_play_and_wait(chan, "vm-novou");
08006             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08007                res = ast_play_and_wait(chan, "vm-nove");
08008             if (vms->newmessages > 4)
08009                res = ast_play_and_wait(chan, "vm-novych");
08010          }
08011          if (vms->oldmessages && !res)
08012             res = ast_play_and_wait(chan, "vm-and");
08013          else if (!res) {
08014             if ((vms->newmessages == 1))
08015                res = ast_play_and_wait(chan, "vm-zpravu");
08016             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08017                res = ast_play_and_wait(chan, "vm-zpravy");
08018             if (vms->newmessages > 4)
08019                res = ast_play_and_wait(chan, "vm-zprav");
08020          }
08021       }
08022       if (!res && vms->oldmessages) {
08023          res = say_and_wait(chan, vms->oldmessages, chan->language);
08024          if (!res) {
08025             if ((vms->oldmessages == 1))
08026                res = ast_play_and_wait(chan, "vm-starou");
08027             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08028                res = ast_play_and_wait(chan, "vm-stare");
08029             if (vms->oldmessages > 4)
08030                res = ast_play_and_wait(chan, "vm-starych");
08031          }
08032          if (!res) {
08033             if ((vms->oldmessages == 1))
08034                res = ast_play_and_wait(chan, "vm-zpravu");
08035             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08036                res = ast_play_and_wait(chan, "vm-zpravy");
08037             if (vms->oldmessages > 4)
08038                res = ast_play_and_wait(chan, "vm-zprav");
08039          }
08040       }
08041       if (!res) {
08042          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08043             res = ast_play_and_wait(chan, "vm-no");
08044             if (!res)
08045                res = ast_play_and_wait(chan, "vm-zpravy");
08046          }
08047       }
08048    }
08049    return res;
08050 }
08051 
08052 /* CHINESE (Taiwan) syntax */
08053 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08054 {
08055    int res;
08056    /* Introduce messages they have */
08057    res = ast_play_and_wait(chan, "vm-you");
08058 
08059    if (!res && vms->newmessages) {
08060       res = ast_play_and_wait(chan, "vm-have");
08061       if (!res)
08062          res = say_and_wait(chan, vms->newmessages, chan->language);
08063       if (!res)
08064          res = ast_play_and_wait(chan, "vm-tong");
08065       if (!res)
08066          res = ast_play_and_wait(chan, "vm-INBOX");
08067       if (vms->oldmessages && !res)
08068          res = ast_play_and_wait(chan, "vm-and");
08069       else if (!res) 
08070          res = ast_play_and_wait(chan, "vm-messages");
08071    }
08072    if (!res && vms->oldmessages) {
08073       res = ast_play_and_wait(chan, "vm-have");
08074       if (!res)
08075          res = say_and_wait(chan, vms->oldmessages, chan->language);
08076       if (!res)
08077          res = ast_play_and_wait(chan, "vm-tong");
08078       if (!res)
08079          res = ast_play_and_wait(chan, "vm-Old");
08080       if (!res)
08081          res = ast_play_and_wait(chan, "vm-messages");
08082    }
08083    if (!res && !vms->oldmessages && !vms->newmessages) {
08084       res = ast_play_and_wait(chan, "vm-haveno");
08085       if (!res)
08086          res = ast_play_and_wait(chan, "vm-messages");
08087    }
08088    return res;
08089 }
08090 
08091 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08092 {
08093    char prefile[256];
08094    
08095    /* Notify the user that the temp greeting is set and give them the option to remove it */
08096    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08097    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08098       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08099       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08100          ast_play_and_wait(chan, "vm-tempgreetactive");
08101       }
08102       DISPOSE(prefile, -1);
08103    }
08104 
08105    /* Play voicemail intro - syntax is different for different languages */
08106    if (0) {
08107    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08108       return vm_intro_cs(chan, vms);
08109    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08110       static int deprecation_warning = 0;
08111       if (deprecation_warning++ % 10 == 0) {
08112          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08113       }
08114       return vm_intro_cs(chan, vms);
08115    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08116       return vm_intro_de(chan, vms);
08117    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08118       return vm_intro_es(chan, vms);
08119    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08120       return vm_intro_fr(chan, vms);
08121    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08122       return vm_intro_gr(chan, vms);
08123    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08124       return vm_intro_he(chan, vms);
08125    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08126       return vm_intro_it(chan, vms);
08127    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08128       return vm_intro_nl(chan, vms);
08129    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08130       return vm_intro_no(chan, vms);
08131    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08132       return vm_intro_pl(chan, vms);
08133    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08134       return vm_intro_pt_BR(chan, vms);
08135    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08136       return vm_intro_pt(chan, vms);
08137    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08138       return vm_intro_multilang(chan, vms, "n");
08139    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08140       return vm_intro_se(chan, vms);
08141    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08142       return vm_intro_multilang(chan, vms, "n");
08143    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08144       return vm_intro_zh(chan, vms);
08145    } else {                                             /* Default to ENGLISH */
08146       return vm_intro_en(chan, vms);
08147    }
08148 }
08149 
08150 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08151 {
08152    int res = 0;
08153    /* Play instructions and wait for new command */
08154    while (!res) {
08155       if (vms->starting) {
08156          if (vms->lastmsg > -1) {
08157             if (skipadvanced)
08158                res = ast_play_and_wait(chan, "vm-onefor-full");
08159             else
08160                res = ast_play_and_wait(chan, "vm-onefor");
08161             if (!res)
08162                res = vm_play_folder_name(chan, vms->vmbox);
08163          }
08164          if (!res) {
08165             if (skipadvanced)
08166                res = ast_play_and_wait(chan, "vm-opts-full");
08167             else
08168                res = ast_play_and_wait(chan, "vm-opts");
08169          }
08170       } else {
08171          /* Added for additional help */
08172          if (skipadvanced) {
08173             res = ast_play_and_wait(chan, "vm-onefor-full");
08174             if (!res)
08175                res = vm_play_folder_name(chan, vms->vmbox);
08176             res = ast_play_and_wait(chan, "vm-opts-full");
08177          }
08178          /* Logic:
08179           * If the current message is not the first OR
08180           * if we're listening to the first new message and there are
08181           * also urgent messages, then prompt for navigation to the
08182           * previous message
08183           */
08184          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08185             res = ast_play_and_wait(chan, "vm-prev");
08186          }
08187          if (!res && !skipadvanced)
08188             res = ast_play_and_wait(chan, "vm-advopts");
08189          if (!res)
08190             res = ast_play_and_wait(chan, "vm-repeat");
08191          /* Logic:
08192           * If we're not listening to the last message OR
08193           * we're listening to the last urgent message and there are
08194           * also new non-urgent messages, then prompt for navigation
08195           * to the next message
08196           */
08197          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08198             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08199             res = ast_play_and_wait(chan, "vm-next");
08200          }
08201          if (!res) {
08202             if (!vms->deleted[vms->curmsg])
08203                res = ast_play_and_wait(chan, "vm-delete");
08204             else
08205                res = ast_play_and_wait(chan, "vm-undelete");
08206             if (!res)
08207                res = ast_play_and_wait(chan, "vm-toforward");
08208             if (!res)
08209                res = ast_play_and_wait(chan, "vm-savemessage");
08210          }
08211       }
08212       if (!res) {
08213          res = ast_play_and_wait(chan, "vm-helpexit");
08214       }
08215       if (!res)
08216          res = ast_waitfordigit(chan, 6000);
08217       if (!res) {
08218          vms->repeats++;
08219          if (vms->repeats > 2) {
08220             res = 't';
08221          }
08222       }
08223    }
08224    return res;
08225 }
08226 
08227 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08228 {
08229    int res = 0;
08230    /* Play instructions and wait for new command */
08231    while (!res) {
08232       if (vms->lastmsg > -1) {
08233          res = ast_play_and_wait(chan, "vm-listen");
08234          if (!res)
08235             res = vm_play_folder_name(chan, vms->vmbox);
08236          if (!res)
08237             res = ast_play_and_wait(chan, "press");
08238          if (!res)
08239             res = ast_play_and_wait(chan, "digits/1");
08240       }
08241       if (!res)
08242          res = ast_play_and_wait(chan, "vm-opts");
08243       if (!res) {
08244          vms->starting = 0;
08245          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08246       }
08247    }
08248    return res;
08249 }
08250 
08251 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08252 {
08253    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08254       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08255    } else {             /* Default to ENGLISH */
08256       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08257    }
08258 }
08259 
08260 
08261 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08262 {
08263    int cmd = 0;
08264    int duration = 0;
08265    int tries = 0;
08266    char newpassword[80] = "";
08267    char newpassword2[80] = "";
08268    char prefile[PATH_MAX] = "";
08269    unsigned char buf[256];
08270    int bytes=0;
08271 
08272    if (ast_adsi_available(chan)) {
08273       bytes += adsi_logo(buf + bytes);
08274       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08275       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08276       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08277       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08278       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08279    }
08280 
08281    /* First, have the user change their password 
08282       so they won't get here again */
08283    for (;;) {
08284       newpassword[1] = '\0';
08285       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08286       if (cmd == '#')
08287          newpassword[0] = '\0';
08288       if (cmd < 0 || cmd == 't' || cmd == '#')
08289          return cmd;
08290       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08291       if (cmd < 0 || cmd == 't' || cmd == '#')
08292          return cmd;
08293       cmd = check_password(vmu, newpassword); /* perform password validation */
08294       if (cmd != 0) {
08295          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08296          cmd = ast_play_and_wait(chan, vm_invalid_password);
08297       } else {
08298          newpassword2[1] = '\0';
08299          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08300          if (cmd == '#')
08301             newpassword2[0] = '\0';
08302          if (cmd < 0 || cmd == 't' || cmd == '#')
08303             return cmd;
08304          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08305          if (cmd < 0 || cmd == 't' || cmd == '#')
08306             return cmd;
08307          if (!strcmp(newpassword, newpassword2))
08308             break;
08309          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08310          cmd = ast_play_and_wait(chan, vm_mismatch);
08311       }
08312       if (++tries == 3)
08313          return -1;
08314       if (cmd != 0) {
08315          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08316       }
08317    }
08318    if (pwdchange & PWDCHANGE_INTERNAL)
08319       vm_change_password(vmu, newpassword);
08320    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08321       vm_change_password_shell(vmu, newpassword);
08322 
08323    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08324    cmd = ast_play_and_wait(chan, vm_passchanged);
08325 
08326    /* If forcename is set, have the user record their name */  
08327    if (ast_test_flag(vmu, VM_FORCENAME)) {
08328       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08329       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08330          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08331          if (cmd < 0 || cmd == 't' || cmd == '#')
08332             return cmd;
08333       }
08334    }
08335 
08336    /* If forcegreetings is set, have the user record their greetings */
08337    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08338       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08339       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08340          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08341          if (cmd < 0 || cmd == 't' || cmd == '#')
08342             return cmd;
08343       }
08344 
08345       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08346       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08347          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08348          if (cmd < 0 || cmd == 't' || cmd == '#')
08349             return cmd;
08350       }
08351    }
08352 
08353    return cmd;
08354 }
08355 
08356 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08357 {
08358    int cmd = 0;
08359    int retries = 0;
08360    int duration = 0;
08361    char newpassword[80] = "";
08362    char newpassword2[80] = "";
08363    char prefile[PATH_MAX] = "";
08364    unsigned char buf[256];
08365    int bytes=0;
08366 
08367    if (ast_adsi_available(chan)) {
08368       bytes += adsi_logo(buf + bytes);
08369       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08370       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08371       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08372       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08373       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08374    }
08375    while ((cmd >= 0) && (cmd != 't')) {
08376       if (cmd)
08377          retries = 0;
08378       switch (cmd) {
08379       case '1': /* Record your unavailable message */
08380          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08381          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08382          break;
08383       case '2':  /* Record your busy message */
08384          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08385          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08386          break;
08387       case '3': /* Record greeting */
08388          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08389          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08390          break;
08391       case '4':  /* manage the temporary greeting */
08392          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08393          break;
08394       case '5': /* change password */
08395          if (vmu->password[0] == '-') {
08396             cmd = ast_play_and_wait(chan, "vm-no");
08397             break;
08398          }
08399          newpassword[1] = '\0';
08400          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08401          if (cmd == '#')
08402             newpassword[0] = '\0';
08403          else {
08404             if (cmd < 0)
08405                break;
08406             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08407                break;
08408             }
08409          }
08410          cmd = check_password(vmu, newpassword); /* perform password validation */
08411          if (cmd != 0) {
08412             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08413             cmd = ast_play_and_wait(chan, vm_invalid_password);
08414             if (!cmd) {
08415                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08416             }
08417             break;
08418          }
08419          newpassword2[1] = '\0';
08420          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08421          if (cmd == '#')
08422             newpassword2[0] = '\0';
08423          else {
08424             if (cmd < 0)
08425                break;
08426 
08427             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08428                break;
08429             }
08430          }
08431          if (strcmp(newpassword, newpassword2)) {
08432             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08433             cmd = ast_play_and_wait(chan, vm_mismatch);
08434             if (!cmd) {
08435                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08436             }
08437             break;
08438          }
08439          if (pwdchange & PWDCHANGE_INTERNAL)
08440             vm_change_password(vmu, newpassword);
08441          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08442             vm_change_password_shell(vmu, newpassword);
08443 
08444          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08445          cmd = ast_play_and_wait(chan, vm_passchanged);
08446          break;
08447       case '*': 
08448          cmd = 't';
08449          break;
08450       default: 
08451          cmd = 0;
08452          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08453          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08454          if (ast_fileexists(prefile, NULL, NULL)) {
08455             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08456          }
08457          DISPOSE(prefile, -1);
08458          if (!cmd) {
08459             cmd = ast_play_and_wait(chan, "vm-options");
08460          }
08461          if (!cmd) {
08462             cmd = ast_waitfordigit(chan,6000);
08463          }
08464          if (!cmd) {
08465             retries++;
08466          }
08467          if (retries > 3) {
08468             cmd = 't';
08469          }
08470       }
08471    }
08472    if (cmd == 't')
08473       cmd = 0;
08474    return cmd;
08475 }
08476 
08477 /*!
08478  * \brief The handler for 'record a temporary greeting'. 
08479  * \param chan
08480  * \param vmu
08481  * \param vms
08482  * \param fmtc
08483  * \param record_gain
08484  *
08485  * This is option 4 from the mailbox options menu.
08486  * This function manages the following promptings:
08487  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08488  * 2: remove (delete) the temporary greeting.
08489  * *: return to the main menu.
08490  *
08491  * \return zero on success, -1 on error.
08492  */
08493 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08494 {
08495    int cmd = 0;
08496    int retries = 0;
08497    int duration = 0;
08498    char prefile[PATH_MAX] = "";
08499    unsigned char buf[256];
08500    int bytes = 0;
08501 
08502    if (ast_adsi_available(chan)) {
08503       bytes += adsi_logo(buf + bytes);
08504       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08505       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08506       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08507       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08508       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08509    }
08510 
08511    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08512    while ((cmd >= 0) && (cmd != 't')) {
08513       if (cmd)
08514          retries = 0;
08515       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08516       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08517          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08518          cmd = 't';  
08519       } else {
08520          switch (cmd) {
08521          case '1':
08522             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08523             break;
08524          case '2':
08525             DELETE(prefile, -1, prefile, vmu);
08526             ast_play_and_wait(chan, "vm-tempremoved");
08527             cmd = 't';  
08528             break;
08529          case '*': 
08530             cmd = 't';
08531             break;
08532          default:
08533             cmd = ast_play_and_wait(chan,
08534                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08535                   "vm-tempgreeting2" : "vm-tempgreeting");
08536             if (!cmd)
08537                cmd = ast_waitfordigit(chan,6000);
08538             if (!cmd)
08539                retries++;
08540             if (retries > 3)
08541                cmd = 't';
08542          }
08543       }
08544       DISPOSE(prefile, -1);
08545    }
08546    if (cmd == 't')
08547       cmd = 0;
08548    return cmd;
08549 }
08550 
08551 /*!
08552  * \brief Greek syntax for 'You have N messages' greeting.
08553  * \param chan
08554  * \param vms
08555  * \param vmu
08556  *
08557  * \return zero on success, -1 on error.
08558  */   
08559 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08560 {
08561    int cmd=0;
08562 
08563    if (vms->lastmsg > -1) {
08564       cmd = play_message(chan, vmu, vms);
08565    } else {
08566       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08567       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08568          if (!cmd) {
08569             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08570             cmd = ast_play_and_wait(chan, vms->fn);
08571          }
08572          if (!cmd)
08573             cmd = ast_play_and_wait(chan, "vm-messages");
08574       } else {
08575          if (!cmd)
08576             cmd = ast_play_and_wait(chan, "vm-messages");
08577          if (!cmd) {
08578             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08579             cmd = ast_play_and_wait(chan, vms->fn);
08580          }
08581       }
08582    } 
08583    return cmd;
08584 }
08585 
08586 /* Hebrew Syntax */
08587 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08588 {
08589    int cmd = 0;
08590 
08591    if (vms->lastmsg > -1) {
08592       cmd = play_message(chan, vmu, vms);
08593    } else {
08594       if (!strcasecmp(vms->fn, "INBOX")) {
08595          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08596       } else {
08597          cmd = ast_play_and_wait(chan, "vm-nomessages");
08598       }
08599    }
08600    return cmd;
08601 }
08602 
08603 /*! 
08604  * \brief Default English syntax for 'You have N messages' greeting.
08605  * \param chan
08606  * \param vms
08607  * \param vmu
08608  *
08609  * \return zero on success, -1 on error.
08610  */
08611 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08612 {
08613    int cmd=0;
08614 
08615    if (vms->lastmsg > -1) {
08616       cmd = play_message(chan, vmu, vms);
08617    } else {
08618       cmd = ast_play_and_wait(chan, "vm-youhave");
08619       if (!cmd) 
08620          cmd = ast_play_and_wait(chan, "vm-no");
08621       if (!cmd) {
08622          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08623          cmd = ast_play_and_wait(chan, vms->fn);
08624       }
08625       if (!cmd)
08626          cmd = ast_play_and_wait(chan, "vm-messages");
08627    }
08628    return cmd;
08629 }
08630 
08631 /*! 
08632  *\brief Italian syntax for 'You have N messages' greeting.
08633  * \param chan
08634  * \param vms
08635  * \param vmu
08636  *
08637  * \return zero on success, -1 on error.
08638  */
08639 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08640 {
08641    int cmd=0;
08642 
08643    if (vms->lastmsg > -1) {
08644       cmd = play_message(chan, vmu, vms);
08645    } else {
08646       cmd = ast_play_and_wait(chan, "vm-no");
08647       if (!cmd)
08648          cmd = ast_play_and_wait(chan, "vm-message");
08649       if (!cmd) {
08650          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08651          cmd = ast_play_and_wait(chan, vms->fn);
08652       }
08653    }
08654    return cmd;
08655 }
08656 
08657 /*! 
08658  * \brief Spanish syntax for 'You have N messages' greeting.
08659  * \param chan
08660  * \param vms
08661  * \param vmu
08662  *
08663  * \return zero on success, -1 on error.
08664  */
08665 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08666 {
08667    int cmd=0;
08668 
08669    if (vms->lastmsg > -1) {
08670       cmd = play_message(chan, vmu, vms);
08671    } else {
08672       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08673       if (!cmd)
08674          cmd = ast_play_and_wait(chan, "vm-messages");
08675       if (!cmd) {
08676          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08677          cmd = ast_play_and_wait(chan, vms->fn);
08678       }
08679    }
08680    return cmd;
08681 }
08682 
08683 /*! 
08684  * \brief Portuguese syntax for 'You have N messages' greeting.
08685  * \param chan
08686  * \param vms
08687  * \param vmu
08688  *
08689  * \return zero on success, -1 on error.
08690  */
08691 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08692 {
08693    int cmd=0;
08694 
08695    if (vms->lastmsg > -1) {
08696       cmd = play_message(chan, vmu, vms);
08697    } else {
08698       cmd = ast_play_and_wait(chan, "vm-no");
08699       if (!cmd) {
08700          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08701          cmd = ast_play_and_wait(chan, vms->fn);
08702       }
08703       if (!cmd)
08704          cmd = ast_play_and_wait(chan, "vm-messages");
08705    }
08706    return cmd;
08707 }
08708 
08709 /*! 
08710  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08711  * \param chan
08712  * \param vms
08713  * \param vmu
08714  *
08715  * \return zero on success, -1 on error.
08716  */
08717 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08718 {
08719    int cmd=0;
08720 
08721    if (vms->lastmsg > -1) {
08722       cmd = play_message(chan, vmu, vms);
08723    } else {
08724       cmd = ast_play_and_wait(chan, "vm-you");
08725       if (!cmd) 
08726          cmd = ast_play_and_wait(chan, "vm-haveno");
08727       if (!cmd)
08728          cmd = ast_play_and_wait(chan, "vm-messages");
08729       if (!cmd) {
08730          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08731          cmd = ast_play_and_wait(chan, vms->fn);
08732       }
08733    }
08734    return cmd;
08735 }
08736 
08737 /*!
08738  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
08739  * \param chan The channel for the current user. We read the language property from this.
08740  * \param vms passed into the language-specific vm_browse_messages function.
08741  * \param vmu passed into the language-specific vm_browse_messages function.
08742  * 
08743  * The method to be invoked is determined by the value of language code property in the user's channel.
08744  * The default (when unable to match) is to use english.
08745  *
08746  * \return zero on success, -1 on error.
08747  */
08748 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08749 {
08750    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
08751       return vm_browse_messages_es(chan, vms, vmu);
08752    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
08753       return vm_browse_messages_gr(chan, vms, vmu);
08754    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
08755       return vm_browse_messages_he(chan, vms, vmu);
08756    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
08757       return vm_browse_messages_it(chan, vms, vmu);
08758    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
08759       return vm_browse_messages_pt(chan, vms, vmu);
08760    } else if (!strncasecmp(chan->language, "zh", 2)) {
08761       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
08762    } else {                                             /* Default to English syntax */
08763       return vm_browse_messages_en(chan, vms, vmu);
08764    }
08765 }
08766 
08767 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
08768          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
08769          int skipuser, int max_logins, int silent)
08770 {
08771    int useadsi=0, valid=0, logretries=0;
08772    char password[AST_MAX_EXTENSION]="", *passptr;
08773    struct ast_vm_user vmus, *vmu = NULL;
08774 
08775    /* If ADSI is supported, setup login screen */
08776    adsi_begin(chan, &useadsi);
08777    if (!skipuser && useadsi)
08778       adsi_login(chan);
08779    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
08780       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
08781       return -1;
08782    }
08783    
08784    /* Authenticate them and get their mailbox/password */
08785    
08786    while (!valid && (logretries < max_logins)) {
08787       /* Prompt for, and read in the username */
08788       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
08789          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
08790          return -1;
08791       }
08792       if (ast_strlen_zero(mailbox)) {
08793          if (chan->cid.cid_num) {
08794             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
08795          } else {
08796             ast_verb(3,"Username not entered\n");  
08797             return -1;
08798          }
08799       }
08800       if (useadsi)
08801          adsi_password(chan);
08802 
08803       if (!ast_strlen_zero(prefix)) {
08804          char fullusername[80] = "";
08805          ast_copy_string(fullusername, prefix, sizeof(fullusername));
08806          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
08807          ast_copy_string(mailbox, fullusername, mailbox_size);
08808       }
08809 
08810       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
08811       vmu = find_user(&vmus, context, mailbox);
08812       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
08813          /* saved password is blank, so don't bother asking */
08814          password[0] = '\0';
08815       } else {
08816          if (ast_streamfile(chan, vm_password, chan->language)) {
08817             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
08818             return -1;
08819          }
08820          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
08821             ast_log(AST_LOG_WARNING, "Unable to read password\n");
08822             return -1;
08823          }
08824       }
08825 
08826       if (vmu) {
08827          passptr = vmu->password;
08828          if (passptr[0] == '-') passptr++;
08829       }
08830       if (vmu && !strcmp(passptr, password))
08831          valid++;
08832       else {
08833          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
08834          if (!ast_strlen_zero(prefix))
08835             mailbox[0] = '\0';
08836       }
08837       logretries++;
08838       if (!valid) {
08839          if (skipuser || logretries >= max_logins) {
08840             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
08841                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
08842                return -1;
08843             }
08844          } else {
08845             if (useadsi)
08846                adsi_login(chan);
08847             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
08848                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
08849                return -1;
08850             }
08851          }
08852          if (ast_waitstream(chan, "")) /* Channel is hung up */
08853             return -1;
08854       }
08855    }
08856    if (!valid && (logretries >= max_logins)) {
08857       ast_stopstream(chan);
08858       ast_play_and_wait(chan, "vm-goodbye");
08859       return -1;
08860    }
08861    if (vmu && !skipuser) {
08862       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
08863    }
08864    return 0;
08865 }
08866 
08867 static int vm_execmain(struct ast_channel *chan, void *data)
08868 {
08869    /* XXX This is, admittedly, some pretty horrendous code.  For some
08870       reason it just seemed a lot easier to do with GOTO's.  I feel
08871       like I'm back in my GWBASIC days. XXX */
08872    int res=-1;
08873    int cmd=0;
08874    int valid = 0;
08875    char prefixstr[80] ="";
08876    char ext_context[256]="";
08877    int box;
08878    int useadsi = 0;
08879    int skipuser = 0;
08880    struct vm_state vms;
08881    struct ast_vm_user *vmu = NULL, vmus;
08882    char *context=NULL;
08883    int silentexit = 0;
08884    struct ast_flags flags = { 0 };
08885    signed char record_gain = 0;
08886    int play_auto = 0;
08887    int play_folder = 0;
08888    int in_urgent = 0;
08889 #ifdef IMAP_STORAGE
08890    int deleted = 0;
08891 #endif
08892 
08893    /* Add the vm_state to the active list and keep it active */
08894    memset(&vms, 0, sizeof(vms));
08895 
08896    vms.lastmsg = -1;
08897 
08898    memset(&vmus, 0, sizeof(vmus));
08899 
08900    if (chan->_state != AST_STATE_UP) {
08901       ast_debug(1, "Before ast_answer\n");
08902       ast_answer(chan);
08903    }
08904 
08905    if (!ast_strlen_zero(data)) {
08906       char *opts[OPT_ARG_ARRAY_SIZE];
08907       char *parse;
08908       AST_DECLARE_APP_ARGS(args,
08909          AST_APP_ARG(argv0);
08910          AST_APP_ARG(argv1);
08911       );
08912 
08913       parse = ast_strdupa(data);
08914 
08915       AST_STANDARD_APP_ARGS(args, parse);
08916 
08917       if (args.argc == 2) {
08918          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
08919             return -1;
08920          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
08921             int gain;
08922             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
08923                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
08924                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
08925                   return -1;
08926                } else {
08927                   record_gain = (signed char) gain;
08928                }
08929             } else {
08930                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
08931             }
08932          }
08933          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
08934             play_auto = 1;
08935             if (opts[OPT_ARG_PLAYFOLDER]) {
08936                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
08937                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
08938                }
08939             } else {
08940                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
08941             }  
08942             if ( play_folder > 9 || play_folder < 0) {
08943                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
08944                play_folder = 0;
08945             }
08946          }
08947       } else {
08948          /* old style options parsing */
08949          while (*(args.argv0)) {
08950             if (*(args.argv0) == 's')
08951                ast_set_flag(&flags, OPT_SILENT);
08952             else if (*(args.argv0) == 'p')
08953                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
08954             else 
08955                break;
08956             (args.argv0)++;
08957          }
08958 
08959       }
08960 
08961       valid = ast_test_flag(&flags, OPT_SILENT);
08962 
08963       if ((context = strchr(args.argv0, '@')))
08964          *context++ = '\0';
08965 
08966       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
08967          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
08968       else
08969          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
08970 
08971       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
08972          skipuser++;
08973       else
08974          valid = 0;
08975    }
08976 
08977    if (!valid)
08978       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
08979 
08980    ast_debug(1, "After vm_authenticate\n");
08981    if (!res) {
08982       valid = 1;
08983       if (!skipuser)
08984          vmu = &vmus;
08985    } else {
08986       res = 0;
08987    }
08988 
08989    /* If ADSI is supported, setup login screen */
08990    adsi_begin(chan, &useadsi);
08991 
08992    if (!valid) {
08993       goto out;
08994    }
08995 
08996 #ifdef IMAP_STORAGE
08997    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
08998    pthread_setspecific(ts_vmstate.key, &vms);
08999 
09000    vms.interactive = 1;
09001    vms.updated = 1;
09002    if (vmu)
09003       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09004    vmstate_insert(&vms);
09005    init_vm_state(&vms);
09006 #endif
09007    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09008       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09009       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09010       return -1;
09011    }
09012    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09013       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09014       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09015       return -1;
09016    }
09017    
09018    /* Set language from config to override channel language */
09019    if (!ast_strlen_zero(vmu->language))
09020       ast_string_field_set(chan, language, vmu->language);
09021 
09022    /* Retrieve urgent, old and new message counts */
09023    ast_debug(1, "Before open_mailbox\n");
09024    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09025    if (res == ERROR_LOCK_PATH)
09026       goto out;
09027    vms.oldmessages = vms.lastmsg + 1;
09028    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09029    /* check INBOX */
09030    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09031    if (res == ERROR_LOCK_PATH)
09032       goto out;
09033    vms.newmessages = vms.lastmsg + 1;
09034    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09035    /* Start in Urgent */
09036    in_urgent = 1;
09037    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09038    if (res == ERROR_LOCK_PATH)
09039       goto out;
09040    vms.urgentmessages = vms.lastmsg + 1;
09041    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09042 
09043    /* Select proper mailbox FIRST!! */
09044    if (play_auto) {
09045       if (vms.urgentmessages) {
09046          in_urgent = 1;
09047          res = open_mailbox(&vms, vmu, 11);
09048       } else {
09049          in_urgent = 0;
09050          res = open_mailbox(&vms, vmu, play_folder);
09051       }
09052       if (res == ERROR_LOCK_PATH)
09053          goto out;
09054 
09055       /* If there are no new messages, inform the user and hangup */
09056       if (vms.lastmsg == -1) {
09057          in_urgent = 0;
09058          cmd = vm_browse_messages(chan, &vms, vmu);
09059          res = 0;
09060          goto out;
09061       }
09062    } else {
09063       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09064          /* If we only have old messages start here */
09065          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09066          in_urgent = 0;
09067          play_folder = 1;
09068          if (res == ERROR_LOCK_PATH)
09069             goto out;
09070       } else if (!vms.urgentmessages && vms.newmessages) {
09071          /* If we have new messages but none are urgent */
09072          in_urgent = 0;
09073          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09074          if (res == ERROR_LOCK_PATH)
09075             goto out;
09076       }
09077    }
09078 
09079    if (useadsi)
09080       adsi_status(chan, &vms);
09081    res = 0;
09082 
09083    /* Check to see if this is a new user */
09084    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09085       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09086       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09087          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09088       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09089       if ((cmd == 't') || (cmd == '#')) {
09090          /* Timeout */
09091          res = 0;
09092          goto out;
09093       } else if (cmd < 0) {
09094          /* Hangup */
09095          res = -1;
09096          goto out;
09097       }
09098    }
09099 #ifdef IMAP_STORAGE
09100       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09101       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09102          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09103          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09104       }
09105       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09106       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09107          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09108          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09109       }
09110 #endif
09111    if (play_auto) {
09112       cmd = '1';
09113    } else {
09114       cmd = vm_intro(chan, vmu, &vms);
09115    }
09116 
09117    vms.repeats = 0;
09118    vms.starting = 1;
09119    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09120       /* Run main menu */
09121       switch (cmd) {
09122       case '1': /* First message */
09123          vms.curmsg = 0;
09124          /* Fall through */
09125       case '5': /* Play current message */
09126          cmd = vm_browse_messages(chan, &vms, vmu);
09127          break;
09128       case '2': /* Change folders */
09129          if (useadsi)
09130             adsi_folders(chan, 0, "Change to folder...");
09131          cmd = get_folder2(chan, "vm-changeto", 0);
09132          if (cmd == '#') {
09133             cmd = 0;
09134          } else if (cmd > 0) {
09135             cmd = cmd - '0';
09136             res = close_mailbox(&vms, vmu);
09137             if (res == ERROR_LOCK_PATH)
09138                goto out;
09139             /* If folder is not urgent, set in_urgent to zero! */
09140             if (cmd != 11) in_urgent = 0;
09141             res = open_mailbox(&vms, vmu, cmd);
09142             if (res == ERROR_LOCK_PATH)
09143                goto out;
09144             play_folder = cmd;
09145             cmd = 0;
09146          }
09147          if (useadsi)
09148             adsi_status2(chan, &vms);
09149             
09150          if (!cmd)
09151             cmd = vm_play_folder_name(chan, vms.vmbox);
09152 
09153          vms.starting = 1;
09154          break;
09155       case '3': /* Advanced options */
09156          cmd = 0;
09157          vms.repeats = 0;
09158          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09159             switch (cmd) {
09160             case '1': /* Reply */
09161                if (vms.lastmsg > -1 && !vms.starting) {
09162                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09163                   if (cmd == ERROR_LOCK_PATH) {
09164                      res = cmd;
09165                      goto out;
09166                   }
09167                } else
09168                   cmd = ast_play_and_wait(chan, "vm-sorry");
09169                cmd = 't';
09170                break;
09171             case '2': /* Callback */
09172                if (!vms.starting)
09173                   ast_verb(3, "Callback Requested\n");
09174                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09175                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09176                   if (cmd == 9) {
09177                      silentexit = 1;
09178                      goto out;
09179                   } else if (cmd == ERROR_LOCK_PATH) {
09180                      res = cmd;
09181                      goto out;
09182                   }
09183                } else 
09184                   cmd = ast_play_and_wait(chan, "vm-sorry");
09185                cmd = 't';
09186                break;
09187             case '3': /* Envelope */
09188                if (vms.lastmsg > -1 && !vms.starting) {
09189                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09190                   if (cmd == ERROR_LOCK_PATH) {
09191                      res = cmd;
09192                      goto out;
09193                   }
09194                } else
09195                   cmd = ast_play_and_wait(chan, "vm-sorry");
09196                cmd = 't';
09197                break;
09198             case '4': /* Dialout */
09199                if (!ast_strlen_zero(vmu->dialout)) {
09200                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09201                   if (cmd == 9) {
09202                      silentexit = 1;
09203                      goto out;
09204                   }
09205                } else 
09206                   cmd = ast_play_and_wait(chan, "vm-sorry");
09207                cmd = 't';
09208                break;
09209 
09210             case '5': /* Leave VoiceMail */
09211                if (ast_test_flag(vmu, VM_SVMAIL)) {
09212                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09213                   if (cmd == ERROR_LOCK_PATH) {
09214                      res = cmd;
09215                      ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
09216                      goto out;
09217                   }
09218                } else
09219                   cmd = ast_play_and_wait(chan,"vm-sorry");
09220                cmd='t';
09221                break;
09222                
09223             case '*': /* Return to main menu */
09224                cmd = 't';
09225                break;
09226 
09227             default:
09228                cmd = 0;
09229                if (!vms.starting) {
09230                   cmd = ast_play_and_wait(chan, "vm-toreply");
09231                }
09232                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09233                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09234                }
09235                if (!cmd && !vms.starting) {
09236                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09237                }
09238                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09239                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09240                }
09241                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09242                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09243                if (!cmd)
09244                   cmd = ast_play_and_wait(chan, "vm-starmain");
09245                if (!cmd)
09246                   cmd = ast_waitfordigit(chan,6000);
09247                if (!cmd)
09248                   vms.repeats++;
09249                if (vms.repeats > 3)
09250                   cmd = 't';
09251             }
09252          }
09253          if (cmd == 't') {
09254             cmd = 0;
09255             vms.repeats = 0;
09256          }
09257          break;
09258       case '4': /* Go to the previous message */
09259          if (vms.curmsg > 0) {
09260             vms.curmsg--;
09261             cmd = play_message(chan, vmu, &vms);
09262          } else {
09263             /* Check if we were listening to new
09264                messages.  If so, go to Urgent messages
09265                instead of saying "no more messages"
09266             */
09267             if (in_urgent == 0 && vms.urgentmessages > 0) {
09268                /* Check for Urgent messages */
09269                in_urgent = 1;
09270                res = close_mailbox(&vms, vmu);
09271                if (res == ERROR_LOCK_PATH)
09272                   goto out;
09273                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09274                if (res == ERROR_LOCK_PATH)
09275                   goto out;
09276                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09277                vms.curmsg = vms.lastmsg;
09278                if (vms.lastmsg < 0)
09279                   cmd = ast_play_and_wait(chan, "vm-nomore");
09280             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09281                vms.curmsg = vms.lastmsg;
09282                cmd = play_message(chan, vmu, &vms);
09283             } else {
09284                cmd = ast_play_and_wait(chan, "vm-nomore");
09285             }
09286          }
09287          break;
09288       case '6': /* Go to the next message */
09289          if (vms.curmsg < vms.lastmsg) {
09290             vms.curmsg++;
09291             cmd = play_message(chan, vmu, &vms);
09292          } else {
09293             if (in_urgent && vms.newmessages > 0) {
09294                /* Check if we were listening to urgent
09295                 * messages.  If so, go to regular new messages
09296                 * instead of saying "no more messages"
09297                 */
09298                in_urgent = 0;
09299                res = close_mailbox(&vms, vmu);
09300                if (res == ERROR_LOCK_PATH)
09301                   goto out;
09302                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09303                if (res == ERROR_LOCK_PATH)
09304                   goto out;
09305                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09306                vms.curmsg = -1;
09307                if (vms.lastmsg < 0) {
09308                   cmd = ast_play_and_wait(chan, "vm-nomore");
09309                }
09310             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09311                vms.curmsg = 0;
09312                cmd = play_message(chan, vmu, &vms);
09313             } else {
09314                cmd = ast_play_and_wait(chan, "vm-nomore");
09315             }
09316          }
09317          break;
09318       case '7': /* Delete the current message */
09319          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09320             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09321             if (useadsi)
09322                adsi_delete(chan, &vms);
09323             if (vms.deleted[vms.curmsg]) {
09324                if (play_folder == 0) {
09325                   if (in_urgent) {
09326                      vms.urgentmessages--;
09327                   } else {
09328                      vms.newmessages--;
09329                   }
09330                }
09331                else if (play_folder == 1)
09332                   vms.oldmessages--;
09333                cmd = ast_play_and_wait(chan, "vm-deleted");
09334             } else {
09335                if (play_folder == 0) {
09336                   if (in_urgent) {
09337                      vms.urgentmessages++;
09338                   } else {
09339                      vms.newmessages++;
09340                   }
09341                }
09342                else if (play_folder == 1)
09343                   vms.oldmessages++;
09344                cmd = ast_play_and_wait(chan, "vm-undeleted");
09345             }
09346             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09347                if (vms.curmsg < vms.lastmsg) {
09348                   vms.curmsg++;
09349                   cmd = play_message(chan, vmu, &vms);
09350                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09351                   vms.curmsg = 0;
09352                   cmd = play_message(chan, vmu, &vms);
09353                } else {
09354                   /* Check if we were listening to urgent
09355                      messages.  If so, go to regular new messages
09356                      instead of saying "no more messages"
09357                   */
09358                   if (in_urgent == 1) {
09359                      /* Check for new messages */
09360                      in_urgent = 0;
09361                      res = close_mailbox(&vms, vmu);
09362                      if (res == ERROR_LOCK_PATH)
09363                         goto out;
09364                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09365                      if (res == ERROR_LOCK_PATH)
09366                         goto out;
09367                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09368                      vms.curmsg = -1;
09369                      if (vms.lastmsg < 0)
09370                         cmd = ast_play_and_wait(chan, "vm-nomore");
09371                   } else {
09372                      cmd = ast_play_and_wait(chan, "vm-nomore");
09373                   }
09374                }
09375             }
09376          } else /* Delete not valid if we haven't selected a message */
09377             cmd = 0;
09378 #ifdef IMAP_STORAGE
09379          deleted = 1;
09380 #endif
09381          break;
09382    
09383       case '8': /* Forward the current messgae */
09384          if (vms.lastmsg > -1) {
09385             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09386             if (cmd == ERROR_LOCK_PATH) {
09387                res = cmd;
09388                goto out;
09389             }
09390          } else {
09391             /* Check if we were listening to urgent
09392                messages.  If so, go to regular new messages
09393                instead of saying "no more messages"
09394             */
09395             if (in_urgent == 1 && vms.newmessages > 0) {
09396                /* Check for new messages */
09397                in_urgent = 0;
09398                res = close_mailbox(&vms, vmu);
09399                if (res == ERROR_LOCK_PATH)
09400                   goto out;
09401                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09402                if (res == ERROR_LOCK_PATH)
09403                   goto out;
09404                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09405                vms.curmsg = -1;
09406                if (vms.lastmsg < 0)
09407                   cmd = ast_play_and_wait(chan, "vm-nomore");
09408             } else {
09409                cmd = ast_play_and_wait(chan, "vm-nomore");
09410             }
09411          }
09412          break;
09413       case '9': /* Save message to folder */
09414          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09415             /* No message selected */
09416             cmd = 0;
09417             break;
09418          }
09419          if (useadsi)
09420             adsi_folders(chan, 1, "Save to folder...");
09421          cmd = get_folder2(chan, "vm-savefolder", 1);
09422          box = 0; /* Shut up compiler */
09423          if (cmd == '#') {
09424             cmd = 0;
09425             break;
09426          } else if (cmd > 0) {
09427             box = cmd = cmd - '0';
09428             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09429             if (cmd == ERROR_LOCK_PATH) {
09430                res = cmd;
09431                goto out;
09432 #ifndef IMAP_STORAGE
09433             } else if (!cmd) {
09434                vms.deleted[vms.curmsg] = 1;
09435 #endif
09436             } else {
09437                vms.deleted[vms.curmsg] = 0;
09438                vms.heard[vms.curmsg] = 0;
09439             }
09440          }
09441          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09442          if (useadsi)
09443             adsi_message(chan, &vms);
09444          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09445          if (!cmd) {
09446             cmd = ast_play_and_wait(chan, "vm-message");
09447             if (!cmd) 
09448                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09449             if (!cmd)
09450                cmd = ast_play_and_wait(chan, "vm-savedto");
09451             if (!cmd)
09452                cmd = vm_play_folder_name(chan, vms.fn);
09453          } else {
09454             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09455          }
09456          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09457             if (vms.curmsg < vms.lastmsg) {
09458                vms.curmsg++;
09459                cmd = play_message(chan, vmu, &vms);
09460             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09461                vms.curmsg = 0;
09462                cmd = play_message(chan, vmu, &vms);
09463             } else {
09464                /* Check if we were listening to urgent
09465                   messages.  If so, go to regular new messages
09466                   instead of saying "no more messages"
09467                */
09468                if (in_urgent == 1 && vms.newmessages > 0) {
09469                   /* Check for new messages */
09470                   in_urgent = 0;
09471                   res = close_mailbox(&vms, vmu);
09472                   if (res == ERROR_LOCK_PATH)
09473                      goto out;
09474                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09475                   if (res == ERROR_LOCK_PATH)
09476                      goto out;
09477                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09478                   vms.curmsg = -1;
09479                   if (vms.lastmsg < 0)
09480                      cmd = ast_play_and_wait(chan, "vm-nomore");
09481                } else {
09482                   cmd = ast_play_and_wait(chan, "vm-nomore");
09483                }
09484             }
09485          }
09486          break;
09487       case '*': /* Help */
09488          if (!vms.starting) {
09489             cmd = ast_play_and_wait(chan, "vm-onefor");
09490             if (!strncasecmp(chan->language, "he", 2)) {
09491                cmd = ast_play_and_wait(chan, "vm-for");
09492             }
09493             if (!cmd)
09494                cmd = vm_play_folder_name(chan, vms.vmbox);
09495             if (!cmd)
09496                cmd = ast_play_and_wait(chan, "vm-opts");
09497             if (!cmd)
09498                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09499          } else
09500             cmd = 0;
09501          break;
09502       case '0': /* Mailbox options */
09503          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09504          if (useadsi)
09505             adsi_status(chan, &vms);
09506          break;
09507       default: /* Nothing */
09508          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09509          break;
09510       }
09511    }
09512    if ((cmd == 't') || (cmd == '#')) {
09513       /* Timeout */
09514       res = 0;
09515    } else {
09516       /* Hangup */
09517       res = -1;
09518    }
09519 
09520 out:
09521    if (res > -1) {
09522       ast_stopstream(chan);
09523       adsi_goodbye(chan);
09524       if (valid) {
09525          if (silentexit)
09526             res = ast_play_and_wait(chan, "vm-dialout");
09527          else 
09528             res = ast_play_and_wait(chan, "vm-goodbye");
09529          if (res > 0)
09530             res = 0;
09531       }
09532       if (useadsi)
09533          ast_adsi_unload_session(chan);
09534    }
09535    if (vmu)
09536       close_mailbox(&vms, vmu);
09537    if (valid) {
09538       int new = 0, old = 0, urgent = 0;
09539       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09540       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09541       /* Urgent flag not passwd to externnotify here */
09542       run_externnotify(vmu->context, vmu->mailbox, NULL);
09543       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09544       queue_mwi_event(ext_context, urgent, new, old);
09545    }
09546 #ifdef IMAP_STORAGE
09547    /* expunge message - use UID Expunge if supported on IMAP server*/
09548    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09549    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09550       ast_mutex_lock(&vms.lock);
09551 #ifdef HAVE_IMAP_TK2006
09552       if (LEVELUIDPLUS (vms.mailstream)) {
09553          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09554       } else 
09555 #endif
09556          mail_expunge(vms.mailstream);
09557       ast_mutex_unlock(&vms.lock);
09558    }
09559    /*  before we delete the state, we should copy pertinent info
09560     *  back to the persistent model */
09561    if (vmu) {
09562       vmstate_delete(&vms);
09563    }
09564 #endif
09565    if (vmu)
09566       free_user(vmu);
09567    if (vms.deleted)
09568       ast_free(vms.deleted);
09569    if (vms.heard)
09570       ast_free(vms.heard);
09571 
09572 #ifdef IMAP_STORAGE
09573    pthread_setspecific(ts_vmstate.key, NULL);
09574 #endif
09575    return res;
09576 }
09577 
09578 static int vm_exec(struct ast_channel *chan, void *data)
09579 {
09580    int res = 0;
09581    char *tmp;
09582    struct leave_vm_options leave_options;
09583    struct ast_flags flags = { 0 };
09584    char *opts[OPT_ARG_ARRAY_SIZE];
09585    AST_DECLARE_APP_ARGS(args,
09586       AST_APP_ARG(argv0);
09587       AST_APP_ARG(argv1);
09588    );
09589    
09590    memset(&leave_options, 0, sizeof(leave_options));
09591 
09592    if (chan->_state != AST_STATE_UP)
09593       ast_answer(chan);
09594 
09595    if (!ast_strlen_zero(data)) {
09596       tmp = ast_strdupa(data);
09597       AST_STANDARD_APP_ARGS(args, tmp);
09598       if (args.argc == 2) {
09599          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09600             return -1;
09601          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09602          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09603             int gain;
09604 
09605             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09606                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09607                return -1;
09608             } else {
09609                leave_options.record_gain = (signed char) gain;
09610             }
09611          }
09612          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09613             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09614                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09615          }
09616       }
09617    } else {
09618       char temp[256];
09619       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09620       if (res < 0)
09621          return res;
09622       if (ast_strlen_zero(temp))
09623          return 0;
09624       args.argv0 = ast_strdupa(temp);
09625    }
09626 
09627    res = leave_voicemail(chan, args.argv0, &leave_options);
09628 
09629    if (res == ERROR_LOCK_PATH) {
09630       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09631       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09632       res = 0;
09633    }
09634 
09635    return res;
09636 }
09637 
09638 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09639 {
09640    struct ast_vm_user *vmu;
09641 
09642    AST_LIST_TRAVERSE(&users, vmu, list) {
09643       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09644          if (strcasecmp(vmu->context, context)) {
09645             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09646                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09647                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09648                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09649          }
09650          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09651          return NULL;
09652       }
09653       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09654          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09655          return NULL;
09656       }
09657    }
09658    
09659    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09660       return NULL;
09661    
09662    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09663    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09664 
09665    AST_LIST_INSERT_TAIL(&users, vmu, list);
09666    
09667    return vmu;
09668 }
09669 
09670 static int append_mailbox(const char *context, const char *box, const char *data)
09671 {
09672    /* Assumes lock is already held */
09673    char *tmp;
09674    char *stringp;
09675    char *s;
09676    struct ast_vm_user *vmu;
09677    char *mailbox_full;
09678    int new = 0, old = 0, urgent = 0;
09679 
09680    tmp = ast_strdupa(data);
09681 
09682    if (!(vmu = find_or_create(context, box)))
09683       return -1;
09684    
09685    populate_defaults(vmu);
09686 
09687    stringp = tmp;
09688    if ((s = strsep(&stringp, ","))) 
09689       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09690    if (stringp && (s = strsep(&stringp, ","))) 
09691       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09692    if (stringp && (s = strsep(&stringp, ","))) 
09693       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09694    if (stringp && (s = strsep(&stringp, ","))) 
09695       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09696    if (stringp && (s = strsep(&stringp, ","))) 
09697       apply_options(vmu, s);
09698 
09699    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09700    strcpy(mailbox_full, box);
09701    strcat(mailbox_full, "@");
09702    strcat(mailbox_full, context);
09703 
09704    inboxcount2(mailbox_full, &urgent, &new, &old);
09705    queue_mwi_event(mailbox_full, urgent, new, old);
09706 
09707    return 0;
09708 }
09709 
09710 static int vm_box_exists(struct ast_channel *chan, void *data) 
09711 {
09712    struct ast_vm_user svm;
09713    char *context, *box;
09714    AST_DECLARE_APP_ARGS(args,
09715       AST_APP_ARG(mbox);
09716       AST_APP_ARG(options);
09717    );
09718    static int dep_warning = 0;
09719 
09720    if (ast_strlen_zero(data)) {
09721       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
09722       return -1;
09723    }
09724 
09725    if (!dep_warning) {
09726       dep_warning = 1;
09727       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
09728    }
09729 
09730    box = ast_strdupa(data);
09731 
09732    AST_STANDARD_APP_ARGS(args, box);
09733 
09734    if (args.options) {
09735    }
09736 
09737    if ((context = strchr(args.mbox, '@'))) {
09738       *context = '\0';
09739       context++;
09740    }
09741 
09742    if (find_user(&svm, context, args.mbox)) {
09743       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
09744    } else
09745       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
09746 
09747    return 0;
09748 }
09749 
09750 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
09751 {
09752    struct ast_vm_user svm;
09753    AST_DECLARE_APP_ARGS(arg,
09754       AST_APP_ARG(mbox);
09755       AST_APP_ARG(context);
09756    );
09757 
09758    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
09759 
09760    if (ast_strlen_zero(arg.mbox)) {
09761       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
09762       return -1;
09763    }
09764 
09765    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
09766    return 0;
09767 }
09768 
09769 static struct ast_custom_function mailbox_exists_acf = {
09770    .name = "MAILBOX_EXISTS",
09771    .synopsis = "Tell if a mailbox is configured",
09772    .desc =
09773 "Returns a boolean of whether the corresponding mailbox exists.  If context\n"
09774 "is not specified, defaults to the \"default\" context.\n",
09775    .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
09776    .read = acf_mailbox_exists,
09777 };
09778 
09779 static int vmauthenticate(struct ast_channel *chan, void *data)
09780 {
09781    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
09782    struct ast_vm_user vmus;
09783    char *options = NULL;
09784    int silent = 0, skipuser = 0;
09785    int res = -1;
09786    
09787    if (s) {
09788       s = ast_strdupa(s);
09789       user = strsep(&s, ",");
09790       options = strsep(&s, ",");
09791       if (user) {
09792          s = user;
09793          user = strsep(&s, "@");
09794          context = strsep(&s, "");
09795          if (!ast_strlen_zero(user))
09796             skipuser++;
09797          ast_copy_string(mailbox, user, sizeof(mailbox));
09798       }
09799    }
09800 
09801    if (options) {
09802       silent = (strchr(options, 's')) != NULL;
09803    }
09804 
09805    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
09806       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
09807       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
09808       ast_play_and_wait(chan, "auth-thankyou");
09809       res = 0;
09810    }
09811 
09812    return res;
09813 }
09814 
09815 static char *show_users_realtime(int fd, const char *context)
09816 {
09817    struct ast_config *cfg;
09818    const char *cat = NULL;
09819 
09820    if (!(cfg = ast_load_realtime_multientry("voicemail", 
09821       "context", context, SENTINEL))) {
09822       return CLI_FAILURE;
09823    }
09824 
09825    ast_cli(fd,
09826       "\n"
09827       "=============================================================\n"
09828       "=== Configured Voicemail Users ==============================\n"
09829       "=============================================================\n"
09830       "===\n");
09831 
09832    while ((cat = ast_category_browse(cfg, cat))) {
09833       struct ast_variable *var = NULL;
09834       ast_cli(fd,
09835          "=== Mailbox ...\n"
09836          "===\n");
09837       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
09838          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
09839       ast_cli(fd,
09840          "===\n"
09841          "=== ---------------------------------------------------------\n"
09842          "===\n");
09843    }
09844 
09845    ast_cli(fd,
09846       "=============================================================\n"
09847       "\n");
09848 
09849    ast_config_destroy(cfg);
09850 
09851    return CLI_SUCCESS;
09852 }
09853 
09854 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
09855 {
09856    int which = 0;
09857    int wordlen;
09858    struct ast_vm_user *vmu;
09859    const char *context = "";
09860 
09861    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
09862    if (pos > 4)
09863       return NULL;
09864    if (pos == 3)
09865       return (state == 0) ? ast_strdup("for") : NULL;
09866    wordlen = strlen(word);
09867    AST_LIST_TRAVERSE(&users, vmu, list) {
09868       if (!strncasecmp(word, vmu->context, wordlen)) {
09869          if (context && strcmp(context, vmu->context) && ++which > state)
09870             return ast_strdup(vmu->context);
09871          /* ignore repeated contexts ? */
09872          context = vmu->context;
09873       }
09874    }
09875    return NULL;
09876 }
09877 
09878 /*! \brief Show a list of voicemail users in the CLI */
09879 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09880 {
09881    struct ast_vm_user *vmu;
09882 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
09883    const char *context = NULL;
09884    int users_counter = 0;
09885 
09886    switch (cmd) {
09887    case CLI_INIT:
09888       e->command = "voicemail show users";
09889       e->usage =
09890          "Usage: voicemail show users [for <context>]\n"
09891          "       Lists all mailboxes currently set up\n";
09892       return NULL;
09893    case CLI_GENERATE:
09894       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
09895    }  
09896 
09897    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
09898       return CLI_SHOWUSAGE;
09899    if (a->argc == 5) {
09900       if (strcmp(a->argv[3],"for"))
09901          return CLI_SHOWUSAGE;
09902       context = a->argv[4];
09903    }
09904 
09905    if (ast_check_realtime("voicemail")) {
09906       if (!context) {
09907          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
09908          return CLI_SHOWUSAGE;
09909       }
09910       return show_users_realtime(a->fd, context);
09911    }
09912 
09913    AST_LIST_LOCK(&users);
09914    if (AST_LIST_EMPTY(&users)) {
09915       ast_cli(a->fd, "There are no voicemail users currently defined\n");
09916       AST_LIST_UNLOCK(&users);
09917       return CLI_FAILURE;
09918    }
09919    if (a->argc == 3)
09920       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
09921    else {
09922       int count = 0;
09923       AST_LIST_TRAVERSE(&users, vmu, list) {
09924          if (!strcmp(context, vmu->context))
09925             count++;
09926       }
09927       if (count) {
09928          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
09929       } else {
09930          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
09931          AST_LIST_UNLOCK(&users);
09932          return CLI_FAILURE;
09933       }
09934    }
09935    AST_LIST_TRAVERSE(&users, vmu, list) {
09936       int newmsgs = 0, oldmsgs = 0;
09937       char count[12], tmp[256] = "";
09938 
09939       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
09940          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
09941          inboxcount(tmp, &newmsgs, &oldmsgs);
09942          snprintf(count, sizeof(count), "%d", newmsgs);
09943          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
09944          users_counter++;
09945       }
09946    }
09947    AST_LIST_UNLOCK(&users);
09948    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
09949    return CLI_SUCCESS;
09950 }
09951 
09952 /*! \brief Show a list of voicemail zones in the CLI */
09953 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09954 {
09955    struct vm_zone *zone;
09956 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
09957    char *res = CLI_SUCCESS;
09958 
09959    switch (cmd) {
09960    case CLI_INIT:
09961       e->command = "voicemail show zones";
09962       e->usage =
09963          "Usage: voicemail show zones\n"
09964          "       Lists zone message formats\n";
09965       return NULL;
09966    case CLI_GENERATE:
09967       return NULL;
09968    }
09969 
09970    if (a->argc != 3)
09971       return CLI_SHOWUSAGE;
09972 
09973    AST_LIST_LOCK(&zones);
09974    if (!AST_LIST_EMPTY(&zones)) {
09975       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
09976       AST_LIST_TRAVERSE(&zones, zone, list) {
09977          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
09978       }
09979    } else {
09980       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
09981       res = CLI_FAILURE;
09982    }
09983    AST_LIST_UNLOCK(&zones);
09984 
09985    return res;
09986 }
09987 
09988 /*! \brief Reload voicemail configuration from the CLI */
09989 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09990 {
09991    switch (cmd) {
09992    case CLI_INIT:
09993       e->command = "voicemail reload";
09994       e->usage =
09995          "Usage: voicemail reload\n"
09996          "       Reload voicemail configuration\n";
09997       return NULL;
09998    case CLI_GENERATE:
09999       return NULL;
10000    }
10001 
10002    if (a->argc != 2)
10003       return CLI_SHOWUSAGE;
10004 
10005    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10006    load_config(1);
10007    
10008    return CLI_SUCCESS;
10009 }
10010 
10011 static struct ast_cli_entry cli_voicemail[] = {
10012    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10013    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10014    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10015 };
10016 
10017 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10018 {
10019    int new = 0, old = 0, urgent = 0;
10020 
10021    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10022 
10023    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10024       mwi_sub->old_urgent = urgent;
10025       mwi_sub->old_new = new;
10026       mwi_sub->old_old = old;
10027       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10028    }
10029 }
10030 
10031 static void poll_subscribed_mailboxes(void)
10032 {
10033    struct mwi_sub *mwi_sub;
10034 
10035    AST_RWLIST_RDLOCK(&mwi_subs);
10036    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10037       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10038          poll_subscribed_mailbox(mwi_sub);
10039       }
10040    }
10041    AST_RWLIST_UNLOCK(&mwi_subs);
10042 }
10043 
10044 static void *mb_poll_thread(void *data)
10045 {
10046    while (poll_thread_run) {
10047       struct timespec ts = { 0, };
10048       struct timeval wait;
10049 
10050       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10051       ts.tv_sec = wait.tv_sec;
10052       ts.tv_nsec = wait.tv_usec * 1000;
10053 
10054       ast_mutex_lock(&poll_lock);
10055       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10056       ast_mutex_unlock(&poll_lock);
10057 
10058       if (!poll_thread_run)
10059          break;
10060 
10061       poll_subscribed_mailboxes();
10062    }
10063 
10064    return NULL;
10065 }
10066 
10067 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10068 {
10069    ast_free(mwi_sub);
10070 }
10071 
10072 static int handle_unsubscribe(void *datap)
10073 {
10074    struct mwi_sub *mwi_sub;
10075    uint32_t *uniqueid = datap;
10076    
10077    AST_RWLIST_WRLOCK(&mwi_subs);
10078    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10079       if (mwi_sub->uniqueid == *uniqueid) {
10080          AST_LIST_REMOVE_CURRENT(entry);
10081          break;
10082       }
10083    }
10084    AST_RWLIST_TRAVERSE_SAFE_END
10085    AST_RWLIST_UNLOCK(&mwi_subs);
10086 
10087    if (mwi_sub)
10088       mwi_sub_destroy(mwi_sub);
10089 
10090    ast_free(uniqueid);  
10091    return 0;
10092 }
10093 
10094 static int handle_subscribe(void *datap)
10095 {
10096    unsigned int len;
10097    struct mwi_sub *mwi_sub;
10098    struct mwi_sub_task *p = datap;
10099 
10100    len = sizeof(*mwi_sub);
10101    if (!ast_strlen_zero(p->mailbox))
10102       len += strlen(p->mailbox);
10103 
10104    if (!ast_strlen_zero(p->context))
10105       len += strlen(p->context) + 1; /* Allow for seperator */
10106 
10107    if (!(mwi_sub = ast_calloc(1, len)))
10108       return -1;
10109 
10110    mwi_sub->uniqueid = p->uniqueid;
10111    if (!ast_strlen_zero(p->mailbox))
10112       strcpy(mwi_sub->mailbox, p->mailbox);
10113 
10114    if (!ast_strlen_zero(p->context)) {
10115       strcat(mwi_sub->mailbox, "@");
10116       strcat(mwi_sub->mailbox, p->context);
10117    }
10118 
10119    AST_RWLIST_WRLOCK(&mwi_subs);
10120    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10121    AST_RWLIST_UNLOCK(&mwi_subs);
10122    ast_free((void *) p->mailbox);
10123    ast_free((void *) p->context);
10124    ast_free(p);
10125    poll_subscribed_mailbox(mwi_sub);
10126    return 0;
10127 }
10128 
10129 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10130 {
10131    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10132    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10133       return;
10134 
10135    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10136       return;
10137 
10138    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10139    *uniqueid = u;
10140    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10141       ast_free(uniqueid);
10142    }
10143 }
10144 
10145 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10146 {
10147    struct mwi_sub_task *mwist;
10148    
10149    if (ast_event_get_type(event) != AST_EVENT_SUB)
10150       return;
10151 
10152    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10153       return;
10154 
10155    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10156       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10157       return;
10158    }
10159    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10160    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10161    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10162    
10163    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10164       ast_free(mwist);
10165    }
10166 }
10167 
10168 static void start_poll_thread(void)
10169 {
10170    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10171       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10172       AST_EVENT_IE_END);
10173 
10174    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10175       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10176       AST_EVENT_IE_END);
10177 
10178    if (mwi_sub_sub)
10179       ast_event_report_subs(mwi_sub_sub);
10180 
10181    poll_thread_run = 1;
10182 
10183    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10184 }
10185 
10186 static void stop_poll_thread(void)
10187 {
10188    poll_thread_run = 0;
10189 
10190    if (mwi_sub_sub) {
10191       ast_event_unsubscribe(mwi_sub_sub);
10192       mwi_sub_sub = NULL;
10193    }
10194 
10195    if (mwi_unsub_sub) {
10196       ast_event_unsubscribe(mwi_unsub_sub);
10197       mwi_unsub_sub = NULL;
10198    }
10199 
10200    ast_mutex_lock(&poll_lock);
10201    ast_cond_signal(&poll_cond);
10202    ast_mutex_unlock(&poll_lock);
10203 
10204    pthread_join(poll_thread, NULL);
10205 
10206    poll_thread = AST_PTHREADT_NULL;
10207 }
10208 
10209 /*! \brief Manager list voicemail users command */
10210 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10211 {
10212    struct ast_vm_user *vmu = NULL;
10213    const char *id = astman_get_header(m, "ActionID");
10214    char actionid[128] = "";
10215 
10216    if (!ast_strlen_zero(id))
10217       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10218 
10219    AST_LIST_LOCK(&users);
10220 
10221    if (AST_LIST_EMPTY(&users)) {
10222       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10223       AST_LIST_UNLOCK(&users);
10224       return RESULT_SUCCESS;
10225    }
10226    
10227    astman_send_ack(s, m, "Voicemail user list will follow");
10228    
10229    AST_LIST_TRAVERSE(&users, vmu, list) {
10230       char dirname[256];
10231 
10232 #ifdef IMAP_STORAGE
10233       int new, old;
10234       inboxcount(vmu->mailbox, &new, &old);
10235 #endif
10236       
10237       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10238       astman_append(s,
10239          "%s"
10240          "Event: VoicemailUserEntry\r\n"
10241          "VMContext: %s\r\n"
10242          "VoiceMailbox: %s\r\n"
10243          "Fullname: %s\r\n"
10244          "Email: %s\r\n"
10245          "Pager: %s\r\n"
10246          "ServerEmail: %s\r\n"
10247          "MailCommand: %s\r\n"
10248          "Language: %s\r\n"
10249          "TimeZone: %s\r\n"
10250          "Callback: %s\r\n"
10251          "Dialout: %s\r\n"
10252          "UniqueID: %s\r\n"
10253          "ExitContext: %s\r\n"
10254          "SayDurationMinimum: %d\r\n"
10255          "SayEnvelope: %s\r\n"
10256          "SayCID: %s\r\n"
10257          "AttachMessage: %s\r\n"
10258          "AttachmentFormat: %s\r\n"
10259          "DeleteMessage: %s\r\n"
10260          "VolumeGain: %.2f\r\n"
10261          "CanReview: %s\r\n"
10262          "CallOperator: %s\r\n"
10263          "MaxMessageCount: %d\r\n"
10264          "MaxMessageLength: %d\r\n"
10265          "NewMessageCount: %d\r\n"
10266 #ifdef IMAP_STORAGE
10267          "OldMessageCount: %d\r\n"
10268          "IMAPUser: %s\r\n"
10269 #endif
10270          "\r\n",
10271          actionid,
10272          vmu->context,
10273          vmu->mailbox,
10274          vmu->fullname,
10275          vmu->email,
10276          vmu->pager,
10277          vmu->serveremail,
10278          vmu->mailcmd,
10279          vmu->language,
10280          vmu->zonetag,
10281          vmu->callback,
10282          vmu->dialout,
10283          vmu->uniqueid,
10284          vmu->exit,
10285          vmu->saydurationm,
10286          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10287          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10288          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10289          vmu->attachfmt,
10290          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10291          vmu->volgain,
10292          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10293          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10294          vmu->maxmsg,
10295          vmu->maxsecs,
10296 #ifdef IMAP_STORAGE
10297          new, old, vmu->imapuser
10298 #else
10299          count_messages(vmu, dirname)
10300 #endif
10301          );
10302    }     
10303    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10304 
10305    AST_LIST_UNLOCK(&users);
10306 
10307    return RESULT_SUCCESS;
10308 }
10309 
10310 /*! \brief Free the users structure. */
10311 static void free_vm_users(void) 
10312 {
10313    struct ast_vm_user *current;
10314    AST_LIST_LOCK(&users);
10315    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10316       ast_set_flag(current, VM_ALLOCED);
10317       free_user(current);
10318    }
10319    AST_LIST_UNLOCK(&users);
10320 }
10321 
10322 /*! \brief Free the zones structure. */
10323 static void free_vm_zones(void)
10324 {
10325    struct vm_zone *zcur;
10326    AST_LIST_LOCK(&zones);
10327    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10328       free_zone(zcur);
10329    AST_LIST_UNLOCK(&zones);
10330 }
10331 
10332 static char *substitute_escapes(const char *value)
10333 {
10334    char *current, *result;
10335 
10336    /* Add 16 for fudge factor */
10337    struct ast_str *str = ast_str_create(strlen(value) + 16);
10338 
10339    /* Substitute strings \r, \n, and \t into the appropriate characters */
10340    for (current = (char *) value; *current; current++) {
10341       if (*current == '\\') {
10342          current++;
10343          if (!*current) {
10344             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10345             break;
10346          }
10347          switch (*current) {
10348          case 'r':
10349             ast_str_append(&str, 0, "\r");
10350             break;
10351          case 'n':
10352 #ifdef IMAP_STORAGE
10353             if (!str->used || str->str[str->used - 1] != '\r') {
10354                ast_str_append(&str, 0, "\r");
10355             }
10356 #endif
10357             ast_str_append(&str, 0, "\n");
10358             break;
10359          case 't':
10360             ast_str_append(&str, 0, "\t");
10361             break;
10362          default:
10363             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10364             break;
10365          }
10366       } else {
10367          ast_str_append(&str, 0, "%c", *current);
10368       }
10369    }
10370 
10371    result = ast_strdup(str->str);
10372    ast_free(str);
10373 
10374    return result;
10375 }
10376 
10377 static int load_config(int reload)
10378 {
10379    struct ast_vm_user *current;
10380    struct ast_config *cfg, *ucfg;
10381    char *cat;
10382    struct ast_variable *var;
10383    const char *val;
10384    char *q, *stringp, *tmp;
10385    int x;
10386    int tmpadsi[4];
10387    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10388 
10389    ast_unload_realtime("voicemail");
10390    ast_unload_realtime("voicemail_data");
10391 
10392    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10393       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
10394          return 0;
10395       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10396       cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
10397    } else {
10398       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10399       ucfg = ast_config_load("users.conf", config_flags);
10400    }
10401 #ifdef IMAP_STORAGE
10402    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10403 #endif
10404    /* set audio control prompts */
10405    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10406    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10407    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10408    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10409    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10410 
10411    /* Free all the users structure */  
10412    free_vm_users();
10413 
10414    /* Free all the zones structure */
10415    free_vm_zones();
10416 
10417    AST_LIST_LOCK(&users);  
10418 
10419    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10420    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10421 
10422    if (cfg) {
10423       /* General settings */
10424 
10425       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10426          val = "default";
10427       ast_copy_string(userscontext, val, sizeof(userscontext));
10428       /* Attach voice message to mail message ? */
10429       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10430          val = "yes";
10431       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10432 
10433       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10434          val = "no";
10435       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10436 
10437       volgain = 0.0;
10438       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10439          sscanf(val, "%30lf", &volgain);
10440 
10441 #ifdef ODBC_STORAGE
10442       strcpy(odbc_database, "asterisk");
10443       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10444          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10445       }
10446       strcpy(odbc_table, "voicemessages");
10447       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10448          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10449       }
10450 #endif      
10451       /* Mail command */
10452       strcpy(mailcmd, SENDMAIL);
10453       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10454          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10455 
10456       maxsilence = 0;
10457       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10458          maxsilence = atoi(val);
10459          if (maxsilence > 0)
10460             maxsilence *= 1000;
10461       }
10462       
10463       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10464          maxmsg = MAXMSG;
10465       } else {
10466          maxmsg = atoi(val);
10467          if (maxmsg <= 0) {
10468             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10469             maxmsg = MAXMSG;
10470          } else if (maxmsg > MAXMSGLIMIT) {
10471             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10472             maxmsg = MAXMSGLIMIT;
10473          }
10474       }
10475 
10476       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10477          maxdeletedmsg = 0;
10478       } else {
10479          if (sscanf(val, "%30d", &x) == 1)
10480             maxdeletedmsg = x;
10481          else if (ast_true(val))
10482             maxdeletedmsg = MAXMSG;
10483          else
10484             maxdeletedmsg = 0;
10485 
10486          if (maxdeletedmsg < 0) {
10487             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10488             maxdeletedmsg = MAXMSG;
10489          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10490             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10491             maxdeletedmsg = MAXMSGLIMIT;
10492          }
10493       }
10494 
10495       /* Load date format config for voicemail mail */
10496       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10497          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10498       }
10499 
10500       /* External password changing command */
10501       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10502          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10503          pwdchange = PWDCHANGE_EXTERNAL;
10504       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10505          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10506          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10507       }
10508  
10509       /* External password validation command */
10510       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10511          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10512          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10513       }
10514 
10515 #ifdef IMAP_STORAGE
10516       /* IMAP server address */
10517       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10518          ast_copy_string(imapserver, val, sizeof(imapserver));
10519       } else {
10520          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10521       }
10522       /* IMAP server port */
10523       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10524          ast_copy_string(imapport, val, sizeof(imapport));
10525       } else {
10526          ast_copy_string(imapport,"143", sizeof(imapport));
10527       }
10528       /* IMAP server flags */
10529       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10530          ast_copy_string(imapflags, val, sizeof(imapflags));
10531       }
10532       /* IMAP server master username */
10533       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10534          ast_copy_string(authuser, val, sizeof(authuser));
10535       }
10536       /* IMAP server master password */
10537       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10538          ast_copy_string(authpassword, val, sizeof(authpassword));
10539       }
10540       /* Expunge on exit */
10541       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10542          if (ast_false(val))
10543             expungeonhangup = 0;
10544          else
10545             expungeonhangup = 1;
10546       } else {
10547          expungeonhangup = 1;
10548       }
10549       /* IMAP voicemail folder */
10550       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10551          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10552       } else {
10553          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10554       }
10555       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10556          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10557       }
10558       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10559          imapgreetings = ast_true(val);
10560       } else {
10561          imapgreetings = 0;
10562       }
10563       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10564          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10565       } else {
10566          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10567       }
10568 
10569       /* There is some very unorthodox casting done here. This is due
10570        * to the way c-client handles the argument passed in. It expects a 
10571        * void pointer and casts the pointer directly to a long without
10572        * first dereferencing it. */
10573       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10574          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10575       } else {
10576          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10577       }
10578 
10579       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10580          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10581       } else {
10582          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10583       }
10584 
10585       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10586          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10587       } else {
10588          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10589       }
10590 
10591       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10592          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10593       } else {
10594          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10595       }
10596 
10597       /* Increment configuration version */
10598       imapversion++;
10599 #endif
10600       /* External voicemail notify application */
10601       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10602          ast_copy_string(externnotify, val, sizeof(externnotify));
10603          ast_debug(1, "found externnotify: %s\n", externnotify);
10604       } else {
10605          externnotify[0] = '\0';
10606       }
10607 
10608       /* SMDI voicemail notification */
10609       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10610          ast_debug(1, "Enabled SMDI voicemail notification\n");
10611          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10612             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10613          } else {
10614             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10615             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10616          }
10617          if (!smdi_iface) {
10618             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10619          } 
10620       }
10621 
10622       /* Silence treshold */
10623       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10624       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10625          silencethreshold = atoi(val);
10626       
10627       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10628          val = ASTERISK_USERNAME;
10629       ast_copy_string(serveremail, val, sizeof(serveremail));
10630       
10631       vmmaxsecs = 0;
10632       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10633          if (sscanf(val, "%30d", &x) == 1) {
10634             vmmaxsecs = x;
10635          } else {
10636             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10637          }
10638       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10639          static int maxmessage_deprecate = 0;
10640          if (maxmessage_deprecate == 0) {
10641             maxmessage_deprecate = 1;
10642             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10643          }
10644          if (sscanf(val, "%30d", &x) == 1) {
10645             vmmaxsecs = x;
10646          } else {
10647             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10648          }
10649       }
10650 
10651       vmminsecs = 0;
10652       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10653          if (sscanf(val, "%30d", &x) == 1) {
10654             vmminsecs = x;
10655             if (maxsilence / 1000 >= vmminsecs) {
10656                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
10657             }
10658          } else {
10659             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10660          }
10661       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10662          static int maxmessage_deprecate = 0;
10663          if (maxmessage_deprecate == 0) {
10664             maxmessage_deprecate = 1;
10665             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10666          }
10667          if (sscanf(val, "%30d", &x) == 1) {
10668             vmminsecs = x;
10669             if (maxsilence / 1000 >= vmminsecs) {
10670                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10671             }
10672          } else {
10673             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10674          }
10675       }
10676 
10677       val = ast_variable_retrieve(cfg, "general", "format");
10678       if (!val) {
10679          val = "wav";   
10680       } else {
10681          tmp = ast_strdupa(val);
10682          val = ast_format_str_reduce(tmp);
10683          if (!val) {
10684             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
10685             val = "wav";
10686          }
10687       }
10688       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10689 
10690       skipms = 3000;
10691       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10692          if (sscanf(val, "%30d", &x) == 1) {
10693             maxgreet = x;
10694          } else {
10695             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10696          }
10697       }
10698 
10699       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10700          if (sscanf(val, "%30d", &x) == 1) {
10701             skipms = x;
10702          } else {
10703             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10704          }
10705       }
10706 
10707       maxlogins = 3;
10708       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10709          if (sscanf(val, "%30d", &x) == 1) {
10710             maxlogins = x;
10711          } else {
10712             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10713          }
10714       }
10715 
10716       minpassword = MINPASSWORD;
10717       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
10718          if (sscanf(val, "%30d", &x) == 1) {
10719             minpassword = x;
10720          } else {
10721             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
10722          }
10723       }
10724 
10725       /* Force new user to record name ? */
10726       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
10727          val = "no";
10728       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
10729 
10730       /* Force new user to record greetings ? */
10731       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
10732          val = "no";
10733       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
10734 
10735       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
10736          ast_debug(1, "VM_CID Internal context string: %s\n", val);
10737          stringp = ast_strdupa(val);
10738          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
10739             if (!ast_strlen_zero(stringp)) {
10740                q = strsep(&stringp, ",");
10741                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
10742                   q++;
10743                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
10744                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
10745             } else {
10746                cidinternalcontexts[x][0] = '\0';
10747             }
10748          }
10749       }
10750       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
10751          ast_debug(1,"VM Review Option disabled globally\n");
10752          val = "no";
10753       }
10754       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
10755 
10756       /* Temporary greeting reminder */
10757       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
10758          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
10759          val = "no";
10760       } else {
10761          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
10762       }
10763       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
10764       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
10765          ast_debug(1, "VM next message wrap disabled globally\n");
10766          val = "no";
10767       }
10768       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
10769 
10770       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
10771          ast_debug(1,"VM Operator break disabled globally\n");
10772          val = "no";
10773       }
10774       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
10775 
10776       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
10777          ast_debug(1,"VM CID Info before msg disabled globally\n");
10778          val = "no";
10779       } 
10780       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
10781 
10782       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
10783          ast_debug(1,"Send Voicemail msg disabled globally\n");
10784          val = "no";
10785       }
10786       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
10787    
10788       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
10789          ast_debug(1,"ENVELOPE before msg enabled globally\n");
10790          val = "yes";
10791       }
10792       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
10793 
10794       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
10795          ast_debug(1,"Move Heard enabled globally\n");
10796          val = "yes";
10797       }
10798       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
10799 
10800       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
10801          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
10802          val = "no";
10803       }
10804       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
10805 
10806       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
10807          ast_debug(1,"Duration info before msg enabled globally\n");
10808          val = "yes";
10809       }
10810       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
10811 
10812       saydurationminfo = 2;
10813       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
10814          if (sscanf(val, "%30d", &x) == 1) {
10815             saydurationminfo = x;
10816          } else {
10817             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
10818          }
10819       }
10820 
10821       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
10822          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
10823          val = "no";
10824       }
10825       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
10826 
10827       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
10828          ast_copy_string(dialcontext, val, sizeof(dialcontext));
10829          ast_debug(1, "found dialout context: %s\n", dialcontext);
10830       } else {
10831          dialcontext[0] = '\0';  
10832       }
10833       
10834       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
10835          ast_copy_string(callcontext, val, sizeof(callcontext));
10836          ast_debug(1, "found callback context: %s\n", callcontext);
10837       } else {
10838          callcontext[0] = '\0';
10839       }
10840 
10841       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
10842          ast_copy_string(exitcontext, val, sizeof(exitcontext));
10843          ast_debug(1, "found operator context: %s\n", exitcontext);
10844       } else {
10845          exitcontext[0] = '\0';
10846       }
10847       
10848       /* load password sounds configuration */
10849       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
10850          ast_copy_string(vm_password, val, sizeof(vm_password));
10851       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
10852          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
10853       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
10854          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
10855       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
10856          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
10857       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
10858          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
10859       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
10860          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
10861       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
10862          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
10863       }
10864       /* load configurable audio prompts */
10865       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
10866          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
10867       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
10868          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
10869       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
10870          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
10871       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
10872          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
10873       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
10874          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
10875 
10876       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
10877          val = "no";
10878       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
10879 
10880       poll_freq = DEFAULT_POLL_FREQ;
10881       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
10882          if (sscanf(val, "%30u", &poll_freq) != 1) {
10883             poll_freq = DEFAULT_POLL_FREQ;
10884             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
10885          }
10886       }
10887 
10888       poll_mailboxes = 0;
10889       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
10890          poll_mailboxes = ast_true(val);
10891 
10892       if (ucfg) { 
10893          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
10894             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
10895                continue;
10896             if ((current = find_or_create(userscontext, cat))) {
10897                populate_defaults(current);
10898                apply_options_full(current, ast_variable_browse(ucfg, cat));
10899                ast_copy_string(current->context, userscontext, sizeof(current->context));
10900             }
10901          }
10902          ast_config_destroy(ucfg);
10903       }
10904       cat = ast_category_browse(cfg, NULL);
10905       while (cat) {
10906          if (strcasecmp(cat, "general")) {
10907             var = ast_variable_browse(cfg, cat);
10908             if (strcasecmp(cat, "zonemessages")) {
10909                /* Process mailboxes in this context */
10910                while (var) {
10911                   append_mailbox(cat, var->name, var->value);
10912                   var = var->next;
10913                }
10914             } else {
10915                /* Timezones in this context */
10916                while (var) {
10917                   struct vm_zone *z;
10918                   if ((z = ast_malloc(sizeof(*z)))) {
10919                      char *msg_format, *tzone;
10920                      msg_format = ast_strdupa(var->value);
10921                      tzone = strsep(&msg_format, "|");
10922                      if (msg_format) {
10923                         ast_copy_string(z->name, var->name, sizeof(z->name));
10924                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
10925                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
10926                         AST_LIST_LOCK(&zones);
10927                         AST_LIST_INSERT_HEAD(&zones, z, list);
10928                         AST_LIST_UNLOCK(&zones);
10929                      } else {
10930                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
10931                         ast_free(z);
10932                      }
10933                   } else {
10934                      AST_LIST_UNLOCK(&users);
10935                      ast_config_destroy(cfg);
10936                      return -1;
10937                   }
10938                   var = var->next;
10939                }
10940             }
10941          }
10942          cat = ast_category_browse(cfg, cat);
10943       }
10944       memset(fromstring, 0, sizeof(fromstring));
10945       memset(pagerfromstring, 0, sizeof(pagerfromstring));
10946       strcpy(charset, "ISO-8859-1");
10947       if (emailbody) {
10948          ast_free(emailbody);
10949          emailbody = NULL;
10950       }
10951       if (emailsubject) {
10952          ast_free(emailsubject);
10953          emailsubject = NULL;
10954       }
10955       if (pagerbody) {
10956          ast_free(pagerbody);
10957          pagerbody = NULL;
10958       }
10959       if (pagersubject) {
10960          ast_free(pagersubject);
10961          pagersubject = NULL;
10962       }
10963       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
10964          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
10965       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
10966          ast_copy_string(fromstring, val, sizeof(fromstring));
10967       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
10968          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
10969       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
10970          ast_copy_string(charset, val, sizeof(charset));
10971       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
10972          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
10973          for (x = 0; x < 4; x++) {
10974             memcpy(&adsifdn[x], &tmpadsi[x], 1);
10975          }
10976       }
10977       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
10978          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
10979          for (x = 0; x < 4; x++) {
10980             memcpy(&adsisec[x], &tmpadsi[x], 1);
10981          }
10982       }
10983       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
10984          if (atoi(val)) {
10985             adsiver = atoi(val);
10986          }
10987       }
10988       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
10989          ast_copy_string(zonetag, val, sizeof(zonetag));
10990       }
10991       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
10992          emailsubject = ast_strdup(val);
10993       }
10994       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
10995          emailbody = substitute_escapes(val);
10996       }
10997       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
10998          pagersubject = ast_strdup(val);
10999       }
11000       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11001          pagerbody = substitute_escapes(val);
11002       }
11003       AST_LIST_UNLOCK(&users);
11004       ast_config_destroy(cfg);
11005 
11006       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11007          start_poll_thread();
11008       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11009          stop_poll_thread();;
11010 
11011       return 0;
11012    } else {
11013       AST_LIST_UNLOCK(&users);
11014       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11015       if (ucfg)
11016          ast_config_destroy(ucfg);
11017       return 0;
11018    }
11019 }
11020 
11021 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11022 {
11023    int res = -1;
11024    char dir[PATH_MAX];
11025    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11026    ast_debug(2, "About to try retrieving name file %s\n", dir);
11027    RETRIEVE(dir, -1, mailbox, context);
11028    if (ast_fileexists(dir, NULL, NULL)) {
11029       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11030    }
11031    DISPOSE(dir, -1);
11032    return res;
11033 }
11034 
11035 static int reload(void)
11036 {
11037    return load_config(1);
11038 }
11039 
11040 static int unload_module(void)
11041 {
11042    int res;
11043 
11044    res = ast_unregister_application(app);
11045    res |= ast_unregister_application(app2);
11046    res |= ast_unregister_application(app3);
11047    res |= ast_unregister_application(app4);
11048    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11049    res |= ast_manager_unregister("VoicemailUsersList");
11050    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
11051    ast_uninstall_vm_functions();
11052 
11053    if (poll_thread != AST_PTHREADT_NULL)
11054       stop_poll_thread();
11055 
11056    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11057    ast_unload_realtime("voicemail");
11058    ast_unload_realtime("voicemail_data");
11059 
11060    free_vm_users();
11061    free_vm_zones();
11062    return res;
11063 }
11064 
11065 static int load_module(void)
11066 {
11067    int res;
11068    my_umask = umask(0);
11069    umask(my_umask);
11070 
11071    /* compute the location of the voicemail spool directory */
11072    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11073    
11074    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11075       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11076    }
11077 
11078    if ((res = load_config(0)))
11079       return res;
11080 
11081    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
11082    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
11083    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
11084    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
11085    res |= ast_custom_function_register(&mailbox_exists_acf);
11086    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11087    if (res)
11088       return res;
11089 
11090    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
11091 
11092    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11093    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11094    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11095 
11096    return res;
11097 }
11098 
11099 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11100 {
11101    int cmd = 0;
11102    char destination[80] = "";
11103    int retries = 0;
11104 
11105    if (!num) {
11106       ast_verb(3, "Destination number will be entered manually\n");
11107       while (retries < 3 && cmd != 't') {
11108          destination[1] = '\0';
11109          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11110          if (!cmd)
11111             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11112          if (!cmd)
11113             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11114          if (!cmd) {
11115             cmd = ast_waitfordigit(chan, 6000);
11116             if (cmd)
11117                destination[0] = cmd;
11118          }
11119          if (!cmd) {
11120             retries++;
11121          } else {
11122 
11123             if (cmd < 0)
11124                return 0;
11125             if (cmd == '*') {
11126                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11127                return 0;
11128             }
11129             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11130                retries++;
11131             else
11132                cmd = 't';
11133          }
11134       }
11135       if (retries >= 3) {
11136          return 0;
11137       }
11138       
11139    } else {
11140       if (option_verbose > 2)
11141          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11142       ast_copy_string(destination, num, sizeof(destination));
11143    }
11144 
11145    if (!ast_strlen_zero(destination)) {
11146       if (destination[strlen(destination) -1 ] == '*')
11147          return 0; 
11148       if (option_verbose > 2)
11149          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11150       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11151       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11152       chan->priority = 0;
11153       return 9;
11154    }
11155    return 0;
11156 }
11157 
11158 /*!
11159  * \brief The advanced options within a message.
11160  * \param chan
11161  * \param vmu 
11162  * \param vms
11163  * \param msg
11164  * \param option
11165  * \param record_gain
11166  *
11167  * Provides handling for the play message envelope, call the person back, or reply to message. 
11168  *
11169  * \return zero on success, -1 on error.
11170  */
11171 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)
11172 {
11173    int res = 0;
11174    char filename[PATH_MAX];
11175    struct ast_config *msg_cfg = NULL;
11176    const char *origtime, *context;
11177    char *name, *num;
11178    int retries = 0;
11179    char *cid;
11180    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11181 
11182    vms->starting = 0; 
11183 
11184    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11185 
11186    /* Retrieve info from VM attribute file */
11187    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11188    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11189    msg_cfg = ast_config_load(filename, config_flags);
11190    DISPOSE(vms->curdir, vms->curmsg);
11191    if (!msg_cfg) {
11192       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11193       return 0;
11194    }
11195 
11196    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11197       ast_config_destroy(msg_cfg);
11198       return 0;
11199    }
11200 
11201    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11202 
11203    context = ast_variable_retrieve(msg_cfg, "message", "context");
11204    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11205       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11206    switch (option) {
11207    case 3: /* Play message envelope */
11208       if (!res)
11209          res = play_message_datetime(chan, vmu, origtime, filename);
11210       if (!res)
11211          res = play_message_callerid(chan, vms, cid, context, 0);
11212 
11213       res = 't';
11214       break;
11215 
11216    case 2:  /* Call back */
11217 
11218       if (ast_strlen_zero(cid))
11219          break;
11220 
11221       ast_callerid_parse(cid, &name, &num);
11222       while ((res > -1) && (res != 't')) {
11223          switch (res) {
11224          case '1':
11225             if (num) {
11226                /* Dial the CID number */
11227                res = dialout(chan, vmu, num, vmu->callback);
11228                if (res) {
11229                   ast_config_destroy(msg_cfg);
11230                   return 9;
11231                }
11232             } else {
11233                res = '2';
11234             }
11235             break;
11236 
11237          case '2':
11238             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11239             if (!ast_strlen_zero(vmu->dialout)) {
11240                res = dialout(chan, vmu, NULL, vmu->dialout);
11241                if (res) {
11242                   ast_config_destroy(msg_cfg);
11243                   return 9;
11244                }
11245             } else {
11246                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11247                res = ast_play_and_wait(chan, "vm-sorry");
11248             }
11249             ast_config_destroy(msg_cfg);
11250             return res;
11251          case '*':
11252             res = 't';
11253             break;
11254          case '3':
11255          case '4':
11256          case '5':
11257          case '6':
11258          case '7':
11259          case '8':
11260          case '9':
11261          case '0':
11262 
11263             res = ast_play_and_wait(chan, "vm-sorry");
11264             retries++;
11265             break;
11266          default:
11267             if (num) {
11268                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11269                res = ast_play_and_wait(chan, "vm-num-i-have");
11270                if (!res)
11271                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11272                if (!res)
11273                   res = ast_play_and_wait(chan, "vm-tocallnum");
11274                /* Only prompt for a caller-specified number if there is a dialout context specified */
11275                if (!ast_strlen_zero(vmu->dialout)) {
11276                   if (!res)
11277                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11278                }
11279             } else {
11280                res = ast_play_and_wait(chan, "vm-nonumber");
11281                if (!ast_strlen_zero(vmu->dialout)) {
11282                   if (!res)
11283                      res = ast_play_and_wait(chan, "vm-toenternumber");
11284                }
11285             }
11286             if (!res)
11287                res = ast_play_and_wait(chan, "vm-star-cancel");
11288             if (!res)
11289                res = ast_waitfordigit(chan, 6000);
11290             if (!res) {
11291                retries++;
11292                if (retries > 3)
11293                   res = 't';
11294             }
11295             break; 
11296             
11297          }
11298          if (res == 't')
11299             res = 0;
11300          else if (res == '*')
11301             res = -1;
11302       }
11303       break;
11304       
11305    case 1:  /* Reply */
11306       /* Send reply directly to sender */
11307       if (ast_strlen_zero(cid))
11308          break;
11309 
11310       ast_callerid_parse(cid, &name, &num);
11311       if (!num) {
11312          ast_verb(3, "No CID number available, no reply sent\n");
11313          if (!res)
11314             res = ast_play_and_wait(chan, "vm-nonumber");
11315          ast_config_destroy(msg_cfg);
11316          return res;
11317       } else {
11318          struct ast_vm_user vmu2;
11319          if (find_user(&vmu2, vmu->context, num)) {
11320             struct leave_vm_options leave_options;
11321             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11322             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11323 
11324             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11325             
11326             memset(&leave_options, 0, sizeof(leave_options));
11327             leave_options.record_gain = record_gain;
11328             res = leave_voicemail(chan, mailbox, &leave_options);
11329             if (!res)
11330                res = 't';
11331             ast_config_destroy(msg_cfg);
11332             return res;
11333          } else {
11334             /* Sender has no mailbox, can't reply */
11335             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11336             ast_play_and_wait(chan, "vm-nobox");
11337             res = 't';
11338             ast_config_destroy(msg_cfg);
11339             return res;
11340          }
11341       } 
11342       res = 0;
11343 
11344       break;
11345    }
11346 
11347 #ifndef IMAP_STORAGE
11348    ast_config_destroy(msg_cfg);
11349 
11350    if (!res) {
11351       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11352       vms->heard[msg] = 1;
11353       res = wait_file(chan, vms, vms->fn);
11354    }
11355 #endif
11356    return res;
11357 }
11358 
11359 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11360          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11361          signed char record_gain, struct vm_state *vms, char *flag)
11362 {
11363    /* Record message & let caller review or re-record it, or set options if applicable */
11364    int res = 0;
11365    int cmd = 0;
11366    int max_attempts = 3;
11367    int attempts = 0;
11368    int recorded = 0;
11369    int msg_exists = 0;
11370    signed char zero_gain = 0;
11371    char tempfile[PATH_MAX];
11372    char *acceptdtmf = "#";
11373    char *canceldtmf = "";
11374 
11375    /* Note that urgent and private are for flagging messages as such in the future */
11376 
11377    /* barf if no pointer passed to store duration in */
11378    if (duration == NULL) {
11379       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11380       return -1;
11381    }
11382 
11383    if (!outsidecaller)
11384       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11385    else
11386       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11387 
11388    cmd = '3';  /* Want to start by recording */
11389 
11390    while ((cmd >= 0) && (cmd != 't')) {
11391       switch (cmd) {
11392       case '1':
11393          if (!msg_exists) {
11394             /* In this case, 1 is to record a message */
11395             cmd = '3';
11396             break;
11397          } else {
11398             /* Otherwise 1 is to save the existing message */
11399             ast_verb(3, "Saving message as is\n");
11400             if (!outsidecaller) 
11401                ast_filerename(tempfile, recordfile, NULL);
11402             ast_stream_and_wait(chan, "vm-msgsaved", "");
11403             if (!outsidecaller) {
11404                /* Saves to IMAP server only if imapgreeting=yes */
11405                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11406                DISPOSE(recordfile, -1);
11407             }
11408             cmd = 't';
11409             return res;
11410          }
11411       case '2':
11412          /* Review */
11413          ast_verb(3, "Reviewing the message\n");
11414          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11415          break;
11416       case '3':
11417          msg_exists = 0;
11418          /* Record */
11419          if (recorded == 1) 
11420             ast_verb(3, "Re-recording the message\n");
11421          else  
11422             ast_verb(3, "Recording the message\n");
11423          
11424          if (recorded && outsidecaller) {
11425             cmd = ast_play_and_wait(chan, INTRO);
11426             cmd = ast_play_and_wait(chan, "beep");
11427          }
11428          recorded = 1;
11429          /* 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 */
11430          if (record_gain)
11431             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11432          if (ast_test_flag(vmu, VM_OPERATOR))
11433             canceldtmf = "0";
11434          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11435          if (record_gain)
11436             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11437          if (cmd == -1) {
11438             /* User has hung up, no options to give */
11439             if (!outsidecaller) {
11440                /* user was recording a greeting and they hung up, so let's delete the recording. */
11441                ast_filedelete(tempfile, NULL);
11442             }     
11443             return cmd;
11444          }
11445          if (cmd == '0') {
11446             break;
11447          } else if (cmd == '*') {
11448             break;
11449 #if 0
11450          } else if (vmu->review && (*duration < 5)) {
11451             /* Message is too short */
11452             ast_verb(3, "Message too short\n");
11453             cmd = ast_play_and_wait(chan, "vm-tooshort");
11454             cmd = ast_filedelete(tempfile, NULL);
11455             break;
11456          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11457             /* Message is all silence */
11458             ast_verb(3, "Nothing recorded\n");
11459             cmd = ast_filedelete(tempfile, NULL);
11460             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11461             if (!cmd)
11462                cmd = ast_play_and_wait(chan, "vm-speakup");
11463             break;
11464 #endif
11465          } else {
11466             /* If all is well, a message exists */
11467             msg_exists = 1;
11468             cmd = 0;
11469          }
11470          break;
11471       case '4':
11472          if (outsidecaller) {  /* only mark vm messages */
11473             /* Mark Urgent */
11474             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11475                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11476                ast_debug(1000, "This message is too urgent!\n");
11477                res = ast_play_and_wait(chan, "vm-marked-urgent");
11478                strcpy(flag, "Urgent");
11479             } else if (flag) {
11480                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11481                res = ast_play_and_wait(chan, "vm-urgent-removed");
11482                strcpy(flag, "");
11483             } else {
11484                ast_play_and_wait(chan, "vm-sorry");
11485             }
11486             cmd = 0;
11487          } else {
11488             cmd = ast_play_and_wait(chan, "vm-sorry");
11489          }
11490          break;
11491       case '5':
11492       case '6':
11493       case '7':
11494       case '8':
11495       case '9':
11496       case '*':
11497       case '#':
11498          cmd = ast_play_and_wait(chan, "vm-sorry");
11499          break;
11500 #if 0 
11501 /*  XXX Commented out for the moment because of the dangers of deleting
11502     a message while recording (can put the message numbers out of sync) */
11503       case '*':
11504          /* Cancel recording, delete message, offer to take another message*/
11505          cmd = ast_play_and_wait(chan, "vm-deleted");
11506          cmd = ast_filedelete(tempfile, NULL);
11507          if (outsidecaller) {
11508             res = vm_exec(chan, NULL);
11509             return res;
11510          }
11511          else
11512             return 1;
11513 #endif
11514       case '0':
11515          if (!ast_test_flag(vmu, VM_OPERATOR)) {
11516             cmd = ast_play_and_wait(chan, "vm-sorry");
11517             break;
11518          }
11519          if (msg_exists || recorded) {
11520             cmd = ast_play_and_wait(chan, "vm-saveoper");
11521             if (!cmd)
11522                cmd = ast_waitfordigit(chan, 3000);
11523             if (cmd == '1') {
11524                ast_play_and_wait(chan, "vm-msgsaved");
11525                cmd = '0';
11526             } else if (cmd == '4') {
11527                if (flag) {
11528                   ast_play_and_wait(chan, "vm-marked-urgent");
11529                   strcpy(flag, "Urgent");
11530                }
11531                ast_play_and_wait(chan, "vm-msgsaved");
11532                cmd = '0';
11533             } else {
11534                ast_play_and_wait(chan, "vm-deleted");
11535                DELETE(recordfile, -1, recordfile, vmu);
11536                cmd = '0';
11537             }
11538          }
11539          return cmd;
11540       default:
11541          /* If the caller is an ouside caller, and the review option is enabled,
11542             allow them to review the message, but let the owner of the box review
11543             their OGM's */
11544          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11545             return cmd;
11546          if (msg_exists) {
11547             cmd = ast_play_and_wait(chan, "vm-review");
11548             if (!cmd && outsidecaller) {
11549                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11550                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11551                } else if (flag) {
11552                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11553                }
11554             }
11555          } else {
11556             cmd = ast_play_and_wait(chan, "vm-torerecord");
11557             if (!cmd)
11558                cmd = ast_waitfordigit(chan, 600);
11559          }
11560          
11561          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11562             cmd = ast_play_and_wait(chan, "vm-reachoper");
11563             if (!cmd)
11564                cmd = ast_waitfordigit(chan, 600);
11565          }
11566 #if 0
11567          if (!cmd)
11568             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11569 #endif
11570          if (!cmd)
11571             cmd = ast_waitfordigit(chan, 6000);
11572          if (!cmd) {
11573             attempts++;
11574          }
11575          if (attempts > max_attempts) {
11576             cmd = 't';
11577          }
11578       }
11579    }
11580    if (outsidecaller)
11581       ast_play_and_wait(chan, "vm-goodbye");
11582    if (cmd == 't')
11583       cmd = 0;
11584    return cmd;
11585 }
11586 
11587 /* This is a workaround so that menuselect displays a proper description
11588  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11589  */
11590 
11591 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11592       .load = load_module,
11593       .unload = unload_module,
11594       .reload = reload,
11595       );

Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1