Wed Mar 3 22:35:54 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: 211569 $")
00093 
00094 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00095 #include <sys/time.h>
00096 #include <sys/stat.h>
00097 #include <sys/mman.h>
00098 #include <time.h>
00099 #include <dirent.h>
00100 
00101 #include "asterisk/logger.h"
00102 #include "asterisk/lock.h"
00103 #include "asterisk/file.h"
00104 #include "asterisk/channel.h"
00105 #include "asterisk/pbx.h"
00106 #include "asterisk/config.h"
00107 #include "asterisk/say.h"
00108 #include "asterisk/module.h"
00109 #include "asterisk/adsi.h"
00110 #include "asterisk/app.h"
00111 #include "asterisk/manager.h"
00112 #include "asterisk/dsp.h"
00113 #include "asterisk/localtime.h"
00114 #include "asterisk/cli.h"
00115 #include "asterisk/utils.h"
00116 #include "asterisk/stringfields.h"
00117 #include "asterisk/smdi.h"
00118 #include "asterisk/event.h"
00119 #include "asterisk/taskprocessor.h"
00120 
00121 #ifdef ODBC_STORAGE
00122 #include "asterisk/res_odbc.h"
00123 #endif
00124 
00125 #ifdef IMAP_STORAGE
00126 #include "asterisk/threadstorage.h"
00127 
00128 static char imapserver[48];
00129 static char imapport[8];
00130 static char imapflags[128];
00131 static char imapfolder[64];
00132 static char imapparentfolder[64] = "\0";
00133 static char greetingfolder[64];
00134 static char authuser[32];
00135 static char authpassword[42];
00136 
00137 static int expungeonhangup = 1;
00138 static int imapgreetings = 0;
00139 static char delimiter = '\0';
00140 
00141 struct vm_state;
00142 struct ast_vm_user;
00143 
00144 AST_THREADSTORAGE(ts_vmstate);
00145 
00146 /* Forward declarations for IMAP */
00147 static int init_mailstream(struct vm_state *vms, int box);
00148 static void write_file(char *filename, char *buffer, unsigned long len);
00149 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00150 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
00151 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00152 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00153 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00154 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00155 static void vmstate_insert(struct vm_state *vms);
00156 static void vmstate_delete(struct vm_state *vms);
00157 static void set_update(MAILSTREAM * stream);
00158 static void init_vm_state(struct vm_state *vms);
00159 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00160 static void get_mailbox_delimiter(MAILSTREAM *stream);
00161 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00162 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00163 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
00164 static void update_messages_by_imapuser(const char *user, unsigned long number);
00165 static int vm_delete(char *file);
00166 
00167 static int imap_remove_file (char *dir, int msgnum);
00168 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00169 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00170 static void check_quota(struct vm_state *vms, char *mailbox);
00171 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00172 struct vmstate {
00173    struct vm_state *vms;
00174    AST_LIST_ENTRY(vmstate) list;
00175 };
00176 
00177 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00178 
00179 #endif
00180 
00181 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00182 
00183 #define COMMAND_TIMEOUT 5000
00184 /* Don't modify these here; set your umask at runtime instead */
00185 #define  VOICEMAIL_DIR_MODE   0777
00186 #define  VOICEMAIL_FILE_MODE  0666
00187 #define  CHUNKSIZE   65536
00188 
00189 #define VOICEMAIL_CONFIG "voicemail.conf"
00190 #define ASTERISK_USERNAME "asterisk"
00191 
00192 /* Define fast-forward, pause, restart, and reverse keys
00193    while listening to a voicemail message - these are
00194    strings, not characters */
00195 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00196 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00197 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00198 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00199 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00200 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00201 
00202 /* Default mail command to mail voicemail. Change it with the
00203     mailcmd= command in voicemail.conf */
00204 #define SENDMAIL "/usr/sbin/sendmail -t"
00205 
00206 #define INTRO "vm-intro"
00207 
00208 #define MAXMSG 100
00209 #define MAXMSGLIMIT 9999
00210 
00211 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00212 
00213 #define BASELINELEN 72
00214 #define BASEMAXINLINE 256
00215 #define eol "\r\n"
00216 
00217 #define MAX_DATETIME_FORMAT   512
00218 #define MAX_NUM_CID_CONTEXTS 10
00219 
00220 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00221 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00222 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00223 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00224 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00225 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00226 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00227 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00228 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00229 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00230 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00231 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00232 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00233 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00234 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00235 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00236 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00237 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00238 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00239 #define ERROR_LOCK_PATH  -100
00240 
00241 
00242 enum {
00243    NEW_FOLDER,
00244    OLD_FOLDER,
00245    WORK_FOLDER,
00246    FAMILY_FOLDER,
00247    FRIENDS_FOLDER,
00248    GREETINGS_FOLDER
00249 } vm_box;
00250 
00251 enum {
00252    OPT_SILENT =           (1 << 0),
00253    OPT_BUSY_GREETING =    (1 << 1),
00254    OPT_UNAVAIL_GREETING = (1 << 2),
00255    OPT_RECORDGAIN =       (1 << 3),
00256    OPT_PREPEND_MAILBOX =  (1 << 4),
00257    OPT_AUTOPLAY =         (1 << 6),
00258    OPT_DTMFEXIT =         (1 << 7),
00259    OPT_MESSAGE_Urgent =   (1 << 8),
00260    OPT_MESSAGE_PRIORITY = (1 << 9)
00261 } vm_option_flags;
00262 
00263 enum {
00264    OPT_ARG_RECORDGAIN = 0,
00265    OPT_ARG_PLAYFOLDER = 1,
00266    OPT_ARG_DTMFEXIT   = 2,
00267    /* This *must* be the last value in this enum! */
00268    OPT_ARG_ARRAY_SIZE = 3,
00269 } vm_option_args;
00270 
00271 AST_APP_OPTIONS(vm_app_options, {
00272    AST_APP_OPTION('s', OPT_SILENT),
00273    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00274    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00275    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00276    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00277    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00278    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00279    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00280    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00281 });
00282 
00283 static int load_config(int reload);
00284 
00285 /*! \page vmlang Voicemail Language Syntaxes Supported
00286 
00287    \par Syntaxes supported, not really language codes.
00288    \arg \b en    - English
00289    \arg \b de    - German
00290    \arg \b es    - Spanish
00291    \arg \b fr    - French
00292    \arg \b it    - Italian
00293    \arg \b nl    - Dutch
00294    \arg \b pt    - Portuguese
00295    \arg \b pt_BR - Portuguese (Brazil)
00296    \arg \b gr    - Greek
00297    \arg \b no    - Norwegian
00298    \arg \b se    - Swedish
00299    \arg \b tw    - Chinese (Taiwan)
00300    \arg \b ua - Ukrainian
00301 
00302 German requires the following additional soundfile:
00303 \arg \b 1F  einE (feminine)
00304 
00305 Spanish requires the following additional soundfile:
00306 \arg \b 1M      un (masculine)
00307 
00308 Dutch, Portuguese & Spanish require the following additional soundfiles:
00309 \arg \b vm-INBOXs singular of 'new'
00310 \arg \b vm-Olds      singular of 'old/heard/read'
00311 
00312 NB these are plural:
00313 \arg \b vm-INBOX  nieuwe (nl)
00314 \arg \b vm-Old    oude (nl)
00315 
00316 Polish uses:
00317 \arg \b vm-new-a  'new', feminine singular accusative
00318 \arg \b vm-new-e  'new', feminine plural accusative
00319 \arg \b vm-new-ych   'new', feminine plural genitive
00320 \arg \b vm-old-a  'old', feminine singular accusative
00321 \arg \b vm-old-e  'old', feminine plural accusative
00322 \arg \b vm-old-ych   'old', feminine plural genitive
00323 \arg \b digits/1-a   'one', not always same as 'digits/1'
00324 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00325 
00326 Swedish uses:
00327 \arg \b vm-nytt      singular of 'new'
00328 \arg \b vm-nya    plural of 'new'
00329 \arg \b vm-gammalt   singular of 'old'
00330 \arg \b vm-gamla  plural of 'old'
00331 \arg \b digits/ett   'one', not always same as 'digits/1'
00332 
00333 Norwegian uses:
00334 \arg \b vm-ny     singular of 'new'
00335 \arg \b vm-nye    plural of 'new'
00336 \arg \b vm-gammel singular of 'old'
00337 \arg \b vm-gamle  plural of 'old'
00338 
00339 Dutch also uses:
00340 \arg \b nl-om     'at'?
00341 
00342 Spanish also uses:
00343 \arg \b vm-youhaveno
00344 
00345 Italian requires the following additional soundfile:
00346 
00347 For vm_intro_it:
00348 \arg \b vm-nuovo  new
00349 \arg \b vm-nuovi  new plural
00350 \arg \b vm-vecchio   old
00351 \arg \b vm-vecchi old plural
00352 
00353 Chinese (Taiwan) requires the following additional soundfile:
00354 \arg \b vm-tong      A class-word for call (tong1)
00355 \arg \b vm-ri     A class-word for day (ri4)
00356 \arg \b vm-you    You (ni3)
00357 \arg \b vm-haveno   Have no (mei2 you3)
00358 \arg \b vm-have     Have (you3)
00359 \arg \b vm-listen   To listen (yao4 ting1)
00360 
00361 
00362 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00363 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00364 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00365 
00366 */
00367 
00368 struct baseio {
00369    int iocp;
00370    int iolen;
00371    int linelength;
00372    int ateof;
00373    unsigned char iobuf[BASEMAXINLINE];
00374 };
00375 
00376 /*! Structure for linked list of users 
00377  * Use ast_vm_user_destroy() to free one of these structures. */
00378 struct ast_vm_user {
00379    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00380    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00381    char password[80];               /*!< Secret pin code, numbers only */
00382    char fullname[80];               /*!< Full name, for directory app */
00383    char email[80];                  /*!< E-mail address */
00384    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00385    char serveremail[80];            /*!< From: Mail address */
00386    char mailcmd[160];               /*!< Configurable mail command */
00387    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00388    char zonetag[80];                /*!< Time zone */
00389    char callback[80];
00390    char dialout[80];
00391    char uniqueid[80];               /*!< Unique integer identifier */
00392    char exit[80];
00393    char attachfmt[20];              /*!< Attachment format */
00394    unsigned int flags;              /*!< VM_ flags */ 
00395    int saydurationm;
00396    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00397    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00398    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00399 #ifdef IMAP_STORAGE
00400    char imapuser[80];               /*!< IMAP server login */
00401    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00402    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00403 #endif
00404    double volgain;                  /*!< Volume gain for voicemails sent via email */
00405    AST_LIST_ENTRY(ast_vm_user) list;
00406 };
00407 
00408 /*! Voicemail time zones */
00409 struct vm_zone {
00410    AST_LIST_ENTRY(vm_zone) list;
00411    char name[80];
00412    char timezone[80];
00413    char msg_format[512];
00414 };
00415 
00416 #define VMSTATE_MAX_MSG_ARRAY 256
00417 
00418 /*! Voicemail mailbox state */
00419 struct vm_state {
00420    char curbox[80];
00421    char username[80];
00422    char context[80];
00423    char curdir[PATH_MAX];
00424    char vmbox[PATH_MAX];
00425    char fn[PATH_MAX];
00426    char intro[PATH_MAX];
00427    int *deleted;
00428    int *heard;
00429    int curmsg;
00430    int lastmsg;
00431    int newmessages;
00432    int oldmessages;
00433    int urgentmessages;
00434    int starting;
00435    int repeats;
00436 #ifdef IMAP_STORAGE
00437    ast_mutex_t lock;
00438    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00439    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00440    MAILSTREAM *mailstream;
00441    int vmArrayIndex;
00442    char imapuser[80];                   /*!< IMAP server login */
00443    int interactive;
00444    char introfn[PATH_MAX];              /*!< Name of prepended file */
00445    unsigned int quota_limit;
00446    unsigned int quota_usage;
00447    struct vm_state *persist_vms;
00448 #endif
00449 };
00450 
00451 #ifdef ODBC_STORAGE
00452 static char odbc_database[80];
00453 static char odbc_table[80];
00454 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00455 #define DISPOSE(a,b) remove_file(a,b)
00456 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00457 #define EXISTS(a,b,c,d) (message_exists(a,b))
00458 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00459 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00460 #define DELETE(a,b,c,d) (delete_file(a,b))
00461 #else
00462 #ifdef IMAP_STORAGE
00463 #define DISPOSE(a,b) (imap_remove_file(a,b))
00464 #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))
00465 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00466 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00467 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00468 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00469 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
00470 #else
00471 #define RETRIEVE(a,b,c,d)
00472 #define DISPOSE(a,b)
00473 #define STORE(a,b,c,d,e,f,g,h,i,j)
00474 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00475 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00476 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00477 #define DELETE(a,b,c,d) (vm_delete(c))
00478 #endif
00479 #endif
00480 
00481 static char VM_SPOOL_DIR[PATH_MAX];
00482 
00483 static char ext_pass_cmd[128];
00484 static char ext_pass_check_cmd[128];
00485 
00486 static int my_umask;
00487 
00488 #define PWDCHANGE_INTERNAL (1 << 1)
00489 #define PWDCHANGE_EXTERNAL (1 << 2)
00490 static int pwdchange = PWDCHANGE_INTERNAL;
00491 
00492 #ifdef ODBC_STORAGE
00493 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00494 #else
00495 # ifdef IMAP_STORAGE
00496 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00497 # else
00498 # define tdesc "Comedian Mail (Voicemail System)"
00499 # endif
00500 #endif
00501 
00502 static char userscontext[AST_MAX_EXTENSION] = "default";
00503 
00504 static char *addesc = "Comedian Mail";
00505 
00506 static char *synopsis_vm = "Leave a Voicemail message";
00507 
00508 static char *descrip_vm =
00509    "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
00510    "application allows the calling party to leave a message for the specified\n"
00511    "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00512    "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00513    "specified mailbox does not exist.\n"
00514    "  The Voicemail application will exit if any of the following DTMF digits are\n"
00515    "received:\n"
00516    "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00517    "    * - Jump to the 'a' extension in the current dialplan context.\n"
00518    "  This application will set the following channel variable upon completion:\n"
00519    "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00520    "               application. The possible values are:\n"
00521    "               SUCCESS | USEREXIT | FAILED\n\n"
00522    "  Options:\n"
00523    "    b    - Play the 'busy' greeting to the calling party.\n"
00524    "    d([c]) - Accept digits for a new extension in context c, if played during\n"
00525    "             the greeting.  Context defaults to the current context.\n"
00526    "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00527    "           message. The units are whole-number decibels (dB).\n"
00528    "           Only works on supported technologies, which is DAHDI only.\n"
00529    "    s    - Skip the playback of instructions for leaving a message to the\n"
00530    "           calling party.\n"
00531    "    u    - Play the 'unavailable' greeting.\n"
00532    "    U    - Mark message as Urgent.\n"
00533    "    P    - Mark message as PRIORITY.\n";
00534 
00535 static char *synopsis_vmain = "Check Voicemail messages";
00536 
00537 static char *descrip_vmain =
00538    "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
00539    "calling party to check voicemail messages. A specific mailbox, and optional\n"
00540    "corresponding context, may be specified. If a mailbox is not provided, the\n"
00541    "calling party will be prompted to enter one. If a context is not specified,\n"
00542    "the 'default' context will be used.\n\n"
00543    "  Options:\n"
00544    "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00545    "           is entered by the caller.\n"
00546    "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00547    "           message. The units are whole-number decibels (dB).\n"
00548    "    s    - Skip checking the passcode for the mailbox.\n"
00549    "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00550    "           Defaults to INBOX\n";
00551 
00552 static char *synopsis_vm_box_exists =
00553 "Check to see if Voicemail mailbox exists";
00554 
00555 static char *descrip_vm_box_exists =
00556    "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
00557    "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00558    "will be used.\n"
00559    "  This application will set the following channel variable upon completion:\n"
00560    "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00561    "                        MailboxExists application. Possible values include:\n"
00562    "                        SUCCESS | FAILED\n\n"
00563    "  Options: (none)\n";
00564 
00565 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
00566 
00567 static char *descrip_vmauthenticate =
00568    "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
00569    "same way as the Authenticate application, but the passwords are taken from\n"
00570    "voicemail.conf.\n"
00571    "  If the mailbox is specified, only that mailbox's password will be considered\n"
00572    "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00573    "be set with the authenticated mailbox.\n\n"
00574    "  Options:\n"
00575    "    s - Skip playing the initial prompts.\n";
00576 
00577 /* Leave a message */
00578 static char *app = "VoiceMail";
00579 
00580 /* Check mail, control, etc */
00581 static char *app2 = "VoiceMailMain";
00582 
00583 static char *app3 = "MailboxExists";
00584 static char *app4 = "VMAuthenticate";
00585 
00586 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00587 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00588 static char zonetag[80];
00589 static int maxsilence;
00590 static int maxmsg;
00591 static int maxdeletedmsg;
00592 static int silencethreshold = 128;
00593 static char serveremail[80];
00594 static char mailcmd[160];  /* Configurable mail cmd */
00595 static char externnotify[160]; 
00596 static struct ast_smdi_interface *smdi_iface = NULL;
00597 static char vmfmts[80];
00598 static double volgain;
00599 static int vmminsecs;
00600 static int vmmaxsecs;
00601 static int maxgreet;
00602 static int skipms;
00603 static int maxlogins;
00604 static int minpassword;
00605 
00606 /*! Poll mailboxes for changes since there is something external to
00607  *  app_voicemail that may change them. */
00608 static unsigned int poll_mailboxes;
00609 
00610 /*! Polling frequency */
00611 static unsigned int poll_freq;
00612 /*! By default, poll every 30 seconds */
00613 #define DEFAULT_POLL_FREQ 30
00614 
00615 AST_MUTEX_DEFINE_STATIC(poll_lock);
00616 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00617 static pthread_t poll_thread = AST_PTHREADT_NULL;
00618 static unsigned char poll_thread_run;
00619 
00620 /*! Subscription to ... MWI event subscriptions */
00621 static struct ast_event_sub *mwi_sub_sub;
00622 /*! Subscription to ... MWI event un-subscriptions */
00623 static struct ast_event_sub *mwi_unsub_sub;
00624 
00625 /*!
00626  * \brief An MWI subscription
00627  *
00628  * This is so we can keep track of which mailboxes are subscribed to.
00629  * This way, we know which mailboxes to poll when the pollmailboxes
00630  * option is being used.
00631  */
00632 struct mwi_sub {
00633    AST_RWLIST_ENTRY(mwi_sub) entry;
00634    int old_urgent;
00635    int old_new;
00636    int old_old;
00637    uint32_t uniqueid;
00638    char mailbox[1];
00639 };
00640 
00641 struct mwi_sub_task {
00642    const char *mailbox;
00643    const char *context;
00644    uint32_t uniqueid;
00645 };
00646 
00647 static struct ast_taskprocessor *mwi_subscription_tps;
00648 
00649 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00650 
00651 /* custom audio control prompts for voicemail playback */
00652 static char listen_control_forward_key[12];
00653 static char listen_control_reverse_key[12];
00654 static char listen_control_pause_key[12];
00655 static char listen_control_restart_key[12];
00656 static char listen_control_stop_key[12];
00657 
00658 /* custom password sounds */
00659 static char vm_password[80] = "vm-password";
00660 static char vm_newpassword[80] = "vm-newpassword";
00661 static char vm_passchanged[80] = "vm-passchanged";
00662 static char vm_reenterpassword[80] = "vm-reenterpassword";
00663 static char vm_mismatch[80] = "vm-mismatch";
00664 static char vm_invalid_password[80] = "vm-invalid-password";
00665 
00666 static struct ast_flags globalflags = {0};
00667 
00668 static int saydurationminfo;
00669 
00670 static char dialcontext[AST_MAX_CONTEXT] = "";
00671 static char callcontext[AST_MAX_CONTEXT] = "";
00672 static char exitcontext[AST_MAX_CONTEXT] = "";
00673 
00674 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00675 
00676 
00677 static char *emailbody = NULL;
00678 static char *emailsubject = NULL;
00679 static char *pagerbody = NULL;
00680 static char *pagersubject = NULL;
00681 static char fromstring[100];
00682 static char pagerfromstring[100];
00683 static char charset[32] = "ISO-8859-1";
00684 
00685 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00686 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00687 static int adsiver = 1;
00688 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00689 
00690 /* Forward declarations - generic */
00691 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00692 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);
00693 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00694 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00695          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00696          signed char record_gain, struct vm_state *vms, char *flag);
00697 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00698 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00699 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);
00700 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);
00701 static void apply_options(struct ast_vm_user *vmu, const char *options);
00702 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);
00703 static int is_valid_dtmf(const char *key);
00704 
00705 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00706 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00707 #endif
00708 
00709 static char *strip_control(const char *input, char *buf, size_t buflen)
00710 {
00711    char *bufptr = buf;
00712    for (; *input; input++) {
00713       if (*input < 32) {
00714          continue;
00715       }
00716       *bufptr++ = *input;
00717       if (bufptr == buf + buflen - 1) {
00718          break;
00719       }
00720    }
00721    *bufptr = '\0';
00722    return buf;
00723 }
00724 
00725 
00726 /*!
00727  * \brief Sets default voicemail system options to a voicemail user.
00728  *
00729  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00730  * - all the globalflags
00731  * - the saydurationminfo
00732  * - the callcontext
00733  * - the dialcontext
00734  * - the exitcontext
00735  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00736  * - volume gain.
00737  */
00738 static void populate_defaults(struct ast_vm_user *vmu)
00739 {
00740    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00741    if (saydurationminfo)
00742       vmu->saydurationm = saydurationminfo;
00743    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00744    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00745    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00746    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00747    if (vmmaxsecs)
00748       vmu->maxsecs = vmmaxsecs;
00749    if (maxmsg)
00750       vmu->maxmsg = maxmsg;
00751    if (maxdeletedmsg)
00752       vmu->maxdeletedmsg = maxdeletedmsg;
00753    vmu->volgain = volgain;
00754 }
00755 
00756 /*!
00757  * \brief Sets a a specific property value.
00758  * \param vmu The voicemail user object to work with.
00759  * \param var The name of the property to be set.
00760  * \param value The value to be set to the property.
00761  * 
00762  * The property name must be one of the understood properties. See the source for details.
00763  */
00764 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00765 {
00766    int x;
00767    if (!strcasecmp(var, "attach")) {
00768       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00769    } else if (!strcasecmp(var, "attachfmt")) {
00770       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00771    } else if (!strcasecmp(var, "serveremail")) {
00772       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00773    } else if (!strcasecmp(var, "language")) {
00774       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00775    } else if (!strcasecmp(var, "tz")) {
00776       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00777 #ifdef IMAP_STORAGE
00778    } else if (!strcasecmp(var, "imapuser")) {
00779       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00780    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00781       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00782    } else if (!strcasecmp(var, "imapvmshareid")) {
00783       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00784 #endif
00785    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00786       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00787    } else if (!strcasecmp(var, "saycid")){
00788       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00789    } else if (!strcasecmp(var,"sendvoicemail")){
00790       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00791    } else if (!strcasecmp(var, "review")){
00792       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00793    } else if (!strcasecmp(var, "tempgreetwarn")){
00794       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00795    } else if (!strcasecmp(var, "messagewrap")){
00796       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00797    } else if (!strcasecmp(var, "operator")) {
00798       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00799    } else if (!strcasecmp(var, "envelope")){
00800       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00801    } else if (!strcasecmp(var, "moveheard")){
00802       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00803    } else if (!strcasecmp(var, "sayduration")){
00804       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00805    } else if (!strcasecmp(var, "saydurationm")){
00806       if (sscanf(value, "%30d", &x) == 1) {
00807          vmu->saydurationm = x;
00808       } else {
00809          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
00810       }
00811    } else if (!strcasecmp(var, "forcename")){
00812       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00813    } else if (!strcasecmp(var, "forcegreetings")){
00814       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00815    } else if (!strcasecmp(var, "callback")) {
00816       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00817    } else if (!strcasecmp(var, "dialout")) {
00818       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00819    } else if (!strcasecmp(var, "exitcontext")) {
00820       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00821    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
00822       if (vmu->maxsecs <= 0) {
00823          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
00824          vmu->maxsecs = vmmaxsecs;
00825       } else {
00826          vmu->maxsecs = atoi(value);
00827       }
00828       if (!strcasecmp(var, "maxmessage"))
00829          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
00830    } else if (!strcasecmp(var, "maxmsg")) {
00831       vmu->maxmsg = atoi(value);
00832       if (vmu->maxmsg <= 0) {
00833          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
00834          vmu->maxmsg = MAXMSG;
00835       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00836          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00837          vmu->maxmsg = MAXMSGLIMIT;
00838       }
00839    } else if (!strcasecmp(var, "backupdeleted")) {
00840       if (sscanf(value, "%30d", &x) == 1)
00841          vmu->maxdeletedmsg = x;
00842       else if (ast_true(value))
00843          vmu->maxdeletedmsg = MAXMSG;
00844       else
00845          vmu->maxdeletedmsg = 0;
00846 
00847       if (vmu->maxdeletedmsg < 0) {
00848          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
00849          vmu->maxdeletedmsg = MAXMSG;
00850       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
00851          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
00852          vmu->maxdeletedmsg = MAXMSGLIMIT;
00853       }
00854    } else if (!strcasecmp(var, "volgain")) {
00855       sscanf(value, "%30lf", &vmu->volgain);
00856    } else if (!strcasecmp(var, "options")) {
00857       apply_options(vmu, value);
00858    }
00859 }
00860 
00861 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
00862 {
00863    int fds[2], pid = 0;
00864 
00865    memset(buf, 0, len);
00866 
00867    if (pipe(fds)) {
00868       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
00869    } else {
00870       /* good to go*/
00871       pid = ast_safe_fork(0);
00872 
00873       if (pid < 0) {
00874          /* ok maybe not */
00875          close(fds[0]);
00876          close(fds[1]);
00877          snprintf(buf, len, "FAILURE: Fork failed");
00878       } else if (pid) {
00879          /* parent */
00880          close(fds[1]);
00881          if (read(fds[0], buf, len) < 0) {
00882             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00883          }
00884          close(fds[0]);
00885       } else {
00886          /*  child */
00887          AST_DECLARE_APP_ARGS(arg,
00888             AST_APP_ARG(v)[20];
00889          );
00890          char *mycmd = ast_strdupa(command);
00891 
00892          close(fds[0]);
00893          dup2(fds[1], STDOUT_FILENO);
00894          close(fds[1]);
00895          ast_close_fds_above_n(STDOUT_FILENO);
00896 
00897          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
00898 
00899          execv(arg.v[0], arg.v); 
00900          printf("FAILURE: %s", strerror(errno));
00901          _exit(0);
00902       }
00903    }
00904    return buf;
00905 }
00906 
00907 /*!
00908  * \brief Check that password meets minimum required length
00909  * \param vmu The voicemail user to change the password for.
00910  * \param password The password string to check
00911  *
00912  * \return zero on ok, 1 on not ok.
00913  */
00914 static int check_password(struct ast_vm_user *vmu, char *password)
00915 {
00916    /* check minimum length */
00917    if (strlen(password) < minpassword)
00918       return 1;
00919    if (!ast_strlen_zero(ext_pass_check_cmd)) {
00920       char cmd[255], buf[255];
00921 
00922       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
00923 
00924       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
00925       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
00926          ast_debug(5, "Result: %s\n", buf);
00927          if (!strncasecmp(buf, "VALID", 5)) {
00928             ast_debug(3, "Passed password check: '%s'\n", buf);
00929             return 0;
00930          } else if (!strncasecmp(buf, "FAILURE", 7)) {
00931             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
00932             return 0;
00933          } else {
00934             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
00935             return 1;
00936          }
00937       }
00938    }
00939    return 0;
00940 }
00941 
00942 /*! 
00943  * \brief Performs a change of the voicemail passowrd in the realtime engine.
00944  * \param vmu The voicemail user to change the password for.
00945  * \param password The new value to be set to the password for this user.
00946  * 
00947  * This only works if the voicemail user has a unique id, and if there is a realtime engine configured.
00948  * This is called from the (top level) vm_change_password.
00949  *
00950  * \return zero on success, -1 on error.
00951  */
00952 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00953 {
00954    int res;
00955    if (!ast_strlen_zero(vmu->uniqueid)) {
00956       if (strlen(password) > 10) {
00957          ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
00958       }
00959       res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL);
00960       if (res > 0) {
00961          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00962          res = 0;
00963       } else if (!res) {
00964          res = -1;
00965       }
00966       return res;
00967    }
00968    return -1;
00969 }
00970 
00971 /*!
00972  * \brief Destructively Parse options and apply.
00973  */
00974 static void apply_options(struct ast_vm_user *vmu, const char *options)
00975 {  
00976    char *stringp;
00977    char *s;
00978    char *var, *value;
00979    stringp = ast_strdupa(options);
00980    while ((s = strsep(&stringp, "|"))) {
00981       value = s;
00982       if ((var = strsep(&value, "=")) && value) {
00983          apply_option(vmu, var, value);
00984       }
00985    }  
00986 }
00987 
00988 /*!
00989  * \brief Loads the options specific to a voicemail user.
00990  * 
00991  * This is called when a vm_user structure is being set up, such as from load_options.
00992  */
00993 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
00994 {
00995    struct ast_variable *tmp;
00996    tmp = var;
00997    while (tmp) {
00998       if (!strcasecmp(tmp->name, "vmsecret")) {
00999          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
01000       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
01001          if (ast_strlen_zero(retval->password))
01002             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
01003       } else if (!strcasecmp(tmp->name, "uniqueid")) {
01004          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
01005       } else if (!strcasecmp(tmp->name, "pager")) {
01006          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
01007       } else if (!strcasecmp(tmp->name, "email")) {
01008          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
01009       } else if (!strcasecmp(tmp->name, "fullname")) {
01010          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
01011       } else if (!strcasecmp(tmp->name, "context")) {
01012          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
01013 #ifdef IMAP_STORAGE
01014       } else if (!strcasecmp(tmp->name, "imapuser")) {
01015          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
01016       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
01017          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
01018       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01019          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01020 #endif
01021       } else
01022          apply_option(retval, tmp->name, tmp->value);
01023       tmp = tmp->next;
01024    } 
01025 }
01026 
01027 /*!
01028  * \brief Determines if a DTMF key entered is valid.
01029  * \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.
01030  *
01031  * Tests the character entered against the set of valid DTMF characters. 
01032  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01033  */
01034 static int is_valid_dtmf(const char *key)
01035 {
01036    int i;
01037    char *local_key = ast_strdupa(key);
01038 
01039    for (i = 0; i < strlen(key); ++i) {
01040       if (!strchr(VALID_DTMF, *local_key)) {
01041          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01042          return 0;
01043       }
01044       local_key++;
01045    }
01046    return 1;
01047 }
01048 
01049 /*!
01050  * \brief Finds a voicemail user from the realtime engine.
01051  * \param ivm
01052  * \param context
01053  * \param mailbox
01054  *
01055  * 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.
01056  *
01057  * \return The ast_vm_user structure for the user that was found.
01058  */
01059 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01060 {
01061    struct ast_variable *var;
01062    struct ast_vm_user *retval;
01063 
01064    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01065       if (!ivm)
01066          ast_set_flag(retval, VM_ALLOCED);   
01067       else
01068          memset(retval, 0, sizeof(*retval));
01069       if (mailbox) 
01070          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01071       populate_defaults(retval);
01072       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01073          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01074       else
01075          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01076       if (var) {
01077          apply_options_full(retval, var);
01078          ast_variables_destroy(var);
01079       } else { 
01080          if (!ivm) 
01081             ast_free(retval);
01082          retval = NULL;
01083       }  
01084    } 
01085    return retval;
01086 }
01087 
01088 /*!
01089  * \brief Finds a voicemail user from the users file or the realtime engine.
01090  * \param ivm
01091  * \param context
01092  * \param mailbox
01093  * 
01094  * \return The ast_vm_user structure for the user that was found.
01095  */
01096 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01097 {
01098    /* This function could be made to generate one from a database, too */
01099    struct ast_vm_user *vmu=NULL, *cur;
01100    AST_LIST_LOCK(&users);
01101 
01102    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01103       context = "default";
01104 
01105    AST_LIST_TRAVERSE(&users, cur, list) {
01106       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01107          break;
01108       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01109          break;
01110    }
01111    if (cur) {
01112       /* Make a copy, so that on a reload, we have no race */
01113       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01114          memcpy(vmu, cur, sizeof(*vmu));
01115          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01116          AST_LIST_NEXT(vmu, list) = NULL;
01117       }
01118    } else
01119       vmu = find_user_realtime(ivm, context, mailbox);
01120    AST_LIST_UNLOCK(&users);
01121    return vmu;
01122 }
01123 
01124 /*!
01125  * \brief Resets a user password to a specified password.
01126  * \param context
01127  * \param mailbox
01128  * \param newpass
01129  *
01130  * This does the actual change password work, called by the vm_change_password() function.
01131  *
01132  * \return zero on success, -1 on error.
01133  */
01134 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01135 {
01136    /* This function could be made to generate one from a database, too */
01137    struct ast_vm_user *cur;
01138    int res = -1;
01139    AST_LIST_LOCK(&users);
01140    AST_LIST_TRAVERSE(&users, cur, list) {
01141       if ((!context || !strcasecmp(context, cur->context)) &&
01142          (!strcasecmp(mailbox, cur->mailbox)))
01143             break;
01144    }
01145    if (cur) {
01146       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01147       res = 0;
01148    }
01149    AST_LIST_UNLOCK(&users);
01150    return res;
01151 }
01152 
01153 /*! 
01154  * \brief The handler for the change password option.
01155  * \param vmu The voicemail user to work with.
01156  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01157  * 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.
01158  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01159  */
01160 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01161 {
01162    struct ast_config   *cfg=NULL;
01163    struct ast_variable *var=NULL;
01164    struct ast_category *cat=NULL;
01165    char *category=NULL, *value=NULL, *new=NULL;
01166    const char *tmp=NULL;
01167    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01168    if (!change_password_realtime(vmu, newpassword))
01169       return;
01170 
01171    /* check voicemail.conf */
01172    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
01173       while ((category = ast_category_browse(cfg, category))) {
01174          if (!strcasecmp(category, vmu->context)) {
01175             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01176                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01177                break;
01178             }
01179             value = strstr(tmp,",");
01180             if (!value) {
01181                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01182                break;
01183             }
01184             new = alloca((strlen(value)+strlen(newpassword)+1));
01185             sprintf(new,"%s%s", newpassword, value);
01186             if (!(cat = ast_category_get(cfg, category))) {
01187                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01188                break;
01189             }
01190             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01191          }
01192       }
01193       /* save the results */
01194       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01195       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01196       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01197    }
01198    category = NULL;
01199    var = NULL;
01200    /* check users.conf and update the password stored for the mailbox*/
01201    /* if no vmsecret entry exists create one. */
01202    if ((cfg = ast_config_load("users.conf", config_flags))) {
01203       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01204       while ((category = ast_category_browse(cfg, category))) {
01205          ast_debug(4, "users.conf: %s\n", category);
01206          if (!strcasecmp(category, vmu->mailbox)) {
01207             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01208                ast_debug(3, "looks like we need to make vmsecret!\n");
01209                var = ast_variable_new("vmsecret", newpassword, "");
01210             } 
01211             new = alloca(strlen(newpassword)+1);
01212             sprintf(new, "%s", newpassword);
01213             if (!(cat = ast_category_get(cfg, category))) {
01214                ast_debug(4, "failed to get category!\n");
01215                break;
01216             }
01217             if (!var)      
01218                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01219             else
01220                ast_variable_append(cat, var);
01221          }
01222       }
01223       /* save the results and clean things up */
01224       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01225       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01226       config_text_file_save("users.conf", cfg, "AppVoicemail");
01227    }
01228 }
01229 
01230 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01231 {
01232    char buf[255];
01233    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01234    if (!ast_safe_system(buf)) {
01235       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01236       /* Reset the password in memory, too */
01237       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01238    }
01239 }
01240 
01241 /*! 
01242  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01243  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01244  * \param len The length of the path string that was written out.
01245  * 
01246  * The path is constructed as 
01247  *    VM_SPOOL_DIRcontext/ext/folder
01248  *
01249  * \return zero on success, -1 on error.
01250  */
01251 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01252 {
01253    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01254 }
01255 
01256 /*! 
01257  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01258  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01259  * \param len The length of the path string that was written out.
01260  * 
01261  * The path is constructed as 
01262  *    VM_SPOOL_DIRcontext/ext/folder
01263  *
01264  * \return zero on success, -1 on error.
01265  */
01266 static int make_file(char *dest, const int len, const char *dir, const int num)
01267 {
01268    return snprintf(dest, len, "%s/msg%04d", dir, num);
01269 }
01270 
01271 /* same as mkstemp, but return a FILE * */
01272 static FILE *vm_mkftemp(char *template)
01273 {
01274    FILE *p = NULL;
01275    int pfd = mkstemp(template);
01276    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01277    if (pfd > -1) {
01278       p = fdopen(pfd, "w+");
01279       if (!p) {
01280          close(pfd);
01281          pfd = -1;
01282       }
01283    }
01284    return p;
01285 }
01286 
01287 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01288  * \param dest    String. base directory.
01289  * \param len     Length of dest.
01290  * \param context String. Ignored if is null or empty string.
01291  * \param ext     String. Ignored if is null or empty string.
01292  * \param folder  String. Ignored if is null or empty string. 
01293  * \return -1 on failure, 0 on success.
01294  */
01295 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01296 {
01297    mode_t   mode = VOICEMAIL_DIR_MODE;
01298    int res;
01299 
01300    make_dir(dest, len, context, ext, folder);
01301    if ((res = ast_mkdir(dest, mode))) {
01302       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01303       return -1;
01304    }
01305    return 0;
01306 }
01307 
01308 static const char *mbox(int id)
01309 {
01310    static const char *msgs[] = {
01311 #ifdef IMAP_STORAGE
01312       imapfolder,
01313 #else
01314       "INBOX",
01315 #endif
01316       "Old",
01317       "Work",
01318       "Family",
01319       "Friends",
01320       "Cust1",
01321       "Cust2",
01322       "Cust3",
01323       "Cust4",
01324       "Cust5",
01325       "Deleted",
01326       "Urgent"
01327    };
01328    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
01329 }
01330 
01331 static void free_user(struct ast_vm_user *vmu)
01332 {
01333    if (ast_test_flag(vmu, VM_ALLOCED))
01334       ast_free(vmu);
01335 }
01336 
01337 /* All IMAP-specific functions should go in this block. This
01338  * keeps them from being spread out all over the code */
01339 #ifdef IMAP_STORAGE
01340 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
01341 {
01342    char arg[10];
01343    struct vm_state *vms;
01344    unsigned long messageNum;
01345 
01346    /* Greetings aren't stored in IMAP, so we can't delete them there */
01347    if (msgnum < 0) {
01348       return;
01349    }
01350 
01351    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01352       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);
01353       return;
01354    }
01355 
01356    /* find real message number based on msgnum */
01357    /* this may be an index into vms->msgArray based on the msgnum. */
01358    messageNum = vms->msgArray[msgnum];
01359    if (messageNum == 0) {
01360       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01361       return;
01362    }
01363    if (option_debug > 2)
01364       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01365    /* delete message */
01366    snprintf (arg, sizeof(arg), "%lu",messageNum);
01367    ast_mutex_lock(&vms->lock);
01368    mail_setflag (vms->mailstream,arg,"\\DELETED");
01369    ast_mutex_unlock(&vms->lock);
01370 }
01371 
01372 static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
01373 {
01374    struct vm_state *vms_p;
01375    char *file, *filename;
01376    char *attachment;
01377    int ret = 0, i;
01378    BODY *body;
01379 
01380    /* This function is only used for retrieval of IMAP greetings
01381     * regular messages are not retrieved this way, nor are greetings
01382     * if they are stored locally*/
01383    if (msgnum > -1 || !imapgreetings) {
01384       return 0;
01385    } else {
01386       file = strrchr(ast_strdupa(dir), '/');
01387       if (file)
01388          *file++ = '\0';
01389       else {
01390          ast_debug (1, "Failed to procure file name from directory passed.\n");
01391          return -1;
01392       }
01393    }
01394 
01395    /* check if someone is accessing this box right now... */
01396    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) ||!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01397       ast_log(AST_LOG_ERROR, "Voicemail state not found!\n");
01398       return -1;
01399    }
01400    
01401    /* Greetings will never have a prepended message */
01402    *vms_p->introfn = '\0';
01403 
01404    ast_mutex_lock(&vms_p->lock);
01405    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01406    if (!vms_p->mailstream) {
01407       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01408       ast_mutex_unlock(&vms_p->lock);
01409       return -1;
01410    }
01411 
01412    /*XXX Yuck, this could probably be done a lot better */
01413    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01414       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01415       /* We have the body, now we extract the file name of the first attachment. */
01416       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01417          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01418       } else {
01419          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01420          ast_mutex_unlock(&vms_p->lock);
01421          return -1;
01422       }
01423       filename = strsep(&attachment, ".");
01424       if (!strcmp(filename, file)) {
01425          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01426          vms_p->msgArray[vms_p->curmsg] = i + 1;
01427          save_body(body, vms_p, "2", attachment, 0);
01428          ast_mutex_unlock(&vms_p->lock);
01429          return 0;
01430       }
01431    }
01432    ast_mutex_unlock(&vms_p->lock);
01433 
01434    return -1;
01435 }
01436 
01437 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01438 {
01439    BODY *body;
01440    char *header_content;
01441    char *attachedfilefmt;
01442    char buf[80];
01443    struct vm_state *vms;
01444    char text_file[PATH_MAX];
01445    FILE *text_file_ptr;
01446    int res = 0;
01447    struct ast_vm_user *vmu;
01448 
01449    if (!(vmu = find_user(NULL, context, mailbox))) {
01450       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01451       return -1;
01452    }
01453    
01454    if (msgnum < 0) {
01455       if (imapgreetings) {
01456          res = imap_retrieve_greeting(dir, msgnum, vmu);
01457          goto exit;
01458       } else {
01459          res = 0;
01460          goto exit;
01461       }
01462    }
01463 
01464    /* Before anything can happen, we need a vm_state so that we can
01465     * actually access the imap server through the vms->mailstream
01466     */
01467    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01468       /* This should not happen. If it does, then I guess we'd
01469        * need to create the vm_state, extract which mailbox to
01470        * open, and then set up the msgArray so that the correct
01471        * IMAP message could be accessed. If I have seen correctly
01472        * though, the vms should be obtainable from the vmstates list
01473        * and should have its msgArray properly set up.
01474        */
01475       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01476       res = -1;
01477       goto exit;
01478    }
01479    
01480    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01481    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01482 
01483    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01484    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01485       res = 0;
01486       goto exit;
01487    }
01488 
01489    if (option_debug > 2)
01490       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01491    if (vms->msgArray[msgnum] == 0) {
01492       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01493       res = -1;
01494       goto exit;
01495    }
01496 
01497    /* This will only work for new messages... */
01498    ast_mutex_lock(&vms->lock);
01499    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01500    ast_mutex_unlock(&vms->lock);
01501    /* empty string means no valid header */
01502    if (ast_strlen_zero(header_content)) {
01503       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01504       res = -1;
01505       goto exit;
01506    }
01507 
01508    ast_mutex_lock(&vms->lock);
01509    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01510    ast_mutex_unlock(&vms->lock);
01511 
01512    /* We have the body, now we extract the file name of the first attachment. */
01513    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01514       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01515    } else {
01516       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01517       res = -1;
01518       goto exit;
01519    }
01520    
01521    /* Find the format of the attached file */
01522 
01523    strsep(&attachedfilefmt, ".");
01524    if (!attachedfilefmt) {
01525       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01526       res = -1;
01527       goto exit;
01528    }
01529    
01530    save_body(body, vms, "2", attachedfilefmt, 0);
01531    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01532       *vms->introfn = '\0';
01533    }
01534 
01535    /* Get info from headers!! */
01536    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01537 
01538    if (!(text_file_ptr = fopen(text_file, "w"))) {
01539       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01540    }
01541 
01542    fprintf(text_file_ptr, "%s\n", "[message]");
01543 
01544    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01545    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01546    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01547    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01548    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01549    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01550    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01551    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01552    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01553    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01554    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01555    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01556    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01557    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01558    fclose(text_file_ptr);
01559 
01560 exit:
01561    free_user(vmu);
01562    return res;
01563 }
01564 
01565 static int folder_int(const char *folder)
01566 {
01567    /*assume a NULL folder means INBOX*/
01568    if (!folder)
01569       return 0;
01570 #ifdef IMAP_STORAGE
01571    if (!strcasecmp(folder, imapfolder))
01572 #else
01573    if (!strcasecmp(folder, "INBOX"))
01574 #endif
01575       return 0;
01576    else if (!strcasecmp(folder, "Old"))
01577       return 1;
01578    else if (!strcasecmp(folder, "Work"))
01579       return 2;
01580    else if (!strcasecmp(folder, "Family"))
01581       return 3;
01582    else if (!strcasecmp(folder, "Friends"))
01583       return 4;
01584    else if (!strcasecmp(folder, "Cust1"))
01585       return 5;
01586    else if (!strcasecmp(folder, "Cust2"))
01587       return 6;
01588    else if (!strcasecmp(folder, "Cust3"))
01589       return 7;
01590    else if (!strcasecmp(folder, "Cust4"))
01591       return 8;
01592    else if (!strcasecmp(folder, "Cust5"))
01593       return 9;
01594    else /*assume they meant INBOX if folder is not found otherwise*/
01595       return 0;
01596 }
01597 
01598 /*!
01599  * \brief Gets the number of messages that exist in a mailbox folder.
01600  * \param context
01601  * \param mailbox
01602  * \param folder
01603  * 
01604  * This method is used when IMAP backend is used.
01605  * \return The number of messages in this mailbox folder (zero or more).
01606  */
01607 static int messagecount(const char *context, const char *mailbox, const char *folder)
01608 {
01609    SEARCHPGM *pgm;
01610    SEARCHHEADER *hdr;
01611 
01612    struct ast_vm_user *vmu, vmus;
01613    struct vm_state *vms_p;
01614    int ret = 0;
01615    int fold = folder_int(folder);
01616    int urgent = 0;
01617    
01618    /* If URGENT, then look at INBOX */
01619    if (fold == 11) {
01620       fold = NEW_FOLDER;
01621       urgent = 1;
01622    }
01623 
01624    if (ast_strlen_zero(mailbox))
01625       return 0;
01626 
01627    /* We have to get the user before we can open the stream! */
01628    vmu = find_user(&vmus, context, mailbox);
01629    if (!vmu) {
01630       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01631       return -1;
01632    } else {
01633       /* No IMAP account available */
01634       if (vmu->imapuser[0] == '\0') {
01635          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01636          return -1;
01637       }
01638    }
01639    
01640    /* No IMAP account available */
01641    if (vmu->imapuser[0] == '\0') {
01642       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01643       free_user(vmu);
01644       return -1;
01645    }
01646 
01647    /* check if someone is accessing this box right now... */
01648    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01649    if (!vms_p) {
01650       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01651    }
01652    if (vms_p) {
01653       ast_debug(3, "Returning before search - user is logged in\n");
01654       if (fold == 0) { /* INBOX */
01655          return vms_p->newmessages;
01656       }
01657       if (fold == 1) { /* Old messages */
01658          return vms_p->oldmessages;
01659       }
01660       if (fold == 11) {/*Urgent messages*/
01661          return vms_p->urgentmessages;
01662       }
01663    }
01664 
01665    /* add one if not there... */
01666    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01667    if (!vms_p) {
01668       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01669    }
01670 
01671    if (!vms_p) {
01672       vms_p = create_vm_state_from_user(vmu);
01673    }
01674    ret = init_mailstream(vms_p, fold);
01675    if (!vms_p->mailstream) {
01676       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01677       return -1;
01678    }
01679    if (ret == 0) {
01680       ast_mutex_lock(&vms_p->lock);
01681       pgm = mail_newsearchpgm ();
01682       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01683       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01684       pgm->header = hdr;
01685       if (fold != 1) {
01686          pgm->unseen = 1;
01687          pgm->seen = 0;
01688       }
01689       /* In the special case where fold is 1 (old messages) we have to do things a bit
01690        * differently. Old messages are stored in the INBOX but are marked as "seen"
01691        */
01692       else {
01693          pgm->unseen = 0;
01694          pgm->seen = 1;
01695       }
01696       /* look for urgent messages */
01697       if (urgent) {
01698          pgm->flagged = 1;
01699          pgm->unflagged = 0;
01700       }
01701       pgm->undeleted = 1;
01702       pgm->deleted = 0;
01703 
01704       vms_p->vmArrayIndex = 0;
01705       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01706       if (fold == 0 && urgent == 0)
01707          vms_p->newmessages = vms_p->vmArrayIndex;
01708       if (fold == 1)
01709          vms_p->oldmessages = vms_p->vmArrayIndex;
01710       if (fold == 0 && urgent == 1)
01711          vms_p->urgentmessages = vms_p->vmArrayIndex;
01712       /*Freeing the searchpgm also frees the searchhdr*/
01713       mail_free_searchpgm(&pgm);
01714       ast_mutex_unlock(&vms_p->lock);
01715       vms_p->updated = 0;
01716       return vms_p->vmArrayIndex;
01717    } else {
01718       ast_mutex_lock(&vms_p->lock);
01719       mail_ping(vms_p->mailstream);
01720       ast_mutex_unlock(&vms_p->lock);
01721    }
01722    return 0;
01723 }
01724 
01725 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
01726 {
01727    char *myserveremail = serveremail;
01728    char fn[PATH_MAX];
01729    char introfn[PATH_MAX];
01730    char mailbox[256];
01731    char *stringp;
01732    FILE *p=NULL;
01733    char tmp[80] = "/tmp/astmail-XXXXXX";
01734    long len;
01735    void *buf;
01736    int tempcopy = 0;
01737    STRING str;
01738    int ret; /* for better error checking */
01739    char *imap_flags = NIL;
01740 
01741    /* Set urgent flag for IMAP message */
01742    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
01743       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
01744       imap_flags="\\FLAGGED";
01745    }
01746    
01747    /* Attach only the first format */
01748    fmt = ast_strdupa(fmt);
01749    stringp = fmt;
01750    strsep(&stringp, "|");
01751 
01752    if (!ast_strlen_zero(vmu->serveremail))
01753       myserveremail = vmu->serveremail;
01754 
01755    if (msgnum > -1)
01756       make_file(fn, sizeof(fn), dir, msgnum);
01757    else
01758       ast_copy_string (fn, dir, sizeof(fn));
01759 
01760    snprintf(introfn, sizeof(introfn), "%sintro", fn);
01761    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
01762       *introfn = '\0';
01763    }
01764    
01765    if (ast_strlen_zero(vmu->email)) {
01766       /* We need the vmu->email to be set when we call make_email_file, but
01767        * if we keep it set, a duplicate e-mail will be created. So at the end
01768        * of this function, we will revert back to an empty string if tempcopy
01769        * is 1.
01770        */
01771       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01772       tempcopy = 1;
01773    }
01774 
01775    if (!strcmp(fmt, "wav49"))
01776       fmt = "WAV";
01777    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
01778 
01779    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01780       command hangs. */
01781    if (!(p = vm_mkftemp(tmp))) {
01782       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01783       if (tempcopy)
01784          *(vmu->email) = '\0';
01785       return -1;
01786    }
01787 
01788    if (msgnum < 0 && imapgreetings) {
01789       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
01790          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
01791          return -1;
01792       }
01793       imap_delete_old_greeting(fn, vms);
01794    }
01795 
01796    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);
01797    /* read mail file to memory */
01798    len = ftell(p);
01799    rewind(p);
01800    if (!(buf = ast_malloc(len + 1))) {
01801       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
01802       fclose(p);
01803       if (tempcopy)
01804          *(vmu->email) = '\0';
01805       return -1;
01806    }
01807    if (fread(buf, len, 1, p) < len) {
01808       if (ferror(p)) {
01809          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
01810          return -1;
01811       }
01812    }
01813    ((char *)buf)[len] = '\0';
01814    INIT(&str, mail_string, buf, len);
01815    ret = init_mailstream(vms, NEW_FOLDER);
01816    if (ret == 0) {
01817       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
01818       ast_mutex_lock(&vms->lock);
01819       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
01820          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01821       ast_mutex_unlock(&vms->lock);
01822       fclose(p);
01823       unlink(tmp);
01824       ast_free(buf);
01825    } else {
01826       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
01827       fclose(p);
01828       unlink(tmp);
01829       ast_free(buf);
01830       return -1;
01831    }
01832    ast_debug(3, "%s stored\n", fn);
01833    
01834    if (tempcopy)
01835       *(vmu->email) = '\0';
01836    
01837    return 0;
01838 
01839 }
01840 
01841 /*!
01842  * \brief Gets the number of messages that exist in the inbox folder.
01843  * \param mailbox_context
01844  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
01845  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
01846  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
01847  * 
01848  * This method is used when IMAP backend is used.
01849  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
01850  *
01851  * \return zero on success, -1 on error.
01852  */
01853 
01854 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
01855 {
01856    char tmp[PATH_MAX] = "";
01857    char *mailboxnc;
01858    char *context;
01859    char *mb;
01860    char *cur;
01861    if (newmsgs)
01862       *newmsgs = 0;
01863    if (oldmsgs)
01864       *oldmsgs = 0;
01865    if (urgentmsgs)
01866       *urgentmsgs = 0;
01867 
01868    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
01869    /* If no mailbox, return immediately */
01870    if (ast_strlen_zero(mailbox_context))
01871       return 0;
01872    
01873    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01874    context = strchr(tmp, '@');
01875    if (strchr(mailbox_context, ',')) {
01876       int tmpnew, tmpold, tmpurgent;
01877       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01878       mb = tmp;
01879       while ((cur = strsep(&mb, ", "))) {
01880          if (!ast_strlen_zero(cur)) {
01881             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01882                return -1;
01883             else {
01884                if (newmsgs)
01885                   *newmsgs += tmpnew; 
01886                if (oldmsgs)
01887                   *oldmsgs += tmpold;
01888                if (urgentmsgs)
01889                   *urgentmsgs += tmpurgent;
01890             }
01891          }
01892       }
01893       return 0;
01894    }
01895    if (context) {
01896       *context = '\0';
01897       mailboxnc = tmp;
01898       context++;
01899    } else {
01900       context = "default";
01901       mailboxnc = (char *)mailbox_context;
01902    }
01903    if (newmsgs) {
01904       if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
01905          return -1;
01906    }
01907    if (oldmsgs) {
01908       if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
01909          return -1;
01910    }
01911    if (urgentmsgs) {
01912       if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
01913          return -1;
01914    }
01915    return 0;
01916 }
01917 
01918 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
01919 {
01920    return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
01921 }
01922 
01923 /** 
01924  * \brief Determines if the given folder has messages.
01925  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
01926  * \param folder the folder to look in
01927  *
01928  * This function is used when the mailbox is stored in an IMAP back end.
01929  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
01930  * \return 1 if the folder has one or more messages. zero otherwise.
01931  */
01932 
01933 static int has_voicemail(const char *mailbox, const char *folder)
01934 {
01935    char tmp[256], *tmp2, *box, *context;
01936    ast_copy_string(tmp, mailbox, sizeof(tmp));
01937    tmp2 = tmp;
01938    if (strchr(tmp2, ',')) {
01939       while ((box = strsep(&tmp2, ","))) {
01940          if (!ast_strlen_zero(box)) {
01941             if (has_voicemail(box, folder))
01942                return 1;
01943          }
01944       }
01945    }
01946    if ((context= strchr(tmp, '@')))
01947       *context++ = '\0';
01948    else
01949       context = "default";
01950    return messagecount(context, tmp, folder) ? 1 : 0;
01951 }
01952 
01953 /*!
01954  * \brief Copies a message from one mailbox to another.
01955  * \param chan
01956  * \param vmu
01957  * \param imbox
01958  * \param msgnum
01959  * \param duration
01960  * \param recip
01961  * \param fmt
01962  * \param dir
01963  *
01964  * This works with IMAP storage based mailboxes.
01965  *
01966  * \return zero on success, -1 on error.
01967  */
01968 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)
01969 {
01970    struct vm_state *sendvms = NULL, *destvms = NULL;
01971    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
01972    if (msgnum >= recip->maxmsg) {
01973       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
01974       return -1;
01975    }
01976    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
01977       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
01978       return -1;
01979    }
01980    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
01981       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
01982       return -1;
01983    }
01984    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
01985    ast_mutex_lock(&sendvms->lock);
01986    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
01987       ast_mutex_unlock(&sendvms->lock);
01988       return 0;
01989    }
01990    ast_mutex_unlock(&sendvms->lock);
01991    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
01992    return -1;
01993 }
01994 
01995 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
01996 {
01997    char tmp[256], *t = tmp;
01998    size_t left = sizeof(tmp);
01999    
02000    if (box == OLD_FOLDER) {
02001       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
02002    } else {
02003       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
02004    }
02005 
02006    if (box == NEW_FOLDER) {
02007       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02008    } else {
02009       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02010    }
02011 
02012    /* Build up server information */
02013    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02014 
02015    /* Add authentication user if present */
02016    if (!ast_strlen_zero(authuser))
02017       ast_build_string(&t, &left, "/authuser=%s", authuser);
02018 
02019    /* Add flags if present */
02020    if (!ast_strlen_zero(imapflags))
02021       ast_build_string(&t, &left, "/%s", imapflags);
02022 
02023    /* End with username */
02024    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02025    if (box == NEW_FOLDER || box == OLD_FOLDER)
02026       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02027    else if (box == GREETINGS_FOLDER)
02028       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02029    else {   /* Other folders such as Friends, Family, etc... */
02030       if (!ast_strlen_zero(imapparentfolder)) {
02031          /* imapparentfolder would typically be set to INBOX */
02032          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02033       } else {
02034          snprintf(spec, len, "%s%s", tmp, mbox(box));
02035       }
02036    }
02037 }
02038 
02039 static int init_mailstream(struct vm_state *vms, int box)
02040 {
02041    MAILSTREAM *stream = NIL;
02042    long debug;
02043    char tmp[256];
02044    
02045    if (!vms) {
02046       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02047       return -1;
02048    }
02049    if (option_debug > 2)
02050       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02051    if (vms->mailstream == NIL || !vms->mailstream) {
02052       if (option_debug)
02053          ast_log (LOG_DEBUG,"mailstream not set.\n");
02054    } else {
02055       stream = vms->mailstream;
02056    }
02057    /* debug = T;  user wants protocol telemetry? */
02058    debug = NIL;  /* NO protocol telemetry? */
02059 
02060    if (delimiter == '\0') {      /* did not probe the server yet */
02061       char *cp;
02062 #ifdef USE_SYSTEM_IMAP
02063 #include <imap/linkage.c>
02064 #elif defined(USE_SYSTEM_CCLIENT)
02065 #include <c-client/linkage.c>
02066 #else
02067 #include "linkage.c"
02068 #endif
02069       /* Connect to INBOX first to get folders delimiter */
02070       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02071       ast_mutex_lock(&vms->lock);
02072       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02073       ast_mutex_unlock(&vms->lock);
02074       if (stream == NIL) {
02075          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02076          return -1;
02077       }
02078       get_mailbox_delimiter(stream);
02079       /* update delimiter in imapfolder */
02080       for (cp = imapfolder; *cp; cp++)
02081          if (*cp == '/')
02082             *cp = delimiter;
02083    }
02084    /* Now connect to the target folder */
02085    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02086    if (option_debug > 2)
02087       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02088    ast_mutex_lock(&vms->lock);
02089    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02090    ast_mutex_unlock(&vms->lock);
02091    if (vms->mailstream == NIL) {
02092       return -1;
02093    } else {
02094       return 0;
02095    }
02096 }
02097 
02098 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02099 {
02100    SEARCHPGM *pgm;
02101    SEARCHHEADER *hdr;
02102    int ret, urgent = 0;
02103 
02104    /* If Urgent, then look at INBOX */
02105    if (box == 11) {
02106       box = NEW_FOLDER;
02107       urgent = 1;
02108    }
02109 
02110    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02111    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02112 
02113    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02114       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02115       return -1;
02116    }
02117    
02118    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02119    
02120    /* Check Quota */
02121    if  (box == 0)  {
02122       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02123       check_quota(vms,(char *)mbox(box));
02124    }
02125 
02126    ast_mutex_lock(&vms->lock);
02127    pgm = mail_newsearchpgm();
02128 
02129    /* Check IMAP folder for Asterisk messages only... */
02130    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02131    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02132    pgm->header = hdr;
02133    pgm->deleted = 0;
02134    pgm->undeleted = 1;
02135 
02136    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02137    if (box == NEW_FOLDER && urgent == 1) {
02138       pgm->unseen = 1;
02139       pgm->seen = 0;
02140       pgm->flagged = 1;
02141       pgm->unflagged = 0;
02142    } else if (box == NEW_FOLDER && urgent == 0) {
02143       pgm->unseen = 1;
02144       pgm->seen = 0;
02145       pgm->flagged = 0;
02146       pgm->unflagged = 1;
02147    } else if (box == OLD_FOLDER) {
02148       pgm->seen = 1;
02149       pgm->unseen = 0;
02150    }
02151 
02152    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02153 
02154    vms->vmArrayIndex = 0;
02155    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02156    vms->lastmsg = vms->vmArrayIndex - 1;
02157    mail_free_searchpgm(&pgm);
02158 
02159    ast_mutex_unlock(&vms->lock);
02160    return 0;
02161 }
02162 
02163 static void write_file(char *filename, char *buffer, unsigned long len)
02164 {
02165    FILE *output;
02166 
02167    output = fopen (filename, "w");
02168    if (fwrite(buffer, len, 1, output) != 1) {
02169       if (ferror(output)) {
02170          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02171       }
02172    }
02173    fclose (output);
02174 }
02175 
02176 static void update_messages_by_imapuser(const char *user, unsigned long number)
02177 {
02178    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02179 
02180    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02181       return;
02182    }
02183 
02184    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02185    vms->msgArray[vms->vmArrayIndex++] = number;
02186 }
02187 
02188 void mm_searched(MAILSTREAM *stream, unsigned long number)
02189 {
02190    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02191 
02192    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02193       return;
02194 
02195    update_messages_by_imapuser(user, number);
02196 }
02197 
02198 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02199 {
02200    struct ast_variable *var;
02201    struct ast_vm_user *vmu;
02202 
02203    vmu = ast_calloc(1, sizeof *vmu);
02204    if (!vmu)
02205       return NULL;
02206    ast_set_flag(vmu, VM_ALLOCED);
02207    populate_defaults(vmu);
02208 
02209    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02210    if (var) {
02211       apply_options_full(vmu, var);
02212       ast_variables_destroy(var);
02213       return vmu;
02214    } else {
02215       free(vmu);
02216       return NULL;
02217    }
02218 }
02219 
02220 /* Interfaces to C-client */
02221 
02222 void mm_exists(MAILSTREAM * stream, unsigned long number)
02223 {
02224    /* mail_ping will callback here if new mail! */
02225    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02226    if (number == 0) return;
02227    set_update(stream);
02228 }
02229 
02230 
02231 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02232 {
02233    /* mail_ping will callback here if expunged mail! */
02234    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02235    if (number == 0) return;
02236    set_update(stream);
02237 }
02238 
02239 
02240 void mm_flags(MAILSTREAM * stream, unsigned long number)
02241 {
02242    /* mail_ping will callback here if read mail! */
02243    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02244    if (number == 0) return;
02245    set_update(stream);
02246 }
02247 
02248 
02249 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02250 {
02251    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02252    mm_log (string, errflg);
02253 }
02254 
02255 
02256 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02257 {
02258    if (delimiter == '\0') {
02259       delimiter = delim;
02260    }
02261 
02262    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02263    if (attributes & LATT_NOINFERIORS)
02264       ast_debug(5, "no inferiors\n");
02265    if (attributes & LATT_NOSELECT)
02266       ast_debug(5, "no select\n");
02267    if (attributes & LATT_MARKED)
02268       ast_debug(5, "marked\n");
02269    if (attributes & LATT_UNMARKED)
02270       ast_debug(5, "unmarked\n");
02271 }
02272 
02273 
02274 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02275 {
02276    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02277    if (attributes & LATT_NOINFERIORS)
02278       ast_debug(5, "no inferiors\n");
02279    if (attributes & LATT_NOSELECT)
02280       ast_debug(5, "no select\n");
02281    if (attributes & LATT_MARKED)
02282       ast_debug(5, "marked\n");
02283    if (attributes & LATT_UNMARKED)
02284       ast_debug(5, "unmarked\n");
02285 }
02286 
02287 
02288 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02289 {
02290    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02291    if (status->flags & SA_MESSAGES)
02292       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02293    if (status->flags & SA_RECENT)
02294       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02295    if (status->flags & SA_UNSEEN)
02296       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02297    if (status->flags & SA_UIDVALIDITY)
02298       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02299    if (status->flags & SA_UIDNEXT)
02300       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02301    ast_log(AST_LOG_NOTICE, "\n");
02302 }
02303 
02304 
02305 void mm_log(char *string, long errflg)
02306 {
02307    switch ((short) errflg) {
02308       case NIL:
02309          ast_debug(1,"IMAP Info: %s\n", string);
02310          break;
02311       case PARSE:
02312       case WARN:
02313          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02314          break;
02315       case ERROR:
02316          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02317          break;
02318    }
02319 }
02320 
02321 
02322 void mm_dlog(char *string)
02323 {
02324    ast_log(AST_LOG_NOTICE, "%s\n", string);
02325 }
02326 
02327 
02328 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02329 {
02330    struct ast_vm_user *vmu;
02331 
02332    ast_debug(4, "Entering callback mm_login\n");
02333 
02334    ast_copy_string(user, mb->user, MAILTMPLEN);
02335 
02336    /* We should only do this when necessary */
02337    if (!ast_strlen_zero(authpassword)) {
02338       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02339    } else {
02340       AST_LIST_TRAVERSE(&users, vmu, list) {
02341          if (!strcasecmp(mb->user, vmu->imapuser)) {
02342             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02343             break;
02344          }
02345       }
02346       if (!vmu) {
02347          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02348             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02349             free_user(vmu);
02350          }
02351       }
02352    }
02353 }
02354 
02355 
02356 void mm_critical(MAILSTREAM * stream)
02357 {
02358 }
02359 
02360 
02361 void mm_nocritical(MAILSTREAM * stream)
02362 {
02363 }
02364 
02365 
02366 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02367 {
02368    kill (getpid (), SIGSTOP);
02369    return NIL;
02370 }
02371 
02372 
02373 void mm_fatal(char *string)
02374 {
02375    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02376 }
02377 
02378 /* C-client callback to handle quota */
02379 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02380 {
02381    struct vm_state *vms;
02382    char *mailbox = stream->mailbox, *user;
02383    char buf[1024] = "";
02384    unsigned long usage = 0, limit = 0;
02385    
02386    while (pquota) {
02387       usage = pquota->usage;
02388       limit = pquota->limit;
02389       pquota = pquota->next;
02390    }
02391    
02392    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)))) {
02393       ast_log(AST_LOG_ERROR, "No state found.\n");
02394       return;
02395    }
02396 
02397    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02398 
02399    vms->quota_usage = usage;
02400    vms->quota_limit = limit;
02401 }
02402 
02403 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02404 {
02405    char *start, *eol_pnt;
02406    int taglen;
02407 
02408    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02409       return NULL;
02410 
02411    taglen = strlen(tag) + 1;
02412    if (taglen < 1)
02413       return NULL;
02414 
02415    if (!(start = strstr(header, tag)))
02416       return NULL;
02417 
02418    /* Since we can be called multiple times we should clear our buffer */
02419    memset(buf, 0, len);
02420 
02421    ast_copy_string(buf, start+taglen, len);
02422    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02423       *eol_pnt = '\0';
02424    return buf;
02425 }
02426 
02427 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02428 {
02429    char *start, *quote, *eol_pnt;
02430 
02431    if (ast_strlen_zero(mailbox))
02432       return NULL;
02433 
02434    if (!(start = strstr(mailbox, "/user=")))
02435       return NULL;
02436 
02437    ast_copy_string(buf, start+6, len);
02438 
02439    if (!(quote = strchr(buf, '\"'))) {
02440       if (!(eol_pnt = strchr(buf, '/')))
02441          eol_pnt = strchr(buf,'}');
02442       *eol_pnt = '\0';
02443       return buf;
02444    } else {
02445       eol_pnt = strchr(buf+1,'\"');
02446       *eol_pnt = '\0';
02447       return buf+1;
02448    }
02449 }
02450 
02451 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02452 {
02453    struct vm_state *vms_p;
02454 
02455    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02456    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02457       return vms_p;
02458    }
02459    if (option_debug > 4)
02460       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02461    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02462       return NULL;
02463    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02464    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02465    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02466    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02467    if (option_debug > 4)
02468       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02469    vms_p->updated = 1;
02470    /* set mailbox to INBOX! */
02471    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02472    init_vm_state(vms_p);
02473    vmstate_insert(vms_p);
02474    return vms_p;
02475 }
02476 
02477 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02478 {
02479    struct vmstate *vlist = NULL;
02480 
02481    if (interactive) {
02482       struct vm_state *vms;
02483       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02484       vms = pthread_getspecific(ts_vmstate.key);
02485       return vms;
02486    }
02487 
02488    AST_LIST_LOCK(&vmstates);
02489    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02490       if (!vlist->vms) {
02491          ast_debug(3, "error: vms is NULL for %s\n", user);
02492          continue;
02493       }
02494       if (!vlist->vms->imapuser) {
02495          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02496          continue;
02497       }
02498 
02499       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02500          AST_LIST_UNLOCK(&vmstates);
02501          return vlist->vms;
02502       }
02503    }
02504    AST_LIST_UNLOCK(&vmstates);
02505 
02506    ast_debug(3, "%s not found in vmstates\n", user);
02507 
02508    return NULL;
02509 }
02510 
02511 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02512 {
02513 
02514    struct vmstate *vlist = NULL;
02515    const char *local_context = S_OR(context, "default");
02516 
02517    if (interactive) {
02518       struct vm_state *vms;
02519       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02520       vms = pthread_getspecific(ts_vmstate.key);
02521       return vms;
02522    }
02523 
02524    AST_LIST_LOCK(&vmstates);
02525    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02526       if (!vlist->vms) {
02527          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02528          continue;
02529       }
02530       if (!vlist->vms->username || !vlist->vms->context) {
02531          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02532          continue;
02533       }
02534 
02535       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);
02536       
02537       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02538          ast_debug(3, "Found it!\n");
02539          AST_LIST_UNLOCK(&vmstates);
02540          return vlist->vms;
02541       }
02542    }
02543    AST_LIST_UNLOCK(&vmstates);
02544 
02545    ast_debug(3, "%s not found in vmstates\n", mailbox);
02546 
02547    return NULL;
02548 }
02549 
02550 static void vmstate_insert(struct vm_state *vms) 
02551 {
02552    struct vmstate *v;
02553    struct vm_state *altvms;
02554 
02555    /* If interactive, it probably already exists, and we should
02556       use the one we already have since it is more up to date.
02557       We can compare the username to find the duplicate */
02558    if (vms->interactive == 1) {
02559       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02560       if (altvms) {  
02561          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02562          vms->newmessages = altvms->newmessages;
02563          vms->oldmessages = altvms->oldmessages;
02564          vms->vmArrayIndex = altvms->vmArrayIndex;
02565          vms->lastmsg = altvms->lastmsg;
02566          vms->curmsg = altvms->curmsg;
02567          /* get a pointer to the persistent store */
02568          vms->persist_vms = altvms;
02569          /* Reuse the mailstream? */
02570 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02571          vms->mailstream = altvms->mailstream;
02572 #else
02573          vms->mailstream = NIL;
02574 #endif
02575       }
02576       return;
02577    }
02578 
02579    if (!(v = ast_calloc(1, sizeof(*v))))
02580       return;
02581    
02582    v->vms = vms;
02583 
02584    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02585 
02586    AST_LIST_LOCK(&vmstates);
02587    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02588    AST_LIST_UNLOCK(&vmstates);
02589 }
02590 
02591 static void vmstate_delete(struct vm_state *vms) 
02592 {
02593    struct vmstate *vc = NULL;
02594    struct vm_state *altvms = NULL;
02595 
02596    /* If interactive, we should copy pertinent info
02597       back to the persistent state (to make update immediate) */
02598    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02599       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02600       altvms->newmessages = vms->newmessages;
02601       altvms->oldmessages = vms->oldmessages;
02602       altvms->updated = 1;
02603       vms->mailstream = mail_close(vms->mailstream);
02604 
02605       /* Interactive states are not stored within the persistent list */
02606       return;
02607    }
02608    
02609    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02610    
02611    AST_LIST_LOCK(&vmstates);
02612    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02613       if (vc->vms == vms) {
02614          AST_LIST_REMOVE_CURRENT(list);
02615          break;
02616       }
02617    }
02618    AST_LIST_TRAVERSE_SAFE_END
02619    AST_LIST_UNLOCK(&vmstates);
02620    
02621    if (vc) {
02622       ast_mutex_destroy(&vc->vms->lock);
02623       ast_free(vc);
02624    }
02625    else
02626       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02627 }
02628 
02629 static void set_update(MAILSTREAM * stream) 
02630 {
02631    struct vm_state *vms;
02632    char *mailbox = stream->mailbox, *user;
02633    char buf[1024] = "";
02634 
02635    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02636       if (user && option_debug > 2)
02637          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02638       return;
02639    }
02640 
02641    ast_debug(3, "User %s mailbox set for update.\n", user);
02642 
02643    vms->updated = 1; /* Set updated flag since mailbox changed */
02644 }
02645 
02646 static void init_vm_state(struct vm_state *vms) 
02647 {
02648    int x;
02649    vms->vmArrayIndex = 0;
02650    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02651       vms->msgArray[x] = 0;
02652    }
02653    ast_mutex_init(&vms->lock);
02654 }
02655 
02656 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02657 {
02658    char *body_content;
02659    char *body_decoded;
02660    char *fn = is_intro ? vms->introfn : vms->fn;
02661    unsigned long len;
02662    unsigned long newlen;
02663    char filename[256];
02664    
02665    if (!body || body == NIL)
02666       return -1;
02667 
02668    ast_mutex_lock(&vms->lock);
02669    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02670    ast_mutex_unlock(&vms->lock);
02671    if (body_content != NIL) {
02672       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02673       /* ast_debug(1,body_content); */
02674       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02675       /* If the body of the file is empty, return an error */
02676       if (!newlen) {
02677          return -1;
02678       }
02679       write_file(filename, (char *) body_decoded, newlen);
02680    } else {
02681       ast_debug(5, "Body of message is NULL.\n");
02682       return -1;
02683    }
02684    return 0;
02685 }
02686 
02687 /*! 
02688  * \brief Get delimiter via mm_list callback 
02689  * \param stream
02690  *
02691  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02692  */
02693 /* MUTEX should already be held */
02694 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02695    char tmp[50];
02696    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02697    mail_list(stream, tmp, "*");
02698 }
02699 
02700 /*! 
02701  * \brief Check Quota for user 
02702  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02703  * \param mailbox the mailbox to check the quota for.
02704  *
02705  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02706  */
02707 static void check_quota(struct vm_state *vms, char *mailbox) {
02708    ast_mutex_lock(&vms->lock);
02709    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02710    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02711    if (vms && vms->mailstream != NULL) {
02712       imap_getquotaroot(vms->mailstream, mailbox);
02713    } else {
02714       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02715    }
02716    ast_mutex_unlock(&vms->lock);
02717 }
02718 
02719 #endif /* IMAP_STORAGE */
02720 
02721 /*! \brief Lock file path
02722     only return failure if ast_lock_path returns 'timeout',
02723    not if the path does not exist or any other reason
02724 */
02725 static int vm_lock_path(const char *path)
02726 {
02727    switch (ast_lock_path(path)) {
02728    case AST_LOCK_TIMEOUT:
02729       return -1;
02730    default:
02731       return 0;
02732    }
02733 }
02734 
02735 
02736 #ifdef ODBC_STORAGE
02737 struct generic_prepare_struct {
02738    char *sql;
02739    int argc;
02740    char **argv;
02741 };
02742 
02743 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02744 {
02745    struct generic_prepare_struct *gps = data;
02746    int res, i;
02747    SQLHSTMT stmt;
02748 
02749    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02750    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02751       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
02752       return NULL;
02753    }
02754    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02755    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02756       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02757       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02758       return NULL;
02759    }
02760    for (i = 0; i < gps->argc; i++)
02761       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02762 
02763    return stmt;
02764 }
02765 
02766 /*!
02767  * \brief Retrieves a file from an ODBC data store.
02768  * \param dir the path to the file to be retreived.
02769  * \param msgnum the message number, such as within a mailbox folder.
02770  * 
02771  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
02772  * 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.
02773  *
02774  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
02775  * The output is the message information file with the name msgnum and the extension .txt
02776  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
02777  * 
02778  * \return 0 on success, -1 on error.
02779  */
02780 static int retrieve_file(char *dir, int msgnum)
02781 {
02782    int x = 0;
02783    int res;
02784    int fd=-1;
02785    size_t fdlen = 0;
02786    void *fdm = MAP_FAILED;
02787    SQLSMALLINT colcount=0;
02788    SQLHSTMT stmt;
02789    char sql[PATH_MAX];
02790    char fmt[80]="";
02791    char *c;
02792    char coltitle[256];
02793    SQLSMALLINT collen;
02794    SQLSMALLINT datatype;
02795    SQLSMALLINT decimaldigits;
02796    SQLSMALLINT nullable;
02797    SQLULEN colsize;
02798    SQLLEN colsize2;
02799    FILE *f=NULL;
02800    char rowdata[80];
02801    char fn[PATH_MAX];
02802    char full_fn[PATH_MAX];
02803    char msgnums[80];
02804    char *argv[] = { dir, msgnums };
02805    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02806 
02807    struct odbc_obj *obj;
02808    obj = ast_odbc_request_obj(odbc_database, 0);
02809    if (obj) {
02810       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02811       c = strchr(fmt, '|');
02812       if (c)
02813          *c = '\0';
02814       if (!strcasecmp(fmt, "wav49"))
02815          strcpy(fmt, "WAV");
02816       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02817       if (msgnum > -1)
02818          make_file(fn, sizeof(fn), dir, msgnum);
02819       else
02820          ast_copy_string(fn, dir, sizeof(fn));
02821 
02822       /* Create the information file */
02823       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02824       
02825       if (!(f = fopen(full_fn, "w+"))) {
02826          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02827          goto yuck;
02828       }
02829       
02830       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02831       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02832       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02833       if (!stmt) {
02834          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02835          ast_odbc_release_obj(obj);
02836          goto yuck;
02837       }
02838       res = SQLFetch(stmt);
02839       if (res == SQL_NO_DATA) {
02840          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02841          ast_odbc_release_obj(obj);
02842          goto yuck;
02843       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02844          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02845          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02846          ast_odbc_release_obj(obj);
02847          goto yuck;
02848       }
02849       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
02850       if (fd < 0) {
02851          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02852          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02853          ast_odbc_release_obj(obj);
02854          goto yuck;
02855       }
02856       res = SQLNumResultCols(stmt, &colcount);
02857       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02858          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02859          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02860          ast_odbc_release_obj(obj);
02861          goto yuck;
02862       }
02863       if (f) 
02864          fprintf(f, "[message]\n");
02865       for (x=0;x<colcount;x++) {
02866          rowdata[0] = '\0';
02867          collen = sizeof(coltitle);
02868          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02869                   &datatype, &colsize, &decimaldigits, &nullable);
02870          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02871             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02872             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02873             ast_odbc_release_obj(obj);
02874             goto yuck;
02875          }
02876          if (!strcasecmp(coltitle, "recording")) {
02877             off_t offset;
02878             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02879             fdlen = colsize2;
02880             if (fd > -1) {
02881                char tmp[1]="";
02882                lseek(fd, fdlen - 1, SEEK_SET);
02883                if (write(fd, tmp, 1) != 1) {
02884                   close(fd);
02885                   fd = -1;
02886                   continue;
02887                }
02888                /* Read out in small chunks */
02889                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
02890                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
02891                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
02892                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02893                      ast_odbc_release_obj(obj);
02894                      goto yuck;
02895                   } else {
02896                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
02897                      munmap(fdm, CHUNKSIZE);
02898                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02899                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02900                         unlink(full_fn);
02901                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02902                         ast_odbc_release_obj(obj);
02903                         goto yuck;
02904                      }
02905                   }
02906                }
02907                if (truncate(full_fn, fdlen) < 0) {
02908                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
02909                }
02910             }
02911          } else {
02912             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02913             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02914                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
02915                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02916                ast_odbc_release_obj(obj);
02917                goto yuck;
02918             }
02919             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
02920                fprintf(f, "%s=%s\n", coltitle, rowdata);
02921          }
02922       }
02923       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02924       ast_odbc_release_obj(obj);
02925    } else
02926       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02927 yuck: 
02928    if (f)
02929       fclose(f);
02930    if (fd > -1)
02931       close(fd);
02932    return x - 1;
02933 }
02934 
02935 /*!
02936  * \brief Determines the highest message number in use for a given user and mailbox folder.
02937  * \param vmu 
02938  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
02939  *
02940  * This method is used when mailboxes are stored in an ODBC back end.
02941  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
02942  *
02943  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
02944  */
02945 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02946 {
02947    int x = 0;
02948    int res;
02949    SQLHSTMT stmt;
02950    char sql[PATH_MAX];
02951    char rowdata[20];
02952    char *argv[] = { dir };
02953    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02954 
02955    struct odbc_obj *obj;
02956    obj = ast_odbc_request_obj(odbc_database, 0);
02957    if (obj) {
02958       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
02959       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02960       if (!stmt) {
02961          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02962          ast_odbc_release_obj(obj);
02963          goto yuck;
02964       }
02965       res = SQLFetch(stmt);
02966       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02967          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02968          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02969          ast_odbc_release_obj(obj);
02970          goto yuck;
02971       }
02972       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02973       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02974          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02975          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02976          ast_odbc_release_obj(obj);
02977          goto yuck;
02978       }
02979       if (sscanf(rowdata, "%30d", &x) != 1)
02980          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
02981       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02982       ast_odbc_release_obj(obj);
02983    } else
02984       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02985 yuck: 
02986    return x - 1;
02987 }
02988 
02989 /*!
02990  * \brief Determines if the specified message exists.
02991  * \param dir the folder the mailbox folder to look for messages. 
02992  * \param msgnum the message index to query for.
02993  *
02994  * This method is used when mailboxes are stored in an ODBC back end.
02995  *
02996  * \return greater than zero if the message exists, zero when the message does not exist or on error.
02997  */
02998 static int message_exists(char *dir, int msgnum)
02999 {
03000    int x = 0;
03001    int res;
03002    SQLHSTMT stmt;
03003    char sql[PATH_MAX];
03004    char rowdata[20];
03005    char msgnums[20];
03006    char *argv[] = { dir, msgnums };
03007    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03008 
03009    struct odbc_obj *obj;
03010    obj = ast_odbc_request_obj(odbc_database, 0);
03011    if (obj) {
03012       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03013       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03014       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03015       if (!stmt) {
03016          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03017          ast_odbc_release_obj(obj);
03018          goto yuck;
03019       }
03020       res = SQLFetch(stmt);
03021       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03022          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03023          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03024          ast_odbc_release_obj(obj);
03025          goto yuck;
03026       }
03027       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03028       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03029          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03030          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03031          ast_odbc_release_obj(obj);
03032          goto yuck;
03033       }
03034       if (sscanf(rowdata, "%30d", &x) != 1)
03035          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03036       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03037       ast_odbc_release_obj(obj);
03038    } else
03039       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03040 yuck: 
03041    return x;
03042 }
03043 
03044 /*!
03045  * \brief returns the one-based count for messages.
03046  * \param vmu
03047  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03048  *
03049  * This method is used when mailboxes are stored in an ODBC back end.
03050  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03051  * one-based messages.
03052  * This method just calls last_message_index and returns +1 of its value.
03053  *
03054  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03055  */
03056 static int count_messages(struct ast_vm_user *vmu, char *dir)
03057 {
03058    return last_message_index(vmu, dir) + 1;
03059 }
03060 
03061 /*!
03062  * \brief Deletes a message from the mailbox folder.
03063  * \param sdir The mailbox folder to work in.
03064  * \param smsg The message index to be deleted.
03065  *
03066  * This method is used when mailboxes are stored in an ODBC back end.
03067  * The specified message is directly deleted from the database 'voicemessages' table.
03068  * 
03069  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03070  */
03071 static void delete_file(char *sdir, int smsg)
03072 {
03073    SQLHSTMT stmt;
03074    char sql[PATH_MAX];
03075    char msgnums[20];
03076    char *argv[] = { sdir, msgnums };
03077    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03078 
03079    struct odbc_obj *obj;
03080    obj = ast_odbc_request_obj(odbc_database, 0);
03081    if (obj) {
03082       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03083       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03084       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03085       if (!stmt)
03086          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03087       else
03088          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03089       ast_odbc_release_obj(obj);
03090    } else
03091       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03092    return;  
03093 }
03094 
03095 /*!
03096  * \brief Copies a voicemail from one mailbox to another.
03097  * \param sdir the folder for which to look for the message to be copied.
03098  * \param smsg the index of the message to be copied.
03099  * \param ddir the destination folder to copy the message into.
03100  * \param dmsg the index to be used for the copied message.
03101  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03102  * \param dmailboxcontext The context for the destination user.
03103  *
03104  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03105  */
03106 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03107 {
03108    SQLHSTMT stmt;
03109    char sql[512];
03110    char msgnums[20];
03111    char msgnumd[20];
03112    struct odbc_obj *obj;
03113    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03114    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03115 
03116    delete_file(ddir, dmsg);
03117    obj = ast_odbc_request_obj(odbc_database, 0);
03118    if (obj) {
03119       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03120       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03121       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);
03122       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03123       if (!stmt)
03124          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03125       else
03126          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03127       ast_odbc_release_obj(obj);
03128    } else
03129       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03130    return;  
03131 }
03132 
03133 struct insert_data {
03134    char *sql;
03135    char *dir;
03136    char *msgnums;
03137    void *data;
03138    SQLLEN datalen;
03139    SQLLEN indlen;
03140    const char *context;
03141    const char *macrocontext;
03142    const char *callerid;
03143    const char *origtime;
03144    const char *duration;
03145    char *mailboxuser;
03146    char *mailboxcontext;
03147    const char *category;
03148    const char *flag;
03149 };
03150 
03151 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03152 {
03153    struct insert_data *data = vdata;
03154    int res;
03155    SQLHSTMT stmt;
03156 
03157    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03158    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03159       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03160       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03161       return NULL;
03162    }
03163 
03164    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03165    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03166    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03167    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03168    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03169    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03170    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03171    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03172    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03173    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03174    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03175    if (!ast_strlen_zero(data->category)) {
03176       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03177    }
03178    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03179    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03180       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03181       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03182       return NULL;
03183    }
03184 
03185    return stmt;
03186 }
03187 
03188 /*!
03189  * \brief Stores a voicemail into the database.
03190  * \param dir the folder the mailbox folder to store the message.
03191  * \param mailboxuser the user owning the mailbox folder.
03192  * \param mailboxcontext
03193  * \param msgnum the message index for the message to be stored.
03194  *
03195  * This method is used when mailboxes are stored in an ODBC back end.
03196  * The message sound file and information file is looked up on the file system. 
03197  * A SQL query is invoked to store the message into the (MySQL) database.
03198  *
03199  * \return the zero on success -1 on error.
03200  */
03201 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
03202 {
03203    int res = 0;
03204    int fd = -1;
03205    void *fdm = MAP_FAILED;
03206    size_t fdlen = -1;
03207    SQLHSTMT stmt;
03208    char sql[PATH_MAX];
03209    char msgnums[20];
03210    char fn[PATH_MAX];
03211    char full_fn[PATH_MAX];
03212    char fmt[80]="";
03213    char *c;
03214    struct ast_config *cfg=NULL;
03215    struct odbc_obj *obj;
03216    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
03217       .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
03218    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03219 
03220    delete_file(dir, msgnum);
03221    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03222       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03223       return -1;
03224    }
03225 
03226    do {
03227       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03228       c = strchr(fmt, '|');
03229       if (c)
03230          *c = '\0';
03231       if (!strcasecmp(fmt, "wav49"))
03232          strcpy(fmt, "WAV");
03233       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03234       if (msgnum > -1)
03235          make_file(fn, sizeof(fn), dir, msgnum);
03236       else
03237          ast_copy_string(fn, dir, sizeof(fn));
03238       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03239       cfg = ast_config_load(full_fn, config_flags);
03240       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03241       fd = open(full_fn, O_RDWR);
03242       if (fd < 0) {
03243          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03244          res = -1;
03245          break;
03246       }
03247       if (cfg) {
03248          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03249             idata.context = "";
03250          }
03251          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03252             idata.macrocontext = "";
03253          }
03254          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03255             idata.callerid = "";
03256          }
03257          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03258             idata.origtime = "";
03259          }
03260          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03261             idata.duration = "";
03262          }
03263          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03264             idata.category = "";
03265          }
03266          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03267             idata.flag = "";
03268          }
03269       }
03270       fdlen = lseek(fd, 0, SEEK_END);
03271       lseek(fd, 0, SEEK_SET);
03272       printf("Length is %zd\n", fdlen);
03273       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03274       if (fdm == MAP_FAILED) {
03275          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03276          res = -1;
03277          break;
03278       } 
03279       idata.data = fdm;
03280       idata.datalen = idata.indlen = fdlen;
03281 
03282       if (!ast_strlen_zero(idata.category)) 
03283          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03284       else
03285          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03286 
03287       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03288          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03289       } else {
03290          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03291          res = -1;
03292       }
03293    } while (0);
03294    if (obj) {
03295       ast_odbc_release_obj(obj);
03296    }
03297    if (cfg)
03298       ast_config_destroy(cfg);
03299    if (fdm != MAP_FAILED)
03300       munmap(fdm, fdlen);
03301    if (fd > -1)
03302       close(fd);
03303    return res;
03304 }
03305 
03306 /*!
03307  * \brief Renames a message in a mailbox folder.
03308  * \param sdir The folder of the message to be renamed.
03309  * \param smsg The index of the message to be renamed.
03310  * \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.
03311  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03312  * \param ddir The destination folder for the message to be renamed into
03313  * \param dmsg The destination message for the message to be renamed.
03314  *
03315  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03316  * 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.
03317  * 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.
03318  */
03319 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03320 {
03321    SQLHSTMT stmt;
03322    char sql[PATH_MAX];
03323    char msgnums[20];
03324    char msgnumd[20];
03325    struct odbc_obj *obj;
03326    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03327    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03328 
03329    delete_file(ddir, dmsg);
03330    obj = ast_odbc_request_obj(odbc_database, 0);
03331    if (obj) {
03332       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03333       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03334       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03335       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03336       if (!stmt)
03337          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03338       else
03339          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03340       ast_odbc_release_obj(obj);
03341    } else
03342       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03343    return;  
03344 }
03345 
03346 /*!
03347  * \brief Removes a voicemail message file.
03348  * \param dir the path to the message file.
03349  * \param msgnum the unique number for the message within the mailbox.
03350  *
03351  * Removes the message content file and the information file.
03352  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03353  * Typical use is to clean up after a RETRIEVE operation. 
03354  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03355  * \return zero on success, -1 on error.
03356  */
03357 static int remove_file(char *dir, int msgnum)
03358 {
03359    char fn[PATH_MAX];
03360    char full_fn[PATH_MAX];
03361    char msgnums[80];
03362    
03363    if (msgnum > -1) {
03364       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03365       make_file(fn, sizeof(fn), dir, msgnum);
03366    } else
03367       ast_copy_string(fn, dir, sizeof(fn));
03368    ast_filedelete(fn, NULL);  
03369    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03370    unlink(full_fn);
03371    return 0;
03372 }
03373 #else
03374 #ifndef IMAP_STORAGE
03375 /*!
03376  * \brief Find all .txt files - even if they are not in sequence from 0000.
03377  * \param vmu
03378  * \param dir
03379  *
03380  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03381  *
03382  * \return the count of messages, zero or more.
03383  */
03384 static int count_messages(struct ast_vm_user *vmu, char *dir)
03385 {
03386 
03387    int vmcount = 0;
03388    DIR *vmdir = NULL;
03389    struct dirent *vment = NULL;
03390 
03391    if (vm_lock_path(dir))
03392       return ERROR_LOCK_PATH;
03393 
03394    if ((vmdir = opendir(dir))) {
03395       while ((vment = readdir(vmdir))) {
03396          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03397             vmcount++;
03398          }
03399       }
03400       closedir(vmdir);
03401    }
03402    ast_unlock_path(dir);
03403    
03404    return vmcount;
03405 }
03406 
03407 /*!
03408  * \brief Renames a message in a mailbox folder.
03409  * \param sfn The path to the mailbox information and data file to be renamed.
03410  * \param dfn The path for where the message data and information files will be renamed to.
03411  *
03412  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03413  */
03414 static void rename_file(char *sfn, char *dfn)
03415 {
03416    char stxt[PATH_MAX];
03417    char dtxt[PATH_MAX];
03418    ast_filerename(sfn,dfn,NULL);
03419    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03420    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03421    if (ast_check_realtime("voicemail_data")) {
03422       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03423    }
03424    rename(stxt, dtxt);
03425 }
03426 
03427 /*! 
03428  * \brief Determines the highest message number in use for a given user and mailbox folder.
03429  * \param vmu 
03430  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03431  *
03432  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03433  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03434  *
03435  * \note Should always be called with a lock already set on dir.
03436  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03437  */
03438 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03439 {
03440    int x;
03441    unsigned char map[MAXMSGLIMIT] = "";
03442    DIR *msgdir;
03443    struct dirent *msgdirent;
03444    int msgdirint;
03445 
03446    /* Reading the entire directory into a file map scales better than
03447     * doing a stat repeatedly on a predicted sequence.  I suspect this
03448     * is partially due to stat(2) internally doing a readdir(2) itself to
03449     * find each file. */
03450    msgdir = opendir(dir);
03451    while ((msgdirent = readdir(msgdir))) {
03452       if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03453          map[msgdirint] = 1;
03454    }
03455    closedir(msgdir);
03456 
03457    for (x = 0; x < vmu->maxmsg; x++) {
03458       if (map[x] == 0)
03459          break;
03460    }
03461 
03462    return x - 1;
03463 }
03464 
03465 #endif /* #ifndef IMAP_STORAGE */
03466 #endif /* #else of #ifdef ODBC_STORAGE */
03467 #ifndef IMAP_STORAGE
03468 /*!
03469  * \brief Utility function to copy a file.
03470  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03471  * \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.
03472  *
03473  * 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.
03474  * The copy operation copies up to 4096 bytes at once.
03475  *
03476  * \return zero on success, -1 on error.
03477  */
03478 static int copy(char *infile, char *outfile)
03479 {
03480    int ifd;
03481    int ofd;
03482    int res;
03483    int len;
03484    char buf[4096];
03485 
03486 #ifdef HARDLINK_WHEN_POSSIBLE
03487    /* Hard link if possible; saves disk space & is faster */
03488    if (link(infile, outfile)) {
03489 #endif
03490       if ((ifd = open(infile, O_RDONLY)) < 0) {
03491          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03492          return -1;
03493       }
03494       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03495          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03496          close(ifd);
03497          return -1;
03498       }
03499       do {
03500          len = read(ifd, buf, sizeof(buf));
03501          if (len < 0) {
03502             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03503             close(ifd);
03504             close(ofd);
03505             unlink(outfile);
03506          }
03507          if (len) {
03508             res = write(ofd, buf, len);
03509             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03510                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03511                close(ifd);
03512                close(ofd);
03513                unlink(outfile);
03514             }
03515          }
03516       } while (len);
03517       close(ifd);
03518       close(ofd);
03519       return 0;
03520 #ifdef HARDLINK_WHEN_POSSIBLE
03521    } else {
03522       /* Hard link succeeded */
03523       return 0;
03524    }
03525 #endif
03526 }
03527 
03528 /*!
03529  * \brief Copies a voicemail information (envelope) file.
03530  * \param frompath
03531  * \param topath 
03532  *
03533  * Every voicemail has the data (.wav) file, and the information file.
03534  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03535  * This is used by the COPY macro when not using IMAP storage.
03536  */
03537 static void copy_plain_file(char *frompath, char *topath)
03538 {
03539    char frompath2[PATH_MAX], topath2[PATH_MAX];
03540    struct ast_variable *tmp,*var = NULL;
03541    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03542    ast_filecopy(frompath, topath, NULL);
03543    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03544    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03545    if (ast_check_realtime("voicemail_data")) {
03546       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03547       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03548       for (tmp = var; tmp; tmp = tmp->next) {
03549          if (!strcasecmp(tmp->name, "origmailbox")) {
03550             origmailbox = tmp->value;
03551          } else if (!strcasecmp(tmp->name, "context")) {
03552             context = tmp->value;
03553          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03554             macrocontext = tmp->value;
03555          } else if (!strcasecmp(tmp->name, "exten")) {
03556             exten = tmp->value;
03557          } else if (!strcasecmp(tmp->name, "priority")) {
03558             priority = tmp->value;
03559          } else if (!strcasecmp(tmp->name, "callerchan")) {
03560             callerchan = tmp->value;
03561          } else if (!strcasecmp(tmp->name, "callerid")) {
03562             callerid = tmp->value;
03563          } else if (!strcasecmp(tmp->name, "origdate")) {
03564             origdate = tmp->value;
03565          } else if (!strcasecmp(tmp->name, "origtime")) {
03566             origtime = tmp->value;
03567          } else if (!strcasecmp(tmp->name, "category")) {
03568             category = tmp->value;
03569          } else if (!strcasecmp(tmp->name, "duration")) {
03570             duration = tmp->value;
03571          }
03572       }
03573       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);
03574    }
03575    copy(frompath2, topath2);
03576    ast_variables_destroy(var);
03577 }
03578 #endif
03579 
03580 /*! 
03581  * \brief Removes the voicemail sound and information file.
03582  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03583  *
03584  * This is used by the DELETE macro when voicemails are stored on the file system.
03585  *
03586  * \return zero on success, -1 on error.
03587  */
03588 static int vm_delete(char *file)
03589 {
03590    char *txt;
03591    int txtsize = 0;
03592 
03593    txtsize = (strlen(file) + 5)*sizeof(char);
03594    txt = alloca(txtsize);
03595    /* Sprintf here would safe because we alloca'd exactly the right length,
03596     * but trying to eliminate all sprintf's anyhow
03597     */
03598    if (ast_check_realtime("voicemail_data")) {
03599       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03600    }
03601    snprintf(txt, txtsize, "%s.txt", file);
03602    unlink(txt);
03603    return ast_filedelete(file, NULL);
03604 }
03605 
03606 /*!
03607  * \brief utility used by inchar(), for base_encode()
03608  */
03609 static int inbuf(struct baseio *bio, FILE *fi)
03610 {
03611    int l;
03612 
03613    if (bio->ateof)
03614       return 0;
03615 
03616    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03617       if (ferror(fi))
03618          return -1;
03619 
03620       bio->ateof = 1;
03621       return 0;
03622    }
03623 
03624    bio->iolen= l;
03625    bio->iocp= 0;
03626 
03627    return 1;
03628 }
03629 
03630 /*!
03631  * \brief utility used by base_encode()
03632  */
03633 static int inchar(struct baseio *bio, FILE *fi)
03634 {
03635    if (bio->iocp>=bio->iolen) {
03636       if (!inbuf(bio, fi))
03637          return EOF;
03638    }
03639 
03640    return bio->iobuf[bio->iocp++];
03641 }
03642 
03643 /*!
03644  * \brief utility used by base_encode()
03645  */
03646 static int ochar(struct baseio *bio, int c, FILE *so)
03647 {
03648    if (bio->linelength >= BASELINELEN) {
03649       if (fputs(eol,so) == EOF)
03650          return -1;
03651 
03652       bio->linelength= 0;
03653    }
03654 
03655    if (putc(((unsigned char)c),so) == EOF)
03656       return -1;
03657 
03658    bio->linelength++;
03659 
03660    return 1;
03661 }
03662 
03663 /*!
03664  * \brief Performs a base 64 encode algorithm on the contents of a File
03665  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03666  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03667  *
03668  * 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 ?
03669  *
03670  * \return zero on success, -1 on error.
03671  */
03672 static int base_encode(char *filename, FILE *so)
03673 {
03674    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03675       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03676       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03677       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03678    int i,hiteof= 0;
03679    FILE *fi;
03680    struct baseio bio;
03681 
03682    memset(&bio, 0, sizeof(bio));
03683    bio.iocp = BASEMAXINLINE;
03684 
03685    if (!(fi = fopen(filename, "rb"))) {
03686       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03687       return -1;
03688    }
03689 
03690    while (!hiteof){
03691       unsigned char igroup[3], ogroup[4];
03692       int c,n;
03693 
03694       igroup[0]= igroup[1]= igroup[2]= 0;
03695 
03696       for (n= 0;n<3;n++) {
03697          if ((c = inchar(&bio, fi)) == EOF) {
03698             hiteof= 1;
03699             break;
03700          }
03701 
03702          igroup[n]= (unsigned char)c;
03703       }
03704 
03705       if (n> 0) {
03706          ogroup[0]= dtable[igroup[0]>>2];
03707          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03708          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03709          ogroup[3]= dtable[igroup[2]&0x3F];
03710 
03711          if (n<3) {
03712             ogroup[3]= '=';
03713 
03714             if (n<2)
03715                ogroup[2]= '=';
03716          }
03717 
03718          for (i= 0;i<4;i++)
03719             ochar(&bio, ogroup[i], so);
03720       }
03721    }
03722 
03723    fclose(fi);
03724    
03725    if (fputs(eol,so)==EOF)
03726       return 0;
03727 
03728    return 1;
03729 }
03730 
03731 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)
03732 {
03733    char callerid[256];
03734    char fromdir[256], fromfile[256];
03735    struct ast_config *msg_cfg;
03736    const char *origcallerid, *origtime;
03737    char origcidname[80], origcidnum[80], origdate[80];
03738    int inttime;
03739    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03740 
03741    /* Prepare variables for substitution in email body and subject */
03742    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03743    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03744    snprintf(passdata, passdatasize, "%d", msgnum);
03745    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03746    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03747    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03748    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03749       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03750    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03751    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03752    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03753    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03754    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
03755 
03756    /* Retrieve info from VM attribute file */
03757    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03758    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03759    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03760       strcat(fromfile, ".txt");
03761    }
03762    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
03763       if (option_debug > 0) {
03764          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03765       }
03766       return;
03767    }
03768 
03769    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03770       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03771       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03772       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03773       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03774    }
03775 
03776    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
03777       struct timeval tv = { inttime, };
03778       struct ast_tm tm;
03779       ast_localtime(&tv, &tm, NULL);
03780       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03781       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03782    }
03783    ast_config_destroy(msg_cfg);
03784 }
03785 
03786 /*!
03787  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
03788  * \param from The string to work with.
03789  * \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.
03790  * 
03791  * \return The destination string with quotes wrapped on it (the to field).
03792  */
03793 static char *quote(const char *from, char *to, size_t len)
03794 {
03795    char *ptr = to;
03796    *ptr++ = '"';
03797    for (; ptr < to + len - 1; from++) {
03798       if (*from == '"')
03799          *ptr++ = '\\';
03800       else if (*from == '\0')
03801          break;
03802       *ptr++ = *from;
03803    }
03804    if (ptr < to + len - 1)
03805       *ptr++ = '"';
03806    *ptr = '\0';
03807    return to;
03808 }
03809 
03810 /*! \brief
03811  * fill in *tm for current time according to the proper timezone, if any.
03812  * Return tm so it can be used as a function argument.
03813  */
03814 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
03815 {
03816    const struct vm_zone *z = NULL;
03817    struct timeval t = ast_tvnow();
03818 
03819    /* Does this user have a timezone specified? */
03820    if (!ast_strlen_zero(vmu->zonetag)) {
03821       /* Find the zone in the list */
03822       AST_LIST_LOCK(&zones);
03823       AST_LIST_TRAVERSE(&zones, z, list) {
03824          if (!strcmp(z->name, vmu->zonetag))
03825             break;
03826       }
03827       AST_LIST_UNLOCK(&zones);
03828    }
03829    ast_localtime(&t, tm, z ? z->timezone : NULL);
03830    return tm;
03831 }
03832 
03833 /*!\brief Check if the string would need encoding within the MIME standard, to
03834  * avoid confusing certain mail software that expects messages to be 7-bit
03835  * clean.
03836  */
03837 static int check_mime(const char *str)
03838 {
03839    for (; *str; str++) {
03840       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03841          return 1;
03842       }
03843    }
03844    return 0;
03845 }
03846 
03847 /*!\brief Encode a string according to the MIME rules for encoding strings
03848  * that are not 7-bit clean or contain control characters.
03849  *
03850  * Additionally, if the encoded string would exceed the MIME limit of 76
03851  * characters per line, then the encoding will be broken up into multiple
03852  * sections, separated by a space character, in order to facilitate
03853  * breaking up the associated header across multiple lines.
03854  *
03855  * \param start A string to be encoded
03856  * \param end An expandable buffer for holding the result
03857  * \param preamble The length of the first line already used for this string,
03858  * to ensure that each line maintains a maximum length of 76 chars.
03859  * \param postamble the length of any additional characters appended to the
03860  * line, used to ensure proper field wrapping.
03861  * \retval The encoded string.
03862  */
03863 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03864 {
03865    char tmp[80];
03866    int first_section = 1;
03867    size_t endlen = 0, tmplen = 0;
03868    *end = '\0';
03869 
03870    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03871    for (; *start; start++) {
03872       int need_encoding = 0;
03873       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03874          need_encoding = 1;
03875       }
03876       if ((first_section && need_encoding && preamble + tmplen > 70) ||
03877          (first_section && !need_encoding && preamble + tmplen > 72) ||
03878          (!first_section && need_encoding && tmplen > 70) ||
03879          (!first_section && !need_encoding && tmplen > 72)) {
03880          /* Start new line */
03881          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
03882          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03883          first_section = 0;
03884       }
03885       if (need_encoding && *start == ' ') {
03886          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
03887       } else if (need_encoding) {
03888          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
03889       } else {
03890          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
03891       }
03892    }
03893    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
03894    return end;
03895 }
03896 
03897 /*!
03898  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
03899  * \param p The output file to generate the email contents into.
03900  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
03901  * \param vmu The voicemail user who is sending the voicemail.
03902  * \param msgnum The message index in the mailbox folder.
03903  * \param context 
03904  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
03905  * \param cidnum The caller ID number.
03906  * \param cidname The caller ID name.
03907  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
03908  * \param format The message sound file format. i.e. .wav
03909  * \param duration The time of the message content, in seconds.
03910  * \param attach_user_voicemail if 1, the sound file is attached to the email.
03911  * \param chan
03912  * \param category
03913  * \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.
03914  *
03915  * 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.
03916  */
03917 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)
03918 {
03919    char date[256];
03920    char host[MAXHOSTNAMELEN] = "";
03921    char who[256];
03922    char bound[256];
03923    char dur[256];
03924    struct ast_tm tm;
03925    char enc_cidnum[256] = "", enc_cidname[256] = "";
03926    char *passdata = NULL, *passdata2;
03927    size_t len_passdata = 0, len_passdata2, tmplen;
03928    char *greeting_attachment; 
03929    char filename[256];
03930 
03931 #ifdef IMAP_STORAGE
03932 #define ENDL "\r\n"
03933 #else
03934 #define ENDL "\n"
03935 #endif
03936 
03937    /* One alloca for multiple fields */
03938    len_passdata2 = strlen(vmu->fullname);
03939    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
03940       len_passdata2 = tmplen;
03941    }
03942    if ((tmplen = strlen(fromstring)) > len_passdata2) {
03943       len_passdata2 = tmplen;
03944    }
03945    len_passdata2 = len_passdata2 * 3 + 200;
03946    passdata2 = alloca(len_passdata2);
03947 
03948    if (cidnum) {
03949       strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
03950    }
03951    if (cidname) {
03952       strip_control(cidname, enc_cidname, sizeof(enc_cidname));
03953    }
03954    gethostname(host, sizeof(host) - 1);
03955 
03956    if (strchr(srcemail, '@'))
03957       ast_copy_string(who, srcemail, sizeof(who));
03958    else 
03959       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03960    
03961    greeting_attachment = strrchr(ast_strdupa(attach), '/');
03962    if (greeting_attachment)
03963       *greeting_attachment++ = '\0';
03964 
03965    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03966    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03967    fprintf(p, "Date: %s" ENDL, date);
03968 
03969    /* Set date format for voicemail mail */
03970    ast_strftime(date, sizeof(date), emaildateformat, &tm);
03971 
03972    if (!ast_strlen_zero(fromstring)) {
03973       struct ast_channel *ast;
03974       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03975          char *ptr;
03976          memset(passdata2, 0, len_passdata2);
03977          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
03978          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
03979          len_passdata = strlen(passdata2) * 3 + 300;
03980          passdata = alloca(len_passdata);
03981          if (check_mime(passdata2)) {
03982             int first_line = 1;
03983             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
03984             while ((ptr = strchr(passdata, ' '))) {
03985                *ptr = '\0';
03986                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
03987                first_line = 0;
03988                passdata = ptr + 1;
03989             }
03990             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
03991          } else {
03992             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
03993          }
03994          ast_channel_free(ast);
03995       } else {
03996          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03997       }
03998    } else {
03999       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
04000    }
04001 
04002    if (check_mime(vmu->fullname)) {
04003       int first_line = 1;
04004       char *ptr;
04005       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
04006       while ((ptr = strchr(passdata2, ' '))) {
04007          *ptr = '\0';
04008          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
04009          first_line = 0;
04010          passdata2 = ptr + 1;
04011       }
04012       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04013    } else {
04014       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04015    }
04016    if (!ast_strlen_zero(emailsubject)) {
04017       struct ast_channel *ast;
04018       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04019          int vmlen = strlen(emailsubject) * 3 + 200;
04020          /* Only allocate more space if the previous was not large enough */
04021          if (vmlen > len_passdata) {
04022             passdata = alloca(vmlen);
04023             len_passdata = vmlen;
04024          }
04025 
04026          memset(passdata, 0, len_passdata);
04027          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04028          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
04029          if (check_mime(passdata)) {
04030             int first_line = 1;
04031             char *ptr;
04032             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04033             while ((ptr = strchr(passdata2, ' '))) {
04034                *ptr = '\0';
04035                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04036                first_line = 0;
04037                passdata2 = ptr + 1;
04038             }
04039             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04040          } else {
04041             fprintf(p, "Subject: %s" ENDL, passdata);
04042          }
04043          ast_channel_free(ast);
04044       } else {
04045          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04046       }
04047    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04048       if (ast_strlen_zero(flag)) {
04049          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04050       } else {
04051          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04052       }
04053    } else {
04054       if (ast_strlen_zero(flag)) {
04055          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04056       } else {
04057          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04058       }
04059    }
04060 
04061    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04062    if (imap) {
04063       /* additional information needed for IMAP searching */
04064       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04065       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04066       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04067       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04068 #ifdef IMAP_STORAGE
04069       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04070 #else
04071       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04072 #endif
04073       /* flag added for Urgent */
04074       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04075       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04076       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04077       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04078       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04079       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04080       if (!ast_strlen_zero(category)) {
04081          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04082       } else {
04083          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04084       }
04085       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04086       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04087       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04088    }
04089    if (!ast_strlen_zero(cidnum)) {
04090       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04091    }
04092    if (!ast_strlen_zero(cidname)) {
04093       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04094    }
04095    fprintf(p, "MIME-Version: 1.0" ENDL);
04096    if (attach_user_voicemail) {
04097       /* Something unique. */
04098       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04099 
04100       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04101       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04102       fprintf(p, "--%s" ENDL, bound);
04103    }
04104    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04105    if (emailbody) {
04106       struct ast_channel *ast;
04107       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04108          char *passdata;
04109          int vmlen = strlen(emailbody)*3 + 200;
04110          passdata = alloca(vmlen);
04111          memset(passdata, 0, vmlen);
04112          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04113          pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
04114          fprintf(p, "%s" ENDL, passdata);
04115          ast_channel_free(ast);
04116       } else
04117          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04118    } else if (msgnum > -1) {
04119       if (strcmp(vmu->mailbox, mailbox)) {
04120          /* Forwarded type */
04121          struct ast_config *msg_cfg;
04122          const char *v;
04123          int inttime;
04124          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04125          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04126          /* Retrieve info from VM attribute file */
04127          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04128          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04129          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04130             strcat(fromfile, ".txt");
04131          }
04132          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04133             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04134                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04135             }
04136 
04137             /* You might be tempted to do origdate, except that a) it's in the wrong
04138              * format, and b) it's missing for IMAP recordings. */
04139             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
04140                struct timeval tv = { inttime, };
04141                struct ast_tm tm;
04142                ast_localtime(&tv, &tm, NULL);
04143                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04144             }
04145             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04146                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04147                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04148                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04149                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04150                date, origcallerid, origdate);
04151             ast_config_destroy(msg_cfg);
04152          } else {
04153             goto plain_message;
04154          }
04155       } else {
04156 plain_message:
04157          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04158             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04159             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04160             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04161             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04162       }
04163    } else {
04164       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04165             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04166    }
04167 
04168    if (imap || attach_user_voicemail) {
04169       if (!ast_strlen_zero(attach2)) {
04170          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04171          ast_debug(5, "creating second attachment filename %s\n", filename);
04172          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04173          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04174          ast_debug(5, "creating attachment filename %s\n", filename);
04175          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04176       } else {
04177          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04178          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04179          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04180       }
04181    }
04182 }
04183 
04184 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)
04185 {
04186    char tmpdir[256], newtmp[256];
04187    char fname[256];
04188    char tmpcmd[256];
04189    int tmpfd = -1;
04190 
04191    /* Eww. We want formats to tell us their own MIME type */
04192    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04193 
04194    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04195       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04196       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04197       tmpfd = mkstemp(newtmp);
04198       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04199       ast_debug(3, "newtmp: %s\n", newtmp);
04200       if (tmpfd > -1) {
04201          int soxstatus;
04202          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04203          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04204             attach = newtmp;
04205             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04206          } else {
04207             ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04208                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04209             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04210          }
04211       }
04212    }
04213    fprintf(p, "--%s" ENDL, bound);
04214    if (msgnum > -1)
04215       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04216    else
04217       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
04218    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04219    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04220    if (msgnum > -1)
04221       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04222    else
04223       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
04224    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04225    base_encode(fname, p);
04226    if (last)
04227       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04228    if (tmpfd > -1) {
04229       unlink(fname);
04230       close(tmpfd);
04231       unlink(newtmp);
04232    }
04233    return 0;
04234 }
04235 #undef ENDL
04236 
04237 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)
04238 {
04239    FILE *p=NULL;
04240    char tmp[80] = "/tmp/astmail-XXXXXX";
04241    char tmp2[256];
04242 
04243    if (vmu && ast_strlen_zero(vmu->email)) {
04244       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04245       return(0);
04246    }
04247    if (!strcmp(format, "wav49"))
04248       format = "WAV";
04249    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));
04250    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04251       command hangs */
04252    if ((p = vm_mkftemp(tmp)) == NULL) {
04253       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04254       return -1;
04255    } else {
04256       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04257       fclose(p);
04258       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04259       ast_safe_system(tmp2);
04260       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04261    }
04262    return 0;
04263 }
04264 
04265 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)
04266 {
04267    char date[256];
04268    char host[MAXHOSTNAMELEN] = "";
04269    char who[256];
04270    char dur[PATH_MAX];
04271    char tmp[80] = "/tmp/astmail-XXXXXX";
04272    char tmp2[PATH_MAX];
04273    struct ast_tm tm;
04274    FILE *p;
04275 
04276    if ((p = vm_mkftemp(tmp)) == NULL) {
04277       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04278       return -1;
04279    }
04280    gethostname(host, sizeof(host)-1);
04281    if (strchr(srcemail, '@'))
04282       ast_copy_string(who, srcemail, sizeof(who));
04283    else 
04284       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04285    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04286    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04287    fprintf(p, "Date: %s\n", date);
04288 
04289    if (*pagerfromstring) {
04290       struct ast_channel *ast;
04291       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04292          char *passdata;
04293          int vmlen = strlen(fromstring)*3 + 200;
04294          passdata = alloca(vmlen);
04295          memset(passdata, 0, vmlen);
04296          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04297          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04298          fprintf(p, "From: %s <%s>\n", passdata, who);
04299          ast_channel_free(ast);
04300       } else 
04301          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04302    } else
04303       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04304    fprintf(p, "To: %s\n", pager);
04305    if (pagersubject) {
04306       struct ast_channel *ast;
04307       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04308          char *passdata;
04309          int vmlen = strlen(pagersubject) * 3 + 200;
04310          passdata = alloca(vmlen);
04311          memset(passdata, 0, vmlen);
04312          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04313          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04314          fprintf(p, "Subject: %s\n\n", passdata);
04315          ast_channel_free(ast);
04316       } else
04317          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04318    } else {
04319       if (ast_strlen_zero(flag)) {
04320          fprintf(p, "Subject: New VM\n\n");
04321       } else {
04322          fprintf(p, "Subject: New %s VM\n\n", flag);
04323       }
04324    }
04325 
04326    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04327    if (pagerbody) {
04328       struct ast_channel *ast;
04329       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04330          char *passdata;
04331          int vmlen = strlen(pagerbody) * 3 + 200;
04332          passdata = alloca(vmlen);
04333          memset(passdata, 0, vmlen);
04334          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04335          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04336          fprintf(p, "%s\n", passdata);
04337          ast_channel_free(ast);
04338       } else
04339          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04340    } else {
04341       fprintf(p, "New %s long %s msg in box %s\n"
04342             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04343    }
04344    fclose(p);
04345    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04346    ast_safe_system(tmp2);
04347    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04348    return 0;
04349 }
04350 
04351 /*!
04352  * \brief Gets the current date and time, as formatted string.
04353  * \param s The buffer to hold the output formatted date.
04354  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04355  * 
04356  * The date format string used is "%a %b %e %r UTC %Y".
04357  * 
04358  * \return zero on success, -1 on error.
04359  */
04360 static int get_date(char *s, int len)
04361 {
04362    struct ast_tm tm;
04363    struct timeval t = ast_tvnow();
04364    
04365    ast_localtime(&t, &tm, "UTC");
04366 
04367    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04368 }
04369 
04370 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04371 {
04372    int res;
04373    char fn[PATH_MAX];
04374    char dest[PATH_MAX];
04375 
04376    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04377 
04378    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04379       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04380       return -1;
04381    }
04382 
04383    RETRIEVE(fn, -1, ext, context);
04384    if (ast_fileexists(fn, NULL, NULL) > 0) {
04385       res = ast_stream_and_wait(chan, fn, ecodes);
04386       if (res) {
04387          DISPOSE(fn, -1);
04388          return res;
04389       }
04390    } else {
04391       /* Dispose just in case */
04392       DISPOSE(fn, -1);
04393       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04394       if (res)
04395          return res;
04396       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04397       if (res)
04398          return res;
04399    }
04400    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04401    return res;
04402 }
04403 
04404 static void free_zone(struct vm_zone *z)
04405 {
04406    ast_free(z);
04407 }
04408 
04409 #ifdef ODBC_STORAGE
04410 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
04411 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04412 {
04413    int x = -1;
04414    int res;
04415    SQLHSTMT stmt;
04416    char sql[PATH_MAX];
04417    char rowdata[20];
04418    char tmp[PATH_MAX] = "";
04419    struct odbc_obj *obj;
04420    char *context;
04421    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04422 
04423    if (newmsgs)
04424       *newmsgs = 0;
04425    if (oldmsgs)
04426       *oldmsgs = 0;
04427    if (urgentmsgs)
04428       *urgentmsgs = 0;
04429 
04430    /* If no mailbox, return immediately */
04431    if (ast_strlen_zero(mailbox))
04432       return 0;
04433 
04434    ast_copy_string(tmp, mailbox, sizeof(tmp));
04435    
04436    context = strchr(tmp, '@');
04437    if (context) {
04438       *context = '\0';
04439       context++;
04440    } else
04441       context = "default";
04442    
04443    obj = ast_odbc_request_obj(odbc_database, 0);
04444    if (obj) {
04445       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04446       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04447       if (!stmt) {
04448          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04449          ast_odbc_release_obj(obj);
04450          goto yuck;
04451       }
04452       res = SQLFetch(stmt);
04453       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04454          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04455          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04456          ast_odbc_release_obj(obj);
04457          goto yuck;
04458       }
04459       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04460       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04461          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04462          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04463          ast_odbc_release_obj(obj);
04464          goto yuck;
04465       }
04466       *newmsgs = atoi(rowdata);
04467       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04468 
04469       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04470       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04471       if (!stmt) {
04472          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04473          ast_odbc_release_obj(obj);
04474          goto yuck;
04475       }
04476       res = SQLFetch(stmt);
04477       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04478          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04479          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04480          ast_odbc_release_obj(obj);
04481          goto yuck;
04482       }
04483       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04484       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04485          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04486          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04487          ast_odbc_release_obj(obj);
04488          goto yuck;
04489       }
04490       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04491       *oldmsgs = atoi(rowdata);
04492 
04493       if (!urgentmsgs) {
04494          x = 0;
04495          ast_odbc_release_obj(obj);
04496          goto yuck;
04497       }
04498 
04499       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04500       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04501       if (!stmt) {
04502          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04503          ast_odbc_release_obj(obj);
04504          goto yuck;
04505       }
04506       res = SQLFetch(stmt);
04507       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04508          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04509          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04510          ast_odbc_release_obj(obj);
04511          goto yuck;
04512       }
04513       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04514       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04515          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04516          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04517          ast_odbc_release_obj(obj);
04518          goto yuck;
04519       }
04520       *urgentmsgs = atoi(rowdata);
04521       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04522       ast_odbc_release_obj(obj);
04523       x = 0;
04524    } else
04525       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04526       
04527 yuck: 
04528    return x;
04529 }
04530 
04531 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04532 {
04533    return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
04534 }
04535 
04536 /*!
04537  * \brief Gets the number of messages that exist in a mailbox folder.
04538  * \param context
04539  * \param mailbox
04540  * \param folder
04541  * 
04542  * This method is used when ODBC backend is used.
04543  * \return The number of messages in this mailbox folder (zero or more).
04544  */
04545 static int messagecount(const char *context, const char *mailbox, const char *folder)
04546 {
04547    struct odbc_obj *obj = NULL;
04548    int nummsgs = 0;
04549    int res;
04550    SQLHSTMT stmt = NULL;
04551    char sql[PATH_MAX];
04552    char rowdata[20];
04553    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04554    if (!folder)
04555       folder = "INBOX";
04556    /* If no mailbox, return immediately */
04557    if (ast_strlen_zero(mailbox))
04558       return 0;
04559 
04560    obj = ast_odbc_request_obj(odbc_database, 0);
04561    if (obj) {
04562       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04563       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04564       if (!stmt) {
04565          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04566          goto yuck;
04567       }
04568       res = SQLFetch(stmt);
04569       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04570          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04571          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04572          goto yuck;
04573       }
04574       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04575       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04576          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04577          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04578          goto yuck;
04579       }
04580       nummsgs = atoi(rowdata);
04581       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04582    } else
04583       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04584 
04585 yuck:
04586    if (obj)
04587       ast_odbc_release_obj(obj);
04588    return nummsgs;
04589 }
04590 
04591 /** 
04592  * \brief Determines if the given folder has messages.
04593  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04594  * 
04595  * This function is used when the mailbox is stored in an ODBC back end.
04596  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04597  * \return 1 if the folder has one or more messages. zero otherwise.
04598  */
04599 static int has_voicemail(const char *mailbox, const char *folder)
04600 {
04601    char tmp[256], *tmp2 = tmp, *box, *context;
04602    ast_copy_string(tmp, mailbox, sizeof(tmp));
04603    while ((context = box = strsep(&tmp2, ","))) {
04604       strsep(&context, "@");
04605       if (ast_strlen_zero(context))
04606          context = "default";
04607       if (messagecount(context, box, folder))
04608          return 1;
04609    }
04610    return 0;
04611 }
04612 #endif
04613 #ifndef IMAP_STORAGE
04614 /*! 
04615  * \brief Copies a message from one mailbox to another.
04616  * \param chan
04617  * \param vmu
04618  * \param imbox
04619  * \param msgnum
04620  * \param duration
04621  * \param recip
04622  * \param fmt
04623  * \param dir
04624  *
04625  * This is only used by file storage based mailboxes.
04626  *
04627  * \return zero on success, -1 on error.
04628  */
04629 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)
04630 {
04631    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04632    const char *frombox = mbox(imbox);
04633    int recipmsgnum;
04634 
04635    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04636 
04637    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04638       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04639    } else {
04640       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04641    }
04642    
04643    if (!dir)
04644       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04645    else
04646       ast_copy_string(fromdir, dir, sizeof(fromdir));
04647 
04648    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04649    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04650 
04651    if (vm_lock_path(todir))
04652       return ERROR_LOCK_PATH;
04653 
04654    recipmsgnum = last_message_index(recip, todir) + 1;
04655    if (recipmsgnum < recip->maxmsg) {
04656       make_file(topath, sizeof(topath), todir, recipmsgnum);
04657       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04658          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04659       } else {
04660          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04661           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04662           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04663           * much worse problem happening and IMAP storage doesn't call this function
04664           */
04665          copy_plain_file(frompath, topath);
04666          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04667          vm_delete(topath);
04668       }
04669    } else {
04670       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04671    }
04672    ast_unlock_path(todir);
04673    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04674    
04675    return 0;
04676 }
04677 #endif
04678 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04679 
04680 static int messagecount(const char *context, const char *mailbox, const char *folder)
04681 {
04682    return __has_voicemail(context, mailbox, folder, 0);
04683 }
04684 
04685 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04686 {
04687    DIR *dir;
04688    struct dirent *de;
04689    char fn[256];
04690    int ret = 0;
04691 
04692    /* If no mailbox, return immediately */
04693    if (ast_strlen_zero(mailbox))
04694       return 0;
04695 
04696    if (ast_strlen_zero(folder))
04697       folder = "INBOX";
04698    if (ast_strlen_zero(context))
04699       context = "default";
04700 
04701    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04702 
04703    if (!(dir = opendir(fn)))
04704       return 0;
04705 
04706    while ((de = readdir(dir))) {
04707       if (!strncasecmp(de->d_name, "msg", 3)) {
04708          if (shortcircuit) {
04709             ret = 1;
04710             break;
04711          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
04712             if (shortcircuit) return 1;
04713             ret++;
04714          }
04715       }
04716    }
04717 
04718    closedir(dir);
04719 
04720    /* If we are checking INBOX, we should check Urgent as well */
04721    if (strcmp(folder, "INBOX") == 0) {
04722       return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
04723    } else {
04724       return ret;
04725    }
04726 }
04727 
04728 /** 
04729  * \brief Determines if the given folder has messages.
04730  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04731  * \param folder the folder to look in
04732  *
04733  * This function is used when the mailbox is stored in a filesystem back end.
04734  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04735  * \return 1 if the folder has one or more messages. zero otherwise.
04736  */
04737 static int has_voicemail(const char *mailbox, const char *folder)
04738 {
04739    char tmp[256], *tmp2 = tmp, *box, *context;
04740    ast_copy_string(tmp, mailbox, sizeof(tmp));
04741    while ((box = strsep(&tmp2, ","))) {
04742       if ((context = strchr(box, '@')))
04743          *context++ = '\0';
04744       else
04745          context = "default";
04746       if (__has_voicemail(context, box, folder, 1))
04747          return 1;
04748    }
04749    return 0;
04750 }
04751 
04752 
04753 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04754 {
04755    char tmp[256];
04756    char *context;
04757 
04758    /* If no mailbox, return immediately */
04759    if (ast_strlen_zero(mailbox))
04760       return 0;
04761 
04762    if (newmsgs)
04763       *newmsgs = 0;
04764    if (oldmsgs)
04765       *oldmsgs = 0;
04766    if (urgentmsgs)
04767       *urgentmsgs = 0;
04768 
04769    if (strchr(mailbox, ',')) {
04770       int tmpnew, tmpold, tmpurgent;
04771       char *mb, *cur;
04772 
04773       ast_copy_string(tmp, mailbox, sizeof(tmp));
04774       mb = tmp;
04775       while ((cur = strsep(&mb, ", "))) {
04776          if (!ast_strlen_zero(cur)) {
04777             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04778                return -1;
04779             else {
04780                if (newmsgs)
04781                   *newmsgs += tmpnew; 
04782                if (oldmsgs)
04783                   *oldmsgs += tmpold;
04784                if (urgentmsgs)
04785                   *urgentmsgs += tmpurgent;
04786             }
04787          }
04788       }
04789       return 0;
04790    }
04791 
04792    ast_copy_string(tmp, mailbox, sizeof(tmp));
04793    
04794    if ((context = strchr(tmp, '@')))
04795       *context++ = '\0';
04796    else
04797       context = "default";
04798 
04799    if (newmsgs)
04800       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04801    if (oldmsgs)
04802       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04803    if (urgentmsgs)
04804       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
04805 
04806    return 0;
04807 }
04808 
04809 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04810 {
04811    return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
04812 }
04813 
04814 #endif
04815 
04816 static void run_externnotify(char *context, char *extension, const char *flag)
04817 {
04818    char arguments[255];
04819    char ext_context[256] = "";
04820    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
04821    struct ast_smdi_mwi_message *mwi_msg;
04822 
04823    if (!ast_strlen_zero(context))
04824       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04825    else
04826       ast_copy_string(ext_context, extension, sizeof(ext_context));
04827 
04828    if (smdi_iface) {
04829       if (ast_app_has_voicemail(ext_context, NULL)) 
04830          ast_smdi_mwi_set(smdi_iface, extension);
04831       else
04832          ast_smdi_mwi_unset(smdi_iface, extension);
04833 
04834       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04835          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04836          if (!strncmp(mwi_msg->cause, "INV", 3))
04837             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04838          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04839             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04840          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04841          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04842       } else {
04843          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
04844       }
04845    }
04846 
04847    if (!ast_strlen_zero(externnotify)) {
04848       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
04849          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04850       } else {
04851          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
04852          ast_debug(1, "Executing %s\n", arguments);
04853          ast_safe_system(arguments);
04854       }
04855    }
04856 }
04857 
04858 /*!
04859  * \brief Variables used for saving a voicemail.
04860  *
04861  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
04862  */
04863 struct leave_vm_options {
04864    unsigned int flags;
04865    signed char record_gain;
04866    char *exitcontext;
04867 };
04868 
04869 /*!
04870  * \brief Prompts the user and records a voicemail to a mailbox.
04871  * \param chan
04872  * \param ext
04873  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
04874  * 
04875  * 
04876  * 
04877  * \return zero on success, -1 on error.
04878  */
04879 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04880 {
04881 #ifdef IMAP_STORAGE
04882    int newmsgs, oldmsgs;
04883 #else
04884    char urgdir[PATH_MAX];
04885 #endif
04886    char txtfile[PATH_MAX];
04887    char tmptxtfile[PATH_MAX];
04888    struct vm_state *vms = NULL;
04889    char callerid[256];
04890    FILE *txt;
04891    char date[256];
04892    int txtdes;
04893    int res = 0;
04894    int msgnum;
04895    int duration = 0;
04896    int ausemacro = 0;
04897    int ousemacro = 0;
04898    int ouseexten = 0;
04899    int rtmsgid = 0;
04900    char tmpid[16];
04901    char tmpdur[16];
04902    char priority[16];
04903    char origtime[16];
04904    char dir[PATH_MAX];
04905    char tmpdir[PATH_MAX];
04906    char fn[PATH_MAX];
04907    char prefile[PATH_MAX] = "";
04908    char tempfile[PATH_MAX] = "";
04909    char ext_context[256] = "";
04910    char fmt[80];
04911    char *context;
04912    char ecodes[17] = "#";
04913    struct ast_str *tmp = ast_str_create(16);
04914    char *tmpptr;
04915    struct ast_vm_user *vmu;
04916    struct ast_vm_user svm;
04917    const char *category = NULL;
04918    const char *code;
04919    const char *alldtmf = "0123456789ABCD*#";
04920    char flag[80];
04921 
04922    ast_str_set(&tmp, 0, "%s", ext);
04923    ext = ast_str_buffer(tmp);
04924    if ((context = strchr(ext, '@'))) {
04925       *context++ = '\0';
04926       tmpptr = strchr(context, '&');
04927    } else {
04928       tmpptr = strchr(ext, '&');
04929    }
04930 
04931    if (tmpptr)
04932       *tmpptr++ = '\0';
04933 
04934    ast_channel_lock(chan);
04935    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
04936       category = ast_strdupa(category);
04937    }
04938    ast_channel_unlock(chan);
04939 
04940    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
04941       ast_copy_string(flag, "Urgent", sizeof(flag));
04942    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
04943       ast_copy_string(flag, "PRIORITY", sizeof(flag));
04944    } else {
04945       flag[0] = '\0';
04946    }
04947 
04948    ast_debug(3, "Before find_user\n");
04949    if (!(vmu = find_user(&svm, context, ext))) {
04950       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
04951       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04952       ast_free(tmp);
04953       return res;
04954    }
04955    /* Setup pre-file if appropriate */
04956    if (strcmp(vmu->context, "default"))
04957       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
04958    else
04959       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
04960 
04961    /* Set the path to the prefile. Will be one of 
04962       VM_SPOOL_DIRcontext/ext/busy
04963       VM_SPOOL_DIRcontext/ext/unavail
04964       Depending on the flag set in options.
04965    */
04966    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
04967       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
04968    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
04969       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
04970    }
04971    /* Set the path to the tmpfile as
04972       VM_SPOOL_DIR/context/ext/temp
04973       and attempt to create the folder structure.
04974    */
04975    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
04976    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
04977       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
04978       ast_free(tmp);
04979       return -1;
04980    }
04981    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
04982    if (ast_fileexists(tempfile, NULL, NULL) > 0)
04983       ast_copy_string(prefile, tempfile, sizeof(prefile));
04984 
04985    DISPOSE(tempfile, -1);
04986    /* It's easier just to try to make it than to check for its existence */
04987    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
04988 
04989    /* Check current or macro-calling context for special extensions */
04990    if (ast_test_flag(vmu, VM_OPERATOR)) {
04991       if (!ast_strlen_zero(vmu->exit)) {
04992          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
04993             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04994             ouseexten = 1;
04995          }
04996       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
04997          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04998          ouseexten = 1;
04999       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
05000          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
05001          ousemacro = 1;
05002       }
05003    }
05004 
05005    if (!ast_strlen_zero(vmu->exit)) {
05006       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
05007          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05008    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
05009       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05010    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
05011       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
05012       ausemacro = 1;
05013    }
05014 
05015    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05016       for (code = alldtmf; *code; code++) {
05017          char e[2] = "";
05018          e[0] = *code;
05019          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05020             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05021       }
05022    }
05023 
05024    /* Play the beginning intro if desired */
05025    if (!ast_strlen_zero(prefile)) {
05026 #ifdef ODBC_STORAGE
05027       int success = 
05028 #endif
05029          RETRIEVE(prefile, -1, ext, context);
05030       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05031          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05032             res = ast_waitstream(chan, ecodes);
05033 #ifdef ODBC_STORAGE
05034          if (success == -1) {
05035             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05036             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05037             store_file(prefile, vmu->mailbox, vmu->context, -1);
05038          }
05039 #endif
05040       } else {
05041          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05042          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05043       }
05044       DISPOSE(prefile, -1);
05045       if (res < 0) {
05046          ast_debug(1, "Hang up during prefile playback\n");
05047          free_user(vmu);
05048          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05049          ast_free(tmp);
05050          return -1;
05051       }
05052    }
05053    if (res == '#') {
05054       /* On a '#' we skip the instructions */
05055       ast_set_flag(options, OPT_SILENT);
05056       res = 0;
05057    }
05058    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05059       res = ast_stream_and_wait(chan, INTRO, ecodes);
05060       if (res == '#') {
05061          ast_set_flag(options, OPT_SILENT);
05062          res = 0;
05063       }
05064    }
05065    if (res > 0)
05066       ast_stopstream(chan);
05067    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05068     other than the operator -- an automated attendant or mailbox login for example */
05069    if (res == '*') {
05070       chan->exten[0] = 'a';
05071       chan->exten[1] = '\0';
05072       if (!ast_strlen_zero(vmu->exit)) {
05073          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05074       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05075          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05076       }
05077       chan->priority = 0;
05078       free_user(vmu);
05079       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05080       ast_free(tmp);
05081       return 0;
05082    }
05083 
05084    /* Check for a '0' here */
05085    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05086    transfer:
05087       if (ouseexten || ousemacro) {
05088          chan->exten[0] = 'o';
05089          chan->exten[1] = '\0';
05090          if (!ast_strlen_zero(vmu->exit)) {
05091             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05092          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05093             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05094          }
05095          ast_play_and_wait(chan, "transfer");
05096          chan->priority = 0;
05097          free_user(vmu);
05098          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05099       }
05100       ast_free(tmp);
05101       return 0;
05102    }
05103 
05104    /* Allow all other digits to exit Voicemail and return to the dialplan */
05105    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05106       if (!ast_strlen_zero(options->exitcontext))
05107          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05108       free_user(vmu);
05109       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05110       ast_free(tmp);
05111       return res;
05112    }
05113 
05114    if (res < 0) {
05115       free_user(vmu);
05116       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05117       ast_free(tmp);
05118       return -1;
05119    }
05120    /* The meat of recording the message...  All the announcements and beeps have been played*/
05121    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05122    if (!ast_strlen_zero(fmt)) {
05123       msgnum = 0;
05124 
05125 #ifdef IMAP_STORAGE
05126       /* Is ext a mailbox? */
05127       /* must open stream for this user to get info! */
05128       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05129       if (res < 0) {
05130          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05131          ast_free(tmp);
05132          return -1;
05133       }
05134       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05135       /* It is possible under certain circumstances that inboxcount did not
05136        * create a vm_state when it was needed. This is a catchall which will
05137        * rarely be used.
05138        */
05139          if (!(vms = create_vm_state_from_user(vmu))) {
05140             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05141             ast_free(tmp);
05142             return -1;
05143          }
05144       }
05145       vms->newmessages++;
05146       
05147       /* here is a big difference! We add one to it later */
05148       msgnum = newmsgs + oldmsgs;
05149       ast_debug(3, "Messagecount set to %d\n",msgnum);
05150       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05151       /* set variable for compatibility */
05152       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05153 
05154       /* Check if mailbox is full */
05155       check_quota(vms, imapfolder);
05156       if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
05157          ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
05158          ast_play_and_wait(chan, "vm-mailboxfull");
05159          ast_free(tmp);
05160          return -1;
05161       }
05162       
05163       /* Check if we have exceeded maxmsg */
05164       if (msgnum >= vmu->maxmsg) {
05165          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);
05166          ast_play_and_wait(chan, "vm-mailboxfull");
05167          ast_free(tmp);
05168          return -1;
05169       }
05170 #else
05171       if (count_messages(vmu, dir) >= vmu->maxmsg) {
05172          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05173          if (!res)
05174             res = ast_waitstream(chan, "");
05175          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05176          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05177          goto leave_vm_out;
05178       }
05179 
05180 #endif
05181       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05182       txtdes = mkstemp(tmptxtfile);
05183       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05184       if (txtdes < 0) {
05185          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05186          if (!res)
05187             res = ast_waitstream(chan, "");
05188          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05189          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05190          goto leave_vm_out;
05191       }
05192 
05193       /* Now play the beep once we have the message number for our next message. */
05194       if (res >= 0) {
05195          /* Unless we're *really* silent, try to send the beep */
05196          res = ast_stream_and_wait(chan, "beep", "");
05197       }
05198             
05199       /* Store information in real-time storage */
05200       if (ast_check_realtime("voicemail_data")) {
05201          snprintf(priority, sizeof(priority), "%d", chan->priority);
05202          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05203          get_date(date, sizeof(date));
05204          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);
05205       }
05206 
05207       /* Store information */
05208       txt = fdopen(txtdes, "w+");
05209       if (txt) {
05210          get_date(date, sizeof(date));
05211          fprintf(txt, 
05212             ";\n"
05213             "; Message Information file\n"
05214             ";\n"
05215             "[message]\n"
05216             "origmailbox=%s\n"
05217             "context=%s\n"
05218             "macrocontext=%s\n"
05219             "exten=%s\n"
05220             "priority=%d\n"
05221             "callerchan=%s\n"
05222             "callerid=%s\n"
05223             "origdate=%s\n"
05224             "origtime=%ld\n"
05225             "category=%s\n",
05226             ext,
05227             chan->context,
05228             chan->macrocontext, 
05229             chan->exten,
05230             chan->priority,
05231             chan->name,
05232             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05233             date, (long)time(NULL),
05234             category ? category : "");
05235       } else
05236          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05237       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05238 
05239       if (txt) {
05240          fprintf(txt, "flag=%s\n", flag);
05241          if (duration < vmminsecs) {
05242             fclose(txt);
05243             if (option_verbose > 2) 
05244                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05245             ast_filedelete(tmptxtfile, NULL);
05246             unlink(tmptxtfile);
05247             if (ast_check_realtime("voicemail_data")) {
05248                snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05249                ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05250             }
05251          } else {
05252             fprintf(txt, "duration=%d\n", duration);
05253             fclose(txt);
05254             if (vm_lock_path(dir)) {
05255                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05256                /* Delete files */
05257                ast_filedelete(tmptxtfile, NULL);
05258                unlink(tmptxtfile);
05259             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05260                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05261                unlink(tmptxtfile);
05262                ast_unlock_path(dir);
05263                if (ast_check_realtime("voicemail_data")) {
05264                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05265                   ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05266                }
05267             } else {
05268 #ifndef IMAP_STORAGE
05269                msgnum = last_message_index(vmu, dir) + 1;
05270 #endif
05271                make_file(fn, sizeof(fn), dir, msgnum);
05272 
05273                /* assign a variable with the name of the voicemail file */ 
05274 #ifndef IMAP_STORAGE
05275                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05276 #else
05277                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05278 #endif
05279 
05280                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05281                ast_filerename(tmptxtfile, fn, NULL);
05282                rename(tmptxtfile, txtfile);
05283 
05284                /* Properly set permissions on voicemail text descriptor file.
05285                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05286                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05287                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05288 
05289                ast_unlock_path(dir);
05290                if (ast_check_realtime("voicemail_data")) {
05291                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05292                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05293                   ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
05294                }
05295                /* We must store the file first, before copying the message, because
05296                 * ODBC storage does the entire copy with SQL.
05297                 */
05298                if (ast_fileexists(fn, NULL, NULL) > 0) {
05299                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05300                }
05301 
05302                /* Are there to be more recipients of this message? */
05303                while (tmpptr) {
05304                   struct ast_vm_user recipu, *recip;
05305                   char *exten, *cntx;
05306                
05307                   exten = strsep(&tmpptr, "&");
05308                   cntx = strchr(exten, '@');
05309                   if (cntx) {
05310                      *cntx = '\0';
05311                      cntx++;
05312                   }
05313                   if ((recip = find_user(&recipu, cntx, exten))) {
05314                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05315                      free_user(recip);
05316                   }
05317                }
05318 #ifndef IMAP_STORAGE
05319                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05320                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05321                   char sfn[PATH_MAX];
05322                   char dfn[PATH_MAX];
05323                   int x;
05324                   /* It's easier just to try to make it than to check for its existence */
05325                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05326                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n",sfn,dfn);
05327                   x = last_message_index(vmu, urgdir) + 1;
05328                   make_file(sfn, sizeof(sfn), dir, msgnum);
05329                   make_file(dfn, sizeof(dfn), urgdir, x);
05330                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05331                }
05332 #endif
05333                /* Notification needs to happen after the copy, though. */
05334                if (ast_fileexists(fn, NULL, NULL)) {
05335 #ifdef IMAP_STORAGE
05336                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05337 #else
05338                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05339 #endif
05340                }
05341 
05342                /* Disposal needs to happen after the optional move and copy */
05343                if (ast_fileexists(fn, NULL, NULL)) {
05344                   DISPOSE(dir, msgnum);
05345                }
05346             }
05347          }
05348       }
05349       if (res == '0') {
05350          goto transfer;
05351       } else if (res > 0)
05352          res = 0;
05353 
05354       if (duration < vmminsecs)
05355          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05356          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05357       else
05358          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05359    } else
05360       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05361 leave_vm_out:
05362    free_user(vmu);
05363 
05364 #ifdef IMAP_STORAGE
05365    /* expunge message - use UID Expunge if supported on IMAP server*/
05366    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05367    if (expungeonhangup == 1) {
05368       ast_mutex_lock(&vms->lock);
05369 #ifdef HAVE_IMAP_TK2006
05370       if (LEVELUIDPLUS (vms->mailstream)) {
05371          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05372       } else 
05373 #endif
05374          mail_expunge(vms->mailstream);
05375       ast_mutex_unlock(&vms->lock);
05376    }
05377 #endif
05378 
05379    ast_free(tmp);
05380    return res;
05381 }
05382 
05383 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05384 {
05385    int d;
05386    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05387    return d;
05388 }
05389 
05390 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05391 {
05392 #ifdef IMAP_STORAGE
05393    /* we must use mbox(x) folder names, and copy the message there */
05394    /* simple. huh? */
05395    char sequence[10];
05396    char mailbox[256];
05397    int res;
05398 
05399    /* get the real IMAP message number for this message */
05400    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05401    
05402    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05403    ast_mutex_lock(&vms->lock);
05404    /* if save to Old folder, put in INBOX as read */
05405    if (box == OLD_FOLDER) {
05406       mail_setflag(vms->mailstream, sequence, "\\Seen");
05407       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05408    } else if (box == NEW_FOLDER) {
05409       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05410       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05411    }
05412    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05413       ast_mutex_unlock(&vms->lock);
05414       return 0;
05415    }
05416    /* Create the folder if it don't exist */
05417    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05418    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05419    if (mail_create(vms->mailstream, mailbox) == NIL) 
05420       ast_debug(5, "Folder exists.\n");
05421    else
05422       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05423    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05424    ast_mutex_unlock(&vms->lock);
05425    return res;
05426 #else
05427    char *dir = vms->curdir;
05428    char *username = vms->username;
05429    char *context = vmu->context;
05430    char sfn[PATH_MAX];
05431    char dfn[PATH_MAX];
05432    char ddir[PATH_MAX];
05433    const char *dbox = mbox(box);
05434    int x, i;
05435    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05436 
05437    if (vm_lock_path(ddir))
05438       return ERROR_LOCK_PATH;
05439 
05440    x = last_message_index(vmu, ddir) + 1;
05441 
05442    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05443       x--;
05444       for (i = 1; i <= x; i++) {
05445          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05446          make_file(sfn, sizeof(sfn), ddir, i);
05447          make_file(dfn, sizeof(dfn), ddir, i - 1);
05448          if (EXISTS(ddir, i, sfn, NULL)) {
05449             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05450          } else
05451             break;
05452       }
05453    } else {
05454       if (x >= vmu->maxmsg) {
05455          ast_unlock_path(ddir);
05456          return -1;
05457       }
05458    }
05459    make_file(sfn, sizeof(sfn), dir, msg);
05460    make_file(dfn, sizeof(dfn), ddir, x);
05461    if (strcmp(sfn, dfn)) {
05462       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05463    }
05464    ast_unlock_path(ddir);
05465 #endif
05466    return 0;
05467 }
05468 
05469 static int adsi_logo(unsigned char *buf)
05470 {
05471    int bytes = 0;
05472    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05473    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05474    return bytes;
05475 }
05476 
05477 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05478 {
05479    unsigned char buf[256];
05480    int bytes=0;
05481    int x;
05482    char num[5];
05483 
05484    *useadsi = 0;
05485    bytes += ast_adsi_data_mode(buf + bytes);
05486    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05487 
05488    bytes = 0;
05489    bytes += adsi_logo(buf);
05490    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05491 #ifdef DISPLAY
05492    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05493 #endif
05494    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05495    bytes += ast_adsi_data_mode(buf + bytes);
05496    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05497 
05498    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05499       bytes = 0;
05500       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05501       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05502       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05503       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05504       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05505       return 0;
05506    }
05507 
05508 #ifdef DISPLAY
05509    /* Add a dot */
05510    bytes = 0;
05511    bytes += ast_adsi_logo(buf);
05512    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05513    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05514    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05515    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05516 #endif
05517    bytes = 0;
05518    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05519    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05520    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05521    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05522    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05523    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05524    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05525 
05526 #ifdef DISPLAY
05527    /* Add another dot */
05528    bytes = 0;
05529    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05530    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05531 
05532    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05533    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05534 #endif
05535 
05536    bytes = 0;
05537    /* These buttons we load but don't use yet */
05538    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05539    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05540    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05541    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05542    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05543    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05544    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05545 
05546 #ifdef DISPLAY
05547    /* Add another dot */
05548    bytes = 0;
05549    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05550    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05551    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05552 #endif
05553 
05554    bytes = 0;
05555    for (x=0;x<5;x++) {
05556       snprintf(num, sizeof(num), "%d", x);
05557       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05558    }
05559    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05560    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05561 
05562 #ifdef DISPLAY
05563    /* Add another dot */
05564    bytes = 0;
05565    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05566    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05567    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05568 #endif
05569 
05570    if (ast_adsi_end_download(chan)) {
05571       bytes = 0;
05572       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05573       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05574       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05575       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05576       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05577       return 0;
05578    }
05579    bytes = 0;
05580    bytes += ast_adsi_download_disconnect(buf + bytes);
05581    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05582    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05583 
05584    ast_debug(1, "Done downloading scripts...\n");
05585 
05586 #ifdef DISPLAY
05587    /* Add last dot */
05588    bytes = 0;
05589    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05590    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05591 #endif
05592    ast_debug(1, "Restarting session...\n");
05593 
05594    bytes = 0;
05595    /* Load the session now */
05596    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05597       *useadsi = 1;
05598       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05599    } else
05600       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05601 
05602    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05603    return 0;
05604 }
05605 
05606 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05607 {
05608    int x;
05609    if (!ast_adsi_available(chan))
05610       return;
05611    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05612    if (x < 0)
05613       return;
05614    if (!x) {
05615       if (adsi_load_vmail(chan, useadsi)) {
05616          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05617          return;
05618       }
05619    } else
05620       *useadsi = 1;
05621 }
05622 
05623 static void adsi_login(struct ast_channel *chan)
05624 {
05625    unsigned char buf[256];
05626    int bytes=0;
05627    unsigned char keys[8];
05628    int x;
05629    if (!ast_adsi_available(chan))
05630       return;
05631 
05632    for (x=0;x<8;x++)
05633       keys[x] = 0;
05634    /* Set one key for next */
05635    keys[3] = ADSI_KEY_APPS + 3;
05636 
05637    bytes += adsi_logo(buf + bytes);
05638    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05639    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05640    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05641    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05642    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05643    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05644    bytes += ast_adsi_set_keys(buf + bytes, keys);
05645    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05646    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05647 }
05648 
05649 static void adsi_password(struct ast_channel *chan)
05650 {
05651    unsigned char buf[256];
05652    int bytes=0;
05653    unsigned char keys[8];
05654    int x;
05655    if (!ast_adsi_available(chan))
05656       return;
05657 
05658    for (x=0;x<8;x++)
05659       keys[x] = 0;
05660    /* Set one key for next */
05661    keys[3] = ADSI_KEY_APPS + 3;
05662 
05663    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05664    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05665    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05666    bytes += ast_adsi_set_keys(buf + bytes, keys);
05667    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05668    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05669 }
05670 
05671 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05672 {
05673    unsigned char buf[256];
05674    int bytes=0;
05675    unsigned char keys[8];
05676    int x,y;
05677 
05678    if (!ast_adsi_available(chan))
05679       return;
05680 
05681    for (x=0;x<5;x++) {
05682       y = ADSI_KEY_APPS + 12 + start + x;
05683       if (y > ADSI_KEY_APPS + 12 + 4)
05684          y = 0;
05685       keys[x] = ADSI_KEY_SKT | y;
05686    }
05687    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05688    keys[6] = 0;
05689    keys[7] = 0;
05690 
05691    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05692    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
05693    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05694    bytes += ast_adsi_set_keys(buf + bytes, keys);
05695    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05696 
05697    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05698 }
05699 
05700 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
05701 {
05702    int bytes=0;
05703    unsigned char buf[256]; 
05704    char buf1[256], buf2[256];
05705    char fn2[PATH_MAX];
05706 
05707    char cid[256]="";
05708    char *val;
05709    char *name, *num;
05710    char datetime[21]="";
05711    FILE *f;
05712 
05713    unsigned char keys[8];
05714 
05715    int x;
05716 
05717    if (!ast_adsi_available(chan))
05718       return;
05719 
05720    /* Retrieve important info */
05721    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
05722    f = fopen(fn2, "r");
05723    if (f) {
05724       while (!feof(f)) {   
05725          if (!fgets((char *)buf, sizeof(buf), f)) {
05726             continue;
05727          }
05728          if (!feof(f)) {
05729             char *stringp=NULL;
05730             stringp = (char *)buf;
05731             strsep(&stringp, "=");
05732             val = strsep(&stringp, "=");
05733             if (!ast_strlen_zero(val)) {
05734                if (!strcmp((char *)buf, "callerid"))
05735                   ast_copy_string(cid, val, sizeof(cid));
05736                if (!strcmp((char *)buf, "origdate"))
05737                   ast_copy_string(datetime, val, sizeof(datetime));
05738             }
05739          }
05740       }
05741       fclose(f);
05742    }
05743    /* New meaning for keys */
05744    for (x=0;x<5;x++)
05745       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05746    keys[6] = 0x0;
05747    keys[7] = 0x0;
05748 
05749    if (!vms->curmsg) {
05750       /* No prev key, provide "Folder" instead */
05751       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05752    }
05753    if (vms->curmsg >= vms->lastmsg) {
05754       /* If last message ... */
05755       if (vms->curmsg) {
05756          /* but not only message, provide "Folder" instead */
05757          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05758          bytes += ast_adsi_voice_mode(buf + bytes, 0);
05759 
05760       } else {
05761          /* Otherwise if only message, leave blank */
05762          keys[3] = 1;
05763       }
05764    }
05765 
05766    if (!ast_strlen_zero(cid)) {
05767       ast_callerid_parse(cid, &name, &num);
05768       if (!name)
05769          name = num;
05770    } else
05771       name = "Unknown Caller";
05772 
05773    /* If deleted, show "undeleted" */
05774 
05775    if (vms->deleted[vms->curmsg])
05776       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05777 
05778    /* Except "Exit" */
05779    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05780    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05781       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05782    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05783 
05784    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05785    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05786    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05787    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05788    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05789    bytes += ast_adsi_set_keys(buf + bytes, keys);
05790    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05791 
05792    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05793 }
05794 
05795 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05796 {
05797    int bytes=0;
05798    unsigned char buf[256];
05799    unsigned char keys[8];
05800 
05801    int x;
05802 
05803    if (!ast_adsi_available(chan))
05804       return;
05805 
05806    /* New meaning for keys */
05807    for (x=0;x<5;x++)
05808       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05809 
05810    keys[6] = 0x0;
05811    keys[7] = 0x0;
05812 
05813    if (!vms->curmsg) {
05814       /* No prev key, provide "Folder" instead */
05815       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05816    }
05817    if (vms->curmsg >= vms->lastmsg) {
05818       /* If last message ... */
05819       if (vms->curmsg) {
05820          /* but not only message, provide "Folder" instead */
05821          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05822       } else {
05823          /* Otherwise if only message, leave blank */
05824          keys[3] = 1;
05825       }
05826    }
05827 
05828    /* If deleted, show "undeleted" */
05829    if (vms->deleted[vms->curmsg]) 
05830       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05831 
05832    /* Except "Exit" */
05833    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05834    bytes += ast_adsi_set_keys(buf + bytes, keys);
05835    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05836 
05837    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05838 }
05839 
05840 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05841 {
05842    unsigned char buf[256] = "";
05843    char buf1[256] = "", buf2[256] = "";
05844    int bytes=0;
05845    unsigned char keys[8];
05846    int x;
05847 
05848    char *newm = (vms->newmessages == 1) ? "message" : "messages";
05849    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05850    if (!ast_adsi_available(chan))
05851       return;
05852    if (vms->newmessages) {
05853       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05854       if (vms->oldmessages) {
05855          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05856          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05857       } else {
05858          snprintf(buf2, sizeof(buf2), "%s.", newm);
05859       }
05860    } else if (vms->oldmessages) {
05861       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05862       snprintf(buf2, sizeof(buf2), "%s.", oldm);
05863    } else {
05864       strcpy(buf1, "You have no messages.");
05865       buf2[0] = ' ';
05866       buf2[1] = '\0';
05867    }
05868    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05869    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05870    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05871 
05872    for (x=0;x<6;x++)
05873       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05874    keys[6] = 0;
05875    keys[7] = 0;
05876 
05877    /* Don't let them listen if there are none */
05878    if (vms->lastmsg < 0)
05879       keys[0] = 1;
05880    bytes += ast_adsi_set_keys(buf + bytes, keys);
05881 
05882    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05883 
05884    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05885 }
05886 
05887 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05888 {
05889    unsigned char buf[256] = "";
05890    char buf1[256] = "", buf2[256] = "";
05891    int bytes=0;
05892    unsigned char keys[8];
05893    int x;
05894 
05895    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05896 
05897    if (!ast_adsi_available(chan))
05898       return;
05899 
05900    /* Original command keys */
05901    for (x=0;x<6;x++)
05902       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05903 
05904    keys[6] = 0;
05905    keys[7] = 0;
05906 
05907    if ((vms->lastmsg + 1) < 1)
05908       keys[0] = 0;
05909 
05910    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05911       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05912 
05913    if (vms->lastmsg + 1)
05914       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05915    else
05916       strcpy(buf2, "no messages.");
05917    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05918    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05919    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05920    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05921    bytes += ast_adsi_set_keys(buf + bytes, keys);
05922 
05923    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05924 
05925    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05926    
05927 }
05928 
05929 /*
05930 static void adsi_clear(struct ast_channel *chan)
05931 {
05932    char buf[256];
05933    int bytes=0;
05934    if (!ast_adsi_available(chan))
05935       return;
05936    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05937    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05938 
05939    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05940 }
05941 */
05942 
05943 static void adsi_goodbye(struct ast_channel *chan)
05944 {
05945    unsigned char buf[256];
05946    int bytes=0;
05947 
05948    if (!ast_adsi_available(chan))
05949       return;
05950    bytes += adsi_logo(buf + bytes);
05951    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
05952    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
05953    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05954    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05955 
05956    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05957 }
05958 
05959 /*!\brief get_folder: Folder menu
05960  * Plays "press 1 for INBOX messages" etc.
05961  * Should possibly be internationalized
05962  */
05963 static int get_folder(struct ast_channel *chan, int start)
05964 {
05965    int x;
05966    int d;
05967    char fn[PATH_MAX];
05968    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
05969    if (d)
05970       return d;
05971    for (x = start; x< 5; x++) {  /* For all folders */
05972       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
05973          return d;
05974       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
05975       if (d)
05976          return d;
05977       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
05978       d = vm_play_folder_name(chan, fn);
05979       if (d)
05980          return d;
05981       d = ast_waitfordigit(chan, 500);
05982       if (d)
05983          return d;
05984    }
05985    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
05986    if (d)
05987       return d;
05988    d = ast_waitfordigit(chan, 4000);
05989    return d;
05990 }
05991 
05992 /*!
05993  * \brief plays a prompt and waits for a keypress.
05994  * \param chan
05995  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
05996  * \param start Does not appear to be used at this time.
05997  *
05998  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
05999  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
06000  * prompting for the number inputs that correspond to the available folders.
06001  * 
06002  * \return zero on success, or -1 on error.
06003  */
06004 static int get_folder2(struct ast_channel *chan, char *fn, int start)
06005 {
06006    int res = 0;
06007    res = ast_play_and_wait(chan, fn);  /* Folder name */
06008    while (((res < '0') || (res > '9')) &&
06009          (res != '#') && (res >= 0)) {
06010       res = get_folder(chan, 0);
06011    }
06012    return res;
06013 }
06014 
06015 /*!
06016  * \brief presents the option to prepend to an existing message when forwarding it.
06017  * \param chan
06018  * \param vmu
06019  * \param curdir
06020  * \param curmsg
06021  * \param vmfmts
06022  * \param context
06023  * \param record_gain
06024  * \param duration
06025  * \param vms
06026  *
06027  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06028  *
06029  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06030  * \return zero on success, -1 on error.
06031  */
06032 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06033          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06034 {
06035 #ifdef IMAP_STORAGE
06036    int res;
06037 #endif
06038    int cmd = 0;
06039    int retries = 0, prepend_duration = 0, already_recorded = 0;
06040    char msgfile[PATH_MAX], backup[PATH_MAX];
06041    char textfile[PATH_MAX];
06042    struct ast_config *msg_cfg;
06043    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06044 #ifndef IMAP_STORAGE
06045    signed char zero_gain = 0;
06046 #endif
06047    const char *duration_str;
06048 
06049    /* Must always populate duration correctly */
06050    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06051    strcpy(textfile, msgfile);
06052    strcpy(backup, msgfile);
06053    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06054    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06055 
06056    if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06057       *duration = atoi(duration_str);
06058    } else {
06059       *duration = 0;
06060    }
06061 
06062    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06063       if (cmd)
06064          retries = 0;
06065       switch (cmd) {
06066       case '1': 
06067 
06068 #ifdef IMAP_STORAGE
06069          /* Record new intro file */
06070          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06071          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06072          res = ast_play_and_wait(chan, INTRO);
06073          res = ast_play_and_wait(chan, "beep");
06074          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06075          cmd = 't';
06076 #else
06077 
06078          /* prepend a message to the current message, update the metadata and return */
06079 
06080          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06081          strcpy(textfile, msgfile);
06082          strncat(textfile, ".txt", sizeof(textfile) - 1);
06083          *duration = 0;
06084 
06085          /* if we can't read the message metadata, stop now */
06086          if (!msg_cfg) {
06087             cmd = 0;
06088             break;
06089          }
06090          
06091          /* Back up the original file, so we can retry the prepend */
06092          if (already_recorded)
06093             ast_filecopy(backup, msgfile, NULL);
06094          else
06095             ast_filecopy(msgfile, backup, NULL);
06096          already_recorded = 1;
06097 
06098          if (record_gain)
06099             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06100 
06101          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06102          if (record_gain)
06103             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06104 
06105          
06106          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06107             *duration = atoi(duration_str);
06108 
06109          if (prepend_duration) {
06110             struct ast_category *msg_cat;
06111             /* need enough space for a maximum-length message duration */
06112             char duration_buf[12];
06113 
06114             *duration += prepend_duration;
06115             msg_cat = ast_category_get(msg_cfg, "message");
06116             snprintf(duration_buf, 11, "%ld", *duration);
06117             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06118                config_text_file_save(textfile, msg_cfg, "app_voicemail");
06119             }
06120          }
06121 
06122 #endif
06123          break;
06124       case '2': 
06125          /* NULL out introfile so we know there is no intro! */
06126 #ifdef IMAP_STORAGE
06127          *vms->introfn = '\0';
06128 #endif
06129          cmd = 't';
06130          break;
06131       case '*':
06132          cmd = '*';
06133          break;
06134       default: 
06135          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06136             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06137          if (!cmd)
06138             cmd = ast_play_and_wait(chan,"vm-starmain");
06139             /* "press star to return to the main menu" */
06140          if (!cmd)
06141             cmd = ast_waitfordigit(chan,6000);
06142          if (!cmd)
06143             retries++;
06144          if (retries > 3)
06145             cmd = 't';
06146       }
06147    }
06148 
06149    if (msg_cfg)
06150       ast_config_destroy(msg_cfg);
06151    if (already_recorded)
06152       ast_filedelete(backup, NULL);
06153    if (prepend_duration)
06154       *duration = prepend_duration;
06155 
06156    if (cmd == 't' || cmd == 'S')
06157       cmd = 0;
06158    return cmd;
06159 }
06160 
06161 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06162 {
06163    struct ast_event *event;
06164    char *mailbox, *context;
06165 
06166    /* Strip off @default */
06167    context = mailbox = ast_strdupa(box);
06168    strsep(&context, "@");
06169    if (ast_strlen_zero(context))
06170       context = "default";
06171 
06172    if (!(event = ast_event_new(AST_EVENT_MWI,
06173          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06174          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06175          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06176          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06177          AST_EVENT_IE_END))) {
06178       return;
06179    }
06180 
06181    ast_event_queue_and_cache(event);
06182 }
06183 
06184 /*!
06185  * \brief Sends email notification that a user has a new voicemail waiting for them.
06186  * \param chan
06187  * \param vmu
06188  * \param vms
06189  * \param msgnum
06190  * \param duration
06191  * \param fmt
06192  * \param cidnum The Caller ID phone number value.
06193  * \param cidname The Caller ID name value.
06194  *
06195  * \return zero on success, -1 on error.
06196  */
06197 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)
06198 {
06199    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06200    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06201    const char *category;
06202    char *myserveremail = serveremail;
06203 
06204    ast_channel_lock(chan);
06205    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06206       category = ast_strdupa(category);
06207    }
06208    ast_channel_unlock(chan);
06209 
06210    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
06211    make_file(fn, sizeof(fn), todir, msgnum);
06212    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06213 
06214    if (!ast_strlen_zero(vmu->attachfmt)) {
06215       if (strstr(fmt, vmu->attachfmt))
06216          fmt = vmu->attachfmt;
06217       else
06218          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);
06219    }
06220 
06221    /* Attach only the first format */
06222    fmt = ast_strdupa(fmt);
06223    stringp = fmt;
06224    strsep(&stringp, "|");
06225 
06226    if (!ast_strlen_zero(vmu->serveremail))
06227       myserveremail = vmu->serveremail;
06228 
06229    if (!ast_strlen_zero(vmu->email)) {
06230       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06231       if (!attach_user_voicemail)
06232          attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
06233 
06234       if (attach_user_voicemail)
06235          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06236 
06237       /* XXX possible imap issue, should category be NULL XXX */
06238       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06239 
06240       if (attach_user_voicemail)
06241          DISPOSE(todir, msgnum);
06242    }
06243 
06244    if (!ast_strlen_zero(vmu->pager)) {
06245       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06246    }
06247 
06248    if (ast_test_flag(vmu, VM_DELETE))
06249       DELETE(todir, msgnum, fn, vmu);
06250 
06251    /* Leave voicemail for someone */
06252    if (ast_app_has_voicemail(ext_context, NULL)) 
06253       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06254 
06255    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06256 
06257    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);
06258    run_externnotify(vmu->context, vmu->mailbox, flag);
06259 
06260 #ifdef IMAP_STORAGE
06261    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06262    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06263       vm_imap_delete(vms->curmsg, vmu);
06264       vms->newmessages--;  /* Fix new message count */
06265    }
06266 #endif
06267 
06268    return 0;
06269 }
06270 
06271 /*!
06272  * \brief Sends a voicemail message to a mailbox recipient.
06273  * \param ast_channel
06274  * \param context
06275  * \param vms
06276  * \param sender
06277  * \param fmt
06278  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06279  *             Will be 0 when called to forward an existing message (option 8)
06280  *             Will be 1 when called to leave a message (option 3->5)
06281  * \param record_gain 
06282  *
06283  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06284  * 
06285  * When in the leave message mode (is_new_message == 1):
06286  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06287  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06288  *
06289  * When in the forward message mode (is_new_message == 0):
06290  *   - retreives the current message to be forwarded
06291  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06292  *   - determines the target mailbox and folders
06293  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06294  *
06295  * \return zero on success, -1 on error.
06296  */
06297 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)
06298 {
06299 #ifdef IMAP_STORAGE
06300    int todircount=0;
06301    struct vm_state *dstvms;
06302 #endif
06303    char username[70]="";
06304    char fn[PATH_MAX]; /* for playback of name greeting */
06305    char ecodes[16] = "#";
06306    int res = 0, cmd = 0;
06307    struct ast_vm_user *receiver = NULL, *vmtmp;
06308    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06309    char *stringp;
06310    const char *s;
06311    int saved_messages = 0, found = 0;
06312    int valid_extensions = 0;
06313    char *dir;
06314    int curmsg;
06315    char urgent_str[7] = "";
06316    char tmptxtfile[PATH_MAX];
06317 
06318    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06319       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06320    }
06321 
06322    if (vms == NULL) return -1;
06323    dir = vms->curdir;
06324    curmsg = vms->curmsg;
06325 
06326    tmptxtfile[0] = '\0';
06327    while (!res && !valid_extensions) {
06328       int use_directory = 0;
06329       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06330          int done = 0;
06331          int retries = 0;
06332          cmd=0;
06333          while ((cmd >= 0) && !done ){
06334             if (cmd)
06335                retries = 0;
06336             switch (cmd) {
06337             case '1': 
06338                use_directory = 0;
06339                done = 1;
06340                break;
06341             case '2': 
06342                use_directory = 1;
06343                done=1;
06344                break;
06345             case '*': 
06346                cmd = 't';
06347                done = 1;
06348                break;
06349             default: 
06350                /* Press 1 to enter an extension press 2 to use the directory */
06351                cmd = ast_play_and_wait(chan,"vm-forward");
06352                if (!cmd)
06353                   cmd = ast_waitfordigit(chan,3000);
06354                if (!cmd)
06355                   retries++;
06356                if (retries > 3) {
06357                   cmd = 't';
06358                   done = 1;
06359                }
06360                
06361             }
06362          }
06363          if (cmd < 0 || cmd == 't')
06364             break;
06365       }
06366       
06367       if (use_directory) {
06368          /* use app_directory */
06369          
06370          char old_context[sizeof(chan->context)];
06371          char old_exten[sizeof(chan->exten)];
06372          int old_priority;
06373          struct ast_app* directory_app;
06374 
06375          directory_app = pbx_findapp("Directory");
06376          if (directory_app) {
06377             char vmcontext[256];
06378             /* make backup copies */
06379             memcpy(old_context, chan->context, sizeof(chan->context));
06380             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06381             old_priority = chan->priority;
06382             
06383             /* call the the Directory, changes the channel */
06384             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06385             res = pbx_exec(chan, directory_app, vmcontext);
06386             
06387             ast_copy_string(username, chan->exten, sizeof(username));
06388             
06389             /* restore the old context, exten, and priority */
06390             memcpy(chan->context, old_context, sizeof(chan->context));
06391             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06392             chan->priority = old_priority;
06393          } else {
06394             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06395             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06396          }
06397       } else {
06398          /* Ask for an extension */
06399          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06400          if (res)
06401             break;
06402          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06403             break;
06404       }
06405       
06406       /* start all over if no username */
06407       if (ast_strlen_zero(username))
06408          continue;
06409       stringp = username;
06410       s = strsep(&stringp, "*");
06411       /* start optimistic */
06412       valid_extensions = 1;
06413       while (s) {
06414          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06415             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06416             found++;
06417          } else {
06418             /* XXX Optimization for the future.  When we encounter a single bad extension,
06419              * bailing out on all of the extensions may not be the way to go.  We should
06420              * probably just bail on that single extension, then allow the user to enter
06421              * several more. XXX
06422              */
06423             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06424                free_user(receiver);
06425             }
06426             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06427             valid_extensions = 0;
06428             break;
06429          }
06430 
06431          /* play name if available, else play extension number */
06432          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06433          RETRIEVE(fn, -1, s, receiver->context);
06434          if (ast_fileexists(fn, NULL, NULL) > 0) {
06435             res = ast_stream_and_wait(chan, fn, ecodes);
06436             if (res) {
06437                DISPOSE(fn, -1);
06438                return res;
06439             }
06440          } else {
06441             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06442          }
06443          DISPOSE(fn, -1);
06444 
06445          s = strsep(&stringp, "*");
06446       }
06447       /* break from the loop of reading the extensions */
06448       if (valid_extensions)
06449          break;
06450       /* "I am sorry, that's not a valid extension.  Please try again." */
06451       res = ast_play_and_wait(chan, "pbx-invalid");
06452    }
06453    /* check if we're clear to proceed */
06454    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06455       return res;
06456    if (is_new_message == 1) {
06457       struct leave_vm_options leave_options;
06458       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06459       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06460 
06461       /* Send VoiceMail */
06462       memset(&leave_options, 0, sizeof(leave_options));
06463       leave_options.record_gain = record_gain;
06464       cmd = leave_voicemail(chan, mailbox, &leave_options);
06465    } else {
06466       /* Forward VoiceMail */
06467       long duration = 0;
06468       struct vm_state vmstmp;
06469       memcpy(&vmstmp, vms, sizeof(vmstmp));
06470 
06471       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06472 
06473       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06474       if (!cmd) {
06475          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06476 #ifdef IMAP_STORAGE
06477             int attach_user_voicemail;
06478             char *myserveremail = serveremail;
06479             
06480             /* get destination mailbox */
06481             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06482             if (!dstvms) {
06483                dstvms = create_vm_state_from_user(vmtmp);
06484             }
06485             if (dstvms) {
06486                init_mailstream(dstvms, 0);
06487                if (!dstvms->mailstream) {
06488                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06489                } else {
06490                   STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06491                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06492                }
06493             } else {
06494                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06495             }
06496             if (!ast_strlen_zero(vmtmp->serveremail))
06497                myserveremail = vmtmp->serveremail;
06498             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06499             /* NULL category for IMAP storage */
06500             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);
06501 #else
06502             copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06503 #endif
06504             saved_messages++;
06505             AST_LIST_REMOVE_CURRENT(list);
06506             free_user(vmtmp);
06507             if (res)
06508                break;
06509          }
06510          AST_LIST_TRAVERSE_SAFE_END;
06511          if (saved_messages > 0) {
06512             /* give confirmation that the message was saved */
06513             /* commented out since we can't forward batches yet
06514             if (saved_messages == 1)
06515                res = ast_play_and_wait(chan, "vm-message");
06516             else
06517                res = ast_play_and_wait(chan, "vm-messages");
06518             if (!res)
06519                res = ast_play_and_wait(chan, "vm-saved"); */
06520 #ifdef IMAP_STORAGE
06521             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06522             if (ast_strlen_zero(vmstmp.introfn))
06523 #endif
06524             res = ast_play_and_wait(chan, "vm-msgsaved");
06525          }  
06526       }
06527       DISPOSE(dir, curmsg);
06528    }
06529 
06530    /* If anything failed above, we still have this list to free */
06531    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06532       free_user(vmtmp);
06533    return res ? res : cmd;
06534 }
06535 
06536 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06537 {
06538    int res;
06539    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06540       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06541    return res;
06542 }
06543 
06544 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06545 {
06546    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);
06547 }
06548 
06549 static int play_message_category(struct ast_channel *chan, const char *category)
06550 {
06551    int res = 0;
06552 
06553    if (!ast_strlen_zero(category))
06554       res = ast_play_and_wait(chan, category);
06555 
06556    if (res) {
06557       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06558       res = 0;
06559    }
06560 
06561    return res;
06562 }
06563 
06564 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06565 {
06566    int res = 0;
06567    struct vm_zone *the_zone = NULL;
06568    time_t t;
06569 
06570    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06571       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06572       return 0;
06573    }
06574 
06575    /* Does this user have a timezone specified? */
06576    if (!ast_strlen_zero(vmu->zonetag)) {
06577       /* Find the zone in the list */
06578       struct vm_zone *z;
06579       AST_LIST_LOCK(&zones);
06580       AST_LIST_TRAVERSE(&zones, z, list) {
06581          if (!strcmp(z->name, vmu->zonetag)) {
06582             the_zone = z;
06583             break;
06584          }
06585       }
06586       AST_LIST_UNLOCK(&zones);
06587    }
06588 
06589 /* No internal variable parsing for now, so we'll comment it out for the time being */
06590 #if 0
06591    /* Set the DIFF_* variables */
06592    ast_localtime(&t, &time_now, NULL);
06593    tv_now = ast_tvnow();
06594    ast_localtime(&tv_now, &time_then, NULL);
06595 
06596    /* Day difference */
06597    if (time_now.tm_year == time_then.tm_year)
06598       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06599    else
06600       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06601    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06602 
06603    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06604 #endif
06605    if (the_zone) {
06606       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06607    }
06608    else if (!strcasecmp(chan->language,"pl"))       /* POLISH syntax */
06609       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06610    else if (!strcasecmp(chan->language,"se"))       /* SWEDISH syntax */
06611       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06612    else if (!strcasecmp(chan->language,"no"))       /* NORWEGIAN syntax */
06613       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06614    else if (!strcasecmp(chan->language,"de"))       /* GERMAN syntax */
06615       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06616    else if (!strcasecmp(chan->language,"nl"))      /* DUTCH syntax */
06617       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06618    else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
06619       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);
06620    else if (!strcasecmp(chan->language,"gr"))
06621       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06622    else if (!strcasecmp(chan->language,"pt_BR"))
06623       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);
06624    else if (!strncasecmp(chan->language, "zh", 2)) /* CHINESE (Taiwan) syntax */
06625       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);     
06626    else {
06627       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06628    }
06629 #if 0
06630    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06631 #endif
06632    return res;
06633 }
06634 
06635 
06636 
06637 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06638 {
06639    int res = 0;
06640    int i;
06641    char *callerid, *name;
06642    char prefile[PATH_MAX] = "";
06643    
06644 
06645    /* If voicemail cid is not enabled, or we didn't get cid or context from
06646     * the attribute file, leave now.
06647     *
06648     * TODO Still need to change this so that if this function is called by the
06649     * message envelope (and someone is explicitly requesting to hear the CID),
06650     * it does not check to see if CID is enabled in the config file.
06651     */
06652    if ((cid == NULL)||(context == NULL))
06653       return res;
06654 
06655    /* Strip off caller ID number from name */
06656    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
06657    ast_callerid_parse(cid, &name, &callerid);
06658    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
06659       /* Check for internal contexts and only */
06660       /* say extension when the call didn't come from an internal context in the list */
06661       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
06662          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
06663          if ((strcmp(cidinternalcontexts[i], context) == 0))
06664             break;
06665       }
06666       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
06667          if (!res) {
06668             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
06669             if (!ast_strlen_zero(prefile)) {
06670             /* See if we can find a recorded name for this person instead of their extension number */
06671                if (ast_fileexists(prefile, NULL, NULL) > 0) {
06672                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
06673                   if (!callback)
06674                      res = wait_file2(chan, vms, "vm-from");
06675                   res = ast_stream_and_wait(chan, prefile, "");
06676                } else {
06677                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
06678                   /* Say "from extension" as one saying to sound smoother */
06679                   if (!callback)
06680                      res = wait_file2(chan, vms, "vm-from-extension");
06681                   res = ast_say_digit_str(chan, callerid, "", chan->language);
06682                }
06683             }
06684          }
06685       } else if (!res) {
06686          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
06687          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
06688          if (!callback)
06689             res = wait_file2(chan, vms, "vm-from-phonenumber");
06690          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
06691       }
06692    } else {
06693       /* Number unknown */
06694       ast_debug(1, "VM-CID: From an unknown number\n");
06695       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
06696       res = wait_file2(chan, vms, "vm-unknown-caller");
06697    }
06698    return res;
06699 }
06700 
06701 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
06702 {
06703    int res = 0;
06704    int durationm;
06705    int durations;
06706    /* Verify that we have a duration for the message */
06707    if (duration == NULL)
06708       return res;
06709 
06710    /* Convert from seconds to minutes */
06711    durations=atoi(duration);
06712    durationm=(durations / 60);
06713 
06714    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
06715 
06716    if ((!res) && (durationm >= minduration)) {
06717       res = wait_file2(chan, vms, "vm-duration");
06718 
06719       /* POLISH syntax */
06720       if (!strcasecmp(chan->language, "pl")) {
06721          div_t num = div(durationm, 10);
06722 
06723          if (durationm == 1) {
06724             res = ast_play_and_wait(chan, "digits/1z");
06725             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
06726          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06727             if (num.rem == 2) {
06728                if (!num.quot) {
06729                   res = ast_play_and_wait(chan, "digits/2-ie");
06730                } else {
06731                   res = say_and_wait(chan, durationm - 2 , chan->language);
06732                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06733                }
06734             } else {
06735                res = say_and_wait(chan, durationm, chan->language);
06736             }
06737             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
06738          } else {
06739             res = say_and_wait(chan, durationm, chan->language);
06740             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
06741          }
06742       /* DEFAULT syntax */
06743       } else {
06744          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
06745          res = wait_file2(chan, vms, "vm-minutes");
06746       }
06747    }
06748    return res;
06749 }
06750 
06751 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
06752 {
06753    int res = 0;
06754    char filename[256], *cid;
06755    const char *origtime, *context, *category, *duration, *flag;
06756    struct ast_config *msg_cfg;
06757    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06758 
06759    vms->starting = 0; 
06760    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06761    adsi_message(chan, vms);
06762    if (!vms->curmsg)
06763       res = wait_file2(chan, vms, "vm-first");  /* "First" */
06764    else if (vms->curmsg == vms->lastmsg)
06765       res = wait_file2(chan, vms, "vm-last");      /* "last" */
06766 
06767    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
06768    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
06769    msg_cfg = ast_config_load(filename, config_flags);
06770    if (!msg_cfg) {
06771       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06772       return 0;
06773    }
06774    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
06775 
06776    /* Play the word urgent if we are listening to urgent messages */
06777    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
06778       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
06779    }
06780 
06781    if (!res) {
06782       /* POLISH syntax */
06783       if (!strcasecmp(chan->language, "pl")) { 
06784          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06785             int ten, one;
06786             char nextmsg[256];
06787             ten = (vms->curmsg + 1) / 10;
06788             one = (vms->curmsg + 1) % 10;
06789             
06790             if (vms->curmsg < 20) {
06791                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
06792                res = wait_file2(chan, vms, nextmsg);
06793             } else {
06794                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
06795                res = wait_file2(chan, vms, nextmsg);
06796                if (one > 0) {
06797                   if (!res) {
06798                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
06799                      res = wait_file2(chan, vms, nextmsg);
06800                   }
06801                }
06802             }
06803          }
06804          if (!res)
06805             res = wait_file2(chan, vms, "vm-message");
06806       /* HEBREW syntax */
06807       } else if (!strcasecmp(chan->language, "he")) {
06808          if (!vms->curmsg) {
06809             res = wait_file2(chan, vms, "vm-message");
06810             res = wait_file2(chan, vms, "vm-first");
06811          } else if (vms->curmsg == vms->lastmsg) {
06812             res = wait_file2(chan, vms, "vm-message");
06813             res = wait_file2(chan, vms, "vm-last");
06814          } else {
06815             res = wait_file2(chan, vms, "vm-message");
06816             res = wait_file2(chan, vms, "vm-number");
06817             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
06818          }
06819       } else {
06820          if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
06821             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
06822          else /* DEFAULT syntax */ {
06823             res = wait_file2(chan, vms, "vm-message");
06824          }
06825          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06826             if (!res) {
06827                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06828             }
06829          }
06830       }
06831    }
06832 
06833    if (!msg_cfg) {
06834       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06835       return 0;
06836    }
06837 
06838    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06839       ast_log(AST_LOG_WARNING, "No origtime?!\n");
06840       DISPOSE(vms->curdir, vms->curmsg);
06841       ast_config_destroy(msg_cfg);
06842       return 0;
06843    }
06844 
06845    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06846    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06847    category = ast_variable_retrieve(msg_cfg, "message", "category");
06848 
06849    context = ast_variable_retrieve(msg_cfg, "message", "context");
06850    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06851       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06852    if (!res) {
06853       res = play_message_category(chan, category);
06854    }
06855    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06856       res = play_message_datetime(chan, vmu, origtime, filename);
06857    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06858       res = play_message_callerid(chan, vms, cid, context, 0);
06859    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06860       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06861    /* Allow pressing '1' to skip envelope / callerid */
06862    if (res == '1')
06863       res = 0;
06864    ast_config_destroy(msg_cfg);
06865 
06866    if (!res) {
06867       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06868       vms->heard[vms->curmsg] = 1;
06869 #ifdef IMAP_STORAGE
06870       /*IMAP storage stores any prepended message from a forward
06871        * as a separate file from the rest of the message
06872        */
06873       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
06874          wait_file(chan, vms, vms->introfn);
06875       }
06876 #endif
06877       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06878          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06879          res = 0;
06880       }
06881    }
06882    DISPOSE(vms->curdir, vms->curmsg);
06883    return res;
06884 }
06885 
06886 #ifdef IMAP_STORAGE
06887 static int imap_remove_file(char *dir, int msgnum)
06888 {
06889    char fn[PATH_MAX];
06890    char full_fn[PATH_MAX];
06891    char intro[PATH_MAX] = {0,};
06892    
06893    if (msgnum > -1) {
06894       make_file(fn, sizeof(fn), dir, msgnum);
06895       snprintf(intro, sizeof(intro), "%sintro", fn);
06896    } else
06897       ast_copy_string(fn, dir, sizeof(fn));
06898    
06899    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
06900       ast_filedelete(fn, NULL);
06901       if (!ast_strlen_zero(intro)) {
06902          ast_filedelete(intro, NULL);
06903       }
06904       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
06905       unlink(full_fn);
06906    }
06907    return 0;
06908 }
06909 
06910 
06911 
06912 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
06913 {
06914    char *file, *filename;
06915    char *attachment;
06916    char arg[10];
06917    int i;
06918    BODY* body;
06919 
06920    
06921    file = strrchr(ast_strdupa(dir), '/');
06922    if (file)
06923       *file++ = '\0';
06924    else {
06925       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
06926       return -1;
06927    }
06928 
06929    ast_mutex_lock(&vms->lock);
06930    for (i = 0; i < vms->mailstream->nmsgs; i++) {
06931       mail_fetchstructure(vms->mailstream, i + 1, &body);
06932       /* We have the body, now we extract the file name of the first attachment. */
06933       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
06934          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
06935       } else {
06936          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
06937          ast_mutex_unlock(&vms->lock);
06938          return -1;
06939       }
06940       filename = strsep(&attachment, ".");
06941       if (!strcmp(filename, file)) {
06942          sprintf (arg,"%d", i+1);
06943          mail_setflag (vms->mailstream,arg,"\\DELETED");
06944       }
06945    }
06946    mail_expunge(vms->mailstream);
06947    ast_mutex_unlock(&vms->lock);
06948    return 0;
06949 }
06950 
06951 #else
06952 #ifndef IMAP_STORAGE
06953 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
06954 {
06955    int count_msg, last_msg;
06956 
06957    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
06958    
06959    /* Rename the member vmbox HERE so that we don't try to return before
06960     * we know what's going on.
06961     */
06962    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
06963    
06964    /* Faster to make the directory than to check if it exists. */
06965    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
06966 
06967    count_msg = count_messages(vmu, vms->curdir);
06968    if (count_msg < 0)
06969       return count_msg;
06970    else
06971       vms->lastmsg = count_msg - 1;
06972 
06973    /*
06974    The following test is needed in case sequencing gets messed up.
06975    There appears to be more than one way to mess up sequence, so
06976    we will not try to find all of the root causes--just fix it when
06977    detected.
06978    */
06979 
06980    if (vm_lock_path(vms->curdir)) {
06981       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
06982       return -1;
06983    }
06984 
06985    last_msg = last_message_index(vmu, vms->curdir);
06986    ast_unlock_path(vms->curdir);
06987 
06988    if (last_msg < 0) 
06989       return last_msg;
06990 
06991    return 0;
06992 }
06993 #endif
06994 #endif
06995 
06996 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
06997 {
06998    int x = 0;
06999 #ifndef IMAP_STORAGE
07000    int res = 0, nummsg;
07001    char fn2[PATH_MAX];
07002 #endif
07003 
07004    if (vms->lastmsg <= -1)
07005       goto done;
07006 
07007    vms->curmsg = -1; 
07008 #ifndef IMAP_STORAGE
07009    /* Get the deleted messages fixed */ 
07010    if (vm_lock_path(vms->curdir))
07011       return ERROR_LOCK_PATH;
07012 
07013    for (x = 0; x < vmu->maxmsg; x++) { 
07014       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
07015          /* Save this message.  It's not in INBOX or hasn't been heard */ 
07016          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
07017          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
07018             break;
07019          vms->curmsg++; 
07020          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg); 
07021          if (strcmp(vms->fn, fn2)) { 
07022             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07023          } 
07024       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
07025          /* Move to old folder before deleting */ 
07026          res = save_to_folder(vmu, vms, x, 1);
07027          if (res == ERROR_LOCK_PATH) {
07028             /* If save failed do not delete the message */
07029             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07030             vms->deleted[x] = 0;
07031             vms->heard[x] = 0;
07032             --x;
07033          }
07034       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07035          /* Move to deleted folder */ 
07036          res = save_to_folder(vmu, vms, x, 10);
07037          if (res == ERROR_LOCK_PATH) {
07038             /* If save failed do not delete the message */
07039             vms->deleted[x] = 0;
07040             vms->heard[x] = 0;
07041             --x;
07042          }
07043       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07044          /* If realtime storage enabled - we should explicitly delete this message,
07045          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07046          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07047          if (EXISTS(vms->curdir, x, vms->fn, NULL))
07048             DELETE(vms->curdir, x, vms->fn, vmu);
07049       }
07050    } 
07051 
07052    /* Delete ALL remaining messages */
07053    nummsg = x - 1;
07054    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07055       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07056       if (EXISTS(vms->curdir, x, vms->fn, NULL))
07057          DELETE(vms->curdir, x, vms->fn, vmu);
07058    }
07059    ast_unlock_path(vms->curdir);
07060 #else
07061    if (vms->deleted) {
07062       for (x=0;x < vmu->maxmsg;x++) { 
07063          if (vms->deleted[x]) { 
07064             ast_debug(3,"IMAP delete of %d\n",x);
07065             DELETE(vms->curdir, x, vms->fn, vmu);
07066          }
07067       }
07068    }
07069 #endif
07070 
07071 done:
07072    if (vms->deleted)
07073       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
07074    if (vms->heard)
07075       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
07076 
07077    return 0;
07078 }
07079 
07080 /* In Greek even though we CAN use a syntax like "friends messages"
07081  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07082  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
07083  * syntax for the above three categories which is more elegant. 
07084  */
07085 
07086 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07087 {
07088    int cmd;
07089    char *buf;
07090 
07091    buf = alloca(strlen(box)+2); 
07092    strcpy(buf, box);
07093    strcat(buf,"s");
07094 
07095    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07096       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07097       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07098    } else {
07099       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07100       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07101    }
07102 }
07103 
07104 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07105 {
07106    int cmd;
07107 
07108    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07109       if (!strcasecmp(box, "vm-INBOX"))
07110          cmd = ast_play_and_wait(chan, "vm-new-e");
07111       else
07112          cmd = ast_play_and_wait(chan, "vm-old-e");
07113       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07114    } else {
07115       cmd = ast_play_and_wait(chan, "vm-messages");
07116       return cmd ? cmd : ast_play_and_wait(chan, box);
07117    }
07118 }
07119 
07120 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07121 {
07122    int cmd;
07123 
07124    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07125       cmd = ast_play_and_wait(chan, "vm-messages");
07126       return cmd ? cmd : ast_play_and_wait(chan, box);
07127    } else {
07128       cmd = ast_play_and_wait(chan, box);
07129       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07130    }
07131 }
07132 
07133 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07134 {
07135    int cmd;
07136 
07137    if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
07138       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07139       return cmd ? cmd : ast_play_and_wait(chan, box);
07140    } else if (!strcasecmp(chan->language, "gr")){
07141       return vm_play_folder_name_gr(chan, box);
07142    } else if (!strcasecmp(chan->language, "pl")){
07143       return vm_play_folder_name_pl(chan, box);
07144    } else if (!strcasecmp(chan->language, "ua")){  /* Ukrainian syntax */
07145       return vm_play_folder_name_ua(chan, box);
07146    } else {  /* Default English */
07147       cmd = ast_play_and_wait(chan, box);
07148       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07149    }
07150 }
07151 
07152 /* GREEK SYNTAX 
07153    In greek the plural for old/new is
07154    different so we need the following files
07155    We also need vm-denExeteMynhmata because 
07156    this syntax is different.
07157    
07158    -> vm-Olds.wav : "Palia"
07159    -> vm-INBOXs.wav : "Nea"
07160    -> vm-denExeteMynhmata : "den exete mynhmata"
07161 */
07162                
07163    
07164 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07165 {
07166    int res = 0;
07167 
07168    if (vms->newmessages) {
07169       res = ast_play_and_wait(chan, "vm-youhave");
07170       if (!res) 
07171          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07172       if (!res) {
07173          if ((vms->newmessages == 1)) {
07174             res = ast_play_and_wait(chan, "vm-INBOX");
07175             if (!res)
07176                res = ast_play_and_wait(chan, "vm-message");
07177          } else {
07178             res = ast_play_and_wait(chan, "vm-INBOXs");
07179             if (!res)
07180                res = ast_play_and_wait(chan, "vm-messages");
07181          }
07182       }
07183    } else if (vms->oldmessages){
07184       res = ast_play_and_wait(chan, "vm-youhave");
07185       if (!res)
07186          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07187       if ((vms->oldmessages == 1)){
07188          res = ast_play_and_wait(chan, "vm-Old");
07189          if (!res)
07190             res = ast_play_and_wait(chan, "vm-message");
07191       } else {
07192          res = ast_play_and_wait(chan, "vm-Olds");
07193          if (!res)
07194             res = ast_play_and_wait(chan, "vm-messages");
07195       }
07196    } else if (!vms->oldmessages && !vms->newmessages) 
07197       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07198    return res;
07199 }
07200 
07201 /* Version of vm_intro() designed to work for many languages.
07202  *
07203  * It is hoped that this function can prevent the proliferation of 
07204  * language-specific vm_intro() functions and in time replace the language-
07205  * specific functions which already exist.  An examination of the language-
07206  * specific functions revealed that they all corrected the same deficiencies
07207  * in vm_intro_en() (which was the default function). Namely:
07208  *
07209  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07210  *     wording of the voicemail greeting hides this problem.  For example,
07211  *     vm-INBOX contains only the word "new".  This means that both of these
07212  *     sequences produce valid utterances:
07213  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07214  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07215  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07216  *     in many languages) the first utterance becomes "you have 1 the new message".
07217  *  2) The function contains hardcoded rules for pluralizing the word "message".
07218  *     These rules are correct for English, but not for many other languages.
07219  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07220  *     required in many languages.
07221  *  4) The gender of the word for "message" is not specified. This is a problem
07222  *     because in many languages the gender of the number in phrases such
07223  *     as "you have one new message" must match the gender of the word
07224  *     meaning "message".
07225  *
07226  * Fixing these problems for each new language has meant duplication of effort.
07227  * This new function solves the problems in the following general ways:
07228  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07229  *     and vm-Old respectively for those languages where it makes sense.
07230  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07231  *     on vm-message.
07232  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07233  *     prefix on vm-new and vm-old (none for English).
07234  *  4) Pass the gender of the language's word for "message" as an agument to
07235  *     this function which is can in turn pass on to the functions which 
07236  *     say numbers and put endings on nounds and adjectives.
07237  *
07238  * All languages require these messages:
07239  *  vm-youhave    "You have..."
07240  *  vm-and     "and"
07241  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07242  *
07243  * To use it for English, you will need these additional sound files:
07244  *  vm-new     "new"
07245  *  vm-message    "message", singular
07246  *  vm-messages      "messages", plural
07247  *
07248  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07249  *
07250  *  vm-newn    "novoye" (singular, neuter)
07251  *  vm-newx    "novikh" (counting plural form, genative plural)
07252  *  vm-message    "sobsheniye" (singular form)
07253  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07254  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07255  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07256  *  digits/2n     "dva" (neuter singular)
07257  */
07258 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07259 {
07260    int res;
07261    int lastnum = 0;
07262 
07263    res = ast_play_and_wait(chan, "vm-youhave");
07264 
07265    if (!res && vms->newmessages) {
07266       lastnum = vms->newmessages;
07267 
07268       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07269          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07270       }
07271 
07272       if (!res && vms->oldmessages) {
07273          res = ast_play_and_wait(chan, "vm-and");
07274       }
07275    }
07276 
07277    if (!res && vms->oldmessages) {
07278       lastnum = vms->oldmessages;
07279 
07280       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07281          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07282       }
07283    }
07284 
07285    if (!res) {
07286       if (lastnum == 0) {
07287          res = ast_play_and_wait(chan, "vm-no");
07288       }
07289       if (!res) {
07290          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07291       }
07292    }
07293 
07294    return res;
07295 }
07296 
07297 /* Default Hebrew syntax */
07298 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07299 {
07300    int res = 0;
07301 
07302    /* Introduce messages they have */
07303    if (!res) {
07304       if ((vms->newmessages) || (vms->oldmessages)) {
07305          res = ast_play_and_wait(chan, "vm-youhave");
07306       }
07307       /*
07308        * The word "shtei" refers to the number 2 in hebrew when performing a count
07309        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07310        * an element, this is one of them.
07311        */
07312       if (vms->newmessages) {
07313          if (!res) {
07314             if (vms->newmessages == 1) {
07315                res = ast_play_and_wait(chan, "vm-INBOX1");
07316             } else {
07317                if (vms->newmessages == 2) {
07318                   res = ast_play_and_wait(chan, "vm-shtei");
07319                } else {
07320                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07321                }
07322                res = ast_play_and_wait(chan, "vm-INBOX");
07323             }
07324          }
07325          if (vms->oldmessages && !res) {
07326             res = ast_play_and_wait(chan, "vm-and");
07327             if (vms->oldmessages == 1) {
07328                res = ast_play_and_wait(chan, "vm-Old1");
07329             } else {
07330                if (vms->oldmessages == 2) {
07331                   res = ast_play_and_wait(chan, "vm-shtei");
07332                } else {
07333                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07334                }
07335                res = ast_play_and_wait(chan, "vm-Old");
07336             }
07337          }
07338       }
07339       if (!res && vms->oldmessages && !vms->newmessages) {
07340          if (!res) {
07341             if (vms->oldmessages == 1) {
07342                res = ast_play_and_wait(chan, "vm-Old1");
07343             } else {
07344                if (vms->oldmessages == 2) {
07345                   res = ast_play_and_wait(chan, "vm-shtei");
07346                } else {
07347                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07348                }
07349                res = ast_play_and_wait(chan, "vm-Old");
07350             }
07351          }
07352       }
07353       if (!res) {
07354          if (!vms->oldmessages && !vms->newmessages) {
07355             if (!res) {
07356                res = ast_play_and_wait(chan, "vm-nomessages");
07357             }
07358          }
07359       }
07360    }
07361    return res;
07362 }
07363    
07364 /* Default English syntax */
07365 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07366 {
07367    int res;
07368 
07369    /* Introduce messages they have */
07370    res = ast_play_and_wait(chan, "vm-youhave");
07371    if (!res) {
07372       if (vms->urgentmessages) {
07373          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07374          if (!res)
07375             res = ast_play_and_wait(chan, "vm-Urgent");
07376          if ((vms->oldmessages || vms->newmessages) && !res) {
07377             res = ast_play_and_wait(chan, "vm-and");
07378          } else if (!res) {
07379             if ((vms->urgentmessages == 1))
07380                res = ast_play_and_wait(chan, "vm-message");
07381             else
07382                res = ast_play_and_wait(chan, "vm-messages");
07383          }
07384       }
07385       if (vms->newmessages) {
07386          res = say_and_wait(chan, vms->newmessages, chan->language);
07387          if (!res)
07388             res = ast_play_and_wait(chan, "vm-INBOX");
07389          if (vms->oldmessages && !res)
07390             res = ast_play_and_wait(chan, "vm-and");
07391          else if (!res) {
07392             if ((vms->newmessages == 1))
07393                res = ast_play_and_wait(chan, "vm-message");
07394             else
07395                res = ast_play_and_wait(chan, "vm-messages");
07396          }
07397             
07398       }
07399       if (!res && vms->oldmessages) {
07400          res = say_and_wait(chan, vms->oldmessages, chan->language);
07401          if (!res)
07402             res = ast_play_and_wait(chan, "vm-Old");
07403          if (!res) {
07404             if (vms->oldmessages == 1)
07405                res = ast_play_and_wait(chan, "vm-message");
07406             else
07407                res = ast_play_and_wait(chan, "vm-messages");
07408          }
07409       }
07410       if (!res) {
07411          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07412             res = ast_play_and_wait(chan, "vm-no");
07413             if (!res)
07414                res = ast_play_and_wait(chan, "vm-messages");
07415          }
07416       }
07417    }
07418    return res;
07419 }
07420 
07421 /* ITALIAN syntax */
07422 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07423 {
07424    /* Introduce messages they have */
07425    int res;
07426    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07427       res = ast_play_and_wait(chan, "vm-no") ||
07428          ast_play_and_wait(chan, "vm-message");
07429    else
07430       res = ast_play_and_wait(chan, "vm-youhave");
07431    if (!res && vms->newmessages) {
07432       res = (vms->newmessages == 1) ?
07433          ast_play_and_wait(chan, "digits/un") ||
07434          ast_play_and_wait(chan, "vm-nuovo") ||
07435          ast_play_and_wait(chan, "vm-message") :
07436          /* 2 or more new messages */
07437          say_and_wait(chan, vms->newmessages, chan->language) ||
07438          ast_play_and_wait(chan, "vm-nuovi") ||
07439          ast_play_and_wait(chan, "vm-messages");
07440       if (!res && vms->oldmessages)
07441          res = ast_play_and_wait(chan, "vm-and");
07442    }
07443    if (!res && vms->oldmessages) {
07444       res = (vms->oldmessages == 1) ?
07445          ast_play_and_wait(chan, "digits/un") ||
07446          ast_play_and_wait(chan, "vm-vecchio") ||
07447          ast_play_and_wait(chan, "vm-message") :
07448          /* 2 or more old messages */
07449          say_and_wait(chan, vms->oldmessages, chan->language) ||
07450          ast_play_and_wait(chan, "vm-vecchi") ||
07451          ast_play_and_wait(chan, "vm-messages");
07452    }
07453    return res;
07454 }
07455 
07456 /* POLISH syntax */
07457 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07458 {
07459    /* Introduce messages they have */
07460    int res;
07461    div_t num;
07462 
07463    if (!vms->oldmessages && !vms->newmessages) {
07464       res = ast_play_and_wait(chan, "vm-no");
07465       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07466       return res;
07467    } else {
07468       res = ast_play_and_wait(chan, "vm-youhave");
07469    }
07470 
07471    if (vms->newmessages) {
07472       num = div(vms->newmessages, 10);
07473       if (vms->newmessages == 1) {
07474          res = ast_play_and_wait(chan, "digits/1-a");
07475          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07476          res = res ? res : ast_play_and_wait(chan, "vm-message");
07477       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07478          if (num.rem == 2) {
07479             if (!num.quot) {
07480                res = ast_play_and_wait(chan, "digits/2-ie");
07481             } else {
07482                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07483                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07484             }
07485          } else {
07486             res = say_and_wait(chan, vms->newmessages, chan->language);
07487          }
07488          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07489          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07490       } else {
07491          res = say_and_wait(chan, vms->newmessages, chan->language);
07492          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07493          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07494       }
07495       if (!res && vms->oldmessages)
07496          res = ast_play_and_wait(chan, "vm-and");
07497    }
07498    if (!res && vms->oldmessages) {
07499       num = div(vms->oldmessages, 10);
07500       if (vms->oldmessages == 1) {
07501          res = ast_play_and_wait(chan, "digits/1-a");
07502          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07503          res = res ? res : ast_play_and_wait(chan, "vm-message");
07504       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07505          if (num.rem == 2) {
07506             if (!num.quot) {
07507                res = ast_play_and_wait(chan, "digits/2-ie");
07508             } else {
07509                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07510                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07511             }
07512          } else {
07513             res = say_and_wait(chan, vms->oldmessages, chan->language);
07514          }
07515          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07516          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07517       } else {
07518          res = say_and_wait(chan, vms->oldmessages, chan->language);
07519          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07520          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07521       }
07522    }
07523 
07524    return res;
07525 }
07526 
07527 /* SWEDISH syntax */
07528 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07529 {
07530    /* Introduce messages they have */
07531    int res;
07532 
07533    res = ast_play_and_wait(chan, "vm-youhave");
07534    if (res)
07535       return res;
07536 
07537    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07538       res = ast_play_and_wait(chan, "vm-no");
07539       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07540       return res;
07541    }
07542 
07543    if (vms->newmessages) {
07544       if ((vms->newmessages == 1)) {
07545          res = ast_play_and_wait(chan, "digits/ett");
07546          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07547          res = res ? res : ast_play_and_wait(chan, "vm-message");
07548       } else {
07549          res = say_and_wait(chan, vms->newmessages, chan->language);
07550          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07551          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07552       }
07553       if (!res && vms->oldmessages)
07554          res = ast_play_and_wait(chan, "vm-and");
07555    }
07556    if (!res && vms->oldmessages) {
07557       if (vms->oldmessages == 1) {
07558          res = ast_play_and_wait(chan, "digits/ett");
07559          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07560          res = res ? res : ast_play_and_wait(chan, "vm-message");
07561       } else {
07562          res = say_and_wait(chan, vms->oldmessages, chan->language);
07563          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07564          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07565       }
07566    }
07567 
07568    return res;
07569 }
07570 
07571 /* NORWEGIAN syntax */
07572 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07573 {
07574    /* Introduce messages they have */
07575    int res;
07576 
07577    res = ast_play_and_wait(chan, "vm-youhave");
07578    if (res)
07579       return res;
07580 
07581    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07582       res = ast_play_and_wait(chan, "vm-no");
07583       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07584       return res;
07585    }
07586 
07587    if (vms->newmessages) {
07588       if ((vms->newmessages == 1)) {
07589          res = ast_play_and_wait(chan, "digits/1");
07590          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07591          res = res ? res : ast_play_and_wait(chan, "vm-message");
07592       } else {
07593          res = say_and_wait(chan, vms->newmessages, chan->language);
07594          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07595          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07596       }
07597       if (!res && vms->oldmessages)
07598          res = ast_play_and_wait(chan, "vm-and");
07599    }
07600    if (!res && vms->oldmessages) {
07601       if (vms->oldmessages == 1) {
07602          res = ast_play_and_wait(chan, "digits/1");
07603          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07604          res = res ? res : ast_play_and_wait(chan, "vm-message");
07605       } else {
07606          res = say_and_wait(chan, vms->oldmessages, chan->language);
07607          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07608          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07609       }
07610    }
07611 
07612    return res;
07613 }
07614 
07615 /* GERMAN syntax */
07616 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07617 {
07618    /* Introduce messages they have */
07619    int res;
07620    res = ast_play_and_wait(chan, "vm-youhave");
07621    if (!res) {
07622       if (vms->newmessages) {
07623          if ((vms->newmessages == 1))
07624             res = ast_play_and_wait(chan, "digits/1F");
07625          else
07626             res = say_and_wait(chan, vms->newmessages, chan->language);
07627          if (!res)
07628             res = ast_play_and_wait(chan, "vm-INBOX");
07629          if (vms->oldmessages && !res)
07630             res = ast_play_and_wait(chan, "vm-and");
07631          else if (!res) {
07632             if ((vms->newmessages == 1))
07633                res = ast_play_and_wait(chan, "vm-message");
07634             else
07635                res = ast_play_and_wait(chan, "vm-messages");
07636          }
07637             
07638       }
07639       if (!res && vms->oldmessages) {
07640          if (vms->oldmessages == 1)
07641             res = ast_play_and_wait(chan, "digits/1F");
07642          else
07643             res = say_and_wait(chan, vms->oldmessages, chan->language);
07644          if (!res)
07645             res = ast_play_and_wait(chan, "vm-Old");
07646          if (!res) {
07647             if (vms->oldmessages == 1)
07648                res = ast_play_and_wait(chan, "vm-message");
07649             else
07650                res = ast_play_and_wait(chan, "vm-messages");
07651          }
07652       }
07653       if (!res) {
07654          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07655             res = ast_play_and_wait(chan, "vm-no");
07656             if (!res)
07657                res = ast_play_and_wait(chan, "vm-messages");
07658          }
07659       }
07660    }
07661    return res;
07662 }
07663 
07664 /* SPANISH syntax */
07665 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
07666 {
07667    /* Introduce messages they have */
07668    int res;
07669    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07670       res = ast_play_and_wait(chan, "vm-youhaveno");
07671       if (!res)
07672          res = ast_play_and_wait(chan, "vm-messages");
07673    } else {
07674       res = ast_play_and_wait(chan, "vm-youhave");
07675    }
07676    if (!res) {
07677       if (vms->newmessages) {
07678          if (!res) {
07679             if ((vms->newmessages == 1)) {
07680                res = ast_play_and_wait(chan, "digits/1M");
07681                if (!res)
07682                   res = ast_play_and_wait(chan, "vm-message");
07683                if (!res)
07684                   res = ast_play_and_wait(chan, "vm-INBOXs");
07685             } else {
07686                res = say_and_wait(chan, vms->newmessages, chan->language);
07687                if (!res)
07688                   res = ast_play_and_wait(chan, "vm-messages");
07689                if (!res)
07690                   res = ast_play_and_wait(chan, "vm-INBOX");
07691             }
07692          }
07693          if (vms->oldmessages && !res)
07694             res = ast_play_and_wait(chan, "vm-and");
07695       }
07696       if (vms->oldmessages) {
07697          if (!res) {
07698             if (vms->oldmessages == 1) {
07699                res = ast_play_and_wait(chan, "digits/1M");
07700                if (!res)
07701                   res = ast_play_and_wait(chan, "vm-message");
07702                if (!res)
07703                   res = ast_play_and_wait(chan, "vm-Olds");
07704             } else {
07705                res = say_and_wait(chan, vms->oldmessages, chan->language);
07706                if (!res)
07707                   res = ast_play_and_wait(chan, "vm-messages");
07708                if (!res)
07709                   res = ast_play_and_wait(chan, "vm-Old");
07710             }
07711          }
07712       }
07713    }
07714 return res;
07715 }
07716 
07717 /* BRAZILIAN PORTUGUESE syntax */
07718 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
07719    /* Introduce messages they have */
07720    int res;
07721    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07722       res = ast_play_and_wait(chan, "vm-nomessages");
07723       return res;
07724    } else {
07725       res = ast_play_and_wait(chan, "vm-youhave");
07726    }
07727    if (vms->newmessages) {
07728       if (!res)
07729          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07730       if ((vms->newmessages == 1)) {
07731          if (!res)
07732             res = ast_play_and_wait(chan, "vm-message");
07733          if (!res)
07734             res = ast_play_and_wait(chan, "vm-INBOXs");
07735       } else {
07736          if (!res)
07737             res = ast_play_and_wait(chan, "vm-messages");
07738          if (!res)
07739             res = ast_play_and_wait(chan, "vm-INBOX");
07740       }
07741       if (vms->oldmessages && !res)
07742          res = ast_play_and_wait(chan, "vm-and");
07743    }
07744    if (vms->oldmessages) {
07745       if (!res)
07746          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07747       if (vms->oldmessages == 1) {
07748          if (!res)
07749             res = ast_play_and_wait(chan, "vm-message");
07750          if (!res)
07751             res = ast_play_and_wait(chan, "vm-Olds");
07752       } else {
07753          if (!res)
07754             res = ast_play_and_wait(chan, "vm-messages");
07755          if (!res)
07756             res = ast_play_and_wait(chan, "vm-Old");
07757       }
07758    }
07759    return res;
07760 }
07761 
07762 /* FRENCH syntax */
07763 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
07764 {
07765    /* Introduce messages they have */
07766    int res;
07767    res = ast_play_and_wait(chan, "vm-youhave");
07768    if (!res) {
07769       if (vms->newmessages) {
07770          res = say_and_wait(chan, vms->newmessages, chan->language);
07771          if (!res)
07772             res = ast_play_and_wait(chan, "vm-INBOX");
07773          if (vms->oldmessages && !res)
07774             res = ast_play_and_wait(chan, "vm-and");
07775          else if (!res) {
07776             if ((vms->newmessages == 1))
07777                res = ast_play_and_wait(chan, "vm-message");
07778             else
07779                res = ast_play_and_wait(chan, "vm-messages");
07780          }
07781             
07782       }
07783       if (!res && vms->oldmessages) {
07784          res = say_and_wait(chan, vms->oldmessages, chan->language);
07785          if (!res)
07786             res = ast_play_and_wait(chan, "vm-Old");
07787          if (!res) {
07788             if (vms->oldmessages == 1)
07789                res = ast_play_and_wait(chan, "vm-message");
07790             else
07791                res = ast_play_and_wait(chan, "vm-messages");
07792          }
07793       }
07794       if (!res) {
07795          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07796             res = ast_play_and_wait(chan, "vm-no");
07797             if (!res)
07798                res = ast_play_and_wait(chan, "vm-messages");
07799          }
07800       }
07801    }
07802    return res;
07803 }
07804 
07805 /* DUTCH syntax */
07806 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
07807 {
07808    /* Introduce messages they have */
07809    int res;
07810    res = ast_play_and_wait(chan, "vm-youhave");
07811    if (!res) {
07812       if (vms->newmessages) {
07813          res = say_and_wait(chan, vms->newmessages, chan->language);
07814          if (!res) {
07815             if (vms->newmessages == 1)
07816                res = ast_play_and_wait(chan, "vm-INBOXs");
07817             else
07818                res = ast_play_and_wait(chan, "vm-INBOX");
07819          }
07820          if (vms->oldmessages && !res)
07821             res = ast_play_and_wait(chan, "vm-and");
07822          else if (!res) {
07823             if ((vms->newmessages == 1))
07824                res = ast_play_and_wait(chan, "vm-message");
07825             else
07826                res = ast_play_and_wait(chan, "vm-messages");
07827          }
07828             
07829       }
07830       if (!res && vms->oldmessages) {
07831          res = say_and_wait(chan, vms->oldmessages, chan->language);
07832          if (!res) {
07833             if (vms->oldmessages == 1)
07834                res = ast_play_and_wait(chan, "vm-Olds");
07835             else
07836                res = ast_play_and_wait(chan, "vm-Old");
07837          }
07838          if (!res) {
07839             if (vms->oldmessages == 1)
07840                res = ast_play_and_wait(chan, "vm-message");
07841             else
07842                res = ast_play_and_wait(chan, "vm-messages");
07843          }
07844       }
07845       if (!res) {
07846          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07847             res = ast_play_and_wait(chan, "vm-no");
07848             if (!res)
07849                res = ast_play_and_wait(chan, "vm-messages");
07850          }
07851       }
07852    }
07853    return res;
07854 }
07855 
07856 /* PORTUGUESE syntax */
07857 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
07858 {
07859    /* Introduce messages they have */
07860    int res;
07861    res = ast_play_and_wait(chan, "vm-youhave");
07862    if (!res) {
07863       if (vms->newmessages) {
07864          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07865          if (!res) {
07866             if ((vms->newmessages == 1)) {
07867                res = ast_play_and_wait(chan, "vm-message");
07868                if (!res)
07869                   res = ast_play_and_wait(chan, "vm-INBOXs");
07870             } else {
07871                res = ast_play_and_wait(chan, "vm-messages");
07872                if (!res)
07873                   res = ast_play_and_wait(chan, "vm-INBOX");
07874             }
07875          }
07876          if (vms->oldmessages && !res)
07877             res = ast_play_and_wait(chan, "vm-and");
07878       }
07879       if (!res && vms->oldmessages) {
07880          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07881          if (!res) {
07882             if (vms->oldmessages == 1) {
07883                res = ast_play_and_wait(chan, "vm-message");
07884                if (!res)
07885                   res = ast_play_and_wait(chan, "vm-Olds");
07886             } else {
07887                res = ast_play_and_wait(chan, "vm-messages");
07888                if (!res)
07889                   res = ast_play_and_wait(chan, "vm-Old");
07890             }
07891          }
07892       }
07893       if (!res) {
07894          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07895             res = ast_play_and_wait(chan, "vm-no");
07896             if (!res)
07897                res = ast_play_and_wait(chan, "vm-messages");
07898          }
07899       }
07900    }
07901    return res;
07902 }
07903 
07904 
07905 /* CZECH syntax */
07906 /* in czech there must be declension of word new and message
07907  * czech        : english        : czech      : english
07908  * --------------------------------------------------------
07909  * vm-youhave   : you have 
07910  * vm-novou     : one new        : vm-zpravu  : message
07911  * vm-nove      : 2-4 new        : vm-zpravy  : messages
07912  * vm-novych    : 5-infinite new : vm-zprav   : messages
07913  * vm-starou   : one old
07914  * vm-stare     : 2-4 old 
07915  * vm-starych   : 5-infinite old
07916  * jednu        : one   - falling 4. 
07917  * vm-no        : no  ( no messages )
07918  */
07919 
07920 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
07921 {
07922    int res;
07923    res = ast_play_and_wait(chan, "vm-youhave");
07924    if (!res) {
07925       if (vms->newmessages) {
07926          if (vms->newmessages == 1) {
07927             res = ast_play_and_wait(chan, "digits/jednu");
07928          } else {
07929             res = say_and_wait(chan, vms->newmessages, chan->language);
07930          }
07931          if (!res) {
07932             if ((vms->newmessages == 1))
07933                res = ast_play_and_wait(chan, "vm-novou");
07934             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07935                res = ast_play_and_wait(chan, "vm-nove");
07936             if (vms->newmessages > 4)
07937                res = ast_play_and_wait(chan, "vm-novych");
07938          }
07939          if (vms->oldmessages && !res)
07940             res = ast_play_and_wait(chan, "vm-and");
07941          else if (!res) {
07942             if ((vms->newmessages == 1))
07943                res = ast_play_and_wait(chan, "vm-zpravu");
07944             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07945                res = ast_play_and_wait(chan, "vm-zpravy");
07946             if (vms->newmessages > 4)
07947                res = ast_play_and_wait(chan, "vm-zprav");
07948          }
07949       }
07950       if (!res && vms->oldmessages) {
07951          res = say_and_wait(chan, vms->oldmessages, chan->language);
07952          if (!res) {
07953             if ((vms->oldmessages == 1))
07954                res = ast_play_and_wait(chan, "vm-starou");
07955             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07956                res = ast_play_and_wait(chan, "vm-stare");
07957             if (vms->oldmessages > 4)
07958                res = ast_play_and_wait(chan, "vm-starych");
07959          }
07960          if (!res) {
07961             if ((vms->oldmessages == 1))
07962                res = ast_play_and_wait(chan, "vm-zpravu");
07963             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07964                res = ast_play_and_wait(chan, "vm-zpravy");
07965             if (vms->oldmessages > 4)
07966                res = ast_play_and_wait(chan, "vm-zprav");
07967          }
07968       }
07969       if (!res) {
07970          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07971             res = ast_play_and_wait(chan, "vm-no");
07972             if (!res)
07973                res = ast_play_and_wait(chan, "vm-zpravy");
07974          }
07975       }
07976    }
07977    return res;
07978 }
07979 
07980 /* CHINESE (Taiwan) syntax */
07981 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
07982 {
07983    int res;
07984    /* Introduce messages they have */
07985    res = ast_play_and_wait(chan, "vm-you");
07986 
07987    if (!res && vms->newmessages) {
07988       res = ast_play_and_wait(chan, "vm-have");
07989       if (!res)
07990          res = say_and_wait(chan, vms->newmessages, chan->language);
07991       if (!res)
07992          res = ast_play_and_wait(chan, "vm-tong");
07993       if (!res)
07994          res = ast_play_and_wait(chan, "vm-INBOX");
07995       if (vms->oldmessages && !res)
07996          res = ast_play_and_wait(chan, "vm-and");
07997       else if (!res) 
07998          res = ast_play_and_wait(chan, "vm-messages");
07999    }
08000    if (!res && vms->oldmessages) {
08001       res = ast_play_and_wait(chan, "vm-have");
08002       if (!res)
08003          res = say_and_wait(chan, vms->oldmessages, chan->language);
08004       if (!res)
08005          res = ast_play_and_wait(chan, "vm-tong");
08006       if (!res)
08007          res = ast_play_and_wait(chan, "vm-Old");
08008       if (!res)
08009          res = ast_play_and_wait(chan, "vm-messages");
08010    }
08011    if (!res && !vms->oldmessages && !vms->newmessages) {
08012       res = ast_play_and_wait(chan, "vm-haveno");
08013       if (!res)
08014          res = ast_play_and_wait(chan, "vm-messages");
08015    }
08016    return res;
08017 }
08018 
08019 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
08020 {
08021    char prefile[256];
08022    
08023    /* Notify the user that the temp greeting is set and give them the option to remove it */
08024    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08025    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08026       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08027       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08028          ast_play_and_wait(chan, "vm-tempgreetactive");
08029       }
08030       DISPOSE(prefile, -1);
08031    }
08032 
08033    /* Play voicemail intro - syntax is different for different languages */
08034    if (!strcasecmp(chan->language, "de")) {  /* GERMAN syntax */
08035       return vm_intro_de(chan, vms);
08036    } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
08037       return vm_intro_es(chan, vms);
08038    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
08039       return vm_intro_it(chan, vms);
08040    } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
08041       return vm_intro_fr(chan, vms);
08042    } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
08043       return vm_intro_nl(chan, vms);
08044    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
08045       return vm_intro_pt(chan, vms);
08046    } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
08047       return vm_intro_pt_BR(chan, vms);
08048    } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
08049       return vm_intro_cz(chan, vms);
08050    } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
08051       return vm_intro_gr(chan, vms);
08052    } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
08053       return vm_intro_pl(chan, vms);
08054    } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
08055       return vm_intro_se(chan, vms);
08056    } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
08057       return vm_intro_no(chan, vms);
08058    } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
08059       return vm_intro_multilang(chan, vms, "n");
08060    } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08061       return vm_intro_zh(chan, vms);
08062    } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
08063       return vm_intro_multilang(chan, vms, "n");
08064    } else if (!strcasecmp(chan->language, "he")) { /* HEBREW syntax */
08065        return vm_intro_he(chan, vms);
08066    } else {             /* Default to ENGLISH */
08067       return vm_intro_en(chan, vms);
08068    }
08069 }
08070 
08071 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08072 {
08073    int res = 0;
08074    /* Play instructions and wait for new command */
08075    while (!res) {
08076       if (vms->starting) {
08077          if (vms->lastmsg > -1) {
08078             if (skipadvanced)
08079                res = ast_play_and_wait(chan, "vm-onefor-full");
08080             else
08081                res = ast_play_and_wait(chan, "vm-onefor");
08082             if (!res)
08083                res = vm_play_folder_name(chan, vms->vmbox);
08084          }
08085          if (!res) {
08086             if (skipadvanced)
08087                res = ast_play_and_wait(chan, "vm-opts-full");
08088             else
08089                res = ast_play_and_wait(chan, "vm-opts");
08090          }
08091       } else {
08092          /* Added for additional help */
08093          if (skipadvanced) {
08094             res = ast_play_and_wait(chan, "vm-onefor-full");
08095             if (!res)
08096                res = vm_play_folder_name(chan, vms->vmbox);
08097             res = ast_play_and_wait(chan, "vm-opts-full");
08098          }
08099          /* Logic:
08100           * If the current message is not the first OR
08101           * if we're listening to the first new message and there are
08102           * also urgent messages, then prompt for navigation to the
08103           * previous message
08104           */
08105          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08106             res = ast_play_and_wait(chan, "vm-prev");
08107          }
08108          if (!res && !skipadvanced)
08109             res = ast_play_and_wait(chan, "vm-advopts");
08110          if (!res)
08111             res = ast_play_and_wait(chan, "vm-repeat");
08112          /* Logic:
08113           * If we're not listening to the last message OR
08114           * we're listening to the last urgent message and there are
08115           * also new non-urgent messages, then prompt for navigation
08116           * to the next message
08117           */
08118          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08119             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08120             res = ast_play_and_wait(chan, "vm-next");
08121          }
08122          if (!res) {
08123             if (!vms->deleted[vms->curmsg])
08124                res = ast_play_and_wait(chan, "vm-delete");
08125             else
08126                res = ast_play_and_wait(chan, "vm-undelete");
08127             if (!res)
08128                res = ast_play_and_wait(chan, "vm-toforward");
08129             if (!res)
08130                res = ast_play_and_wait(chan, "vm-savemessage");
08131          }
08132       }
08133       if (!res) {
08134          res = ast_play_and_wait(chan, "vm-helpexit");
08135       }
08136       if (!res)
08137          res = ast_waitfordigit(chan, 6000);
08138       if (!res) {
08139          vms->repeats++;
08140          if (vms->repeats > 2) {
08141             res = 't';
08142          }
08143       }
08144    }
08145    return res;
08146 }
08147 
08148 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08149 {
08150    int res = 0;
08151    /* Play instructions and wait for new command */
08152    while (!res) {
08153       if (vms->lastmsg > -1) {
08154          res = ast_play_and_wait(chan, "vm-listen");
08155          if (!res)
08156             res = vm_play_folder_name(chan, vms->vmbox);
08157          if (!res)
08158             res = ast_play_and_wait(chan, "press");
08159          if (!res)
08160             res = ast_play_and_wait(chan, "digits/1");
08161       }
08162       if (!res)
08163          res = ast_play_and_wait(chan, "vm-opts");
08164       if (!res) {
08165          vms->starting = 0;
08166          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08167       }
08168    }
08169    return res;
08170 }
08171 
08172 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08173 {
08174    if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
08175       return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
08176    } else {             /* Default to ENGLISH */
08177       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08178    }
08179 }
08180 
08181 
08182 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08183 {
08184    int cmd = 0;
08185    int duration = 0;
08186    int tries = 0;
08187    char newpassword[80] = "";
08188    char newpassword2[80] = "";
08189    char prefile[PATH_MAX] = "";
08190    unsigned char buf[256];
08191    int bytes=0;
08192 
08193    if (ast_adsi_available(chan)) {
08194       bytes += adsi_logo(buf + bytes);
08195       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08196       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08197       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08198       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08199       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08200    }
08201 
08202    /* First, have the user change their password 
08203       so they won't get here again */
08204    for (;;) {
08205       newpassword[1] = '\0';
08206       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08207       if (cmd == '#')
08208          newpassword[0] = '\0';
08209       if (cmd < 0 || cmd == 't' || cmd == '#')
08210          return cmd;
08211       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08212       if (cmd < 0 || cmd == 't' || cmd == '#')
08213          return cmd;
08214       cmd = check_password(vmu, newpassword); /* perform password validation */
08215       if (cmd != 0) {
08216          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08217          cmd = ast_play_and_wait(chan, vm_invalid_password);
08218       } else {
08219          newpassword2[1] = '\0';
08220          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08221          if (cmd == '#')
08222             newpassword2[0] = '\0';
08223          if (cmd < 0 || cmd == 't' || cmd == '#')
08224             return cmd;
08225          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08226          if (cmd < 0 || cmd == 't' || cmd == '#')
08227             return cmd;
08228          if (!strcmp(newpassword, newpassword2))
08229             break;
08230          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08231          cmd = ast_play_and_wait(chan, vm_mismatch);
08232       }
08233       if (++tries == 3)
08234          return -1;
08235    }
08236    if (pwdchange & PWDCHANGE_INTERNAL)
08237       vm_change_password(vmu, newpassword);
08238    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08239       vm_change_password_shell(vmu, newpassword);
08240 
08241    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08242    cmd = ast_play_and_wait(chan, vm_passchanged);
08243 
08244    /* If forcename is set, have the user record their name */  
08245    if (ast_test_flag(vmu, VM_FORCENAME)) {
08246       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08247       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08248          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08249          if (cmd < 0 || cmd == 't' || cmd == '#')
08250             return cmd;
08251       }
08252    }
08253 
08254    /* If forcegreetings is set, have the user record their greetings */
08255    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08256       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08257       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08258          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08259          if (cmd < 0 || cmd == 't' || cmd == '#')
08260             return cmd;
08261       }
08262 
08263       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08264       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08265          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08266          if (cmd < 0 || cmd == 't' || cmd == '#')
08267             return cmd;
08268       }
08269    }
08270 
08271    return cmd;
08272 }
08273 
08274 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08275 {
08276    int cmd = 0;
08277    int retries = 0;
08278    int duration = 0;
08279    char newpassword[80] = "";
08280    char newpassword2[80] = "";
08281    char prefile[PATH_MAX] = "";
08282    unsigned char buf[256];
08283    int bytes=0;
08284 
08285    if (ast_adsi_available(chan)) {
08286       bytes += adsi_logo(buf + bytes);
08287       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08288       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08289       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08290       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08291       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08292    }
08293    while ((cmd >= 0) && (cmd != 't')) {
08294       if (cmd)
08295          retries = 0;
08296       switch (cmd) {
08297       case '1': /* Record your unavailable message */
08298          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08299          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08300          break;
08301       case '2':  /* Record your busy message */
08302          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08303          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08304          break;
08305       case '3': /* Record greeting */
08306          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08307          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08308          break;
08309       case '4':  /* manage the temporary greeting */
08310          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08311          break;
08312       case '5': /* change password */
08313          if (vmu->password[0] == '-') {
08314             cmd = ast_play_and_wait(chan, "vm-no");
08315             break;
08316          }
08317          newpassword[1] = '\0';
08318          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08319          if (cmd == '#')
08320             newpassword[0] = '\0';
08321          else {
08322             if (cmd < 0)
08323                break;
08324             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08325                break;
08326             }
08327          }
08328          cmd = check_password(vmu, newpassword); /* perform password validation */
08329          if (cmd != 0) {
08330             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08331             cmd = ast_play_and_wait(chan, vm_invalid_password);
08332             break;
08333          }
08334          newpassword2[1] = '\0';
08335          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08336          if (cmd == '#')
08337             newpassword2[0] = '\0';
08338          else {
08339             if (cmd < 0)
08340                break;
08341 
08342             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08343                break;
08344             }
08345          }
08346          if (strcmp(newpassword, newpassword2)) {
08347             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08348             cmd = ast_play_and_wait(chan, vm_mismatch);
08349             break;
08350          }
08351          if (pwdchange & PWDCHANGE_INTERNAL)
08352             vm_change_password(vmu, newpassword);
08353          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08354             vm_change_password_shell(vmu, newpassword);
08355 
08356          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08357          cmd = ast_play_and_wait(chan, vm_passchanged);
08358          break;
08359       case '*': 
08360          cmd = 't';
08361          break;
08362       default: 
08363          cmd = 0;
08364          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08365          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08366          if (ast_fileexists(prefile, NULL, NULL)) {
08367             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08368          }
08369          DISPOSE(prefile, -1);
08370          if (!cmd) {
08371             cmd = ast_play_and_wait(chan, "vm-options");
08372          }
08373          if (!cmd) {
08374             cmd = ast_waitfordigit(chan,6000);
08375          }
08376          if (!cmd) {
08377             retries++;
08378          }
08379          if (retries > 3) {
08380             cmd = 't';
08381          }
08382       }
08383    }
08384    if (cmd == 't')
08385       cmd = 0;
08386    return cmd;
08387 }
08388 
08389 /*!
08390  * \brief The handler for 'record a temporary greeting'. 
08391  * \param chan
08392  * \param vmu
08393  * \param vms
08394  * \param fmtc
08395  * \param record_gain
08396  *
08397  * This is option 4 from the mailbox options menu.
08398  * This function manages the following promptings:
08399  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08400  * 2: remove (delete) the temporary greeting.
08401  * *: return to the main menu.
08402  *
08403  * \return zero on success, -1 on error.
08404  */
08405 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08406 {
08407    int cmd = 0;
08408    int retries = 0;
08409    int duration = 0;
08410    char prefile[PATH_MAX] = "";
08411    unsigned char buf[256];
08412    int bytes = 0;
08413 
08414    if (ast_adsi_available(chan)) {
08415       bytes += adsi_logo(buf + bytes);
08416       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08417       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08418       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08419       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08420       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08421    }
08422 
08423    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08424    while ((cmd >= 0) && (cmd != 't')) {
08425       if (cmd)
08426          retries = 0;
08427       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08428       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08429          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08430          cmd = 't';  
08431       } else {
08432          switch (cmd) {
08433          case '1':
08434             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08435             break;
08436          case '2':
08437             DELETE(prefile, -1, prefile, vmu);
08438             ast_play_and_wait(chan, "vm-tempremoved");
08439             cmd = 't';  
08440             break;
08441          case '*': 
08442             cmd = 't';
08443             break;
08444          default:
08445             cmd = ast_play_and_wait(chan,
08446                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08447                   "vm-tempgreeting2" : "vm-tempgreeting");
08448             if (!cmd)
08449                cmd = ast_waitfordigit(chan,6000);
08450             if (!cmd)
08451                retries++;
08452             if (retries > 3)
08453                cmd = 't';
08454          }
08455       }
08456       DISPOSE(prefile, -1);
08457    }
08458    if (cmd == 't')
08459       cmd = 0;
08460    return cmd;
08461 }
08462 
08463 /*!
08464  * \brief Greek syntax for 'You have N messages' greeting.
08465  * \param chan
08466  * \param vms
08467  * \param vmu
08468  *
08469  * \return zero on success, -1 on error.
08470  */   
08471 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08472 {
08473    int cmd=0;
08474 
08475    if (vms->lastmsg > -1) {
08476       cmd = play_message(chan, vmu, vms);
08477    } else {
08478       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08479       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08480          if (!cmd) {
08481             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08482             cmd = ast_play_and_wait(chan, vms->fn);
08483          }
08484          if (!cmd)
08485             cmd = ast_play_and_wait(chan, "vm-messages");
08486       } else {
08487          if (!cmd)
08488             cmd = ast_play_and_wait(chan, "vm-messages");
08489          if (!cmd) {
08490             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08491             cmd = ast_play_and_wait(chan, vms->fn);
08492          }
08493       }
08494    } 
08495    return cmd;
08496 }
08497 
08498 /* Hebrew Syntax */
08499 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08500 {
08501    int cmd = 0;
08502 
08503    if (vms->lastmsg > -1) {
08504       cmd = play_message(chan, vmu, vms);
08505    } else {
08506       if (!strcasecmp(vms->fn, "INBOX")) {
08507          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08508       } else {
08509          cmd = ast_play_and_wait(chan, "vm-nomessages");
08510       }
08511    }
08512    return cmd;
08513 }
08514 
08515 /*! 
08516  * \brief Default English syntax for 'You have N messages' greeting.
08517  * \param chan
08518  * \param vms
08519  * \param vmu
08520  *
08521  * \return zero on success, -1 on error.
08522  */
08523 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08524 {
08525    int cmd=0;
08526 
08527    if (vms->lastmsg > -1) {
08528       cmd = play_message(chan, vmu, vms);
08529    } else {
08530       cmd = ast_play_and_wait(chan, "vm-youhave");
08531       if (!cmd) 
08532          cmd = ast_play_and_wait(chan, "vm-no");
08533       if (!cmd) {
08534          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08535          cmd = ast_play_and_wait(chan, vms->fn);
08536       }
08537       if (!cmd)
08538          cmd = ast_play_and_wait(chan, "vm-messages");
08539    }
08540    return cmd;
08541 }
08542 
08543 /*! 
08544  *\brief Italian syntax for 'You have N messages' greeting.
08545  * \param chan
08546  * \param vms
08547  * \param vmu
08548  *
08549  * \return zero on success, -1 on error.
08550  */
08551 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08552 {
08553    int cmd=0;
08554 
08555    if (vms->lastmsg > -1) {
08556       cmd = play_message(chan, vmu, vms);
08557    } else {
08558       cmd = ast_play_and_wait(chan, "vm-no");
08559       if (!cmd)
08560          cmd = ast_play_and_wait(chan, "vm-message");
08561       if (!cmd) {
08562          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08563          cmd = ast_play_and_wait(chan, vms->fn);
08564       }
08565    }
08566    return cmd;
08567 }
08568 
08569 /*! 
08570  * \brief Spanish syntax for 'You have N messages' greeting.
08571  * \param chan
08572  * \param vms
08573  * \param vmu
08574  *
08575  * \return zero on success, -1 on error.
08576  */
08577 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08578 {
08579    int cmd=0;
08580 
08581    if (vms->lastmsg > -1) {
08582       cmd = play_message(chan, vmu, vms);
08583    } else {
08584       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08585       if (!cmd)
08586          cmd = ast_play_and_wait(chan, "vm-messages");
08587       if (!cmd) {
08588          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08589          cmd = ast_play_and_wait(chan, vms->fn);
08590       }
08591    }
08592    return cmd;
08593 }
08594 
08595 /*! 
08596  * \brief Portuguese syntax for 'You have N messages' greeting.
08597  * \param chan
08598  * \param vms
08599  * \param vmu
08600  *
08601  * \return zero on success, -1 on error.
08602  */
08603 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08604 {
08605    int cmd=0;
08606 
08607    if (vms->lastmsg > -1) {
08608       cmd = play_message(chan, vmu, vms);
08609    } else {
08610       cmd = ast_play_and_wait(chan, "vm-no");
08611       if (!cmd) {
08612          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08613          cmd = ast_play_and_wait(chan, vms->fn);
08614       }
08615       if (!cmd)
08616          cmd = ast_play_and_wait(chan, "vm-messages");
08617    }
08618    return cmd;
08619 }
08620 
08621 /*! 
08622  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08623  * \param chan
08624  * \param vms
08625  * \param vmu
08626  *
08627  * \return zero on success, -1 on error.
08628  */
08629 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08630 {
08631    int cmd=0;
08632 
08633    if (vms->lastmsg > -1) {
08634       cmd = play_message(chan, vmu, vms);
08635    } else {
08636       cmd = ast_play_and_wait(chan, "vm-you");
08637       if (!cmd) 
08638          cmd = ast_play_and_wait(chan, "vm-haveno");
08639       if (!cmd)
08640          cmd = ast_play_and_wait(chan, "vm-messages");
08641       if (!cmd) {
08642          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08643          cmd = ast_play_and_wait(chan, vms->fn);
08644       }
08645    }
08646    return cmd;
08647 }
08648 
08649 /*!
08650  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
08651  * \param chan The channel for the current user. We read the language property from this.
08652  * \param vms passed into the language-specific vm_browse_messages function.
08653  * \param vmu passed into the language-specific vm_browse_messages function.
08654  * 
08655  * The method to be invoked is determined by the value of language code property in the user's channel.
08656  * The default (when unable to match) is to use english.
08657  *
08658  * \return zero on success, -1 on error.
08659  */
08660 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08661 {
08662    if (!strcasecmp(chan->language, "es")) {  /* SPANISH */
08663       return vm_browse_messages_es(chan, vms, vmu);
08664    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
08665       return vm_browse_messages_it(chan, vms, vmu);
08666    } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) {   /* PORTUGUESE */
08667       return vm_browse_messages_pt(chan, vms, vmu);
08668    } else if (!strcasecmp(chan->language, "gr")){
08669       return vm_browse_messages_gr(chan, vms, vmu);   /* GREEK */
08670    } else if (!strncasecmp(chan->language, "zh", 2)) {
08671       return vm_browse_messages_zh(chan, vms, vmu);   /* CHINESE (Taiwan) */
08672    } else if (!strcasecmp(chan->language, "he")) {
08673       return vm_browse_messages_he(chan, vms, vmu);   /* HEBREW */
08674    } else { /* Default to English syntax */
08675       return vm_browse_messages_en(chan, vms, vmu);
08676    }
08677 }
08678 
08679 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
08680          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
08681          int skipuser, int max_logins, int silent)
08682 {
08683    int useadsi=0, valid=0, logretries=0;
08684    char password[AST_MAX_EXTENSION]="", *passptr;
08685    struct ast_vm_user vmus, *vmu = NULL;
08686 
08687    /* If ADSI is supported, setup login screen */
08688    adsi_begin(chan, &useadsi);
08689    if (!skipuser && useadsi)
08690       adsi_login(chan);
08691    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
08692       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
08693       return -1;
08694    }
08695    
08696    /* Authenticate them and get their mailbox/password */
08697    
08698    while (!valid && (logretries < max_logins)) {
08699       /* Prompt for, and read in the username */
08700       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
08701          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
08702          return -1;
08703       }
08704       if (ast_strlen_zero(mailbox)) {
08705          if (chan->cid.cid_num) {
08706             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
08707          } else {
08708             ast_verb(3,"Username not entered\n");  
08709             return -1;
08710          }
08711       }
08712       if (useadsi)
08713          adsi_password(chan);
08714 
08715       if (!ast_strlen_zero(prefix)) {
08716          char fullusername[80] = "";
08717          ast_copy_string(fullusername, prefix, sizeof(fullusername));
08718          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
08719          ast_copy_string(mailbox, fullusername, mailbox_size);
08720       }
08721 
08722       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
08723       vmu = find_user(&vmus, context, mailbox);
08724       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
08725          /* saved password is blank, so don't bother asking */
08726          password[0] = '\0';
08727       } else {
08728          if (ast_streamfile(chan, vm_password, chan->language)) {
08729             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
08730             return -1;
08731          }
08732          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
08733             ast_log(AST_LOG_WARNING, "Unable to read password\n");
08734             return -1;
08735          }
08736       }
08737 
08738       if (vmu) {
08739          passptr = vmu->password;
08740          if (passptr[0] == '-') passptr++;
08741       }
08742       if (vmu && !strcmp(passptr, password))
08743          valid++;
08744       else {
08745          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
08746          if (!ast_strlen_zero(prefix))
08747             mailbox[0] = '\0';
08748       }
08749       logretries++;
08750       if (!valid) {
08751          if (skipuser || logretries >= max_logins) {
08752             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
08753                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
08754                return -1;
08755             }
08756          } else {
08757             if (useadsi)
08758                adsi_login(chan);
08759             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
08760                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
08761                return -1;
08762             }
08763          }
08764          if (ast_waitstream(chan, "")) /* Channel is hung up */
08765             return -1;
08766       }
08767    }
08768    if (!valid && (logretries >= max_logins)) {
08769       ast_stopstream(chan);
08770       ast_play_and_wait(chan, "vm-goodbye");
08771       return -1;
08772    }
08773    if (vmu && !skipuser) {
08774       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
08775    }
08776    return 0;
08777 }
08778 
08779 static int vm_execmain(struct ast_channel *chan, void *data)
08780 {
08781    /* XXX This is, admittedly, some pretty horrendous code.  For some
08782       reason it just seemed a lot easier to do with GOTO's.  I feel
08783       like I'm back in my GWBASIC days. XXX */
08784    int res=-1;
08785    int cmd=0;
08786    int valid = 0;
08787    char prefixstr[80] ="";
08788    char ext_context[256]="";
08789    int box;
08790    int useadsi = 0;
08791    int skipuser = 0;
08792    struct vm_state vms;
08793    struct ast_vm_user *vmu = NULL, vmus;
08794    char *context=NULL;
08795    int silentexit = 0;
08796    struct ast_flags flags = { 0 };
08797    signed char record_gain = 0;
08798    int play_auto = 0;
08799    int play_folder = 0;
08800    int in_urgent = 0;
08801 #ifdef IMAP_STORAGE
08802    int deleted = 0;
08803 #endif
08804 
08805    /* Add the vm_state to the active list and keep it active */
08806    memset(&vms, 0, sizeof(vms));
08807 
08808    vms.lastmsg = -1;
08809 
08810    memset(&vmus, 0, sizeof(vmus));
08811 
08812    if (chan->_state != AST_STATE_UP) {
08813       ast_debug(1, "Before ast_answer\n");
08814       ast_answer(chan);
08815    }
08816 
08817    if (!ast_strlen_zero(data)) {
08818       char *opts[OPT_ARG_ARRAY_SIZE];
08819       char *parse;
08820       AST_DECLARE_APP_ARGS(args,
08821          AST_APP_ARG(argv0);
08822          AST_APP_ARG(argv1);
08823       );
08824 
08825       parse = ast_strdupa(data);
08826 
08827       AST_STANDARD_APP_ARGS(args, parse);
08828 
08829       if (args.argc == 2) {
08830          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
08831             return -1;
08832          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
08833             int gain;
08834             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
08835                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
08836                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
08837                   return -1;
08838                } else {
08839                   record_gain = (signed char) gain;
08840                }
08841             } else {
08842                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
08843             }
08844          }
08845          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
08846             play_auto = 1;
08847             if (opts[OPT_ARG_PLAYFOLDER]) {
08848                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
08849                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
08850                }
08851             } else {
08852                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
08853             }  
08854             if ( play_folder > 9 || play_folder < 0) {
08855                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
08856                play_folder = 0;
08857             }
08858          }
08859       } else {
08860          /* old style options parsing */
08861          while (*(args.argv0)) {
08862             if (*(args.argv0) == 's')
08863                ast_set_flag(&flags, OPT_SILENT);
08864             else if (*(args.argv0) == 'p')
08865                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
08866             else 
08867                break;
08868             (args.argv0)++;
08869          }
08870 
08871       }
08872 
08873       valid = ast_test_flag(&flags, OPT_SILENT);
08874 
08875       if ((context = strchr(args.argv0, '@')))
08876          *context++ = '\0';
08877 
08878       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
08879          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
08880       else
08881          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
08882 
08883       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
08884          skipuser++;
08885       else
08886          valid = 0;
08887    }
08888 
08889    if (!valid)
08890       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
08891 
08892    ast_debug(1, "After vm_authenticate\n");
08893    if (!res) {
08894       valid = 1;
08895       if (!skipuser)
08896          vmu = &vmus;
08897    } else {
08898       res = 0;
08899    }
08900 
08901    /* If ADSI is supported, setup login screen */
08902    adsi_begin(chan, &useadsi);
08903 
08904    if (!valid) {
08905       goto out;
08906    }
08907 
08908 #ifdef IMAP_STORAGE
08909    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
08910    pthread_setspecific(ts_vmstate.key, &vms);
08911 
08912    vms.interactive = 1;
08913    vms.updated = 1;
08914    if (vmu)
08915       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
08916    vmstate_insert(&vms);
08917    init_vm_state(&vms);
08918 #endif
08919    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
08920       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
08921       cmd = ast_play_and_wait(chan, "an-error-has-occured");
08922       return -1;
08923    }
08924    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
08925       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
08926       cmd = ast_play_and_wait(chan, "an-error-has-occured");
08927       return -1;
08928    }
08929    
08930    /* Set language from config to override channel language */
08931    if (!ast_strlen_zero(vmu->language))
08932       ast_string_field_set(chan, language, vmu->language);
08933 
08934    /* Retrieve urgent, old and new message counts */
08935    ast_debug(1, "Before open_mailbox\n");
08936    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
08937    if (res == ERROR_LOCK_PATH)
08938       goto out;
08939    vms.oldmessages = vms.lastmsg + 1;
08940    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
08941    /* check INBOX */
08942    res = open_mailbox(&vms, vmu, NEW_FOLDER);
08943    if (res == ERROR_LOCK_PATH)
08944       goto out;
08945    vms.newmessages = vms.lastmsg + 1;
08946    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
08947    /* Start in Urgent */
08948    in_urgent = 1;
08949    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
08950    if (res == ERROR_LOCK_PATH)
08951       goto out;
08952    vms.urgentmessages = vms.lastmsg + 1;
08953    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
08954 
08955    /* Select proper mailbox FIRST!! */
08956    if (play_auto) {
08957       if (vms.urgentmessages) {
08958          in_urgent = 1;
08959          res = open_mailbox(&vms, vmu, 11);
08960       } else {
08961          in_urgent = 0;
08962          res = open_mailbox(&vms, vmu, play_folder);
08963       }
08964       if (res == ERROR_LOCK_PATH)
08965          goto out;
08966 
08967       /* If there are no new messages, inform the user and hangup */
08968       if (vms.lastmsg == -1) {
08969          in_urgent = 0;
08970          cmd = vm_browse_messages(chan, &vms, vmu);
08971          res = 0;
08972          goto out;
08973       }
08974    } else {
08975       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
08976          /* If we only have old messages start here */
08977          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
08978          in_urgent = 0;
08979          play_folder = 1;
08980          if (res == ERROR_LOCK_PATH)
08981             goto out;
08982       } else if (!vms.urgentmessages && vms.newmessages) {
08983          /* If we have new messages but none are urgent */
08984          in_urgent = 0;
08985          res = open_mailbox(&vms, vmu, NEW_FOLDER);
08986          if (res == ERROR_LOCK_PATH)
08987             goto out;
08988       }
08989    }
08990 
08991    if (useadsi)
08992       adsi_status(chan, &vms);
08993    res = 0;
08994 
08995    /* Check to see if this is a new user */
08996    if (!strcasecmp(vmu->mailbox, vmu->password) && 
08997       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
08998       if (ast_play_and_wait(chan, "vm-newuser") == -1)
08999          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
09000       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
09001       if ((cmd == 't') || (cmd == '#')) {
09002          /* Timeout */
09003          res = 0;
09004          goto out;
09005       } else if (cmd < 0) {
09006          /* Hangup */
09007          res = -1;
09008          goto out;
09009       }
09010    }
09011 #ifdef IMAP_STORAGE
09012       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
09013       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
09014          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
09015          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09016       }
09017       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
09018       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
09019          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
09020          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09021       }
09022 #endif
09023    if (play_auto) {
09024       cmd = '1';
09025    } else {
09026       cmd = vm_intro(chan, vmu, &vms);
09027    }
09028 
09029    vms.repeats = 0;
09030    vms.starting = 1;
09031    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09032       /* Run main menu */
09033       switch (cmd) {
09034       case '1': /* First message */
09035          vms.curmsg = 0;
09036          /* Fall through */
09037       case '5': /* Play current message */
09038          cmd = vm_browse_messages(chan, &vms, vmu);
09039          break;
09040       case '2': /* Change folders */
09041          if (useadsi)
09042             adsi_folders(chan, 0, "Change to folder...");
09043          cmd = get_folder2(chan, "vm-changeto", 0);
09044          if (cmd == '#') {
09045             cmd = 0;
09046          } else if (cmd > 0) {
09047             cmd = cmd - '0';
09048             res = close_mailbox(&vms, vmu);
09049             if (res == ERROR_LOCK_PATH)
09050                goto out;
09051             /* If folder is not urgent, set in_urgent to zero! */
09052             if (cmd != 11) in_urgent = 0;
09053             res = open_mailbox(&vms, vmu, cmd);
09054             if (res == ERROR_LOCK_PATH)
09055                goto out;
09056             play_folder = cmd;
09057             cmd = 0;
09058          }
09059          if (useadsi)
09060             adsi_status2(chan, &vms);
09061             
09062          if (!cmd)
09063             cmd = vm_play_folder_name(chan, vms.vmbox);
09064 
09065          vms.starting = 1;
09066          break;
09067       case '3': /* Advanced options */
09068          cmd = 0;
09069          vms.repeats = 0;
09070          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09071             switch (cmd) {
09072             case '1': /* Reply */
09073                if (vms.lastmsg > -1 && !vms.starting) {
09074                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09075                   if (cmd == ERROR_LOCK_PATH) {
09076                      res = cmd;
09077                      goto out;
09078                   }
09079                } else
09080                   cmd = ast_play_and_wait(chan, "vm-sorry");
09081                cmd = 't';
09082                break;
09083             case '2': /* Callback */
09084                if (!vms.starting)
09085                   ast_verb(3, "Callback Requested\n");
09086                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09087                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09088                   if (cmd == 9) {
09089                      silentexit = 1;
09090                      goto out;
09091                   } else if (cmd == ERROR_LOCK_PATH) {
09092                      res = cmd;
09093                      goto out;
09094                   }
09095                } else 
09096                   cmd = ast_play_and_wait(chan, "vm-sorry");
09097                cmd = 't';
09098                break;
09099             case '3': /* Envelope */
09100                if (vms.lastmsg > -1 && !vms.starting) {
09101                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09102                   if (cmd == ERROR_LOCK_PATH) {
09103                      res = cmd;
09104                      goto out;
09105                   }
09106                } else
09107                   cmd = ast_play_and_wait(chan, "vm-sorry");
09108                cmd = 't';
09109                break;
09110             case '4': /* Dialout */
09111                if (!ast_strlen_zero(vmu->dialout)) {
09112                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09113                   if (cmd == 9) {
09114                      silentexit = 1;
09115                      goto out;
09116                   }
09117                } else 
09118                   cmd = ast_play_and_wait(chan, "vm-sorry");
09119                cmd = 't';
09120                break;
09121 
09122             case '5': /* Leave VoiceMail */
09123                if (ast_test_flag(vmu, VM_SVMAIL)) {
09124                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09125                   if (cmd == ERROR_LOCK_PATH) {
09126                      res = cmd;
09127                      ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
09128                      goto out;
09129                   }
09130                } else
09131                   cmd = ast_play_and_wait(chan,"vm-sorry");
09132                cmd='t';
09133                break;
09134                
09135             case '*': /* Return to main menu */
09136                cmd = 't';
09137                break;
09138 
09139             default:
09140                cmd = 0;
09141                if (!vms.starting) {
09142                   cmd = ast_play_and_wait(chan, "vm-toreply");
09143                }
09144                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09145                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09146                }
09147                if (!cmd && !vms.starting) {
09148                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09149                }
09150                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09151                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09152                }
09153                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09154                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09155                if (!cmd)
09156                   cmd = ast_play_and_wait(chan, "vm-starmain");
09157                if (!cmd)
09158                   cmd = ast_waitfordigit(chan,6000);
09159                if (!cmd)
09160                   vms.repeats++;
09161                if (vms.repeats > 3)
09162                   cmd = 't';
09163             }
09164          }
09165          if (cmd == 't') {
09166             cmd = 0;
09167             vms.repeats = 0;
09168          }
09169          break;
09170       case '4': /* Go to the previous message */
09171          if (vms.curmsg > 0) {
09172             vms.curmsg--;
09173             cmd = play_message(chan, vmu, &vms);
09174          } else {
09175             /* Check if we were listening to new
09176                messages.  If so, go to Urgent messages
09177                instead of saying "no more messages"
09178             */
09179             if (in_urgent == 0 && vms.urgentmessages > 0) {
09180                /* Check for Urgent messages */
09181                in_urgent = 1;
09182                res = close_mailbox(&vms, vmu);
09183                if (res == ERROR_LOCK_PATH)
09184                   goto out;
09185                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09186                if (res == ERROR_LOCK_PATH)
09187                   goto out;
09188                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09189                vms.curmsg = vms.lastmsg;
09190                if (vms.lastmsg < 0)
09191                   cmd = ast_play_and_wait(chan, "vm-nomore");
09192             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09193                vms.curmsg = vms.lastmsg;
09194                cmd = play_message(chan, vmu, &vms);
09195             } else {
09196                cmd = ast_play_and_wait(chan, "vm-nomore");
09197             }
09198          }
09199          break;
09200       case '6': /* Go to the next message */
09201          if (vms.curmsg < vms.lastmsg) {
09202             vms.curmsg++;
09203             cmd = play_message(chan, vmu, &vms);
09204          } else {
09205             if (in_urgent && vms.newmessages > 0) {
09206                /* Check if we were listening to urgent
09207                 * messages.  If so, go to regular new messages
09208                 * instead of saying "no more messages"
09209                 */
09210                in_urgent = 0;
09211                res = close_mailbox(&vms, vmu);
09212                if (res == ERROR_LOCK_PATH)
09213                   goto out;
09214                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09215                if (res == ERROR_LOCK_PATH)
09216                   goto out;
09217                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09218                vms.curmsg = -1;
09219                if (vms.lastmsg < 0) {
09220                   cmd = ast_play_and_wait(chan, "vm-nomore");
09221                }
09222             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09223                vms.curmsg = 0;
09224                cmd = play_message(chan, vmu, &vms);
09225             } else {
09226                cmd = ast_play_and_wait(chan, "vm-nomore");
09227             }
09228          }
09229          break;
09230       case '7': /* Delete the current message */
09231          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09232             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09233             if (useadsi)
09234                adsi_delete(chan, &vms);
09235             if (vms.deleted[vms.curmsg]) {
09236                if (play_folder == 0) {
09237                   if (in_urgent) {
09238                      vms.urgentmessages--;
09239                   } else {
09240                      vms.newmessages--;
09241                   }
09242                }
09243                else if (play_folder == 1)
09244                   vms.oldmessages--;
09245                cmd = ast_play_and_wait(chan, "vm-deleted");
09246             } else {
09247                if (play_folder == 0) {
09248                   if (in_urgent) {
09249                      vms.urgentmessages++;
09250                   } else {
09251                      vms.newmessages++;
09252                   }
09253                }
09254                else if (play_folder == 1)
09255                   vms.oldmessages++;
09256                cmd = ast_play_and_wait(chan, "vm-undeleted");
09257             }
09258             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09259                if (vms.curmsg < vms.lastmsg) {
09260                   vms.curmsg++;
09261                   cmd = play_message(chan, vmu, &vms);
09262                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09263                   vms.curmsg = 0;
09264                   cmd = play_message(chan, vmu, &vms);
09265                } else {
09266                   /* Check if we were listening to urgent
09267                      messages.  If so, go to regular new messages
09268                      instead of saying "no more messages"
09269                   */
09270                   if (in_urgent == 1) {
09271                      /* Check for new messages */
09272                      in_urgent = 0;
09273                      res = close_mailbox(&vms, vmu);
09274                      if (res == ERROR_LOCK_PATH)
09275                         goto out;
09276                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09277                      if (res == ERROR_LOCK_PATH)
09278                         goto out;
09279                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09280                      vms.curmsg = -1;
09281                      if (vms.lastmsg < 0)
09282                         cmd = ast_play_and_wait(chan, "vm-nomore");
09283                   } else {
09284                      cmd = ast_play_and_wait(chan, "vm-nomore");
09285                   }
09286                }
09287             }
09288          } else /* Delete not valid if we haven't selected a message */
09289             cmd = 0;
09290 #ifdef IMAP_STORAGE
09291          deleted = 1;
09292 #endif
09293          break;
09294    
09295       case '8': /* Forward the current messgae */
09296          if (vms.lastmsg > -1) {
09297             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09298             if (cmd == ERROR_LOCK_PATH) {
09299                res = cmd;
09300                goto out;
09301             }
09302          } else {
09303             /* Check if we were listening to urgent
09304                messages.  If so, go to regular new messages
09305                instead of saying "no more messages"
09306             */
09307             if (in_urgent == 1 && vms.newmessages > 0) {
09308                /* Check for new messages */
09309                in_urgent = 0;
09310                res = close_mailbox(&vms, vmu);
09311                if (res == ERROR_LOCK_PATH)
09312                   goto out;
09313                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09314                if (res == ERROR_LOCK_PATH)
09315                   goto out;
09316                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09317                vms.curmsg = -1;
09318                if (vms.lastmsg < 0)
09319                   cmd = ast_play_and_wait(chan, "vm-nomore");
09320             } else {
09321                cmd = ast_play_and_wait(chan, "vm-nomore");
09322             }
09323          }
09324          break;
09325       case '9': /* Save message to folder */
09326          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09327             /* No message selected */
09328             cmd = 0;
09329             break;
09330          }
09331          if (useadsi)
09332             adsi_folders(chan, 1, "Save to folder...");
09333          cmd = get_folder2(chan, "vm-savefolder", 1);
09334          box = 0; /* Shut up compiler */
09335          if (cmd == '#') {
09336             cmd = 0;
09337             break;
09338          } else if (cmd > 0) {
09339             box = cmd = cmd - '0';
09340             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09341             if (cmd == ERROR_LOCK_PATH) {
09342                res = cmd;
09343                goto out;
09344 #ifndef IMAP_STORAGE
09345             } else if (!cmd) {
09346                vms.deleted[vms.curmsg] = 1;
09347 #endif
09348             } else {
09349                vms.deleted[vms.curmsg] = 0;
09350                vms.heard[vms.curmsg] = 0;
09351             }
09352          }
09353          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09354          if (useadsi)
09355             adsi_message(chan, &vms);
09356          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09357          if (!cmd) {
09358             cmd = ast_play_and_wait(chan, "vm-message");
09359             if (!cmd) 
09360                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09361             if (!cmd)
09362                cmd = ast_play_and_wait(chan, "vm-savedto");
09363             if (!cmd)
09364                cmd = vm_play_folder_name(chan, vms.fn);
09365          } else {
09366             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09367          }
09368          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09369             if (vms.curmsg < vms.lastmsg) {
09370                vms.curmsg++;
09371                cmd = play_message(chan, vmu, &vms);
09372             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09373                vms.curmsg = 0;
09374                cmd = play_message(chan, vmu, &vms);
09375             } else {
09376                /* Check if we were listening to urgent
09377                   messages.  If so, go to regular new messages
09378                   instead of saying "no more messages"
09379                */
09380                if (in_urgent == 1 && vms.newmessages > 0) {
09381                   /* Check for new messages */
09382                   in_urgent = 0;
09383                   res = close_mailbox(&vms, vmu);
09384                   if (res == ERROR_LOCK_PATH)
09385                      goto out;
09386                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09387                   if (res == ERROR_LOCK_PATH)
09388                      goto out;
09389                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09390                   vms.curmsg = -1;
09391                   if (vms.lastmsg < 0)
09392                      cmd = ast_play_and_wait(chan, "vm-nomore");
09393                } else {
09394                   cmd = ast_play_and_wait(chan, "vm-nomore");
09395                }
09396             }
09397          }
09398          break;
09399       case '*': /* Help */
09400          if (!vms.starting) {
09401             cmd = ast_play_and_wait(chan, "vm-onefor");
09402             if (!cmd)
09403                cmd = vm_play_folder_name(chan, vms.vmbox);
09404             if (!cmd)
09405                cmd = ast_play_and_wait(chan, "vm-opts");
09406             if (!cmd)
09407                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09408          } else
09409             cmd = 0;
09410          break;
09411       case '0': /* Mailbox options */
09412          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09413          if (useadsi)
09414             adsi_status(chan, &vms);
09415          break;
09416       default: /* Nothing */
09417          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09418          break;
09419       }
09420    }
09421    if ((cmd == 't') || (cmd == '#')) {
09422       /* Timeout */
09423       res = 0;
09424    } else {
09425       /* Hangup */
09426       res = -1;
09427    }
09428 
09429 out:
09430    if (res > -1) {
09431       ast_stopstream(chan);
09432       adsi_goodbye(chan);
09433       if (valid) {
09434          if (silentexit)
09435             res = ast_play_and_wait(chan, "vm-dialout");
09436          else 
09437             res = ast_play_and_wait(chan, "vm-goodbye");
09438          if (res > 0)
09439             res = 0;
09440       }
09441       if (useadsi)
09442          ast_adsi_unload_session(chan);
09443    }
09444    if (vmu)
09445       close_mailbox(&vms, vmu);
09446    if (valid) {
09447       int new = 0, old = 0, urgent = 0;
09448       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09449       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09450       /* Urgent flag not passwd to externnotify here */
09451       run_externnotify(vmu->context, vmu->mailbox, NULL);
09452       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09453       queue_mwi_event(ext_context, urgent, new, old);
09454    }
09455 #ifdef IMAP_STORAGE
09456    /* expunge message - use UID Expunge if supported on IMAP server*/
09457    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09458    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09459       ast_mutex_lock(&vms.lock);
09460 #ifdef HAVE_IMAP_TK2006
09461       if (LEVELUIDPLUS (vms.mailstream)) {
09462          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09463       } else 
09464 #endif
09465          mail_expunge(vms.mailstream);
09466       ast_mutex_unlock(&vms.lock);
09467    }
09468    /*  before we delete the state, we should copy pertinent info
09469     *  back to the persistent model */
09470    if (vmu) {
09471       vmstate_delete(&vms);
09472    }
09473 #endif
09474    if (vmu)
09475       free_user(vmu);
09476    if (vms.deleted)
09477       ast_free(vms.deleted);
09478    if (vms.heard)
09479       ast_free(vms.heard);
09480 
09481 #ifdef IMAP_STORAGE
09482    pthread_setspecific(ts_vmstate.key, NULL);
09483 #endif
09484    return res;
09485 }
09486 
09487 static int vm_exec(struct ast_channel *chan, void *data)
09488 {
09489    int res = 0;
09490    char *tmp;
09491    struct leave_vm_options leave_options;
09492    struct ast_flags flags = { 0 };
09493    char *opts[OPT_ARG_ARRAY_SIZE];
09494    AST_DECLARE_APP_ARGS(args,
09495       AST_APP_ARG(argv0);
09496       AST_APP_ARG(argv1);
09497    );
09498    
09499    memset(&leave_options, 0, sizeof(leave_options));
09500 
09501    if (chan->_state != AST_STATE_UP)
09502       ast_answer(chan);
09503 
09504    if (!ast_strlen_zero(data)) {
09505       tmp = ast_strdupa(data);
09506       AST_STANDARD_APP_ARGS(args, tmp);
09507       if (args.argc == 2) {
09508          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09509             return -1;
09510          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09511          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09512             int gain;
09513 
09514             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
09515                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09516                return -1;
09517             } else {
09518                leave_options.record_gain = (signed char) gain;
09519             }
09520          }
09521          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09522             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09523                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09524          }
09525       }
09526    } else {
09527       char temp[256];
09528       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09529       if (res < 0)
09530          return res;
09531       if (ast_strlen_zero(temp))
09532          return 0;
09533       args.argv0 = ast_strdupa(temp);
09534    }
09535 
09536    res = leave_voicemail(chan, args.argv0, &leave_options);
09537 
09538    if (res == ERROR_LOCK_PATH) {
09539       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09540       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09541       res = 0;
09542    }
09543 
09544    return res;
09545 }
09546 
09547 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09548 {
09549    struct ast_vm_user *vmu;
09550 
09551    AST_LIST_TRAVERSE(&users, vmu, list) {
09552       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09553          if (strcasecmp(vmu->context, context)) {
09554             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09555                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09556                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09557                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09558          }
09559          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09560          return NULL;
09561       }
09562       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09563          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09564          return NULL;
09565       }
09566    }
09567    
09568    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09569       return NULL;
09570    
09571    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09572    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09573 
09574    AST_LIST_INSERT_TAIL(&users, vmu, list);
09575    
09576    return vmu;
09577 }
09578 
09579 static int append_mailbox(const char *context, const char *box, const char *data)
09580 {
09581    /* Assumes lock is already held */
09582    char *tmp;
09583    char *stringp;
09584    char *s;
09585    struct ast_vm_user *vmu;
09586    char *mailbox_full;
09587    int new = 0, old = 0, urgent = 0;
09588 
09589    tmp = ast_strdupa(data);
09590 
09591    if (!(vmu = find_or_create(context, box)))
09592       return -1;
09593    
09594    populate_defaults(vmu);
09595 
09596    stringp = tmp;
09597    if ((s = strsep(&stringp, ","))) 
09598       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09599    if (stringp && (s = strsep(&stringp, ","))) 
09600       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09601    if (stringp && (s = strsep(&stringp, ","))) 
09602       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09603    if (stringp && (s = strsep(&stringp, ","))) 
09604       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09605    if (stringp && (s = strsep(&stringp, ","))) 
09606       apply_options(vmu, s);
09607 
09608    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09609    strcpy(mailbox_full, box);
09610    strcat(mailbox_full, "@");
09611    strcat(mailbox_full, context);
09612 
09613    inboxcount2(mailbox_full, &urgent, &new, &old);
09614    queue_mwi_event(mailbox_full, urgent, new, old);
09615 
09616    return 0;
09617 }
09618 
09619 static int vm_box_exists(struct ast_channel *chan, void *data) 
09620 {
09621    struct ast_vm_user svm;
09622    char *context, *box;
09623    AST_DECLARE_APP_ARGS(args,
09624       AST_APP_ARG(mbox);
09625       AST_APP_ARG(options);
09626    );
09627    static int dep_warning = 0;
09628 
09629    if (ast_strlen_zero(data)) {
09630       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
09631       return -1;
09632    }
09633 
09634    if (!dep_warning) {
09635       dep_warning = 1;
09636       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
09637    }
09638 
09639    box = ast_strdupa(data);
09640 
09641    AST_STANDARD_APP_ARGS(args, box);
09642 
09643    if (args.options) {
09644    }
09645 
09646    if ((context = strchr(args.mbox, '@'))) {
09647       *context = '\0';
09648       context++;
09649    }
09650 
09651    if (find_user(&svm, context, args.mbox)) {
09652       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
09653    } else
09654       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
09655 
09656    return 0;
09657 }
09658 
09659 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
09660 {
09661    struct ast_vm_user svm;
09662    AST_DECLARE_APP_ARGS(arg,
09663       AST_APP_ARG(mbox);
09664       AST_APP_ARG(context);
09665    );
09666 
09667    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
09668 
09669    if (ast_strlen_zero(arg.mbox)) {
09670       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
09671       return -1;
09672    }
09673 
09674    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
09675    return 0;
09676 }
09677 
09678 static struct ast_custom_function mailbox_exists_acf = {
09679    .name = "MAILBOX_EXISTS",
09680    .synopsis = "Tell if a mailbox is configured",
09681    .desc =
09682 "Returns a boolean of whether the corresponding mailbox exists.  If context\n"
09683 "is not specified, defaults to the \"default\" context.\n",
09684    .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
09685    .read = acf_mailbox_exists,
09686 };
09687 
09688 static int vmauthenticate(struct ast_channel *chan, void *data)
09689 {
09690    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
09691    struct ast_vm_user vmus;
09692    char *options = NULL;
09693    int silent = 0, skipuser = 0;
09694    int res = -1;
09695    
09696    if (s) {
09697       s = ast_strdupa(s);
09698       user = strsep(&s, ",");
09699       options = strsep(&s, ",");
09700       if (user) {
09701          s = user;
09702          user = strsep(&s, "@");
09703          context = strsep(&s, "");
09704          if (!ast_strlen_zero(user))
09705             skipuser++;
09706          ast_copy_string(mailbox, user, sizeof(mailbox));
09707       }
09708    }
09709 
09710    if (options) {
09711       silent = (strchr(options, 's')) != NULL;
09712    }
09713 
09714    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
09715       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
09716       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
09717       ast_play_and_wait(chan, "auth-thankyou");
09718       res = 0;
09719    }
09720 
09721    return res;
09722 }
09723 
09724 static char *show_users_realtime(int fd, const char *context)
09725 {
09726    struct ast_config *cfg;
09727    const char *cat = NULL;
09728 
09729    if (!(cfg = ast_load_realtime_multientry("voicemail", 
09730       "context", context, SENTINEL))) {
09731       return CLI_FAILURE;
09732    }
09733 
09734    ast_cli(fd,
09735       "\n"
09736       "=============================================================\n"
09737       "=== Configured Voicemail Users ==============================\n"
09738       "=============================================================\n"
09739       "===\n");
09740 
09741    while ((cat = ast_category_browse(cfg, cat))) {
09742       struct ast_variable *var = NULL;
09743       ast_cli(fd,
09744          "=== Mailbox ...\n"
09745          "===\n");
09746       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
09747          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
09748       ast_cli(fd,
09749          "===\n"
09750          "=== ---------------------------------------------------------\n"
09751          "===\n");
09752    }
09753 
09754    ast_cli(fd,
09755       "=============================================================\n"
09756       "\n");
09757 
09758    ast_config_destroy(cfg);
09759 
09760    return CLI_SUCCESS;
09761 }
09762 
09763 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
09764 {
09765    int which = 0;
09766    int wordlen;
09767    struct ast_vm_user *vmu;
09768    const char *context = "";
09769 
09770    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
09771    if (pos > 4)
09772       return NULL;
09773    if (pos == 3)
09774       return (state == 0) ? ast_strdup("for") : NULL;
09775    wordlen = strlen(word);
09776    AST_LIST_TRAVERSE(&users, vmu, list) {
09777       if (!strncasecmp(word, vmu->context, wordlen)) {
09778          if (context && strcmp(context, vmu->context) && ++which > state)
09779             return ast_strdup(vmu->context);
09780          /* ignore repeated contexts ? */
09781          context = vmu->context;
09782       }
09783    }
09784    return NULL;
09785 }
09786 
09787 /*! \brief Show a list of voicemail users in the CLI */
09788 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09789 {
09790    struct ast_vm_user *vmu;
09791 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
09792    const char *context = NULL;
09793    int users_counter = 0;
09794 
09795    switch (cmd) {
09796    case CLI_INIT:
09797       e->command = "voicemail show users";
09798       e->usage =
09799          "Usage: voicemail show users [for <context>]\n"
09800          "       Lists all mailboxes currently set up\n";
09801       return NULL;
09802    case CLI_GENERATE:
09803       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
09804    }  
09805 
09806    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
09807       return CLI_SHOWUSAGE;
09808    if (a->argc == 5) {
09809       if (strcmp(a->argv[3],"for"))
09810          return CLI_SHOWUSAGE;
09811       context = a->argv[4];
09812    }
09813 
09814    if (ast_check_realtime("voicemail")) {
09815       if (!context) {
09816          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
09817          return CLI_SHOWUSAGE;
09818       }
09819       return show_users_realtime(a->fd, context);
09820    }
09821 
09822    AST_LIST_LOCK(&users);
09823    if (AST_LIST_EMPTY(&users)) {
09824       ast_cli(a->fd, "There are no voicemail users currently defined\n");
09825       AST_LIST_UNLOCK(&users);
09826       return CLI_FAILURE;
09827    }
09828    if (a->argc == 3)
09829       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
09830    else {
09831       int count = 0;
09832       AST_LIST_TRAVERSE(&users, vmu, list) {
09833          if (!strcmp(context, vmu->context))
09834             count++;
09835       }
09836       if (count) {
09837          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
09838       } else {
09839          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
09840          AST_LIST_UNLOCK(&users);
09841          return CLI_FAILURE;
09842       }
09843    }
09844    AST_LIST_TRAVERSE(&users, vmu, list) {
09845       int newmsgs = 0, oldmsgs = 0;
09846       char count[12], tmp[256] = "";
09847 
09848       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
09849          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
09850          inboxcount(tmp, &newmsgs, &oldmsgs);
09851          snprintf(count, sizeof(count), "%d", newmsgs);
09852          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
09853          users_counter++;
09854       }
09855    }
09856    AST_LIST_UNLOCK(&users);
09857    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
09858    return CLI_SUCCESS;
09859 }
09860 
09861 /*! \brief Show a list of voicemail zones in the CLI */
09862 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09863 {
09864    struct vm_zone *zone;
09865 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
09866    char *res = CLI_SUCCESS;
09867 
09868    switch (cmd) {
09869    case CLI_INIT:
09870       e->command = "voicemail show zones";
09871       e->usage =
09872          "Usage: voicemail show zones\n"
09873          "       Lists zone message formats\n";
09874       return NULL;
09875    case CLI_GENERATE:
09876       return NULL;
09877    }
09878 
09879    if (a->argc != 3)
09880       return CLI_SHOWUSAGE;
09881 
09882    AST_LIST_LOCK(&zones);
09883    if (!AST_LIST_EMPTY(&zones)) {
09884       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
09885       AST_LIST_TRAVERSE(&zones, zone, list) {
09886          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
09887       }
09888    } else {
09889       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
09890       res = CLI_FAILURE;
09891    }
09892    AST_LIST_UNLOCK(&zones);
09893 
09894    return res;
09895 }
09896 
09897 /*! \brief Reload voicemail configuration from the CLI */
09898 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09899 {
09900    switch (cmd) {
09901    case CLI_INIT:
09902       e->command = "voicemail reload";
09903       e->usage =
09904          "Usage: voicemail reload\n"
09905          "       Reload voicemail configuration\n";
09906       return NULL;
09907    case CLI_GENERATE:
09908       return NULL;
09909    }
09910 
09911    if (a->argc != 2)
09912       return CLI_SHOWUSAGE;
09913 
09914    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
09915    load_config(1);
09916    
09917    return CLI_SUCCESS;
09918 }
09919 
09920 static struct ast_cli_entry cli_voicemail[] = {
09921    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
09922    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
09923    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
09924 };
09925 
09926 static void poll_subscribed_mailboxes(void)
09927 {
09928    struct mwi_sub *mwi_sub;
09929 
09930    AST_RWLIST_RDLOCK(&mwi_subs);
09931    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
09932       int new = 0, old = 0, urgent = 0;
09933 
09934       if (ast_strlen_zero(mwi_sub->mailbox))
09935          continue;
09936 
09937       inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
09938 
09939       if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
09940          mwi_sub->old_urgent = urgent;
09941          mwi_sub->old_new = new;
09942          mwi_sub->old_old = old;
09943          queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
09944       }
09945    }
09946    AST_RWLIST_UNLOCK(&mwi_subs);
09947 }
09948 
09949 static void *mb_poll_thread(void *data)
09950 {
09951    while (poll_thread_run) {
09952       struct timespec ts = { 0, };
09953       struct timeval wait;
09954 
09955       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
09956       ts.tv_sec = wait.tv_sec;
09957       ts.tv_nsec = wait.tv_usec * 1000;
09958 
09959       ast_mutex_lock(&poll_lock);
09960       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
09961       ast_mutex_unlock(&poll_lock);
09962 
09963       if (!poll_thread_run)
09964          break;
09965 
09966       poll_subscribed_mailboxes();
09967    }
09968 
09969    return NULL;
09970 }
09971 
09972 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
09973 {
09974    ast_free(mwi_sub);
09975 }
09976 
09977 static int handle_unsubscribe(void *datap)
09978 {
09979    struct mwi_sub *mwi_sub;
09980    uint32_t *uniqueid = datap;
09981    
09982    AST_RWLIST_WRLOCK(&mwi_subs);
09983    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
09984       if (mwi_sub->uniqueid == *uniqueid) {
09985          AST_LIST_REMOVE_CURRENT(entry);
09986          break;
09987       }
09988    }
09989    AST_RWLIST_TRAVERSE_SAFE_END
09990    AST_RWLIST_UNLOCK(&mwi_subs);
09991 
09992    if (mwi_sub)
09993       mwi_sub_destroy(mwi_sub);
09994 
09995    ast_free(uniqueid);  
09996    return 0;
09997 }
09998 
09999 static int handle_subscribe(void *datap)
10000 {
10001    unsigned int len;
10002    struct mwi_sub *mwi_sub;
10003    struct mwi_sub_task *p = datap;
10004 
10005    len = sizeof(*mwi_sub);
10006    if (!ast_strlen_zero(p->mailbox))
10007       len += strlen(p->mailbox);
10008 
10009    if (!ast_strlen_zero(p->context))
10010       len += strlen(p->context) + 1; /* Allow for seperator */
10011 
10012    if (!(mwi_sub = ast_calloc(1, len)))
10013       return -1;
10014 
10015    mwi_sub->uniqueid = p->uniqueid;
10016    if (!ast_strlen_zero(p->mailbox))
10017       strcpy(mwi_sub->mailbox, p->mailbox);
10018 
10019    if (!ast_strlen_zero(p->context)) {
10020       strcat(mwi_sub->mailbox, "@");
10021       strcat(mwi_sub->mailbox, p->context);
10022    }
10023 
10024    AST_RWLIST_WRLOCK(&mwi_subs);
10025    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10026    AST_RWLIST_UNLOCK(&mwi_subs);
10027    ast_free((void *) p->mailbox);
10028    ast_free((void *) p->context);
10029    ast_free(p);   
10030    return 0;
10031 }
10032 
10033 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10034 {
10035    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10036    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10037       return;
10038 
10039    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10040       return;
10041 
10042    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10043    *uniqueid = u;
10044    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10045       ast_free(uniqueid);
10046    }
10047 }
10048 
10049 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10050 {
10051    struct mwi_sub_task *mwist;
10052    
10053    if (ast_event_get_type(event) != AST_EVENT_SUB)
10054       return;
10055 
10056    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10057       return;
10058 
10059    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10060       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10061       return;
10062    }
10063    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10064    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10065    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10066    
10067    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10068       ast_free(mwist);
10069    }
10070 }
10071 
10072 static void start_poll_thread(void)
10073 {
10074    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10075       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10076       AST_EVENT_IE_END);
10077 
10078    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10079       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10080       AST_EVENT_IE_END);
10081 
10082    if (mwi_sub_sub)
10083       ast_event_report_subs(mwi_sub_sub);
10084 
10085    poll_thread_run = 1;
10086 
10087    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10088 }
10089 
10090 static void stop_poll_thread(void)
10091 {
10092    poll_thread_run = 0;
10093 
10094    if (mwi_sub_sub) {
10095       ast_event_unsubscribe(mwi_sub_sub);
10096       mwi_sub_sub = NULL;
10097    }
10098 
10099    if (mwi_unsub_sub) {
10100       ast_event_unsubscribe(mwi_unsub_sub);
10101       mwi_unsub_sub = NULL;
10102    }
10103 
10104    ast_mutex_lock(&poll_lock);
10105    ast_cond_signal(&poll_cond);
10106    ast_mutex_unlock(&poll_lock);
10107 
10108    pthread_join(poll_thread, NULL);
10109 
10110    poll_thread = AST_PTHREADT_NULL;
10111 }
10112 
10113 /*! \brief Manager list voicemail users command */
10114 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10115 {
10116    struct ast_vm_user *vmu = NULL;
10117    const char *id = astman_get_header(m, "ActionID");
10118    char actionid[128] = "";
10119 
10120    if (!ast_strlen_zero(id))
10121       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10122 
10123    AST_LIST_LOCK(&users);
10124 
10125    if (AST_LIST_EMPTY(&users)) {
10126       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10127       AST_LIST_UNLOCK(&users);
10128       return RESULT_SUCCESS;
10129    }
10130    
10131    astman_send_ack(s, m, "Voicemail user list will follow");
10132    
10133    AST_LIST_TRAVERSE(&users, vmu, list) {
10134       char dirname[256];
10135 
10136 #ifdef IMAP_STORAGE
10137       int new, old;
10138       inboxcount(vmu->mailbox, &new, &old);
10139 #endif
10140       
10141       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10142       astman_append(s,
10143          "%s"
10144          "Event: VoicemailUserEntry\r\n"
10145          "VMContext: %s\r\n"
10146          "VoiceMailbox: %s\r\n"
10147          "Fullname: %s\r\n"
10148          "Email: %s\r\n"
10149          "Pager: %s\r\n"
10150          "ServerEmail: %s\r\n"
10151          "MailCommand: %s\r\n"
10152          "Language: %s\r\n"
10153          "TimeZone: %s\r\n"
10154          "Callback: %s\r\n"
10155          "Dialout: %s\r\n"
10156          "UniqueID: %s\r\n"
10157          "ExitContext: %s\r\n"
10158          "SayDurationMinimum: %d\r\n"
10159          "SayEnvelope: %s\r\n"
10160          "SayCID: %s\r\n"
10161          "AttachMessage: %s\r\n"
10162          "AttachmentFormat: %s\r\n"
10163          "DeleteMessage: %s\r\n"
10164          "VolumeGain: %.2f\r\n"
10165          "CanReview: %s\r\n"
10166          "CallOperator: %s\r\n"
10167          "MaxMessageCount: %d\r\n"
10168          "MaxMessageLength: %d\r\n"
10169          "NewMessageCount: %d\r\n"
10170 #ifdef IMAP_STORAGE
10171          "OldMessageCount: %d\r\n"
10172          "IMAPUser: %s\r\n"
10173 #endif
10174          "\r\n",
10175          actionid,
10176          vmu->context,
10177          vmu->mailbox,
10178          vmu->fullname,
10179          vmu->email,
10180          vmu->pager,
10181          vmu->serveremail,
10182          vmu->mailcmd,
10183          vmu->language,
10184          vmu->zonetag,
10185          vmu->callback,
10186          vmu->dialout,
10187          vmu->uniqueid,
10188          vmu->exit,
10189          vmu->saydurationm,
10190          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10191          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10192          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10193          vmu->attachfmt,
10194          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10195          vmu->volgain,
10196          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10197          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10198          vmu->maxmsg,
10199          vmu->maxsecs,
10200 #ifdef IMAP_STORAGE
10201          new, old, vmu->imapuser
10202 #else
10203          count_messages(vmu, dirname)
10204 #endif
10205          );
10206    }     
10207    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10208 
10209    AST_LIST_UNLOCK(&users);
10210 
10211    return RESULT_SUCCESS;
10212 }
10213 
10214 /*! \brief Free the users structure. */
10215 static void free_vm_users(void) 
10216 {
10217    struct ast_vm_user *current;
10218    AST_LIST_LOCK(&users);
10219    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10220       ast_set_flag(current, VM_ALLOCED);
10221       free_user(current);
10222    }
10223    AST_LIST_UNLOCK(&users);
10224 }
10225 
10226 /*! \brief Free the zones structure. */
10227 static void free_vm_zones(void)
10228 {
10229    struct vm_zone *zcur;
10230    AST_LIST_LOCK(&zones);
10231    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10232       free_zone(zcur);
10233    AST_LIST_UNLOCK(&zones);
10234 }
10235 
10236 static char *substitute_escapes(const char *value)
10237 {
10238    char *current, *result;
10239 
10240    /* Add 16 for fudge factor */
10241    struct ast_str *str = ast_str_create(strlen(value) + 16);
10242 
10243    /* Substitute strings \r, \n, and \t into the appropriate characters */
10244    for (current = (char *) value; *current; current++) {
10245       if (*current == '\\') {
10246          current++;
10247          if (!*current) {
10248             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10249             break;
10250          }
10251          switch (*current) {
10252          case 'r':
10253             ast_str_append(&str, 0, "\r");
10254             break;
10255          case 'n':
10256 #ifdef IMAP_STORAGE
10257             if (!str->used || str->str[str->used - 1] != '\r') {
10258                ast_str_append(&str, 0, "\r");
10259             }
10260 #endif
10261             ast_str_append(&str, 0, "\n");
10262             break;
10263          case 't':
10264             ast_str_append(&str, 0, "\t");
10265             break;
10266          default:
10267             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10268             break;
10269          }
10270       } else {
10271          ast_str_append(&str, 0, "%c", *current);
10272       }
10273    }
10274 
10275    result = ast_strdup(str->str);
10276    ast_free(str);
10277 
10278    return result;
10279 }
10280 
10281 static int load_config(int reload)
10282 {
10283    struct ast_vm_user *current;
10284    struct ast_config *cfg, *ucfg;
10285    char *cat;
10286    struct ast_variable *var;
10287    const char *val;
10288    char *q, *stringp;
10289    int x;
10290    int tmpadsi[4];
10291    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10292 
10293    ast_unload_realtime("voicemail");
10294    ast_unload_realtime("voicemail_data");
10295 
10296    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10297       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
10298          return 0;
10299       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10300       cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
10301    } else {
10302       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10303       ucfg = ast_config_load("users.conf", config_flags);
10304    }
10305 #ifdef IMAP_STORAGE
10306    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10307 #endif
10308    /* set audio control prompts */
10309    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10310    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10311    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10312    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10313    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10314 
10315    /* Free all the users structure */  
10316    free_vm_users();
10317 
10318    /* Free all the zones structure */
10319    free_vm_zones();
10320 
10321    AST_LIST_LOCK(&users);  
10322 
10323    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10324    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10325 
10326    if (cfg) {
10327       /* General settings */
10328 
10329       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10330          val = "default";
10331       ast_copy_string(userscontext, val, sizeof(userscontext));
10332       /* Attach voice message to mail message ? */
10333       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10334          val = "yes";
10335       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10336 
10337       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10338          val = "no";
10339       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10340 
10341       volgain = 0.0;
10342       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10343          sscanf(val, "%30lf", &volgain);
10344 
10345 #ifdef ODBC_STORAGE
10346       strcpy(odbc_database, "asterisk");
10347       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10348          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10349       }
10350       strcpy(odbc_table, "voicemessages");
10351       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10352          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10353       }
10354 #endif      
10355       /* Mail command */
10356       strcpy(mailcmd, SENDMAIL);
10357       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10358          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10359 
10360       maxsilence = 0;
10361       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10362          maxsilence = atoi(val);
10363          if (maxsilence > 0)
10364             maxsilence *= 1000;
10365       }
10366       
10367       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10368          maxmsg = MAXMSG;
10369       } else {
10370          maxmsg = atoi(val);
10371          if (maxmsg <= 0) {
10372             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10373             maxmsg = MAXMSG;
10374          } else if (maxmsg > MAXMSGLIMIT) {
10375             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10376             maxmsg = MAXMSGLIMIT;
10377          }
10378       }
10379 
10380       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10381          maxdeletedmsg = 0;
10382       } else {
10383          if (sscanf(val, "%30d", &x) == 1)
10384             maxdeletedmsg = x;
10385          else if (ast_true(val))
10386             maxdeletedmsg = MAXMSG;
10387          else
10388             maxdeletedmsg = 0;
10389 
10390          if (maxdeletedmsg < 0) {
10391             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10392             maxdeletedmsg = MAXMSG;
10393          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10394             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10395             maxdeletedmsg = MAXMSGLIMIT;
10396          }
10397       }
10398 
10399       /* Load date format config for voicemail mail */
10400       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10401          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10402       }
10403 
10404       /* External password changing command */
10405       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10406          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10407          pwdchange = PWDCHANGE_EXTERNAL;
10408       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10409          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10410          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10411       }
10412  
10413       /* External password validation command */
10414       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10415          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10416          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10417       }
10418 
10419 #ifdef IMAP_STORAGE
10420       /* IMAP server address */
10421       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10422          ast_copy_string(imapserver, val, sizeof(imapserver));
10423       } else {
10424          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10425       }
10426       /* IMAP server port */
10427       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10428          ast_copy_string(imapport, val, sizeof(imapport));
10429       } else {
10430          ast_copy_string(imapport,"143", sizeof(imapport));
10431       }
10432       /* IMAP server flags */
10433       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10434          ast_copy_string(imapflags, val, sizeof(imapflags));
10435       }
10436       /* IMAP server master username */
10437       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10438          ast_copy_string(authuser, val, sizeof(authuser));
10439       }
10440       /* IMAP server master password */
10441       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10442          ast_copy_string(authpassword, val, sizeof(authpassword));
10443       }
10444       /* Expunge on exit */
10445       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10446          if (ast_false(val))
10447             expungeonhangup = 0;
10448          else
10449             expungeonhangup = 1;
10450       } else {
10451          expungeonhangup = 1;
10452       }
10453       /* IMAP voicemail folder */
10454       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10455          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10456       } else {
10457          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10458       }
10459       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10460          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10461       }
10462       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10463          imapgreetings = ast_true(val);
10464       } else {
10465          imapgreetings = 0;
10466       }
10467       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10468          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10469       } else {
10470          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10471       }
10472 
10473       /* There is some very unorthodox casting done here. This is due
10474        * to the way c-client handles the argument passed in. It expects a 
10475        * void pointer and casts the pointer directly to a long without
10476        * first dereferencing it. */
10477       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10478          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10479       } else {
10480          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10481       }
10482 
10483       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10484          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10485       } else {
10486          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10487       }
10488 
10489       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10490          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10491       } else {
10492          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10493       }
10494 
10495       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10496          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10497       } else {
10498          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10499       }
10500 
10501 #endif
10502       /* External voicemail notify application */
10503       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10504          ast_copy_string(externnotify, val, sizeof(externnotify));
10505          ast_debug(1, "found externnotify: %s\n", externnotify);
10506       } else {
10507          externnotify[0] = '\0';
10508       }
10509 
10510       /* SMDI voicemail notification */
10511       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10512          ast_debug(1, "Enabled SMDI voicemail notification\n");
10513          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10514             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10515          } else {
10516             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10517             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10518          }
10519          if (!smdi_iface) {
10520             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10521          } 
10522       }
10523 
10524       /* Silence treshold */
10525       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10526       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10527          silencethreshold = atoi(val);
10528       
10529       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10530          val = ASTERISK_USERNAME;
10531       ast_copy_string(serveremail, val, sizeof(serveremail));
10532       
10533       vmmaxsecs = 0;
10534       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10535          if (sscanf(val, "%30d", &x) == 1) {
10536             vmmaxsecs = x;
10537          } else {
10538             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10539          }
10540       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10541          static int maxmessage_deprecate = 0;
10542          if (maxmessage_deprecate == 0) {
10543             maxmessage_deprecate = 1;
10544             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10545          }
10546          if (sscanf(val, "%30d", &x) == 1) {
10547             vmmaxsecs = x;
10548          } else {
10549             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10550          }
10551       }
10552 
10553       vmminsecs = 0;
10554       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10555          if (sscanf(val, "%30d", &x) == 1) {
10556             vmminsecs = x;
10557             if (maxsilence / 1000 >= vmminsecs) {
10558                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10559             }
10560          } else {
10561             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10562          }
10563       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10564          static int maxmessage_deprecate = 0;
10565          if (maxmessage_deprecate == 0) {
10566             maxmessage_deprecate = 1;
10567             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10568          }
10569          if (sscanf(val, "%30d", &x) == 1) {
10570             vmminsecs = x;
10571             if (maxsilence / 1000 >= vmminsecs) {
10572                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10573             }
10574          } else {
10575             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10576          }
10577       }
10578 
10579       val = ast_variable_retrieve(cfg, "general", "format");
10580       if (!val)
10581          val = "wav";   
10582       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10583 
10584       skipms = 3000;
10585       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10586          if (sscanf(val, "%30d", &x) == 1) {
10587             maxgreet = x;
10588          } else {
10589             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10590          }
10591       }
10592 
10593       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10594          if (sscanf(val, "%30d", &x) == 1) {
10595             skipms = x;
10596          } else {
10597             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10598          }
10599       }
10600 
10601       maxlogins = 3;
10602       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10603          if (sscanf(val, "%30d", &x) == 1) {
10604             maxlogins = x;
10605          } else {
10606             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10607          }
10608       }
10609 
10610       minpassword = MINPASSWORD;
10611       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
10612          if (sscanf(val, "%30d", &x) == 1) {
10613             minpassword = x;
10614          } else {
10615             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
10616          }
10617       }
10618 
10619       /* Force new user to record name ? */
10620       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
10621          val = "no";
10622       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
10623 
10624       /* Force new user to record greetings ? */
10625       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
10626          val = "no";
10627       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
10628 
10629       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
10630          ast_debug(1, "VM_CID Internal context string: %s\n", val);
10631          stringp = ast_strdupa(val);
10632          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
10633             if (!ast_strlen_zero(stringp)) {
10634                q = strsep(&stringp, ",");
10635                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
10636                   q++;
10637                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
10638                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
10639             } else {
10640                cidinternalcontexts[x][0] = '\0';
10641             }
10642          }
10643       }
10644       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
10645          ast_debug(1,"VM Review Option disabled globally\n");
10646          val = "no";
10647       }
10648       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
10649 
10650       /* Temporary greeting reminder */
10651       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
10652          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
10653          val = "no";
10654       } else {
10655          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
10656       }
10657       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
10658       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
10659          ast_debug(1, "VM next message wrap disabled globally\n");
10660          val = "no";
10661       }
10662       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
10663 
10664       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
10665          ast_debug(1,"VM Operator break disabled globally\n");
10666          val = "no";
10667       }
10668       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
10669 
10670       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
10671          ast_debug(1,"VM CID Info before msg disabled globally\n");
10672          val = "no";
10673       } 
10674       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
10675 
10676       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
10677          ast_debug(1,"Send Voicemail msg disabled globally\n");
10678          val = "no";
10679       }
10680       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
10681    
10682       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
10683          ast_debug(1,"ENVELOPE before msg enabled globally\n");
10684          val = "yes";
10685       }
10686       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
10687 
10688       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
10689          ast_debug(1,"Move Heard enabled globally\n");
10690          val = "yes";
10691       }
10692       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
10693 
10694       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
10695          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
10696          val = "no";
10697       }
10698       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
10699 
10700       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
10701          ast_debug(1,"Duration info before msg enabled globally\n");
10702          val = "yes";
10703       }
10704       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
10705 
10706       saydurationminfo = 2;
10707       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
10708          if (sscanf(val, "%30d", &x) == 1) {
10709             saydurationminfo = x;
10710          } else {
10711             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
10712          }
10713       }
10714 
10715       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
10716          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
10717          val = "no";
10718       }
10719       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
10720 
10721       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
10722          ast_copy_string(dialcontext, val, sizeof(dialcontext));
10723          ast_debug(1, "found dialout context: %s\n", dialcontext);
10724       } else {
10725          dialcontext[0] = '\0';  
10726       }
10727       
10728       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
10729          ast_copy_string(callcontext, val, sizeof(callcontext));
10730          ast_debug(1, "found callback context: %s\n", callcontext);
10731       } else {
10732          callcontext[0] = '\0';
10733       }
10734 
10735       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
10736          ast_copy_string(exitcontext, val, sizeof(exitcontext));
10737          ast_debug(1, "found operator context: %s\n", exitcontext);
10738       } else {
10739          exitcontext[0] = '\0';
10740       }
10741       
10742       /* load password sounds configuration */
10743       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
10744          ast_copy_string(vm_password, val, sizeof(vm_password));
10745       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
10746          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
10747       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
10748          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
10749       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
10750          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
10751       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
10752          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
10753       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
10754          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
10755       /* load configurable audio prompts */
10756       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
10757          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
10758       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
10759          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
10760       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
10761          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
10762       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
10763          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
10764       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
10765          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
10766 
10767       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
10768          val = "no";
10769       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
10770 
10771       poll_freq = DEFAULT_POLL_FREQ;
10772       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
10773          if (sscanf(val, "%30u", &poll_freq) != 1) {
10774             poll_freq = DEFAULT_POLL_FREQ;
10775             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
10776          }
10777       }
10778 
10779       poll_mailboxes = 0;
10780       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
10781          poll_mailboxes = ast_true(val);
10782 
10783       if (ucfg) { 
10784          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
10785             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
10786                continue;
10787             if ((current = find_or_create(userscontext, cat))) {
10788                populate_defaults(current);
10789                apply_options_full(current, ast_variable_browse(ucfg, cat));
10790                ast_copy_string(current->context, userscontext, sizeof(current->context));
10791             }
10792          }
10793          ast_config_destroy(ucfg);
10794       }
10795       cat = ast_category_browse(cfg, NULL);
10796       while (cat) {
10797          if (strcasecmp(cat, "general")) {
10798             var = ast_variable_browse(cfg, cat);
10799             if (strcasecmp(cat, "zonemessages")) {
10800                /* Process mailboxes in this context */
10801                while (var) {
10802                   append_mailbox(cat, var->name, var->value);
10803                   var = var->next;
10804                }
10805             } else {
10806                /* Timezones in this context */
10807                while (var) {
10808                   struct vm_zone *z;
10809                   if ((z = ast_malloc(sizeof(*z)))) {
10810                      char *msg_format, *tzone;
10811                      msg_format = ast_strdupa(var->value);
10812                      tzone = strsep(&msg_format, "|");
10813                      if (msg_format) {
10814                         ast_copy_string(z->name, var->name, sizeof(z->name));
10815                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
10816                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
10817                         AST_LIST_LOCK(&zones);
10818                         AST_LIST_INSERT_HEAD(&zones, z, list);
10819                         AST_LIST_UNLOCK(&zones);
10820                      } else {
10821                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
10822                         ast_free(z);
10823                      }
10824                   } else {
10825                      AST_LIST_UNLOCK(&users);
10826                      ast_config_destroy(cfg);
10827                      return -1;
10828                   }
10829                   var = var->next;
10830                }
10831             }
10832          }
10833          cat = ast_category_browse(cfg, cat);
10834       }
10835       memset(fromstring, 0, sizeof(fromstring));
10836       memset(pagerfromstring, 0, sizeof(pagerfromstring));
10837       strcpy(charset, "ISO-8859-1");
10838       if (emailbody) {
10839          ast_free(emailbody);
10840          emailbody = NULL;
10841       }
10842       if (emailsubject) {
10843          ast_free(emailsubject);
10844          emailsubject = NULL;
10845       }
10846       if (pagerbody) {
10847          ast_free(pagerbody);
10848          pagerbody = NULL;
10849       }
10850       if (pagersubject) {
10851          ast_free(pagersubject);
10852          pagersubject = NULL;
10853       }
10854       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
10855          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
10856       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
10857          ast_copy_string(fromstring, val, sizeof(fromstring));
10858       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
10859          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
10860       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
10861          ast_copy_string(charset, val, sizeof(charset));
10862       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
10863          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
10864          for (x = 0; x < 4; x++) {
10865             memcpy(&adsifdn[x], &tmpadsi[x], 1);
10866          }
10867       }
10868       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
10869          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
10870          for (x = 0; x < 4; x++) {
10871             memcpy(&adsisec[x], &tmpadsi[x], 1);
10872          }
10873       }
10874       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
10875          if (atoi(val)) {
10876             adsiver = atoi(val);
10877          }
10878       }
10879       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
10880          ast_copy_string(zonetag, val, sizeof(zonetag));
10881       }
10882       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
10883          emailsubject = ast_strdup(val);
10884       }
10885       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
10886          emailbody = substitute_escapes(val);
10887       }
10888       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
10889          pagersubject = ast_strdup(val);
10890       }
10891       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
10892          pagerbody = substitute_escapes(val);
10893       }
10894       AST_LIST_UNLOCK(&users);
10895       ast_config_destroy(cfg);
10896 
10897       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
10898          start_poll_thread();
10899       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
10900          stop_poll_thread();;
10901 
10902       return 0;
10903    } else {
10904       AST_LIST_UNLOCK(&users);
10905       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
10906       if (ucfg)
10907          ast_config_destroy(ucfg);
10908       return 0;
10909    }
10910 }
10911 
10912 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
10913 {
10914    int res = -1;
10915    char dir[PATH_MAX];
10916    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
10917    ast_debug(2, "About to try retrieving name file %s\n", dir);
10918    RETRIEVE(dir, -1, mailbox, context);
10919    if (ast_fileexists(dir, NULL, NULL)) {
10920       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
10921    }
10922    DISPOSE(dir, -1);
10923    return res;
10924 }
10925 
10926 static int reload(void)
10927 {
10928    return load_config(1);
10929 }
10930 
10931 static int unload_module(void)
10932 {
10933    int res;
10934 
10935    res = ast_unregister_application(app);
10936    res |= ast_unregister_application(app2);
10937    res |= ast_unregister_application(app3);
10938    res |= ast_unregister_application(app4);
10939    res |= ast_custom_function_unregister(&mailbox_exists_acf);
10940    res |= ast_manager_unregister("VoicemailUsersList");
10941    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
10942    ast_uninstall_vm_functions();
10943 
10944    if (poll_thread != AST_PTHREADT_NULL)
10945       stop_poll_thread();
10946 
10947    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
10948    ast_unload_realtime("voicemail");
10949    ast_unload_realtime("voicemail_data");
10950 
10951    free_vm_users();
10952    free_vm_zones();
10953    return res;
10954 }
10955 
10956 static int load_module(void)
10957 {
10958    int res;
10959    my_umask = umask(0);
10960    umask(my_umask);
10961 
10962    /* compute the location of the voicemail spool directory */
10963    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
10964    
10965    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
10966       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
10967    }
10968 
10969    if ((res = load_config(0)))
10970       return res;
10971 
10972    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
10973    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
10974    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
10975    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
10976    res |= ast_custom_function_register(&mailbox_exists_acf);
10977    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
10978    if (res)
10979       return res;
10980 
10981    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
10982 
10983    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
10984    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
10985    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
10986 
10987    return res;
10988 }
10989 
10990 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
10991 {
10992    int cmd = 0;
10993    char destination[80] = "";
10994    int retries = 0;
10995 
10996    if (!num) {
10997       ast_verb(3, "Destination number will be entered manually\n");
10998       while (retries < 3 && cmd != 't') {
10999          destination[1] = '\0';
11000          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11001          if (!cmd)
11002             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11003          if (!cmd)
11004             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11005          if (!cmd) {
11006             cmd = ast_waitfordigit(chan, 6000);
11007             if (cmd)
11008                destination[0] = cmd;
11009          }
11010          if (!cmd) {
11011             retries++;
11012          } else {
11013 
11014             if (cmd < 0)
11015                return 0;
11016             if (cmd == '*') {
11017                ast_verb(3, "User hit '*' to cancel outgoing call\n");
11018                return 0;
11019             }
11020             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
11021                retries++;
11022             else
11023                cmd = 't';
11024          }
11025       }
11026       if (retries >= 3) {
11027          return 0;
11028       }
11029       
11030    } else {
11031       if (option_verbose > 2)
11032          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11033       ast_copy_string(destination, num, sizeof(destination));
11034    }
11035 
11036    if (!ast_strlen_zero(destination)) {
11037       if (destination[strlen(destination) -1 ] == '*')
11038          return 0; 
11039       if (option_verbose > 2)
11040          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11041       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11042       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11043       chan->priority = 0;
11044       return 9;
11045    }
11046    return 0;
11047 }
11048 
11049 /*!
11050  * \brief The advanced options within a message.
11051  * \param chan
11052  * \param vmu 
11053  * \param vms
11054  * \param msg
11055  * \param option
11056  * \param record_gain
11057  *
11058  * Provides handling for the play message envelope, call the person back, or reply to message. 
11059  *
11060  * \return zero on success, -1 on error.
11061  */
11062 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)
11063 {
11064    int res = 0;
11065    char filename[PATH_MAX];
11066    struct ast_config *msg_cfg = NULL;
11067    const char *origtime, *context;
11068    char *name, *num;
11069    int retries = 0;
11070    char *cid;
11071    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11072 
11073    vms->starting = 0; 
11074 
11075    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11076 
11077    /* Retrieve info from VM attribute file */
11078    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11079    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11080    msg_cfg = ast_config_load(filename, config_flags);
11081    DISPOSE(vms->curdir, vms->curmsg);
11082    if (!msg_cfg) {
11083       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11084       return 0;
11085    }
11086 
11087    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11088       ast_config_destroy(msg_cfg);
11089       return 0;
11090    }
11091 
11092    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11093 
11094    context = ast_variable_retrieve(msg_cfg, "message", "context");
11095    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11096       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11097    switch (option) {
11098    case 3: /* Play message envelope */
11099       if (!res)
11100          res = play_message_datetime(chan, vmu, origtime, filename);
11101       if (!res)
11102          res = play_message_callerid(chan, vms, cid, context, 0);
11103 
11104       res = 't';
11105       break;
11106 
11107    case 2:  /* Call back */
11108 
11109       if (ast_strlen_zero(cid))
11110          break;
11111 
11112       ast_callerid_parse(cid, &name, &num);
11113       while ((res > -1) && (res != 't')) {
11114          switch (res) {
11115          case '1':
11116             if (num) {
11117                /* Dial the CID number */
11118                res = dialout(chan, vmu, num, vmu->callback);
11119                if (res) {
11120                   ast_config_destroy(msg_cfg);
11121                   return 9;
11122                }
11123             } else {
11124                res = '2';
11125             }
11126             break;
11127 
11128          case '2':
11129             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11130             if (!ast_strlen_zero(vmu->dialout)) {
11131                res = dialout(chan, vmu, NULL, vmu->dialout);
11132                if (res) {
11133                   ast_config_destroy(msg_cfg);
11134                   return 9;
11135                }
11136             } else {
11137                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11138                res = ast_play_and_wait(chan, "vm-sorry");
11139             }
11140             ast_config_destroy(msg_cfg);
11141             return res;
11142          case '*':
11143             res = 't';
11144             break;
11145          case '3':
11146          case '4':
11147          case '5':
11148          case '6':
11149          case '7':
11150          case '8':
11151          case '9':
11152          case '0':
11153 
11154             res = ast_play_and_wait(chan, "vm-sorry");
11155             retries++;
11156             break;
11157          default:
11158             if (num) {
11159                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11160                res = ast_play_and_wait(chan, "vm-num-i-have");
11161                if (!res)
11162                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11163                if (!res)
11164                   res = ast_play_and_wait(chan, "vm-tocallnum");
11165                /* Only prompt for a caller-specified number if there is a dialout context specified */
11166                if (!ast_strlen_zero(vmu->dialout)) {
11167                   if (!res)
11168                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11169                }
11170             } else {
11171                res = ast_play_and_wait(chan, "vm-nonumber");
11172                if (!ast_strlen_zero(vmu->dialout)) {
11173                   if (!res)
11174                      res = ast_play_and_wait(chan, "vm-toenternumber");
11175                }
11176             }
11177             if (!res)
11178                res = ast_play_and_wait(chan, "vm-star-cancel");
11179             if (!res)
11180                res = ast_waitfordigit(chan, 6000);
11181             if (!res) {
11182                retries++;
11183                if (retries > 3)
11184                   res = 't';
11185             }
11186             break; 
11187             
11188          }
11189          if (res == 't')
11190             res = 0;
11191          else if (res == '*')
11192             res = -1;
11193       }
11194       break;
11195       
11196    case 1:  /* Reply */
11197       /* Send reply directly to sender */
11198       if (ast_strlen_zero(cid))
11199          break;
11200 
11201       ast_callerid_parse(cid, &name, &num);
11202       if (!num) {
11203          ast_verb(3, "No CID number available, no reply sent\n");
11204          if (!res)
11205             res = ast_play_and_wait(chan, "vm-nonumber");
11206          ast_config_destroy(msg_cfg);
11207          return res;
11208       } else {
11209          struct ast_vm_user vmu2;
11210          if (find_user(&vmu2, vmu->context, num)) {
11211             struct leave_vm_options leave_options;
11212             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11213             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11214 
11215             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11216             
11217             memset(&leave_options, 0, sizeof(leave_options));
11218             leave_options.record_gain = record_gain;
11219             res = leave_voicemail(chan, mailbox, &leave_options);
11220             if (!res)
11221                res = 't';
11222             ast_config_destroy(msg_cfg);
11223             return res;
11224          } else {
11225             /* Sender has no mailbox, can't reply */
11226             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11227             ast_play_and_wait(chan, "vm-nobox");
11228             res = 't';
11229             ast_config_destroy(msg_cfg);
11230             return res;
11231          }
11232       } 
11233       res = 0;
11234 
11235       break;
11236    }
11237 
11238 #ifndef IMAP_STORAGE
11239    ast_config_destroy(msg_cfg);
11240 
11241    if (!res) {
11242       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11243       vms->heard[msg] = 1;
11244       res = wait_file(chan, vms, vms->fn);
11245    }
11246 #endif
11247    return res;
11248 }
11249 
11250 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11251          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11252          signed char record_gain, struct vm_state *vms, char *flag)
11253 {
11254    /* Record message & let caller review or re-record it, or set options if applicable */
11255    int res = 0;
11256    int cmd = 0;
11257    int max_attempts = 3;
11258    int attempts = 0;
11259    int recorded = 0;
11260    int msg_exists = 0;
11261    signed char zero_gain = 0;
11262    char tempfile[PATH_MAX];
11263    char *acceptdtmf = "#";
11264    char *canceldtmf = "";
11265 
11266    /* Note that urgent and private are for flagging messages as such in the future */
11267 
11268    /* barf if no pointer passed to store duration in */
11269    if (duration == NULL) {
11270       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11271       return -1;
11272    }
11273 
11274    if (!outsidecaller)
11275       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11276    else
11277       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11278 
11279    cmd = '3';  /* Want to start by recording */
11280 
11281    while ((cmd >= 0) && (cmd != 't')) {
11282       switch (cmd) {
11283       case '1':
11284          if (!msg_exists) {
11285             /* In this case, 1 is to record a message */
11286             cmd = '3';
11287             break;
11288          } else {
11289             /* Otherwise 1 is to save the existing message */
11290             ast_verb(3, "Saving message as is\n");
11291             if (!outsidecaller) 
11292                ast_filerename(tempfile, recordfile, NULL);
11293             ast_stream_and_wait(chan, "vm-msgsaved", "");
11294             if (!outsidecaller) {
11295                /* Saves to IMAP server - but SHOULD save to filesystem ONLY if recording greetings! */
11296 #ifndef IMAP_STORAGE
11297                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11298                DISPOSE(recordfile, -1);
11299 #endif
11300             }
11301             cmd = 't';
11302             return res;
11303          }
11304       case '2':
11305          /* Review */
11306          ast_verb(3, "Reviewing the message\n");
11307          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11308          break;
11309       case '3':
11310          msg_exists = 0;
11311          /* Record */
11312          if (recorded == 1) 
11313             ast_verb(3, "Re-recording the message\n");
11314          else  
11315             ast_verb(3, "Recording the message\n");
11316          
11317          if (recorded && outsidecaller) {
11318             cmd = ast_play_and_wait(chan, INTRO);
11319             cmd = ast_play_and_wait(chan, "beep");
11320          }
11321          recorded = 1;
11322          /* 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 */
11323          if (record_gain)
11324             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11325          if (ast_test_flag(vmu, VM_OPERATOR))
11326             canceldtmf = "0";
11327          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11328          if (record_gain)
11329             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11330          if (cmd == -1) {
11331             /* User has hung up, no options to give */
11332             if (!outsidecaller) {
11333                /* user was recording a greeting and they hung up, so let's delete the recording. */
11334                ast_filedelete(tempfile, NULL);
11335             }     
11336             return cmd;
11337          }
11338          if (cmd == '0') {
11339             break;
11340          } else if (cmd == '*') {
11341             break;
11342 #if 0
11343          } else if (vmu->review && (*duration < 5)) {
11344             /* Message is too short */
11345             ast_verb(3, "Message too short\n");
11346             cmd = ast_play_and_wait(chan, "vm-tooshort");
11347             cmd = ast_filedelete(tempfile, NULL);
11348             break;
11349          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11350             /* Message is all silence */
11351             ast_verb(3, "Nothing recorded\n");
11352             cmd = ast_filedelete(tempfile, NULL);
11353             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11354             if (!cmd)
11355                cmd = ast_play_and_wait(chan, "vm-speakup");
11356             break;
11357 #endif
11358          } else {
11359             /* If all is well, a message exists */
11360             msg_exists = 1;
11361             cmd = 0;
11362          }
11363          break;
11364       case '4':
11365          if (outsidecaller) {  /* only mark vm messages */
11366             /* Mark Urgent */
11367             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11368                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11369                ast_debug(1000, "This message is too urgent!\n");
11370                res = ast_play_and_wait(chan, "vm-marked-urgent");
11371                strcpy(flag, "Urgent");
11372             } else if (flag) {
11373                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11374                res = ast_play_and_wait(chan, "vm-urgent-removed");
11375                strcpy(flag, "");
11376             } else {
11377                ast_play_and_wait(chan, "vm-sorry");
11378             }
11379             cmd = 0;
11380          } else {
11381             cmd = ast_play_and_wait(chan, "vm-sorry");
11382          }
11383          break;
11384       case '5':
11385       case '6':
11386       case '7':
11387       case '8':
11388       case '9':
11389       case '*':
11390       case '#':
11391          cmd = ast_play_and_wait(chan, "vm-sorry");
11392          break;
11393 #if 0 
11394 /*  XXX Commented out for the moment because of the dangers of deleting
11395     a message while recording (can put the message numbers out of sync) */
11396       case '*':
11397          /* Cancel recording, delete message, offer to take another message*/
11398          cmd = ast_play_and_wait(chan, "vm-deleted");
11399          cmd = ast_filedelete(tempfile, NULL);
11400          if (outsidecaller) {
11401             res = vm_exec(chan, NULL);
11402             return res;
11403          }
11404          else
11405             return 1;
11406 #endif
11407       case '0':
11408          if (!ast_test_flag(vmu, VM_OPERATOR)) {
11409             cmd = ast_play_and_wait(chan, "vm-sorry");
11410             break;
11411          }
11412          if (msg_exists || recorded) {
11413             cmd = ast_play_and_wait(chan, "vm-saveoper");
11414             if (!cmd)
11415                cmd = ast_waitfordigit(chan, 3000);
11416             if (cmd == '1') {
11417                ast_play_and_wait(chan, "vm-msgsaved");
11418                cmd = '0';
11419             } else if (cmd == '4') {
11420                if (flag) {
11421                   ast_play_and_wait(chan, "vm-marked-urgent");
11422                   strcpy(flag, "Urgent");
11423                }
11424                ast_play_and_wait(chan, "vm-msgsaved");
11425                cmd = '0';
11426             } else {
11427                ast_play_and_wait(chan, "vm-deleted");
11428                DELETE(recordfile, -1, recordfile, vmu);
11429                cmd = '0';
11430             }
11431          }
11432          return cmd;
11433       default:
11434          /* If the caller is an ouside caller, and the review option is enabled,
11435             allow them to review the message, but let the owner of the box review
11436             their OGM's */
11437          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11438             return cmd;
11439          if (msg_exists) {
11440             cmd = ast_play_and_wait(chan, "vm-review");
11441             if (!cmd && outsidecaller) {
11442                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11443                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11444                } else if (flag) {
11445                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11446                }
11447             }
11448          } else {
11449             cmd = ast_play_and_wait(chan, "vm-torerecord");
11450             if (!cmd)
11451                cmd = ast_waitfordigit(chan, 600);
11452          }
11453          
11454          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11455             cmd = ast_play_and_wait(chan, "vm-reachoper");
11456             if (!cmd)
11457                cmd = ast_waitfordigit(chan, 600);
11458          }
11459 #if 0
11460          if (!cmd)
11461             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11462 #endif
11463          if (!cmd)
11464             cmd = ast_waitfordigit(chan, 6000);
11465          if (!cmd) {
11466             attempts++;
11467          }
11468          if (attempts > max_attempts) {
11469             cmd = 't';
11470          }
11471       }
11472    }
11473    if (outsidecaller)
11474       ast_play_and_wait(chan, "vm-goodbye");
11475    if (cmd == 't')
11476       cmd = 0;
11477    return cmd;
11478 }
11479 
11480 /* This is a workaround so that menuselect displays a proper description
11481  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11482  */
11483 
11484 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11485       .load = load_module,
11486       .unload = unload_module,
11487       .reload = reload,
11488       );

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