Thu Apr 8 01:20:53 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: 250976 $")
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/astobj2.h"
00119 #include "asterisk/event.h"
00120 #include "asterisk/taskprocessor.h"
00121 
00122 #ifdef ODBC_STORAGE
00123 #include "asterisk/res_odbc.h"
00124 #endif
00125 
00126 #ifdef IMAP_STORAGE
00127 #include "asterisk/threadstorage.h"
00128 
00129 static char imapserver[48];
00130 static char imapport[8];
00131 static char imapflags[128];
00132 static char imapfolder[64];
00133 static char imapparentfolder[64] = "\0";
00134 static char greetingfolder[64];
00135 static char authuser[32];
00136 static char authpassword[42];
00137 static int imapversion = 1;
00138 
00139 static int expungeonhangup = 1;
00140 static int imapgreetings = 0;
00141 static char delimiter = '\0';
00142 
00143 struct vm_state;
00144 struct ast_vm_user;
00145 
00146 AST_THREADSTORAGE(ts_vmstate);
00147 
00148 /* Forward declarations for IMAP */
00149 static int init_mailstream(struct vm_state *vms, int box);
00150 static void write_file(char *filename, char *buffer, unsigned long len);
00151 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00152 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00153 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00154 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00155 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00156 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00157 static void vmstate_insert(struct vm_state *vms);
00158 static void vmstate_delete(struct vm_state *vms);
00159 static void set_update(MAILSTREAM * stream);
00160 static void init_vm_state(struct vm_state *vms);
00161 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00162 static void get_mailbox_delimiter(MAILSTREAM *stream);
00163 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00164 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00165 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00166 static void update_messages_by_imapuser(const char *user, unsigned long number);
00167 static int vm_delete(char *file);
00168 
00169 static int imap_remove_file (char *dir, int msgnum);
00170 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00171 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00172 static void check_quota(struct vm_state *vms, char *mailbox);
00173 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00174 struct vmstate {
00175    struct vm_state *vms;
00176    AST_LIST_ENTRY(vmstate) list;
00177 };
00178 
00179 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00180 
00181 #endif
00182 
00183 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00184 
00185 #define COMMAND_TIMEOUT 5000
00186 /* Don't modify these here; set your umask at runtime instead */
00187 #define  VOICEMAIL_DIR_MODE   0777
00188 #define  VOICEMAIL_FILE_MODE  0666
00189 #define  CHUNKSIZE   65536
00190 
00191 #define VOICEMAIL_CONFIG "voicemail.conf"
00192 #define ASTERISK_USERNAME "asterisk"
00193 
00194 /* Define fast-forward, pause, restart, and reverse keys
00195    while listening to a voicemail message - these are
00196    strings, not characters */
00197 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00198 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00199 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00200 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00201 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00202 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00203 
00204 /* Default mail command to mail voicemail. Change it with the
00205     mailcmd= command in voicemail.conf */
00206 #define SENDMAIL "/usr/sbin/sendmail -t"
00207 
00208 #define INTRO "vm-intro"
00209 
00210 #define MAXMSG 100
00211 #define MAXMSGLIMIT 9999
00212 
00213 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00214 
00215 #define BASELINELEN 72
00216 #define BASEMAXINLINE 256
00217 #define eol "\r\n"
00218 
00219 #define MAX_DATETIME_FORMAT   512
00220 #define MAX_NUM_CID_CONTEXTS 10
00221 
00222 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00223 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00224 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00225 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00226 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00227 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00228 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00229 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00230 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00231 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00232 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00233 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00234 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00235 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00236 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00237 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00238 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00239 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00240 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00241 #define ERROR_LOCK_PATH  -100
00242 
00243 
00244 enum {
00245    NEW_FOLDER,
00246    OLD_FOLDER,
00247    WORK_FOLDER,
00248    FAMILY_FOLDER,
00249    FRIENDS_FOLDER,
00250    GREETINGS_FOLDER
00251 } vm_box;
00252 
00253 enum {
00254    OPT_SILENT =           (1 << 0),
00255    OPT_BUSY_GREETING =    (1 << 1),
00256    OPT_UNAVAIL_GREETING = (1 << 2),
00257    OPT_RECORDGAIN =       (1 << 3),
00258    OPT_PREPEND_MAILBOX =  (1 << 4),
00259    OPT_AUTOPLAY =         (1 << 6),
00260    OPT_DTMFEXIT =         (1 << 7),
00261    OPT_MESSAGE_Urgent =   (1 << 8),
00262    OPT_MESSAGE_PRIORITY = (1 << 9)
00263 } vm_option_flags;
00264 
00265 enum {
00266    OPT_ARG_RECORDGAIN = 0,
00267    OPT_ARG_PLAYFOLDER = 1,
00268    OPT_ARG_DTMFEXIT   = 2,
00269    /* This *must* be the last value in this enum! */
00270    OPT_ARG_ARRAY_SIZE = 3,
00271 } vm_option_args;
00272 
00273 AST_APP_OPTIONS(vm_app_options, {
00274    AST_APP_OPTION('s', OPT_SILENT),
00275    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00276    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00277    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00278    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00279    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00280    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00281    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00282    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00283 });
00284 
00285 static int load_config(int reload);
00286 
00287 /*! \page vmlang Voicemail Language Syntaxes Supported
00288 
00289    \par Syntaxes supported, not really language codes.
00290    \arg \b en    - English
00291    \arg \b de    - German
00292    \arg \b es    - Spanish
00293    \arg \b fr    - French
00294    \arg \b it    - Italian
00295    \arg \b nl    - Dutch
00296    \arg \b pt    - Portuguese
00297    \arg \b pt_BR - Portuguese (Brazil)
00298    \arg \b gr    - Greek
00299    \arg \b no    - Norwegian
00300    \arg \b se    - Swedish
00301    \arg \b tw    - Chinese (Taiwan)
00302    \arg \b ua - Ukrainian
00303 
00304 German requires the following additional soundfile:
00305 \arg \b 1F  einE (feminine)
00306 
00307 Spanish requires the following additional soundfile:
00308 \arg \b 1M      un (masculine)
00309 
00310 Dutch, Portuguese & Spanish require the following additional soundfiles:
00311 \arg \b vm-INBOXs singular of 'new'
00312 \arg \b vm-Olds      singular of 'old/heard/read'
00313 
00314 NB these are plural:
00315 \arg \b vm-INBOX  nieuwe (nl)
00316 \arg \b vm-Old    oude (nl)
00317 
00318 Polish uses:
00319 \arg \b vm-new-a  'new', feminine singular accusative
00320 \arg \b vm-new-e  'new', feminine plural accusative
00321 \arg \b vm-new-ych   'new', feminine plural genitive
00322 \arg \b vm-old-a  'old', feminine singular accusative
00323 \arg \b vm-old-e  'old', feminine plural accusative
00324 \arg \b vm-old-ych   'old', feminine plural genitive
00325 \arg \b digits/1-a   'one', not always same as 'digits/1'
00326 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00327 
00328 Swedish uses:
00329 \arg \b vm-nytt      singular of 'new'
00330 \arg \b vm-nya    plural of 'new'
00331 \arg \b vm-gammalt   singular of 'old'
00332 \arg \b vm-gamla  plural of 'old'
00333 \arg \b digits/ett   'one', not always same as 'digits/1'
00334 
00335 Norwegian uses:
00336 \arg \b vm-ny     singular of 'new'
00337 \arg \b vm-nye    plural of 'new'
00338 \arg \b vm-gammel singular of 'old'
00339 \arg \b vm-gamle  plural of 'old'
00340 
00341 Dutch also uses:
00342 \arg \b nl-om     'at'?
00343 
00344 Spanish also uses:
00345 \arg \b vm-youhaveno
00346 
00347 Italian requires the following additional soundfile:
00348 
00349 For vm_intro_it:
00350 \arg \b vm-nuovo  new
00351 \arg \b vm-nuovi  new plural
00352 \arg \b vm-vecchio   old
00353 \arg \b vm-vecchi old plural
00354 
00355 Chinese (Taiwan) requires the following additional soundfile:
00356 \arg \b vm-tong      A class-word for call (tong1)
00357 \arg \b vm-ri     A class-word for day (ri4)
00358 \arg \b vm-you    You (ni3)
00359 \arg \b vm-haveno   Have no (mei2 you3)
00360 \arg \b vm-have     Have (you3)
00361 \arg \b vm-listen   To listen (yao4 ting1)
00362 
00363 
00364 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00365 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00366 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00367 
00368 */
00369 
00370 struct baseio {
00371    int iocp;
00372    int iolen;
00373    int linelength;
00374    int ateof;
00375    unsigned char iobuf[BASEMAXINLINE];
00376 };
00377 
00378 /*! Structure for linked list of users 
00379  * Use ast_vm_user_destroy() to free one of these structures. */
00380 struct ast_vm_user {
00381    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00382    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00383    char password[80];               /*!< Secret pin code, numbers only */
00384    char fullname[80];               /*!< Full name, for directory app */
00385    char email[80];                  /*!< E-mail address */
00386    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00387    char serveremail[80];            /*!< From: Mail address */
00388    char mailcmd[160];               /*!< Configurable mail command */
00389    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00390    char zonetag[80];                /*!< Time zone */
00391    char callback[80];
00392    char dialout[80];
00393    char uniqueid[80];               /*!< Unique integer identifier */
00394    char exit[80];
00395    char attachfmt[20];              /*!< Attachment format */
00396    unsigned int flags;              /*!< VM_ flags */ 
00397    int saydurationm;
00398    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00399    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00400    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00401 #ifdef IMAP_STORAGE
00402    char imapuser[80];               /*!< IMAP server login */
00403    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00404    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00405    int imapversion;                 /*!< If configuration changes, use the new values */
00406 #endif
00407    double volgain;                  /*!< Volume gain for voicemails sent via email */
00408    AST_LIST_ENTRY(ast_vm_user) list;
00409 };
00410 
00411 /*! Voicemail time zones */
00412 struct vm_zone {
00413    AST_LIST_ENTRY(vm_zone) list;
00414    char name[80];
00415    char timezone[80];
00416    char msg_format[512];
00417 };
00418 
00419 #define VMSTATE_MAX_MSG_ARRAY 256
00420 
00421 /*! Voicemail mailbox state */
00422 struct vm_state {
00423    char curbox[80];
00424    char username[80];
00425    char context[80];
00426    char curdir[PATH_MAX];
00427    char vmbox[PATH_MAX];
00428    char fn[PATH_MAX];
00429    char intro[PATH_MAX];
00430    int *deleted;
00431    int *heard;
00432    int curmsg;
00433    int lastmsg;
00434    int newmessages;
00435    int oldmessages;
00436    int urgentmessages;
00437    int starting;
00438    int repeats;
00439 #ifdef IMAP_STORAGE
00440    ast_mutex_t lock;
00441    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00442    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00443    MAILSTREAM *mailstream;
00444    int vmArrayIndex;
00445    char imapuser[80];                   /*!< IMAP server login */
00446    int imapversion;
00447    int interactive;
00448    char introfn[PATH_MAX];              /*!< Name of prepended file */
00449    unsigned int quota_limit;
00450    unsigned int quota_usage;
00451    struct vm_state *persist_vms;
00452 #endif
00453 };
00454 
00455 #ifdef ODBC_STORAGE
00456 static char odbc_database[80];
00457 static char odbc_table[80];
00458 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00459 #define DISPOSE(a,b) remove_file(a,b)
00460 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00461 #define EXISTS(a,b,c,d) (message_exists(a,b))
00462 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00463 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00464 #define DELETE(a,b,c,d) (delete_file(a,b))
00465 #else
00466 #ifdef IMAP_STORAGE
00467 #define DISPOSE(a,b) (imap_remove_file(a,b))
00468 #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))
00469 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00470 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00471 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00472 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00473 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00474 #else
00475 #define RETRIEVE(a,b,c,d)
00476 #define DISPOSE(a,b)
00477 #define STORE(a,b,c,d,e,f,g,h,i,j)
00478 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00479 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00480 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00481 #define DELETE(a,b,c,d) (vm_delete(c))
00482 #endif
00483 #endif
00484 
00485 static char VM_SPOOL_DIR[PATH_MAX];
00486 
00487 static char ext_pass_cmd[128];
00488 static char ext_pass_check_cmd[128];
00489 
00490 static int my_umask;
00491 
00492 #define PWDCHANGE_INTERNAL (1 << 1)
00493 #define PWDCHANGE_EXTERNAL (1 << 2)
00494 static int pwdchange = PWDCHANGE_INTERNAL;
00495 
00496 #ifdef ODBC_STORAGE
00497 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00498 #else
00499 # ifdef IMAP_STORAGE
00500 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00501 # else
00502 # define tdesc "Comedian Mail (Voicemail System)"
00503 # endif
00504 #endif
00505 
00506 static char userscontext[AST_MAX_EXTENSION] = "default";
00507 
00508 static char *addesc = "Comedian Mail";
00509 
00510 static char *synopsis_vm = "Leave a Voicemail message";
00511 
00512 static char *descrip_vm =
00513    "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
00514    "application allows the calling party to leave a message for the specified\n"
00515    "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00516    "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00517    "specified mailbox does not exist.\n"
00518    "  The Voicemail application will exit if any of the following DTMF digits are\n"
00519    "received:\n"
00520    "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00521    "    * - Jump to the 'a' extension in the current dialplan context.\n"
00522    "  This application will set the following channel variable upon completion:\n"
00523    "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00524    "               application. The possible values are:\n"
00525    "               SUCCESS | USEREXIT | FAILED\n\n"
00526    "  Options:\n"
00527    "    b    - Play the 'busy' greeting to the calling party.\n"
00528    "    d([c]) - Accept digits for a new extension in context c, if played during\n"
00529    "             the greeting.  Context defaults to the current context.\n"
00530    "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00531    "           message. The units are whole-number decibels (dB).\n"
00532    "           Only works on supported technologies, which is DAHDI only.\n"
00533    "    s    - Skip the playback of instructions for leaving a message to the\n"
00534    "           calling party.\n"
00535    "    u    - Play the 'unavailable' greeting.\n"
00536    "    U    - Mark message as Urgent.\n"
00537    "    P    - Mark message as PRIORITY.\n";
00538 
00539 static char *synopsis_vmain = "Check Voicemail messages";
00540 
00541 static char *descrip_vmain =
00542    "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
00543    "calling party to check voicemail messages. A specific mailbox, and optional\n"
00544    "corresponding context, may be specified. If a mailbox is not provided, the\n"
00545    "calling party will be prompted to enter one. If a context is not specified,\n"
00546    "the 'default' context will be used.\n\n"
00547    "  Options:\n"
00548    "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00549    "           is entered by the caller.\n"
00550    "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00551    "           message. The units are whole-number decibels (dB).\n"
00552    "    s    - Skip checking the passcode for the mailbox.\n"
00553    "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00554    "           Accepted values are:\n"
00555    "               0 for INBOX\n"
00556    "               1 for Old\n"
00557    "               2 for Work\n"
00558    "               3 for Family\n"
00559    "               4 for Friends\n"
00560    "               5 for Cust1\n"
00561    "               6 for Cust2\n"
00562    "               7 for Cust3\n"
00563    "               8 for Cust4\n"
00564    "               9 for Cust5\n"
00565    "           Defaults to 0 (INBOX).\n";
00566 
00567 static char *synopsis_vm_box_exists =
00568 "Check to see if Voicemail mailbox exists";
00569 
00570 static char *descrip_vm_box_exists =
00571    "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
00572    "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00573    "will be used.\n"
00574    "  This application will set the following channel variable upon completion:\n"
00575    "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00576    "                        MailboxExists application. Possible values include:\n"
00577    "                        SUCCESS | FAILED\n\n"
00578    "  Options: (none)\n";
00579 
00580 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
00581 
00582 static char *descrip_vmauthenticate =
00583    "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
00584    "same way as the Authenticate application, but the passwords are taken from\n"
00585    "voicemail.conf.\n"
00586    "  If the mailbox is specified, only that mailbox's password will be considered\n"
00587    "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00588    "be set with the authenticated mailbox.\n\n"
00589    "  Options:\n"
00590    "    s - Skip playing the initial prompts.\n";
00591 
00592 /* Leave a message */
00593 static char *app = "VoiceMail";
00594 
00595 /* Check mail, control, etc */
00596 static char *app2 = "VoiceMailMain";
00597 
00598 static char *app3 = "MailboxExists";
00599 static char *app4 = "VMAuthenticate";
00600 
00601 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00602 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00603 static char zonetag[80];
00604 static int maxsilence;
00605 static int maxmsg;
00606 static int maxdeletedmsg;
00607 static int silencethreshold = 128;
00608 static char serveremail[80];
00609 static char mailcmd[160];  /* Configurable mail cmd */
00610 static char externnotify[160]; 
00611 static struct ast_smdi_interface *smdi_iface = NULL;
00612 static char vmfmts[80];
00613 static double volgain;
00614 static int vmminsecs;
00615 static int vmmaxsecs;
00616 static int maxgreet;
00617 static int skipms;
00618 static int maxlogins;
00619 static int minpassword;
00620 
00621 /*! Poll mailboxes for changes since there is something external to
00622  *  app_voicemail that may change them. */
00623 static unsigned int poll_mailboxes;
00624 
00625 /*! Polling frequency */
00626 static unsigned int poll_freq;
00627 /*! By default, poll every 30 seconds */
00628 #define DEFAULT_POLL_FREQ 30
00629 
00630 AST_MUTEX_DEFINE_STATIC(poll_lock);
00631 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00632 static pthread_t poll_thread = AST_PTHREADT_NULL;
00633 static unsigned char poll_thread_run;
00634 
00635 /*! Subscription to ... MWI event subscriptions */
00636 static struct ast_event_sub *mwi_sub_sub;
00637 /*! Subscription to ... MWI event un-subscriptions */
00638 static struct ast_event_sub *mwi_unsub_sub;
00639 
00640 /*!
00641  * \brief An MWI subscription
00642  *
00643  * This is so we can keep track of which mailboxes are subscribed to.
00644  * This way, we know which mailboxes to poll when the pollmailboxes
00645  * option is being used.
00646  */
00647 struct mwi_sub {
00648    AST_RWLIST_ENTRY(mwi_sub) entry;
00649    int old_urgent;
00650    int old_new;
00651    int old_old;
00652    uint32_t uniqueid;
00653    char mailbox[1];
00654 };
00655 
00656 struct mwi_sub_task {
00657    const char *mailbox;
00658    const char *context;
00659    uint32_t uniqueid;
00660 };
00661 
00662 static struct ast_taskprocessor *mwi_subscription_tps;
00663 
00664 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00665 
00666 /* custom audio control prompts for voicemail playback */
00667 static char listen_control_forward_key[12];
00668 static char listen_control_reverse_key[12];
00669 static char listen_control_pause_key[12];
00670 static char listen_control_restart_key[12];
00671 static char listen_control_stop_key[12];
00672 
00673 /* custom password sounds */
00674 static char vm_password[80] = "vm-password";
00675 static char vm_newpassword[80] = "vm-newpassword";
00676 static char vm_passchanged[80] = "vm-passchanged";
00677 static char vm_reenterpassword[80] = "vm-reenterpassword";
00678 static char vm_mismatch[80] = "vm-mismatch";
00679 static char vm_invalid_password[80] = "vm-invalid-password";
00680 static char vm_pls_try_again[80] = "vm-pls-try-again";
00681 
00682 static struct ast_flags globalflags = {0};
00683 
00684 static int saydurationminfo;
00685 
00686 static char dialcontext[AST_MAX_CONTEXT] = "";
00687 static char callcontext[AST_MAX_CONTEXT] = "";
00688 static char exitcontext[AST_MAX_CONTEXT] = "";
00689 
00690 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00691 
00692 
00693 static char *emailbody = NULL;
00694 static char *emailsubject = NULL;
00695 static char *pagerbody = NULL;
00696 static char *pagersubject = NULL;
00697 static char fromstring[100];
00698 static char pagerfromstring[100];
00699 static char charset[32] = "ISO-8859-1";
00700 
00701 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00702 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00703 static int adsiver = 1;
00704 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00705 
00706 /* Forward declarations - generic */
00707 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00708 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);
00709 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00710 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00711          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00712          signed char record_gain, struct vm_state *vms, char *flag);
00713 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00714 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00715 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);
00716 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);
00717 static void apply_options(struct ast_vm_user *vmu, const char *options);
00718 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);
00719 static int is_valid_dtmf(const char *key);
00720 
00721 struct ao2_container *inprocess_container;
00722 
00723 struct inprocess {
00724    int count;
00725    char *context;
00726    char mailbox[0];
00727 };
00728 
00729 static int inprocess_hash_fn(const void *obj, const int flags)
00730 {
00731    const struct inprocess *i = obj;
00732    return atoi(i->mailbox);
00733 }
00734 
00735 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00736 {
00737    struct inprocess *i = obj, *j = arg;
00738    if (!strcmp(i->mailbox, j->mailbox)) {
00739       return 0;
00740    }
00741    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00742 }
00743 
00744 static int inprocess_count(const char *context, const char *mailbox, int delta)
00745 {
00746    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00747    arg->context = arg->mailbox + strlen(mailbox) + 1;
00748    strcpy(arg->mailbox, mailbox); /* SAFE */
00749    strcpy(arg->context, context); /* SAFE */
00750    ao2_lock(inprocess_container);
00751    if ((i = ao2_find(inprocess_container, arg, 0))) {
00752       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00753       ao2_unlock(inprocess_container);
00754       ao2_ref(i, -1);
00755       return ret;
00756    }
00757    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00758       ao2_unlock(inprocess_container);
00759       return 0;
00760    }
00761    i->context = i->mailbox + strlen(mailbox) + 1;
00762    strcpy(i->mailbox, mailbox); /* SAFE */
00763    strcpy(i->context, context); /* SAFE */
00764    i->count = delta;
00765    ao2_link(inprocess_container, i);
00766    ao2_unlock(inprocess_container);
00767    ao2_ref(i, -1);
00768    return 0;
00769 }
00770 
00771 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00772 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00773 #endif
00774 
00775 /*!
00776  * \brief Strips control and non 7-bit clean characters from input string.
00777  *
00778  * \note To map control and none 7-bit characters to a 7-bit clean characters
00779  *  please use ast_str_encode_mine().
00780  */
00781 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00782 {
00783    char *bufptr = buf;
00784    for (; *input; input++) {
00785       if (*input < 32) {
00786          continue;
00787       }
00788       *bufptr++ = *input;
00789       if (bufptr == buf + buflen - 1) {
00790          break;
00791       }
00792    }
00793    *bufptr = '\0';
00794    return buf;
00795 }
00796 
00797 
00798 /*!
00799  * \brief Sets default voicemail system options to a voicemail user.
00800  *
00801  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00802  * - all the globalflags
00803  * - the saydurationminfo
00804  * - the callcontext
00805  * - the dialcontext
00806  * - the exitcontext
00807  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00808  * - volume gain.
00809  */
00810 static void populate_defaults(struct ast_vm_user *vmu)
00811 {
00812    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00813    if (saydurationminfo)
00814       vmu->saydurationm = saydurationminfo;
00815    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00816    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00817    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00818    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00819    if (vmmaxsecs)
00820       vmu->maxsecs = vmmaxsecs;
00821    if (maxmsg)
00822       vmu->maxmsg = maxmsg;
00823    if (maxdeletedmsg)
00824       vmu->maxdeletedmsg = maxdeletedmsg;
00825    vmu->volgain = volgain;
00826 }
00827 
00828 /*!
00829  * \brief Sets a a specific property value.
00830  * \param vmu The voicemail user object to work with.
00831  * \param var The name of the property to be set.
00832  * \param value The value to be set to the property.
00833  * 
00834  * The property name must be one of the understood properties. See the source for details.
00835  */
00836 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00837 {
00838    int x;
00839    if (!strcasecmp(var, "attach")) {
00840       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00841    } else if (!strcasecmp(var, "attachfmt")) {
00842       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00843    } else if (!strcasecmp(var, "serveremail")) {
00844       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00845    } else if (!strcasecmp(var, "language")) {
00846       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00847    } else if (!strcasecmp(var, "tz")) {
00848       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00849 #ifdef IMAP_STORAGE
00850    } else if (!strcasecmp(var, "imapuser")) {
00851       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00852       vmu->imapversion = imapversion;
00853    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00854       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00855       vmu->imapversion = imapversion;
00856    } else if (!strcasecmp(var, "imapvmshareid")) {
00857       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00858       vmu->imapversion = imapversion;
00859 #endif
00860    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00861       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00862    } else if (!strcasecmp(var, "saycid")){
00863       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00864    } else if (!strcasecmp(var,"sendvoicemail")){
00865       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00866    } else if (!strcasecmp(var, "review")){
00867       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00868    } else if (!strcasecmp(var, "tempgreetwarn")){
00869       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00870    } else if (!strcasecmp(var, "messagewrap")){
00871       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00872    } else if (!strcasecmp(var, "operator")) {
00873       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00874    } else if (!strcasecmp(var, "envelope")){
00875       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00876    } else if (!strcasecmp(var, "moveheard")){
00877       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00878    } else if (!strcasecmp(var, "sayduration")){
00879       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00880    } else if (!strcasecmp(var, "saydurationm")){
00881       if (sscanf(value, "%30d", &x) == 1) {
00882          vmu->saydurationm = x;
00883       } else {
00884          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
00885       }
00886    } else if (!strcasecmp(var, "forcename")){
00887       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00888    } else if (!strcasecmp(var, "forcegreetings")){
00889       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00890    } else if (!strcasecmp(var, "callback")) {
00891       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00892    } else if (!strcasecmp(var, "dialout")) {
00893       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00894    } else if (!strcasecmp(var, "exitcontext")) {
00895       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00896    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
00897       vmu->maxsecs = atoi(value);
00898       if (vmu->maxsecs <= 0) {
00899          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
00900          vmu->maxsecs = vmmaxsecs;
00901       } else {
00902          vmu->maxsecs = atoi(value);
00903       }
00904       if (!strcasecmp(var, "maxmessage"))
00905          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
00906    } else if (!strcasecmp(var, "maxmsg")) {
00907       vmu->maxmsg = atoi(value);
00908       if (vmu->maxmsg <= 0) {
00909          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
00910          vmu->maxmsg = MAXMSG;
00911       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00912          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00913          vmu->maxmsg = MAXMSGLIMIT;
00914       }
00915    } else if (!strcasecmp(var, "backupdeleted")) {
00916       if (sscanf(value, "%30d", &x) == 1)
00917          vmu->maxdeletedmsg = x;
00918       else if (ast_true(value))
00919          vmu->maxdeletedmsg = MAXMSG;
00920       else
00921          vmu->maxdeletedmsg = 0;
00922 
00923       if (vmu->maxdeletedmsg < 0) {
00924          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
00925          vmu->maxdeletedmsg = MAXMSG;
00926       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
00927          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
00928          vmu->maxdeletedmsg = MAXMSGLIMIT;
00929       }
00930    } else if (!strcasecmp(var, "volgain")) {
00931       sscanf(value, "%30lf", &vmu->volgain);
00932    } else if (!strcasecmp(var, "options")) {
00933       apply_options(vmu, value);
00934    }
00935 }
00936 
00937 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
00938 {
00939    int fds[2], pid = 0;
00940 
00941    memset(buf, 0, len);
00942 
00943    if (pipe(fds)) {
00944       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
00945    } else {
00946       /* good to go*/
00947       pid = ast_safe_fork(0);
00948 
00949       if (pid < 0) {
00950          /* ok maybe not */
00951          close(fds[0]);
00952          close(fds[1]);
00953          snprintf(buf, len, "FAILURE: Fork failed");
00954       } else if (pid) {
00955          /* parent */
00956          close(fds[1]);
00957          if (read(fds[0], buf, len) < 0) {
00958             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00959          }
00960          close(fds[0]);
00961       } else {
00962          /*  child */
00963          AST_DECLARE_APP_ARGS(arg,
00964             AST_APP_ARG(v)[20];
00965          );
00966          char *mycmd = ast_strdupa(command);
00967 
00968          close(fds[0]);
00969          dup2(fds[1], STDOUT_FILENO);
00970          close(fds[1]);
00971          ast_close_fds_above_n(STDOUT_FILENO);
00972 
00973          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
00974 
00975          execv(arg.v[0], arg.v); 
00976          printf("FAILURE: %s", strerror(errno));
00977          _exit(0);
00978       }
00979    }
00980    return buf;
00981 }
00982 
00983 /*!
00984  * \brief Check that password meets minimum required length
00985  * \param vmu The voicemail user to change the password for.
00986  * \param password The password string to check
00987  *
00988  * \return zero on ok, 1 on not ok.
00989  */
00990 static int check_password(struct ast_vm_user *vmu, char *password)
00991 {
00992    /* check minimum length */
00993    if (strlen(password) < minpassword)
00994       return 1;
00995    if (!ast_strlen_zero(ext_pass_check_cmd)) {
00996       char cmd[255], buf[255];
00997 
00998       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
00999 
01000       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
01001       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
01002          ast_debug(5, "Result: %s\n", buf);
01003          if (!strncasecmp(buf, "VALID", 5)) {
01004             ast_debug(3, "Passed password check: '%s'\n", buf);
01005             return 0;
01006          } else if (!strncasecmp(buf, "FAILURE", 7)) {
01007             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
01008             return 0;
01009          } else {
01010             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
01011             return 1;
01012          }
01013       }
01014    }
01015    return 0;
01016 }
01017 
01018 /*! 
01019  * \brief Performs a change of the voicemail passowrd in the realtime engine.
01020  * \param vmu The voicemail user to change the password for.
01021  * \param password The new value to be set to the password for this user.
01022  * 
01023  * This only works if there is a realtime engine configured.
01024  * This is called from the (top level) vm_change_password.
01025  *
01026  * \return zero on success, -1 on error.
01027  */
01028 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
01029 {
01030    int res = -1;
01031    if (!strcmp(vmu->password, password)) {
01032       /* No change (but an update would return 0 rows updated, so we opt out here) */
01033       res = 0;
01034    } else if (!ast_strlen_zero(vmu->uniqueid)) {
01035       if (strlen(password) > 10) {
01036          ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
01037       }
01038       if (ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL) > 0) {
01039          ast_copy_string(vmu->password, password, sizeof(vmu->password));
01040          res = 0;
01041       }
01042    }
01043    return res;
01044 }
01045 
01046 /*!
01047  * \brief Destructively Parse options and apply.
01048  */
01049 static void apply_options(struct ast_vm_user *vmu, const char *options)
01050 {  
01051    char *stringp;
01052    char *s;
01053    char *var, *value;
01054    stringp = ast_strdupa(options);
01055    while ((s = strsep(&stringp, "|"))) {
01056       value = s;
01057       if ((var = strsep(&value, "=")) && value) {
01058          apply_option(vmu, var, value);
01059       }
01060    }  
01061 }
01062 
01063 /*!
01064  * \brief Loads the options specific to a voicemail user.
01065  * 
01066  * This is called when a vm_user structure is being set up, such as from load_options.
01067  */
01068 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
01069 {
01070    struct ast_variable *tmp;
01071    tmp = var;
01072    while (tmp) {
01073       if (!strcasecmp(tmp->name, "vmsecret")) {
01074          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
01075       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
01076          if (ast_strlen_zero(retval->password))
01077             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
01078       } else if (!strcasecmp(tmp->name, "uniqueid")) {
01079          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
01080       } else if (!strcasecmp(tmp->name, "pager")) {
01081          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
01082       } else if (!strcasecmp(tmp->name, "email")) {
01083          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
01084       } else if (!strcasecmp(tmp->name, "fullname")) {
01085          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
01086       } else if (!strcasecmp(tmp->name, "context")) {
01087          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
01088 #ifdef IMAP_STORAGE
01089       } else if (!strcasecmp(tmp->name, "imapuser")) {
01090          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
01091          retval->imapversion = imapversion;
01092       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
01093          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
01094          retval->imapversion = imapversion;
01095       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01096          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01097          retval->imapversion = imapversion;
01098 #endif
01099       } else
01100          apply_option(retval, tmp->name, tmp->value);
01101       tmp = tmp->next;
01102    } 
01103 }
01104 
01105 /*!
01106  * \brief Determines if a DTMF key entered is valid.
01107  * \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.
01108  *
01109  * Tests the character entered against the set of valid DTMF characters. 
01110  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01111  */
01112 static int is_valid_dtmf(const char *key)
01113 {
01114    int i;
01115    char *local_key = ast_strdupa(key);
01116 
01117    for (i = 0; i < strlen(key); ++i) {
01118       if (!strchr(VALID_DTMF, *local_key)) {
01119          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01120          return 0;
01121       }
01122       local_key++;
01123    }
01124    return 1;
01125 }
01126 
01127 /*!
01128  * \brief Finds a voicemail user from the realtime engine.
01129  * \param ivm
01130  * \param context
01131  * \param mailbox
01132  *
01133  * 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.
01134  *
01135  * \return The ast_vm_user structure for the user that was found.
01136  */
01137 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01138 {
01139    struct ast_variable *var;
01140    struct ast_vm_user *retval;
01141 
01142    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01143       if (!ivm)
01144          ast_set_flag(retval, VM_ALLOCED);   
01145       else
01146          memset(retval, 0, sizeof(*retval));
01147       if (mailbox) 
01148          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01149       populate_defaults(retval);
01150       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01151          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01152       else
01153          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01154       if (var) {
01155          apply_options_full(retval, var);
01156          ast_variables_destroy(var);
01157       } else { 
01158          if (!ivm) 
01159             ast_free(retval);
01160          retval = NULL;
01161       }  
01162    } 
01163    return retval;
01164 }
01165 
01166 /*!
01167  * \brief Finds a voicemail user from the users file or the realtime engine.
01168  * \param ivm
01169  * \param context
01170  * \param mailbox
01171  * 
01172  * \return The ast_vm_user structure for the user that was found.
01173  */
01174 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01175 {
01176    /* This function could be made to generate one from a database, too */
01177    struct ast_vm_user *vmu=NULL, *cur;
01178    AST_LIST_LOCK(&users);
01179 
01180    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01181       context = "default";
01182 
01183    AST_LIST_TRAVERSE(&users, cur, list) {
01184 #ifdef IMAP_STORAGE
01185       if (cur->imapversion != imapversion) {
01186          continue;
01187       }
01188 #endif
01189       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01190          break;
01191       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01192          break;
01193    }
01194    if (cur) {
01195       /* Make a copy, so that on a reload, we have no race */
01196       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01197          memcpy(vmu, cur, sizeof(*vmu));
01198          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01199          AST_LIST_NEXT(vmu, list) = NULL;
01200       }
01201    } else
01202       vmu = find_user_realtime(ivm, context, mailbox);
01203    AST_LIST_UNLOCK(&users);
01204    return vmu;
01205 }
01206 
01207 /*!
01208  * \brief Resets a user password to a specified password.
01209  * \param context
01210  * \param mailbox
01211  * \param newpass
01212  *
01213  * This does the actual change password work, called by the vm_change_password() function.
01214  *
01215  * \return zero on success, -1 on error.
01216  */
01217 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01218 {
01219    /* This function could be made to generate one from a database, too */
01220    struct ast_vm_user *cur;
01221    int res = -1;
01222    AST_LIST_LOCK(&users);
01223    AST_LIST_TRAVERSE(&users, cur, list) {
01224       if ((!context || !strcasecmp(context, cur->context)) &&
01225          (!strcasecmp(mailbox, cur->mailbox)))
01226             break;
01227    }
01228    if (cur) {
01229       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01230       res = 0;
01231    }
01232    AST_LIST_UNLOCK(&users);
01233    return res;
01234 }
01235 
01236 /*! 
01237  * \brief The handler for the change password option.
01238  * \param vmu The voicemail user to work with.
01239  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01240  * 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.
01241  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01242  */
01243 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01244 {
01245    struct ast_config   *cfg=NULL;
01246    struct ast_variable *var=NULL;
01247    struct ast_category *cat=NULL;
01248    char *category=NULL, *value=NULL, *new=NULL;
01249    const char *tmp=NULL;
01250    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01251    if (!change_password_realtime(vmu, newpassword))
01252       return;
01253 
01254    /* check voicemail.conf */
01255    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
01256       while ((category = ast_category_browse(cfg, category))) {
01257          if (!strcasecmp(category, vmu->context)) {
01258             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01259                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01260                break;
01261             }
01262             value = strstr(tmp,",");
01263             if (!value) {
01264                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01265                break;
01266             }
01267             new = alloca((strlen(value)+strlen(newpassword)+1));
01268             sprintf(new,"%s%s", newpassword, value);
01269             if (!(cat = ast_category_get(cfg, category))) {
01270                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01271                break;
01272             }
01273             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01274          }
01275       }
01276       /* save the results */
01277       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01278       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01279       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01280    }
01281    category = NULL;
01282    var = NULL;
01283    /* check users.conf and update the password stored for the mailbox*/
01284    /* if no vmsecret entry exists create one. */
01285    if ((cfg = ast_config_load("users.conf", config_flags))) {
01286       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01287       while ((category = ast_category_browse(cfg, category))) {
01288          ast_debug(4, "users.conf: %s\n", category);
01289          if (!strcasecmp(category, vmu->mailbox)) {
01290             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01291                ast_debug(3, "looks like we need to make vmsecret!\n");
01292                var = ast_variable_new("vmsecret", newpassword, "");
01293             } 
01294             new = alloca(strlen(newpassword)+1);
01295             sprintf(new, "%s", newpassword);
01296             if (!(cat = ast_category_get(cfg, category))) {
01297                ast_debug(4, "failed to get category!\n");
01298                break;
01299             }
01300             if (!var)      
01301                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01302             else
01303                ast_variable_append(cat, var);
01304          }
01305       }
01306       /* save the results and clean things up */
01307       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01308       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01309       config_text_file_save("users.conf", cfg, "AppVoicemail");
01310    }
01311 }
01312 
01313 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01314 {
01315    char buf[255];
01316    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01317    if (!ast_safe_system(buf)) {
01318       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01319       /* Reset the password in memory, too */
01320       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01321    }
01322 }
01323 
01324 /*! 
01325  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01326  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01327  * \param len The length of the path string that was written out.
01328  * 
01329  * The path is constructed as 
01330  *    VM_SPOOL_DIRcontext/ext/folder
01331  *
01332  * \return zero on success, -1 on error.
01333  */
01334 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01335 {
01336    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01337 }
01338 
01339 /*! 
01340  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01341  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01342  * \param len The length of the path string that was written out.
01343  * 
01344  * The path is constructed as 
01345  *    VM_SPOOL_DIRcontext/ext/folder
01346  *
01347  * \return zero on success, -1 on error.
01348  */
01349 static int make_file(char *dest, const int len, const char *dir, const int num)
01350 {
01351    return snprintf(dest, len, "%s/msg%04d", dir, num);
01352 }
01353 
01354 /* same as mkstemp, but return a FILE * */
01355 static FILE *vm_mkftemp(char *template)
01356 {
01357    FILE *p = NULL;
01358    int pfd = mkstemp(template);
01359    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01360    if (pfd > -1) {
01361       p = fdopen(pfd, "w+");
01362       if (!p) {
01363          close(pfd);
01364          pfd = -1;
01365       }
01366    }
01367    return p;
01368 }
01369 
01370 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01371  * \param dest    String. base directory.
01372  * \param len     Length of dest.
01373  * \param context String. Ignored if is null or empty string.
01374  * \param ext     String. Ignored if is null or empty string.
01375  * \param folder  String. Ignored if is null or empty string. 
01376  * \return -1 on failure, 0 on success.
01377  */
01378 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01379 {
01380    mode_t   mode = VOICEMAIL_DIR_MODE;
01381    int res;
01382 
01383    make_dir(dest, len, context, ext, folder);
01384    if ((res = ast_mkdir(dest, mode))) {
01385       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01386       return -1;
01387    }
01388    return 0;
01389 }
01390 
01391 static const char *mbox(int id)
01392 {
01393    static const char *msgs[] = {
01394 #ifdef IMAP_STORAGE
01395       imapfolder,
01396 #else
01397       "INBOX",
01398 #endif
01399       "Old",
01400       "Work",
01401       "Family",
01402       "Friends",
01403       "Cust1",
01404       "Cust2",
01405       "Cust3",
01406       "Cust4",
01407       "Cust5",
01408       "Deleted",
01409       "Urgent"
01410    };
01411    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
01412 }
01413 
01414 static void free_user(struct ast_vm_user *vmu)
01415 {
01416    if (ast_test_flag(vmu, VM_ALLOCED))
01417       ast_free(vmu);
01418 }
01419 
01420 /* All IMAP-specific functions should go in this block. This
01421  * keeps them from being spread out all over the code */
01422 #ifdef IMAP_STORAGE
01423 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01424 {
01425    char arg[10];
01426    struct vm_state *vms;
01427    unsigned long messageNum;
01428 
01429    /* If greetings aren't stored in IMAP, just delete the file */
01430    if (msgnum < 0 && !imapgreetings) {
01431       ast_filedelete(file, NULL);
01432       return;
01433    }
01434 
01435    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01436       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);
01437       return;
01438    }
01439 
01440    /* find real message number based on msgnum */
01441    /* this may be an index into vms->msgArray based on the msgnum. */
01442    messageNum = vms->msgArray[msgnum];
01443    if (messageNum == 0) {
01444       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01445       return;
01446    }
01447    if (option_debug > 2)
01448       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01449    /* delete message */
01450    snprintf (arg, sizeof(arg), "%lu",messageNum);
01451    ast_mutex_lock(&vms->lock);
01452    mail_setflag (vms->mailstream,arg,"\\DELETED");
01453    mail_expunge(vms->mailstream);
01454    ast_mutex_unlock(&vms->lock);
01455 }
01456 
01457 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
01458 {
01459    struct vm_state *vms_p;
01460    char *file, *filename;
01461    char *attachment;
01462    int ret = 0, i;
01463    BODY *body;
01464 
01465    /* This function is only used for retrieval of IMAP greetings
01466     * regular messages are not retrieved this way, nor are greetings
01467     * if they are stored locally*/
01468    if (msgnum > -1 || !imapgreetings) {
01469       return 0;
01470    } else {
01471       file = strrchr(ast_strdupa(dir), '/');
01472       if (file)
01473          *file++ = '\0';
01474       else {
01475          ast_debug (1, "Failed to procure file name from directory passed.\n");
01476          return -1;
01477       }
01478    }
01479 
01480    /* check if someone is accessing this box right now... */
01481    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
01482       !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01483       /* Unlike when retrieving a message, it is reasonable not to be able to find a 
01484       * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
01485       * that's all we need to do.
01486       */
01487       if (!(vms_p = create_vm_state_from_user(vmu))) {
01488          ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
01489          return -1;
01490       }
01491    }
01492    
01493    /* Greetings will never have a prepended message */
01494    *vms_p->introfn = '\0';
01495 
01496    ast_mutex_lock(&vms_p->lock);
01497    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01498    if (!vms_p->mailstream) {
01499       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01500       ast_mutex_unlock(&vms_p->lock);
01501       return -1;
01502    }
01503 
01504    /*XXX Yuck, this could probably be done a lot better */
01505    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01506       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01507       /* We have the body, now we extract the file name of the first attachment. */
01508       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01509          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01510       } else {
01511          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01512          ast_mutex_unlock(&vms_p->lock);
01513          return -1;
01514       }
01515       filename = strsep(&attachment, ".");
01516       if (!strcmp(filename, file)) {
01517          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01518          vms_p->msgArray[vms_p->curmsg] = i + 1;
01519          save_body(body, vms_p, "2", attachment, 0);
01520          ast_mutex_unlock(&vms_p->lock);
01521          return 0;
01522       }
01523    }
01524    ast_mutex_unlock(&vms_p->lock);
01525 
01526    return -1;
01527 }
01528 
01529 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01530 {
01531    BODY *body;
01532    char *header_content;
01533    char *attachedfilefmt;
01534    char buf[80];
01535    struct vm_state *vms;
01536    char text_file[PATH_MAX];
01537    FILE *text_file_ptr;
01538    int res = 0;
01539    struct ast_vm_user *vmu;
01540 
01541    if (!(vmu = find_user(NULL, context, mailbox))) {
01542       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01543       return -1;
01544    }
01545    
01546    if (msgnum < 0) {
01547       if (imapgreetings) {
01548          res = imap_retrieve_greeting(dir, msgnum, vmu);
01549          goto exit;
01550       } else {
01551          res = 0;
01552          goto exit;
01553       }
01554    }
01555 
01556    /* Before anything can happen, we need a vm_state so that we can
01557     * actually access the imap server through the vms->mailstream
01558     */
01559    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01560       /* This should not happen. If it does, then I guess we'd
01561        * need to create the vm_state, extract which mailbox to
01562        * open, and then set up the msgArray so that the correct
01563        * IMAP message could be accessed. If I have seen correctly
01564        * though, the vms should be obtainable from the vmstates list
01565        * and should have its msgArray properly set up.
01566        */
01567       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01568       res = -1;
01569       goto exit;
01570    }
01571    
01572    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01573    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01574 
01575    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01576    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01577       res = 0;
01578       goto exit;
01579    }
01580 
01581    if (option_debug > 2)
01582       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01583    if (vms->msgArray[msgnum] == 0) {
01584       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01585       res = -1;
01586       goto exit;
01587    }
01588 
01589    /* This will only work for new messages... */
01590    ast_mutex_lock(&vms->lock);
01591    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01592    ast_mutex_unlock(&vms->lock);
01593    /* empty string means no valid header */
01594    if (ast_strlen_zero(header_content)) {
01595       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01596       res = -1;
01597       goto exit;
01598    }
01599 
01600    ast_mutex_lock(&vms->lock);
01601    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01602    ast_mutex_unlock(&vms->lock);
01603 
01604    /* We have the body, now we extract the file name of the first attachment. */
01605    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01606       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01607    } else {
01608       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01609       res = -1;
01610       goto exit;
01611    }
01612    
01613    /* Find the format of the attached file */
01614 
01615    strsep(&attachedfilefmt, ".");
01616    if (!attachedfilefmt) {
01617       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01618       res = -1;
01619       goto exit;
01620    }
01621    
01622    save_body(body, vms, "2", attachedfilefmt, 0);
01623    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01624       *vms->introfn = '\0';
01625    }
01626 
01627    /* Get info from headers!! */
01628    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01629 
01630    if (!(text_file_ptr = fopen(text_file, "w"))) {
01631       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01632    }
01633 
01634    fprintf(text_file_ptr, "%s\n", "[message]");
01635 
01636    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01637    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01638    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01639    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01640    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01641    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01642    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01643    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01644    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01645    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01646    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01647    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01648    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01649    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01650    fclose(text_file_ptr);
01651 
01652 exit:
01653    free_user(vmu);
01654    return res;
01655 }
01656 
01657 static int folder_int(const char *folder)
01658 {
01659    /*assume a NULL folder means INBOX*/
01660    if (!folder)
01661       return 0;
01662 #ifdef IMAP_STORAGE
01663    if (!strcasecmp(folder, imapfolder))
01664 #else
01665    if (!strcasecmp(folder, "INBOX"))
01666 #endif
01667       return 0;
01668    else if (!strcasecmp(folder, "Old"))
01669       return 1;
01670    else if (!strcasecmp(folder, "Work"))
01671       return 2;
01672    else if (!strcasecmp(folder, "Family"))
01673       return 3;
01674    else if (!strcasecmp(folder, "Friends"))
01675       return 4;
01676    else if (!strcasecmp(folder, "Cust1"))
01677       return 5;
01678    else if (!strcasecmp(folder, "Cust2"))
01679       return 6;
01680    else if (!strcasecmp(folder, "Cust3"))
01681       return 7;
01682    else if (!strcasecmp(folder, "Cust4"))
01683       return 8;
01684    else if (!strcasecmp(folder, "Cust5"))
01685       return 9;
01686    else /*assume they meant INBOX if folder is not found otherwise*/
01687       return 0;
01688 }
01689 
01690 static int __messagecount(const char *context, const char *mailbox, const char *folder)
01691 {
01692    SEARCHPGM *pgm;
01693    SEARCHHEADER *hdr;
01694 
01695    struct ast_vm_user *vmu, vmus;
01696    struct vm_state *vms_p;
01697    int ret = 0;
01698    int fold = folder_int(folder);
01699    int urgent = 0;
01700    
01701    /* If URGENT, then look at INBOX */
01702    if (fold == 11) {
01703       fold = NEW_FOLDER;
01704       urgent = 1;
01705    }
01706 
01707    if (ast_strlen_zero(mailbox))
01708       return 0;
01709 
01710    /* We have to get the user before we can open the stream! */
01711    vmu = find_user(&vmus, context, mailbox);
01712    if (!vmu) {
01713       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01714       return -1;
01715    } else {
01716       /* No IMAP account available */
01717       if (vmu->imapuser[0] == '\0') {
01718          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01719          return -1;
01720       }
01721    }
01722    
01723    /* No IMAP account available */
01724    if (vmu->imapuser[0] == '\0') {
01725       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01726       free_user(vmu);
01727       return -1;
01728    }
01729 
01730    /* check if someone is accessing this box right now... */
01731    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01732    if (!vms_p) {
01733       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01734    }
01735    if (vms_p) {
01736       ast_debug(3, "Returning before search - user is logged in\n");
01737       if (fold == 0) { /* INBOX */
01738          return vms_p->newmessages;
01739       }
01740       if (fold == 1) { /* Old messages */
01741          return vms_p->oldmessages;
01742       }
01743       if (fold == 11) {/*Urgent messages*/
01744          return vms_p->urgentmessages;
01745       }
01746    }
01747 
01748    /* add one if not there... */
01749    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01750    if (!vms_p) {
01751       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01752    }
01753 
01754    if (!vms_p) {
01755       vms_p = create_vm_state_from_user(vmu);
01756    }
01757    ret = init_mailstream(vms_p, fold);
01758    if (!vms_p->mailstream) {
01759       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01760       return -1;
01761    }
01762    if (ret == 0) {
01763       ast_mutex_lock(&vms_p->lock);
01764       pgm = mail_newsearchpgm ();
01765       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01766       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01767       pgm->header = hdr;
01768       if (fold != 1) {
01769          pgm->unseen = 1;
01770          pgm->seen = 0;
01771       }
01772       /* In the special case where fold is 1 (old messages) we have to do things a bit
01773        * differently. Old messages are stored in the INBOX but are marked as "seen"
01774        */
01775       else {
01776          pgm->unseen = 0;
01777          pgm->seen = 1;
01778       }
01779       /* look for urgent messages */
01780       if (urgent) {
01781          pgm->flagged = 1;
01782          pgm->unflagged = 0;
01783       }
01784       pgm->undeleted = 1;
01785       pgm->deleted = 0;
01786 
01787       vms_p->vmArrayIndex = 0;
01788       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01789       if (fold == 0 && urgent == 0)
01790          vms_p->newmessages = vms_p->vmArrayIndex;
01791       if (fold == 1)
01792          vms_p->oldmessages = vms_p->vmArrayIndex;
01793       if (fold == 0 && urgent == 1)
01794          vms_p->urgentmessages = vms_p->vmArrayIndex;
01795       /*Freeing the searchpgm also frees the searchhdr*/
01796       mail_free_searchpgm(&pgm);
01797       ast_mutex_unlock(&vms_p->lock);
01798       vms_p->updated = 0;
01799       return vms_p->vmArrayIndex;
01800    } else {
01801       ast_mutex_lock(&vms_p->lock);
01802       mail_ping(vms_p->mailstream);
01803       ast_mutex_unlock(&vms_p->lock);
01804    }
01805    return 0;
01806 }
01807 
01808 /*!
01809  * \brief Gets the number of messages that exist in a mailbox folder.
01810  * \param context
01811  * \param mailbox
01812  * \param folder
01813  * 
01814  * This method is used when IMAP backend is used.
01815  * \return The number of messages in this mailbox folder (zero or more).
01816  */
01817 static int messagecount(const char *context, const char *mailbox, const char *folder)
01818 {
01819    if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
01820       return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
01821    } else {
01822       return __messagecount(context, mailbox, folder);
01823    }
01824 }
01825 
01826 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
01827 {
01828    char *myserveremail = serveremail;
01829    char fn[PATH_MAX];
01830    char introfn[PATH_MAX];
01831    char mailbox[256];
01832    char *stringp;
01833    FILE *p=NULL;
01834    char tmp[80] = "/tmp/astmail-XXXXXX";
01835    long len;
01836    void *buf;
01837    int tempcopy = 0;
01838    STRING str;
01839    int ret; /* for better error checking */
01840    char *imap_flags = NIL;
01841 
01842     /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
01843     if (msgnum < 0 && !imapgreetings) {
01844         return 0;
01845     }
01846 
01847    /* Set urgent flag for IMAP message */
01848    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
01849       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
01850       imap_flags="\\FLAGGED";
01851    }
01852    
01853    /* Attach only the first format */
01854    fmt = ast_strdupa(fmt);
01855    stringp = fmt;
01856    strsep(&stringp, "|");
01857 
01858    if (!ast_strlen_zero(vmu->serveremail))
01859       myserveremail = vmu->serveremail;
01860 
01861    if (msgnum > -1)
01862       make_file(fn, sizeof(fn), dir, msgnum);
01863    else
01864       ast_copy_string (fn, dir, sizeof(fn));
01865 
01866    snprintf(introfn, sizeof(introfn), "%sintro", fn);
01867    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
01868       *introfn = '\0';
01869    }
01870    
01871    if (ast_strlen_zero(vmu->email)) {
01872       /* We need the vmu->email to be set when we call make_email_file, but
01873        * if we keep it set, a duplicate e-mail will be created. So at the end
01874        * of this function, we will revert back to an empty string if tempcopy
01875        * is 1.
01876        */
01877       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01878       tempcopy = 1;
01879    }
01880 
01881    if (!strcmp(fmt, "wav49"))
01882       fmt = "WAV";
01883    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
01884 
01885    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01886       command hangs. */
01887    if (!(p = vm_mkftemp(tmp))) {
01888       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01889       if (tempcopy)
01890          *(vmu->email) = '\0';
01891       return -1;
01892    }
01893 
01894    if (msgnum < 0 && imapgreetings) {
01895       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
01896          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
01897          return -1;
01898       }
01899       imap_delete_old_greeting(fn, vms);
01900    }
01901 
01902    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);
01903    /* read mail file to memory */
01904    len = ftell(p);
01905    rewind(p);
01906    if (!(buf = ast_malloc(len + 1))) {
01907       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
01908       fclose(p);
01909       if (tempcopy)
01910          *(vmu->email) = '\0';
01911       return -1;
01912    }
01913    if (fread(buf, len, 1, p) < len) {
01914       if (ferror(p)) {
01915          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
01916          return -1;
01917       }
01918    }
01919    ((char *)buf)[len] = '\0';
01920    INIT(&str, mail_string, buf, len);
01921    ret = init_mailstream(vms, NEW_FOLDER);
01922    if (ret == 0) {
01923       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
01924       ast_mutex_lock(&vms->lock);
01925       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
01926          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01927       ast_mutex_unlock(&vms->lock);
01928       fclose(p);
01929       unlink(tmp);
01930       ast_free(buf);
01931    } else {
01932       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
01933       fclose(p);
01934       unlink(tmp);
01935       ast_free(buf);
01936       return -1;
01937    }
01938    ast_debug(3, "%s stored\n", fn);
01939    
01940    if (tempcopy)
01941       *(vmu->email) = '\0';
01942    
01943    return 0;
01944 
01945 }
01946 
01947 /*!
01948  * \brief Gets the number of messages that exist in the inbox folder.
01949  * \param mailbox_context
01950  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
01951  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
01952  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
01953  * 
01954  * This method is used when IMAP backend is used.
01955  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
01956  *
01957  * \return zero on success, -1 on error.
01958  */
01959 
01960 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
01961 {
01962    char tmp[PATH_MAX] = "";
01963    char *mailboxnc;
01964    char *context;
01965    char *mb;
01966    char *cur;
01967    if (newmsgs)
01968       *newmsgs = 0;
01969    if (oldmsgs)
01970       *oldmsgs = 0;
01971    if (urgentmsgs)
01972       *urgentmsgs = 0;
01973 
01974    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
01975    /* If no mailbox, return immediately */
01976    if (ast_strlen_zero(mailbox_context))
01977       return 0;
01978    
01979    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01980    context = strchr(tmp, '@');
01981    if (strchr(mailbox_context, ',')) {
01982       int tmpnew, tmpold, tmpurgent;
01983       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01984       mb = tmp;
01985       while ((cur = strsep(&mb, ", "))) {
01986          if (!ast_strlen_zero(cur)) {
01987             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01988                return -1;
01989             else {
01990                if (newmsgs)
01991                   *newmsgs += tmpnew; 
01992                if (oldmsgs)
01993                   *oldmsgs += tmpold;
01994                if (urgentmsgs)
01995                   *urgentmsgs += tmpurgent;
01996             }
01997          }
01998       }
01999       return 0;
02000    }
02001    if (context) {
02002       *context = '\0';
02003       mailboxnc = tmp;
02004       context++;
02005    } else {
02006       context = "default";
02007       mailboxnc = (char *)mailbox_context;
02008    }
02009    if (newmsgs) {
02010       if ((*newmsgs = __messagecount(context, mailboxnc, imapfolder)) < 0) {
02011          return -1;
02012       }
02013    }
02014    if (oldmsgs) {
02015       if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
02016          return -1;
02017       }
02018    }
02019    if (urgentmsgs) {
02020       if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
02021          return -1;
02022       }
02023    }
02024    return 0;
02025 }
02026 
02027 /** 
02028  * \brief Determines if the given folder has messages.
02029  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
02030  * \param folder the folder to look in
02031  *
02032  * This function is used when the mailbox is stored in an IMAP back end.
02033  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
02034  * \return 1 if the folder has one or more messages. zero otherwise.
02035  */
02036 
02037 static int has_voicemail(const char *mailbox, const char *folder)
02038 {
02039    char tmp[256], *tmp2, *box, *context;
02040    ast_copy_string(tmp, mailbox, sizeof(tmp));
02041    tmp2 = tmp;
02042    if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
02043       while ((box = strsep(&tmp2, ",&"))) {
02044          if (!ast_strlen_zero(box)) {
02045             if (has_voicemail(box, folder)) {
02046                return 1;
02047             }
02048          }
02049       }
02050    }
02051    if ((context = strchr(tmp, '@'))) {
02052       *context++ = '\0';
02053    } else {
02054       context = "default";
02055    }
02056    return __messagecount(context, tmp, folder) ? 1 : 0;
02057 }
02058 
02059 /*!
02060  * \brief Copies a message from one mailbox to another.
02061  * \param chan
02062  * \param vmu
02063  * \param imbox
02064  * \param msgnum
02065  * \param duration
02066  * \param recip
02067  * \param fmt
02068  * \param dir
02069  *
02070  * This works with IMAP storage based mailboxes.
02071  *
02072  * \return zero on success, -1 on error.
02073  */
02074 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)
02075 {
02076    struct vm_state *sendvms = NULL, *destvms = NULL;
02077    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
02078    if (msgnum >= recip->maxmsg) {
02079       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
02080       return -1;
02081    }
02082    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
02083       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
02084       return -1;
02085    }
02086    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
02087       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
02088       return -1;
02089    }
02090    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
02091    ast_mutex_lock(&sendvms->lock);
02092    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
02093       ast_mutex_unlock(&sendvms->lock);
02094       return 0;
02095    }
02096    ast_mutex_unlock(&sendvms->lock);
02097    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
02098    return -1;
02099 }
02100 
02101 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
02102 {
02103    char tmp[256], *t = tmp;
02104    size_t left = sizeof(tmp);
02105    
02106    if (box == OLD_FOLDER) {
02107       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02108    } else {
02109       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02110    }
02111 
02112    if (box == NEW_FOLDER) {
02113       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02114    } else {
02115       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02116    }
02117 
02118    /* Build up server information */
02119    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02120 
02121    /* Add authentication user if present */
02122    if (!ast_strlen_zero(authuser))
02123       ast_build_string(&t, &left, "/authuser=%s", authuser);
02124 
02125    /* Add flags if present */
02126    if (!ast_strlen_zero(imapflags))
02127       ast_build_string(&t, &left, "/%s", imapflags);
02128 
02129    /* End with username */
02130 #if 1
02131    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02132 #else
02133    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
02134 #endif
02135    if (box == NEW_FOLDER || box == OLD_FOLDER)
02136       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02137    else if (box == GREETINGS_FOLDER)
02138       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02139    else {   /* Other folders such as Friends, Family, etc... */
02140       if (!ast_strlen_zero(imapparentfolder)) {
02141          /* imapparentfolder would typically be set to INBOX */
02142          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02143       } else {
02144          snprintf(spec, len, "%s%s", tmp, mbox(box));
02145       }
02146    }
02147 }
02148 
02149 static int init_mailstream(struct vm_state *vms, int box)
02150 {
02151    MAILSTREAM *stream = NIL;
02152    long debug;
02153    char tmp[256];
02154    
02155    if (!vms) {
02156       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02157       return -1;
02158    }
02159    if (option_debug > 2)
02160       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02161    if (vms->mailstream == NIL || !vms->mailstream) {
02162       if (option_debug)
02163          ast_log (LOG_DEBUG,"mailstream not set.\n");
02164    } else {
02165       stream = vms->mailstream;
02166    }
02167    /* debug = T;  user wants protocol telemetry? */
02168    debug = NIL;  /* NO protocol telemetry? */
02169 
02170    if (delimiter == '\0') {      /* did not probe the server yet */
02171       char *cp;
02172 #ifdef USE_SYSTEM_IMAP
02173 #include <imap/linkage.c>
02174 #elif defined(USE_SYSTEM_CCLIENT)
02175 #include <c-client/linkage.c>
02176 #else
02177 #include "linkage.c"
02178 #endif
02179       /* Connect to INBOX first to get folders delimiter */
02180       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02181       ast_mutex_lock(&vms->lock);
02182       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02183       ast_mutex_unlock(&vms->lock);
02184       if (stream == NIL) {
02185          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02186          return -1;
02187       }
02188       get_mailbox_delimiter(stream);
02189       /* update delimiter in imapfolder */
02190       for (cp = imapfolder; *cp; cp++)
02191          if (*cp == '/')
02192             *cp = delimiter;
02193    }
02194    /* Now connect to the target folder */
02195    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02196    if (option_debug > 2)
02197       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02198    ast_mutex_lock(&vms->lock);
02199    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02200    ast_mutex_unlock(&vms->lock);
02201    if (vms->mailstream == NIL) {
02202       return -1;
02203    } else {
02204       return 0;
02205    }
02206 }
02207 
02208 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02209 {
02210    SEARCHPGM *pgm;
02211    SEARCHHEADER *hdr;
02212    int ret, urgent = 0;
02213 
02214    /* If Urgent, then look at INBOX */
02215    if (box == 11) {
02216       box = NEW_FOLDER;
02217       urgent = 1;
02218    }
02219 
02220    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02221    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02222    vms->imapversion = vmu->imapversion;
02223 
02224    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02225       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02226       return -1;
02227    }
02228    
02229    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02230    
02231    /* Check Quota */
02232    if  (box == 0)  {
02233       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02234       check_quota(vms,(char *)mbox(box));
02235    }
02236 
02237    ast_mutex_lock(&vms->lock);
02238    pgm = mail_newsearchpgm();
02239 
02240    /* Check IMAP folder for Asterisk messages only... */
02241    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02242    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02243    pgm->header = hdr;
02244    pgm->deleted = 0;
02245    pgm->undeleted = 1;
02246 
02247    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02248    if (box == NEW_FOLDER && urgent == 1) {
02249       pgm->unseen = 1;
02250       pgm->seen = 0;
02251       pgm->flagged = 1;
02252       pgm->unflagged = 0;
02253    } else if (box == NEW_FOLDER && urgent == 0) {
02254       pgm->unseen = 1;
02255       pgm->seen = 0;
02256       pgm->flagged = 0;
02257       pgm->unflagged = 1;
02258    } else if (box == OLD_FOLDER) {
02259       pgm->seen = 1;
02260       pgm->unseen = 0;
02261    }
02262 
02263    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02264 
02265    vms->vmArrayIndex = 0;
02266    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02267    vms->lastmsg = vms->vmArrayIndex - 1;
02268    mail_free_searchpgm(&pgm);
02269 
02270    ast_mutex_unlock(&vms->lock);
02271    return 0;
02272 }
02273 
02274 static void write_file(char *filename, char *buffer, unsigned long len)
02275 {
02276    FILE *output;
02277 
02278    output = fopen (filename, "w");
02279    if (fwrite(buffer, len, 1, output) != 1) {
02280       if (ferror(output)) {
02281          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02282       }
02283    }
02284    fclose (output);
02285 }
02286 
02287 static void update_messages_by_imapuser(const char *user, unsigned long number)
02288 {
02289    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02290 
02291    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02292       return;
02293    }
02294 
02295    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02296    vms->msgArray[vms->vmArrayIndex++] = number;
02297 }
02298 
02299 void mm_searched(MAILSTREAM *stream, unsigned long number)
02300 {
02301    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02302 
02303    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02304       return;
02305 
02306    update_messages_by_imapuser(user, number);
02307 }
02308 
02309 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02310 {
02311    struct ast_variable *var;
02312    struct ast_vm_user *vmu;
02313 
02314    vmu = ast_calloc(1, sizeof *vmu);
02315    if (!vmu)
02316       return NULL;
02317    ast_set_flag(vmu, VM_ALLOCED);
02318    populate_defaults(vmu);
02319 
02320    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02321    if (var) {
02322       apply_options_full(vmu, var);
02323       ast_variables_destroy(var);
02324       return vmu;
02325    } else {
02326       ast_free(vmu);
02327       return NULL;
02328    }
02329 }
02330 
02331 /* Interfaces to C-client */
02332 
02333 void mm_exists(MAILSTREAM * stream, unsigned long number)
02334 {
02335    /* mail_ping will callback here if new mail! */
02336    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02337    if (number == 0) return;
02338    set_update(stream);
02339 }
02340 
02341 
02342 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02343 {
02344    /* mail_ping will callback here if expunged mail! */
02345    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02346    if (number == 0) return;
02347    set_update(stream);
02348 }
02349 
02350 
02351 void mm_flags(MAILSTREAM * stream, unsigned long number)
02352 {
02353    /* mail_ping will callback here if read mail! */
02354    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02355    if (number == 0) return;
02356    set_update(stream);
02357 }
02358 
02359 
02360 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02361 {
02362    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02363    mm_log (string, errflg);
02364 }
02365 
02366 
02367 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02368 {
02369    if (delimiter == '\0') {
02370       delimiter = delim;
02371    }
02372 
02373    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02374    if (attributes & LATT_NOINFERIORS)
02375       ast_debug(5, "no inferiors\n");
02376    if (attributes & LATT_NOSELECT)
02377       ast_debug(5, "no select\n");
02378    if (attributes & LATT_MARKED)
02379       ast_debug(5, "marked\n");
02380    if (attributes & LATT_UNMARKED)
02381       ast_debug(5, "unmarked\n");
02382 }
02383 
02384 
02385 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02386 {
02387    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02388    if (attributes & LATT_NOINFERIORS)
02389       ast_debug(5, "no inferiors\n");
02390    if (attributes & LATT_NOSELECT)
02391       ast_debug(5, "no select\n");
02392    if (attributes & LATT_MARKED)
02393       ast_debug(5, "marked\n");
02394    if (attributes & LATT_UNMARKED)
02395       ast_debug(5, "unmarked\n");
02396 }
02397 
02398 
02399 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02400 {
02401    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02402    if (status->flags & SA_MESSAGES)
02403       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02404    if (status->flags & SA_RECENT)
02405       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02406    if (status->flags & SA_UNSEEN)
02407       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02408    if (status->flags & SA_UIDVALIDITY)
02409       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02410    if (status->flags & SA_UIDNEXT)
02411       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02412    ast_log(AST_LOG_NOTICE, "\n");
02413 }
02414 
02415 
02416 void mm_log(char *string, long errflg)
02417 {
02418    switch ((short) errflg) {
02419       case NIL:
02420          ast_debug(1,"IMAP Info: %s\n", string);
02421          break;
02422       case PARSE:
02423       case WARN:
02424          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02425          break;
02426       case ERROR:
02427          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02428          break;
02429    }
02430 }
02431 
02432 
02433 void mm_dlog(char *string)
02434 {
02435    ast_log(AST_LOG_NOTICE, "%s\n", string);
02436 }
02437 
02438 
02439 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02440 {
02441    struct ast_vm_user *vmu;
02442 
02443    ast_debug(4, "Entering callback mm_login\n");
02444 
02445    ast_copy_string(user, mb->user, MAILTMPLEN);
02446 
02447    /* We should only do this when necessary */
02448    if (!ast_strlen_zero(authpassword)) {
02449       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02450    } else {
02451       AST_LIST_TRAVERSE(&users, vmu, list) {
02452          if (!strcasecmp(mb->user, vmu->imapuser)) {
02453             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02454             break;
02455          }
02456       }
02457       if (!vmu) {
02458          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02459             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02460             free_user(vmu);
02461          }
02462       }
02463    }
02464 }
02465 
02466 
02467 void mm_critical(MAILSTREAM * stream)
02468 {
02469 }
02470 
02471 
02472 void mm_nocritical(MAILSTREAM * stream)
02473 {
02474 }
02475 
02476 
02477 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02478 {
02479    kill (getpid (), SIGSTOP);
02480    return NIL;
02481 }
02482 
02483 
02484 void mm_fatal(char *string)
02485 {
02486    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02487 }
02488 
02489 /* C-client callback to handle quota */
02490 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02491 {
02492    struct vm_state *vms;
02493    char *mailbox = stream->mailbox, *user;
02494    char buf[1024] = "";
02495    unsigned long usage = 0, limit = 0;
02496    
02497    while (pquota) {
02498       usage = pquota->usage;
02499       limit = pquota->limit;
02500       pquota = pquota->next;
02501    }
02502    
02503    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)))) {
02504       ast_log(AST_LOG_ERROR, "No state found.\n");
02505       return;
02506    }
02507 
02508    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02509 
02510    vms->quota_usage = usage;
02511    vms->quota_limit = limit;
02512 }
02513 
02514 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02515 {
02516    char *start, *eol_pnt;
02517    int taglen;
02518 
02519    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02520       return NULL;
02521 
02522    taglen = strlen(tag) + 1;
02523    if (taglen < 1)
02524       return NULL;
02525 
02526    if (!(start = strstr(header, tag)))
02527       return NULL;
02528 
02529    /* Since we can be called multiple times we should clear our buffer */
02530    memset(buf, 0, len);
02531 
02532    ast_copy_string(buf, start+taglen, len);
02533    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02534       *eol_pnt = '\0';
02535    return buf;
02536 }
02537 
02538 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02539 {
02540    char *start, *quote, *eol_pnt;
02541 
02542    if (ast_strlen_zero(mailbox))
02543       return NULL;
02544 
02545    if (!(start = strstr(mailbox, "/user=")))
02546       return NULL;
02547 
02548    ast_copy_string(buf, start+6, len);
02549 
02550    if (!(quote = strchr(buf, '\"'))) {
02551       if (!(eol_pnt = strchr(buf, '/')))
02552          eol_pnt = strchr(buf,'}');
02553       *eol_pnt = '\0';
02554       return buf;
02555    } else {
02556       eol_pnt = strchr(buf+1,'\"');
02557       *eol_pnt = '\0';
02558       return buf+1;
02559    }
02560 }
02561 
02562 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02563 {
02564    struct vm_state *vms_p;
02565 
02566    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02567    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02568       return vms_p;
02569    }
02570    if (option_debug > 4)
02571       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02572    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02573       return NULL;
02574    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02575    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02576    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02577    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02578    vms_p->imapversion = vmu->imapversion;
02579    if (option_debug > 4)
02580       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02581    vms_p->updated = 1;
02582    /* set mailbox to INBOX! */
02583    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02584    init_vm_state(vms_p);
02585    vmstate_insert(vms_p);
02586    return vms_p;
02587 }
02588 
02589 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02590 {
02591    struct vmstate *vlist = NULL;
02592 
02593    if (interactive) {
02594       struct vm_state *vms;
02595       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02596       vms = pthread_getspecific(ts_vmstate.key);
02597       return vms;
02598    }
02599 
02600    AST_LIST_LOCK(&vmstates);
02601    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02602       if (!vlist->vms) {
02603          ast_debug(3, "error: vms is NULL for %s\n", user);
02604          continue;
02605       }
02606       if (vlist->vms->imapversion != imapversion) {
02607          continue;
02608       }
02609       if (!vlist->vms->imapuser) {
02610          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02611          continue;
02612       }
02613 
02614       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02615          AST_LIST_UNLOCK(&vmstates);
02616          return vlist->vms;
02617       }
02618    }
02619    AST_LIST_UNLOCK(&vmstates);
02620 
02621    ast_debug(3, "%s not found in vmstates\n", user);
02622 
02623    return NULL;
02624 }
02625 
02626 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02627 {
02628 
02629    struct vmstate *vlist = NULL;
02630    const char *local_context = S_OR(context, "default");
02631 
02632    if (interactive) {
02633       struct vm_state *vms;
02634       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02635       vms = pthread_getspecific(ts_vmstate.key);
02636       return vms;
02637    }
02638 
02639    AST_LIST_LOCK(&vmstates);
02640    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02641       if (!vlist->vms) {
02642          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02643          continue;
02644       }
02645       if (vlist->vms->imapversion != imapversion) {
02646          continue;
02647       }
02648       if (!vlist->vms->username || !vlist->vms->context) {
02649          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02650          continue;
02651       }
02652 
02653       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);
02654       
02655       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02656          ast_debug(3, "Found it!\n");
02657          AST_LIST_UNLOCK(&vmstates);
02658          return vlist->vms;
02659       }
02660    }
02661    AST_LIST_UNLOCK(&vmstates);
02662 
02663    ast_debug(3, "%s not found in vmstates\n", mailbox);
02664 
02665    return NULL;
02666 }
02667 
02668 static void vmstate_insert(struct vm_state *vms) 
02669 {
02670    struct vmstate *v;
02671    struct vm_state *altvms;
02672 
02673    /* If interactive, it probably already exists, and we should
02674       use the one we already have since it is more up to date.
02675       We can compare the username to find the duplicate */
02676    if (vms->interactive == 1) {
02677       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02678       if (altvms) {  
02679          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02680          vms->newmessages = altvms->newmessages;
02681          vms->oldmessages = altvms->oldmessages;
02682          vms->vmArrayIndex = altvms->vmArrayIndex;
02683          vms->lastmsg = altvms->lastmsg;
02684          vms->curmsg = altvms->curmsg;
02685          /* get a pointer to the persistent store */
02686          vms->persist_vms = altvms;
02687          /* Reuse the mailstream? */
02688 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02689          vms->mailstream = altvms->mailstream;
02690 #else
02691          vms->mailstream = NIL;
02692 #endif
02693       }
02694       return;
02695    }
02696 
02697    if (!(v = ast_calloc(1, sizeof(*v))))
02698       return;
02699    
02700    v->vms = vms;
02701 
02702    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02703 
02704    AST_LIST_LOCK(&vmstates);
02705    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02706    AST_LIST_UNLOCK(&vmstates);
02707 }
02708 
02709 static void vmstate_delete(struct vm_state *vms) 
02710 {
02711    struct vmstate *vc = NULL;
02712    struct vm_state *altvms = NULL;
02713 
02714    /* If interactive, we should copy pertinent info
02715       back to the persistent state (to make update immediate) */
02716    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02717       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02718       altvms->newmessages = vms->newmessages;
02719       altvms->oldmessages = vms->oldmessages;
02720       altvms->updated = 1;
02721       vms->mailstream = mail_close(vms->mailstream);
02722 
02723       /* Interactive states are not stored within the persistent list */
02724       return;
02725    }
02726    
02727    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02728    
02729    AST_LIST_LOCK(&vmstates);
02730    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02731       if (vc->vms == vms) {
02732          AST_LIST_REMOVE_CURRENT(list);
02733          break;
02734       }
02735    }
02736    AST_LIST_TRAVERSE_SAFE_END
02737    AST_LIST_UNLOCK(&vmstates);
02738    
02739    if (vc) {
02740       ast_mutex_destroy(&vc->vms->lock);
02741       ast_free(vc);
02742    }
02743    else
02744       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02745 }
02746 
02747 static void set_update(MAILSTREAM * stream) 
02748 {
02749    struct vm_state *vms;
02750    char *mailbox = stream->mailbox, *user;
02751    char buf[1024] = "";
02752 
02753    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02754       if (user && option_debug > 2)
02755          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02756       return;
02757    }
02758 
02759    ast_debug(3, "User %s mailbox set for update.\n", user);
02760 
02761    vms->updated = 1; /* Set updated flag since mailbox changed */
02762 }
02763 
02764 static void init_vm_state(struct vm_state *vms) 
02765 {
02766    int x;
02767    vms->vmArrayIndex = 0;
02768    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02769       vms->msgArray[x] = 0;
02770    }
02771    ast_mutex_init(&vms->lock);
02772 }
02773 
02774 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02775 {
02776    char *body_content;
02777    char *body_decoded;
02778    char *fn = is_intro ? vms->introfn : vms->fn;
02779    unsigned long len;
02780    unsigned long newlen;
02781    char filename[256];
02782    
02783    if (!body || body == NIL)
02784       return -1;
02785 
02786    ast_mutex_lock(&vms->lock);
02787    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02788    ast_mutex_unlock(&vms->lock);
02789    if (body_content != NIL) {
02790       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02791       /* ast_debug(1,body_content); */
02792       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02793       /* If the body of the file is empty, return an error */
02794       if (!newlen) {
02795          return -1;
02796       }
02797       write_file(filename, (char *) body_decoded, newlen);
02798    } else {
02799       ast_debug(5, "Body of message is NULL.\n");
02800       return -1;
02801    }
02802    return 0;
02803 }
02804 
02805 /*! 
02806  * \brief Get delimiter via mm_list callback 
02807  * \param stream
02808  *
02809  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02810  */
02811 /* MUTEX should already be held */
02812 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02813    char tmp[50];
02814    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02815    mail_list(stream, tmp, "*");
02816 }
02817 
02818 /*! 
02819  * \brief Check Quota for user 
02820  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02821  * \param mailbox the mailbox to check the quota for.
02822  *
02823  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02824  */
02825 static void check_quota(struct vm_state *vms, char *mailbox) {
02826    ast_mutex_lock(&vms->lock);
02827    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02828    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02829    if (vms && vms->mailstream != NULL) {
02830       imap_getquotaroot(vms->mailstream, mailbox);
02831    } else {
02832       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02833    }
02834    ast_mutex_unlock(&vms->lock);
02835 }
02836 
02837 #endif /* IMAP_STORAGE */
02838 
02839 /*! \brief Lock file path
02840     only return failure if ast_lock_path returns 'timeout',
02841    not if the path does not exist or any other reason
02842 */
02843 static int vm_lock_path(const char *path)
02844 {
02845    switch (ast_lock_path(path)) {
02846    case AST_LOCK_TIMEOUT:
02847       return -1;
02848    default:
02849       return 0;
02850    }
02851 }
02852 
02853 
02854 #ifdef ODBC_STORAGE
02855 struct generic_prepare_struct {
02856    char *sql;
02857    int argc;
02858    char **argv;
02859 };
02860 
02861 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02862 {
02863    struct generic_prepare_struct *gps = data;
02864    int res, i;
02865    SQLHSTMT stmt;
02866 
02867    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02868    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02869       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
02870       return NULL;
02871    }
02872    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02873    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02874       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02875       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02876       return NULL;
02877    }
02878    for (i = 0; i < gps->argc; i++)
02879       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02880 
02881    return stmt;
02882 }
02883 
02884 /*!
02885  * \brief Retrieves a file from an ODBC data store.
02886  * \param dir the path to the file to be retreived.
02887  * \param msgnum the message number, such as within a mailbox folder.
02888  * 
02889  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
02890  * 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.
02891  *
02892  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
02893  * The output is the message information file with the name msgnum and the extension .txt
02894  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
02895  * 
02896  * \return 0 on success, -1 on error.
02897  */
02898 static int retrieve_file(char *dir, int msgnum)
02899 {
02900    int x = 0;
02901    int res;
02902    int fd=-1;
02903    size_t fdlen = 0;
02904    void *fdm = MAP_FAILED;
02905    SQLSMALLINT colcount=0;
02906    SQLHSTMT stmt;
02907    char sql[PATH_MAX];
02908    char fmt[80]="";
02909    char *c;
02910    char coltitle[256];
02911    SQLSMALLINT collen;
02912    SQLSMALLINT datatype;
02913    SQLSMALLINT decimaldigits;
02914    SQLSMALLINT nullable;
02915    SQLULEN colsize;
02916    SQLLEN colsize2;
02917    FILE *f=NULL;
02918    char rowdata[80];
02919    char fn[PATH_MAX];
02920    char full_fn[PATH_MAX];
02921    char msgnums[80];
02922    char *argv[] = { dir, msgnums };
02923    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02924 
02925    struct odbc_obj *obj;
02926    obj = ast_odbc_request_obj(odbc_database, 0);
02927    if (obj) {
02928       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02929       c = strchr(fmt, '|');
02930       if (c)
02931          *c = '\0';
02932       if (!strcasecmp(fmt, "wav49"))
02933          strcpy(fmt, "WAV");
02934       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02935       if (msgnum > -1)
02936          make_file(fn, sizeof(fn), dir, msgnum);
02937       else
02938          ast_copy_string(fn, dir, sizeof(fn));
02939 
02940       /* Create the information file */
02941       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02942       
02943       if (!(f = fopen(full_fn, "w+"))) {
02944          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02945          goto yuck;
02946       }
02947       
02948       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02949       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02950       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02951       if (!stmt) {
02952          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02953          ast_odbc_release_obj(obj);
02954          goto yuck;
02955       }
02956       res = SQLFetch(stmt);
02957       if (res == SQL_NO_DATA) {
02958          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02959          ast_odbc_release_obj(obj);
02960          goto yuck;
02961       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02962          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02963          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02964          ast_odbc_release_obj(obj);
02965          goto yuck;
02966       }
02967       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
02968       if (fd < 0) {
02969          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02970          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02971          ast_odbc_release_obj(obj);
02972          goto yuck;
02973       }
02974       res = SQLNumResultCols(stmt, &colcount);
02975       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02976          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02977          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02978          ast_odbc_release_obj(obj);
02979          goto yuck;
02980       }
02981       if (f) 
02982          fprintf(f, "[message]\n");
02983       for (x=0;x<colcount;x++) {
02984          rowdata[0] = '\0';
02985          collen = sizeof(coltitle);
02986          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02987                   &datatype, &colsize, &decimaldigits, &nullable);
02988          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02989             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02990             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02991             ast_odbc_release_obj(obj);
02992             goto yuck;
02993          }
02994          if (!strcasecmp(coltitle, "recording")) {
02995             off_t offset;
02996             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02997             fdlen = colsize2;
02998             if (fd > -1) {
02999                char tmp[1]="";
03000                lseek(fd, fdlen - 1, SEEK_SET);
03001                if (write(fd, tmp, 1) != 1) {
03002                   close(fd);
03003                   fd = -1;
03004                   continue;
03005                }
03006                /* Read out in small chunks */
03007                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
03008                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
03009                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
03010                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03011                      ast_odbc_release_obj(obj);
03012                      goto yuck;
03013                   } else {
03014                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
03015                      munmap(fdm, CHUNKSIZE);
03016                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03017                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03018                         unlink(full_fn);
03019                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03020                         ast_odbc_release_obj(obj);
03021                         goto yuck;
03022                      }
03023                   }
03024                }
03025                if (truncate(full_fn, fdlen) < 0) {
03026                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
03027                }
03028             }
03029          } else {
03030             res = SQLGetData(stmt, x + 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! coltitle=%s\n[%s]\n\n", coltitle, sql);
03033                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03034                ast_odbc_release_obj(obj);
03035                goto yuck;
03036             }
03037             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
03038                fprintf(f, "%s=%s\n", coltitle, rowdata);
03039          }
03040       }
03041       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03042       ast_odbc_release_obj(obj);
03043    } else
03044       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03045 yuck: 
03046    if (f)
03047       fclose(f);
03048    if (fd > -1)
03049       close(fd);
03050    return x - 1;
03051 }
03052 
03053 /*!
03054  * \brief Determines the highest message number in use for a given user and mailbox folder.
03055  * \param vmu 
03056  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03057  *
03058  * This method is used when mailboxes are stored in an ODBC back end.
03059  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03060  *
03061  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03062  */
03063 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03064 {
03065    int x = 0;
03066    int res;
03067    SQLHSTMT stmt;
03068    char sql[PATH_MAX];
03069    char rowdata[20];
03070    char *argv[] = { dir };
03071    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
03072 
03073    struct odbc_obj *obj;
03074    obj = ast_odbc_request_obj(odbc_database, 0);
03075    if (obj) {
03076       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
03077       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03078       if (!stmt) {
03079          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03080          ast_odbc_release_obj(obj);
03081          goto yuck;
03082       }
03083       res = SQLFetch(stmt);
03084       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03085          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03086          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03087          ast_odbc_release_obj(obj);
03088          goto yuck;
03089       }
03090       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03091       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03092          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03093          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03094          ast_odbc_release_obj(obj);
03095          goto yuck;
03096       }
03097       if (sscanf(rowdata, "%30d", &x) != 1)
03098          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03099       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03100       ast_odbc_release_obj(obj);
03101    } else
03102       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03103 yuck: 
03104    return x - 1;
03105 }
03106 
03107 /*!
03108  * \brief Determines if the specified message exists.
03109  * \param dir the folder the mailbox folder to look for messages. 
03110  * \param msgnum the message index to query for.
03111  *
03112  * This method is used when mailboxes are stored in an ODBC back end.
03113  *
03114  * \return greater than zero if the message exists, zero when the message does not exist or on error.
03115  */
03116 static int message_exists(char *dir, int msgnum)
03117 {
03118    int x = 0;
03119    int res;
03120    SQLHSTMT stmt;
03121    char sql[PATH_MAX];
03122    char rowdata[20];
03123    char msgnums[20];
03124    char *argv[] = { dir, msgnums };
03125    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03126 
03127    struct odbc_obj *obj;
03128    obj = ast_odbc_request_obj(odbc_database, 0);
03129    if (obj) {
03130       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03131       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03132       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03133       if (!stmt) {
03134          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03135          ast_odbc_release_obj(obj);
03136          goto yuck;
03137       }
03138       res = SQLFetch(stmt);
03139       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03140          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03141          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03142          ast_odbc_release_obj(obj);
03143          goto yuck;
03144       }
03145       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03146       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03147          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03148          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03149          ast_odbc_release_obj(obj);
03150          goto yuck;
03151       }
03152       if (sscanf(rowdata, "%30d", &x) != 1)
03153          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03154       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03155       ast_odbc_release_obj(obj);
03156    } else
03157       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03158 yuck: 
03159    return x;
03160 }
03161 
03162 /*!
03163  * \brief returns the one-based count for messages.
03164  * \param vmu
03165  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03166  *
03167  * This method is used when mailboxes are stored in an ODBC back end.
03168  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03169  * one-based messages.
03170  * This method just calls last_message_index and returns +1 of its value.
03171  *
03172  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03173  */
03174 static int count_messages(struct ast_vm_user *vmu, char *dir)
03175 {
03176    return last_message_index(vmu, dir) + 1;
03177 }
03178 
03179 /*!
03180  * \brief Deletes a message from the mailbox folder.
03181  * \param sdir The mailbox folder to work in.
03182  * \param smsg The message index to be deleted.
03183  *
03184  * This method is used when mailboxes are stored in an ODBC back end.
03185  * The specified message is directly deleted from the database 'voicemessages' table.
03186  * 
03187  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03188  */
03189 static void delete_file(const char *sdir, int smsg)
03190 {
03191    SQLHSTMT stmt;
03192    char sql[PATH_MAX];
03193    char msgnums[20];
03194    char *argv[] = { NULL, msgnums };
03195    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03196    struct odbc_obj *obj;
03197 
03198    argv[0] = ast_strdupa(sdir);
03199 
03200    obj = ast_odbc_request_obj(odbc_database, 0);
03201    if (obj) {
03202       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03203       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03204       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03205       if (!stmt)
03206          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03207       else
03208          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03209       ast_odbc_release_obj(obj);
03210    } else
03211       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03212    return;  
03213 }
03214 
03215 /*!
03216  * \brief Copies a voicemail from one mailbox to another.
03217  * \param sdir the folder for which to look for the message to be copied.
03218  * \param smsg the index of the message to be copied.
03219  * \param ddir the destination folder to copy the message into.
03220  * \param dmsg the index to be used for the copied message.
03221  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03222  * \param dmailboxcontext The context for the destination user.
03223  *
03224  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03225  */
03226 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03227 {
03228    SQLHSTMT stmt;
03229    char sql[512];
03230    char msgnums[20];
03231    char msgnumd[20];
03232    struct odbc_obj *obj;
03233    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03234    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03235 
03236    delete_file(ddir, dmsg);
03237    obj = ast_odbc_request_obj(odbc_database, 0);
03238    if (obj) {
03239       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03240       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03241       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);
03242       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03243       if (!stmt)
03244          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03245       else
03246          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03247       ast_odbc_release_obj(obj);
03248    } else
03249       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03250    return;  
03251 }
03252 
03253 struct insert_data {
03254    char *sql;
03255    const char *dir;
03256    const char *msgnums;
03257    void *data;
03258    SQLLEN datalen;
03259    SQLLEN indlen;
03260    const char *context;
03261    const char *macrocontext;
03262    const char *callerid;
03263    const char *origtime;
03264    const char *duration;
03265    const char *mailboxuser;
03266    const char *mailboxcontext;
03267    const char *category;
03268    const char *flag;
03269 };
03270 
03271 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03272 {
03273    struct insert_data *data = vdata;
03274    int res;
03275    SQLHSTMT stmt;
03276 
03277    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03278    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03279       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03280       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03281       return NULL;
03282    }
03283 
03284    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03285    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03286    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03287    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03288    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03289    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03290    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03291    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03292    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03293    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03294    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03295    if (!ast_strlen_zero(data->category)) {
03296       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03297    }
03298    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03299    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03300       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03301       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03302       return NULL;
03303    }
03304 
03305    return stmt;
03306 }
03307 
03308 /*!
03309  * \brief Stores a voicemail into the database.
03310  * \param dir the folder the mailbox folder to store the message.
03311  * \param mailboxuser the user owning the mailbox folder.
03312  * \param mailboxcontext
03313  * \param msgnum the message index for the message to be stored.
03314  *
03315  * This method is used when mailboxes are stored in an ODBC back end.
03316  * The message sound file and information file is looked up on the file system. 
03317  * A SQL query is invoked to store the message into the (MySQL) database.
03318  *
03319  * \return the zero on success -1 on error.
03320  */
03321 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
03322 {
03323    int res = 0;
03324    int fd = -1;
03325    void *fdm = MAP_FAILED;
03326    size_t fdlen = -1;
03327    SQLHSTMT stmt;
03328    char sql[PATH_MAX];
03329    char msgnums[20];
03330    char fn[PATH_MAX];
03331    char full_fn[PATH_MAX];
03332    char fmt[80]="";
03333    char *c;
03334    struct ast_config *cfg=NULL;
03335    struct odbc_obj *obj;
03336    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03337       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03338    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03339 
03340    delete_file(dir, msgnum);
03341    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03342       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03343       return -1;
03344    }
03345 
03346    do {
03347       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03348       c = strchr(fmt, '|');
03349       if (c)
03350          *c = '\0';
03351       if (!strcasecmp(fmt, "wav49"))
03352          strcpy(fmt, "WAV");
03353       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03354       if (msgnum > -1)
03355          make_file(fn, sizeof(fn), dir, msgnum);
03356       else
03357          ast_copy_string(fn, dir, sizeof(fn));
03358       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03359       cfg = ast_config_load(full_fn, config_flags);
03360       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03361       fd = open(full_fn, O_RDWR);
03362       if (fd < 0) {
03363          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03364          res = -1;
03365          break;
03366       }
03367       if (cfg) {
03368          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03369             idata.context = "";
03370          }
03371          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03372             idata.macrocontext = "";
03373          }
03374          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03375             idata.callerid = "";
03376          }
03377          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03378             idata.origtime = "";
03379          }
03380          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03381             idata.duration = "";
03382          }
03383          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03384             idata.category = "";
03385          }
03386          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03387             idata.flag = "";
03388          }
03389       }
03390       fdlen = lseek(fd, 0, SEEK_END);
03391       lseek(fd, 0, SEEK_SET);
03392       printf("Length is %zd\n", fdlen);
03393       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03394       if (fdm == MAP_FAILED) {
03395          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03396          res = -1;
03397          break;
03398       } 
03399       idata.data = fdm;
03400       idata.datalen = idata.indlen = fdlen;
03401 
03402       if (!ast_strlen_zero(idata.category)) 
03403          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03404       else
03405          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03406 
03407       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03408          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03409       } else {
03410          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03411          res = -1;
03412       }
03413    } while (0);
03414    if (obj) {
03415       ast_odbc_release_obj(obj);
03416    }
03417    if (cfg)
03418       ast_config_destroy(cfg);
03419    if (fdm != MAP_FAILED)
03420       munmap(fdm, fdlen);
03421    if (fd > -1)
03422       close(fd);
03423    return res;
03424 }
03425 
03426 /*!
03427  * \brief Renames a message in a mailbox folder.
03428  * \param sdir The folder of the message to be renamed.
03429  * \param smsg The index of the message to be renamed.
03430  * \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.
03431  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03432  * \param ddir The destination folder for the message to be renamed into
03433  * \param dmsg The destination message for the message to be renamed.
03434  *
03435  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03436  * 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.
03437  * 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.
03438  */
03439 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03440 {
03441    SQLHSTMT stmt;
03442    char sql[PATH_MAX];
03443    char msgnums[20];
03444    char msgnumd[20];
03445    struct odbc_obj *obj;
03446    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03447    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03448 
03449    delete_file(ddir, dmsg);
03450    obj = ast_odbc_request_obj(odbc_database, 0);
03451    if (obj) {
03452       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03453       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03454       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03455       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03456       if (!stmt)
03457          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03458       else
03459          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03460       ast_odbc_release_obj(obj);
03461    } else
03462       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03463    return;  
03464 }
03465 
03466 /*!
03467  * \brief Removes a voicemail message file.
03468  * \param dir the path to the message file.
03469  * \param msgnum the unique number for the message within the mailbox.
03470  *
03471  * Removes the message content file and the information file.
03472  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03473  * Typical use is to clean up after a RETRIEVE operation. 
03474  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03475  * \return zero on success, -1 on error.
03476  */
03477 static int remove_file(char *dir, int msgnum)
03478 {
03479    char fn[PATH_MAX];
03480    char full_fn[PATH_MAX];
03481    char msgnums[80];
03482    
03483    if (msgnum > -1) {
03484       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03485       make_file(fn, sizeof(fn), dir, msgnum);
03486    } else
03487       ast_copy_string(fn, dir, sizeof(fn));
03488    ast_filedelete(fn, NULL);  
03489    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03490    unlink(full_fn);
03491    return 0;
03492 }
03493 #else
03494 #ifndef IMAP_STORAGE
03495 /*!
03496  * \brief Find all .txt files - even if they are not in sequence from 0000.
03497  * \param vmu
03498  * \param dir
03499  *
03500  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03501  *
03502  * \return the count of messages, zero or more.
03503  */
03504 static int count_messages(struct ast_vm_user *vmu, char *dir)
03505 {
03506 
03507    int vmcount = 0;
03508    DIR *vmdir = NULL;
03509    struct dirent *vment = NULL;
03510 
03511    if (vm_lock_path(dir))
03512       return ERROR_LOCK_PATH;
03513 
03514    if ((vmdir = opendir(dir))) {
03515       while ((vment = readdir(vmdir))) {
03516          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03517             vmcount++;
03518          }
03519       }
03520       closedir(vmdir);
03521    }
03522    ast_unlock_path(dir);
03523    
03524    return vmcount;
03525 }
03526 
03527 /*!
03528  * \brief Renames a message in a mailbox folder.
03529  * \param sfn The path to the mailbox information and data file to be renamed.
03530  * \param dfn The path for where the message data and information files will be renamed to.
03531  *
03532  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03533  */
03534 static void rename_file(char *sfn, char *dfn)
03535 {
03536    char stxt[PATH_MAX];
03537    char dtxt[PATH_MAX];
03538    ast_filerename(sfn,dfn,NULL);
03539    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03540    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03541    if (ast_check_realtime("voicemail_data")) {
03542       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03543    }
03544    rename(stxt, dtxt);
03545 }
03546 
03547 /*! 
03548  * \brief Determines the highest message number in use for a given user and mailbox folder.
03549  * \param vmu 
03550  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03551  *
03552  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03553  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03554  *
03555  * \note Should always be called with a lock already set on dir.
03556  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03557  */
03558 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03559 {
03560    int x;
03561    unsigned char map[MAXMSGLIMIT] = "";
03562    DIR *msgdir;
03563    struct dirent *msgdirent;
03564    int msgdirint;
03565 
03566    /* Reading the entire directory into a file map scales better than
03567     * doing a stat repeatedly on a predicted sequence.  I suspect this
03568     * is partially due to stat(2) internally doing a readdir(2) itself to
03569     * find each file. */
03570    if (!(msgdir = opendir(dir))) {
03571       return -1;
03572    }
03573 
03574    while ((msgdirent = readdir(msgdir))) {
03575       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03576          map[msgdirint] = 1;
03577    }
03578    closedir(msgdir);
03579 
03580    for (x = 0; x < vmu->maxmsg; x++) {
03581       if (map[x] == 0)
03582          break;
03583    }
03584 
03585    return x - 1;
03586 }
03587 
03588 #endif /* #ifndef IMAP_STORAGE */
03589 #endif /* #else of #ifdef ODBC_STORAGE */
03590 #ifndef IMAP_STORAGE
03591 /*!
03592  * \brief Utility function to copy a file.
03593  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03594  * \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.
03595  *
03596  * 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.
03597  * The copy operation copies up to 4096 bytes at once.
03598  *
03599  * \return zero on success, -1 on error.
03600  */
03601 static int copy(char *infile, char *outfile)
03602 {
03603    int ifd;
03604    int ofd;
03605    int res;
03606    int len;
03607    char buf[4096];
03608 
03609 #ifdef HARDLINK_WHEN_POSSIBLE
03610    /* Hard link if possible; saves disk space & is faster */
03611    if (link(infile, outfile)) {
03612 #endif
03613       if ((ifd = open(infile, O_RDONLY)) < 0) {
03614          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03615          return -1;
03616       }
03617       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03618          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03619          close(ifd);
03620          return -1;
03621       }
03622       do {
03623          len = read(ifd, buf, sizeof(buf));
03624          if (len < 0) {
03625             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03626             close(ifd);
03627             close(ofd);
03628             unlink(outfile);
03629          }
03630          if (len) {
03631             res = write(ofd, buf, len);
03632             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03633                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03634                close(ifd);
03635                close(ofd);
03636                unlink(outfile);
03637             }
03638          }
03639       } while (len);
03640       close(ifd);
03641       close(ofd);
03642       return 0;
03643 #ifdef HARDLINK_WHEN_POSSIBLE
03644    } else {
03645       /* Hard link succeeded */
03646       return 0;
03647    }
03648 #endif
03649 }
03650 
03651 /*!
03652  * \brief Copies a voicemail information (envelope) file.
03653  * \param frompath
03654  * \param topath 
03655  *
03656  * Every voicemail has the data (.wav) file, and the information file.
03657  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03658  * This is used by the COPY macro when not using IMAP storage.
03659  */
03660 static void copy_plain_file(char *frompath, char *topath)
03661 {
03662    char frompath2[PATH_MAX], topath2[PATH_MAX];
03663    struct ast_variable *tmp,*var = NULL;
03664    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03665    ast_filecopy(frompath, topath, NULL);
03666    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03667    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03668    if (ast_check_realtime("voicemail_data")) {
03669       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03670       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03671       for (tmp = var; tmp; tmp = tmp->next) {
03672          if (!strcasecmp(tmp->name, "origmailbox")) {
03673             origmailbox = tmp->value;
03674          } else if (!strcasecmp(tmp->name, "context")) {
03675             context = tmp->value;
03676          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03677             macrocontext = tmp->value;
03678          } else if (!strcasecmp(tmp->name, "exten")) {
03679             exten = tmp->value;
03680          } else if (!strcasecmp(tmp->name, "priority")) {
03681             priority = tmp->value;
03682          } else if (!strcasecmp(tmp->name, "callerchan")) {
03683             callerchan = tmp->value;
03684          } else if (!strcasecmp(tmp->name, "callerid")) {
03685             callerid = tmp->value;
03686          } else if (!strcasecmp(tmp->name, "origdate")) {
03687             origdate = tmp->value;
03688          } else if (!strcasecmp(tmp->name, "origtime")) {
03689             origtime = tmp->value;
03690          } else if (!strcasecmp(tmp->name, "category")) {
03691             category = tmp->value;
03692          } else if (!strcasecmp(tmp->name, "duration")) {
03693             duration = tmp->value;
03694          }
03695       }
03696       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);
03697    }
03698    copy(frompath2, topath2);
03699    ast_variables_destroy(var);
03700 }
03701 #endif
03702 
03703 /*! 
03704  * \brief Removes the voicemail sound and information file.
03705  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03706  *
03707  * This is used by the DELETE macro when voicemails are stored on the file system.
03708  *
03709  * \return zero on success, -1 on error.
03710  */
03711 static int vm_delete(char *file)
03712 {
03713    char *txt;
03714    int txtsize = 0;
03715 
03716    txtsize = (strlen(file) + 5)*sizeof(char);
03717    txt = alloca(txtsize);
03718    /* Sprintf here would safe because we alloca'd exactly the right length,
03719     * but trying to eliminate all sprintf's anyhow
03720     */
03721    if (ast_check_realtime("voicemail_data")) {
03722       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03723    }
03724    snprintf(txt, txtsize, "%s.txt", file);
03725    unlink(txt);
03726    return ast_filedelete(file, NULL);
03727 }
03728 
03729 /*!
03730  * \brief utility used by inchar(), for base_encode()
03731  */
03732 static int inbuf(struct baseio *bio, FILE *fi)
03733 {
03734    int l;
03735 
03736    if (bio->ateof)
03737       return 0;
03738 
03739    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03740       if (ferror(fi))
03741          return -1;
03742 
03743       bio->ateof = 1;
03744       return 0;
03745    }
03746 
03747    bio->iolen= l;
03748    bio->iocp= 0;
03749 
03750    return 1;
03751 }
03752 
03753 /*!
03754  * \brief utility used by base_encode()
03755  */
03756 static int inchar(struct baseio *bio, FILE *fi)
03757 {
03758    if (bio->iocp>=bio->iolen) {
03759       if (!inbuf(bio, fi))
03760          return EOF;
03761    }
03762 
03763    return bio->iobuf[bio->iocp++];
03764 }
03765 
03766 /*!
03767  * \brief utility used by base_encode()
03768  */
03769 static int ochar(struct baseio *bio, int c, FILE *so)
03770 {
03771    if (bio->linelength >= BASELINELEN) {
03772       if (fputs(eol,so) == EOF)
03773          return -1;
03774 
03775       bio->linelength= 0;
03776    }
03777 
03778    if (putc(((unsigned char)c),so) == EOF)
03779       return -1;
03780 
03781    bio->linelength++;
03782 
03783    return 1;
03784 }
03785 
03786 /*!
03787  * \brief Performs a base 64 encode algorithm on the contents of a File
03788  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03789  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03790  *
03791  * 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 ?
03792  *
03793  * \return zero on success, -1 on error.
03794  */
03795 static int base_encode(char *filename, FILE *so)
03796 {
03797    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03798       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03799       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03800       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03801    int i,hiteof= 0;
03802    FILE *fi;
03803    struct baseio bio;
03804 
03805    memset(&bio, 0, sizeof(bio));
03806    bio.iocp = BASEMAXINLINE;
03807 
03808    if (!(fi = fopen(filename, "rb"))) {
03809       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03810       return -1;
03811    }
03812 
03813    while (!hiteof){
03814       unsigned char igroup[3], ogroup[4];
03815       int c,n;
03816 
03817       igroup[0]= igroup[1]= igroup[2]= 0;
03818 
03819       for (n= 0;n<3;n++) {
03820          if ((c = inchar(&bio, fi)) == EOF) {
03821             hiteof= 1;
03822             break;
03823          }
03824 
03825          igroup[n]= (unsigned char)c;
03826       }
03827 
03828       if (n> 0) {
03829          ogroup[0]= dtable[igroup[0]>>2];
03830          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03831          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03832          ogroup[3]= dtable[igroup[2]&0x3F];
03833 
03834          if (n<3) {
03835             ogroup[3]= '=';
03836 
03837             if (n<2)
03838                ogroup[2]= '=';
03839          }
03840 
03841          for (i= 0;i<4;i++)
03842             ochar(&bio, ogroup[i], so);
03843       }
03844    }
03845 
03846    fclose(fi);
03847    
03848    if (fputs(eol,so)==EOF)
03849       return 0;
03850 
03851    return 1;
03852 }
03853 
03854 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)
03855 {
03856    char callerid[256];
03857    char fromdir[256], fromfile[256];
03858    struct ast_config *msg_cfg;
03859    const char *origcallerid, *origtime;
03860    char origcidname[80], origcidnum[80], origdate[80];
03861    int inttime;
03862    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03863 
03864    /* Prepare variables for substitution in email body and subject */
03865    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03866    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03867    snprintf(passdata, passdatasize, "%d", msgnum);
03868    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03869    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03870    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03871    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03872       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03873    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03874    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03875    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03876    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03877    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
03878 
03879    /* Retrieve info from VM attribute file */
03880    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03881    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03882    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03883       strcat(fromfile, ".txt");
03884    }
03885    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
03886       if (option_debug > 0) {
03887          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03888       }
03889       return;
03890    }
03891 
03892    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03893       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03894       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03895       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03896       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03897    }
03898 
03899    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
03900       struct timeval tv = { inttime, };
03901       struct ast_tm tm;
03902       ast_localtime(&tv, &tm, NULL);
03903       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03904       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03905    }
03906    ast_config_destroy(msg_cfg);
03907 }
03908 
03909 /*!
03910  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
03911  * \param from The string to work with.
03912  * \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.
03913  * 
03914  * \return The destination string with quotes wrapped on it (the to field).
03915  */
03916 static char *quote(const char *from, char *to, size_t len)
03917 {
03918    char *ptr = to;
03919    *ptr++ = '"';
03920    for (; ptr < to + len - 1; from++) {
03921       if (*from == '"')
03922          *ptr++ = '\\';
03923       else if (*from == '\0')
03924          break;
03925       *ptr++ = *from;
03926    }
03927    if (ptr < to + len - 1)
03928       *ptr++ = '"';
03929    *ptr = '\0';
03930    return to;
03931 }
03932 
03933 /*! \brief
03934  * fill in *tm for current time according to the proper timezone, if any.
03935  * Return tm so it can be used as a function argument.
03936  */
03937 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
03938 {
03939    const struct vm_zone *z = NULL;
03940    struct timeval t = ast_tvnow();
03941 
03942    /* Does this user have a timezone specified? */
03943    if (!ast_strlen_zero(vmu->zonetag)) {
03944       /* Find the zone in the list */
03945       AST_LIST_LOCK(&zones);
03946       AST_LIST_TRAVERSE(&zones, z, list) {
03947          if (!strcmp(z->name, vmu->zonetag))
03948             break;
03949       }
03950       AST_LIST_UNLOCK(&zones);
03951    }
03952    ast_localtime(&t, tm, z ? z->timezone : NULL);
03953    return tm;
03954 }
03955 
03956 /*!\brief Check if the string would need encoding within the MIME standard, to
03957  * avoid confusing certain mail software that expects messages to be 7-bit
03958  * clean.
03959  */
03960 static int check_mime(const char *str)
03961 {
03962    for (; *str; str++) {
03963       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03964          return 1;
03965       }
03966    }
03967    return 0;
03968 }
03969 
03970 /*!\brief Encode a string according to the MIME rules for encoding strings
03971  * that are not 7-bit clean or contain control characters.
03972  *
03973  * Additionally, if the encoded string would exceed the MIME limit of 76
03974  * characters per line, then the encoding will be broken up into multiple
03975  * sections, separated by a space character, in order to facilitate
03976  * breaking up the associated header across multiple lines.
03977  *
03978  * \param start A string to be encoded
03979  * \param end An expandable buffer for holding the result
03980  * \param preamble The length of the first line already used for this string,
03981  * to ensure that each line maintains a maximum length of 76 chars.
03982  * \param postamble the length of any additional characters appended to the
03983  * line, used to ensure proper field wrapping.
03984  * \retval The encoded string.
03985  */
03986 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03987 {
03988    char tmp[80];
03989    int first_section = 1;
03990    size_t endlen = 0, tmplen = 0;
03991    *end = '\0';
03992 
03993    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03994    for (; *start; start++) {
03995       int need_encoding = 0;
03996       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03997          need_encoding = 1;
03998       }
03999       if ((first_section && need_encoding && preamble + tmplen > 70) ||
04000          (first_section && !need_encoding && preamble + tmplen > 72) ||
04001          (!first_section && need_encoding && tmplen > 70) ||
04002          (!first_section && !need_encoding && tmplen > 72)) {
04003          /* Start new line */
04004          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
04005          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
04006          first_section = 0;
04007       }
04008       if (need_encoding && *start == ' ') {
04009          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
04010       } else if (need_encoding) {
04011          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
04012       } else {
04013          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
04014       }
04015    }
04016    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
04017    return end;
04018 }
04019 
04020 /*!
04021  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
04022  * \param p The output file to generate the email contents into.
04023  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
04024  * \param vmu The voicemail user who is sending the voicemail.
04025  * \param msgnum The message index in the mailbox folder.
04026  * \param context 
04027  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
04028  * \param cidnum The caller ID number.
04029  * \param cidname The caller ID name.
04030  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
04031  * \param format The message sound file format. i.e. .wav
04032  * \param duration The time of the message content, in seconds.
04033  * \param attach_user_voicemail if 1, the sound file is attached to the email.
04034  * \param chan
04035  * \param category
04036  * \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.
04037  *
04038  * 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.
04039  */
04040 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)
04041 {
04042    char date[256];
04043    char host[MAXHOSTNAMELEN] = "";
04044    char who[256];
04045    char bound[256];
04046    char dur[256];
04047    struct ast_tm tm;
04048    char enc_cidnum[256] = "", enc_cidname[256] = "";
04049    char *passdata = NULL, *passdata2;
04050    size_t len_passdata = 0, len_passdata2, tmplen;
04051    char *greeting_attachment; 
04052    char filename[256];
04053 
04054 #ifdef IMAP_STORAGE
04055 #define ENDL "\r\n"
04056 #else
04057 #define ENDL "\n"
04058 #endif
04059 
04060    /* One alloca for multiple fields */
04061    len_passdata2 = strlen(vmu->fullname);
04062    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
04063       len_passdata2 = tmplen;
04064    }
04065    if ((tmplen = strlen(fromstring)) > len_passdata2) {
04066       len_passdata2 = tmplen;
04067    }
04068    len_passdata2 = len_passdata2 * 3 + 200;
04069    passdata2 = alloca(len_passdata2);
04070 
04071    if (cidnum) {
04072       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
04073    }
04074    if (cidname) {
04075       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
04076    }
04077    gethostname(host, sizeof(host) - 1);
04078 
04079    if (strchr(srcemail, '@'))
04080       ast_copy_string(who, srcemail, sizeof(who));
04081    else 
04082       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04083    
04084    greeting_attachment = strrchr(ast_strdupa(attach), '/');
04085    if (greeting_attachment)
04086       *greeting_attachment++ = '\0';
04087 
04088    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04089    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04090    fprintf(p, "Date: %s" ENDL, date);
04091 
04092    /* Set date format for voicemail mail */
04093    ast_strftime(date, sizeof(date), emaildateformat, &tm);
04094 
04095    if (!ast_strlen_zero(fromstring)) {
04096       struct ast_channel *ast;
04097       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04098          char *ptr;
04099          memset(passdata2, 0, len_passdata2);
04100          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
04101          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
04102          len_passdata = strlen(passdata2) * 3 + 300;
04103          passdata = alloca(len_passdata);
04104          if (check_mime(passdata2)) {
04105             int first_line = 1;
04106             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
04107             while ((ptr = strchr(passdata, ' '))) {
04108                *ptr = '\0';
04109                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
04110                first_line = 0;
04111                passdata = ptr + 1;
04112             }
04113             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
04114          } else {
04115             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
04116          }
04117          ast_channel_free(ast);
04118       } else {
04119          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04120       }
04121    } else {
04122       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04123    }
04124 
04125    if (check_mime(vmu->fullname)) {
04126       int first_line = 1;
04127       char *ptr;
04128       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04129       while ((ptr = strchr(passdata2, ' '))) {
04130          *ptr = '\0';
04131          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04132          first_line = 0;
04133          passdata2 = ptr + 1;
04134       }
04135       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04136    } else {
04137       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04138    }
04139    if (!ast_strlen_zero(emailsubject)) {
04140       struct ast_channel *ast;
04141       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04142          int vmlen = strlen(emailsubject) * 3 + 200;
04143          /* Only allocate more space if the previous was not large enough */
04144          if (vmlen > len_passdata) {
04145             passdata = alloca(vmlen);
04146             len_passdata = vmlen;
04147          }
04148 
04149          memset(passdata, 0, len_passdata);
04150          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04151          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
04152          if (check_mime(passdata)) {
04153             int first_line = 1;
04154             char *ptr;
04155             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04156             while ((ptr = strchr(passdata2, ' '))) {
04157                *ptr = '\0';
04158                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04159                first_line = 0;
04160                passdata2 = ptr + 1;
04161             }
04162             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04163          } else {
04164             fprintf(p, "Subject: %s" ENDL, passdata);
04165          }
04166          ast_channel_free(ast);
04167       } else {
04168          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04169       }
04170    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04171       if (ast_strlen_zero(flag)) {
04172          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04173       } else {
04174          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04175       }
04176    } else {
04177       if (ast_strlen_zero(flag)) {
04178          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04179       } else {
04180          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04181       }
04182    }
04183 
04184    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04185    if (imap) {
04186       /* additional information needed for IMAP searching */
04187       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04188       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04189       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04190       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04191 #ifdef IMAP_STORAGE
04192       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04193 #else
04194       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04195 #endif
04196       /* flag added for Urgent */
04197       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04198       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04199       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04200       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04201       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04202       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04203       if (!ast_strlen_zero(category)) {
04204          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04205       } else {
04206          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04207       }
04208       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04209       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04210       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04211    }
04212    if (!ast_strlen_zero(cidnum)) {
04213       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04214    }
04215    if (!ast_strlen_zero(cidname)) {
04216       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04217    }
04218    fprintf(p, "MIME-Version: 1.0" ENDL);
04219    if (attach_user_voicemail) {
04220       /* Something unique. */
04221       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04222 
04223       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04224       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04225       fprintf(p, "--%s" ENDL, bound);
04226    }
04227    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04228    if (emailbody) {
04229       struct ast_channel *ast;
04230       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04231          char *passdata;
04232          int vmlen = strlen(emailbody)*3 + 200;
04233          passdata = alloca(vmlen);
04234          memset(passdata, 0, vmlen);
04235          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04236          pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
04237          fprintf(p, "%s" ENDL, passdata);
04238          ast_channel_free(ast);
04239       } else
04240          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04241    } else if (msgnum > -1) {
04242       if (strcmp(vmu->mailbox, mailbox)) {
04243          /* Forwarded type */
04244          struct ast_config *msg_cfg;
04245          const char *v;
04246          int inttime;
04247          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04248          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04249          /* Retrieve info from VM attribute file */
04250          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04251          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04252          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04253             strcat(fromfile, ".txt");
04254          }
04255          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04256             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04257                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04258             }
04259 
04260             /* You might be tempted to do origdate, except that a) it's in the wrong
04261              * format, and b) it's missing for IMAP recordings. */
04262             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04263                struct timeval tv = { inttime, };
04264                struct ast_tm tm;
04265                ast_localtime(&tv, &tm, NULL);
04266                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04267             }
04268             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04269                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04270                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04271                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04272                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04273                date, origcallerid, origdate);
04274             ast_config_destroy(msg_cfg);
04275          } else {
04276             goto plain_message;
04277          }
04278       } else {
04279 plain_message:
04280          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04281             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04282             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04283             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04284             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04285       }
04286    } else {
04287       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04288             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04289    }
04290 
04291    if (imap || attach_user_voicemail) {
04292       if (!ast_strlen_zero(attach2)) {
04293          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04294          ast_debug(5, "creating second attachment filename %s\n", filename);
04295          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04296          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04297          ast_debug(5, "creating attachment filename %s\n", filename);
04298          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04299       } else {
04300          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04301          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04302          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04303       }
04304    }
04305 }
04306 
04307 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)
04308 {
04309    char tmpdir[256], newtmp[256];
04310    char fname[256];
04311    char tmpcmd[256];
04312    int tmpfd = -1;
04313 
04314    /* Eww. We want formats to tell us their own MIME type */
04315    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04316 
04317    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04318       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04319       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04320       tmpfd = mkstemp(newtmp);
04321       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04322       ast_debug(3, "newtmp: %s\n", newtmp);
04323       if (tmpfd > -1) {
04324          int soxstatus;
04325          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04326          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04327             attach = newtmp;
04328             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04329          } else {
04330             ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04331                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04332             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04333          }
04334       }
04335    }
04336    fprintf(p, "--%s" ENDL, bound);
04337    if (msgnum > -1)
04338       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04339    else
04340       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
04341    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04342    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04343    if (msgnum > -1)
04344       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04345    else
04346       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
04347    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04348    base_encode(fname, p);
04349    if (last)
04350       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04351    if (tmpfd > -1) {
04352       unlink(fname);
04353       close(tmpfd);
04354       unlink(newtmp);
04355    }
04356    return 0;
04357 }
04358 #undef ENDL
04359 
04360 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)
04361 {
04362    FILE *p=NULL;
04363    char tmp[80] = "/tmp/astmail-XXXXXX";
04364    char tmp2[256];
04365 
04366    if (vmu && ast_strlen_zero(vmu->email)) {
04367       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04368       return(0);
04369    }
04370    if (!strcmp(format, "wav49"))
04371       format = "WAV";
04372    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));
04373    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04374       command hangs */
04375    if ((p = vm_mkftemp(tmp)) == NULL) {
04376       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04377       return -1;
04378    } else {
04379       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04380       fclose(p);
04381       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04382       ast_safe_system(tmp2);
04383       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04384    }
04385    return 0;
04386 }
04387 
04388 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)
04389 {
04390    char date[256];
04391    char host[MAXHOSTNAMELEN] = "";
04392    char who[256];
04393    char dur[PATH_MAX];
04394    char tmp[80] = "/tmp/astmail-XXXXXX";
04395    char tmp2[PATH_MAX];
04396    struct ast_tm tm;
04397    FILE *p;
04398 
04399    if ((p = vm_mkftemp(tmp)) == NULL) {
04400       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04401       return -1;
04402    }
04403    gethostname(host, sizeof(host)-1);
04404    if (strchr(srcemail, '@'))
04405       ast_copy_string(who, srcemail, sizeof(who));
04406    else 
04407       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04408    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04409    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04410    fprintf(p, "Date: %s\n", date);
04411 
04412    if (*pagerfromstring) {
04413       struct ast_channel *ast;
04414       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04415          char *passdata;
04416          int vmlen = strlen(fromstring)*3 + 200;
04417          passdata = alloca(vmlen);
04418          memset(passdata, 0, vmlen);
04419          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04420          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04421          fprintf(p, "From: %s <%s>\n", passdata, who);
04422          ast_channel_free(ast);
04423       } else 
04424          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04425    } else
04426       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04427    fprintf(p, "To: %s\n", pager);
04428    if (pagersubject) {
04429       struct ast_channel *ast;
04430       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04431          char *passdata;
04432          int vmlen = strlen(pagersubject) * 3 + 200;
04433          passdata = alloca(vmlen);
04434          memset(passdata, 0, vmlen);
04435          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04436          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04437          fprintf(p, "Subject: %s\n\n", passdata);
04438          ast_channel_free(ast);
04439       } else
04440          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04441    } else {
04442       if (ast_strlen_zero(flag)) {
04443          fprintf(p, "Subject: New VM\n\n");
04444       } else {
04445          fprintf(p, "Subject: New %s VM\n\n", flag);
04446       }
04447    }
04448 
04449    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04450    if (pagerbody) {
04451       struct ast_channel *ast;
04452       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04453          char *passdata;
04454          int vmlen = strlen(pagerbody) * 3 + 200;
04455          passdata = alloca(vmlen);
04456          memset(passdata, 0, vmlen);
04457          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04458          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04459          fprintf(p, "%s\n", passdata);
04460          ast_channel_free(ast);
04461       } else
04462          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04463    } else {
04464       fprintf(p, "New %s long %s msg in box %s\n"
04465             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04466    }
04467    fclose(p);
04468    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04469    ast_safe_system(tmp2);
04470    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04471    return 0;
04472 }
04473 
04474 /*!
04475  * \brief Gets the current date and time, as formatted string.
04476  * \param s The buffer to hold the output formatted date.
04477  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04478  * 
04479  * The date format string used is "%a %b %e %r UTC %Y".
04480  * 
04481  * \return zero on success, -1 on error.
04482  */
04483 static int get_date(char *s, int len)
04484 {
04485    struct ast_tm tm;
04486    struct timeval t = ast_tvnow();
04487    
04488    ast_localtime(&t, &tm, "UTC");
04489 
04490    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04491 }
04492 
04493 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04494 {
04495    int res;
04496    char fn[PATH_MAX];
04497    char dest[PATH_MAX];
04498 
04499    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04500 
04501    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04502       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04503       return -1;
04504    }
04505 
04506    RETRIEVE(fn, -1, ext, context);
04507    if (ast_fileexists(fn, NULL, NULL) > 0) {
04508       res = ast_stream_and_wait(chan, fn, ecodes);
04509       if (res) {
04510          DISPOSE(fn, -1);
04511          return res;
04512       }
04513    } else {
04514       /* Dispose just in case */
04515       DISPOSE(fn, -1);
04516       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04517       if (res)
04518          return res;
04519       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04520       if (res)
04521          return res;
04522    }
04523    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04524    return res;
04525 }
04526 
04527 static void free_zone(struct vm_zone *z)
04528 {
04529    ast_free(z);
04530 }
04531 
04532 #ifdef ODBC_STORAGE
04533 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04534 {
04535    int x = -1;
04536    int res;
04537    SQLHSTMT stmt = NULL;
04538    char sql[PATH_MAX];
04539    char rowdata[20];
04540    char tmp[PATH_MAX] = "";
04541    struct odbc_obj *obj = NULL;
04542    char *context;
04543    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04544 
04545    if (newmsgs)
04546       *newmsgs = 0;
04547    if (oldmsgs)
04548       *oldmsgs = 0;
04549    if (urgentmsgs)
04550       *urgentmsgs = 0;
04551 
04552    /* If no mailbox, return immediately */
04553    if (ast_strlen_zero(mailbox))
04554       return 0;
04555 
04556    ast_copy_string(tmp, mailbox, sizeof(tmp));
04557 
04558    if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
04559       int u, n, o;
04560       char *next, *remaining = tmp;
04561       while ((next = strsep(&remaining, " ,"))) {
04562          if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
04563             return -1;
04564          }
04565          if (urgentmsgs) {
04566             *urgentmsgs += u;
04567          }
04568          if (newmsgs) {
04569             *newmsgs += n;
04570          }
04571          if (oldmsgs) {
04572             *oldmsgs += o;
04573          }
04574       }
04575       return 0;
04576    }
04577 
04578    context = strchr(tmp, '@');
04579    if (context) {
04580       *context = '\0';
04581       context++;
04582    } else
04583       context = "default";
04584 
04585    if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
04586       do {
04587          if (newmsgs) {
04588             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04589             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04590                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04591                break;
04592             }
04593             res = SQLFetch(stmt);
04594             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04595                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04596                break;
04597             }
04598             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04599             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04600                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04601                break;
04602             }
04603             *newmsgs = atoi(rowdata);
04604             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04605          }
04606 
04607          if (oldmsgs) {
04608             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04609             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04610                ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04611                break;
04612             }
04613             res = SQLFetch(stmt);
04614             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04615                ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04616                break;
04617             }
04618             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04619             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04620                ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04621                break;
04622             }
04623             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
04624             *oldmsgs = atoi(rowdata);
04625          }
04626 
04627          if (urgentmsgs) {
04628             snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04629             if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
04630                ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04631                break;
04632             }
04633             res = SQLFetch(stmt);
04634             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04635                ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04636                break;
04637             }
04638             res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04639             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04640                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04641                break;
04642             }
04643             *urgentmsgs = atoi(rowdata);
04644          }
04645 
04646          x = 0;
04647       } while (0);
04648    } else {
04649       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04650    }
04651 
04652    if (stmt) {
04653       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04654    }
04655    if (obj) {
04656       ast_odbc_release_obj(obj);
04657    }
04658 
04659    return x;
04660 }
04661 
04662 /*!
04663  * \brief Gets the number of messages that exist in a mailbox folder.
04664  * \param context
04665  * \param mailbox
04666  * \param folder
04667  * 
04668  * This method is used when ODBC backend is used.
04669  * \return The number of messages in this mailbox folder (zero or more).
04670  */
04671 static int messagecount(const char *context, const char *mailbox, const char *folder)
04672 {
04673    struct odbc_obj *obj = NULL;
04674    int nummsgs = 0;
04675    int res;
04676    SQLHSTMT stmt = NULL;
04677    char sql[PATH_MAX];
04678    char rowdata[20];
04679    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04680    if (!folder)
04681       folder = "INBOX";
04682    /* If no mailbox, return immediately */
04683    if (ast_strlen_zero(mailbox))
04684       return 0;
04685 
04686    obj = ast_odbc_request_obj(odbc_database, 0);
04687    if (obj) {
04688       if (!strcmp(folder, "INBOX")) {
04689          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
04690       } else {
04691          snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04692       }
04693       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04694       if (!stmt) {
04695          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04696          goto yuck;
04697       }
04698       res = SQLFetch(stmt);
04699       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04700          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04701          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04702          goto yuck;
04703       }
04704       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04705       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04706          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04707          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04708          goto yuck;
04709       }
04710       nummsgs = atoi(rowdata);
04711       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04712    } else
04713       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04714 
04715 yuck:
04716    if (obj)
04717       ast_odbc_release_obj(obj);
04718    return nummsgs;
04719 }
04720 
04721 /** 
04722  * \brief Determines if the given folder has messages.
04723  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04724  * 
04725  * This function is used when the mailbox is stored in an ODBC back end.
04726  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04727  * \return 1 if the folder has one or more messages. zero otherwise.
04728  */
04729 static int has_voicemail(const char *mailbox, const char *folder)
04730 {
04731    char tmp[256], *tmp2 = tmp, *box, *context;
04732    ast_copy_string(tmp, mailbox, sizeof(tmp));
04733    while ((context = box = strsep(&tmp2, ",&"))) {
04734       strsep(&context, "@");
04735       if (ast_strlen_zero(context))
04736          context = "default";
04737       if (messagecount(context, box, folder))
04738          return 1;
04739    }
04740    return 0;
04741 }
04742 #endif
04743 #ifndef IMAP_STORAGE
04744 /*! 
04745  * \brief Copies a message from one mailbox to another.
04746  * \param chan
04747  * \param vmu
04748  * \param imbox
04749  * \param msgnum
04750  * \param duration
04751  * \param recip
04752  * \param fmt
04753  * \param dir
04754  *
04755  * This is only used by file storage based mailboxes.
04756  *
04757  * \return zero on success, -1 on error.
04758  */
04759 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)
04760 {
04761    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04762    const char *frombox = mbox(imbox);
04763    int recipmsgnum;
04764 
04765    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04766 
04767    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04768       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04769    } else {
04770       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04771    }
04772    
04773    if (!dir)
04774       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04775    else
04776       ast_copy_string(fromdir, dir, sizeof(fromdir));
04777 
04778    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04779    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04780 
04781    if (vm_lock_path(todir))
04782       return ERROR_LOCK_PATH;
04783 
04784    recipmsgnum = last_message_index(recip, todir) + 1;
04785    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04786       make_file(topath, sizeof(topath), todir, recipmsgnum);
04787       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04788          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04789       } else {
04790          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04791           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04792           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04793           * much worse problem happening and IMAP storage doesn't call this function
04794           */
04795          copy_plain_file(frompath, topath);
04796          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04797          vm_delete(topath);
04798       }
04799    } else {
04800       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04801    }
04802    ast_unlock_path(todir);
04803    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04804    
04805    return 0;
04806 }
04807 #endif
04808 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04809 
04810 static int messagecount(const char *context, const char *mailbox, const char *folder)
04811 {
04812    return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
04813 }
04814 
04815 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04816 {
04817    DIR *dir;
04818    struct dirent *de;
04819    char fn[256];
04820    int ret = 0;
04821 
04822    /* If no mailbox, return immediately */
04823    if (ast_strlen_zero(mailbox))
04824       return 0;
04825 
04826    if (ast_strlen_zero(folder))
04827       folder = "INBOX";
04828    if (ast_strlen_zero(context))
04829       context = "default";
04830 
04831    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04832 
04833    if (!(dir = opendir(fn)))
04834       return 0;
04835 
04836    while ((de = readdir(dir))) {
04837       if (!strncasecmp(de->d_name, "msg", 3)) {
04838          if (shortcircuit) {
04839             ret = 1;
04840             break;
04841          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
04842             ret++;
04843          }
04844       }
04845    }
04846 
04847    closedir(dir);
04848 
04849    return ret;
04850 }
04851 
04852 /** 
04853  * \brief Determines if the given folder has messages.
04854  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04855  * \param folder the folder to look in
04856  *
04857  * This function is used when the mailbox is stored in a filesystem back end.
04858  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04859  * \return 1 if the folder has one or more messages. zero otherwise.
04860  */
04861 static int has_voicemail(const char *mailbox, const char *folder)
04862 {
04863    char tmp[256], *tmp2 = tmp, *box, *context;
04864    ast_copy_string(tmp, mailbox, sizeof(tmp));
04865    if (ast_strlen_zero(folder)) {
04866       folder = "INBOX";
04867    }
04868    while ((box = strsep(&tmp2, ",&"))) {
04869       if ((context = strchr(box, '@')))
04870          *context++ = '\0';
04871       else
04872          context = "default";
04873       if (__has_voicemail(context, box, folder, 1))
04874          return 1;
04875       /* If we are checking INBOX, we should check Urgent as well */
04876       if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
04877          return 1;
04878       }
04879    }
04880    return 0;
04881 }
04882 
04883 
04884 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04885 {
04886    char tmp[256];
04887    char *context;
04888 
04889    /* If no mailbox, return immediately */
04890    if (ast_strlen_zero(mailbox))
04891       return 0;
04892 
04893    if (newmsgs)
04894       *newmsgs = 0;
04895    if (oldmsgs)
04896       *oldmsgs = 0;
04897    if (urgentmsgs)
04898       *urgentmsgs = 0;
04899 
04900    if (strchr(mailbox, ',')) {
04901       int tmpnew, tmpold, tmpurgent;
04902       char *mb, *cur;
04903 
04904       ast_copy_string(tmp, mailbox, sizeof(tmp));
04905       mb = tmp;
04906       while ((cur = strsep(&mb, ", "))) {
04907          if (!ast_strlen_zero(cur)) {
04908             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04909                return -1;
04910             else {
04911                if (newmsgs)
04912                   *newmsgs += tmpnew; 
04913                if (oldmsgs)
04914                   *oldmsgs += tmpold;
04915                if (urgentmsgs)
04916                   *urgentmsgs += tmpurgent;
04917             }
04918          }
04919       }
04920       return 0;
04921    }
04922 
04923    ast_copy_string(tmp, mailbox, sizeof(tmp));
04924    
04925    if ((context = strchr(tmp, '@')))
04926       *context++ = '\0';
04927    else
04928       context = "default";
04929 
04930    if (newmsgs)
04931       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04932    if (oldmsgs)
04933       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04934    if (urgentmsgs)
04935       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
04936 
04937    return 0;
04938 }
04939 
04940 #endif
04941 
04942 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
04943 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04944 {
04945    int urgentmsgs = 0;
04946    int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
04947    if (newmsgs) {
04948       *newmsgs += urgentmsgs;
04949    }
04950    return res;
04951 }
04952 
04953 static void run_externnotify(char *context, char *extension, const char *flag)
04954 {
04955    char arguments[255];
04956    char ext_context[256] = "";
04957    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
04958    struct ast_smdi_mwi_message *mwi_msg;
04959 
04960    if (!ast_strlen_zero(context))
04961       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04962    else
04963       ast_copy_string(ext_context, extension, sizeof(ext_context));
04964 
04965    if (smdi_iface) {
04966       if (ast_app_has_voicemail(ext_context, NULL)) 
04967          ast_smdi_mwi_set(smdi_iface, extension);
04968       else
04969          ast_smdi_mwi_unset(smdi_iface, extension);
04970 
04971       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04972          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04973          if (!strncmp(mwi_msg->cause, "INV", 3))
04974             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04975          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04976             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04977          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04978          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04979       } else {
04980          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
04981       }
04982    }
04983 
04984    if (!ast_strlen_zero(externnotify)) {
04985       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
04986          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04987       } else {
04988          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
04989          ast_debug(1, "Executing %s\n", arguments);
04990          ast_safe_system(arguments);
04991       }
04992    }
04993 }
04994 
04995 /*!
04996  * \brief Variables used for saving a voicemail.
04997  *
04998  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
04999  */
05000 struct leave_vm_options {
05001    unsigned int flags;
05002    signed char record_gain;
05003    char *exitcontext;
05004 };
05005 
05006 /*!
05007  * \brief Prompts the user and records a voicemail to a mailbox.
05008  * \param chan
05009  * \param ext
05010  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
05011  * 
05012  * 
05013  * 
05014  * \return zero on success, -1 on error.
05015  */
05016 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
05017 {
05018 #ifdef IMAP_STORAGE
05019    int newmsgs, oldmsgs;
05020 #else
05021    char urgdir[PATH_MAX];
05022 #endif
05023    char txtfile[PATH_MAX];
05024    char tmptxtfile[PATH_MAX];
05025    struct vm_state *vms = NULL;
05026    char callerid[256];
05027    FILE *txt;
05028    char date[256];
05029    int txtdes;
05030    int res = 0;
05031    int msgnum;
05032    int duration = 0;
05033    int ausemacro = 0;
05034    int ousemacro = 0;
05035    int ouseexten = 0;
05036    int rtmsgid = 0;
05037    char tmpid[16];
05038    char tmpdur[16];
05039    char priority[16];
05040    char origtime[16];
05041    char dir[PATH_MAX];
05042    char tmpdir[PATH_MAX];
05043    char fn[PATH_MAX];
05044    char prefile[PATH_MAX] = "";
05045    char tempfile[PATH_MAX] = "";
05046    char ext_context[256] = "";
05047    char fmt[80];
05048    char *context;
05049    char ecodes[17] = "#";
05050    struct ast_str *tmp = ast_str_create(16);
05051    char *tmpptr;
05052    struct ast_vm_user *vmu;
05053    struct ast_vm_user svm;
05054    const char *category = NULL;
05055    const char *code;
05056    const char *alldtmf = "0123456789ABCD*#";
05057    char flag[80];
05058 
05059    ast_str_set(&tmp, 0, "%s", ext);
05060    ext = ast_str_buffer(tmp);
05061    if ((context = strchr(ext, '@'))) {
05062       *context++ = '\0';
05063       tmpptr = strchr(context, '&');
05064    } else {
05065       tmpptr = strchr(ext, '&');
05066    }
05067 
05068    if (tmpptr)
05069       *tmpptr++ = '\0';
05070 
05071    ast_channel_lock(chan);
05072    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
05073       category = ast_strdupa(category);
05074    }
05075    ast_channel_unlock(chan);
05076 
05077    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
05078       ast_copy_string(flag, "Urgent", sizeof(flag));
05079    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
05080       ast_copy_string(flag, "PRIORITY", sizeof(flag));
05081    } else {
05082       flag[0] = '\0';
05083    }
05084 
05085    ast_debug(3, "Before find_user\n");
05086    if (!(vmu = find_user(&svm, context, ext))) {
05087       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
05088       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05089       ast_free(tmp);
05090       return res;
05091    }
05092    /* Setup pre-file if appropriate */
05093    if (strcmp(vmu->context, "default"))
05094       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
05095    else
05096       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
05097 
05098    /* Set the path to the prefile. Will be one of 
05099       VM_SPOOL_DIRcontext/ext/busy
05100       VM_SPOOL_DIRcontext/ext/unavail
05101       Depending on the flag set in options.
05102    */
05103    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
05104       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
05105    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
05106       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
05107    }
05108    /* Set the path to the tmpfile as
05109       VM_SPOOL_DIR/context/ext/temp
05110       and attempt to create the folder structure.
05111    */
05112    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
05113    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
05114       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
05115       ast_free(tmp);
05116       return -1;
05117    }
05118    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
05119    if (ast_fileexists(tempfile, NULL, NULL) > 0)
05120       ast_copy_string(prefile, tempfile, sizeof(prefile));
05121 
05122    DISPOSE(tempfile, -1);
05123    /* It's easier just to try to make it than to check for its existence */
05124    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
05125 
05126    /* Check current or macro-calling context for special extensions */
05127    if (ast_test_flag(vmu, VM_OPERATOR)) {
05128       if (!ast_strlen_zero(vmu->exit)) {
05129          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
05130             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05131             ouseexten = 1;
05132          }
05133       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
05134          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05135          ouseexten = 1;
05136       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05137          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05138          ousemacro = 1;
05139       }
05140    }
05141 
05142    if (!ast_strlen_zero(vmu->exit)) {
05143       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05144          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05145    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05146       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05147    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05148       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05149       ausemacro = 1;
05150    }
05151 
05152    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05153       for (code = alldtmf; *code; code++) {
05154          char e[2] = "";
05155          e[0] = *code;
05156          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05157             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05158       }
05159    }
05160 
05161    /* Play the beginning intro if desired */
05162    if (!ast_strlen_zero(prefile)) {
05163 #ifdef ODBC_STORAGE
05164       int success = 
05165 #endif
05166          RETRIEVE(prefile, -1, ext, context);
05167       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05168          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05169             res = ast_waitstream(chan, ecodes);
05170 #ifdef ODBC_STORAGE
05171          if (success == -1) {
05172             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05173             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05174             store_file(prefile, vmu->mailbox, vmu->context, -1);
05175          }
05176 #endif
05177       } else {
05178          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05179          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05180       }
05181       DISPOSE(prefile, -1);
05182       if (res < 0) {
05183          ast_debug(1, "Hang up during prefile playback\n");
05184          free_user(vmu);
05185          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05186          ast_free(tmp);
05187          return -1;
05188       }
05189    }
05190    if (res == '#') {
05191       /* On a '#' we skip the instructions */
05192       ast_set_flag(options, OPT_SILENT);
05193       res = 0;
05194    }
05195    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05196       res = ast_stream_and_wait(chan, INTRO, ecodes);
05197       if (res == '#') {
05198          ast_set_flag(options, OPT_SILENT);
05199          res = 0;
05200       }
05201    }
05202    if (res > 0)
05203       ast_stopstream(chan);
05204    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05205     other than the operator -- an automated attendant or mailbox login for example */
05206    if (res == '*') {
05207       chan->exten[0] = 'a';
05208       chan->exten[1] = '\0';
05209       if (!ast_strlen_zero(vmu->exit)) {
05210          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05211       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05212          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05213       }
05214       chan->priority = 0;
05215       free_user(vmu);
05216       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05217       ast_free(tmp);
05218       return 0;
05219    }
05220 
05221    /* Check for a '0' here */
05222    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05223    transfer:
05224       if (ouseexten || ousemacro) {
05225          chan->exten[0] = 'o';
05226          chan->exten[1] = '\0';
05227          if (!ast_strlen_zero(vmu->exit)) {
05228             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05229          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05230             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05231          }
05232          ast_play_and_wait(chan, "transfer");
05233          chan->priority = 0;
05234          free_user(vmu);
05235          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05236       }
05237       ast_free(tmp);
05238       return 0;
05239    }
05240 
05241    /* Allow all other digits to exit Voicemail and return to the dialplan */
05242    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05243       if (!ast_strlen_zero(options->exitcontext))
05244          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05245       free_user(vmu);
05246       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05247       ast_free(tmp);
05248       return res;
05249    }
05250 
05251    if (res < 0) {
05252       free_user(vmu);
05253       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05254       ast_free(tmp);
05255       return -1;
05256    }
05257    /* The meat of recording the message...  All the announcements and beeps have been played*/
05258    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05259    if (!ast_strlen_zero(fmt)) {
05260       msgnum = 0;
05261 
05262 #ifdef IMAP_STORAGE
05263       /* Is ext a mailbox? */
05264       /* must open stream for this user to get info! */
05265       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05266       if (res < 0) {
05267          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05268          ast_free(tmp);
05269          return -1;
05270       }
05271       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05272       /* It is possible under certain circumstances that inboxcount did not
05273        * create a vm_state when it was needed. This is a catchall which will
05274        * rarely be used.
05275        */
05276          if (!(vms = create_vm_state_from_user(vmu))) {
05277             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05278             ast_free(tmp);
05279             return -1;
05280          }
05281       }
05282       vms->newmessages++;
05283       
05284       /* here is a big difference! We add one to it later */
05285       msgnum = newmsgs + oldmsgs;
05286       ast_debug(3, "Messagecount set to %d\n",msgnum);
05287       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05288       /* set variable for compatibility */
05289       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05290 
05291       /* Check if mailbox is full */
05292       check_quota(vms, imapfolder);
05293       if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
05294          ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
05295          ast_play_and_wait(chan, "vm-mailboxfull");
05296          ast_free(tmp);
05297          return -1;
05298       }
05299       
05300       /* Check if we have exceeded maxmsg */
05301       if (msgnum >= vmu->maxmsg  - inprocess_count(vmu->mailbox, vmu->context, 0)) {
05302          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);
05303          ast_play_and_wait(chan, "vm-mailboxfull");
05304          ast_free(tmp);
05305          return -1;
05306       }
05307 #else
05308       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
05309          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05310          if (!res)
05311             res = ast_waitstream(chan, "");
05312          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05313          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05314          inprocess_count(vmu->mailbox, vmu->context, -1);
05315          goto leave_vm_out;
05316       }
05317 
05318 #endif
05319       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05320       txtdes = mkstemp(tmptxtfile);
05321       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05322       if (txtdes < 0) {
05323          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05324          if (!res)
05325             res = ast_waitstream(chan, "");
05326          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05327          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05328          inprocess_count(vmu->mailbox, vmu->context, -1);
05329          goto leave_vm_out;
05330       }
05331 
05332       /* Now play the beep once we have the message number for our next message. */
05333       if (res >= 0) {
05334          /* Unless we're *really* silent, try to send the beep */
05335          res = ast_stream_and_wait(chan, "beep", "");
05336       }
05337             
05338       /* Store information in real-time storage */
05339       if (ast_check_realtime("voicemail_data")) {
05340          snprintf(priority, sizeof(priority), "%d", chan->priority);
05341          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05342          get_date(date, sizeof(date));
05343          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);
05344       }
05345 
05346       /* Store information */
05347       txt = fdopen(txtdes, "w+");
05348       if (txt) {
05349          get_date(date, sizeof(date));
05350          fprintf(txt, 
05351             ";\n"
05352             "; Message Information file\n"
05353             ";\n"
05354             "[message]\n"
05355             "origmailbox=%s\n"
05356             "context=%s\n"
05357             "macrocontext=%s\n"
05358             "exten=%s\n"
05359             "priority=%d\n"
05360             "callerchan=%s\n"
05361             "callerid=%s\n"
05362             "origdate=%s\n"
05363             "origtime=%ld\n"
05364             "category=%s\n",
05365             ext,
05366             chan->context,
05367             chan->macrocontext, 
05368             chan->exten,
05369             chan->priority,
05370             chan->name,
05371             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05372             date, (long)time(NULL),
05373             category ? category : "");
05374       } else {
05375          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05376          inprocess_count(vmu->mailbox, vmu->context, -1);
05377          if (ast_check_realtime("voicemail_data")) {
05378             ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
05379          }
05380          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05381          goto leave_vm_out;
05382       }
05383       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05384 
05385       if (txt) {
05386          fprintf(txt, "flag=%s\n", flag);
05387          if (duration < vmminsecs) {
05388             fclose(txt);
05389             if (option_verbose > 2) 
05390                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05391             ast_filedelete(tmptxtfile, NULL);
05392             unlink(tmptxtfile);
05393             if (ast_check_realtime("voicemail_data")) {
05394                snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05395                ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05396             }
05397             inprocess_count(vmu->mailbox, vmu->context, -1);
05398          } else {
05399             fprintf(txt, "duration=%d\n", duration);
05400             fclose(txt);
05401             if (vm_lock_path(dir)) {
05402                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05403                /* Delete files */
05404                ast_filedelete(tmptxtfile, NULL);
05405                unlink(tmptxtfile);
05406                inprocess_count(vmu->mailbox, vmu->context, -1);
05407             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05408                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05409                unlink(tmptxtfile);
05410                ast_unlock_path(dir);
05411                inprocess_count(vmu->mailbox, vmu->context, -1);
05412                if (ast_check_realtime("voicemail_data")) {
05413                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05414                   ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05415                }
05416             } else {
05417 #ifndef IMAP_STORAGE
05418                msgnum = last_message_index(vmu, dir) + 1;
05419 #endif
05420                make_file(fn, sizeof(fn), dir, msgnum);
05421 
05422                /* assign a variable with the name of the voicemail file */ 
05423 #ifndef IMAP_STORAGE
05424                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05425 #else
05426                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05427 #endif
05428 
05429                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05430                ast_filerename(tmptxtfile, fn, NULL);
05431                rename(tmptxtfile, txtfile);
05432                inprocess_count(vmu->mailbox, vmu->context, -1);
05433 
05434                /* Properly set permissions on voicemail text descriptor file.
05435                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05436                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05437                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05438 
05439                ast_unlock_path(dir);
05440                if (ast_check_realtime("voicemail_data")) {
05441                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05442                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05443                   ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
05444                }
05445                /* We must store the file first, before copying the message, because
05446                 * ODBC storage does the entire copy with SQL.
05447                 */
05448                if (ast_fileexists(fn, NULL, NULL) > 0) {
05449                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05450                }
05451 
05452                /* Are there to be more recipients of this message? */
05453                while (tmpptr) {
05454                   struct ast_vm_user recipu, *recip;
05455                   char *exten, *cntx;
05456                
05457                   exten = strsep(&tmpptr, "&");
05458                   cntx = strchr(exten, '@');
05459                   if (cntx) {
05460                      *cntx = '\0';
05461                      cntx++;
05462                   }
05463                   if ((recip = find_user(&recipu, cntx, exten))) {
05464                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05465                      free_user(recip);
05466                   }
05467                }
05468 #ifndef IMAP_STORAGE
05469                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05470                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05471                   char sfn[PATH_MAX];
05472                   char dfn[PATH_MAX];
05473                   int x;
05474                   /* It's easier just to try to make it than to check for its existence */
05475                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05476                   x = last_message_index(vmu, urgdir) + 1;
05477                   make_file(sfn, sizeof(sfn), dir, msgnum);
05478                   make_file(dfn, sizeof(dfn), urgdir, x);
05479                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
05480                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05481                   /* Notification must happen for this new message in Urgent folder, not INBOX */
05482                   ast_copy_string(fn, dfn, sizeof(fn));
05483                   msgnum = x;
05484                }
05485 #endif
05486                /* Notification needs to happen after the copy, though. */
05487                if (ast_fileexists(fn, NULL, NULL)) {
05488 #ifdef IMAP_STORAGE
05489                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05490 #else
05491                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05492 #endif
05493                }
05494 
05495                /* Disposal needs to happen after the optional move and copy */
05496                if (ast_fileexists(fn, NULL, NULL)) {
05497                   DISPOSE(dir, msgnum);
05498                }
05499             }
05500          }
05501       } else {
05502          inprocess_count(vmu->mailbox, vmu->context, -1);
05503       }
05504       if (res == '0') {
05505          goto transfer;
05506       } else if (res > 0)
05507          res = 0;
05508 
05509       if (duration < vmminsecs)
05510          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05511          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05512       else
05513          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05514    } else
05515       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05516 leave_vm_out:
05517    free_user(vmu);
05518 
05519 #ifdef IMAP_STORAGE
05520    /* expunge message - use UID Expunge if supported on IMAP server*/
05521    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05522    if (expungeonhangup == 1) {
05523       ast_mutex_lock(&vms->lock);
05524 #ifdef HAVE_IMAP_TK2006
05525       if (LEVELUIDPLUS (vms->mailstream)) {
05526          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05527       } else 
05528 #endif
05529          mail_expunge(vms->mailstream);
05530       ast_mutex_unlock(&vms->lock);
05531    }
05532 #endif
05533 
05534    ast_free(tmp);
05535    return res;
05536 }
05537 
05538 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05539 {
05540    int d;
05541    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05542    return d;
05543 }
05544 
05545 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05546 {
05547 #ifdef IMAP_STORAGE
05548    /* we must use mbox(x) folder names, and copy the message there */
05549    /* simple. huh? */
05550    char sequence[10];
05551    char mailbox[256];
05552    int res;
05553 
05554    /* get the real IMAP message number for this message */
05555    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05556    
05557    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05558    ast_mutex_lock(&vms->lock);
05559    /* if save to Old folder, put in INBOX as read */
05560    if (box == OLD_FOLDER) {
05561       mail_setflag(vms->mailstream, sequence, "\\Seen");
05562       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05563    } else if (box == NEW_FOLDER) {
05564       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05565       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05566    }
05567    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05568       ast_mutex_unlock(&vms->lock);
05569       return 0;
05570    }
05571    /* Create the folder if it don't exist */
05572    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05573    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05574    if (mail_create(vms->mailstream, mailbox) == NIL) 
05575       ast_debug(5, "Folder exists.\n");
05576    else
05577       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05578    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05579    ast_mutex_unlock(&vms->lock);
05580    return res;
05581 #else
05582    char *dir = vms->curdir;
05583    char *username = vms->username;
05584    char *context = vmu->context;
05585    char sfn[PATH_MAX];
05586    char dfn[PATH_MAX];
05587    char ddir[PATH_MAX];
05588    const char *dbox = mbox(box);
05589    int x, i;
05590    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05591 
05592    if (vm_lock_path(ddir))
05593       return ERROR_LOCK_PATH;
05594 
05595    x = last_message_index(vmu, ddir) + 1;
05596 
05597    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05598       x--;
05599       for (i = 1; i <= x; i++) {
05600          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05601          make_file(sfn, sizeof(sfn), ddir, i);
05602          make_file(dfn, sizeof(dfn), ddir, i - 1);
05603          if (EXISTS(ddir, i, sfn, NULL)) {
05604             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05605          } else
05606             break;
05607       }
05608    } else {
05609       if (x >= vmu->maxmsg) {
05610          ast_unlock_path(ddir);
05611          return -1;
05612       }
05613    }
05614    make_file(sfn, sizeof(sfn), dir, msg);
05615    make_file(dfn, sizeof(dfn), ddir, x);
05616    if (strcmp(sfn, dfn)) {
05617       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05618    }
05619    ast_unlock_path(ddir);
05620 #endif
05621    return 0;
05622 }
05623 
05624 static int adsi_logo(unsigned char *buf)
05625 {
05626    int bytes = 0;
05627    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05628    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05629    return bytes;
05630 }
05631 
05632 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05633 {
05634    unsigned char buf[256];
05635    int bytes=0;
05636    int x;
05637    char num[5];
05638 
05639    *useadsi = 0;
05640    bytes += ast_adsi_data_mode(buf + bytes);
05641    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05642 
05643    bytes = 0;
05644    bytes += adsi_logo(buf);
05645    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05646 #ifdef DISPLAY
05647    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05648 #endif
05649    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05650    bytes += ast_adsi_data_mode(buf + bytes);
05651    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05652 
05653    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05654       bytes = 0;
05655       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05656       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05657       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05658       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05659       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05660       return 0;
05661    }
05662 
05663 #ifdef DISPLAY
05664    /* Add a dot */
05665    bytes = 0;
05666    bytes += ast_adsi_logo(buf);
05667    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05668    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05669    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05670    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05671 #endif
05672    bytes = 0;
05673    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05674    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05675    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05676    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05677    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05678    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05679    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05680 
05681 #ifdef DISPLAY
05682    /* Add another dot */
05683    bytes = 0;
05684    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05685    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05686 
05687    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05688    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05689 #endif
05690 
05691    bytes = 0;
05692    /* These buttons we load but don't use yet */
05693    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05694    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05695    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05696    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05697    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05698    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05699    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05700 
05701 #ifdef DISPLAY
05702    /* Add another dot */
05703    bytes = 0;
05704    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05705    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05706    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05707 #endif
05708 
05709    bytes = 0;
05710    for (x=0;x<5;x++) {
05711       snprintf(num, sizeof(num), "%d", x);
05712       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05713    }
05714    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05715    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05716 
05717 #ifdef DISPLAY
05718    /* Add another dot */
05719    bytes = 0;
05720    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05721    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05722    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05723 #endif
05724 
05725    if (ast_adsi_end_download(chan)) {
05726       bytes = 0;
05727       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05728       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05729       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05730       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05731       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05732       return 0;
05733    }
05734    bytes = 0;
05735    bytes += ast_adsi_download_disconnect(buf + bytes);
05736    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05737    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05738 
05739    ast_debug(1, "Done downloading scripts...\n");
05740 
05741 #ifdef DISPLAY
05742    /* Add last dot */
05743    bytes = 0;
05744    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05745    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05746 #endif
05747    ast_debug(1, "Restarting session...\n");
05748 
05749    bytes = 0;
05750    /* Load the session now */
05751    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05752       *useadsi = 1;
05753       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05754    } else
05755       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05756 
05757    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05758    return 0;
05759 }
05760 
05761 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05762 {
05763    int x;
05764    if (!ast_adsi_available(chan))
05765       return;
05766    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05767    if (x < 0)
05768       return;
05769    if (!x) {
05770       if (adsi_load_vmail(chan, useadsi)) {
05771          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05772          return;
05773       }
05774    } else
05775       *useadsi = 1;
05776 }
05777 
05778 static void adsi_login(struct ast_channel *chan)
05779 {
05780    unsigned char buf[256];
05781    int bytes=0;
05782    unsigned char keys[8];
05783    int x;
05784    if (!ast_adsi_available(chan))
05785       return;
05786 
05787    for (x=0;x<8;x++)
05788       keys[x] = 0;
05789    /* Set one key for next */
05790    keys[3] = ADSI_KEY_APPS + 3;
05791 
05792    bytes += adsi_logo(buf + bytes);
05793    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05794    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05795    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05796    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05797    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05798    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05799    bytes += ast_adsi_set_keys(buf + bytes, keys);
05800    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05801    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05802 }
05803 
05804 static void adsi_password(struct ast_channel *chan)
05805 {
05806    unsigned char buf[256];
05807    int bytes=0;
05808    unsigned char keys[8];
05809    int x;
05810    if (!ast_adsi_available(chan))
05811       return;
05812 
05813    for (x=0;x<8;x++)
05814       keys[x] = 0;
05815    /* Set one key for next */
05816    keys[3] = ADSI_KEY_APPS + 3;
05817 
05818    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05819    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05820    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05821    bytes += ast_adsi_set_keys(buf + bytes, keys);
05822    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05823    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05824 }
05825 
05826 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05827 {
05828    unsigned char buf[256];
05829    int bytes=0;
05830    unsigned char keys[8];
05831    int x,y;
05832 
05833    if (!ast_adsi_available(chan))
05834       return;
05835 
05836    for (x=0;x<5;x++) {
05837       y = ADSI_KEY_APPS + 12 + start + x;
05838       if (y > ADSI_KEY_APPS + 12 + 4)
05839          y = 0;
05840       keys[x] = ADSI_KEY_SKT | y;
05841    }
05842    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05843    keys[6] = 0;
05844    keys[7] = 0;
05845 
05846    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05847    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
05848    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05849    bytes += ast_adsi_set_keys(buf + bytes, keys);
05850    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05851 
05852    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05853 }
05854 
05855 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
05856 {
05857    int bytes=0;
05858    unsigned char buf[256]; 
05859    char buf1[256], buf2[256];
05860    char fn2[PATH_MAX];
05861 
05862    char cid[256]="";
05863    char *val;
05864    char *name, *num;
05865    char datetime[21]="";
05866    FILE *f;
05867 
05868    unsigned char keys[8];
05869 
05870    int x;
05871 
05872    if (!ast_adsi_available(chan))
05873       return;
05874 
05875    /* Retrieve important info */
05876    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
05877    f = fopen(fn2, "r");
05878    if (f) {
05879       while (!feof(f)) {   
05880          if (!fgets((char *)buf, sizeof(buf), f)) {
05881             continue;
05882          }
05883          if (!feof(f)) {
05884             char *stringp=NULL;
05885             stringp = (char *)buf;
05886             strsep(&stringp, "=");
05887             val = strsep(&stringp, "=");
05888             if (!ast_strlen_zero(val)) {
05889                if (!strcmp((char *)buf, "callerid"))
05890                   ast_copy_string(cid, val, sizeof(cid));
05891                if (!strcmp((char *)buf, "origdate"))
05892                   ast_copy_string(datetime, val, sizeof(datetime));
05893             }
05894          }
05895       }
05896       fclose(f);
05897    }
05898    /* New meaning for keys */
05899    for (x=0;x<5;x++)
05900       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05901    keys[6] = 0x0;
05902    keys[7] = 0x0;
05903 
05904    if (!vms->curmsg) {
05905       /* No prev key, provide "Folder" instead */
05906       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05907    }
05908    if (vms->curmsg >= vms->lastmsg) {
05909       /* If last message ... */
05910       if (vms->curmsg) {
05911          /* but not only message, provide "Folder" instead */
05912          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05913          bytes += ast_adsi_voice_mode(buf + bytes, 0);
05914 
05915       } else {
05916          /* Otherwise if only message, leave blank */
05917          keys[3] = 1;
05918       }
05919    }
05920 
05921    if (!ast_strlen_zero(cid)) {
05922       ast_callerid_parse(cid, &name, &num);
05923       if (!name)
05924          name = num;
05925    } else
05926       name = "Unknown Caller";
05927 
05928    /* If deleted, show "undeleted" */
05929 
05930    if (vms->deleted[vms->curmsg])
05931       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05932 
05933    /* Except "Exit" */
05934    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05935    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05936       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05937    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05938 
05939    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05940    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05941    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05942    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05943    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05944    bytes += ast_adsi_set_keys(buf + bytes, keys);
05945    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05946 
05947    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05948 }
05949 
05950 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05951 {
05952    int bytes=0;
05953    unsigned char buf[256];
05954    unsigned char keys[8];
05955 
05956    int x;
05957 
05958    if (!ast_adsi_available(chan))
05959       return;
05960 
05961    /* New meaning for keys */
05962    for (x=0;x<5;x++)
05963       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05964 
05965    keys[6] = 0x0;
05966    keys[7] = 0x0;
05967 
05968    if (!vms->curmsg) {
05969       /* No prev key, provide "Folder" instead */
05970       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05971    }
05972    if (vms->curmsg >= vms->lastmsg) {
05973       /* If last message ... */
05974       if (vms->curmsg) {
05975          /* but not only message, provide "Folder" instead */
05976          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05977       } else {
05978          /* Otherwise if only message, leave blank */
05979          keys[3] = 1;
05980       }
05981    }
05982 
05983    /* If deleted, show "undeleted" */
05984    if (vms->deleted[vms->curmsg]) 
05985       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05986 
05987    /* Except "Exit" */
05988    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05989    bytes += ast_adsi_set_keys(buf + bytes, keys);
05990    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05991 
05992    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05993 }
05994 
05995 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05996 {
05997    unsigned char buf[256] = "";
05998    char buf1[256] = "", buf2[256] = "";
05999    int bytes=0;
06000    unsigned char keys[8];
06001    int x;
06002 
06003    char *newm = (vms->newmessages == 1) ? "message" : "messages";
06004    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
06005    if (!ast_adsi_available(chan))
06006       return;
06007    if (vms->newmessages) {
06008       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
06009       if (vms->oldmessages) {
06010          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
06011          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
06012       } else {
06013          snprintf(buf2, sizeof(buf2), "%s.", newm);
06014       }
06015    } else if (vms->oldmessages) {
06016       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
06017       snprintf(buf2, sizeof(buf2), "%s.", oldm);
06018    } else {
06019       strcpy(buf1, "You have no messages.");
06020       buf2[0] = ' ';
06021       buf2[1] = '\0';
06022    }
06023    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06024    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06025    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06026 
06027    for (x=0;x<6;x++)
06028       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06029    keys[6] = 0;
06030    keys[7] = 0;
06031 
06032    /* Don't let them listen if there are none */
06033    if (vms->lastmsg < 0)
06034       keys[0] = 1;
06035    bytes += ast_adsi_set_keys(buf + bytes, keys);
06036 
06037    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06038 
06039    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06040 }
06041 
06042 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
06043 {
06044    unsigned char buf[256] = "";
06045    char buf1[256] = "", buf2[256] = "";
06046    int bytes=0;
06047    unsigned char keys[8];
06048    int x;
06049 
06050    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
06051 
06052    if (!ast_adsi_available(chan))
06053       return;
06054 
06055    /* Original command keys */
06056    for (x=0;x<6;x++)
06057       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
06058 
06059    keys[6] = 0;
06060    keys[7] = 0;
06061 
06062    if ((vms->lastmsg + 1) < 1)
06063       keys[0] = 0;
06064 
06065    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
06066       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
06067 
06068    if (vms->lastmsg + 1)
06069       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
06070    else
06071       strcpy(buf2, "no messages.");
06072    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
06073    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
06074    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
06075    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06076    bytes += ast_adsi_set_keys(buf + bytes, keys);
06077 
06078    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06079 
06080    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06081    
06082 }
06083 
06084 /*
06085 static void adsi_clear(struct ast_channel *chan)
06086 {
06087    char buf[256];
06088    int bytes=0;
06089    if (!ast_adsi_available(chan))
06090       return;
06091    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06092    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06093 
06094    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06095 }
06096 */
06097 
06098 static void adsi_goodbye(struct ast_channel *chan)
06099 {
06100    unsigned char buf[256];
06101    int bytes=0;
06102 
06103    if (!ast_adsi_available(chan))
06104       return;
06105    bytes += adsi_logo(buf + bytes);
06106    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
06107    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
06108    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
06109    bytes += ast_adsi_voice_mode(buf + bytes, 0);
06110 
06111    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
06112 }
06113 
06114 /*!\brief get_folder: Folder menu
06115  * Plays "press 1 for INBOX messages" etc.
06116  * Should possibly be internationalized
06117  */
06118 static int get_folder(struct ast_channel *chan, int start)
06119 {
06120    int x;
06121    int d;
06122    char fn[PATH_MAX];
06123    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
06124    if (d)
06125       return d;
06126    for (x = start; x< 5; x++) {  /* For all folders */
06127       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
06128          return d;
06129       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
06130       if (d)
06131          return d;
06132       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
06133       d = vm_play_folder_name(chan, fn);
06134       if (d)
06135          return d;
06136       d = ast_waitfordigit(chan, 500);
06137       if (d)
06138          return d;
06139    }
06140    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
06141    if (d)
06142       return d;
06143    d = ast_waitfordigit(chan, 4000);
06144    return d;
06145 }
06146 
06147 /*!
06148  * \brief plays a prompt and waits for a keypress.
06149  * \param chan
06150  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
06151  * \param start Does not appear to be used at this time.
06152  *
06153  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
06154  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06155  * prompting for the number inputs that correspond to the available folders.
06156  * 
06157  * \return zero on success, or -1 on error.
06158  */
06159 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06160 {
06161    int res = 0;
06162    res = ast_play_and_wait(chan, fn);  /* Folder name */
06163    while (((res < '0') || (res > '9')) &&
06164          (res != '#') && (res >= 0)) {
06165       res = get_folder(chan, 0);
06166    }
06167    return res;
06168 }
06169 
06170 /*!
06171  * \brief presents the option to prepend to an existing message when forwarding it.
06172  * \param chan
06173  * \param vmu
06174  * \param curdir
06175  * \param curmsg
06176  * \param vmfmts
06177  * \param context
06178  * \param record_gain
06179  * \param duration
06180  * \param vms
06181  *
06182  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06183  *
06184  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06185  * \return zero on success, -1 on error.
06186  */
06187 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06188          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06189 {
06190 #ifdef IMAP_STORAGE
06191    int res;
06192 #endif
06193    int cmd = 0;
06194    int retries = 0, prepend_duration = 0, already_recorded = 0;
06195    char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06196    char textfile[PATH_MAX];
06197    struct ast_config *msg_cfg;
06198    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06199 #ifndef IMAP_STORAGE
06200    signed char zero_gain = 0;
06201 #endif
06202    const char *duration_str;
06203 
06204    /* Must always populate duration correctly */
06205    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06206    strcpy(textfile, msgfile);
06207    strcpy(backup, msgfile);
06208    strcpy(backup_textfile, msgfile);
06209    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06210    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06211    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06212 
06213    if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06214       *duration = atoi(duration_str);
06215    } else {
06216       *duration = 0;
06217    }
06218 
06219    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06220       if (cmd)
06221          retries = 0;
06222       switch (cmd) {
06223       case '1': 
06224 
06225 #ifdef IMAP_STORAGE
06226          /* Record new intro file */
06227          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06228          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06229          res = ast_play_and_wait(chan, INTRO);
06230          res = ast_play_and_wait(chan, "beep");
06231          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06232          cmd = 't';
06233 #else
06234 
06235          /* prepend a message to the current message, update the metadata and return */
06236 
06237          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06238          strcpy(textfile, msgfile);
06239          strncat(textfile, ".txt", sizeof(textfile) - 1);
06240          *duration = 0;
06241 
06242          /* if we can't read the message metadata, stop now */
06243          if (!msg_cfg) {
06244             cmd = 0;
06245             break;
06246          }
06247          
06248          /* Back up the original file, so we can retry the prepend and restore it after forward. */
06249          if (already_recorded) {
06250             ast_filecopy(backup, msgfile, NULL);
06251             copy(backup_textfile, textfile);
06252          }
06253          else {
06254             ast_filecopy(msgfile, backup, NULL);
06255             copy(textfile,backup_textfile);
06256          }
06257          already_recorded = 1;
06258 
06259          if (record_gain)
06260             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06261 
06262          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06263          if (record_gain)
06264             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06265 
06266          
06267          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06268             *duration = atoi(duration_str);
06269 
06270          if (prepend_duration) {
06271             struct ast_category *msg_cat;
06272             /* need enough space for a maximum-length message duration */
06273             char duration_buf[12];
06274 
06275             *duration += prepend_duration;
06276             msg_cat = ast_category_get(msg_cfg, "message");
06277             snprintf(duration_buf, 11, "%ld", *duration);
06278             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06279                config_text_file_save(textfile, msg_cfg, "app_voicemail");
06280             }
06281          }
06282 
06283 #endif
06284          break;
06285       case '2': 
06286          /* NULL out introfile so we know there is no intro! */
06287 #ifdef IMAP_STORAGE
06288          *vms->introfn = '\0';
06289 #endif
06290          cmd = 't';
06291          break;
06292       case '*':
06293          cmd = '*';
06294          break;
06295       default: 
06296          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06297             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06298          if (!cmd)
06299             cmd = ast_play_and_wait(chan,"vm-starmain");
06300             /* "press star to return to the main menu" */
06301          if (!cmd)
06302             cmd = ast_waitfordigit(chan,6000);
06303          if (!cmd)
06304             retries++;
06305          if (retries > 3)
06306             cmd = 't';
06307       }
06308    }
06309 
06310    if (msg_cfg)
06311       ast_config_destroy(msg_cfg);
06312    if (prepend_duration)
06313       *duration = prepend_duration;
06314 
06315    if (already_recorded && cmd == -1) {
06316       /* restore original message if prepention cancelled */
06317       ast_filerename(backup, msgfile, NULL);
06318       rename(backup_textfile, textfile);
06319    }
06320 
06321    if (cmd == 't' || cmd == 'S')
06322       cmd = 0;
06323    return cmd;
06324 }
06325 
06326 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06327 {
06328    struct ast_event *event;
06329    char *mailbox, *context;
06330 
06331    /* Strip off @default */
06332    context = mailbox = ast_strdupa(box);
06333    strsep(&context, "@");
06334    if (ast_strlen_zero(context))
06335       context = "default";
06336 
06337    if (!(event = ast_event_new(AST_EVENT_MWI,
06338          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06339          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06340          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06341          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06342          AST_EVENT_IE_END))) {
06343       return;
06344    }
06345 
06346    ast_event_queue_and_cache(event);
06347 }
06348 
06349 /*!
06350  * \brief Sends email notification that a user has a new voicemail waiting for them.
06351  * \param chan
06352  * \param vmu
06353  * \param vms
06354  * \param msgnum
06355  * \param duration
06356  * \param fmt
06357  * \param cidnum The Caller ID phone number value.
06358  * \param cidname The Caller ID name value.
06359  *
06360  * \return zero on success, -1 on error.
06361  */
06362 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)
06363 {
06364    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06365    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06366    const char *category;
06367    char *myserveremail = serveremail;
06368 
06369    ast_channel_lock(chan);
06370    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06371       category = ast_strdupa(category);
06372    }
06373    ast_channel_unlock(chan);
06374 
06375    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
06376    make_file(fn, sizeof(fn), todir, msgnum);
06377    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06378 
06379    if (!ast_strlen_zero(vmu->attachfmt)) {
06380       if (strstr(fmt, vmu->attachfmt))
06381          fmt = vmu->attachfmt;
06382       else
06383          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);
06384    }
06385 
06386    /* Attach only the first format */
06387    fmt = ast_strdupa(fmt);
06388    stringp = fmt;
06389    strsep(&stringp, "|");
06390 
06391    if (!ast_strlen_zero(vmu->serveremail))
06392       myserveremail = vmu->serveremail;
06393 
06394    if (!ast_strlen_zero(vmu->email)) {
06395       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06396       if (!attach_user_voicemail)
06397          attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
06398 
06399       if (attach_user_voicemail)
06400          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06401 
06402       /* XXX possible imap issue, should category be NULL XXX */
06403       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06404 
06405       if (attach_user_voicemail)
06406          DISPOSE(todir, msgnum);
06407    }
06408 
06409    if (!ast_strlen_zero(vmu->pager)) {
06410       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06411    }
06412 
06413    if (ast_test_flag(vmu, VM_DELETE))
06414       DELETE(todir, msgnum, fn, vmu);
06415 
06416    /* Leave voicemail for someone */
06417    if (ast_app_has_voicemail(ext_context, NULL)) 
06418       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06419 
06420    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06421 
06422    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);
06423    run_externnotify(vmu->context, vmu->mailbox, flag);
06424 
06425 #ifdef IMAP_STORAGE
06426    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06427    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06428       vm_imap_delete(NULL, vms->curmsg, vmu);
06429       vms->newmessages--;  /* Fix new message count */
06430    }
06431 #endif
06432 
06433    return 0;
06434 }
06435 
06436 /*!
06437  * \brief Sends a voicemail message to a mailbox recipient.
06438  * \param ast_channel
06439  * \param context
06440  * \param vms
06441  * \param sender
06442  * \param fmt
06443  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06444  *             Will be 0 when called to forward an existing message (option 8)
06445  *             Will be 1 when called to leave a message (option 3->5)
06446  * \param record_gain 
06447  *
06448  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06449  * 
06450  * When in the leave message mode (is_new_message == 1):
06451  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06452  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06453  *
06454  * When in the forward message mode (is_new_message == 0):
06455  *   - retreives the current message to be forwarded
06456  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06457  *   - determines the target mailbox and folders
06458  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06459  *
06460  * \return zero on success, -1 on error.
06461  */
06462 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)
06463 {
06464 #ifdef IMAP_STORAGE
06465    int todircount=0;
06466    struct vm_state *dstvms;
06467 #endif
06468    char username[70]="";
06469    char fn[PATH_MAX]; /* for playback of name greeting */
06470    char ecodes[16] = "#";
06471    int res = 0, cmd = 0;
06472    struct ast_vm_user *receiver = NULL, *vmtmp;
06473    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06474    char *stringp;
06475    const char *s;
06476    int saved_messages = 0, found = 0;
06477    int valid_extensions = 0;
06478    char *dir;
06479    int curmsg;
06480    char urgent_str[7] = "";
06481    char tmptxtfile[PATH_MAX];
06482 #ifndef IMAP_STORAGE
06483    char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
06484 #endif
06485    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06486       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06487    }
06488 
06489    if (vms == NULL) return -1;
06490    dir = vms->curdir;
06491    curmsg = vms->curmsg;
06492 
06493    tmptxtfile[0] = '\0';
06494    while (!res && !valid_extensions) {
06495       int use_directory = 0;
06496       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06497          int done = 0;
06498          int retries = 0;
06499          cmd=0;
06500          while ((cmd >= 0) && !done ){
06501             if (cmd)
06502                retries = 0;
06503             switch (cmd) {
06504             case '1': 
06505                use_directory = 0;
06506                done = 1;
06507                break;
06508             case '2': 
06509                use_directory = 1;
06510                done=1;
06511                break;
06512             case '*': 
06513                cmd = 't';
06514                done = 1;
06515                break;
06516             default: 
06517                /* Press 1 to enter an extension press 2 to use the directory */
06518                cmd = ast_play_and_wait(chan,"vm-forward");
06519                if (!cmd)
06520                   cmd = ast_waitfordigit(chan,3000);
06521                if (!cmd)
06522                   retries++;
06523                if (retries > 3) {
06524                   cmd = 't';
06525                   done = 1;
06526                }
06527                
06528             }
06529          }
06530          if (cmd < 0 || cmd == 't')
06531             break;
06532       }
06533       
06534       if (use_directory) {
06535          /* use app_directory */
06536          
06537          char old_context[sizeof(chan->context)];
06538          char old_exten[sizeof(chan->exten)];
06539          int old_priority;
06540          struct ast_app* directory_app;
06541 
06542          directory_app = pbx_findapp("Directory");
06543          if (directory_app) {
06544             char vmcontext[256];
06545             /* make backup copies */
06546             memcpy(old_context, chan->context, sizeof(chan->context));
06547             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06548             old_priority = chan->priority;
06549             
06550             /* call the the Directory, changes the channel */
06551             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06552             res = pbx_exec(chan, directory_app, vmcontext);
06553             
06554             ast_copy_string(username, chan->exten, sizeof(username));
06555             
06556             /* restore the old context, exten, and priority */
06557             memcpy(chan->context, old_context, sizeof(chan->context));
06558             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06559             chan->priority = old_priority;
06560          } else {
06561             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06562             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06563          }
06564       } else {
06565          /* Ask for an extension */
06566          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06567          if (res)
06568             break;
06569          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06570             break;
06571       }
06572       
06573       /* start all over if no username */
06574       if (ast_strlen_zero(username))
06575          continue;
06576       stringp = username;
06577       s = strsep(&stringp, "*");
06578       /* start optimistic */
06579       valid_extensions = 1;
06580       while (s) {
06581          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06582             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06583             found++;
06584          } else {
06585             /* XXX Optimization for the future.  When we encounter a single bad extension,
06586              * bailing out on all of the extensions may not be the way to go.  We should
06587              * probably just bail on that single extension, then allow the user to enter
06588              * several more. XXX
06589              */
06590             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06591                free_user(receiver);
06592             }
06593             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06594             valid_extensions = 0;
06595             break;
06596          }
06597 
06598          /* play name if available, else play extension number */
06599          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06600          RETRIEVE(fn, -1, s, receiver->context);
06601          if (ast_fileexists(fn, NULL, NULL) > 0) {
06602             res = ast_stream_and_wait(chan, fn, ecodes);
06603             if (res) {
06604                DISPOSE(fn, -1);
06605                return res;
06606             }
06607          } else {
06608             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06609          }
06610          DISPOSE(fn, -1);
06611 
06612          s = strsep(&stringp, "*");
06613       }
06614       /* break from the loop of reading the extensions */
06615       if (valid_extensions)
06616          break;
06617       /* "I am sorry, that's not a valid extension.  Please try again." */
06618       res = ast_play_and_wait(chan, "pbx-invalid");
06619    }
06620    /* check if we're clear to proceed */
06621    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06622       return res;
06623    if (is_new_message == 1) {
06624       struct leave_vm_options leave_options;
06625       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06626       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06627 
06628       /* Send VoiceMail */
06629       memset(&leave_options, 0, sizeof(leave_options));
06630       leave_options.record_gain = record_gain;
06631       cmd = leave_voicemail(chan, mailbox, &leave_options);
06632    } else {
06633       /* Forward VoiceMail */
06634       long duration = 0;
06635       struct vm_state vmstmp;
06636       memcpy(&vmstmp, vms, sizeof(vmstmp));
06637 
06638       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06639 
06640       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06641       if (!cmd) {
06642          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06643 #ifdef IMAP_STORAGE
06644             int attach_user_voicemail;
06645             char *myserveremail = serveremail;
06646             
06647             /* get destination mailbox */
06648             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06649             if (!dstvms) {
06650                dstvms = create_vm_state_from_user(vmtmp);
06651             }
06652             if (dstvms) {
06653                init_mailstream(dstvms, 0);
06654                if (!dstvms->mailstream) {
06655                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06656                } else {
06657                   STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06658                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06659                }
06660             } else {
06661                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06662             }
06663             if (!ast_strlen_zero(vmtmp->serveremail))
06664                myserveremail = vmtmp->serveremail;
06665             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06666             /* NULL category for IMAP storage */
06667             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);
06668 #else
06669             copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06670 #endif
06671             saved_messages++;
06672             AST_LIST_REMOVE_CURRENT(list);
06673             free_user(vmtmp);
06674             if (res)
06675                break;
06676          }
06677          AST_LIST_TRAVERSE_SAFE_END;
06678          if (saved_messages > 0) {
06679             /* give confirmation that the message was saved */
06680             /* commented out since we can't forward batches yet
06681             if (saved_messages == 1)
06682                res = ast_play_and_wait(chan, "vm-message");
06683             else
06684                res = ast_play_and_wait(chan, "vm-messages");
06685             if (!res)
06686                res = ast_play_and_wait(chan, "vm-saved"); */
06687 #ifdef IMAP_STORAGE
06688             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06689             if (ast_strlen_zero(vmstmp.introfn))
06690 #endif
06691             res = ast_play_and_wait(chan, "vm-msgsaved");
06692          }  
06693 #ifndef IMAP_STORAGE
06694          /* Restore original message without prepended message if backup exists */
06695          make_file(msgfile, sizeof(msgfile), dir, curmsg);
06696          strcpy(textfile, msgfile);
06697          strcpy(backup, msgfile);
06698          strcpy(backup_textfile, msgfile);
06699          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06700          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06701          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
06702          if (ast_fileexists(backup, NULL, NULL) > 0) {
06703             ast_filerename(backup, msgfile, NULL);
06704             rename(backup_textfile, textfile);
06705          }
06706 #endif
06707       }
06708       DISPOSE(dir, curmsg);
06709    }
06710 
06711    /* If anything failed above, we still have this list to free */
06712    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06713       free_user(vmtmp);
06714    return res ? res : cmd;
06715 }
06716 
06717 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06718 {
06719    int res;
06720    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06721       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06722    return res;
06723 }
06724 
06725 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06726 {
06727    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);
06728 }
06729 
06730 static int play_message_category(struct ast_channel *chan, const char *category)
06731 {
06732    int res = 0;
06733 
06734    if (!ast_strlen_zero(category))
06735       res = ast_play_and_wait(chan, category);
06736 
06737    if (res) {
06738       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06739       res = 0;
06740    }
06741 
06742    return res;
06743 }
06744 
06745 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06746 {
06747    int res = 0;
06748    struct vm_zone *the_zone = NULL;
06749    time_t t;
06750 
06751    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06752       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06753       return 0;
06754    }
06755 
06756    /* Does this user have a timezone specified? */
06757    if (!ast_strlen_zero(vmu->zonetag)) {
06758       /* Find the zone in the list */
06759       struct vm_zone *z;
06760       AST_LIST_LOCK(&zones);
06761       AST_LIST_TRAVERSE(&zones, z, list) {
06762          if (!strcmp(z->name, vmu->zonetag)) {
06763             the_zone = z;
06764             break;
06765          }
06766       }
06767       AST_LIST_UNLOCK(&zones);
06768    }
06769 
06770 /* No internal variable parsing for now, so we'll comment it out for the time being */
06771 #if 0
06772    /* Set the DIFF_* variables */
06773    ast_localtime(&t, &time_now, NULL);
06774    tv_now = ast_tvnow();
06775    ast_localtime(&tv_now, &time_then, NULL);
06776 
06777    /* Day difference */
06778    if (time_now.tm_year == time_then.tm_year)
06779       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06780    else
06781       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06782    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06783 
06784    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06785 #endif
06786    if (the_zone) {
06787       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06788    } else if (!strncasecmp(chan->language, "de", 2)) {     /* GERMAN syntax */
06789       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06790    } else if (!strncasecmp(chan->language, "gr", 2)) {     /* GREEK syntax */
06791       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06792    } else if (!strncasecmp(chan->language, "it", 2)) {     /* ITALIAN syntax */
06793       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);
06794    } else if (!strncasecmp(chan->language, "nl", 2)) {     /* DUTCH syntax */
06795       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06796    } else if (!strncasecmp(chan->language, "no", 2)) {     /* NORWEGIAN syntax */
06797       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06798    } else if (!strncasecmp(chan->language, "pl", 2)) {     /* POLISH syntax */
06799       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06800    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* Brazillian PORTUGUESE syntax */
06801       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);
06802    } else if (!strncasecmp(chan->language, "se", 2)) {     /* SWEDISH syntax */
06803       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06804    } else if (!strncasecmp(chan->language, "zh", 2)) {     /* CHINESE (Taiwan) syntax */
06805       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
06806    } else {
06807       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06808    }
06809 #if 0
06810    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06811 #endif
06812    return res;
06813 }
06814 
06815 
06816 
06817 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06818 {
06819    int res = 0;
06820    int i;
06821    char *callerid, *name;
06822    char prefile[PATH_MAX] = "";
06823    
06824 
06825    /* If voicemail cid is not enabled, or we didn't get cid or context from
06826     * the attribute file, leave now.
06827     *
06828     * TODO Still need to change this so that if this function is called by the
06829     * message envelope (and someone is explicitly requesting to hear the CID),
06830     * it does not check to see if CID is enabled in the config file.
06831     */
06832    if ((cid == NULL)||(context == NULL))
06833       return res;
06834 
06835    /* Strip off caller ID number from name */
06836    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
06837    ast_callerid_parse(cid, &name, &callerid);
06838    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
06839       /* Check for internal contexts and only */
06840       /* say extension when the call didn't come from an internal context in the list */
06841       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
06842          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
06843          if ((strcmp(cidinternalcontexts[i], context) == 0))
06844             break;
06845       }
06846       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
06847          if (!res) {
06848             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
06849             if (!ast_strlen_zero(prefile)) {
06850             /* See if we can find a recorded name for this person instead of their extension number */
06851                if (ast_fileexists(prefile, NULL, NULL) > 0) {
06852                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
06853                   if (!callback)
06854                      res = wait_file2(chan, vms, "vm-from");
06855                   res = ast_stream_and_wait(chan, prefile, "");
06856                } else {
06857                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
06858                   /* Say "from extension" as one saying to sound smoother */
06859                   if (!callback)
06860                      res = wait_file2(chan, vms, "vm-from-extension");
06861                   res = ast_say_digit_str(chan, callerid, "", chan->language);
06862                }
06863             }
06864          }
06865       } else if (!res) {
06866          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
06867          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
06868          if (!callback)
06869             res = wait_file2(chan, vms, "vm-from-phonenumber");
06870          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
06871       }
06872    } else {
06873       /* Number unknown */
06874       ast_debug(1, "VM-CID: From an unknown number\n");
06875       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
06876       res = wait_file2(chan, vms, "vm-unknown-caller");
06877    }
06878    return res;
06879 }
06880 
06881 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
06882 {
06883    int res = 0;
06884    int durationm;
06885    int durations;
06886    /* Verify that we have a duration for the message */
06887    if (duration == NULL)
06888       return res;
06889 
06890    /* Convert from seconds to minutes */
06891    durations=atoi(duration);
06892    durationm=(durations / 60);
06893 
06894    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
06895 
06896    if ((!res) && (durationm >= minduration)) {
06897       res = wait_file2(chan, vms, "vm-duration");
06898 
06899       /* POLISH syntax */
06900       if (!strncasecmp(chan->language, "pl", 2)) {
06901          div_t num = div(durationm, 10);
06902 
06903          if (durationm == 1) {
06904             res = ast_play_and_wait(chan, "digits/1z");
06905             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
06906          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06907             if (num.rem == 2) {
06908                if (!num.quot) {
06909                   res = ast_play_and_wait(chan, "digits/2-ie");
06910                } else {
06911                   res = say_and_wait(chan, durationm - 2 , chan->language);
06912                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06913                }
06914             } else {
06915                res = say_and_wait(chan, durationm, chan->language);
06916             }
06917             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
06918          } else {
06919             res = say_and_wait(chan, durationm, chan->language);
06920             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
06921          }
06922       /* DEFAULT syntax */
06923       } else {
06924          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
06925          res = wait_file2(chan, vms, "vm-minutes");
06926       }
06927    }
06928    return res;
06929 }
06930 
06931 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
06932 {
06933    int res = 0;
06934    char filename[256], *cid;
06935    const char *origtime, *context, *category, *duration, *flag;
06936    struct ast_config *msg_cfg;
06937    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06938 
06939    vms->starting = 0;
06940    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06941    adsi_message(chan, vms);
06942    if (!vms->curmsg)
06943       res = wait_file2(chan, vms, "vm-first");  /* "First" */
06944    else if (vms->curmsg == vms->lastmsg)
06945       res = wait_file2(chan, vms, "vm-last");      /* "last" */
06946 
06947    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
06948    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
06949    msg_cfg = ast_config_load(filename, config_flags);
06950    if (!msg_cfg) {
06951       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06952       return 0;
06953    }
06954    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
06955 
06956    /* Play the word urgent if we are listening to urgent messages */
06957    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
06958       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
06959    }
06960 
06961    if (!res) {
06962       /* POLISH syntax */
06963       if (!strncasecmp(chan->language, "pl", 2)) {
06964          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06965             int ten, one;
06966             char nextmsg[256];
06967             ten = (vms->curmsg + 1) / 10;
06968             one = (vms->curmsg + 1) % 10;
06969 
06970             if (vms->curmsg < 20) {
06971                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
06972                res = wait_file2(chan, vms, nextmsg);
06973             } else {
06974                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
06975                res = wait_file2(chan, vms, nextmsg);
06976                if (one > 0) {
06977                   if (!res) {
06978                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
06979                      res = wait_file2(chan, vms, nextmsg);
06980                   }
06981                }
06982             }
06983          }
06984          if (!res)
06985             res = wait_file2(chan, vms, "vm-message");
06986       /* HEBREW syntax */
06987       } else if (!strncasecmp(chan->language, "he", 2)) {
06988          if (!vms->curmsg) {
06989             res = wait_file2(chan, vms, "vm-message");
06990             res = wait_file2(chan, vms, "vm-first");
06991          } else if (vms->curmsg == vms->lastmsg) {
06992             res = wait_file2(chan, vms, "vm-message");
06993             res = wait_file2(chan, vms, "vm-last");
06994          } else {
06995             res = wait_file2(chan, vms, "vm-message");
06996             res = wait_file2(chan, vms, "vm-number");
06997             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
06998          }
06999       } else {
07000          if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
07001             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
07002          } else { /* DEFAULT syntax */
07003             res = wait_file2(chan, vms, "vm-message");
07004          }
07005          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
07006             if (!res) {
07007                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
07008             }
07009          }
07010       }
07011    }
07012 
07013    if (!msg_cfg) {
07014       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
07015       return 0;
07016    }
07017 
07018    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
07019       ast_log(AST_LOG_WARNING, "No origtime?!\n");
07020       DISPOSE(vms->curdir, vms->curmsg);
07021       ast_config_destroy(msg_cfg);
07022       return 0;
07023    }
07024 
07025    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
07026    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
07027    category = ast_variable_retrieve(msg_cfg, "message", "category");
07028 
07029    context = ast_variable_retrieve(msg_cfg, "message", "context");
07030    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
07031       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
07032    if (!res) {
07033       res = play_message_category(chan, category);
07034    }
07035    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
07036       res = play_message_datetime(chan, vmu, origtime, filename);
07037    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
07038       res = play_message_callerid(chan, vms, cid, context, 0);
07039    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
07040       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
07041    /* Allow pressing '1' to skip envelope / callerid */
07042    if (res == '1')
07043       res = 0;
07044    ast_config_destroy(msg_cfg);
07045 
07046    if (!res) {
07047       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
07048       vms->heard[vms->curmsg] = 1;
07049 #ifdef IMAP_STORAGE
07050       /*IMAP storage stores any prepended message from a forward
07051        * as a separate file from the rest of the message
07052        */
07053       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
07054          wait_file(chan, vms, vms->introfn);
07055       }
07056 #endif
07057       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
07058          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
07059          res = 0;
07060       }
07061    }
07062    DISPOSE(vms->curdir, vms->curmsg);
07063    return res;
07064 }
07065 
07066 #ifdef IMAP_STORAGE
07067 static int imap_remove_file(char *dir, int msgnum)
07068 {
07069    char fn[PATH_MAX];
07070    char full_fn[PATH_MAX];
07071    char intro[PATH_MAX] = {0,};
07072    
07073    if (msgnum > -1) {
07074       make_file(fn, sizeof(fn), dir, msgnum);
07075       snprintf(intro, sizeof(intro), "%sintro", fn);
07076    } else
07077       ast_copy_string(fn, dir, sizeof(fn));
07078    
07079    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
07080       ast_filedelete(fn, NULL);
07081       if (!ast_strlen_zero(intro)) {
07082          ast_filedelete(intro, NULL);
07083       }
07084       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
07085       unlink(full_fn);
07086    }
07087    return 0;
07088 }
07089 
07090 
07091 
07092 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
07093 {
07094    char *file, *filename;
07095    char *attachment;
07096    char arg[10];
07097    int i;
07098    BODY* body;
07099 
07100    
07101    file = strrchr(ast_strdupa(dir), '/');
07102    if (file)
07103       *file++ = '\0';
07104    else {
07105       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
07106       return -1;
07107    }
07108 
07109    ast_mutex_lock(&vms->lock);
07110    for (i = 0; i < vms->mailstream->nmsgs; i++) {
07111       mail_fetchstructure(vms->mailstream, i + 1, &body);
07112       /* We have the body, now we extract the file name of the first attachment. */
07113       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
07114          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
07115       } else {
07116          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
07117          ast_mutex_unlock(&vms->lock);
07118          return -1;
07119       }
07120       filename = strsep(&attachment, ".");
07121       if (!strcmp(filename, file)) {
07122          sprintf (arg,"%d", i+1);
07123          mail_setflag (vms->mailstream,arg,"\\DELETED");
07124       }
07125    }
07126    mail_expunge(vms->mailstream);
07127    ast_mutex_unlock(&vms->lock);
07128    return 0;
07129 }
07130 
07131 #else
07132 #ifndef IMAP_STORAGE
07133 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
07134 {
07135    int count_msg, last_msg;
07136 
07137    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
07138    
07139    /* Rename the member vmbox HERE so that we don't try to return before
07140     * we know what's going on.
07141     */
07142    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
07143    
07144    /* Faster to make the directory than to check if it exists. */
07145    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
07146 
07147    count_msg = count_messages(vmu, vms->curdir);
07148    if (count_msg < 0)
07149       return count_msg;
07150    else
07151       vms->lastmsg = count_msg - 1;
07152 
07153    /*
07154    The following test is needed in case sequencing gets messed up.
07155    There appears to be more than one way to mess up sequence, so
07156    we will not try to find all of the root causes--just fix it when
07157    detected.
07158    */
07159 
07160    if (vm_lock_path(vms->curdir)) {
07161       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
07162       return -1;
07163    }
07164 
07165    last_msg = last_message_index(vmu, vms->curdir);
07166    ast_unlock_path(vms->curdir);
07167 
07168    if (last_msg < 0) 
07169       return last_msg;
07170 
07171    return 0;
07172 }
07173 #endif
07174 #endif
07175 
07176 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
07177 {
07178    int x = 0;
07179 #ifndef IMAP_STORAGE
07180    int res = 0, nummsg;
07181    char fn2[PATH_MAX];
07182 #endif
07183 
07184    if (vms->lastmsg <= -1)
07185       goto done;
07186 
07187    vms->curmsg = -1; 
07188 #ifndef IMAP_STORAGE
07189    /* Get the deleted messages fixed */ 
07190    if (vm_lock_path(vms->curdir))
07191       return ERROR_LOCK_PATH;
07192 
07193    for (x = 0; x < vmu->maxmsg; x++) { 
07194       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
07195          /* Save this message.  It's not in INBOX or hasn't been heard */ 
07196          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
07197          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
07198             break;
07199          vms->curmsg++; 
07200          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg); 
07201          if (strcmp(vms->fn, fn2)) { 
07202             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07203          } 
07204       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
07205          /* Move to old folder before deleting */ 
07206          res = save_to_folder(vmu, vms, x, 1);
07207          if (res == ERROR_LOCK_PATH) {
07208             /* If save failed do not delete the message */
07209             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07210             vms->deleted[x] = 0;
07211             vms->heard[x] = 0;
07212             --x;
07213          }
07214       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07215          /* Move to deleted folder */ 
07216          res = save_to_folder(vmu, vms, x, 10);
07217          if (res == ERROR_LOCK_PATH) {
07218             /* If save failed do not delete the message */
07219             vms->deleted[x] = 0;
07220             vms->heard[x] = 0;
07221             --x;
07222          }
07223       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07224          /* If realtime storage enabled - we should explicitly delete this message,
07225          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07226          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07227          if (EXISTS(vms->curdir, x, vms->fn, NULL))
07228             DELETE(vms->curdir, x, vms->fn, vmu);
07229       }
07230    } 
07231 
07232    /* Delete ALL remaining messages */
07233    nummsg = x - 1;
07234    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07235       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07236       if (EXISTS(vms->curdir, x, vms->fn, NULL))
07237          DELETE(vms->curdir, x, vms->fn, vmu);
07238    }
07239    ast_unlock_path(vms->curdir);
07240 #else
07241    if (vms->deleted) {
07242       for (x=0;x < vmu->maxmsg;x++) { 
07243          if (vms->deleted[x]) { 
07244             ast_debug(3,"IMAP delete of %d\n",x);
07245             DELETE(vms->curdir, x, vms->fn, vmu);
07246          }
07247       }
07248    }
07249 #endif
07250 
07251 done:
07252    if (vms->deleted)
07253       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
07254    if (vms->heard)
07255       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
07256 
07257    return 0;
07258 }
07259 
07260 /* In Greek even though we CAN use a syntax like "friends messages"
07261  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07262  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
07263  * syntax for the above three categories which is more elegant. 
07264  */
07265 
07266 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07267 {
07268    int cmd;
07269    char *buf;
07270 
07271    buf = alloca(strlen(box)+2); 
07272    strcpy(buf, box);
07273    strcat(buf,"s");
07274 
07275    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07276       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07277       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07278    } else {
07279       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07280       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07281    }
07282 }
07283 
07284 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07285 {
07286    int cmd;
07287 
07288    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07289       if (!strcasecmp(box, "vm-INBOX"))
07290          cmd = ast_play_and_wait(chan, "vm-new-e");
07291       else
07292          cmd = ast_play_and_wait(chan, "vm-old-e");
07293       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07294    } else {
07295       cmd = ast_play_and_wait(chan, "vm-messages");
07296       return cmd ? cmd : ast_play_and_wait(chan, box);
07297    }
07298 }
07299 
07300 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07301 {
07302    int cmd;
07303 
07304    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07305       cmd = ast_play_and_wait(chan, "vm-messages");
07306       return cmd ? cmd : ast_play_and_wait(chan, box);
07307    } else {
07308       cmd = ast_play_and_wait(chan, box);
07309       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07310    }
07311 }
07312 
07313 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07314 {
07315    int cmd;
07316 
07317    if (  !strncasecmp(chan->language, "it", 2) ||
07318         !strncasecmp(chan->language, "es", 2) ||
07319         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
07320       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07321       return cmd ? cmd : ast_play_and_wait(chan, box);
07322    } else if (!strncasecmp(chan->language, "gr", 2)) {
07323       return vm_play_folder_name_gr(chan, box);
07324    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
07325       return ast_play_and_wait(chan, box);
07326    } else if (!strncasecmp(chan->language, "pl", 2)) {
07327       return vm_play_folder_name_pl(chan, box);
07328    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
07329       return vm_play_folder_name_ua(chan, box);
07330    } else {  /* Default English */
07331       cmd = ast_play_and_wait(chan, box);
07332       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07333    }
07334 }
07335 
07336 /* GREEK SYNTAX
07337    In greek the plural for old/new is
07338    different so we need the following files
07339    We also need vm-denExeteMynhmata because
07340    this syntax is different.
07341 
07342    -> vm-Olds.wav : "Palia"
07343    -> vm-INBOXs.wav : "Nea"
07344    -> vm-denExeteMynhmata : "den exete mynhmata"
07345 */
07346 
07347 
07348 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07349 {
07350    int res = 0;
07351 
07352    if (vms->newmessages) {
07353       res = ast_play_and_wait(chan, "vm-youhave");
07354       if (!res) 
07355          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07356       if (!res) {
07357          if ((vms->newmessages == 1)) {
07358             res = ast_play_and_wait(chan, "vm-INBOX");
07359             if (!res)
07360                res = ast_play_and_wait(chan, "vm-message");
07361          } else {
07362             res = ast_play_and_wait(chan, "vm-INBOXs");
07363             if (!res)
07364                res = ast_play_and_wait(chan, "vm-messages");
07365          }
07366       }
07367    } else if (vms->oldmessages){
07368       res = ast_play_and_wait(chan, "vm-youhave");
07369       if (!res)
07370          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07371       if ((vms->oldmessages == 1)){
07372          res = ast_play_and_wait(chan, "vm-Old");
07373          if (!res)
07374             res = ast_play_and_wait(chan, "vm-message");
07375       } else {
07376          res = ast_play_and_wait(chan, "vm-Olds");
07377          if (!res)
07378             res = ast_play_and_wait(chan, "vm-messages");
07379       }
07380    } else if (!vms->oldmessages && !vms->newmessages) 
07381       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07382    return res;
07383 }
07384 
07385 /* Version of vm_intro() designed to work for many languages.
07386  *
07387  * It is hoped that this function can prevent the proliferation of 
07388  * language-specific vm_intro() functions and in time replace the language-
07389  * specific functions which already exist.  An examination of the language-
07390  * specific functions revealed that they all corrected the same deficiencies
07391  * in vm_intro_en() (which was the default function). Namely:
07392  *
07393  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07394  *     wording of the voicemail greeting hides this problem.  For example,
07395  *     vm-INBOX contains only the word "new".  This means that both of these
07396  *     sequences produce valid utterances:
07397  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07398  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07399  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07400  *     in many languages) the first utterance becomes "you have 1 the new message".
07401  *  2) The function contains hardcoded rules for pluralizing the word "message".
07402  *     These rules are correct for English, but not for many other languages.
07403  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07404  *     required in many languages.
07405  *  4) The gender of the word for "message" is not specified. This is a problem
07406  *     because in many languages the gender of the number in phrases such
07407  *     as "you have one new message" must match the gender of the word
07408  *     meaning "message".
07409  *
07410  * Fixing these problems for each new language has meant duplication of effort.
07411  * This new function solves the problems in the following general ways:
07412  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07413  *     and vm-Old respectively for those languages where it makes sense.
07414  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07415  *     on vm-message.
07416  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07417  *     prefix on vm-new and vm-old (none for English).
07418  *  4) Pass the gender of the language's word for "message" as an agument to
07419  *     this function which is can in turn pass on to the functions which 
07420  *     say numbers and put endings on nounds and adjectives.
07421  *
07422  * All languages require these messages:
07423  *  vm-youhave    "You have..."
07424  *  vm-and     "and"
07425  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07426  *
07427  * To use it for English, you will need these additional sound files:
07428  *  vm-new     "new"
07429  *  vm-message    "message", singular
07430  *  vm-messages      "messages", plural
07431  *
07432  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07433  *
07434  *  vm-newn    "novoye" (singular, neuter)
07435  *  vm-newx    "novikh" (counting plural form, genative plural)
07436  *  vm-message    "sobsheniye" (singular form)
07437  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07438  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07439  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07440  *  digits/2n     "dva" (neuter singular)
07441  */
07442 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07443 {
07444    int res;
07445    int lastnum = 0;
07446 
07447    res = ast_play_and_wait(chan, "vm-youhave");
07448 
07449    if (!res && vms->newmessages) {
07450       lastnum = vms->newmessages;
07451 
07452       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07453          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07454       }
07455 
07456       if (!res && vms->oldmessages) {
07457          res = ast_play_and_wait(chan, "vm-and");
07458       }
07459    }
07460 
07461    if (!res && vms->oldmessages) {
07462       lastnum = vms->oldmessages;
07463 
07464       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07465          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07466       }
07467    }
07468 
07469    if (!res) {
07470       if (lastnum == 0) {
07471          res = ast_play_and_wait(chan, "vm-no");
07472       }
07473       if (!res) {
07474          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07475       }
07476    }
07477 
07478    return res;
07479 }
07480 
07481 /* Default Hebrew syntax */
07482 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07483 {
07484    int res = 0;
07485 
07486    /* Introduce messages they have */
07487    if (!res) {
07488       if ((vms->newmessages) || (vms->oldmessages)) {
07489          res = ast_play_and_wait(chan, "vm-youhave");
07490       }
07491       /*
07492        * The word "shtei" refers to the number 2 in hebrew when performing a count
07493        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07494        * an element, this is one of them.
07495        */
07496       if (vms->newmessages) {
07497          if (!res) {
07498             if (vms->newmessages == 1) {
07499                res = ast_play_and_wait(chan, "vm-INBOX1");
07500             } else {
07501                if (vms->newmessages == 2) {
07502                   res = ast_play_and_wait(chan, "vm-shtei");
07503                } else {
07504                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07505                }
07506                res = ast_play_and_wait(chan, "vm-INBOX");
07507             }
07508          }
07509          if (vms->oldmessages && !res) {
07510             res = ast_play_and_wait(chan, "vm-and");
07511             if (vms->oldmessages == 1) {
07512                res = ast_play_and_wait(chan, "vm-Old1");
07513             } else {
07514                if (vms->oldmessages == 2) {
07515                   res = ast_play_and_wait(chan, "vm-shtei");
07516                } else {
07517                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07518                }
07519                res = ast_play_and_wait(chan, "vm-Old");
07520             }
07521          }
07522       }
07523       if (!res && vms->oldmessages && !vms->newmessages) {
07524          if (!res) {
07525             if (vms->oldmessages == 1) {
07526                res = ast_play_and_wait(chan, "vm-Old1");
07527             } else {
07528                if (vms->oldmessages == 2) {
07529                   res = ast_play_and_wait(chan, "vm-shtei");
07530                } else {
07531                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07532                }
07533                res = ast_play_and_wait(chan, "vm-Old");
07534             }
07535          }
07536       }
07537       if (!res) {
07538          if (!vms->oldmessages && !vms->newmessages) {
07539             if (!res) {
07540                res = ast_play_and_wait(chan, "vm-nomessages");
07541             }
07542          }
07543       }
07544    }
07545    return res;
07546 }
07547    
07548 /* Default English syntax */
07549 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07550 {
07551    int res;
07552 
07553    /* Introduce messages they have */
07554    res = ast_play_and_wait(chan, "vm-youhave");
07555    if (!res) {
07556       if (vms->urgentmessages) {
07557          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07558          if (!res)
07559             res = ast_play_and_wait(chan, "vm-Urgent");
07560          if ((vms->oldmessages || vms->newmessages) && !res) {
07561             res = ast_play_and_wait(chan, "vm-and");
07562          } else if (!res) {
07563             if ((vms->urgentmessages == 1))
07564                res = ast_play_and_wait(chan, "vm-message");
07565             else
07566                res = ast_play_and_wait(chan, "vm-messages");
07567          }
07568       }
07569       if (vms->newmessages) {
07570          res = say_and_wait(chan, vms->newmessages, chan->language);
07571          if (!res)
07572             res = ast_play_and_wait(chan, "vm-INBOX");
07573          if (vms->oldmessages && !res)
07574             res = ast_play_and_wait(chan, "vm-and");
07575          else if (!res) {
07576             if ((vms->newmessages == 1))
07577                res = ast_play_and_wait(chan, "vm-message");
07578             else
07579                res = ast_play_and_wait(chan, "vm-messages");
07580          }
07581             
07582       }
07583       if (!res && vms->oldmessages) {
07584          res = say_and_wait(chan, vms->oldmessages, chan->language);
07585          if (!res)
07586             res = ast_play_and_wait(chan, "vm-Old");
07587          if (!res) {
07588             if (vms->oldmessages == 1)
07589                res = ast_play_and_wait(chan, "vm-message");
07590             else
07591                res = ast_play_and_wait(chan, "vm-messages");
07592          }
07593       }
07594       if (!res) {
07595          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07596             res = ast_play_and_wait(chan, "vm-no");
07597             if (!res)
07598                res = ast_play_and_wait(chan, "vm-messages");
07599          }
07600       }
07601    }
07602    return res;
07603 }
07604 
07605 /* ITALIAN syntax */
07606 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07607 {
07608    /* Introduce messages they have */
07609    int res;
07610    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07611       res = ast_play_and_wait(chan, "vm-no") ||
07612          ast_play_and_wait(chan, "vm-message");
07613    else
07614       res = ast_play_and_wait(chan, "vm-youhave");
07615    if (!res && vms->newmessages) {
07616       res = (vms->newmessages == 1) ?
07617          ast_play_and_wait(chan, "digits/un") ||
07618          ast_play_and_wait(chan, "vm-nuovo") ||
07619          ast_play_and_wait(chan, "vm-message") :
07620          /* 2 or more new messages */
07621          say_and_wait(chan, vms->newmessages, chan->language) ||
07622          ast_play_and_wait(chan, "vm-nuovi") ||
07623          ast_play_and_wait(chan, "vm-messages");
07624       if (!res && vms->oldmessages)
07625          res = ast_play_and_wait(chan, "vm-and");
07626    }
07627    if (!res && vms->oldmessages) {
07628       res = (vms->oldmessages == 1) ?
07629          ast_play_and_wait(chan, "digits/un") ||
07630          ast_play_and_wait(chan, "vm-vecchio") ||
07631          ast_play_and_wait(chan, "vm-message") :
07632          /* 2 or more old messages */
07633          say_and_wait(chan, vms->oldmessages, chan->language) ||
07634          ast_play_and_wait(chan, "vm-vecchi") ||
07635          ast_play_and_wait(chan, "vm-messages");
07636    }
07637    return res;
07638 }
07639 
07640 /* POLISH syntax */
07641 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07642 {
07643    /* Introduce messages they have */
07644    int res;
07645    div_t num;
07646 
07647    if (!vms->oldmessages && !vms->newmessages) {
07648       res = ast_play_and_wait(chan, "vm-no");
07649       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07650       return res;
07651    } else {
07652       res = ast_play_and_wait(chan, "vm-youhave");
07653    }
07654 
07655    if (vms->newmessages) {
07656       num = div(vms->newmessages, 10);
07657       if (vms->newmessages == 1) {
07658          res = ast_play_and_wait(chan, "digits/1-a");
07659          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07660          res = res ? res : ast_play_and_wait(chan, "vm-message");
07661       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07662          if (num.rem == 2) {
07663             if (!num.quot) {
07664                res = ast_play_and_wait(chan, "digits/2-ie");
07665             } else {
07666                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07667                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07668             }
07669          } else {
07670             res = say_and_wait(chan, vms->newmessages, chan->language);
07671          }
07672          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07673          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07674       } else {
07675          res = say_and_wait(chan, vms->newmessages, chan->language);
07676          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07677          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07678       }
07679       if (!res && vms->oldmessages)
07680          res = ast_play_and_wait(chan, "vm-and");
07681    }
07682    if (!res && vms->oldmessages) {
07683       num = div(vms->oldmessages, 10);
07684       if (vms->oldmessages == 1) {
07685          res = ast_play_and_wait(chan, "digits/1-a");
07686          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07687          res = res ? res : ast_play_and_wait(chan, "vm-message");
07688       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07689          if (num.rem == 2) {
07690             if (!num.quot) {
07691                res = ast_play_and_wait(chan, "digits/2-ie");
07692             } else {
07693                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07694                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07695             }
07696          } else {
07697             res = say_and_wait(chan, vms->oldmessages, chan->language);
07698          }
07699          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07700          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07701       } else {
07702          res = say_and_wait(chan, vms->oldmessages, chan->language);
07703          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07704          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07705       }
07706    }
07707 
07708    return res;
07709 }
07710 
07711 /* SWEDISH syntax */
07712 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07713 {
07714    /* Introduce messages they have */
07715    int res;
07716 
07717    res = ast_play_and_wait(chan, "vm-youhave");
07718    if (res)
07719       return res;
07720 
07721    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07722       res = ast_play_and_wait(chan, "vm-no");
07723       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07724       return res;
07725    }
07726 
07727    if (vms->newmessages) {
07728       if ((vms->newmessages == 1)) {
07729          res = ast_play_and_wait(chan, "digits/ett");
07730          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07731          res = res ? res : ast_play_and_wait(chan, "vm-message");
07732       } else {
07733          res = say_and_wait(chan, vms->newmessages, chan->language);
07734          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07735          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07736       }
07737       if (!res && vms->oldmessages)
07738          res = ast_play_and_wait(chan, "vm-and");
07739    }
07740    if (!res && vms->oldmessages) {
07741       if (vms->oldmessages == 1) {
07742          res = ast_play_and_wait(chan, "digits/ett");
07743          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07744          res = res ? res : ast_play_and_wait(chan, "vm-message");
07745       } else {
07746          res = say_and_wait(chan, vms->oldmessages, chan->language);
07747          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07748          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07749       }
07750    }
07751 
07752    return res;
07753 }
07754 
07755 /* NORWEGIAN syntax */
07756 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07757 {
07758    /* Introduce messages they have */
07759    int res;
07760 
07761    res = ast_play_and_wait(chan, "vm-youhave");
07762    if (res)
07763       return res;
07764 
07765    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07766       res = ast_play_and_wait(chan, "vm-no");
07767       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07768       return res;
07769    }
07770 
07771    if (vms->newmessages) {
07772       if ((vms->newmessages == 1)) {
07773          res = ast_play_and_wait(chan, "digits/1");
07774          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07775          res = res ? res : ast_play_and_wait(chan, "vm-message");
07776       } else {
07777          res = say_and_wait(chan, vms->newmessages, chan->language);
07778          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07779          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07780       }
07781       if (!res && vms->oldmessages)
07782          res = ast_play_and_wait(chan, "vm-and");
07783    }
07784    if (!res && vms->oldmessages) {
07785       if (vms->oldmessages == 1) {
07786          res = ast_play_and_wait(chan, "digits/1");
07787          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07788          res = res ? res : ast_play_and_wait(chan, "vm-message");
07789       } else {
07790          res = say_and_wait(chan, vms->oldmessages, chan->language);
07791          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07792          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07793       }
07794    }
07795 
07796    return res;
07797 }
07798 
07799 /* GERMAN syntax */
07800 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07801 {
07802    /* Introduce messages they have */
07803    int res;
07804    res = ast_play_and_wait(chan, "vm-youhave");
07805    if (!res) {
07806       if (vms->newmessages) {
07807          if ((vms->newmessages == 1))
07808             res = ast_play_and_wait(chan, "digits/1F");
07809          else
07810             res = say_and_wait(chan, vms->newmessages, chan->language);
07811          if (!res)
07812             res = ast_play_and_wait(chan, "vm-INBOX");
07813          if (vms->oldmessages && !res)
07814             res = ast_play_and_wait(chan, "vm-and");
07815          else if (!res) {
07816             if ((vms->newmessages == 1))
07817                res = ast_play_and_wait(chan, "vm-message");
07818             else
07819                res = ast_play_and_wait(chan, "vm-messages");
07820          }
07821             
07822       }
07823       if (!res && vms->oldmessages) {
07824          if (vms->oldmessages == 1)
07825             res = ast_play_and_wait(chan, "digits/1F");
07826          else
07827             res = say_and_wait(chan, vms->oldmessages, chan->language);
07828          if (!res)
07829             res = ast_play_and_wait(chan, "vm-Old");
07830          if (!res) {
07831             if (vms->oldmessages == 1)
07832                res = ast_play_and_wait(chan, "vm-message");
07833             else
07834                res = ast_play_and_wait(chan, "vm-messages");
07835          }
07836       }
07837       if (!res) {
07838          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07839             res = ast_play_and_wait(chan, "vm-no");
07840             if (!res)
07841                res = ast_play_and_wait(chan, "vm-messages");
07842          }
07843       }
07844    }
07845    return res;
07846 }
07847 
07848 /* SPANISH syntax */
07849 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
07850 {
07851    /* Introduce messages they have */
07852    int res;
07853    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07854       res = ast_play_and_wait(chan, "vm-youhaveno");
07855       if (!res)
07856          res = ast_play_and_wait(chan, "vm-messages");
07857    } else {
07858       res = ast_play_and_wait(chan, "vm-youhave");
07859    }
07860    if (!res) {
07861       if (vms->newmessages) {
07862          if (!res) {
07863             if ((vms->newmessages == 1)) {
07864                res = ast_play_and_wait(chan, "digits/1M");
07865                if (!res)
07866                   res = ast_play_and_wait(chan, "vm-message");
07867                if (!res)
07868                   res = ast_play_and_wait(chan, "vm-INBOXs");
07869             } else {
07870                res = say_and_wait(chan, vms->newmessages, chan->language);
07871                if (!res)
07872                   res = ast_play_and_wait(chan, "vm-messages");
07873                if (!res)
07874                   res = ast_play_and_wait(chan, "vm-INBOX");
07875             }
07876          }
07877          if (vms->oldmessages && !res)
07878             res = ast_play_and_wait(chan, "vm-and");
07879       }
07880       if (vms->oldmessages) {
07881          if (!res) {
07882             if (vms->oldmessages == 1) {
07883                res = ast_play_and_wait(chan, "digits/1M");
07884                if (!res)
07885                   res = ast_play_and_wait(chan, "vm-message");
07886                if (!res)
07887                   res = ast_play_and_wait(chan, "vm-Olds");
07888             } else {
07889                res = say_and_wait(chan, vms->oldmessages, chan->language);
07890                if (!res)
07891                   res = ast_play_and_wait(chan, "vm-messages");
07892                if (!res)
07893                   res = ast_play_and_wait(chan, "vm-Old");
07894             }
07895          }
07896       }
07897    }
07898 return res;
07899 }
07900 
07901 /* BRAZILIAN PORTUGUESE syntax */
07902 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
07903    /* Introduce messages they have */
07904    int res;
07905    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07906       res = ast_play_and_wait(chan, "vm-nomessages");
07907       return res;
07908    } else {
07909       res = ast_play_and_wait(chan, "vm-youhave");
07910    }
07911    if (vms->newmessages) {
07912       if (!res)
07913          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07914       if ((vms->newmessages == 1)) {
07915          if (!res)
07916             res = ast_play_and_wait(chan, "vm-message");
07917          if (!res)
07918             res = ast_play_and_wait(chan, "vm-INBOXs");
07919       } else {
07920          if (!res)
07921             res = ast_play_and_wait(chan, "vm-messages");
07922          if (!res)
07923             res = ast_play_and_wait(chan, "vm-INBOX");
07924       }
07925       if (vms->oldmessages && !res)
07926          res = ast_play_and_wait(chan, "vm-and");
07927    }
07928    if (vms->oldmessages) {
07929       if (!res)
07930          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07931       if (vms->oldmessages == 1) {
07932          if (!res)
07933             res = ast_play_and_wait(chan, "vm-message");
07934          if (!res)
07935             res = ast_play_and_wait(chan, "vm-Olds");
07936       } else {
07937          if (!res)
07938             res = ast_play_and_wait(chan, "vm-messages");
07939          if (!res)
07940             res = ast_play_and_wait(chan, "vm-Old");
07941       }
07942    }
07943    return res;
07944 }
07945 
07946 /* FRENCH syntax */
07947 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
07948 {
07949    /* Introduce messages they have */
07950    int res;
07951    res = ast_play_and_wait(chan, "vm-youhave");
07952    if (!res) {
07953       if (vms->newmessages) {
07954          res = say_and_wait(chan, vms->newmessages, chan->language);
07955          if (!res)
07956             res = ast_play_and_wait(chan, "vm-INBOX");
07957          if (vms->oldmessages && !res)
07958             res = ast_play_and_wait(chan, "vm-and");
07959          else if (!res) {
07960             if ((vms->newmessages == 1))
07961                res = ast_play_and_wait(chan, "vm-message");
07962             else
07963                res = ast_play_and_wait(chan, "vm-messages");
07964          }
07965             
07966       }
07967       if (!res && vms->oldmessages) {
07968          res = say_and_wait(chan, vms->oldmessages, chan->language);
07969          if (!res)
07970             res = ast_play_and_wait(chan, "vm-Old");
07971          if (!res) {
07972             if (vms->oldmessages == 1)
07973                res = ast_play_and_wait(chan, "vm-message");
07974             else
07975                res = ast_play_and_wait(chan, "vm-messages");
07976          }
07977       }
07978       if (!res) {
07979          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07980             res = ast_play_and_wait(chan, "vm-no");
07981             if (!res)
07982                res = ast_play_and_wait(chan, "vm-messages");
07983          }
07984       }
07985    }
07986    return res;
07987 }
07988 
07989 /* DUTCH syntax */
07990 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
07991 {
07992    /* Introduce messages they have */
07993    int res;
07994    res = ast_play_and_wait(chan, "vm-youhave");
07995    if (!res) {
07996       if (vms->newmessages) {
07997          res = say_and_wait(chan, vms->newmessages, chan->language);
07998          if (!res) {
07999             if (vms->newmessages == 1)
08000                res = ast_play_and_wait(chan, "vm-INBOXs");
08001             else
08002                res = ast_play_and_wait(chan, "vm-INBOX");
08003          }
08004          if (vms->oldmessages && !res)
08005             res = ast_play_and_wait(chan, "vm-and");
08006          else if (!res) {
08007             if ((vms->newmessages == 1))
08008                res = ast_play_and_wait(chan, "vm-message");
08009             else
08010                res = ast_play_and_wait(chan, "vm-messages");
08011          }
08012             
08013       }
08014       if (!res && vms->oldmessages) {
08015          res = say_and_wait(chan, vms->oldmessages, chan->language);
08016          if (!res) {
08017             if (vms->oldmessages == 1)
08018                res = ast_play_and_wait(chan, "vm-Olds");
08019             else
08020                res = ast_play_and_wait(chan, "vm-Old");
08021          }
08022          if (!res) {
08023             if (vms->oldmessages == 1)
08024                res = ast_play_and_wait(chan, "vm-message");
08025             else
08026                res = ast_play_and_wait(chan, "vm-messages");
08027          }
08028       }
08029       if (!res) {
08030          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08031             res = ast_play_and_wait(chan, "vm-no");
08032             if (!res)
08033                res = ast_play_and_wait(chan, "vm-messages");
08034          }
08035       }
08036    }
08037    return res;
08038 }
08039 
08040 /* PORTUGUESE syntax */
08041 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
08042 {
08043    /* Introduce messages they have */
08044    int res;
08045    res = ast_play_and_wait(chan, "vm-youhave");
08046    if (!res) {
08047       if (vms->newmessages) {
08048          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
08049          if (!res) {
08050             if ((vms->newmessages == 1)) {
08051                res = ast_play_and_wait(chan, "vm-message");
08052                if (!res)
08053                   res = ast_play_and_wait(chan, "vm-INBOXs");
08054             } else {
08055                res = ast_play_and_wait(chan, "vm-messages");
08056                if (!res)
08057                   res = ast_play_and_wait(chan, "vm-INBOX");
08058             }
08059          }
08060          if (vms->oldmessages && !res)
08061             res = ast_play_and_wait(chan, "vm-and");
08062       }
08063       if (!res && vms->oldmessages) {
08064          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
08065          if (!res) {
08066             if (vms->oldmessages == 1) {
08067                res = ast_play_and_wait(chan, "vm-message");
08068                if (!res)
08069                   res = ast_play_and_wait(chan, "vm-Olds");
08070             } else {
08071                res = ast_play_and_wait(chan, "vm-messages");
08072                if (!res)
08073                   res = ast_play_and_wait(chan, "vm-Old");
08074             }
08075          }
08076       }
08077       if (!res) {
08078          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08079             res = ast_play_and_wait(chan, "vm-no");
08080             if (!res)
08081                res = ast_play_and_wait(chan, "vm-messages");
08082          }
08083       }
08084    }
08085    return res;
08086 }
08087 
08088 
08089 /* CZECH syntax */
08090 /* in czech there must be declension of word new and message
08091  * czech        : english        : czech      : english
08092  * --------------------------------------------------------
08093  * vm-youhave   : you have 
08094  * vm-novou     : one new        : vm-zpravu  : message
08095  * vm-nove      : 2-4 new        : vm-zpravy  : messages
08096  * vm-novych    : 5-infinite new : vm-zprav   : messages
08097  * vm-starou   : one old
08098  * vm-stare     : 2-4 old 
08099  * vm-starych   : 5-infinite old
08100  * jednu        : one   - falling 4. 
08101  * vm-no        : no  ( no messages )
08102  */
08103 
08104 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
08105 {
08106    int res;
08107    res = ast_play_and_wait(chan, "vm-youhave");
08108    if (!res) {
08109       if (vms->newmessages) {
08110          if (vms->newmessages == 1) {
08111             res = ast_play_and_wait(chan, "digits/jednu");
08112          } else {
08113             res = say_and_wait(chan, vms->newmessages, chan->language);
08114          }
08115          if (!res) {
08116             if ((vms->newmessages == 1))
08117                res = ast_play_and_wait(chan, "vm-novou");
08118             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08119                res = ast_play_and_wait(chan, "vm-nove");
08120             if (vms->newmessages > 4)
08121                res = ast_play_and_wait(chan, "vm-novych");
08122          }
08123          if (vms->oldmessages && !res)
08124             res = ast_play_and_wait(chan, "vm-and");
08125          else if (!res) {
08126             if ((vms->newmessages == 1))
08127                res = ast_play_and_wait(chan, "vm-zpravu");
08128             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
08129                res = ast_play_and_wait(chan, "vm-zpravy");
08130             if (vms->newmessages > 4)
08131                res = ast_play_and_wait(chan, "vm-zprav");
08132          }
08133       }
08134       if (!res && vms->oldmessages) {
08135          res = say_and_wait(chan, vms->oldmessages, chan->language);
08136          if (!res) {
08137             if ((vms->oldmessages == 1))
08138                res = ast_play_and_wait(chan, "vm-starou");
08139             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08140                res = ast_play_and_wait(chan, "vm-stare");
08141             if (vms->oldmessages > 4)
08142                res = ast_play_and_wait(chan, "vm-starych");
08143          }
08144          if (!res) {
08145             if ((vms->oldmessages == 1))
08146                res = ast_play_and_wait(chan, "vm-zpravu");
08147             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
08148                res = ast_play_and_wait(chan, "vm-zpravy");
08149             if (vms->oldmessages > 4)
08150                res = ast_play_and_wait(chan, "vm-zprav");
08151          }
08152       }
08153       if (!res) {
08154          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
08155             res = ast_play_and_wait(chan, "vm-no");
08156             if (!res)
08157                res = ast_play_and_wait(chan, "vm-zpravy");
08158          }
08159       }
08160    }
08161    return res;
08162 }
08163 
08164 /* CHINESE (Taiwan) syntax */
08165 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
08166 {
08167    int res;
08168    /* Introduce messages they have */
08169    res = ast_play_and_wait(chan, "vm-you");
08170 
08171    if (!res && vms->newmessages) {
08172       res = ast_play_and_wait(chan, "vm-have");
08173       if (!res)
08174          res = say_and_wait(chan, vms->newmessages, chan->language);
08175       if (!res)
08176          res = ast_play_and_wait(chan, "vm-tong");
08177       if (!res)
08178          res = ast_play_and_wait(chan, "vm-INBOX");
08179       if (vms->oldmessages && !res)
08180          res = ast_play_and_wait(chan, "vm-and");
08181       else if (!res) 
08182          res = ast_play_and_wait(chan, "vm-messages");
08183    }
08184    if (!res && vms->oldmessages) {
08185       res = ast_play_and_wait(chan, "vm-have");
08186       if (!res)
08187          res = say_and_wait(chan, vms->oldmessages, chan->language);
08188       if (!res)
08189          res = ast_play_and_wait(chan, "vm-tong");
08190       if (!res)
08191          res = ast_play_and_wait(chan, "vm-Old");
08192       if (!res)
08193          res = ast_play_and_wait(chan, "vm-messages");
08194    }
08195    if (!res && !vms->oldmessages && !vms->newmessages) {
08196       res = ast_play_and_wait(chan, "vm-haveno");
08197       if (!res)
08198          res = ast_play_and_wait(chan, "vm-messages");
08199    }
08200    return res;
08201 }
08202 
08203 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08204 {
08205    char prefile[256];
08206    
08207    /* Notify the user that the temp greeting is set and give them the option to remove it */
08208    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08209    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08210       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08211       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08212          ast_play_and_wait(chan, "vm-tempgreetactive");
08213       }
08214       DISPOSE(prefile, -1);
08215    }
08216 
08217    /* Play voicemail intro - syntax is different for different languages */
08218    if (0) {
08219    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
08220       return vm_intro_cs(chan, vms);
08221    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
08222       static int deprecation_warning = 0;
08223       if (deprecation_warning++ % 10 == 0) {
08224          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
08225       }
08226       return vm_intro_cs(chan, vms);
08227    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
08228       return vm_intro_de(chan, vms);
08229    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
08230       return vm_intro_es(chan, vms);
08231    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
08232       return vm_intro_fr(chan, vms);
08233    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
08234       return vm_intro_gr(chan, vms);
08235    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
08236       return vm_intro_he(chan, vms);
08237    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
08238       return vm_intro_it(chan, vms);
08239    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
08240       return vm_intro_nl(chan, vms);
08241    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
08242       return vm_intro_no(chan, vms);
08243    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
08244       return vm_intro_pl(chan, vms);
08245    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
08246       return vm_intro_pt_BR(chan, vms);
08247    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
08248       return vm_intro_pt(chan, vms);
08249    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
08250       return vm_intro_multilang(chan, vms, "n");
08251    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
08252       return vm_intro_se(chan, vms);
08253    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
08254       return vm_intro_multilang(chan, vms, "n");
08255    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08256       return vm_intro_zh(chan, vms);
08257    } else {                                             /* Default to ENGLISH */
08258       return vm_intro_en(chan, vms);
08259    }
08260 }
08261 
08262 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08263 {
08264    int res = 0;
08265    /* Play instructions and wait for new command */
08266    while (!res) {
08267       if (vms->starting) {
08268          if (vms->lastmsg > -1) {
08269             if (skipadvanced)
08270                res = ast_play_and_wait(chan, "vm-onefor-full");
08271             else
08272                res = ast_play_and_wait(chan, "vm-onefor");
08273             if (!res)
08274                res = vm_play_folder_name(chan, vms->vmbox);
08275          }
08276          if (!res) {
08277             if (skipadvanced)
08278                res = ast_play_and_wait(chan, "vm-opts-full");
08279             else
08280                res = ast_play_and_wait(chan, "vm-opts");
08281          }
08282       } else {
08283          /* Added for additional help */
08284          if (skipadvanced) {
08285             res = ast_play_and_wait(chan, "vm-onefor-full");
08286             if (!res)
08287                res = vm_play_folder_name(chan, vms->vmbox);
08288             res = ast_play_and_wait(chan, "vm-opts-full");
08289          }
08290          /* Logic:
08291           * If the current message is not the first OR
08292           * if we're listening to the first new message and there are
08293           * also urgent messages, then prompt for navigation to the
08294           * previous message
08295           */
08296          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08297             res = ast_play_and_wait(chan, "vm-prev");
08298          }
08299          if (!res && !skipadvanced)
08300             res = ast_play_and_wait(chan, "vm-advopts");
08301          if (!res)
08302             res = ast_play_and_wait(chan, "vm-repeat");
08303          /* Logic:
08304           * If we're not listening to the last message OR
08305           * we're listening to the last urgent message and there are
08306           * also new non-urgent messages, then prompt for navigation
08307           * to the next message
08308           */
08309          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08310             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08311             res = ast_play_and_wait(chan, "vm-next");
08312          }
08313          if (!res) {
08314             if (!vms->deleted[vms->curmsg])
08315                res = ast_play_and_wait(chan, "vm-delete");
08316             else
08317                res = ast_play_and_wait(chan, "vm-undelete");
08318             if (!res)
08319                res = ast_play_and_wait(chan, "vm-toforward");
08320             if (!res)
08321                res = ast_play_and_wait(chan, "vm-savemessage");
08322          }
08323       }
08324       if (!res) {
08325          res = ast_play_and_wait(chan, "vm-helpexit");
08326       }
08327       if (!res)
08328          res = ast_waitfordigit(chan, 6000);
08329       if (!res) {
08330          vms->repeats++;
08331          if (vms->repeats > 2) {
08332             res = 't';
08333          }
08334       }
08335    }
08336    return res;
08337 }
08338 
08339 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08340 {
08341    int res = 0;
08342    /* Play instructions and wait for new command */
08343    while (!res) {
08344       if (vms->lastmsg > -1) {
08345          res = ast_play_and_wait(chan, "vm-listen");
08346          if (!res)
08347             res = vm_play_folder_name(chan, vms->vmbox);
08348          if (!res)
08349             res = ast_play_and_wait(chan, "press");
08350          if (!res)
08351             res = ast_play_and_wait(chan, "digits/1");
08352       }
08353       if (!res)
08354          res = ast_play_and_wait(chan, "vm-opts");
08355       if (!res) {
08356          vms->starting = 0;
08357          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08358       }
08359    }
08360    return res;
08361 }
08362 
08363 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08364 {
08365    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08366       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08367    } else {             /* Default to ENGLISH */
08368       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08369    }
08370 }
08371 
08372 
08373 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08374 {
08375    int cmd = 0;
08376    int duration = 0;
08377    int tries = 0;
08378    char newpassword[80] = "";
08379    char newpassword2[80] = "";
08380    char prefile[PATH_MAX] = "";
08381    unsigned char buf[256];
08382    int bytes=0;
08383 
08384    if (ast_adsi_available(chan)) {
08385       bytes += adsi_logo(buf + bytes);
08386       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08387       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08388       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08389       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08390       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08391    }
08392 
08393    /* First, have the user change their password 
08394       so they won't get here again */
08395    for (;;) {
08396       newpassword[1] = '\0';
08397       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08398       if (cmd == '#')
08399          newpassword[0] = '\0';
08400       if (cmd < 0 || cmd == 't' || cmd == '#')
08401          return cmd;
08402       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08403       if (cmd < 0 || cmd == 't' || cmd == '#')
08404          return cmd;
08405       cmd = check_password(vmu, newpassword); /* perform password validation */
08406       if (cmd != 0) {
08407          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08408          cmd = ast_play_and_wait(chan, vm_invalid_password);
08409       } else {
08410          newpassword2[1] = '\0';
08411          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08412          if (cmd == '#')
08413             newpassword2[0] = '\0';
08414          if (cmd < 0 || cmd == 't' || cmd == '#')
08415             return cmd;
08416          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08417          if (cmd < 0 || cmd == 't' || cmd == '#')
08418             return cmd;
08419          if (!strcmp(newpassword, newpassword2))
08420             break;
08421          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08422          cmd = ast_play_and_wait(chan, vm_mismatch);
08423       }
08424       if (++tries == 3)
08425          return -1;
08426       if (cmd != 0) {
08427          cmd = ast_play_and_wait(chan, vm_pls_try_again);
08428       }
08429    }
08430    if (pwdchange & PWDCHANGE_INTERNAL)
08431       vm_change_password(vmu, newpassword);
08432    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08433       vm_change_password_shell(vmu, newpassword);
08434 
08435    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08436    cmd = ast_play_and_wait(chan, vm_passchanged);
08437 
08438    /* If forcename is set, have the user record their name */  
08439    if (ast_test_flag(vmu, VM_FORCENAME)) {
08440       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08441       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08442          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08443          if (cmd < 0 || cmd == 't' || cmd == '#')
08444             return cmd;
08445       }
08446    }
08447 
08448    /* If forcegreetings is set, have the user record their greetings */
08449    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08450       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08451       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08452          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08453          if (cmd < 0 || cmd == 't' || cmd == '#')
08454             return cmd;
08455       }
08456 
08457       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08458       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08459          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08460          if (cmd < 0 || cmd == 't' || cmd == '#')
08461             return cmd;
08462       }
08463    }
08464 
08465    return cmd;
08466 }
08467 
08468 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08469 {
08470    int cmd = 0;
08471    int retries = 0;
08472    int duration = 0;
08473    char newpassword[80] = "";
08474    char newpassword2[80] = "";
08475    char prefile[PATH_MAX] = "";
08476    unsigned char buf[256];
08477    int bytes=0;
08478 
08479    if (ast_adsi_available(chan)) {
08480       bytes += adsi_logo(buf + bytes);
08481       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08482       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08483       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08484       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08485       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08486    }
08487    while ((cmd >= 0) && (cmd != 't')) {
08488       if (cmd)
08489          retries = 0;
08490       switch (cmd) {
08491       case '1': /* Record your unavailable message */
08492          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08493          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08494          break;
08495       case '2':  /* Record your busy message */
08496          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08497          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08498          break;
08499       case '3': /* Record greeting */
08500          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08501          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08502          break;
08503       case '4':  /* manage the temporary greeting */
08504          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08505          break;
08506       case '5': /* change password */
08507          if (vmu->password[0] == '-') {
08508             cmd = ast_play_and_wait(chan, "vm-no");
08509             break;
08510          }
08511          newpassword[1] = '\0';
08512          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08513          if (cmd == '#')
08514             newpassword[0] = '\0';
08515          else {
08516             if (cmd < 0)
08517                break;
08518             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08519                break;
08520             }
08521          }
08522          cmd = check_password(vmu, newpassword); /* perform password validation */
08523          if (cmd != 0) {
08524             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08525             cmd = ast_play_and_wait(chan, vm_invalid_password);
08526             if (!cmd) {
08527                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08528             }
08529             break;
08530          }
08531          newpassword2[1] = '\0';
08532          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08533          if (cmd == '#')
08534             newpassword2[0] = '\0';
08535          else {
08536             if (cmd < 0)
08537                break;
08538 
08539             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08540                break;
08541             }
08542          }
08543          if (strcmp(newpassword, newpassword2)) {
08544             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08545             cmd = ast_play_and_wait(chan, vm_mismatch);
08546             if (!cmd) {
08547                cmd = ast_play_and_wait(chan, vm_pls_try_again);
08548             }
08549             break;
08550          }
08551          if (pwdchange & PWDCHANGE_INTERNAL)
08552             vm_change_password(vmu, newpassword);
08553          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08554             vm_change_password_shell(vmu, newpassword);
08555 
08556          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08557          cmd = ast_play_and_wait(chan, vm_passchanged);
08558          break;
08559       case '*': 
08560          cmd = 't';
08561          break;
08562       default: 
08563          cmd = 0;
08564          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08565          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08566          if (ast_fileexists(prefile, NULL, NULL)) {
08567             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08568          }
08569          DISPOSE(prefile, -1);
08570          if (!cmd) {
08571             cmd = ast_play_and_wait(chan, "vm-options");
08572          }
08573          if (!cmd) {
08574             cmd = ast_waitfordigit(chan,6000);
08575          }
08576          if (!cmd) {
08577             retries++;
08578          }
08579          if (retries > 3) {
08580             cmd = 't';
08581          }
08582       }
08583    }
08584    if (cmd == 't')
08585       cmd = 0;
08586    return cmd;
08587 }
08588 
08589 /*!
08590  * \brief The handler for 'record a temporary greeting'. 
08591  * \param chan
08592  * \param vmu
08593  * \param vms
08594  * \param fmtc
08595  * \param record_gain
08596  *
08597  * This is option 4 from the mailbox options menu.
08598  * This function manages the following promptings:
08599  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08600  * 2: remove (delete) the temporary greeting.
08601  * *: return to the main menu.
08602  *
08603  * \return zero on success, -1 on error.
08604  */
08605 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08606 {
08607    int cmd = 0;
08608    int retries = 0;
08609    int duration = 0;
08610    char prefile[PATH_MAX] = "";
08611    unsigned char buf[256];
08612    int bytes = 0;
08613 
08614    if (ast_adsi_available(chan)) {
08615       bytes += adsi_logo(buf + bytes);
08616       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08617       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08618       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08619       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08620       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08621    }
08622 
08623    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08624    while ((cmd >= 0) && (cmd != 't')) {
08625       if (cmd)
08626          retries = 0;
08627       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08628       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08629          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08630          cmd = 't';  
08631       } else {
08632          switch (cmd) {
08633          case '1':
08634             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08635             break;
08636          case '2':
08637             DELETE(prefile, -1, prefile, vmu);
08638             ast_play_and_wait(chan, "vm-tempremoved");
08639             cmd = 't';  
08640             break;
08641          case '*': 
08642             cmd = 't';
08643             break;
08644          default:
08645             cmd = ast_play_and_wait(chan,
08646                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08647                   "vm-tempgreeting2" : "vm-tempgreeting");
08648             if (!cmd)
08649                cmd = ast_waitfordigit(chan,6000);
08650             if (!cmd)
08651                retries++;
08652             if (retries > 3)
08653                cmd = 't';
08654          }
08655       }
08656       DISPOSE(prefile, -1);
08657    }
08658    if (cmd == 't')
08659       cmd = 0;
08660    return cmd;
08661 }
08662 
08663 /*!
08664  * \brief Greek syntax for 'You have N messages' greeting.
08665  * \param chan
08666  * \param vms
08667  * \param vmu
08668  *
08669  * \return zero on success, -1 on error.
08670  */   
08671 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08672 {
08673    int cmd=0;
08674 
08675    if (vms->lastmsg > -1) {
08676       cmd = play_message(chan, vmu, vms);
08677    } else {
08678       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08679       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08680          if (!cmd) {
08681             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08682             cmd = ast_play_and_wait(chan, vms->fn);
08683          }
08684          if (!cmd)
08685             cmd = ast_play_and_wait(chan, "vm-messages");
08686       } else {
08687          if (!cmd)
08688             cmd = ast_play_and_wait(chan, "vm-messages");
08689          if (!cmd) {
08690             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08691             cmd = ast_play_and_wait(chan, vms->fn);
08692          }
08693       }
08694    } 
08695    return cmd;
08696 }
08697 
08698 /* Hebrew Syntax */
08699 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08700 {
08701    int cmd = 0;
08702 
08703    if (vms->lastmsg > -1) {
08704       cmd = play_message(chan, vmu, vms);
08705    } else {
08706       if (!strcasecmp(vms->fn, "INBOX")) {
08707          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08708       } else {
08709          cmd = ast_play_and_wait(chan, "vm-nomessages");
08710       }
08711    }
08712    return cmd;
08713 }
08714 
08715 /*! 
08716  * \brief Default English syntax for 'You have N messages' greeting.
08717  * \param chan
08718  * \param vms
08719  * \param vmu
08720  *
08721  * \return zero on success, -1 on error.
08722  */
08723 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08724 {
08725    int cmd=0;
08726 
08727    if (vms->lastmsg > -1) {
08728       cmd = play_message(chan, vmu, vms);
08729    } else {
08730       cmd = ast_play_and_wait(chan, "vm-youhave");
08731       if (!cmd) 
08732          cmd = ast_play_and_wait(chan, "vm-no");
08733       if (!cmd) {
08734          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08735          cmd = ast_play_and_wait(chan, vms->fn);
08736       }
08737       if (!cmd)
08738          cmd = ast_play_and_wait(chan, "vm-messages");
08739    }
08740    return cmd;
08741 }
08742 
08743 /*! 
08744  *\brief Italian syntax for 'You have N messages' greeting.
08745  * \param chan
08746  * \param vms
08747  * \param vmu
08748  *
08749  * \return zero on success, -1 on error.
08750  */
08751 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08752 {
08753    int cmd=0;
08754 
08755    if (vms->lastmsg > -1) {
08756       cmd = play_message(chan, vmu, vms);
08757    } else {
08758       cmd = ast_play_and_wait(chan, "vm-no");
08759       if (!cmd)
08760          cmd = ast_play_and_wait(chan, "vm-message");
08761       if (!cmd) {
08762          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08763          cmd = ast_play_and_wait(chan, vms->fn);
08764       }
08765    }
08766    return cmd;
08767 }
08768 
08769 /*! 
08770  * \brief Spanish syntax for 'You have N messages' greeting.
08771  * \param chan
08772  * \param vms
08773  * \param vmu
08774  *
08775  * \return zero on success, -1 on error.
08776  */
08777 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08778 {
08779    int cmd=0;
08780 
08781    if (vms->lastmsg > -1) {
08782       cmd = play_message(chan, vmu, vms);
08783    } else {
08784       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08785       if (!cmd)
08786          cmd = ast_play_and_wait(chan, "vm-messages");
08787       if (!cmd) {
08788          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08789          cmd = ast_play_and_wait(chan, vms->fn);
08790       }
08791    }
08792    return cmd;
08793 }
08794 
08795 /*! 
08796  * \brief Portuguese syntax for 'You have N messages' greeting.
08797  * \param chan
08798  * \param vms
08799  * \param vmu
08800  *
08801  * \return zero on success, -1 on error.
08802  */
08803 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08804 {
08805    int cmd=0;
08806 
08807    if (vms->lastmsg > -1) {
08808       cmd = play_message(chan, vmu, vms);
08809    } else {
08810       cmd = ast_play_and_wait(chan, "vm-no");
08811       if (!cmd) {
08812          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08813          cmd = ast_play_and_wait(chan, vms->fn);
08814       }
08815       if (!cmd)
08816          cmd = ast_play_and_wait(chan, "vm-messages");
08817    }
08818    return cmd;
08819 }
08820 
08821 /*! 
08822  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08823  * \param chan
08824  * \param vms
08825  * \param vmu
08826  *
08827  * \return zero on success, -1 on error.
08828  */
08829 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08830 {
08831    int cmd=0;
08832 
08833    if (vms->lastmsg > -1) {
08834       cmd = play_message(chan, vmu, vms);
08835    } else {
08836       cmd = ast_play_and_wait(chan, "vm-you");
08837       if (!cmd) 
08838          cmd = ast_play_and_wait(chan, "vm-haveno");
08839       if (!cmd)
08840          cmd = ast_play_and_wait(chan, "vm-messages");
08841       if (!cmd) {
08842          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08843          cmd = ast_play_and_wait(chan, vms->fn);
08844       }
08845    }
08846    return cmd;
08847 }
08848 
08849 /*!
08850  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
08851  * \param chan The channel for the current user. We read the language property from this.
08852  * \param vms passed into the language-specific vm_browse_messages function.
08853  * \param vmu passed into the language-specific vm_browse_messages function.
08854  * 
08855  * The method to be invoked is determined by the value of language code property in the user's channel.
08856  * The default (when unable to match) is to use english.
08857  *
08858  * \return zero on success, -1 on error.
08859  */
08860 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08861 {
08862    if (!strncasecmp(chan->language, "es", 2)) {         /* SPANISH */
08863       return vm_browse_messages_es(chan, vms, vmu);
08864    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK */
08865       return vm_browse_messages_gr(chan, vms, vmu);
08866    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
08867       return vm_browse_messages_he(chan, vms, vmu);
08868    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN */
08869       return vm_browse_messages_it(chan, vms, vmu);
08870    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE */
08871       return vm_browse_messages_pt(chan, vms, vmu);
08872    } else if (!strncasecmp(chan->language, "zh", 2)) {
08873       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
08874    } else {                                             /* Default to English syntax */
08875       return vm_browse_messages_en(chan, vms, vmu);
08876    }
08877 }
08878 
08879 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
08880          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
08881          int skipuser, int max_logins, int silent)
08882 {
08883    int useadsi=0, valid=0, logretries=0;
08884    char password[AST_MAX_EXTENSION]="", *passptr;
08885    struct ast_vm_user vmus, *vmu = NULL;
08886 
08887    /* If ADSI is supported, setup login screen */
08888    adsi_begin(chan, &useadsi);
08889    if (!skipuser && useadsi)
08890       adsi_login(chan);
08891    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
08892       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
08893       return -1;
08894    }
08895    
08896    /* Authenticate them and get their mailbox/password */
08897    
08898    while (!valid && (logretries < max_logins)) {
08899       /* Prompt for, and read in the username */
08900       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
08901          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
08902          return -1;
08903       }
08904       if (ast_strlen_zero(mailbox)) {
08905          if (chan->cid.cid_num) {
08906             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
08907          } else {
08908             ast_verb(3,"Username not entered\n");  
08909             return -1;
08910          }
08911       }
08912       if (useadsi)
08913          adsi_password(chan);
08914 
08915       if (!ast_strlen_zero(prefix)) {
08916          char fullusername[80] = "";
08917          ast_copy_string(fullusername, prefix, sizeof(fullusername));
08918          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
08919          ast_copy_string(mailbox, fullusername, mailbox_size);
08920       }
08921 
08922       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
08923       vmu = find_user(&vmus, context, mailbox);
08924       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
08925          /* saved password is blank, so don't bother asking */
08926          password[0] = '\0';
08927       } else {
08928          if (ast_streamfile(chan, vm_password, chan->language)) {
08929             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
08930             return -1;
08931          }
08932          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
08933             ast_log(AST_LOG_WARNING, "Unable to read password\n");
08934             return -1;
08935          }
08936       }
08937 
08938       if (vmu) {
08939          passptr = vmu->password;
08940          if (passptr[0] == '-') passptr++;
08941       }
08942       if (vmu && !strcmp(passptr, password))
08943          valid++;
08944       else {
08945          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
08946          if (!ast_strlen_zero(prefix))
08947             mailbox[0] = '\0';
08948       }
08949       logretries++;
08950       if (!valid) {
08951          if (skipuser || logretries >= max_logins) {
08952             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
08953                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
08954                return -1;
08955             }
08956          } else {
08957             if (useadsi)
08958                adsi_login(chan);
08959             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
08960                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
08961                return -1;
08962             }
08963          }
08964          if (ast_waitstream(chan, "")) /* Channel is hung up */
08965             return -1;
08966       }
08967    }
08968    if (!valid && (logretries >= max_logins)) {
08969       ast_stopstream(chan);
08970       ast_play_and_wait(chan, "vm-goodbye");
08971       return -1;
08972    }
08973    if (vmu && !skipuser) {
08974       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
08975    }
08976    return 0;
08977 }
08978 
08979 static int vm_execmain(struct ast_channel *chan, void *data)
08980 {
08981    /* XXX This is, admittedly, some pretty horrendous code.  For some
08982       reason it just seemed a lot easier to do with GOTO's.  I feel
08983       like I'm back in my GWBASIC days. XXX */
08984    int res=-1;
08985    int cmd=0;
08986    int valid = 0;
08987    char prefixstr[80] ="";
08988    char ext_context[256]="";
08989    int box;
08990    int useadsi = 0;
08991    int skipuser = 0;
08992    struct vm_state vms;
08993    struct ast_vm_user *vmu = NULL, vmus;
08994    char *context=NULL;
08995    int silentexit = 0;
08996    struct ast_flags flags = { 0 };
08997    signed char record_gain = 0;
08998    int play_auto = 0;
08999    int play_folder = 0;
09000    int in_urgent = 0;
09001 #ifdef IMAP_STORAGE
09002    int deleted = 0;
09003 #endif
09004 
09005    /* Add the vm_state to the active list and keep it active */
09006    memset(&vms, 0, sizeof(vms));
09007 
09008    vms.lastmsg = -1;
09009 
09010    memset(&vmus, 0, sizeof(vmus));
09011 
09012    if (chan->_state != AST_STATE_UP) {
09013       ast_debug(1, "Before ast_answer\n");
09014       ast_answer(chan);
09015    }
09016 
09017    if (!ast_strlen_zero(data)) {
09018       char *opts[OPT_ARG_ARRAY_SIZE];
09019       char *parse;
09020       AST_DECLARE_APP_ARGS(args,
09021          AST_APP_ARG(argv0);
09022          AST_APP_ARG(argv1);
09023       );
09024 
09025       parse = ast_strdupa(data);
09026 
09027       AST_STANDARD_APP_ARGS(args, parse);
09028 
09029       if (args.argc == 2) {
09030          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09031             return -1;
09032          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09033             int gain;
09034             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
09035                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09036                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09037                   return -1;
09038                } else {
09039                   record_gain = (signed char) gain;
09040                }
09041             } else {
09042                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
09043             }
09044          }
09045          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
09046             play_auto = 1;
09047             if (opts[OPT_ARG_PLAYFOLDER]) {
09048                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
09049                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
09050                }
09051             } else {
09052                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
09053             }  
09054             if ( play_folder > 9 || play_folder < 0) {
09055                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
09056                play_folder = 0;
09057             }
09058          }
09059       } else {
09060          /* old style options parsing */
09061          while (*(args.argv0)) {
09062             if (*(args.argv0) == 's')
09063                ast_set_flag(&flags, OPT_SILENT);
09064             else if (*(args.argv0) == 'p')
09065                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
09066             else 
09067                break;
09068             (args.argv0)++;
09069          }
09070 
09071       }
09072 
09073       valid = ast_test_flag(&flags, OPT_SILENT);
09074 
09075       if ((context = strchr(args.argv0, '@')))
09076          *context++ = '\0';
09077 
09078       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
09079          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
09080       else
09081          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
09082 
09083       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
09084          skipuser++;
09085       else
09086          valid = 0;
09087    }
09088 
09089    if (!valid)
09090       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
09091 
09092    ast_debug(1, "After vm_authenticate\n");
09093    if (!res) {
09094       valid = 1;
09095       if (!skipuser)
09096          vmu = &vmus;
09097    } else {
09098       res = 0;
09099    }
09100 
09101    /* If ADSI is supported, setup login screen */
09102    adsi_begin(chan, &useadsi);
09103 
09104    if (!valid) {
09105       goto out;
09106    }
09107 
09108 #ifdef IMAP_STORAGE
09109    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
09110    pthread_setspecific(ts_vmstate.key, &vms);
09111 
09112    vms.interactive = 1;
09113    vms.updated = 1;
09114    if (vmu)
09115       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
09116    vmstate_insert(&vms);
09117    init_vm_state(&vms);
09118 #endif
09119    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09120       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
09121       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09122       return -1;
09123    }
09124    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
09125       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
09126       cmd = ast_play_and_wait(chan, "an-error-has-occured");
09127       return -1;
09128    }
09129    
09130    /* Set language from config to override channel language */
09131    if (!ast_strlen_zero(vmu->language))
09132       ast_string_field_set(chan, language, vmu->language);
09133 
09134    /* Retrieve urgent, old and new message counts */
09135    ast_debug(1, "Before open_mailbox\n");
09136    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09137    if (res == ERROR_LOCK_PATH)
09138       goto out;
09139    vms.oldmessages = vms.lastmsg + 1;
09140    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
09141    /* check INBOX */
09142    res = open_mailbox(&vms, vmu, NEW_FOLDER);
09143    if (res == ERROR_LOCK_PATH)
09144       goto out;
09145    vms.newmessages = vms.lastmsg + 1;
09146    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
09147    /* Start in Urgent */
09148    in_urgent = 1;
09149    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
09150    if (res == ERROR_LOCK_PATH)
09151       goto out;
09152    vms.urgentmessages = vms.lastmsg + 1;
09153    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
09154 
09155    /* Select proper mailbox FIRST!! */
09156    if (play_auto) {
09157       if (vms.urgentmessages) {
09158          in_urgent = 1;
09159          res = open_mailbox(&vms, vmu, 11);
09160       } else {
09161          in_urgent = 0;
09162          res = open_mailbox(&vms, vmu, play_folder);
09163       }
09164       if (res == ERROR_LOCK_PATH)
09165          goto out;
09166 
09167       /* If there are no new messages, inform the user and hangup */
09168       if (vms.lastmsg == -1) {
09169          in_urgent = 0;
09170          cmd = vm_browse_messages(chan, &vms, vmu);
09171          res = 0;
09172          goto out;
09173       }
09174    } else {
09175       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
09176          /* If we only have old messages start here */
09177          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
09178          in_urgent = 0;
09179          play_folder = 1;
09180          if (res == ERROR_LOCK_PATH)
09181             goto out;
09182       } else if (!vms.urgentmessages && vms.newmessages) {
09183          /* If we have new messages but none are urgent */
09184          in_urgent = 0;
09185          res = open_mailbox(&vms, vmu, NEW_FOLDER);
09186          if (res == ERROR_LOCK_PATH)
09187             goto out;
09188       }
09189    }
09190 
09191    if (useadsi)
09192       adsi_status(chan, &vms);
09193    res = 0;
09194 
09195    /* Check to see if this is a new user */
09196    if (!strcasecmp(vmu->mailbox, vmu->password) && 
09197       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
09198       if (ast_play_and_wait(chan, "vm-newuser") == -1)
09199          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09200       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09201       if ((cmd == 't') || (cmd == '#')) {
09202          /* Timeout */
09203          res = 0;
09204          goto out;
09205       } else if (cmd < 0) {
09206          /* Hangup */
09207          res = -1;
09208          goto out;
09209       }
09210    }
09211 #ifdef IMAP_STORAGE
09212       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09213       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09214          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09215          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09216       }
09217       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09218       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09219          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09220          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09221       }
09222 #endif
09223    if (play_auto) {
09224       cmd = '1';
09225    } else {
09226       cmd = vm_intro(chan, vmu, &vms);
09227    }
09228 
09229    vms.repeats = 0;
09230    vms.starting = 1;
09231    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09232       /* Run main menu */
09233       switch (cmd) {
09234       case '1': /* First message */
09235          vms.curmsg = 0;
09236          /* Fall through */
09237       case '5': /* Play current message */
09238          cmd = vm_browse_messages(chan, &vms, vmu);
09239          break;
09240       case '2': /* Change folders */
09241          if (useadsi)
09242             adsi_folders(chan, 0, "Change to folder...");
09243          cmd = get_folder2(chan, "vm-changeto", 0);
09244          if (cmd == '#') {
09245             cmd = 0;
09246          } else if (cmd > 0) {
09247             cmd = cmd - '0';
09248             res = close_mailbox(&vms, vmu);
09249             if (res == ERROR_LOCK_PATH)
09250                goto out;
09251             /* If folder is not urgent, set in_urgent to zero! */
09252             if (cmd != 11) in_urgent = 0;
09253             res = open_mailbox(&vms, vmu, cmd);
09254             if (res == ERROR_LOCK_PATH)
09255                goto out;
09256             play_folder = cmd;
09257             cmd = 0;
09258          }
09259          if (useadsi)
09260             adsi_status2(chan, &vms);
09261             
09262          if (!cmd)
09263             cmd = vm_play_folder_name(chan, vms.vmbox);
09264 
09265          vms.starting = 1;
09266          break;
09267       case '3': /* Advanced options */
09268          cmd = 0;
09269          vms.repeats = 0;
09270          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09271             switch (cmd) {
09272             case '1': /* Reply */
09273                if (vms.lastmsg > -1 && !vms.starting) {
09274                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09275                   if (cmd == ERROR_LOCK_PATH) {
09276                      res = cmd;
09277                      goto out;
09278                   }
09279                } else
09280                   cmd = ast_play_and_wait(chan, "vm-sorry");
09281                cmd = 't';
09282                break;
09283             case '2': /* Callback */
09284                if (!vms.starting)
09285                   ast_verb(3, "Callback Requested\n");
09286                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09287                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09288                   if (cmd == 9) {
09289                      silentexit = 1;
09290                      goto out;
09291                   } else if (cmd == ERROR_LOCK_PATH) {
09292                      res = cmd;
09293                      goto out;
09294                   }
09295                } else 
09296                   cmd = ast_play_and_wait(chan, "vm-sorry");
09297                cmd = 't';
09298                break;
09299             case '3': /* Envelope */
09300                if (vms.lastmsg > -1 && !vms.starting) {
09301                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09302                   if (cmd == ERROR_LOCK_PATH) {
09303                      res = cmd;
09304                      goto out;
09305                   }
09306                } else
09307                   cmd = ast_play_and_wait(chan, "vm-sorry");
09308                cmd = 't';
09309                break;
09310             case '4': /* Dialout */
09311                if (!ast_strlen_zero(vmu->dialout)) {
09312                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09313                   if (cmd == 9) {
09314                      silentexit = 1;
09315                      goto out;
09316                   }
09317                } else 
09318                   cmd = ast_play_and_wait(chan, "vm-sorry");
09319                cmd = 't';
09320                break;
09321 
09322             case '5': /* Leave VoiceMail */
09323                if (ast_test_flag(vmu, VM_SVMAIL)) {
09324                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09325                   if (cmd == ERROR_LOCK_PATH) {
09326                      res = cmd;
09327                      ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
09328                      goto out;
09329                   }
09330                } else
09331                   cmd = ast_play_and_wait(chan,"vm-sorry");
09332                cmd='t';
09333                break;
09334                
09335             case '*': /* Return to main menu */
09336                cmd = 't';
09337                break;
09338 
09339             default:
09340                cmd = 0;
09341                if (!vms.starting) {
09342                   cmd = ast_play_and_wait(chan, "vm-toreply");
09343                }
09344                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09345                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09346                }
09347                if (!cmd && !vms.starting) {
09348                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09349                }
09350                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09351                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09352                }
09353                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09354                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09355                if (!cmd)
09356                   cmd = ast_play_and_wait(chan, "vm-starmain");
09357                if (!cmd)
09358                   cmd = ast_waitfordigit(chan,6000);
09359                if (!cmd)
09360                   vms.repeats++;
09361                if (vms.repeats > 3)
09362                   cmd = 't';
09363             }
09364          }
09365          if (cmd == 't') {
09366             cmd = 0;
09367             vms.repeats = 0;
09368          }
09369          break;
09370       case '4': /* Go to the previous message */
09371          if (vms.curmsg > 0) {
09372             vms.curmsg--;
09373             cmd = play_message(chan, vmu, &vms);
09374          } else {
09375             /* Check if we were listening to new
09376                messages.  If so, go to Urgent messages
09377                instead of saying "no more messages"
09378             */
09379             if (in_urgent == 0 && vms.urgentmessages > 0) {
09380                /* Check for Urgent messages */
09381                in_urgent = 1;
09382                res = close_mailbox(&vms, vmu);
09383                if (res == ERROR_LOCK_PATH)
09384                   goto out;
09385                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09386                if (res == ERROR_LOCK_PATH)
09387                   goto out;
09388                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09389                vms.curmsg = vms.lastmsg;
09390                if (vms.lastmsg < 0)
09391                   cmd = ast_play_and_wait(chan, "vm-nomore");
09392             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09393                vms.curmsg = vms.lastmsg;
09394                cmd = play_message(chan, vmu, &vms);
09395             } else {
09396                cmd = ast_play_and_wait(chan, "vm-nomore");
09397             }
09398          }
09399          break;
09400       case '6': /* Go to the next message */
09401          if (vms.curmsg < vms.lastmsg) {
09402             vms.curmsg++;
09403             cmd = play_message(chan, vmu, &vms);
09404          } else {
09405             if (in_urgent && vms.newmessages > 0) {
09406                /* Check if we were listening to urgent
09407                 * messages.  If so, go to regular new messages
09408                 * instead of saying "no more messages"
09409                 */
09410                in_urgent = 0;
09411                res = close_mailbox(&vms, vmu);
09412                if (res == ERROR_LOCK_PATH)
09413                   goto out;
09414                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09415                if (res == ERROR_LOCK_PATH)
09416                   goto out;
09417                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09418                vms.curmsg = -1;
09419                if (vms.lastmsg < 0) {
09420                   cmd = ast_play_and_wait(chan, "vm-nomore");
09421                }
09422             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09423                vms.curmsg = 0;
09424                cmd = play_message(chan, vmu, &vms);
09425             } else {
09426                cmd = ast_play_and_wait(chan, "vm-nomore");
09427             }
09428          }
09429          break;
09430       case '7': /* Delete the current message */
09431          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09432             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09433             if (useadsi)
09434                adsi_delete(chan, &vms);
09435             if (vms.deleted[vms.curmsg]) {
09436                if (play_folder == 0) {
09437                   if (in_urgent) {
09438                      vms.urgentmessages--;
09439                   } else {
09440                      vms.newmessages--;
09441                   }
09442                }
09443                else if (play_folder == 1)
09444                   vms.oldmessages--;
09445                cmd = ast_play_and_wait(chan, "vm-deleted");
09446             } else {
09447                if (play_folder == 0) {
09448                   if (in_urgent) {
09449                      vms.urgentmessages++;
09450                   } else {
09451                      vms.newmessages++;
09452                   }
09453                }
09454                else if (play_folder == 1)
09455                   vms.oldmessages++;
09456                cmd = ast_play_and_wait(chan, "vm-undeleted");
09457             }
09458             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09459                if (vms.curmsg < vms.lastmsg) {
09460                   vms.curmsg++;
09461                   cmd = play_message(chan, vmu, &vms);
09462                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09463                   vms.curmsg = 0;
09464                   cmd = play_message(chan, vmu, &vms);
09465                } else {
09466                   /* Check if we were listening to urgent
09467                      messages.  If so, go to regular new messages
09468                      instead of saying "no more messages"
09469                   */
09470                   if (in_urgent == 1) {
09471                      /* Check for new messages */
09472                      in_urgent = 0;
09473                      res = close_mailbox(&vms, vmu);
09474                      if (res == ERROR_LOCK_PATH)
09475                         goto out;
09476                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09477                      if (res == ERROR_LOCK_PATH)
09478                         goto out;
09479                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09480                      vms.curmsg = -1;
09481                      if (vms.lastmsg < 0)
09482                         cmd = ast_play_and_wait(chan, "vm-nomore");
09483                   } else {
09484                      cmd = ast_play_and_wait(chan, "vm-nomore");
09485                   }
09486                }
09487             }
09488          } else /* Delete not valid if we haven't selected a message */
09489             cmd = 0;
09490 #ifdef IMAP_STORAGE
09491          deleted = 1;
09492 #endif
09493          break;
09494    
09495       case '8': /* Forward the current messgae */
09496          if (vms.lastmsg > -1) {
09497             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09498             if (cmd == ERROR_LOCK_PATH) {
09499                res = cmd;
09500                goto out;
09501             }
09502          } else {
09503             /* Check if we were listening to urgent
09504                messages.  If so, go to regular new messages
09505                instead of saying "no more messages"
09506             */
09507             if (in_urgent == 1 && vms.newmessages > 0) {
09508                /* Check for new messages */
09509                in_urgent = 0;
09510                res = close_mailbox(&vms, vmu);
09511                if (res == ERROR_LOCK_PATH)
09512                   goto out;
09513                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09514                if (res == ERROR_LOCK_PATH)
09515                   goto out;
09516                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09517                vms.curmsg = -1;
09518                if (vms.lastmsg < 0)
09519                   cmd = ast_play_and_wait(chan, "vm-nomore");
09520             } else {
09521                cmd = ast_play_and_wait(chan, "vm-nomore");
09522             }
09523          }
09524          break;
09525       case '9': /* Save message to folder */
09526          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09527             /* No message selected */
09528             cmd = 0;
09529             break;
09530          }
09531          if (useadsi)
09532             adsi_folders(chan, 1, "Save to folder...");
09533          cmd = get_folder2(chan, "vm-savefolder", 1);
09534          box = 0; /* Shut up compiler */
09535          if (cmd == '#') {
09536             cmd = 0;
09537             break;
09538          } else if (cmd > 0) {
09539             box = cmd = cmd - '0';
09540             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09541             if (cmd == ERROR_LOCK_PATH) {
09542                res = cmd;
09543                goto out;
09544 #ifndef IMAP_STORAGE
09545             } else if (!cmd) {
09546                vms.deleted[vms.curmsg] = 1;
09547 #endif
09548             } else {
09549                vms.deleted[vms.curmsg] = 0;
09550                vms.heard[vms.curmsg] = 0;
09551             }
09552          }
09553          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09554          if (useadsi)
09555             adsi_message(chan, &vms);
09556          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09557          if (!cmd) {
09558             cmd = ast_play_and_wait(chan, "vm-message");
09559             if (!cmd) 
09560                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09561             if (!cmd)
09562                cmd = ast_play_and_wait(chan, "vm-savedto");
09563             if (!cmd)
09564                cmd = vm_play_folder_name(chan, vms.fn);
09565          } else {
09566             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09567          }
09568          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09569             if (vms.curmsg < vms.lastmsg) {
09570                vms.curmsg++;
09571                cmd = play_message(chan, vmu, &vms);
09572             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09573                vms.curmsg = 0;
09574                cmd = play_message(chan, vmu, &vms);
09575             } else {
09576                /* Check if we were listening to urgent
09577                   messages.  If so, go to regular new messages
09578                   instead of saying "no more messages"
09579                */
09580                if (in_urgent == 1 && vms.newmessages > 0) {
09581                   /* Check for new messages */
09582                   in_urgent = 0;
09583                   res = close_mailbox(&vms, vmu);
09584                   if (res == ERROR_LOCK_PATH)
09585                      goto out;
09586                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09587                   if (res == ERROR_LOCK_PATH)
09588                      goto out;
09589                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09590                   vms.curmsg = -1;
09591                   if (vms.lastmsg < 0)
09592                      cmd = ast_play_and_wait(chan, "vm-nomore");
09593                } else {
09594                   cmd = ast_play_and_wait(chan, "vm-nomore");
09595                }
09596             }
09597          }
09598          break;
09599       case '*': /* Help */
09600          if (!vms.starting) {
09601             cmd = ast_play_and_wait(chan, "vm-onefor");
09602             if (!strncasecmp(chan->language, "he", 2)) {
09603                cmd = ast_play_and_wait(chan, "vm-for");
09604             }
09605             if (!cmd)
09606                cmd = vm_play_folder_name(chan, vms.vmbox);
09607             if (!cmd)
09608                cmd = ast_play_and_wait(chan, "vm-opts");
09609             if (!cmd)
09610                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09611          } else
09612             cmd = 0;
09613          break;
09614       case '0': /* Mailbox options */
09615          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09616          if (useadsi)
09617             adsi_status(chan, &vms);
09618          break;
09619       default: /* Nothing */
09620          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09621          break;
09622       }
09623    }
09624    if ((cmd == 't') || (cmd == '#')) {
09625       /* Timeout */
09626       res = 0;
09627    } else {
09628       /* Hangup */
09629       res = -1;
09630    }
09631 
09632 out:
09633    if (res > -1) {
09634       ast_stopstream(chan);
09635       adsi_goodbye(chan);
09636       if (valid) {
09637          if (silentexit)
09638             res = ast_play_and_wait(chan, "vm-dialout");
09639          else 
09640             res = ast_play_and_wait(chan, "vm-goodbye");
09641          if (res > 0)
09642             res = 0;
09643       }
09644       if (useadsi)
09645          ast_adsi_unload_session(chan);
09646    }
09647    if (vmu)
09648       close_mailbox(&vms, vmu);
09649    if (valid) {
09650       int new = 0, old = 0, urgent = 0;
09651       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09652       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09653       /* Urgent flag not passwd to externnotify here */
09654       run_externnotify(vmu->context, vmu->mailbox, NULL);
09655       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09656       queue_mwi_event(ext_context, urgent, new, old);
09657    }
09658 #ifdef IMAP_STORAGE
09659    /* expunge message - use UID Expunge if supported on IMAP server*/
09660    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09661    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09662       ast_mutex_lock(&vms.lock);
09663 #ifdef HAVE_IMAP_TK2006
09664       if (LEVELUIDPLUS (vms.mailstream)) {
09665          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09666       } else 
09667 #endif
09668          mail_expunge(vms.mailstream);
09669       ast_mutex_unlock(&vms.lock);
09670    }
09671    /*  before we delete the state, we should copy pertinent info
09672     *  back to the persistent model */
09673    if (vmu) {
09674       vmstate_delete(&vms);
09675    }
09676 #endif
09677    if (vmu)
09678       free_user(vmu);
09679    if (vms.deleted)
09680       ast_free(vms.deleted);
09681    if (vms.heard)
09682       ast_free(vms.heard);
09683 
09684 #ifdef IMAP_STORAGE
09685    pthread_setspecific(ts_vmstate.key, NULL);
09686 #endif
09687    return res;
09688 }
09689 
09690 static int vm_exec(struct ast_channel *chan, void *data)
09691 {
09692    int res = 0;
09693    char *tmp;
09694    struct leave_vm_options leave_options;
09695    struct ast_flags flags = { 0 };
09696    char *opts[OPT_ARG_ARRAY_SIZE];
09697    AST_DECLARE_APP_ARGS(args,
09698       AST_APP_ARG(argv0);
09699       AST_APP_ARG(argv1);
09700    );
09701    
09702    memset(&leave_options, 0, sizeof(leave_options));
09703 
09704    if (chan->_state != AST_STATE_UP)
09705       ast_answer(chan);
09706 
09707    if (!ast_strlen_zero(data)) {
09708       tmp = ast_strdupa(data);
09709       AST_STANDARD_APP_ARGS(args, tmp);
09710       if (args.argc == 2) {
09711          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09712             return -1;
09713          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09714          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09715             int gain;
09716 
09717             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09718                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09719                return -1;
09720             } else {
09721                leave_options.record_gain = (signed char) gain;
09722             }
09723          }
09724          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09725             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09726                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09727          }
09728       }
09729    } else {
09730       char temp[256];
09731       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09732       if (res < 0)
09733          return res;
09734       if (ast_strlen_zero(temp))
09735          return 0;
09736       args.argv0 = ast_strdupa(temp);
09737    }
09738 
09739    res = leave_voicemail(chan, args.argv0, &leave_options);
09740 
09741    if (res == ERROR_LOCK_PATH) {
09742       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09743       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09744       res = 0;
09745    }
09746 
09747    return res;
09748 }
09749 
09750 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09751 {
09752    struct ast_vm_user *vmu;
09753 
09754    AST_LIST_TRAVERSE(&users, vmu, list) {
09755       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09756          if (strcasecmp(vmu->context, context)) {
09757             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09758                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09759                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09760                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09761          }
09762          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09763          return NULL;
09764       }
09765       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09766          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09767          return NULL;
09768       }
09769    }
09770    
09771    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09772       return NULL;
09773    
09774    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09775    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09776 
09777    AST_LIST_INSERT_TAIL(&users, vmu, list);
09778    
09779    return vmu;
09780 }
09781 
09782 static int append_mailbox(const char *context, const char *box, const char *data)
09783 {
09784    /* Assumes lock is already held */
09785    char *tmp;
09786    char *stringp;
09787    char *s;
09788    struct ast_vm_user *vmu;
09789    char *mailbox_full;
09790    int new = 0, old = 0, urgent = 0;
09791 
09792    tmp = ast_strdupa(data);
09793 
09794    if (!(vmu = find_or_create(context, box)))
09795       return -1;
09796    
09797    populate_defaults(vmu);
09798 
09799    stringp = tmp;
09800    if ((s = strsep(&stringp, ","))) 
09801       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09802    if (stringp && (s = strsep(&stringp, ","))) 
09803       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09804    if (stringp && (s = strsep(&stringp, ","))) 
09805       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09806    if (stringp && (s = strsep(&stringp, ","))) 
09807       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09808    if (stringp && (s = strsep(&stringp, ","))) 
09809       apply_options(vmu, s);
09810 
09811    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09812    strcpy(mailbox_full, box);
09813    strcat(mailbox_full, "@");
09814    strcat(mailbox_full, context);
09815 
09816    inboxcount2(mailbox_full, &urgent, &new, &old);
09817    queue_mwi_event(mailbox_full, urgent, new, old);
09818 
09819    return 0;
09820 }
09821 
09822 static int vm_box_exists(struct ast_channel *chan, void *data) 
09823 {
09824    struct ast_vm_user svm;
09825    char *context, *box;
09826    AST_DECLARE_APP_ARGS(args,
09827       AST_APP_ARG(mbox);
09828       AST_APP_ARG(options);
09829    );
09830    static int dep_warning = 0;
09831 
09832    if (ast_strlen_zero(data)) {
09833       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
09834       return -1;
09835    }
09836 
09837    if (!dep_warning) {
09838       dep_warning = 1;
09839       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
09840    }
09841 
09842    box = ast_strdupa(data);
09843 
09844    AST_STANDARD_APP_ARGS(args, box);
09845 
09846    if (args.options) {
09847    }
09848 
09849    if ((context = strchr(args.mbox, '@'))) {
09850       *context = '\0';
09851       context++;
09852    }
09853 
09854    if (find_user(&svm, context, args.mbox)) {
09855       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
09856    } else
09857       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
09858 
09859    return 0;
09860 }
09861 
09862 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
09863 {
09864    struct ast_vm_user svm;
09865    AST_DECLARE_APP_ARGS(arg,
09866       AST_APP_ARG(mbox);
09867       AST_APP_ARG(context);
09868    );
09869 
09870    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
09871 
09872    if (ast_strlen_zero(arg.mbox)) {
09873       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
09874       return -1;
09875    }
09876 
09877    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
09878    return 0;
09879 }
09880 
09881 static struct ast_custom_function mailbox_exists_acf = {
09882    .name = "MAILBOX_EXISTS",
09883    .synopsis = "Tell if a mailbox is configured",
09884    .desc =
09885 "Returns a boolean of whether the corresponding mailbox exists.  If context\n"
09886 "is not specified, defaults to the \"default\" context.\n",
09887    .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
09888    .read = acf_mailbox_exists,
09889 };
09890 
09891 static int vmauthenticate(struct ast_channel *chan, void *data)
09892 {
09893    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
09894    struct ast_vm_user vmus;
09895    char *options = NULL;
09896    int silent = 0, skipuser = 0;
09897    int res = -1;
09898    
09899    if (s) {
09900       s = ast_strdupa(s);
09901       user = strsep(&s, ",");
09902       options = strsep(&s, ",");
09903       if (user) {
09904          s = user;
09905          user = strsep(&s, "@");
09906          context = strsep(&s, "");
09907          if (!ast_strlen_zero(user))
09908             skipuser++;
09909          ast_copy_string(mailbox, user, sizeof(mailbox));
09910       }
09911    }
09912 
09913    if (options) {
09914       silent = (strchr(options, 's')) != NULL;
09915    }
09916 
09917    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
09918       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
09919       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
09920       ast_play_and_wait(chan, "auth-thankyou");
09921       res = 0;
09922    }
09923 
09924    return res;
09925 }
09926 
09927 static char *show_users_realtime(int fd, const char *context)
09928 {
09929    struct ast_config *cfg;
09930    const char *cat = NULL;
09931 
09932    if (!(cfg = ast_load_realtime_multientry("voicemail", 
09933       "context", context, SENTINEL))) {
09934       return CLI_FAILURE;
09935    }
09936 
09937    ast_cli(fd,
09938       "\n"
09939       "=============================================================\n"
09940       "=== Configured Voicemail Users ==============================\n"
09941       "=============================================================\n"
09942       "===\n");
09943 
09944    while ((cat = ast_category_browse(cfg, cat))) {
09945       struct ast_variable *var = NULL;
09946       ast_cli(fd,
09947          "=== Mailbox ...\n"
09948          "===\n");
09949       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
09950          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
09951       ast_cli(fd,
09952          "===\n"
09953          "=== ---------------------------------------------------------\n"
09954          "===\n");
09955    }
09956 
09957    ast_cli(fd,
09958       "=============================================================\n"
09959       "\n");
09960 
09961    ast_config_destroy(cfg);
09962 
09963    return CLI_SUCCESS;
09964 }
09965 
09966 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
09967 {
09968    int which = 0;
09969    int wordlen;
09970    struct ast_vm_user *vmu;
09971    const char *context = "";
09972 
09973    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
09974    if (pos > 4)
09975       return NULL;
09976    if (pos == 3)
09977       return (state == 0) ? ast_strdup("for") : NULL;
09978    wordlen = strlen(word);
09979    AST_LIST_TRAVERSE(&users, vmu, list) {
09980       if (!strncasecmp(word, vmu->context, wordlen)) {
09981          if (context && strcmp(context, vmu->context) && ++which > state)
09982             return ast_strdup(vmu->context);
09983          /* ignore repeated contexts ? */
09984          context = vmu->context;
09985       }
09986    }
09987    return NULL;
09988 }
09989 
09990 /*! \brief Show a list of voicemail users in the CLI */
09991 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09992 {
09993    struct ast_vm_user *vmu;
09994 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
09995    const char *context = NULL;
09996    int users_counter = 0;
09997 
09998    switch (cmd) {
09999    case CLI_INIT:
10000       e->command = "voicemail show users";
10001       e->usage =
10002          "Usage: voicemail show users [for <context>]\n"
10003          "       Lists all mailboxes currently set up\n";
10004       return NULL;
10005    case CLI_GENERATE:
10006       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10007    }  
10008 
10009    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10010       return CLI_SHOWUSAGE;
10011    if (a->argc == 5) {
10012       if (strcmp(a->argv[3],"for"))
10013          return CLI_SHOWUSAGE;
10014       context = a->argv[4];
10015    }
10016 
10017    if (ast_check_realtime("voicemail")) {
10018       if (!context) {
10019          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10020          return CLI_SHOWUSAGE;
10021       }
10022       return show_users_realtime(a->fd, context);
10023    }
10024 
10025    AST_LIST_LOCK(&users);
10026    if (AST_LIST_EMPTY(&users)) {
10027       ast_cli(a->fd, "There are no voicemail users currently defined\n");
10028       AST_LIST_UNLOCK(&users);
10029       return CLI_FAILURE;
10030    }
10031    if (a->argc == 3)
10032       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10033    else {
10034       int count = 0;
10035       AST_LIST_TRAVERSE(&users, vmu, list) {
10036          if (!strcmp(context, vmu->context))
10037             count++;
10038       }
10039       if (count) {
10040          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10041       } else {
10042          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10043          AST_LIST_UNLOCK(&users);
10044          return CLI_FAILURE;
10045       }
10046    }
10047    AST_LIST_TRAVERSE(&users, vmu, list) {
10048       int newmsgs = 0, oldmsgs = 0;
10049       char count[12], tmp[256] = "";
10050 
10051       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10052          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10053          inboxcount(tmp, &newmsgs, &oldmsgs);
10054          snprintf(count, sizeof(count), "%d", newmsgs);
10055          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10056          users_counter++;
10057       }
10058    }
10059    AST_LIST_UNLOCK(&users);
10060    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10061    return CLI_SUCCESS;
10062 }
10063 
10064 /*! \brief Show a list of voicemail zones in the CLI */
10065 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10066 {
10067    struct vm_zone *zone;
10068 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10069    char *res = CLI_SUCCESS;
10070 
10071    switch (cmd) {
10072    case CLI_INIT:
10073       e->command = "voicemail show zones";
10074       e->usage =
10075          "Usage: voicemail show zones\n"
10076          "       Lists zone message formats\n";
10077       return NULL;
10078    case CLI_GENERATE:
10079       return NULL;
10080    }
10081 
10082    if (a->argc != 3)
10083       return CLI_SHOWUSAGE;
10084 
10085    AST_LIST_LOCK(&zones);
10086    if (!AST_LIST_EMPTY(&zones)) {
10087       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10088       AST_LIST_TRAVERSE(&zones, zone, list) {
10089          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10090       }
10091    } else {
10092       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10093       res = CLI_FAILURE;
10094    }
10095    AST_LIST_UNLOCK(&zones);
10096 
10097    return res;
10098 }
10099 
10100 /*! \brief Reload voicemail configuration from the CLI */
10101 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10102 {
10103    switch (cmd) {
10104    case CLI_INIT:
10105       e->command = "voicemail reload";
10106       e->usage =
10107          "Usage: voicemail reload\n"
10108          "       Reload voicemail configuration\n";
10109       return NULL;
10110    case CLI_GENERATE:
10111       return NULL;
10112    }
10113 
10114    if (a->argc != 2)
10115       return CLI_SHOWUSAGE;
10116 
10117    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
10118    load_config(1);
10119    
10120    return CLI_SUCCESS;
10121 }
10122 
10123 static struct ast_cli_entry cli_voicemail[] = {
10124    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10125    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10126    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10127 };
10128 
10129 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10130 {
10131    int new = 0, old = 0, urgent = 0;
10132 
10133    inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10134 
10135    if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10136       mwi_sub->old_urgent = urgent;
10137       mwi_sub->old_new = new;
10138       mwi_sub->old_old = old;
10139       queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10140    }
10141 }
10142 
10143 static void poll_subscribed_mailboxes(void)
10144 {
10145    struct mwi_sub *mwi_sub;
10146 
10147    AST_RWLIST_RDLOCK(&mwi_subs);
10148    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10149       if (!ast_strlen_zero(mwi_sub->mailbox)) {
10150          poll_subscribed_mailbox(mwi_sub);
10151       }
10152    }
10153    AST_RWLIST_UNLOCK(&mwi_subs);
10154 }
10155 
10156 static void *mb_poll_thread(void *data)
10157 {
10158    while (poll_thread_run) {
10159       struct timespec ts = { 0, };
10160       struct timeval wait;
10161 
10162       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10163       ts.tv_sec = wait.tv_sec;
10164       ts.tv_nsec = wait.tv_usec * 1000;
10165 
10166       ast_mutex_lock(&poll_lock);
10167       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10168       ast_mutex_unlock(&poll_lock);
10169 
10170       if (!poll_thread_run)
10171          break;
10172 
10173       poll_subscribed_mailboxes();
10174    }
10175 
10176    return NULL;
10177 }
10178 
10179 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10180 {
10181    ast_free(mwi_sub);
10182 }
10183 
10184 static int handle_unsubscribe(void *datap)
10185 {
10186    struct mwi_sub *mwi_sub;
10187    uint32_t *uniqueid = datap;
10188    
10189    AST_RWLIST_WRLOCK(&mwi_subs);
10190    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10191       if (mwi_sub->uniqueid == *uniqueid) {
10192          AST_LIST_REMOVE_CURRENT(entry);
10193          break;
10194       }
10195    }
10196    AST_RWLIST_TRAVERSE_SAFE_END
10197    AST_RWLIST_UNLOCK(&mwi_subs);
10198 
10199    if (mwi_sub)
10200       mwi_sub_destroy(mwi_sub);
10201 
10202    ast_free(uniqueid);  
10203    return 0;
10204 }
10205 
10206 static int handle_subscribe(void *datap)
10207 {
10208    unsigned int len;
10209    struct mwi_sub *mwi_sub;
10210    struct mwi_sub_task *p = datap;
10211 
10212    len = sizeof(*mwi_sub);
10213    if (!ast_strlen_zero(p->mailbox))
10214       len += strlen(p->mailbox);
10215 
10216    if (!ast_strlen_zero(p->context))
10217       len += strlen(p->context) + 1; /* Allow for seperator */
10218 
10219    if (!(mwi_sub = ast_calloc(1, len)))
10220       return -1;
10221 
10222    mwi_sub->uniqueid = p->uniqueid;
10223    if (!ast_strlen_zero(p->mailbox))
10224       strcpy(mwi_sub->mailbox, p->mailbox);
10225 
10226    if (!ast_strlen_zero(p->context)) {
10227       strcat(mwi_sub->mailbox, "@");
10228       strcat(mwi_sub->mailbox, p->context);
10229    }
10230 
10231    AST_RWLIST_WRLOCK(&mwi_subs);
10232    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10233    AST_RWLIST_UNLOCK(&mwi_subs);
10234    ast_free((void *) p->mailbox);
10235    ast_free((void *) p->context);
10236    ast_free(p);
10237    poll_subscribed_mailbox(mwi_sub);
10238    return 0;
10239 }
10240 
10241 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10242 {
10243    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10244    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10245       return;
10246 
10247    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10248       return;
10249 
10250    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10251    *uniqueid = u;
10252    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10253       ast_free(uniqueid);
10254    }
10255 }
10256 
10257 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10258 {
10259    struct mwi_sub_task *mwist;
10260    
10261    if (ast_event_get_type(event) != AST_EVENT_SUB)
10262       return;
10263 
10264    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10265       return;
10266 
10267    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10268       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10269       return;
10270    }
10271    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10272    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10273    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10274    
10275    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10276       ast_free(mwist);
10277    }
10278 }
10279 
10280 static void start_poll_thread(void)
10281 {
10282    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10283       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10284       AST_EVENT_IE_END);
10285 
10286    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10287       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10288       AST_EVENT_IE_END);
10289 
10290    if (mwi_sub_sub)
10291       ast_event_report_subs(mwi_sub_sub);
10292 
10293    poll_thread_run = 1;
10294 
10295    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10296 }
10297 
10298 static void stop_poll_thread(void)
10299 {
10300    poll_thread_run = 0;
10301 
10302    if (mwi_sub_sub) {
10303       ast_event_unsubscribe(mwi_sub_sub);
10304       mwi_sub_sub = NULL;
10305    }
10306 
10307    if (mwi_unsub_sub) {
10308       ast_event_unsubscribe(mwi_unsub_sub);
10309       mwi_unsub_sub = NULL;
10310    }
10311 
10312    ast_mutex_lock(&poll_lock);
10313    ast_cond_signal(&poll_cond);
10314    ast_mutex_unlock(&poll_lock);
10315 
10316    pthread_join(poll_thread, NULL);
10317 
10318    poll_thread = AST_PTHREADT_NULL;
10319 }
10320 
10321 /*! \brief Manager list voicemail users command */
10322 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10323 {
10324    struct ast_vm_user *vmu = NULL;
10325    const char *id = astman_get_header(m, "ActionID");
10326    char actionid[128] = "";
10327 
10328    if (!ast_strlen_zero(id))
10329       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10330 
10331    AST_LIST_LOCK(&users);
10332 
10333    if (AST_LIST_EMPTY(&users)) {
10334       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10335       AST_LIST_UNLOCK(&users);
10336       return RESULT_SUCCESS;
10337    }
10338    
10339    astman_send_ack(s, m, "Voicemail user list will follow");
10340    
10341    AST_LIST_TRAVERSE(&users, vmu, list) {
10342       char dirname[256];
10343 
10344 #ifdef IMAP_STORAGE
10345       int new, old;
10346       inboxcount(vmu->mailbox, &new, &old);
10347 #endif
10348       
10349       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10350       astman_append(s,
10351          "%s"
10352          "Event: VoicemailUserEntry\r\n"
10353          "VMContext: %s\r\n"
10354          "VoiceMailbox: %s\r\n"
10355          "Fullname: %s\r\n"
10356          "Email: %s\r\n"
10357          "Pager: %s\r\n"
10358          "ServerEmail: %s\r\n"
10359          "MailCommand: %s\r\n"
10360          "Language: %s\r\n"
10361          "TimeZone: %s\r\n"
10362          "Callback: %s\r\n"
10363          "Dialout: %s\r\n"
10364          "UniqueID: %s\r\n"
10365          "ExitContext: %s\r\n"
10366          "SayDurationMinimum: %d\r\n"
10367          "SayEnvelope: %s\r\n"
10368          "SayCID: %s\r\n"
10369          "AttachMessage: %s\r\n"
10370          "AttachmentFormat: %s\r\n"
10371          "DeleteMessage: %s\r\n"
10372          "VolumeGain: %.2f\r\n"
10373          "CanReview: %s\r\n"
10374          "CallOperator: %s\r\n"
10375          "MaxMessageCount: %d\r\n"
10376          "MaxMessageLength: %d\r\n"
10377          "NewMessageCount: %d\r\n"
10378 #ifdef IMAP_STORAGE
10379          "OldMessageCount: %d\r\n"
10380          "IMAPUser: %s\r\n"
10381 #endif
10382          "\r\n",
10383          actionid,
10384          vmu->context,
10385          vmu->mailbox,
10386          vmu->fullname,
10387          vmu->email,
10388          vmu->pager,
10389          vmu->serveremail,
10390          vmu->mailcmd,
10391          vmu->language,
10392          vmu->zonetag,
10393          vmu->callback,
10394          vmu->dialout,
10395          vmu->uniqueid,
10396          vmu->exit,
10397          vmu->saydurationm,
10398          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10399          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10400          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10401          vmu->attachfmt,
10402          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10403          vmu->volgain,
10404          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10405          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10406          vmu->maxmsg,
10407          vmu->maxsecs,
10408 #ifdef IMAP_STORAGE
10409          new, old, vmu->imapuser
10410 #else
10411          count_messages(vmu, dirname)
10412 #endif
10413          );
10414    }     
10415    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10416 
10417    AST_LIST_UNLOCK(&users);
10418 
10419    return RESULT_SUCCESS;
10420 }
10421 
10422 /*! \brief Free the users structure. */
10423 static void free_vm_users(void) 
10424 {
10425    struct ast_vm_user *current;
10426    AST_LIST_LOCK(&users);
10427    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10428       ast_set_flag(current, VM_ALLOCED);
10429       free_user(current);
10430    }
10431    AST_LIST_UNLOCK(&users);
10432 }
10433 
10434 /*! \brief Free the zones structure. */
10435 static void free_vm_zones(void)
10436 {
10437    struct vm_zone *zcur;
10438    AST_LIST_LOCK(&zones);
10439    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10440       free_zone(zcur);
10441    AST_LIST_UNLOCK(&zones);
10442 }
10443 
10444 static char *substitute_escapes(const char *value)
10445 {
10446    char *current, *result;
10447 
10448    /* Add 16 for fudge factor */
10449    struct ast_str *str = ast_str_create(strlen(value) + 16);
10450 
10451    /* Substitute strings \r, \n, and \t into the appropriate characters */
10452    for (current = (char *) value; *current; current++) {
10453       if (*current == '\\') {
10454          current++;
10455          if (!*current) {
10456             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10457             break;
10458          }
10459          switch (*current) {
10460          case 'r':
10461             ast_str_append(&str, 0, "\r");
10462             break;
10463          case 'n':
10464 #ifdef IMAP_STORAGE
10465             if (!str->used || str->str[str->used - 1] != '\r') {
10466                ast_str_append(&str, 0, "\r");
10467             }
10468 #endif
10469             ast_str_append(&str, 0, "\n");
10470             break;
10471          case 't':
10472             ast_str_append(&str, 0, "\t");
10473             break;
10474          default:
10475             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10476             break;
10477          }
10478       } else {
10479          ast_str_append(&str, 0, "%c", *current);
10480       }
10481    }
10482 
10483    result = ast_strdup(str->str);
10484    ast_free(str);
10485 
10486    return result;
10487 }
10488 
10489 static int load_config(int reload)
10490 {
10491    struct ast_vm_user *current;
10492    struct ast_config *cfg, *ucfg;
10493    char *cat;
10494    struct ast_variable *var;
10495    const char *val;
10496    char *q, *stringp, *tmp;
10497    int x;
10498    int tmpadsi[4];
10499    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10500 
10501    ast_unload_realtime("voicemail");
10502    ast_unload_realtime("voicemail_data");
10503 
10504    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10505       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
10506          return 0;
10507       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10508       cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
10509    } else {
10510       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10511       ucfg = ast_config_load("users.conf", config_flags);
10512    }
10513 #ifdef IMAP_STORAGE
10514    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10515 #endif
10516    /* set audio control prompts */
10517    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10518    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10519    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10520    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10521    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10522 
10523    /* Free all the users structure */  
10524    free_vm_users();
10525 
10526    /* Free all the zones structure */
10527    free_vm_zones();
10528 
10529    AST_LIST_LOCK(&users);  
10530 
10531    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10532    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10533 
10534    if (cfg) {
10535       /* General settings */
10536 
10537       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10538          val = "default";
10539       ast_copy_string(userscontext, val, sizeof(userscontext));
10540       /* Attach voice message to mail message ? */
10541       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10542          val = "yes";
10543       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10544 
10545       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10546          val = "no";
10547       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10548 
10549       volgain = 0.0;
10550       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10551          sscanf(val, "%30lf", &volgain);
10552 
10553 #ifdef ODBC_STORAGE
10554       strcpy(odbc_database, "asterisk");
10555       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10556          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10557       }
10558       strcpy(odbc_table, "voicemessages");
10559       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10560          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10561       }
10562 #endif      
10563       /* Mail command */
10564       strcpy(mailcmd, SENDMAIL);
10565       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10566          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10567 
10568       maxsilence = 0;
10569       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10570          maxsilence = atoi(val);
10571          if (maxsilence > 0)
10572             maxsilence *= 1000;
10573       }
10574       
10575       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10576          maxmsg = MAXMSG;
10577       } else {
10578          maxmsg = atoi(val);
10579          if (maxmsg <= 0) {
10580             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10581             maxmsg = MAXMSG;
10582          } else if (maxmsg > MAXMSGLIMIT) {
10583             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10584             maxmsg = MAXMSGLIMIT;
10585          }
10586       }
10587 
10588       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10589          maxdeletedmsg = 0;
10590       } else {
10591          if (sscanf(val, "%30d", &x) == 1)
10592             maxdeletedmsg = x;
10593          else if (ast_true(val))
10594             maxdeletedmsg = MAXMSG;
10595          else
10596             maxdeletedmsg = 0;
10597 
10598          if (maxdeletedmsg < 0) {
10599             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10600             maxdeletedmsg = MAXMSG;
10601          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10602             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10603             maxdeletedmsg = MAXMSGLIMIT;
10604          }
10605       }
10606 
10607       /* Load date format config for voicemail mail */
10608       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10609          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10610       }
10611 
10612       /* External password changing command */
10613       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10614          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10615          pwdchange = PWDCHANGE_EXTERNAL;
10616       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10617          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10618          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10619       }
10620  
10621       /* External password validation command */
10622       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10623          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10624          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10625       }
10626 
10627 #ifdef IMAP_STORAGE
10628       /* IMAP server address */
10629       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10630          ast_copy_string(imapserver, val, sizeof(imapserver));
10631       } else {
10632          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10633       }
10634       /* IMAP server port */
10635       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10636          ast_copy_string(imapport, val, sizeof(imapport));
10637       } else {
10638          ast_copy_string(imapport,"143", sizeof(imapport));
10639       }
10640       /* IMAP server flags */
10641       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10642          ast_copy_string(imapflags, val, sizeof(imapflags));
10643       }
10644       /* IMAP server master username */
10645       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10646          ast_copy_string(authuser, val, sizeof(authuser));
10647       }
10648       /* IMAP server master password */
10649       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10650          ast_copy_string(authpassword, val, sizeof(authpassword));
10651       }
10652       /* Expunge on exit */
10653       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10654          if (ast_false(val))
10655             expungeonhangup = 0;
10656          else
10657             expungeonhangup = 1;
10658       } else {
10659          expungeonhangup = 1;
10660       }
10661       /* IMAP voicemail folder */
10662       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10663          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10664       } else {
10665          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10666       }
10667       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10668          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10669       }
10670       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10671          imapgreetings = ast_true(val);
10672       } else {
10673          imapgreetings = 0;
10674       }
10675       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10676          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10677       } else {
10678          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10679       }
10680 
10681       /* There is some very unorthodox casting done here. This is due
10682        * to the way c-client handles the argument passed in. It expects a 
10683        * void pointer and casts the pointer directly to a long without
10684        * first dereferencing it. */
10685       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10686          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10687       } else {
10688          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10689       }
10690 
10691       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10692          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10693       } else {
10694          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10695       }
10696 
10697       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10698          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10699       } else {
10700          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10701       }
10702 
10703       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10704          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10705       } else {
10706          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10707       }
10708 
10709       /* Increment configuration version */
10710       imapversion++;
10711 #endif
10712       /* External voicemail notify application */
10713       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10714          ast_copy_string(externnotify, val, sizeof(externnotify));
10715          ast_debug(1, "found externnotify: %s\n", externnotify);
10716       } else {
10717          externnotify[0] = '\0';
10718       }
10719 
10720       /* SMDI voicemail notification */
10721       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10722          ast_debug(1, "Enabled SMDI voicemail notification\n");
10723          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10724             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10725          } else {
10726             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10727             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10728          }
10729          if (!smdi_iface) {
10730             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10731          } 
10732       }
10733 
10734       /* Silence treshold */
10735       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10736       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10737          silencethreshold = atoi(val);
10738       
10739       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10740          val = ASTERISK_USERNAME;
10741       ast_copy_string(serveremail, val, sizeof(serveremail));
10742       
10743       vmmaxsecs = 0;
10744       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10745          if (sscanf(val, "%30d", &x) == 1) {
10746             vmmaxsecs = x;
10747          } else {
10748             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10749          }
10750       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10751          static int maxmessage_deprecate = 0;
10752          if (maxmessage_deprecate == 0) {
10753             maxmessage_deprecate = 1;
10754             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10755          }
10756          if (sscanf(val, "%30d", &x) == 1) {
10757             vmmaxsecs = x;
10758          } else {
10759             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10760          }
10761       }
10762 
10763       vmminsecs = 0;
10764       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10765          if (sscanf(val, "%30d", &x) == 1) {
10766             vmminsecs = x;
10767             if (maxsilence / 1000 >= vmminsecs) {
10768                ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
10769             }
10770          } else {
10771             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10772          }
10773       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10774          static int maxmessage_deprecate = 0;
10775          if (maxmessage_deprecate == 0) {
10776             maxmessage_deprecate = 1;
10777             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10778          }
10779          if (sscanf(val, "%30d", &x) == 1) {
10780             vmminsecs = x;
10781             if (maxsilence / 1000 >= vmminsecs) {
10782                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10783             }
10784          } else {
10785             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10786          }
10787       }
10788 
10789       val = ast_variable_retrieve(cfg, "general", "format");
10790       if (!val) {
10791          val = "wav";   
10792       } else {
10793          tmp = ast_strdupa(val);
10794          val = ast_format_str_reduce(tmp);
10795          if (!val) {
10796             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
10797             val = "wav";
10798          }
10799       }
10800       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10801 
10802       skipms = 3000;
10803       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10804          if (sscanf(val, "%30d", &x) == 1) {
10805             maxgreet = x;
10806          } else {
10807             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10808          }
10809       }
10810 
10811       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10812          if (sscanf(val, "%30d", &x) == 1) {
10813             skipms = x;
10814          } else {
10815             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10816          }
10817       }
10818 
10819       maxlogins = 3;
10820       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10821          if (sscanf(val, "%30d", &x) == 1) {
10822             maxlogins = x;
10823          } else {
10824             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10825          }
10826       }
10827 
10828       minpassword = MINPASSWORD;
10829       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
10830          if (sscanf(val, "%30d", &x) == 1) {
10831             minpassword = x;
10832          } else {
10833             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
10834          }
10835       }
10836 
10837       /* Force new user to record name ? */
10838       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
10839          val = "no";
10840       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
10841 
10842       /* Force new user to record greetings ? */
10843       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
10844          val = "no";
10845       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
10846 
10847       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
10848          ast_debug(1, "VM_CID Internal context string: %s\n", val);
10849          stringp = ast_strdupa(val);
10850          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
10851             if (!ast_strlen_zero(stringp)) {
10852                q = strsep(&stringp, ",");
10853                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
10854                   q++;
10855                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
10856                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
10857             } else {
10858                cidinternalcontexts[x][0] = '\0';
10859             }
10860          }
10861       }
10862       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
10863          ast_debug(1,"VM Review Option disabled globally\n");
10864          val = "no";
10865       }
10866       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
10867 
10868       /* Temporary greeting reminder */
10869       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
10870          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
10871          val = "no";
10872       } else {
10873          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
10874       }
10875       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
10876       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
10877          ast_debug(1, "VM next message wrap disabled globally\n");
10878          val = "no";
10879       }
10880       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
10881 
10882       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
10883          ast_debug(1,"VM Operator break disabled globally\n");
10884          val = "no";
10885       }
10886       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
10887 
10888       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
10889          ast_debug(1,"VM CID Info before msg disabled globally\n");
10890          val = "no";
10891       } 
10892       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
10893 
10894       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
10895          ast_debug(1,"Send Voicemail msg disabled globally\n");
10896          val = "no";
10897       }
10898       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
10899    
10900       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
10901          ast_debug(1,"ENVELOPE before msg enabled globally\n");
10902          val = "yes";
10903       }
10904       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
10905 
10906       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
10907          ast_debug(1,"Move Heard enabled globally\n");
10908          val = "yes";
10909       }
10910       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
10911 
10912       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
10913          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
10914          val = "no";
10915       }
10916       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
10917 
10918       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
10919          ast_debug(1,"Duration info before msg enabled globally\n");
10920          val = "yes";
10921       }
10922       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
10923 
10924       saydurationminfo = 2;
10925       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
10926          if (sscanf(val, "%30d", &x) == 1) {
10927             saydurationminfo = x;
10928          } else {
10929             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
10930          }
10931       }
10932 
10933       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
10934          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
10935          val = "no";
10936       }
10937       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
10938 
10939       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
10940          ast_copy_string(dialcontext, val, sizeof(dialcontext));
10941          ast_debug(1, "found dialout context: %s\n", dialcontext);
10942       } else {
10943          dialcontext[0] = '\0';  
10944       }
10945       
10946       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
10947          ast_copy_string(callcontext, val, sizeof(callcontext));
10948          ast_debug(1, "found callback context: %s\n", callcontext);
10949       } else {
10950          callcontext[0] = '\0';
10951       }
10952 
10953       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
10954          ast_copy_string(exitcontext, val, sizeof(exitcontext));
10955          ast_debug(1, "found operator context: %s\n", exitcontext);
10956       } else {
10957          exitcontext[0] = '\0';
10958       }
10959       
10960       /* load password sounds configuration */
10961       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
10962          ast_copy_string(vm_password, val, sizeof(vm_password));
10963       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
10964          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
10965       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
10966          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
10967       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
10968          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
10969       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
10970          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
10971       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
10972          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
10973       if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
10974          ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
10975       }
10976       /* load configurable audio prompts */
10977       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
10978          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
10979       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
10980          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
10981       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
10982          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
10983       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
10984          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
10985       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
10986          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
10987 
10988       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
10989          val = "no";
10990       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
10991 
10992       poll_freq = DEFAULT_POLL_FREQ;
10993       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
10994          if (sscanf(val, "%30u", &poll_freq) != 1) {
10995             poll_freq = DEFAULT_POLL_FREQ;
10996             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
10997          }
10998       }
10999 
11000       poll_mailboxes = 0;
11001       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11002          poll_mailboxes = ast_true(val);
11003 
11004       if (ucfg) { 
11005          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11006             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11007                continue;
11008             if ((current = find_or_create(userscontext, cat))) {
11009                populate_defaults(current);
11010                apply_options_full(current, ast_variable_browse(ucfg, cat));
11011                ast_copy_string(current->context, userscontext, sizeof(current->context));
11012             }
11013          }
11014          ast_config_destroy(ucfg);
11015       }
11016       cat = ast_category_browse(cfg, NULL);
11017       while (cat) {
11018          if (strcasecmp(cat, "general")) {
11019             var = ast_variable_browse(cfg, cat);
11020             if (strcasecmp(cat, "zonemessages")) {
11021                /* Process mailboxes in this context */
11022                while (var) {
11023                   append_mailbox(cat, var->name, var->value);
11024                   var = var->next;
11025                }
11026             } else {
11027                /* Timezones in this context */
11028                while (var) {
11029                   struct vm_zone *z;
11030                   if ((z = ast_malloc(sizeof(*z)))) {
11031                      char *msg_format, *tzone;
11032                      msg_format = ast_strdupa(var->value);
11033                      tzone = strsep(&msg_format, "|");
11034                      if (msg_format) {
11035                         ast_copy_string(z->name, var->name, sizeof(z->name));
11036                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11037                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11038                         AST_LIST_LOCK(&zones);
11039                         AST_LIST_INSERT_HEAD(&zones, z, list);
11040                         AST_LIST_UNLOCK(&zones);
11041                      } else {
11042                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11043                         ast_free(z);
11044                      }
11045                   } else {
11046                      AST_LIST_UNLOCK(&users);
11047                      ast_config_destroy(cfg);
11048                      return -1;
11049                   }
11050                   var = var->next;
11051                }
11052             }
11053          }
11054          cat = ast_category_browse(cfg, cat);
11055       }
11056       memset(fromstring, 0, sizeof(fromstring));
11057       memset(pagerfromstring, 0, sizeof(pagerfromstring));
11058       strcpy(charset, "ISO-8859-1");
11059       if (emailbody) {
11060          ast_free(emailbody);
11061          emailbody = NULL;
11062       }
11063       if (emailsubject) {
11064          ast_free(emailsubject);
11065          emailsubject = NULL;
11066       }
11067       if (pagerbody) {
11068          ast_free(pagerbody);
11069          pagerbody = NULL;
11070       }
11071       if (pagersubject) {
11072          ast_free(pagersubject);
11073          pagersubject = NULL;
11074       }
11075       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11076          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11077       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11078          ast_copy_string(fromstring, val, sizeof(fromstring));
11079       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11080          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11081       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11082          ast_copy_string(charset, val, sizeof(charset));
11083       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11084          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11085          for (x = 0; x < 4; x++) {
11086             memcpy(&adsifdn[x], &tmpadsi[x], 1);
11087          }
11088       }
11089       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11090          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11091          for (x = 0; x < 4; x++) {
11092             memcpy(&adsisec[x], &tmpadsi[x], 1);
11093          }
11094       }
11095       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11096          if (atoi(val)) {
11097             adsiver = atoi(val);
11098          }
11099       }
11100       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11101          ast_copy_string(zonetag, val, sizeof(zonetag));
11102       }
11103       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11104          emailsubject = ast_strdup(val);
11105       }
11106       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11107          emailbody = substitute_escapes(val);
11108       }
11109       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11110          pagersubject = ast_strdup(val);
11111       }
11112       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11113          pagerbody = substitute_escapes(val);
11114       }
11115       AST_LIST_UNLOCK(&users);
11116       ast_config_destroy(cfg);
11117 
11118       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11119          start_poll_thread();
11120       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11121          stop_poll_thread();;
11122 
11123       return 0;
11124    } else {
11125       AST_LIST_UNLOCK(&users);
11126       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11127       if (ucfg)
11128          ast_config_destroy(ucfg);
11129       return 0;
11130    }
11131 }
11132 
11133 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11134 {
11135    int res = -1;
11136    char dir[PATH_MAX];
11137    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11138    ast_debug(2, "About to try retrieving name file %s\n", dir);
11139    RETRIEVE(dir, -1, mailbox, context);
11140    if (ast_fileexists(dir, NULL, NULL)) {
11141       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11142    }
11143    DISPOSE(dir, -1);
11144    return res;
11145 }
11146 
11147 static int reload(void)
11148 {
11149    return load_config(1);
11150 }
11151 
11152 static int unload_module(void)
11153 {
11154    int res;
11155 
11156    res = ast_unregister_application(app);
11157    res |= ast_unregister_application(app2);
11158    res |= ast_unregister_application(app3);
11159    res |= ast_unregister_application(app4);
11160    res |= ast_custom_function_unregister(&mailbox_exists_acf);
11161    res |= ast_manager_unregister("VoicemailUsersList");
11162    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
11163    ast_uninstall_vm_functions();
11164    ao2_ref(inprocess_container, -1);
11165 
11166    if (poll_thread != AST_PTHREADT_NULL)
11167       stop_poll_thread();
11168 
11169    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11170    ast_unload_realtime("voicemail");
11171    ast_unload_realtime("voicemail_data");
11172 
11173    free_vm_users();
11174    free_vm_zones();
11175    return res;
11176 }
11177 
11178 static int load_module(void)
11179 {
11180    int res;
11181    my_umask = umask(0);
11182    umask(my_umask);
11183 
11184    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
11185       return AST_MODULE_LOAD_DECLINE;
11186    }
11187 
11188    /* compute the location of the voicemail spool directory */
11189    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11190    
11191    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11192       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
11193    }
11194 
11195    if ((res = load_config(0)))
11196       return res;
11197 
11198    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
11199    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
11200    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
11201    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
11202    res |= ast_custom_function_register(&mailbox_exists_acf);
11203    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11204    if (res)
11205       return res;
11206 
11207    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
11208 
11209    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11210    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11211    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11212 
11213    return res;
11214 }
11215 
11216 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
11217 {
11218    int cmd = 0;
11219    char destination[80] = "";
11220    int retries = 0;
11221 
11222    if (!num) {
11223       ast_verb(3, "Destination number will be entered manually\n");
11224       while (retries < 3 && cmd != 't') {
11225          destination[1] = '\0';
11226          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11227          if (!cmd)
11228             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11229          if (!cmd)
11230             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11231          if (!cmd) {
11232             cmd = ast_waitfordigit(chan, 6000);
11233             if (cmd)
11234                destination[0] = cmd;
11235          }
11236          if (!cmd) {
11237             retries++;
11238          } else {
11239 
11240             if (cmd < 0)
11241                return 0;
11242             if (cmd == '*') {
11243                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11244                return 0;
11245             }
11246             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11247                retries++;
11248             else
11249                cmd = 't';
11250          }
11251       }
11252       if (retries >= 3) {
11253          return 0;
11254       }
11255       
11256    } else {
11257       if (option_verbose > 2)
11258          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11259       ast_copy_string(destination, num, sizeof(destination));
11260    }
11261 
11262    if (!ast_strlen_zero(destination)) {
11263       if (destination[strlen(destination) -1 ] == '*')
11264          return 0; 
11265       if (option_verbose > 2)
11266          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11267       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11268       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11269       chan->priority = 0;
11270       return 9;
11271    }
11272    return 0;
11273 }
11274 
11275 /*!
11276  * \brief The advanced options within a message.
11277  * \param chan
11278  * \param vmu 
11279  * \param vms
11280  * \param msg
11281  * \param option
11282  * \param record_gain
11283  *
11284  * Provides handling for the play message envelope, call the person back, or reply to message. 
11285  *
11286  * \return zero on success, -1 on error.
11287  */
11288 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)
11289 {
11290    int res = 0;
11291    char filename[PATH_MAX];
11292    struct ast_config *msg_cfg = NULL;
11293    const char *origtime, *context;
11294    char *name, *num;
11295    int retries = 0;
11296    char *cid;
11297    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11298 
11299    vms->starting = 0; 
11300 
11301    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11302 
11303    /* Retrieve info from VM attribute file */
11304    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11305    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11306    msg_cfg = ast_config_load(filename, config_flags);
11307    DISPOSE(vms->curdir, vms->curmsg);
11308    if (!msg_cfg) {
11309       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11310       return 0;
11311    }
11312 
11313    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11314       ast_config_destroy(msg_cfg);
11315       return 0;
11316    }
11317 
11318    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11319 
11320    context = ast_variable_retrieve(msg_cfg, "message", "context");
11321    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11322       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11323    switch (option) {
11324    case 3: /* Play message envelope */
11325       if (!res)
11326          res = play_message_datetime(chan, vmu, origtime, filename);
11327       if (!res)
11328          res = play_message_callerid(chan, vms, cid, context, 0);
11329 
11330       res = 't';
11331       break;
11332 
11333    case 2:  /* Call back */
11334 
11335       if (ast_strlen_zero(cid))
11336          break;
11337 
11338       ast_callerid_parse(cid, &name, &num);
11339       while ((res > -1) && (res != 't')) {
11340          switch (res) {
11341          case '1':
11342             if (num) {
11343                /* Dial the CID number */
11344                res = dialout(chan, vmu, num, vmu->callback);
11345                if (res) {
11346                   ast_config_destroy(msg_cfg);
11347                   return 9;
11348                }
11349             } else {
11350                res = '2';
11351             }
11352             break;
11353 
11354          case '2':
11355             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11356             if (!ast_strlen_zero(vmu->dialout)) {
11357                res = dialout(chan, vmu, NULL, vmu->dialout);
11358                if (res) {
11359                   ast_config_destroy(msg_cfg);
11360                   return 9;
11361                }
11362             } else {
11363                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11364                res = ast_play_and_wait(chan, "vm-sorry");
11365             }
11366             ast_config_destroy(msg_cfg);
11367             return res;
11368          case '*':
11369             res = 't';
11370             break;
11371          case '3':
11372          case '4':
11373          case '5':
11374          case '6':
11375          case '7':
11376          case '8':
11377          case '9':
11378          case '0':
11379 
11380             res = ast_play_and_wait(chan, "vm-sorry");
11381             retries++;
11382             break;
11383          default:
11384             if (num) {
11385                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11386                res = ast_play_and_wait(chan, "vm-num-i-have");
11387                if (!res)
11388                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11389                if (!res)
11390                   res = ast_play_and_wait(chan, "vm-tocallnum");
11391                /* Only prompt for a caller-specified number if there is a dialout context specified */
11392                if (!ast_strlen_zero(vmu->dialout)) {
11393                   if (!res)
11394                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11395                }
11396             } else {
11397                res = ast_play_and_wait(chan, "vm-nonumber");
11398                if (!ast_strlen_zero(vmu->dialout)) {
11399                   if (!res)
11400                      res = ast_play_and_wait(chan, "vm-toenternumber");
11401                }
11402             }
11403             if (!res)
11404                res = ast_play_and_wait(chan, "vm-star-cancel");
11405             if (!res)
11406                res = ast_waitfordigit(chan, 6000);
11407             if (!res) {
11408                retries++;
11409                if (retries > 3)
11410                   res = 't';
11411             }
11412             break; 
11413             
11414          }
11415          if (res == 't')
11416             res = 0;
11417          else if (res == '*')
11418             res = -1;
11419       }
11420       break;
11421       
11422    case 1:  /* Reply */
11423       /* Send reply directly to sender */
11424       if (ast_strlen_zero(cid))
11425          break;
11426 
11427       ast_callerid_parse(cid, &name, &num);
11428       if (!num) {
11429          ast_verb(3, "No CID number available, no reply sent\n");
11430          if (!res)
11431             res = ast_play_and_wait(chan, "vm-nonumber");
11432          ast_config_destroy(msg_cfg);
11433          return res;
11434       } else {
11435          struct ast_vm_user vmu2;
11436          if (find_user(&vmu2, vmu->context, num)) {
11437             struct leave_vm_options leave_options;
11438             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11439             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11440 
11441             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11442             
11443             memset(&leave_options, 0, sizeof(leave_options));
11444             leave_options.record_gain = record_gain;
11445             res = leave_voicemail(chan, mailbox, &leave_options);
11446             if (!res)
11447                res = 't';
11448             ast_config_destroy(msg_cfg);
11449             return res;
11450          } else {
11451             /* Sender has no mailbox, can't reply */
11452             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11453             ast_play_and_wait(chan, "vm-nobox");
11454             res = 't';
11455             ast_config_destroy(msg_cfg);
11456             return res;
11457          }
11458       } 
11459       res = 0;
11460 
11461       break;
11462    }
11463 
11464 #ifndef IMAP_STORAGE
11465    ast_config_destroy(msg_cfg);
11466 
11467    if (!res) {
11468       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11469       vms->heard[msg] = 1;
11470       res = wait_file(chan, vms, vms->fn);
11471    }
11472 #endif
11473    return res;
11474 }
11475 
11476 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11477          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11478          signed char record_gain, struct vm_state *vms, char *flag)
11479 {
11480    /* Record message & let caller review or re-record it, or set options if applicable */
11481    int res = 0;
11482    int cmd = 0;
11483    int max_attempts = 3;
11484    int attempts = 0;
11485    int recorded = 0;
11486    int msg_exists = 0;
11487    signed char zero_gain = 0;
11488    char tempfile[PATH_MAX];
11489    char *acceptdtmf = "#";
11490    char *canceldtmf = "";
11491 
11492    /* Note that urgent and private are for flagging messages as such in the future */
11493 
11494    /* barf if no pointer passed to store duration in */
11495    if (duration == NULL) {
11496       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11497       return -1;
11498    }
11499 
11500    if (!outsidecaller)
11501       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11502    else
11503       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11504 
11505    cmd = '3';  /* Want to start by recording */
11506 
11507    while ((cmd >= 0) && (cmd != 't')) {
11508       switch (cmd) {
11509       case '1':
11510          if (!msg_exists) {
11511             /* In this case, 1 is to record a message */
11512             cmd = '3';
11513             break;
11514          } else {
11515             /* Otherwise 1 is to save the existing message */
11516             ast_verb(3, "Saving message as is\n");
11517             if (!outsidecaller) 
11518                ast_filerename(tempfile, recordfile, NULL);
11519             ast_stream_and_wait(chan, "vm-msgsaved", "");
11520             if (!outsidecaller) {
11521                /* Saves to IMAP server only if imapgreeting=yes */
11522                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11523                DISPOSE(recordfile, -1);
11524             }
11525             cmd = 't';
11526             return res;
11527          }
11528       case '2':
11529          /* Review */
11530          ast_verb(3, "Reviewing the message\n");
11531          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11532          break;
11533       case '3':
11534          msg_exists = 0;
11535          /* Record */
11536          if (recorded == 1) 
11537             ast_verb(3, "Re-recording the message\n");
11538          else  
11539             ast_verb(3, "Recording the message\n");
11540          
11541          if (recorded && outsidecaller) {
11542             cmd = ast_play_and_wait(chan, INTRO);
11543             cmd = ast_play_and_wait(chan, "beep");
11544          }
11545          recorded = 1;
11546          /* 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 */
11547          if (record_gain)
11548             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11549          if (ast_test_flag(vmu, VM_OPERATOR))
11550             canceldtmf = "0";
11551          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11552          if (record_gain)
11553             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11554          if (cmd == -1) {
11555             /* User has hung up, no options to give */
11556             if (!outsidecaller) {
11557                /* user was recording a greeting and they hung up, so let's delete the recording. */
11558                ast_filedelete(tempfile, NULL);
11559             }     
11560             return cmd;
11561          }
11562          if (cmd == '0') {
11563             break;
11564          } else if (cmd == '*') {
11565             break;
11566 #if 0
11567          } else if (vmu->review && (*duration < 5)) {
11568             /* Message is too short */
11569             ast_verb(3, "Message too short\n");
11570             cmd = ast_play_and_wait(chan, "vm-tooshort");
11571             cmd = ast_filedelete(tempfile, NULL);
11572             break;
11573          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11574             /* Message is all silence */
11575             ast_verb(3, "Nothing recorded\n");
11576             cmd = ast_filedelete(tempfile, NULL);
11577             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11578             if (!cmd)
11579                cmd = ast_play_and_wait(chan, "vm-speakup");
11580             break;
11581 #endif
11582          } else {
11583             /* If all is well, a message exists */
11584             msg_exists = 1;
11585             cmd = 0;
11586          }
11587          break;
11588       case '4':
11589          if (outsidecaller) {  /* only mark vm messages */
11590             /* Mark Urgent */
11591             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11592                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11593                ast_debug(1000, "This message is too urgent!\n");
11594                res = ast_play_and_wait(chan, "vm-marked-urgent");
11595                strcpy(flag, "Urgent");
11596             } else if (flag) {
11597                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11598                res = ast_play_and_wait(chan, "vm-urgent-removed");
11599                strcpy(flag, "");
11600             } else {
11601                ast_play_and_wait(chan, "vm-sorry");
11602             }
11603             cmd = 0;
11604          } else {
11605             cmd = ast_play_and_wait(chan, "vm-sorry");
11606          }
11607          break;
11608       case '5':
11609       case '6':
11610       case '7':
11611       case '8':
11612       case '9':
11613       case '*':
11614       case '#':
11615          cmd = ast_play_and_wait(chan, "vm-sorry");
11616          break;
11617 #if 0 
11618 /*  XXX Commented out for the moment because of the dangers of deleting
11619     a message while recording (can put the message numbers out of sync) */
11620       case '*':
11621          /* Cancel recording, delete message, offer to take another message*/
11622          cmd = ast_play_and_wait(chan, "vm-deleted");
11623          cmd = ast_filedelete(tempfile, NULL);
11624          if (outsidecaller) {
11625             res = vm_exec(chan, NULL);
11626             return res;
11627          }
11628          else
11629             return 1;
11630 #endif
11631       case '0':
11632          if (!ast_test_flag(vmu, VM_OPERATOR)) {
11633             cmd = ast_play_and_wait(chan, "vm-sorry");
11634             break;
11635          }
11636          if (msg_exists || recorded) {
11637             cmd = ast_play_and_wait(chan, "vm-saveoper");
11638             if (!cmd)
11639                cmd = ast_waitfordigit(chan, 3000);
11640             if (cmd == '1') {
11641                ast_play_and_wait(chan, "vm-msgsaved");
11642                cmd = '0';
11643             } else if (cmd == '4') {
11644                if (flag) {
11645                   ast_play_and_wait(chan, "vm-marked-urgent");
11646                   strcpy(flag, "Urgent");
11647                }
11648                ast_play_and_wait(chan, "vm-msgsaved");
11649                cmd = '0';
11650             } else {
11651                ast_play_and_wait(chan, "vm-deleted");
11652                DELETE(recordfile, -1, recordfile, vmu);
11653                cmd = '0';
11654             }
11655          }
11656          return cmd;
11657       default:
11658          /* If the caller is an ouside caller, and the review option is enabled,
11659             allow them to review the message, but let the owner of the box review
11660             their OGM's */
11661          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11662             return cmd;
11663          if (msg_exists) {
11664             cmd = ast_play_and_wait(chan, "vm-review");
11665             if (!cmd && outsidecaller) {
11666                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11667                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11668                } else if (flag) {
11669                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11670                }
11671             }
11672          } else {
11673             cmd = ast_play_and_wait(chan, "vm-torerecord");
11674             if (!cmd)
11675                cmd = ast_waitfordigit(chan, 600);
11676          }
11677          
11678          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11679             cmd = ast_play_and_wait(chan, "vm-reachoper");
11680             if (!cmd)
11681                cmd = ast_waitfordigit(chan, 600);
11682          }
11683 #if 0
11684          if (!cmd)
11685             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11686 #endif
11687          if (!cmd)
11688             cmd = ast_waitfordigit(chan, 6000);
11689          if (!cmd) {
11690             attempts++;
11691          }
11692          if (attempts > max_attempts) {
11693             cmd = 't';
11694          }
11695       }
11696    }
11697    if (outsidecaller)
11698       ast_play_and_wait(chan, "vm-goodbye");
11699    if (cmd == 't')
11700       cmd = 0;
11701    return cmd;
11702 }
11703 
11704 /* This is a workaround so that menuselect displays a proper description
11705  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11706  */
11707 
11708 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11709       .load = load_module,
11710       .unload = unload_module,
11711       .reload = reload,
11712       );

Generated on 8 Apr 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1