Fri Jun 19 12:09:29 2009

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="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00050       <depend>unixodbc</depend>
00051       <depend>ltdl</depend>
00052       <conflict>IMAP_STORAGE</conflict>
00053       <defaultenabled>no</defaultenabled>
00054    </member>
00055    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00056       <depend>imap_tk</depend>
00057       <conflict>ODBC_STORAGE</conflict>
00058       <use>ssl</use>
00059       <defaultenabled>no</defaultenabled>
00060    </member>
00061 </category>
00062  ***/
00063 
00064 #include "asterisk.h"
00065 
00066 #ifdef IMAP_STORAGE
00067 #include <ctype.h>
00068 #include <signal.h>
00069 #include <pwd.h>
00070 #ifdef USE_SYSTEM_IMAP
00071 #include <imap/c-client.h>
00072 #include <imap/imap4r1.h>
00073 #include <imap/linkage.h>
00074 #elif defined (USE_SYSTEM_CCLIENT)
00075 #include <c-client/c-client.h>
00076 #include <c-client/imap4r1.h>
00077 #include <c-client/linkage.h>
00078 #else
00079 #include "c-client.h"
00080 #include "imap4r1.h"
00081 #include "linkage.h"
00082 #endif
00083 #endif
00084 
00085 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 188776 $")
00086 
00087 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00088 #include <sys/time.h>
00089 #include <sys/stat.h>
00090 #include <sys/mman.h>
00091 #include <time.h>
00092 #include <dirent.h>
00093 
00094 #include "asterisk/logger.h"
00095 #include "asterisk/lock.h"
00096 #include "asterisk/file.h"
00097 #include "asterisk/channel.h"
00098 #include "asterisk/pbx.h"
00099 #include "asterisk/config.h"
00100 #include "asterisk/say.h"
00101 #include "asterisk/module.h"
00102 #include "asterisk/adsi.h"
00103 #include "asterisk/app.h"
00104 #include "asterisk/manager.h"
00105 #include "asterisk/dsp.h"
00106 #include "asterisk/localtime.h"
00107 #include "asterisk/cli.h"
00108 #include "asterisk/utils.h"
00109 #include "asterisk/stringfields.h"
00110 #include "asterisk/smdi.h"
00111 #include "asterisk/event.h"
00112 #include "asterisk/taskprocessor.h"
00113 
00114 #ifdef ODBC_STORAGE
00115 #include "asterisk/res_odbc.h"
00116 #endif
00117 
00118 #ifdef IMAP_STORAGE
00119 #include "asterisk/threadstorage.h"
00120 
00121 static char imapserver[48];
00122 static char imapport[8];
00123 static char imapflags[128];
00124 static char imapfolder[64];
00125 static char imapparentfolder[64] = "\0";
00126 static char greetingfolder[64];
00127 static char authuser[32];
00128 static char authpassword[42];
00129 
00130 static int expungeonhangup = 1;
00131 static int imapgreetings = 0;
00132 static char delimiter = '\0';
00133 
00134 struct vm_state;
00135 struct ast_vm_user;
00136 
00137 AST_THREADSTORAGE(ts_vmstate);
00138 
00139 /* Forward declarations for IMAP */
00140 static int init_mailstream(struct vm_state *vms, int box);
00141 static void write_file(char *filename, char *buffer, unsigned long len);
00142 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00143 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
00144 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00145 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00146 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00147 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00148 static void vmstate_insert(struct vm_state *vms);
00149 static void vmstate_delete(struct vm_state *vms);
00150 static void set_update(MAILSTREAM * stream);
00151 static void init_vm_state(struct vm_state *vms);
00152 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
00153 static void get_mailbox_delimiter(MAILSTREAM *stream);
00154 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00155 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00156 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);
00157 static void update_messages_by_imapuser(const char *user, unsigned long number);
00158 static int vm_delete(char *file);
00159 
00160 static int imap_remove_file (char *dir, int msgnum);
00161 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00162 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00163 static void check_quota(struct vm_state *vms, char *mailbox);
00164 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00165 struct vmstate {
00166    struct vm_state *vms;
00167    AST_LIST_ENTRY(vmstate) list;
00168 };
00169 
00170 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00171 
00172 #endif
00173 
00174 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00175 
00176 #define COMMAND_TIMEOUT 5000
00177 /* Don't modify these here; set your umask at runtime instead */
00178 #define  VOICEMAIL_DIR_MODE   0777
00179 #define  VOICEMAIL_FILE_MODE  0666
00180 #define  CHUNKSIZE   65536
00181 
00182 #define VOICEMAIL_CONFIG "voicemail.conf"
00183 #define ASTERISK_USERNAME "asterisk"
00184 
00185 /* Define fast-forward, pause, restart, and reverse keys
00186    while listening to a voicemail message - these are
00187    strings, not characters */
00188 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00189 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00190 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00191 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00192 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00193 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00194 
00195 /* Default mail command to mail voicemail. Change it with the
00196     mailcmd= command in voicemail.conf */
00197 #define SENDMAIL "/usr/sbin/sendmail -t"
00198 
00199 #define INTRO "vm-intro"
00200 
00201 #define MAXMSG 100
00202 #define MAXMSGLIMIT 9999
00203 
00204 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
00205 
00206 #define BASELINELEN 72
00207 #define BASEMAXINLINE 256
00208 #define eol "\r\n"
00209 
00210 #define MAX_DATETIME_FORMAT   512
00211 #define MAX_NUM_CID_CONTEXTS 10
00212 
00213 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
00214 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
00215 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
00216 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
00217 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
00218 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
00219 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
00220 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00221 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00222 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
00223 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
00224 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
00225 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
00226 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
00227 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
00228 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00229 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00230 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
00231 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
00232 #define ERROR_LOCK_PATH  -100
00233 
00234 
00235 enum {
00236    NEW_FOLDER,
00237    OLD_FOLDER,
00238    WORK_FOLDER,
00239    FAMILY_FOLDER,
00240    FRIENDS_FOLDER,
00241    GREETINGS_FOLDER
00242 } vm_box;
00243 
00244 enum {
00245    OPT_SILENT =           (1 << 0),
00246    OPT_BUSY_GREETING =    (1 << 1),
00247    OPT_UNAVAIL_GREETING = (1 << 2),
00248    OPT_RECORDGAIN =       (1 << 3),
00249    OPT_PREPEND_MAILBOX =  (1 << 4),
00250    OPT_AUTOPLAY =         (1 << 6),
00251    OPT_DTMFEXIT =         (1 << 7),
00252    OPT_MESSAGE_Urgent =   (1 << 8),
00253    OPT_MESSAGE_PRIORITY = (1 << 9)
00254 } vm_option_flags;
00255 
00256 enum {
00257    OPT_ARG_RECORDGAIN = 0,
00258    OPT_ARG_PLAYFOLDER = 1,
00259    OPT_ARG_DTMFEXIT   = 2,
00260    /* This *must* be the last value in this enum! */
00261    OPT_ARG_ARRAY_SIZE = 3,
00262 } vm_option_args;
00263 
00264 AST_APP_OPTIONS(vm_app_options, {
00265    AST_APP_OPTION('s', OPT_SILENT),
00266    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00267    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00268    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00269    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00270    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00271    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00272    AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
00273    AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
00274 });
00275 
00276 static int load_config(int reload);
00277 
00278 /*! \page vmlang Voicemail Language Syntaxes Supported
00279 
00280    \par Syntaxes supported, not really language codes.
00281    \arg \b en    - English
00282    \arg \b de    - German
00283    \arg \b es    - Spanish
00284    \arg \b fr    - French
00285    \arg \b it    - Italian
00286    \arg \b nl    - Dutch
00287    \arg \b pt    - Portuguese
00288    \arg \b pt_BR - Portuguese (Brazil)
00289    \arg \b gr    - Greek
00290    \arg \b no    - Norwegian
00291    \arg \b se    - Swedish
00292    \arg \b tw    - Chinese (Taiwan)
00293    \arg \b ua - Ukrainian
00294 
00295 German requires the following additional soundfile:
00296 \arg \b 1F  einE (feminine)
00297 
00298 Spanish requires the following additional soundfile:
00299 \arg \b 1M      un (masculine)
00300 
00301 Dutch, Portuguese & Spanish require the following additional soundfiles:
00302 \arg \b vm-INBOXs singular of 'new'
00303 \arg \b vm-Olds      singular of 'old/heard/read'
00304 
00305 NB these are plural:
00306 \arg \b vm-INBOX  nieuwe (nl)
00307 \arg \b vm-Old    oude (nl)
00308 
00309 Polish uses:
00310 \arg \b vm-new-a  'new', feminine singular accusative
00311 \arg \b vm-new-e  'new', feminine plural accusative
00312 \arg \b vm-new-ych   'new', feminine plural genitive
00313 \arg \b vm-old-a  'old', feminine singular accusative
00314 \arg \b vm-old-e  'old', feminine plural accusative
00315 \arg \b vm-old-ych   'old', feminine plural genitive
00316 \arg \b digits/1-a   'one', not always same as 'digits/1'
00317 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00318 
00319 Swedish uses:
00320 \arg \b vm-nytt      singular of 'new'
00321 \arg \b vm-nya    plural of 'new'
00322 \arg \b vm-gammalt   singular of 'old'
00323 \arg \b vm-gamla  plural of 'old'
00324 \arg \b digits/ett   'one', not always same as 'digits/1'
00325 
00326 Norwegian uses:
00327 \arg \b vm-ny     singular of 'new'
00328 \arg \b vm-nye    plural of 'new'
00329 \arg \b vm-gammel singular of 'old'
00330 \arg \b vm-gamle  plural of 'old'
00331 
00332 Dutch also uses:
00333 \arg \b nl-om     'at'?
00334 
00335 Spanish also uses:
00336 \arg \b vm-youhaveno
00337 
00338 Italian requires the following additional soundfile:
00339 
00340 For vm_intro_it:
00341 \arg \b vm-nuovo  new
00342 \arg \b vm-nuovi  new plural
00343 \arg \b vm-vecchio   old
00344 \arg \b vm-vecchi old plural
00345 
00346 Chinese (Taiwan) requires the following additional soundfile:
00347 \arg \b vm-tong      A class-word for call (tong1)
00348 \arg \b vm-ri     A class-word for day (ri4)
00349 \arg \b vm-you    You (ni3)
00350 \arg \b vm-haveno   Have no (mei2 you3)
00351 \arg \b vm-have     Have (you3)
00352 \arg \b vm-listen   To listen (yao4 ting1)
00353 
00354 
00355 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00356 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00357 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00358 
00359 */
00360 
00361 struct baseio {
00362    int iocp;
00363    int iolen;
00364    int linelength;
00365    int ateof;
00366    unsigned char iobuf[BASEMAXINLINE];
00367 };
00368 
00369 /*! Structure for linked list of users 
00370  * Use ast_vm_user_destroy() to free one of these structures. */
00371 struct ast_vm_user {
00372    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00373    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00374    char password[80];               /*!< Secret pin code, numbers only */
00375    char fullname[80];               /*!< Full name, for directory app */
00376    char email[80];                  /*!< E-mail address */
00377    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00378    char serveremail[80];            /*!< From: Mail address */
00379    char mailcmd[160];               /*!< Configurable mail command */
00380    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00381    char zonetag[80];                /*!< Time zone */
00382    char callback[80];
00383    char dialout[80];
00384    char uniqueid[80];               /*!< Unique integer identifier */
00385    char exit[80];
00386    char attachfmt[20];              /*!< Attachment format */
00387    unsigned int flags;              /*!< VM_ flags */ 
00388    int saydurationm;
00389    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00390    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00391    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00392 #ifdef IMAP_STORAGE
00393    char imapuser[80];               /*!< IMAP server login */
00394    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00395    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00396 #endif
00397    double volgain;                  /*!< Volume gain for voicemails sent via email */
00398    AST_LIST_ENTRY(ast_vm_user) list;
00399 };
00400 
00401 /*! Voicemail time zones */
00402 struct vm_zone {
00403    AST_LIST_ENTRY(vm_zone) list;
00404    char name[80];
00405    char timezone[80];
00406    char msg_format[512];
00407 };
00408 
00409 #define VMSTATE_MAX_MSG_ARRAY 256
00410 
00411 /*! Voicemail mailbox state */
00412 struct vm_state {
00413    char curbox[80];
00414    char username[80];
00415    char context[80];
00416    char curdir[PATH_MAX];
00417    char vmbox[PATH_MAX];
00418    char fn[PATH_MAX];
00419    char intro[PATH_MAX];
00420    int *deleted;
00421    int *heard;
00422    int curmsg;
00423    int lastmsg;
00424    int newmessages;
00425    int oldmessages;
00426    int urgentmessages;
00427    int starting;
00428    int repeats;
00429 #ifdef IMAP_STORAGE
00430    ast_mutex_t lock;
00431    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00432    long msgArray[VMSTATE_MAX_MSG_ARRAY];
00433    MAILSTREAM *mailstream;
00434    int vmArrayIndex;
00435    char imapuser[80];                   /*!< IMAP server login */
00436    int interactive;
00437    char introfn[PATH_MAX];              /*!< Name of prepended file */
00438    unsigned int quota_limit;
00439    unsigned int quota_usage;
00440    struct vm_state *persist_vms;
00441 #endif
00442 };
00443 
00444 #ifdef ODBC_STORAGE
00445 static char odbc_database[80];
00446 static char odbc_table[80];
00447 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00448 #define DISPOSE(a,b) remove_file(a,b)
00449 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
00450 #define EXISTS(a,b,c,d) (message_exists(a,b))
00451 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00452 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00453 #define DELETE(a,b,c,d) (delete_file(a,b))
00454 #else
00455 #ifdef IMAP_STORAGE
00456 #define DISPOSE(a,b) (imap_remove_file(a,b))
00457 #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))
00458 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00459 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00460 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00461 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00462 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
00463 #else
00464 #define RETRIEVE(a,b,c,d)
00465 #define DISPOSE(a,b)
00466 #define STORE(a,b,c,d,e,f,g,h,i,j)
00467 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00468 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00469 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
00470 #define DELETE(a,b,c,d) (vm_delete(c))
00471 #endif
00472 #endif
00473 
00474 static char VM_SPOOL_DIR[PATH_MAX];
00475 
00476 static char ext_pass_cmd[128];
00477 static char ext_pass_check_cmd[128];
00478 
00479 static int my_umask;
00480 
00481 #define PWDCHANGE_INTERNAL (1 << 1)
00482 #define PWDCHANGE_EXTERNAL (1 << 2)
00483 static int pwdchange = PWDCHANGE_INTERNAL;
00484 
00485 #ifdef ODBC_STORAGE
00486 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00487 #else
00488 # ifdef IMAP_STORAGE
00489 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00490 # else
00491 # define tdesc "Comedian Mail (Voicemail System)"
00492 # endif
00493 #endif
00494 
00495 static char userscontext[AST_MAX_EXTENSION] = "default";
00496 
00497 static char *addesc = "Comedian Mail";
00498 
00499 static char *synopsis_vm = "Leave a Voicemail message";
00500 
00501 static char *descrip_vm =
00502    "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
00503    "application allows the calling party to leave a message for the specified\n"
00504    "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00505    "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00506    "specified mailbox does not exist.\n"
00507    "  The Voicemail application will exit if any of the following DTMF digits are\n"
00508    "received:\n"
00509    "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00510    "    * - Jump to the 'a' extension in the current dialplan context.\n"
00511    "  This application will set the following channel variable upon completion:\n"
00512    "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00513    "               application. The possible values are:\n"
00514    "               SUCCESS | USEREXIT | FAILED\n\n"
00515    "  Options:\n"
00516    "    b    - Play the 'busy' greeting to the calling party.\n"
00517    "    d([c]) - Accept digits for a new extension in context c, if played during\n"
00518    "             the greeting.  Context defaults to the current context.\n"
00519    "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00520    "           message. The units are whole-number decibels (dB).\n"
00521    "           Only works on supported technologies, which is DAHDI only.\n"
00522    "    s    - Skip the playback of instructions for leaving a message to the\n"
00523    "           calling party.\n"
00524    "    u    - Play the 'unavailable' greeting.\n"
00525    "    U    - Mark message as Urgent.\n"
00526    "    P    - Mark message as PRIORITY.\n";
00527 
00528 static char *synopsis_vmain = "Check Voicemail messages";
00529 
00530 static char *descrip_vmain =
00531    "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
00532    "calling party to check voicemail messages. A specific mailbox, and optional\n"
00533    "corresponding context, may be specified. If a mailbox is not provided, the\n"
00534    "calling party will be prompted to enter one. If a context is not specified,\n"
00535    "the 'default' context will be used.\n\n"
00536    "  Options:\n"
00537    "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00538    "           is entered by the caller.\n"
00539    "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00540    "           message. The units are whole-number decibels (dB).\n"
00541    "    s    - Skip checking the passcode for the mailbox.\n"
00542    "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00543    "           Defaults to INBOX\n";
00544 
00545 static char *synopsis_vm_box_exists =
00546 "Check to see if Voicemail mailbox exists";
00547 
00548 static char *descrip_vm_box_exists =
00549    "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
00550    "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00551    "will be used.\n"
00552    "  This application will set the following channel variable upon completion:\n"
00553    "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00554    "                        MailboxExists application. Possible values include:\n"
00555    "                        SUCCESS | FAILED\n\n"
00556    "  Options: (none)\n";
00557 
00558 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
00559 
00560 static char *descrip_vmauthenticate =
00561    "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
00562    "same way as the Authenticate application, but the passwords are taken from\n"
00563    "voicemail.conf.\n"
00564    "  If the mailbox is specified, only that mailbox's password will be considered\n"
00565    "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00566    "be set with the authenticated mailbox.\n\n"
00567    "  Options:\n"
00568    "    s - Skip playing the initial prompts.\n";
00569 
00570 /* Leave a message */
00571 static char *app = "VoiceMail";
00572 
00573 /* Check mail, control, etc */
00574 static char *app2 = "VoiceMailMain";
00575 
00576 static char *app3 = "MailboxExists";
00577 static char *app4 = "VMAuthenticate";
00578 
00579 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00580 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00581 static char zonetag[80];
00582 static int maxsilence;
00583 static int maxmsg;
00584 static int maxdeletedmsg;
00585 static int silencethreshold = 128;
00586 static char serveremail[80];
00587 static char mailcmd[160];  /* Configurable mail cmd */
00588 static char externnotify[160]; 
00589 static struct ast_smdi_interface *smdi_iface = NULL;
00590 static char vmfmts[80];
00591 static double volgain;
00592 static int vmminsecs;
00593 static int vmmaxsecs;
00594 static int maxgreet;
00595 static int skipms;
00596 static int maxlogins;
00597 static int minpassword;
00598 
00599 /*! Poll mailboxes for changes since there is something external to
00600  *  app_voicemail that may change them. */
00601 static unsigned int poll_mailboxes;
00602 
00603 /*! Polling frequency */
00604 static unsigned int poll_freq;
00605 /*! By default, poll every 30 seconds */
00606 #define DEFAULT_POLL_FREQ 30
00607 
00608 AST_MUTEX_DEFINE_STATIC(poll_lock);
00609 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00610 static pthread_t poll_thread = AST_PTHREADT_NULL;
00611 static unsigned char poll_thread_run;
00612 
00613 /*! Subscription to ... MWI event subscriptions */
00614 static struct ast_event_sub *mwi_sub_sub;
00615 /*! Subscription to ... MWI event un-subscriptions */
00616 static struct ast_event_sub *mwi_unsub_sub;
00617 
00618 /*!
00619  * \brief An MWI subscription
00620  *
00621  * This is so we can keep track of which mailboxes are subscribed to.
00622  * This way, we know which mailboxes to poll when the pollmailboxes
00623  * option is being used.
00624  */
00625 struct mwi_sub {
00626    AST_RWLIST_ENTRY(mwi_sub) entry;
00627    int old_urgent;
00628    int old_new;
00629    int old_old;
00630    uint32_t uniqueid;
00631    char mailbox[1];
00632 };
00633 
00634 struct mwi_sub_task {
00635    const char *mailbox;
00636    const char *context;
00637    uint32_t uniqueid;
00638 };
00639 
00640 static struct ast_taskprocessor *mwi_subscription_tps;
00641 
00642 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00643 
00644 /* custom audio control prompts for voicemail playback */
00645 static char listen_control_forward_key[12];
00646 static char listen_control_reverse_key[12];
00647 static char listen_control_pause_key[12];
00648 static char listen_control_restart_key[12];
00649 static char listen_control_stop_key[12];
00650 
00651 /* custom password sounds */
00652 static char vm_password[80] = "vm-password";
00653 static char vm_newpassword[80] = "vm-newpassword";
00654 static char vm_passchanged[80] = "vm-passchanged";
00655 static char vm_reenterpassword[80] = "vm-reenterpassword";
00656 static char vm_mismatch[80] = "vm-mismatch";
00657 static char vm_invalid_password[80] = "vm-invalid-password";
00658 
00659 static struct ast_flags globalflags = {0};
00660 
00661 static int saydurationminfo;
00662 
00663 static char dialcontext[AST_MAX_CONTEXT] = "";
00664 static char callcontext[AST_MAX_CONTEXT] = "";
00665 static char exitcontext[AST_MAX_CONTEXT] = "";
00666 
00667 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00668 
00669 
00670 static char *emailbody = NULL;
00671 static char *emailsubject = NULL;
00672 static char *pagerbody = NULL;
00673 static char *pagersubject = NULL;
00674 static char fromstring[100];
00675 static char pagerfromstring[100];
00676 static char charset[32] = "ISO-8859-1";
00677 
00678 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00679 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00680 static int adsiver = 1;
00681 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00682 
00683 /* Forward declarations - generic */
00684 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00685 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);
00686 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00687 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00688          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00689          signed char record_gain, struct vm_state *vms, char *flag);
00690 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00691 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00692 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);
00693 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);
00694 static void apply_options(struct ast_vm_user *vmu, const char *options);
00695 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);
00696 static int is_valid_dtmf(const char *key);
00697 
00698 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00699 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00700 #endif
00701 
00702 static char *strip_control(const char *input, char *buf, size_t buflen)
00703 {
00704    char *bufptr = buf;
00705    for (; *input; input++) {
00706       if (*input < 32) {
00707          continue;
00708       }
00709       *bufptr++ = *input;
00710       if (bufptr == buf + buflen - 1) {
00711          break;
00712       }
00713    }
00714    *bufptr = '\0';
00715    return buf;
00716 }
00717 
00718 
00719 /*!
00720  * \brief Sets default voicemail system options to a voicemail user.
00721  *
00722  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
00723  * - all the globalflags
00724  * - the saydurationminfo
00725  * - the callcontext
00726  * - the dialcontext
00727  * - the exitcontext
00728  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
00729  * - volume gain.
00730  */
00731 static void populate_defaults(struct ast_vm_user *vmu)
00732 {
00733    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00734    if (saydurationminfo)
00735       vmu->saydurationm = saydurationminfo;
00736    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00737    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00738    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00739    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00740    if (vmmaxsecs)
00741       vmu->maxsecs = vmmaxsecs;
00742    if (maxmsg)
00743       vmu->maxmsg = maxmsg;
00744    if (maxdeletedmsg)
00745       vmu->maxdeletedmsg = maxdeletedmsg;
00746    vmu->volgain = volgain;
00747 }
00748 
00749 /*!
00750  * \brief Sets a a specific property value.
00751  * \param vmu The voicemail user object to work with.
00752  * \param var The name of the property to be set.
00753  * \param value The value to be set to the property.
00754  * 
00755  * The property name must be one of the understood properties. See the source for details.
00756  */
00757 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00758 {
00759    int x;
00760    if (!strcasecmp(var, "attach")) {
00761       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00762    } else if (!strcasecmp(var, "attachfmt")) {
00763       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00764    } else if (!strcasecmp(var, "serveremail")) {
00765       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00766    } else if (!strcasecmp(var, "language")) {
00767       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00768    } else if (!strcasecmp(var, "tz")) {
00769       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00770 #ifdef IMAP_STORAGE
00771    } else if (!strcasecmp(var, "imapuser")) {
00772       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00773    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00774       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00775    } else if (!strcasecmp(var, "imapvmshareid")) {
00776       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00777 #endif
00778    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00779       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00780    } else if (!strcasecmp(var, "saycid")){
00781       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00782    } else if (!strcasecmp(var,"sendvoicemail")){
00783       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00784    } else if (!strcasecmp(var, "review")){
00785       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00786    } else if (!strcasecmp(var, "tempgreetwarn")){
00787       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00788    } else if (!strcasecmp(var, "messagewrap")){
00789       ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);  
00790    } else if (!strcasecmp(var, "operator")) {
00791       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00792    } else if (!strcasecmp(var, "envelope")){
00793       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00794    } else if (!strcasecmp(var, "moveheard")){
00795       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00796    } else if (!strcasecmp(var, "sayduration")){
00797       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00798    } else if (!strcasecmp(var, "saydurationm")){
00799       if (sscanf(value, "%d", &x) == 1) {
00800          vmu->saydurationm = x;
00801       } else {
00802          ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
00803       }
00804    } else if (!strcasecmp(var, "forcename")){
00805       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00806    } else if (!strcasecmp(var, "forcegreetings")){
00807       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00808    } else if (!strcasecmp(var, "callback")) {
00809       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00810    } else if (!strcasecmp(var, "dialout")) {
00811       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00812    } else if (!strcasecmp(var, "exitcontext")) {
00813       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00814    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
00815       if (vmu->maxsecs <= 0) {
00816          ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
00817          vmu->maxsecs = vmmaxsecs;
00818       } else {
00819          vmu->maxsecs = atoi(value);
00820       }
00821       if (!strcasecmp(var, "maxmessage"))
00822          ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
00823    } else if (!strcasecmp(var, "maxmsg")) {
00824       vmu->maxmsg = atoi(value);
00825       if (vmu->maxmsg <= 0) {
00826          ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
00827          vmu->maxmsg = MAXMSG;
00828       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00829          ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00830          vmu->maxmsg = MAXMSGLIMIT;
00831       }
00832    } else if (!strcasecmp(var, "backupdeleted")) {
00833       if (sscanf(value, "%d", &x) == 1)
00834          vmu->maxdeletedmsg = x;
00835       else if (ast_true(value))
00836          vmu->maxdeletedmsg = MAXMSG;
00837       else
00838          vmu->maxdeletedmsg = 0;
00839 
00840       if (vmu->maxdeletedmsg < 0) {
00841          ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
00842          vmu->maxdeletedmsg = MAXMSG;
00843       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
00844          ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
00845          vmu->maxdeletedmsg = MAXMSGLIMIT;
00846       }
00847    } else if (!strcasecmp(var, "volgain")) {
00848       sscanf(value, "%lf", &vmu->volgain);
00849    } else if (!strcasecmp(var, "options")) {
00850       apply_options(vmu, value);
00851    }
00852 }
00853 
00854 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
00855 {
00856    int fds[2], pid = 0;
00857 
00858    memset(buf, 0, len);
00859 
00860    if (pipe(fds)) {
00861       snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
00862    } else {
00863       /* good to go*/
00864       pid = ast_safe_fork(0);
00865 
00866       if (pid < 0) {
00867          /* ok maybe not */
00868          close(fds[0]);
00869          close(fds[1]);
00870          snprintf(buf, len, "FAILURE: Fork failed");
00871       } else if (pid) {
00872          /* parent */
00873          close(fds[1]);
00874          if (read(fds[0], buf, len) < 0) {
00875             ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
00876          }
00877          close(fds[0]);
00878       } else {
00879          /*  child */
00880          AST_DECLARE_APP_ARGS(arg,
00881             AST_APP_ARG(v)[20];
00882          );
00883          char *mycmd = ast_strdupa(command);
00884 
00885          close(fds[0]);
00886          dup2(fds[1], STDOUT_FILENO);
00887          close(fds[1]);
00888          ast_close_fds_above_n(STDOUT_FILENO);
00889 
00890          AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
00891 
00892          execv(arg.v[0], arg.v); 
00893          printf("FAILURE: %s", strerror(errno));
00894          _exit(0);
00895       }
00896    }
00897    return buf;
00898 }
00899 
00900 /*!
00901  * \brief Check that password meets minimum required length
00902  * \param vmu The voicemail user to change the password for.
00903  * \param password The password string to check
00904  *
00905  * \return zero on ok, 1 on not ok.
00906  */
00907 static int check_password(struct ast_vm_user *vmu, char *password)
00908 {
00909    /* check minimum length */
00910    if (strlen(password) < minpassword)
00911       return 1;
00912    if (!ast_strlen_zero(ext_pass_check_cmd)) {
00913       char cmd[255], buf[255];
00914 
00915       ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
00916 
00917       snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
00918       if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
00919          ast_debug(5, "Result: %s\n", buf);
00920          if (!strncasecmp(buf, "VALID", 5)) {
00921             ast_debug(3, "Passed password check: '%s'\n", buf);
00922             return 0;
00923          } else if (!strncasecmp(buf, "FAILURE", 7)) {
00924             ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
00925             return 0;
00926          } else {
00927             ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
00928             return 1;
00929          }
00930       }
00931    }
00932    return 0;
00933 }
00934 
00935 /*! 
00936  * \brief Performs a change of the voicemail passowrd in the realtime engine.
00937  * \param vmu The voicemail user to change the password for.
00938  * \param password The new value to be set to the password for this user.
00939  * 
00940  * This only works if the voicemail user has a unique id, and if there is a realtime engine configured.
00941  * This is called from the (top level) vm_change_password.
00942  *
00943  * \return zero on success, -1 on error.
00944  */
00945 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00946 {
00947    int res;
00948    if (!ast_strlen_zero(vmu->uniqueid)) {
00949       if (strlen(password) > 10) {
00950          ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
00951       }
00952       res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL);
00953       if (res > 0) {
00954          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00955          res = 0;
00956       } else if (!res) {
00957          res = -1;
00958       }
00959       return res;
00960    }
00961    return -1;
00962 }
00963 
00964 /*!
00965  * \brief Destructively Parse options and apply.
00966  */
00967 static void apply_options(struct ast_vm_user *vmu, const char *options)
00968 {  
00969    char *stringp;
00970    char *s;
00971    char *var, *value;
00972    stringp = ast_strdupa(options);
00973    while ((s = strsep(&stringp, "|"))) {
00974       value = s;
00975       if ((var = strsep(&value, "=")) && value) {
00976          apply_option(vmu, var, value);
00977       }
00978    }  
00979 }
00980 
00981 /*!
00982  * \brief Loads the options specific to a voicemail user.
00983  * 
00984  * This is called when a vm_user structure is being set up, such as from load_options.
00985  */
00986 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
00987 {
00988    struct ast_variable *tmp;
00989    tmp = var;
00990    while (tmp) {
00991       if (!strcasecmp(tmp->name, "vmsecret")) {
00992          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00993       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
00994          if (ast_strlen_zero(retval->password))
00995             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00996       } else if (!strcasecmp(tmp->name, "uniqueid")) {
00997          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00998       } else if (!strcasecmp(tmp->name, "pager")) {
00999          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
01000       } else if (!strcasecmp(tmp->name, "email")) {
01001          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
01002       } else if (!strcasecmp(tmp->name, "fullname")) {
01003          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
01004       } else if (!strcasecmp(tmp->name, "context")) {
01005          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
01006 #ifdef IMAP_STORAGE
01007       } else if (!strcasecmp(tmp->name, "imapuser")) {
01008          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
01009       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
01010          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
01011       } else if (!strcasecmp(var->name, "imapvmshareid")) {
01012          ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
01013 #endif
01014       } else
01015          apply_option(retval, tmp->name, tmp->value);
01016       tmp = tmp->next;
01017    } 
01018 }
01019 
01020 /*!
01021  * \brief Determines if a DTMF key entered is valid.
01022  * \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.
01023  *
01024  * Tests the character entered against the set of valid DTMF characters. 
01025  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
01026  */
01027 static int is_valid_dtmf(const char *key)
01028 {
01029    int i;
01030    char *local_key = ast_strdupa(key);
01031 
01032    for (i = 0; i < strlen(key); ++i) {
01033       if (!strchr(VALID_DTMF, *local_key)) {
01034          ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
01035          return 0;
01036       }
01037       local_key++;
01038    }
01039    return 1;
01040 }
01041 
01042 /*!
01043  * \brief Finds a voicemail user from the realtime engine.
01044  * \param ivm
01045  * \param context
01046  * \param mailbox
01047  *
01048  * 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.
01049  *
01050  * \return The ast_vm_user structure for the user that was found.
01051  */
01052 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01053 {
01054    struct ast_variable *var;
01055    struct ast_vm_user *retval;
01056 
01057    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
01058       if (!ivm)
01059          ast_set_flag(retval, VM_ALLOCED);   
01060       else
01061          memset(retval, 0, sizeof(*retval));
01062       if (mailbox) 
01063          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
01064       populate_defaults(retval);
01065       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
01066          var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
01067       else
01068          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
01069       if (var) {
01070          apply_options_full(retval, var);
01071          ast_variables_destroy(var);
01072       } else { 
01073          if (!ivm) 
01074             ast_free(retval);
01075          retval = NULL;
01076       }  
01077    } 
01078    return retval;
01079 }
01080 
01081 /*!
01082  * \brief Finds a voicemail user from the users file or the realtime engine.
01083  * \param ivm
01084  * \param context
01085  * \param mailbox
01086  * 
01087  * \return The ast_vm_user structure for the user that was found.
01088  */
01089 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
01090 {
01091    /* This function could be made to generate one from a database, too */
01092    struct ast_vm_user *vmu=NULL, *cur;
01093    AST_LIST_LOCK(&users);
01094 
01095    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
01096       context = "default";
01097 
01098    AST_LIST_TRAVERSE(&users, cur, list) {
01099       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
01100          break;
01101       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
01102          break;
01103    }
01104    if (cur) {
01105       /* Make a copy, so that on a reload, we have no race */
01106       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
01107          memcpy(vmu, cur, sizeof(*vmu));
01108          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
01109          AST_LIST_NEXT(vmu, list) = NULL;
01110       }
01111    } else
01112       vmu = find_user_realtime(ivm, context, mailbox);
01113    AST_LIST_UNLOCK(&users);
01114    return vmu;
01115 }
01116 
01117 /*!
01118  * \brief Resets a user password to a specified password.
01119  * \param context
01120  * \param mailbox
01121  * \param newpass
01122  *
01123  * This does the actual change password work, called by the vm_change_password() function.
01124  *
01125  * \return zero on success, -1 on error.
01126  */
01127 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
01128 {
01129    /* This function could be made to generate one from a database, too */
01130    struct ast_vm_user *cur;
01131    int res = -1;
01132    AST_LIST_LOCK(&users);
01133    AST_LIST_TRAVERSE(&users, cur, list) {
01134       if ((!context || !strcasecmp(context, cur->context)) &&
01135          (!strcasecmp(mailbox, cur->mailbox)))
01136             break;
01137    }
01138    if (cur) {
01139       ast_copy_string(cur->password, newpass, sizeof(cur->password));
01140       res = 0;
01141    }
01142    AST_LIST_UNLOCK(&users);
01143    return res;
01144 }
01145 
01146 /*! 
01147  * \brief The handler for the change password option.
01148  * \param vmu The voicemail user to work with.
01149  * \param newpassword The new password (that has been gathered from the appropriate prompting).
01150  * 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.
01151  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
01152  */
01153 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
01154 {
01155    struct ast_config   *cfg=NULL;
01156    struct ast_variable *var=NULL;
01157    struct ast_category *cat=NULL;
01158    char *category=NULL, *value=NULL, *new=NULL;
01159    const char *tmp=NULL;
01160    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
01161    if (!change_password_realtime(vmu, newpassword))
01162       return;
01163 
01164    /* check voicemail.conf */
01165    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
01166       while ((category = ast_category_browse(cfg, category))) {
01167          if (!strcasecmp(category, vmu->context)) {
01168             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
01169                ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
01170                break;
01171             }
01172             value = strstr(tmp,",");
01173             if (!value) {
01174                ast_log(AST_LOG_WARNING, "variable has bad format.\n");
01175                break;
01176             }
01177             new = alloca((strlen(value)+strlen(newpassword)+1));
01178             sprintf(new,"%s%s", newpassword, value);
01179             if (!(cat = ast_category_get(cfg, category))) {
01180                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
01181                break;
01182             }
01183             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
01184          }
01185       }
01186       /* save the results */
01187       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01188       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01189       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01190    }
01191    category = NULL;
01192    var = NULL;
01193    /* check users.conf and update the password stored for the mailbox*/
01194    /* if no vmsecret entry exists create one. */
01195    if ((cfg = ast_config_load("users.conf", config_flags))) {
01196       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01197       while ((category = ast_category_browse(cfg, category))) {
01198          ast_debug(4, "users.conf: %s\n", category);
01199          if (!strcasecmp(category, vmu->mailbox)) {
01200             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01201                ast_debug(3, "looks like we need to make vmsecret!\n");
01202                var = ast_variable_new("vmsecret", newpassword, "");
01203             } 
01204             new = alloca(strlen(newpassword)+1);
01205             sprintf(new, "%s", newpassword);
01206             if (!(cat = ast_category_get(cfg, category))) {
01207                ast_debug(4, "failed to get category!\n");
01208                break;
01209             }
01210             if (!var)      
01211                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01212             else
01213                ast_variable_append(cat, var);
01214          }
01215       }
01216       /* save the results and clean things up */
01217       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01218       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01219       config_text_file_save("users.conf", cfg, "AppVoicemail");
01220    }
01221 }
01222 
01223 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01224 {
01225    char buf[255];
01226    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
01227    if (!ast_safe_system(buf)) {
01228       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01229       /* Reset the password in memory, too */
01230       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01231    }
01232 }
01233 
01234 /*! 
01235  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01236  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01237  * \param len The length of the path string that was written out.
01238  * 
01239  * The path is constructed as 
01240  *    VM_SPOOL_DIRcontext/ext/folder
01241  *
01242  * \return zero on success, -1 on error.
01243  */
01244 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01245 {
01246    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01247 }
01248 
01249 /*! 
01250  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01251  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01252  * \param len The length of the path string that was written out.
01253  * 
01254  * The path is constructed as 
01255  *    VM_SPOOL_DIRcontext/ext/folder
01256  *
01257  * \return zero on success, -1 on error.
01258  */
01259 static int make_file(char *dest, const int len, const char *dir, const int num)
01260 {
01261    return snprintf(dest, len, "%s/msg%04d", dir, num);
01262 }
01263 
01264 /* same as mkstemp, but return a FILE * */
01265 static FILE *vm_mkftemp(char *template)
01266 {
01267    FILE *p = NULL;
01268    int pfd = mkstemp(template);
01269    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01270    if (pfd > -1) {
01271       p = fdopen(pfd, "w+");
01272       if (!p) {
01273          close(pfd);
01274          pfd = -1;
01275       }
01276    }
01277    return p;
01278 }
01279 
01280 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01281  * \param dest    String. base directory.
01282  * \param len     Length of dest.
01283  * \param context String. Ignored if is null or empty string.
01284  * \param ext     String. Ignored if is null or empty string.
01285  * \param folder  String. Ignored if is null or empty string. 
01286  * \return -1 on failure, 0 on success.
01287  */
01288 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01289 {
01290    mode_t   mode = VOICEMAIL_DIR_MODE;
01291    int res;
01292 
01293    make_dir(dest, len, context, ext, folder);
01294    if ((res = ast_mkdir(dest, mode))) {
01295       ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01296       return -1;
01297    }
01298    return 0;
01299 }
01300 
01301 static const char *mbox(int id)
01302 {
01303    static const char *msgs[] = {
01304 #ifdef IMAP_STORAGE
01305       imapfolder,
01306 #else
01307       "INBOX",
01308 #endif
01309       "Old",
01310       "Work",
01311       "Family",
01312       "Friends",
01313       "Cust1",
01314       "Cust2",
01315       "Cust3",
01316       "Cust4",
01317       "Cust5",
01318       "Deleted",
01319       "Urgent"
01320    };
01321    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
01322 }
01323 
01324 static void free_user(struct ast_vm_user *vmu)
01325 {
01326    if (ast_test_flag(vmu, VM_ALLOCED))
01327       ast_free(vmu);
01328 }
01329 
01330 /* All IMAP-specific functions should go in this block. This
01331  * keeps them from being spread out all over the code */
01332 #ifdef IMAP_STORAGE
01333 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
01334 {
01335    char arg[10];
01336    struct vm_state *vms;
01337    unsigned long messageNum;
01338 
01339    /* Greetings aren't stored in IMAP, so we can't delete them there */
01340    if (msgnum < 0) {
01341       return;
01342    }
01343 
01344    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01345       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);
01346       return;
01347    }
01348 
01349    /* find real message number based on msgnum */
01350    /* this may be an index into vms->msgArray based on the msgnum. */
01351    messageNum = vms->msgArray[msgnum];
01352    if (messageNum == 0) {
01353       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01354       return;
01355    }
01356    if (option_debug > 2)
01357       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01358    /* delete message */
01359    snprintf (arg, sizeof(arg), "%lu",messageNum);
01360    ast_mutex_lock(&vms->lock);
01361    mail_setflag (vms->mailstream,arg,"\\DELETED");
01362    ast_mutex_unlock(&vms->lock);
01363 }
01364 
01365 static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
01366 {
01367    struct vm_state *vms_p;
01368    char *file, *filename;
01369    char *attachment;
01370    int ret = 0, i;
01371    BODY *body;
01372 
01373    /* This function is only used for retrieval of IMAP greetings
01374     * regular messages are not retrieved this way, nor are greetings
01375     * if they are stored locally*/
01376    if (msgnum > -1 || !imapgreetings) {
01377       return 0;
01378    } else {
01379       file = strrchr(ast_strdupa(dir), '/');
01380       if (file)
01381          *file++ = '\0';
01382       else {
01383          ast_debug (1, "Failed to procure file name from directory passed.\n");
01384          return -1;
01385       }
01386    }
01387 
01388    /* check if someone is accessing this box right now... */
01389    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))) {
01390       ast_log(AST_LOG_ERROR, "Voicemail state not found!\n");
01391       return -1;
01392    }
01393    
01394    /* Greetings will never have a prepended message */
01395    *vms_p->introfn = '\0';
01396 
01397    ast_mutex_lock(&vms_p->lock);
01398    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01399    if (!vms_p->mailstream) {
01400       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
01401       ast_mutex_unlock(&vms_p->lock);
01402       return -1;
01403    }
01404 
01405    /*XXX Yuck, this could probably be done a lot better */
01406    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01407       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01408       /* We have the body, now we extract the file name of the first attachment. */
01409       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01410          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01411       } else {
01412          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
01413          ast_mutex_unlock(&vms_p->lock);
01414          return -1;
01415       }
01416       filename = strsep(&attachment, ".");
01417       if (!strcmp(filename, file)) {
01418          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01419          vms_p->msgArray[vms_p->curmsg] = i + 1;
01420          save_body(body, vms_p, "2", attachment, 0);
01421          ast_mutex_unlock(&vms_p->lock);
01422          return 0;
01423       }
01424    }
01425    ast_mutex_unlock(&vms_p->lock);
01426 
01427    return -1;
01428 }
01429 
01430 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01431 {
01432    BODY *body;
01433    char *header_content;
01434    char *attachedfilefmt;
01435    char buf[80];
01436    struct vm_state *vms;
01437    char text_file[PATH_MAX];
01438    FILE *text_file_ptr;
01439    int res = 0;
01440    struct ast_vm_user *vmu;
01441 
01442    if (!(vmu = find_user(NULL, context, mailbox))) {
01443       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01444       return -1;
01445    }
01446    
01447    if (msgnum < 0) {
01448       if (imapgreetings) {
01449          res = imap_retrieve_greeting(dir, msgnum, vmu);
01450          goto exit;
01451       } else {
01452          res = 0;
01453          goto exit;
01454       }
01455    }
01456 
01457    /* Before anything can happen, we need a vm_state so that we can
01458     * actually access the imap server through the vms->mailstream
01459     */
01460    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01461       /* This should not happen. If it does, then I guess we'd
01462        * need to create the vm_state, extract which mailbox to
01463        * open, and then set up the msgArray so that the correct
01464        * IMAP message could be accessed. If I have seen correctly
01465        * though, the vms should be obtainable from the vmstates list
01466        * and should have its msgArray properly set up.
01467        */
01468       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01469       res = -1;
01470       goto exit;
01471    }
01472    
01473    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01474    snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
01475 
01476    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01477    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01478       res = 0;
01479       goto exit;
01480    }
01481 
01482    if (option_debug > 2)
01483       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01484    if (vms->msgArray[msgnum] == 0) {
01485       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01486       res = -1;
01487       goto exit;
01488    }
01489 
01490    /* This will only work for new messages... */
01491    ast_mutex_lock(&vms->lock);
01492    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01493    ast_mutex_unlock(&vms->lock);
01494    /* empty string means no valid header */
01495    if (ast_strlen_zero(header_content)) {
01496       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01497       res = -1;
01498       goto exit;
01499    }
01500 
01501    ast_mutex_lock(&vms->lock);
01502    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01503    ast_mutex_unlock(&vms->lock);
01504 
01505    /* We have the body, now we extract the file name of the first attachment. */
01506    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01507       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01508    } else {
01509       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01510       res = -1;
01511       goto exit;
01512    }
01513    
01514    /* Find the format of the attached file */
01515 
01516    strsep(&attachedfilefmt, ".");
01517    if (!attachedfilefmt) {
01518       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01519       res = -1;
01520       goto exit;
01521    }
01522    
01523    save_body(body, vms, "2", attachedfilefmt, 0);
01524    if (save_body(body, vms, "3", attachedfilefmt, 1)) {
01525       *vms->introfn = '\0';
01526    }
01527 
01528    /* Get info from headers!! */
01529    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01530 
01531    if (!(text_file_ptr = fopen(text_file, "w"))) {
01532       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01533    }
01534 
01535    fprintf(text_file_ptr, "%s\n", "[message]");
01536 
01537    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01538    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01539    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01540    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01541    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01542    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01543    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01544    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01545    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01546    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01547    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01548    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01549    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01550    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01551    fclose(text_file_ptr);
01552 
01553 exit:
01554    free_user(vmu);
01555    return res;
01556 }
01557 
01558 static int folder_int(const char *folder)
01559 {
01560    /*assume a NULL folder means INBOX*/
01561    if (!folder)
01562       return 0;
01563 #ifdef IMAP_STORAGE
01564    if (!strcasecmp(folder, imapfolder))
01565 #else
01566    if (!strcasecmp(folder, "INBOX"))
01567 #endif
01568       return 0;
01569    else if (!strcasecmp(folder, "Old"))
01570       return 1;
01571    else if (!strcasecmp(folder, "Work"))
01572       return 2;
01573    else if (!strcasecmp(folder, "Family"))
01574       return 3;
01575    else if (!strcasecmp(folder, "Friends"))
01576       return 4;
01577    else if (!strcasecmp(folder, "Cust1"))
01578       return 5;
01579    else if (!strcasecmp(folder, "Cust2"))
01580       return 6;
01581    else if (!strcasecmp(folder, "Cust3"))
01582       return 7;
01583    else if (!strcasecmp(folder, "Cust4"))
01584       return 8;
01585    else if (!strcasecmp(folder, "Cust5"))
01586       return 9;
01587    else /*assume they meant INBOX if folder is not found otherwise*/
01588       return 0;
01589 }
01590 
01591 /*!
01592  * \brief Gets the number of messages that exist in a mailbox folder.
01593  * \param context
01594  * \param mailbox
01595  * \param folder
01596  * 
01597  * This method is used when IMAP backend is used.
01598  * \return The number of messages in this mailbox folder (zero or more).
01599  */
01600 static int messagecount(const char *context, const char *mailbox, const char *folder)
01601 {
01602    SEARCHPGM *pgm;
01603    SEARCHHEADER *hdr;
01604 
01605    struct ast_vm_user *vmu, vmus;
01606    struct vm_state *vms_p;
01607    int ret = 0;
01608    int fold = folder_int(folder);
01609    int urgent = 0;
01610    
01611    /* If URGENT, then look at INBOX */
01612    if (fold == 11) {
01613       fold = NEW_FOLDER;
01614       urgent = 1;
01615    }
01616 
01617    if (ast_strlen_zero(mailbox))
01618       return 0;
01619 
01620    /* We have to get the user before we can open the stream! */
01621    vmu = find_user(&vmus, context, mailbox);
01622    if (!vmu) {
01623       ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01624       return -1;
01625    } else {
01626       /* No IMAP account available */
01627       if (vmu->imapuser[0] == '\0') {
01628          ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01629          return -1;
01630       }
01631    }
01632    
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       free_user(vmu);
01637       return -1;
01638    }
01639 
01640    /* check if someone is accessing this box right now... */
01641    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01642    if (!vms_p) {
01643       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01644    }
01645    if (vms_p) {
01646       ast_debug(3, "Returning before search - user is logged in\n");
01647       if (fold == 0) { /* INBOX */
01648          return vms_p->newmessages;
01649       }
01650       if (fold == 1) { /* Old messages */
01651          return vms_p->oldmessages;
01652       }
01653       if (fold == 11) {/*Urgent messages*/
01654          return vms_p->urgentmessages;
01655       }
01656    }
01657 
01658    /* add one if not there... */
01659    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01660    if (!vms_p) {
01661       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01662    }
01663 
01664    if (!vms_p) {
01665       vms_p = create_vm_state_from_user(vmu);
01666    }
01667    ret = init_mailstream(vms_p, fold);
01668    if (!vms_p->mailstream) {
01669       ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01670       return -1;
01671    }
01672    if (ret == 0) {
01673       ast_mutex_lock(&vms_p->lock);
01674       pgm = mail_newsearchpgm ();
01675       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01676       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01677       pgm->header = hdr;
01678       if (fold != 1) {
01679          pgm->unseen = 1;
01680          pgm->seen = 0;
01681       }
01682       /* In the special case where fold is 1 (old messages) we have to do things a bit
01683        * differently. Old messages are stored in the INBOX but are marked as "seen"
01684        */
01685       else {
01686          pgm->unseen = 0;
01687          pgm->seen = 1;
01688       }
01689       /* look for urgent messages */
01690       if (urgent) {
01691          pgm->flagged = 1;
01692          pgm->unflagged = 0;
01693       }
01694       pgm->undeleted = 1;
01695       pgm->deleted = 0;
01696 
01697       vms_p->vmArrayIndex = 0;
01698       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01699       if (fold == 0 && urgent == 0)
01700          vms_p->newmessages = vms_p->vmArrayIndex;
01701       if (fold == 1)
01702          vms_p->oldmessages = vms_p->vmArrayIndex;
01703       if (fold == 0 && urgent == 1)
01704          vms_p->urgentmessages = vms_p->vmArrayIndex;
01705       /*Freeing the searchpgm also frees the searchhdr*/
01706       mail_free_searchpgm(&pgm);
01707       ast_mutex_unlock(&vms_p->lock);
01708       vms_p->updated = 0;
01709       return vms_p->vmArrayIndex;
01710    } else {
01711       ast_mutex_lock(&vms_p->lock);
01712       mail_ping(vms_p->mailstream);
01713       ast_mutex_unlock(&vms_p->lock);
01714    }
01715    return 0;
01716 }
01717 
01718 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)
01719 {
01720    char *myserveremail = serveremail;
01721    char fn[PATH_MAX];
01722    char introfn[PATH_MAX];
01723    char mailbox[256];
01724    char *stringp;
01725    FILE *p=NULL;
01726    char tmp[80] = "/tmp/astmail-XXXXXX";
01727    long len;
01728    void *buf;
01729    int tempcopy = 0;
01730    STRING str;
01731    int ret; /* for better error checking */
01732    char *imap_flags = NIL;
01733 
01734    /* Set urgent flag for IMAP message */
01735    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
01736       ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
01737       imap_flags="\\FLAGGED";
01738    }
01739    
01740    /* Attach only the first format */
01741    fmt = ast_strdupa(fmt);
01742    stringp = fmt;
01743    strsep(&stringp, "|");
01744 
01745    if (!ast_strlen_zero(vmu->serveremail))
01746       myserveremail = vmu->serveremail;
01747 
01748    if (msgnum > -1)
01749       make_file(fn, sizeof(fn), dir, msgnum);
01750    else
01751       ast_copy_string (fn, dir, sizeof(fn));
01752 
01753    snprintf(introfn, sizeof(introfn), "%sintro", fn);
01754    if (ast_fileexists(introfn, NULL, NULL) <= 0) {
01755       *introfn = '\0';
01756    }
01757    
01758    if (ast_strlen_zero(vmu->email)) {
01759       /* We need the vmu->email to be set when we call make_email_file, but
01760        * if we keep it set, a duplicate e-mail will be created. So at the end
01761        * of this function, we will revert back to an empty string if tempcopy
01762        * is 1.
01763        */
01764       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01765       tempcopy = 1;
01766    }
01767 
01768    if (!strcmp(fmt, "wav49"))
01769       fmt = "WAV";
01770    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
01771 
01772    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01773       command hangs. */
01774    if (!(p = vm_mkftemp(tmp))) {
01775       ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01776       if (tempcopy)
01777          *(vmu->email) = '\0';
01778       return -1;
01779    }
01780 
01781    if (msgnum < 0 && imapgreetings) {
01782       if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
01783          ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
01784          return -1;
01785       }
01786       imap_delete_old_greeting(fn, vms);
01787    }
01788 
01789    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);
01790    /* read mail file to memory */
01791    len = ftell(p);
01792    rewind(p);
01793    if (!(buf = ast_malloc(len + 1))) {
01794       ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
01795       fclose(p);
01796       if (tempcopy)
01797          *(vmu->email) = '\0';
01798       return -1;
01799    }
01800    if (fread(buf, len, 1, p) < len) {
01801       if (ferror(p)) {
01802          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
01803          return -1;
01804       }
01805    }
01806    ((char *)buf)[len] = '\0';
01807    INIT(&str, mail_string, buf, len);
01808    ret = init_mailstream(vms, NEW_FOLDER);
01809    if (ret == 0) {
01810       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
01811       ast_mutex_lock(&vms->lock);
01812       if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
01813          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01814       ast_mutex_unlock(&vms->lock);
01815       fclose(p);
01816       unlink(tmp);
01817       ast_free(buf);
01818    } else {
01819       ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
01820       fclose(p);
01821       unlink(tmp);
01822       ast_free(buf);
01823       return -1;
01824    }
01825    ast_debug(3, "%s stored\n", fn);
01826    
01827    if (tempcopy)
01828       *(vmu->email) = '\0';
01829    
01830    return 0;
01831 
01832 }
01833 
01834 /*!
01835  * \brief Gets the number of messages that exist in the inbox folder.
01836  * \param mailbox_context
01837  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
01838  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
01839  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
01840  * 
01841  * This method is used when IMAP backend is used.
01842  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
01843  *
01844  * \return zero on success, -1 on error.
01845  */
01846 
01847 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
01848 {
01849    char tmp[PATH_MAX] = "";
01850    char *mailboxnc;
01851    char *context;
01852    char *mb;
01853    char *cur;
01854    if (newmsgs)
01855       *newmsgs = 0;
01856    if (oldmsgs)
01857       *oldmsgs = 0;
01858    if (urgentmsgs)
01859       *urgentmsgs = 0;
01860 
01861    ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
01862    /* If no mailbox, return immediately */
01863    if (ast_strlen_zero(mailbox_context))
01864       return 0;
01865    
01866    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01867    context = strchr(tmp, '@');
01868    if (strchr(mailbox_context, ',')) {
01869       int tmpnew, tmpold, tmpurgent;
01870       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01871       mb = tmp;
01872       while ((cur = strsep(&mb, ", "))) {
01873          if (!ast_strlen_zero(cur)) {
01874             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01875                return -1;
01876             else {
01877                if (newmsgs)
01878                   *newmsgs += tmpnew; 
01879                if (oldmsgs)
01880                   *oldmsgs += tmpold;
01881                if (urgentmsgs)
01882                   *urgentmsgs += tmpurgent;
01883             }
01884          }
01885       }
01886       return 0;
01887    }
01888    if (context) {
01889       *context = '\0';
01890       mailboxnc = tmp;
01891       context++;
01892    } else {
01893       context = "default";
01894       mailboxnc = (char *)mailbox_context;
01895    }
01896    if (newmsgs) {
01897       if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
01898          return -1;
01899    }
01900    if (oldmsgs) {
01901       if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
01902          return -1;
01903    }
01904    if (urgentmsgs) {
01905       if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
01906          return -1;
01907    }
01908    return 0;
01909 }
01910 
01911 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
01912 {
01913    return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
01914 }
01915 
01916 /** 
01917  * \brief Determines if the given folder has messages.
01918  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
01919  * \param folder the folder to look in
01920  *
01921  * This function is used when the mailbox is stored in an IMAP back end.
01922  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
01923  * \return 1 if the folder has one or more messages. zero otherwise.
01924  */
01925 
01926 static int has_voicemail(const char *mailbox, const char *folder)
01927 {
01928    char tmp[256], *tmp2, *box, *context;
01929    ast_copy_string(tmp, mailbox, sizeof(tmp));
01930    tmp2 = tmp;
01931    if (strchr(tmp2, ',')) {
01932       while ((box = strsep(&tmp2, ","))) {
01933          if (!ast_strlen_zero(box)) {
01934             if (has_voicemail(box, folder))
01935                return 1;
01936          }
01937       }
01938    }
01939    if ((context= strchr(tmp, '@')))
01940       *context++ = '\0';
01941    else
01942       context = "default";
01943    return messagecount(context, tmp, folder) ? 1 : 0;
01944 }
01945 
01946 /*!
01947  * \brief Copies a message from one mailbox to another.
01948  * \param chan
01949  * \param vmu
01950  * \param imbox
01951  * \param msgnum
01952  * \param duration
01953  * \param recip
01954  * \param fmt
01955  * \param dir
01956  *
01957  * This works with IMAP storage based mailboxes.
01958  *
01959  * \return zero on success, -1 on error.
01960  */
01961 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)
01962 {
01963    struct vm_state *sendvms = NULL, *destvms = NULL;
01964    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
01965    if (msgnum >= recip->maxmsg) {
01966       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
01967       return -1;
01968    }
01969    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
01970       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
01971       return -1;
01972    }
01973    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
01974       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
01975       return -1;
01976    }
01977    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
01978    ast_mutex_lock(&sendvms->lock);
01979    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
01980       ast_mutex_unlock(&sendvms->lock);
01981       return 0;
01982    }
01983    ast_mutex_unlock(&sendvms->lock);
01984    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
01985    return -1;
01986 }
01987 
01988 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
01989 {
01990    char tmp[256], *t = tmp;
01991    size_t left = sizeof(tmp);
01992    
01993    if (box == OLD_FOLDER) {
01994       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
01995    } else {
01996       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
01997    }
01998 
01999    if (box == NEW_FOLDER) {
02000       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
02001    } else {
02002       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
02003    }
02004 
02005    /* Build up server information */
02006    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
02007 
02008    /* Add authentication user if present */
02009    if (!ast_strlen_zero(authuser))
02010       ast_build_string(&t, &left, "/authuser=%s", authuser);
02011 
02012    /* Add flags if present */
02013    if (!ast_strlen_zero(imapflags))
02014       ast_build_string(&t, &left, "/%s", imapflags);
02015 
02016    /* End with username */
02017    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
02018    if (box == NEW_FOLDER || box == OLD_FOLDER)
02019       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
02020    else if (box == GREETINGS_FOLDER)
02021       snprintf(spec, len, "%s%s", tmp, greetingfolder);
02022    else {   /* Other folders such as Friends, Family, etc... */
02023       if (!ast_strlen_zero(imapparentfolder)) {
02024          /* imapparentfolder would typically be set to INBOX */
02025          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
02026       } else {
02027          snprintf(spec, len, "%s%s", tmp, mbox(box));
02028       }
02029    }
02030 }
02031 
02032 static int init_mailstream(struct vm_state *vms, int box)
02033 {
02034    MAILSTREAM *stream = NIL;
02035    long debug;
02036    char tmp[256];
02037    
02038    if (!vms) {
02039       ast_log (LOG_ERROR,"vm_state is NULL!\n");
02040       return -1;
02041    }
02042    if (option_debug > 2)
02043       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
02044    if (vms->mailstream == NIL || !vms->mailstream) {
02045       if (option_debug)
02046          ast_log (LOG_DEBUG,"mailstream not set.\n");
02047    } else {
02048       stream = vms->mailstream;
02049    }
02050    /* debug = T;  user wants protocol telemetry? */
02051    debug = NIL;  /* NO protocol telemetry? */
02052 
02053    if (delimiter == '\0') {      /* did not probe the server yet */
02054       char *cp;
02055 #ifdef USE_SYSTEM_IMAP
02056 #include <imap/linkage.c>
02057 #elif defined(USE_SYSTEM_CCLIENT)
02058 #include <c-client/linkage.c>
02059 #else
02060 #include "linkage.c"
02061 #endif
02062       /* Connect to INBOX first to get folders delimiter */
02063       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
02064       ast_mutex_lock(&vms->lock);
02065       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02066       ast_mutex_unlock(&vms->lock);
02067       if (stream == NIL) {
02068          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
02069          return -1;
02070       }
02071       get_mailbox_delimiter(stream);
02072       /* update delimiter in imapfolder */
02073       for (cp = imapfolder; *cp; cp++)
02074          if (*cp == '/')
02075             *cp = delimiter;
02076    }
02077    /* Now connect to the target folder */
02078    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
02079    if (option_debug > 2)
02080       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
02081    ast_mutex_lock(&vms->lock);
02082    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
02083    ast_mutex_unlock(&vms->lock);
02084    if (vms->mailstream == NIL) {
02085       return -1;
02086    } else {
02087       return 0;
02088    }
02089 }
02090 
02091 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
02092 {
02093    SEARCHPGM *pgm;
02094    SEARCHHEADER *hdr;
02095    int ret, urgent = 0;
02096 
02097    /* If Urgent, then look at INBOX */
02098    if (box == 11) {
02099       box = NEW_FOLDER;
02100       urgent = 1;
02101    }
02102 
02103    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
02104    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
02105 
02106    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
02107       ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
02108       return -1;
02109    }
02110    
02111    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
02112    
02113    /* Check Quota */
02114    if  (box == 0)  {
02115       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
02116       check_quota(vms,(char *)mbox(box));
02117    }
02118 
02119    ast_mutex_lock(&vms->lock);
02120    pgm = mail_newsearchpgm();
02121 
02122    /* Check IMAP folder for Asterisk messages only... */
02123    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
02124    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
02125    pgm->header = hdr;
02126    pgm->deleted = 0;
02127    pgm->undeleted = 1;
02128 
02129    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
02130    if (box == NEW_FOLDER && urgent == 1) {
02131       pgm->unseen = 1;
02132       pgm->seen = 0;
02133       pgm->flagged = 1;
02134       pgm->unflagged = 0;
02135    } else if (box == NEW_FOLDER && urgent == 0) {
02136       pgm->unseen = 1;
02137       pgm->seen = 0;
02138       pgm->flagged = 0;
02139       pgm->unflagged = 1;
02140    } else if (box == OLD_FOLDER) {
02141       pgm->seen = 1;
02142       pgm->unseen = 0;
02143    }
02144 
02145    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
02146 
02147    vms->vmArrayIndex = 0;
02148    mail_search_full (vms->mailstream, NULL, pgm, NIL);
02149    vms->lastmsg = vms->vmArrayIndex - 1;
02150    mail_free_searchpgm(&pgm);
02151 
02152    ast_mutex_unlock(&vms->lock);
02153    return 0;
02154 }
02155 
02156 static void write_file(char *filename, char *buffer, unsigned long len)
02157 {
02158    FILE *output;
02159 
02160    output = fopen (filename, "w");
02161    if (fwrite(buffer, len, 1, output) != 1) {
02162       if (ferror(output)) {
02163          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
02164       }
02165    }
02166    fclose (output);
02167 }
02168 
02169 static void update_messages_by_imapuser(const char *user, unsigned long number)
02170 {
02171    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
02172 
02173    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
02174       return;
02175    }
02176 
02177    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
02178    vms->msgArray[vms->vmArrayIndex++] = number;
02179 }
02180 
02181 void mm_searched(MAILSTREAM *stream, unsigned long number)
02182 {
02183    char *mailbox = stream->mailbox, buf[1024] = "", *user;
02184 
02185    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
02186       return;
02187 
02188    update_messages_by_imapuser(user, number);
02189 }
02190 
02191 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
02192 {
02193    struct ast_variable *var;
02194    struct ast_vm_user *vmu;
02195 
02196    vmu = ast_calloc(1, sizeof *vmu);
02197    if (!vmu)
02198       return NULL;
02199    ast_set_flag(vmu, VM_ALLOCED);
02200    populate_defaults(vmu);
02201 
02202    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
02203    if (var) {
02204       apply_options_full(vmu, var);
02205       ast_variables_destroy(var);
02206       return vmu;
02207    } else {
02208       free(vmu);
02209       return NULL;
02210    }
02211 }
02212 
02213 /* Interfaces to C-client */
02214 
02215 void mm_exists(MAILSTREAM * stream, unsigned long number)
02216 {
02217    /* mail_ping will callback here if new mail! */
02218    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
02219    if (number == 0) return;
02220    set_update(stream);
02221 }
02222 
02223 
02224 void mm_expunged(MAILSTREAM * stream, unsigned long number)
02225 {
02226    /* mail_ping will callback here if expunged mail! */
02227    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
02228    if (number == 0) return;
02229    set_update(stream);
02230 }
02231 
02232 
02233 void mm_flags(MAILSTREAM * stream, unsigned long number)
02234 {
02235    /* mail_ping will callback here if read mail! */
02236    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
02237    if (number == 0) return;
02238    set_update(stream);
02239 }
02240 
02241 
02242 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
02243 {
02244    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
02245    mm_log (string, errflg);
02246 }
02247 
02248 
02249 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02250 {
02251    if (delimiter == '\0') {
02252       delimiter = delim;
02253    }
02254 
02255    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02256    if (attributes & LATT_NOINFERIORS)
02257       ast_debug(5, "no inferiors\n");
02258    if (attributes & LATT_NOSELECT)
02259       ast_debug(5, "no select\n");
02260    if (attributes & LATT_MARKED)
02261       ast_debug(5, "marked\n");
02262    if (attributes & LATT_UNMARKED)
02263       ast_debug(5, "unmarked\n");
02264 }
02265 
02266 
02267 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
02268 {
02269    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
02270    if (attributes & LATT_NOINFERIORS)
02271       ast_debug(5, "no inferiors\n");
02272    if (attributes & LATT_NOSELECT)
02273       ast_debug(5, "no select\n");
02274    if (attributes & LATT_MARKED)
02275       ast_debug(5, "marked\n");
02276    if (attributes & LATT_UNMARKED)
02277       ast_debug(5, "unmarked\n");
02278 }
02279 
02280 
02281 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
02282 {
02283    ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
02284    if (status->flags & SA_MESSAGES)
02285       ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
02286    if (status->flags & SA_RECENT)
02287       ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
02288    if (status->flags & SA_UNSEEN)
02289       ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
02290    if (status->flags & SA_UIDVALIDITY)
02291       ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02292    if (status->flags & SA_UIDNEXT)
02293       ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
02294    ast_log(AST_LOG_NOTICE, "\n");
02295 }
02296 
02297 
02298 void mm_log(char *string, long errflg)
02299 {
02300    switch ((short) errflg) {
02301       case NIL:
02302          ast_debug(1,"IMAP Info: %s\n", string);
02303          break;
02304       case PARSE:
02305       case WARN:
02306          ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
02307          break;
02308       case ERROR:
02309          ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
02310          break;
02311    }
02312 }
02313 
02314 
02315 void mm_dlog(char *string)
02316 {
02317    ast_log(AST_LOG_NOTICE, "%s\n", string);
02318 }
02319 
02320 
02321 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02322 {
02323    struct ast_vm_user *vmu;
02324 
02325    ast_debug(4, "Entering callback mm_login\n");
02326 
02327    ast_copy_string(user, mb->user, MAILTMPLEN);
02328 
02329    /* We should only do this when necessary */
02330    if (!ast_strlen_zero(authpassword)) {
02331       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02332    } else {
02333       AST_LIST_TRAVERSE(&users, vmu, list) {
02334          if (!strcasecmp(mb->user, vmu->imapuser)) {
02335             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02336             break;
02337          }
02338       }
02339       if (!vmu) {
02340          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02341             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02342             free_user(vmu);
02343          }
02344       }
02345    }
02346 }
02347 
02348 
02349 void mm_critical(MAILSTREAM * stream)
02350 {
02351 }
02352 
02353 
02354 void mm_nocritical(MAILSTREAM * stream)
02355 {
02356 }
02357 
02358 
02359 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02360 {
02361    kill (getpid (), SIGSTOP);
02362    return NIL;
02363 }
02364 
02365 
02366 void mm_fatal(char *string)
02367 {
02368    ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02369 }
02370 
02371 /* C-client callback to handle quota */
02372 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02373 {
02374    struct vm_state *vms;
02375    char *mailbox = stream->mailbox, *user;
02376    char buf[1024] = "";
02377    unsigned long usage = 0, limit = 0;
02378    
02379    while (pquota) {
02380       usage = pquota->usage;
02381       limit = pquota->limit;
02382       pquota = pquota->next;
02383    }
02384    
02385    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)))) {
02386       ast_log(AST_LOG_ERROR, "No state found.\n");
02387       return;
02388    }
02389 
02390    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02391 
02392    vms->quota_usage = usage;
02393    vms->quota_limit = limit;
02394 }
02395 
02396 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02397 {
02398    char *start, *eol_pnt;
02399    int taglen;
02400 
02401    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02402       return NULL;
02403 
02404    taglen = strlen(tag) + 1;
02405    if (taglen < 1)
02406       return NULL;
02407 
02408    if (!(start = strstr(header, tag)))
02409       return NULL;
02410 
02411    /* Since we can be called multiple times we should clear our buffer */
02412    memset(buf, 0, len);
02413 
02414    ast_copy_string(buf, start+taglen, len);
02415    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02416       *eol_pnt = '\0';
02417    return buf;
02418 }
02419 
02420 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02421 {
02422    char *start, *quote, *eol_pnt;
02423 
02424    if (ast_strlen_zero(mailbox))
02425       return NULL;
02426 
02427    if (!(start = strstr(mailbox, "/user=")))
02428       return NULL;
02429 
02430    ast_copy_string(buf, start+6, len);
02431 
02432    if (!(quote = strchr(buf, '\"'))) {
02433       if (!(eol_pnt = strchr(buf, '/')))
02434          eol_pnt = strchr(buf,'}');
02435       *eol_pnt = '\0';
02436       return buf;
02437    } else {
02438       eol_pnt = strchr(buf+1,'\"');
02439       *eol_pnt = '\0';
02440       return buf+1;
02441    }
02442 }
02443 
02444 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02445 {
02446    struct vm_state *vms_p;
02447 
02448    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02449       return vms_p;
02450    }
02451    if (option_debug > 4)
02452       ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02453    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02454       return NULL;
02455    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02456    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02457    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02458    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02459    if (option_debug > 4)
02460       ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02461    vms_p->updated = 1;
02462    /* set mailbox to INBOX! */
02463    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02464    init_vm_state(vms_p);
02465    vmstate_insert(vms_p);
02466    return vms_p;
02467 }
02468 
02469 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02470 {
02471    struct vmstate *vlist = NULL;
02472 
02473    if (interactive) {
02474       struct vm_state *vms;
02475       vms = pthread_getspecific(ts_vmstate.key);
02476       return vms;
02477    }
02478 
02479    AST_LIST_LOCK(&vmstates);
02480    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02481       if (!vlist->vms) {
02482          ast_debug(3, "error: vms is NULL for %s\n", user);
02483          continue;
02484       }
02485       if (!vlist->vms->imapuser) {
02486          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02487          continue;
02488       }
02489 
02490       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02491          AST_LIST_UNLOCK(&vmstates);
02492          return vlist->vms;
02493       }
02494    }
02495    AST_LIST_UNLOCK(&vmstates);
02496 
02497    ast_debug(3, "%s not found in vmstates\n", user);
02498 
02499    return NULL;
02500 }
02501 
02502 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02503 {
02504 
02505    struct vmstate *vlist = NULL;
02506    const char *local_context = S_OR(context, "default");
02507 
02508    if (interactive) {
02509       struct vm_state *vms;
02510       vms = pthread_getspecific(ts_vmstate.key);
02511       return vms;
02512    }
02513 
02514    AST_LIST_LOCK(&vmstates);
02515    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02516       if (!vlist->vms) {
02517          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02518          continue;
02519       }
02520       if (!vlist->vms->username || !vlist->vms->context) {
02521          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02522          continue;
02523       }
02524 
02525       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);
02526       
02527       if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
02528          ast_debug(3, "Found it!\n");
02529          AST_LIST_UNLOCK(&vmstates);
02530          return vlist->vms;
02531       }
02532    }
02533    AST_LIST_UNLOCK(&vmstates);
02534 
02535    ast_debug(3, "%s not found in vmstates\n", mailbox);
02536 
02537    return NULL;
02538 }
02539 
02540 static void vmstate_insert(struct vm_state *vms) 
02541 {
02542    struct vmstate *v;
02543    struct vm_state *altvms;
02544 
02545    /* If interactive, it probably already exists, and we should
02546       use the one we already have since it is more up to date.
02547       We can compare the username to find the duplicate */
02548    if (vms->interactive == 1) {
02549       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02550       if (altvms) {  
02551          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02552          vms->newmessages = altvms->newmessages;
02553          vms->oldmessages = altvms->oldmessages;
02554          vms->vmArrayIndex = altvms->vmArrayIndex;
02555          vms->lastmsg = altvms->lastmsg;
02556          vms->curmsg = altvms->curmsg;
02557          /* get a pointer to the persistent store */
02558          vms->persist_vms = altvms;
02559          /* Reuse the mailstream? */
02560 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02561          vms->mailstream = altvms->mailstream;
02562 #else
02563          vms->mailstream = NIL;
02564 #endif
02565       }
02566       return;
02567    }
02568 
02569    if (!(v = ast_calloc(1, sizeof(*v))))
02570       return;
02571    
02572    v->vms = vms;
02573 
02574    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02575 
02576    AST_LIST_LOCK(&vmstates);
02577    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02578    AST_LIST_UNLOCK(&vmstates);
02579 }
02580 
02581 static void vmstate_delete(struct vm_state *vms) 
02582 {
02583    struct vmstate *vc = NULL;
02584    struct vm_state *altvms = NULL;
02585 
02586    /* If interactive, we should copy pertinent info
02587       back to the persistent state (to make update immediate) */
02588    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02589       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02590       altvms->newmessages = vms->newmessages;
02591       altvms->oldmessages = vms->oldmessages;
02592       altvms->updated = 1;
02593       vms->mailstream = mail_close(vms->mailstream);
02594 
02595       /* Interactive states are not stored within the persistent list */
02596       return;
02597    }
02598    
02599    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02600    
02601    AST_LIST_LOCK(&vmstates);
02602    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02603       if (vc->vms == vms) {
02604          AST_LIST_REMOVE_CURRENT(list);
02605          break;
02606       }
02607    }
02608    AST_LIST_TRAVERSE_SAFE_END
02609    AST_LIST_UNLOCK(&vmstates);
02610    
02611    if (vc) {
02612       ast_mutex_destroy(&vc->vms->lock);
02613       ast_free(vc);
02614    }
02615    else
02616       ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02617 }
02618 
02619 static void set_update(MAILSTREAM * stream) 
02620 {
02621    struct vm_state *vms;
02622    char *mailbox = stream->mailbox, *user;
02623    char buf[1024] = "";
02624 
02625    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02626       if (user && option_debug > 2)
02627          ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
02628       return;
02629    }
02630 
02631    ast_debug(3, "User %s mailbox set for update.\n", user);
02632 
02633    vms->updated = 1; /* Set updated flag since mailbox changed */
02634 }
02635 
02636 static void init_vm_state(struct vm_state *vms) 
02637 {
02638    int x;
02639    vms->vmArrayIndex = 0;
02640    for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
02641       vms->msgArray[x] = 0;
02642    }
02643    ast_mutex_init(&vms->lock);
02644 }
02645 
02646 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
02647 {
02648    char *body_content;
02649    char *body_decoded;
02650    char *fn = is_intro ? vms->introfn : vms->fn;
02651    unsigned long len;
02652    unsigned long newlen;
02653    char filename[256];
02654    
02655    if (!body || body == NIL)
02656       return -1;
02657 
02658    ast_mutex_lock(&vms->lock);
02659    body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02660    ast_mutex_unlock(&vms->lock);
02661    if (body_content != NIL) {
02662       snprintf(filename, sizeof(filename), "%s.%s", fn, format);
02663       /* ast_debug(1,body_content); */
02664       body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
02665       /* If the body of the file is empty, return an error */
02666       if (!newlen) {
02667          return -1;
02668       }
02669       write_file(filename, (char *) body_decoded, newlen);
02670    } else {
02671       ast_debug(5, "Body of message is NULL.\n");
02672       return -1;
02673    }
02674    return 0;
02675 }
02676 
02677 /*! 
02678  * \brief Get delimiter via mm_list callback 
02679  * \param stream
02680  *
02681  * Determines the delimiter character that is used by the underlying IMAP based mail store.
02682  */
02683 /* MUTEX should already be held */
02684 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02685    char tmp[50];
02686    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02687    mail_list(stream, tmp, "*");
02688 }
02689 
02690 /*! 
02691  * \brief Check Quota for user 
02692  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02693  * \param mailbox the mailbox to check the quota for.
02694  *
02695  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02696  */
02697 static void check_quota(struct vm_state *vms, char *mailbox) {
02698    ast_mutex_lock(&vms->lock);
02699    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02700    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02701    if (vms && vms->mailstream != NULL) {
02702       imap_getquotaroot(vms->mailstream, mailbox);
02703    } else {
02704       ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02705    }
02706    ast_mutex_unlock(&vms->lock);
02707 }
02708 
02709 #endif /* IMAP_STORAGE */
02710 
02711 /*! \brief Lock file path
02712     only return failure if ast_lock_path returns 'timeout',
02713    not if the path does not exist or any other reason
02714 */
02715 static int vm_lock_path(const char *path)
02716 {
02717    switch (ast_lock_path(path)) {
02718    case AST_LOCK_TIMEOUT:
02719       return -1;
02720    default:
02721       return 0;
02722    }
02723 }
02724 
02725 
02726 #ifdef ODBC_STORAGE
02727 struct generic_prepare_struct {
02728    char *sql;
02729    int argc;
02730    char **argv;
02731 };
02732 
02733 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02734 {
02735    struct generic_prepare_struct *gps = data;
02736    int res, i;
02737    SQLHSTMT stmt;
02738 
02739    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02740    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02741       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
02742       return NULL;
02743    }
02744    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02745    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02746       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02747       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02748       return NULL;
02749    }
02750    for (i = 0; i < gps->argc; i++)
02751       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02752 
02753    return stmt;
02754 }
02755 
02756 /*!
02757  * \brief Retrieves a file from an ODBC data store.
02758  * \param dir the path to the file to be retreived.
02759  * \param msgnum the message number, such as within a mailbox folder.
02760  * 
02761  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
02762  * 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.
02763  *
02764  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
02765  * The output is the message information file with the name msgnum and the extension .txt
02766  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
02767  * 
02768  * \return 0 on success, -1 on error.
02769  */
02770 static int retrieve_file(char *dir, int msgnum)
02771 {
02772    int x = 0;
02773    int res;
02774    int fd=-1;
02775    size_t fdlen = 0;
02776    void *fdm = MAP_FAILED;
02777    SQLSMALLINT colcount=0;
02778    SQLHSTMT stmt;
02779    char sql[PATH_MAX];
02780    char fmt[80]="";
02781    char *c;
02782    char coltitle[256];
02783    SQLSMALLINT collen;
02784    SQLSMALLINT datatype;
02785    SQLSMALLINT decimaldigits;
02786    SQLSMALLINT nullable;
02787    SQLULEN colsize;
02788    SQLLEN colsize2;
02789    FILE *f=NULL;
02790    char rowdata[80];
02791    char fn[PATH_MAX];
02792    char full_fn[PATH_MAX];
02793    char msgnums[80];
02794    char *argv[] = { dir, msgnums };
02795    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02796 
02797    struct odbc_obj *obj;
02798    obj = ast_odbc_request_obj(odbc_database, 0);
02799    if (obj) {
02800       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02801       c = strchr(fmt, '|');
02802       if (c)
02803          *c = '\0';
02804       if (!strcasecmp(fmt, "wav49"))
02805          strcpy(fmt, "WAV");
02806       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02807       if (msgnum > -1)
02808          make_file(fn, sizeof(fn), dir, msgnum);
02809       else
02810          ast_copy_string(fn, dir, sizeof(fn));
02811 
02812       /* Create the information file */
02813       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02814       
02815       if (!(f = fopen(full_fn, "w+"))) {
02816          ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02817          goto yuck;
02818       }
02819       
02820       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02821       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02822       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02823       if (!stmt) {
02824          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02825          ast_odbc_release_obj(obj);
02826          goto yuck;
02827       }
02828       res = SQLFetch(stmt);
02829       if (res == SQL_NO_DATA) {
02830          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02831          ast_odbc_release_obj(obj);
02832          goto yuck;
02833       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02834          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02835          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02836          ast_odbc_release_obj(obj);
02837          goto yuck;
02838       }
02839       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
02840       if (fd < 0) {
02841          ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02842          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02843          ast_odbc_release_obj(obj);
02844          goto yuck;
02845       }
02846       res = SQLNumResultCols(stmt, &colcount);
02847       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02848          ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02849          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02850          ast_odbc_release_obj(obj);
02851          goto yuck;
02852       }
02853       if (f) 
02854          fprintf(f, "[message]\n");
02855       for (x=0;x<colcount;x++) {
02856          rowdata[0] = '\0';
02857          collen = sizeof(coltitle);
02858          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02859                   &datatype, &colsize, &decimaldigits, &nullable);
02860          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02861             ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02862             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02863             ast_odbc_release_obj(obj);
02864             goto yuck;
02865          }
02866          if (!strcasecmp(coltitle, "recording")) {
02867             off_t offset;
02868             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02869             fdlen = colsize2;
02870             if (fd > -1) {
02871                char tmp[1]="";
02872                lseek(fd, fdlen - 1, SEEK_SET);
02873                if (write(fd, tmp, 1) != 1) {
02874                   close(fd);
02875                   fd = -1;
02876                   continue;
02877                }
02878                /* Read out in small chunks */
02879                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
02880                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
02881                      ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
02882                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02883                      ast_odbc_release_obj(obj);
02884                      goto yuck;
02885                   } else {
02886                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
02887                      munmap(fdm, CHUNKSIZE);
02888                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02889                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02890                         unlink(full_fn);
02891                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02892                         ast_odbc_release_obj(obj);
02893                         goto yuck;
02894                      }
02895                   }
02896                }
02897                if (truncate(full_fn, fdlen) < 0) {
02898                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
02899                }
02900             }
02901          } else {
02902             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02903             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02904                ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
02905                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02906                ast_odbc_release_obj(obj);
02907                goto yuck;
02908             }
02909             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
02910                fprintf(f, "%s=%s\n", coltitle, rowdata);
02911          }
02912       }
02913       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02914       ast_odbc_release_obj(obj);
02915    } else
02916       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02917 yuck: 
02918    if (f)
02919       fclose(f);
02920    if (fd > -1)
02921       close(fd);
02922    return x - 1;
02923 }
02924 
02925 /*!
02926  * \brief Determines the highest message number in use for a given user and mailbox folder.
02927  * \param vmu 
02928  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
02929  *
02930  * This method is used when mailboxes are stored in an ODBC back end.
02931  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
02932  *
02933  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
02934  */
02935 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02936 {
02937    int x = 0;
02938    int res;
02939    SQLHSTMT stmt;
02940    char sql[PATH_MAX];
02941    char rowdata[20];
02942    char *argv[] = { dir };
02943    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02944 
02945    struct odbc_obj *obj;
02946    obj = ast_odbc_request_obj(odbc_database, 0);
02947    if (obj) {
02948       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
02949       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02950       if (!stmt) {
02951          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02952          ast_odbc_release_obj(obj);
02953          goto yuck;
02954       }
02955       res = SQLFetch(stmt);
02956       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02957          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02958          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02959          ast_odbc_release_obj(obj);
02960          goto yuck;
02961       }
02962       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02963       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02964          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02965          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02966          ast_odbc_release_obj(obj);
02967          goto yuck;
02968       }
02969       if (sscanf(rowdata, "%d", &x) != 1)
02970          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
02971       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02972       ast_odbc_release_obj(obj);
02973    } else
02974       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02975 yuck: 
02976    return x - 1;
02977 }
02978 
02979 /*!
02980  * \brief Determines if the specified message exists.
02981  * \param dir the folder the mailbox folder to look for messages. 
02982  * \param msgnum the message index to query for.
02983  *
02984  * This method is used when mailboxes are stored in an ODBC back end.
02985  *
02986  * \return greater than zero if the message exists, zero when the message does not exist or on error.
02987  */
02988 static int message_exists(char *dir, int msgnum)
02989 {
02990    int x = 0;
02991    int res;
02992    SQLHSTMT stmt;
02993    char sql[PATH_MAX];
02994    char rowdata[20];
02995    char msgnums[20];
02996    char *argv[] = { dir, msgnums };
02997    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02998 
02999    struct odbc_obj *obj;
03000    obj = ast_odbc_request_obj(odbc_database, 0);
03001    if (obj) {
03002       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03003       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03004       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03005       if (!stmt) {
03006          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03007          ast_odbc_release_obj(obj);
03008          goto yuck;
03009       }
03010       res = SQLFetch(stmt);
03011       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03012          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03013          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03014          ast_odbc_release_obj(obj);
03015          goto yuck;
03016       }
03017       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03018       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03019          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03020          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03021          ast_odbc_release_obj(obj);
03022          goto yuck;
03023       }
03024       if (sscanf(rowdata, "%d", &x) != 1)
03025          ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
03026       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03027       ast_odbc_release_obj(obj);
03028    } else
03029       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03030 yuck: 
03031    return x;
03032 }
03033 
03034 /*!
03035  * \brief returns the one-based count for messages.
03036  * \param vmu
03037  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03038  *
03039  * This method is used when mailboxes are stored in an ODBC back end.
03040  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
03041  * one-based messages.
03042  * This method just calls last_message_index and returns +1 of its value.
03043  *
03044  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
03045  */
03046 static int count_messages(struct ast_vm_user *vmu, char *dir)
03047 {
03048    return last_message_index(vmu, dir) + 1;
03049 }
03050 
03051 /*!
03052  * \brief Deletes a message from the mailbox folder.
03053  * \param sdir The mailbox folder to work in.
03054  * \param smsg The message index to be deleted.
03055  *
03056  * This method is used when mailboxes are stored in an ODBC back end.
03057  * The specified message is directly deleted from the database 'voicemessages' table.
03058  * 
03059  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
03060  */
03061 static void delete_file(char *sdir, int smsg)
03062 {
03063    SQLHSTMT stmt;
03064    char sql[PATH_MAX];
03065    char msgnums[20];
03066    char *argv[] = { sdir, msgnums };
03067    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
03068 
03069    struct odbc_obj *obj;
03070    obj = ast_odbc_request_obj(odbc_database, 0);
03071    if (obj) {
03072       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03073       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
03074       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03075       if (!stmt)
03076          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03077       else
03078          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03079       ast_odbc_release_obj(obj);
03080    } else
03081       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03082    return;  
03083 }
03084 
03085 /*!
03086  * \brief Copies a voicemail from one mailbox to another.
03087  * \param sdir the folder for which to look for the message to be copied.
03088  * \param smsg the index of the message to be copied.
03089  * \param ddir the destination folder to copy the message into.
03090  * \param dmsg the index to be used for the copied message.
03091  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
03092  * \param dmailboxcontext The context for the destination user.
03093  *
03094  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
03095  */
03096 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
03097 {
03098    SQLHSTMT stmt;
03099    char sql[512];
03100    char msgnums[20];
03101    char msgnumd[20];
03102    struct odbc_obj *obj;
03103    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
03104    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03105 
03106    delete_file(ddir, dmsg);
03107    obj = ast_odbc_request_obj(odbc_database, 0);
03108    if (obj) {
03109       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03110       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03111       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
03112       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03113       if (!stmt)
03114          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
03115       else
03116          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03117       ast_odbc_release_obj(obj);
03118    } else
03119       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03120    return;  
03121 }
03122 
03123 struct insert_data {
03124    char *sql;
03125    char *dir;
03126    char *msgnums;
03127    void *data;
03128    SQLLEN datalen;
03129    SQLLEN indlen;
03130    const char *context;
03131    const char *macrocontext;
03132    const char *callerid;
03133    const char *origtime;
03134    const char *duration;
03135    char *mailboxuser;
03136    char *mailboxcontext;
03137    const char *category;
03138    const char *flag;
03139 };
03140 
03141 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
03142 {
03143    struct insert_data *data = vdata;
03144    int res;
03145    SQLHSTMT stmt;
03146 
03147    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
03148    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03149       ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
03150       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03151       return NULL;
03152    }
03153 
03154    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
03155    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
03156    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
03157    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
03158    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
03159    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
03160    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
03161    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
03162    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
03163    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
03164    SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
03165    if (!ast_strlen_zero(data->category)) {
03166       SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
03167    }
03168    res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
03169    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03170       ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
03171       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03172       return NULL;
03173    }
03174 
03175    return stmt;
03176 }
03177 
03178 /*!
03179  * \brief Stores a voicemail into the database.
03180  * \param dir the folder the mailbox folder to store the message.
03181  * \param mailboxuser the user owning the mailbox folder.
03182  * \param mailboxcontext
03183  * \param msgnum the message index for the message to be stored.
03184  *
03185  * This method is used when mailboxes are stored in an ODBC back end.
03186  * The message sound file and information file is looked up on the file system. 
03187  * A SQL query is invoked to store the message into the (MySQL) database.
03188  *
03189  * \return the zero on success -1 on error.
03190  */
03191 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
03192 {
03193    int res = 0;
03194    int fd = -1;
03195    void *fdm = MAP_FAILED;
03196    size_t fdlen = -1;
03197    SQLHSTMT stmt;
03198    char sql[PATH_MAX];
03199    char msgnums[20];
03200    char fn[PATH_MAX];
03201    char full_fn[PATH_MAX];
03202    char fmt[80]="";
03203    char *c;
03204    struct ast_config *cfg=NULL;
03205    struct odbc_obj *obj;
03206    struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
03207    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03208 
03209    delete_file(dir, msgnum);
03210    if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
03211       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03212       return -1;
03213    }
03214 
03215    do {
03216       ast_copy_string(fmt, vmfmts, sizeof(fmt));
03217       c = strchr(fmt, '|');
03218       if (c)
03219          *c = '\0';
03220       if (!strcasecmp(fmt, "wav49"))
03221          strcpy(fmt, "WAV");
03222       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
03223       if (msgnum > -1)
03224          make_file(fn, sizeof(fn), dir, msgnum);
03225       else
03226          ast_copy_string(fn, dir, sizeof(fn));
03227       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03228       cfg = ast_config_load(full_fn, config_flags);
03229       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
03230       fd = open(full_fn, O_RDWR);
03231       if (fd < 0) {
03232          ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
03233          res = -1;
03234          break;
03235       }
03236       if (cfg) {
03237          if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
03238             idata.context = "";
03239          }
03240          if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
03241             idata.macrocontext = "";
03242          }
03243          if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
03244             idata.callerid = "";
03245          }
03246          if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
03247             idata.origtime = "";
03248          }
03249          if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
03250             idata.duration = "";
03251          }
03252          if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
03253             idata.category = "";
03254          }
03255          if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
03256             idata.flag = "";
03257          }
03258       }
03259       fdlen = lseek(fd, 0, SEEK_END);
03260       lseek(fd, 0, SEEK_SET);
03261       printf("Length is %zd\n", fdlen);
03262       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
03263       if (fdm == MAP_FAILED) {
03264          ast_log(AST_LOG_WARNING, "Memory map failed!\n");
03265          res = -1;
03266          break;
03267       } 
03268       idata.data = fdm;
03269       idata.datalen = idata.indlen = fdlen;
03270 
03271       if (!ast_strlen_zero(idata.category)) 
03272          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
03273       else
03274          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
03275 
03276       if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
03277          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03278       } else {
03279          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03280          res = -1;
03281       }
03282    } while (0);
03283    if (obj) {
03284       ast_odbc_release_obj(obj);
03285    }
03286    if (cfg)
03287       ast_config_destroy(cfg);
03288    if (fdm != MAP_FAILED)
03289       munmap(fdm, fdlen);
03290    if (fd > -1)
03291       close(fd);
03292    return res;
03293 }
03294 
03295 /*!
03296  * \brief Renames a message in a mailbox folder.
03297  * \param sdir The folder of the message to be renamed.
03298  * \param smsg The index of the message to be renamed.
03299  * \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.
03300  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
03301  * \param ddir The destination folder for the message to be renamed into
03302  * \param dmsg The destination message for the message to be renamed.
03303  *
03304  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
03305  * 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.
03306  * 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.
03307  */
03308 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
03309 {
03310    SQLHSTMT stmt;
03311    char sql[PATH_MAX];
03312    char msgnums[20];
03313    char msgnumd[20];
03314    struct odbc_obj *obj;
03315    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
03316    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
03317 
03318    delete_file(ddir, dmsg);
03319    obj = ast_odbc_request_obj(odbc_database, 0);
03320    if (obj) {
03321       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
03322       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
03323       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
03324       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03325       if (!stmt)
03326          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03327       else
03328          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
03329       ast_odbc_release_obj(obj);
03330    } else
03331       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03332    return;  
03333 }
03334 
03335 /*!
03336  * \brief Removes a voicemail message file.
03337  * \param dir the path to the message file.
03338  * \param msgnum the unique number for the message within the mailbox.
03339  *
03340  * Removes the message content file and the information file.
03341  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
03342  * Typical use is to clean up after a RETRIEVE operation. 
03343  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
03344  * \return zero on success, -1 on error.
03345  */
03346 static int remove_file(char *dir, int msgnum)
03347 {
03348    char fn[PATH_MAX];
03349    char full_fn[PATH_MAX];
03350    char msgnums[80];
03351    
03352    if (msgnum > -1) {
03353       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03354       make_file(fn, sizeof(fn), dir, msgnum);
03355    } else
03356       ast_copy_string(fn, dir, sizeof(fn));
03357    ast_filedelete(fn, NULL);  
03358    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03359    unlink(full_fn);
03360    return 0;
03361 }
03362 #else
03363 #ifndef IMAP_STORAGE
03364 /*!
03365  * \brief Find all .txt files - even if they are not in sequence from 0000.
03366  * \param vmu
03367  * \param dir
03368  *
03369  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03370  *
03371  * \return the count of messages, zero or more.
03372  */
03373 static int count_messages(struct ast_vm_user *vmu, char *dir)
03374 {
03375 
03376    int vmcount = 0;
03377    DIR *vmdir = NULL;
03378    struct dirent *vment = NULL;
03379 
03380    if (vm_lock_path(dir))
03381       return ERROR_LOCK_PATH;
03382 
03383    if ((vmdir = opendir(dir))) {
03384       while ((vment = readdir(vmdir))) {
03385          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
03386             vmcount++;
03387          }
03388       }
03389       closedir(vmdir);
03390    }
03391    ast_unlock_path(dir);
03392    
03393    return vmcount;
03394 }
03395 
03396 /*!
03397  * \brief Renames a message in a mailbox folder.
03398  * \param sfn The path to the mailbox information and data file to be renamed.
03399  * \param dfn The path for where the message data and information files will be renamed to.
03400  *
03401  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03402  */
03403 static void rename_file(char *sfn, char *dfn)
03404 {
03405    char stxt[PATH_MAX];
03406    char dtxt[PATH_MAX];
03407    ast_filerename(sfn,dfn,NULL);
03408    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03409    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03410    if (ast_check_realtime("voicemail_data")) {
03411       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
03412    }
03413    rename(stxt, dtxt);
03414 }
03415 
03416 /*! 
03417  * \brief Determines the highest message number in use for a given user and mailbox folder.
03418  * \param vmu 
03419  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
03420  *
03421  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
03422  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
03423  *
03424  * \note Should always be called with a lock already set on dir.
03425  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
03426  */
03427 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03428 {
03429    int x;
03430    unsigned char map[MAXMSGLIMIT] = "";
03431    DIR *msgdir;
03432    struct dirent *msgdirent;
03433    int msgdirint;
03434 
03435    /* Reading the entire directory into a file map scales better than
03436     * doing a stat repeatedly on a predicted sequence.  I suspect this
03437     * is partially due to stat(2) internally doing a readdir(2) itself to
03438     * find each file. */
03439    msgdir = opendir(dir);
03440    while ((msgdirent = readdir(msgdir))) {
03441       if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03442          map[msgdirint] = 1;
03443    }
03444    closedir(msgdir);
03445 
03446    for (x = 0; x < vmu->maxmsg; x++) {
03447       if (map[x] == 0)
03448          break;
03449    }
03450 
03451    return x - 1;
03452 }
03453 
03454 #endif /* #ifndef IMAP_STORAGE */
03455 #endif /* #else of #ifdef ODBC_STORAGE */
03456 #ifndef IMAP_STORAGE
03457 /*!
03458  * \brief Utility function to copy a file.
03459  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
03460  * \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.
03461  *
03462  * 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.
03463  * The copy operation copies up to 4096 bytes at once.
03464  *
03465  * \return zero on success, -1 on error.
03466  */
03467 static int copy(char *infile, char *outfile)
03468 {
03469    int ifd;
03470    int ofd;
03471    int res;
03472    int len;
03473    char buf[4096];
03474 
03475 #ifdef HARDLINK_WHEN_POSSIBLE
03476    /* Hard link if possible; saves disk space & is faster */
03477    if (link(infile, outfile)) {
03478 #endif
03479       if ((ifd = open(infile, O_RDONLY)) < 0) {
03480          ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03481          return -1;
03482       }
03483       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03484          ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03485          close(ifd);
03486          return -1;
03487       }
03488       do {
03489          len = read(ifd, buf, sizeof(buf));
03490          if (len < 0) {
03491             ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03492             close(ifd);
03493             close(ofd);
03494             unlink(outfile);
03495          }
03496          if (len) {
03497             res = write(ofd, buf, len);
03498             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03499                ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03500                close(ifd);
03501                close(ofd);
03502                unlink(outfile);
03503             }
03504          }
03505       } while (len);
03506       close(ifd);
03507       close(ofd);
03508       return 0;
03509 #ifdef HARDLINK_WHEN_POSSIBLE
03510    } else {
03511       /* Hard link succeeded */
03512       return 0;
03513    }
03514 #endif
03515 }
03516 
03517 /*!
03518  * \brief Copies a voicemail information (envelope) file.
03519  * \param frompath
03520  * \param topath 
03521  *
03522  * Every voicemail has the data (.wav) file, and the information file.
03523  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
03524  * This is used by the COPY macro when not using IMAP storage.
03525  */
03526 static void copy_plain_file(char *frompath, char *topath)
03527 {
03528    char frompath2[PATH_MAX], topath2[PATH_MAX];
03529    struct ast_variable *tmp,*var = NULL;
03530    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03531    ast_filecopy(frompath, topath, NULL);
03532    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03533    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03534    if (ast_check_realtime("voicemail_data")) {
03535       var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
03536       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03537       for (tmp = var; tmp; tmp = tmp->next) {
03538          if (!strcasecmp(tmp->name, "origmailbox")) {
03539             origmailbox = tmp->value;
03540          } else if (!strcasecmp(tmp->name, "context")) {
03541             context = tmp->value;
03542          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03543             macrocontext = tmp->value;
03544          } else if (!strcasecmp(tmp->name, "exten")) {
03545             exten = tmp->value;
03546          } else if (!strcasecmp(tmp->name, "priority")) {
03547             priority = tmp->value;
03548          } else if (!strcasecmp(tmp->name, "callerchan")) {
03549             callerchan = tmp->value;
03550          } else if (!strcasecmp(tmp->name, "callerid")) {
03551             callerid = tmp->value;
03552          } else if (!strcasecmp(tmp->name, "origdate")) {
03553             origdate = tmp->value;
03554          } else if (!strcasecmp(tmp->name, "origtime")) {
03555             origtime = tmp->value;
03556          } else if (!strcasecmp(tmp->name, "category")) {
03557             category = tmp->value;
03558          } else if (!strcasecmp(tmp->name, "duration")) {
03559             duration = tmp->value;
03560          }
03561       }
03562       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);
03563    }
03564    copy(frompath2, topath2);
03565    ast_variables_destroy(var);
03566 }
03567 #endif
03568 
03569 /*! 
03570  * \brief Removes the voicemail sound and information file.
03571  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03572  *
03573  * This is used by the DELETE macro when voicemails are stored on the file system.
03574  *
03575  * \return zero on success, -1 on error.
03576  */
03577 static int vm_delete(char *file)
03578 {
03579    char *txt;
03580    int txtsize = 0;
03581 
03582    txtsize = (strlen(file) + 5)*sizeof(char);
03583    txt = alloca(txtsize);
03584    /* Sprintf here would safe because we alloca'd exactly the right length,
03585     * but trying to eliminate all sprintf's anyhow
03586     */
03587    if (ast_check_realtime("voicemail_data")) {
03588       ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
03589    }
03590    snprintf(txt, txtsize, "%s.txt", file);
03591    unlink(txt);
03592    return ast_filedelete(file, NULL);
03593 }
03594 
03595 /*!
03596  * \brief utility used by inchar(), for base_encode()
03597  */
03598 static int inbuf(struct baseio *bio, FILE *fi)
03599 {
03600    int l;
03601 
03602    if (bio->ateof)
03603       return 0;
03604 
03605    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03606       if (ferror(fi))
03607          return -1;
03608 
03609       bio->ateof = 1;
03610       return 0;
03611    }
03612 
03613    bio->iolen= l;
03614    bio->iocp= 0;
03615 
03616    return 1;
03617 }
03618 
03619 /*!
03620  * \brief utility used by base_encode()
03621  */
03622 static int inchar(struct baseio *bio, FILE *fi)
03623 {
03624    if (bio->iocp>=bio->iolen) {
03625       if (!inbuf(bio, fi))
03626          return EOF;
03627    }
03628 
03629    return bio->iobuf[bio->iocp++];
03630 }
03631 
03632 /*!
03633  * \brief utility used by base_encode()
03634  */
03635 static int ochar(struct baseio *bio, int c, FILE *so)
03636 {
03637    if (bio->linelength >= BASELINELEN) {
03638       if (fputs(eol,so) == EOF)
03639          return -1;
03640 
03641       bio->linelength= 0;
03642    }
03643 
03644    if (putc(((unsigned char)c),so) == EOF)
03645       return -1;
03646 
03647    bio->linelength++;
03648 
03649    return 1;
03650 }
03651 
03652 /*!
03653  * \brief Performs a base 64 encode algorithm on the contents of a File
03654  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
03655  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
03656  *
03657  * 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 ?
03658  *
03659  * \return zero on success, -1 on error.
03660  */
03661 static int base_encode(char *filename, FILE *so)
03662 {
03663    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03664       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03665       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03666       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03667    int i,hiteof= 0;
03668    FILE *fi;
03669    struct baseio bio;
03670 
03671    memset(&bio, 0, sizeof(bio));
03672    bio.iocp = BASEMAXINLINE;
03673 
03674    if (!(fi = fopen(filename, "rb"))) {
03675       ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03676       return -1;
03677    }
03678 
03679    while (!hiteof){
03680       unsigned char igroup[3], ogroup[4];
03681       int c,n;
03682 
03683       igroup[0]= igroup[1]= igroup[2]= 0;
03684 
03685       for (n= 0;n<3;n++) {
03686          if ((c = inchar(&bio, fi)) == EOF) {
03687             hiteof= 1;
03688             break;
03689          }
03690 
03691          igroup[n]= (unsigned char)c;
03692       }
03693 
03694       if (n> 0) {
03695          ogroup[0]= dtable[igroup[0]>>2];
03696          ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
03697          ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
03698          ogroup[3]= dtable[igroup[2]&0x3F];
03699 
03700          if (n<3) {
03701             ogroup[3]= '=';
03702 
03703             if (n<2)
03704                ogroup[2]= '=';
03705          }
03706 
03707          for (i= 0;i<4;i++)
03708             ochar(&bio, ogroup[i], so);
03709       }
03710    }
03711 
03712    fclose(fi);
03713    
03714    if (fputs(eol,so)==EOF)
03715       return 0;
03716 
03717    return 1;
03718 }
03719 
03720 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)
03721 {
03722    char callerid[256];
03723    char fromdir[256], fromfile[256];
03724    struct ast_config *msg_cfg;
03725    const char *origcallerid, *origtime;
03726    char origcidname[80], origcidnum[80], origdate[80];
03727    int inttime;
03728    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03729 
03730    /* Prepare variables for substitution in email body and subject */
03731    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03732    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03733    snprintf(passdata, passdatasize, "%d", msgnum);
03734    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03735    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03736    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03737    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03738       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03739    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03740    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03741    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03742    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03743    pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
03744 
03745    /* Retrieve info from VM attribute file */
03746    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03747    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03748    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03749       strcat(fromfile, ".txt");
03750    }
03751    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
03752       if (option_debug > 0) {
03753          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03754       }
03755       return;
03756    }
03757 
03758    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03759       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03760       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03761       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03762       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03763    }
03764 
03765    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%d", &inttime) == 1) {
03766       struct timeval tv = { inttime, };
03767       struct ast_tm tm;
03768       ast_localtime(&tv, &tm, NULL);
03769       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03770       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03771    }
03772    ast_config_destroy(msg_cfg);
03773 }
03774 
03775 /*!
03776  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
03777  * \param from The string to work with.
03778  * \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.
03779  * 
03780  * \return The destination string with quotes wrapped on it (the to field).
03781  */
03782 static char *quote(const char *from, char *to, size_t len)
03783 {
03784    char *ptr = to;
03785    *ptr++ = '"';
03786    for (; ptr < to + len - 1; from++) {
03787       if (*from == '"')
03788          *ptr++ = '\\';
03789       else if (*from == '\0')
03790          break;
03791       *ptr++ = *from;
03792    }
03793    if (ptr < to + len - 1)
03794       *ptr++ = '"';
03795    *ptr = '\0';
03796    return to;
03797 }
03798 
03799 /*! \brief
03800  * fill in *tm for current time according to the proper timezone, if any.
03801  * Return tm so it can be used as a function argument.
03802  */
03803 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
03804 {
03805    const struct vm_zone *z = NULL;
03806    struct timeval t = ast_tvnow();
03807 
03808    /* Does this user have a timezone specified? */
03809    if (!ast_strlen_zero(vmu->zonetag)) {
03810       /* Find the zone in the list */
03811       AST_LIST_LOCK(&zones);
03812       AST_LIST_TRAVERSE(&zones, z, list) {
03813          if (!strcmp(z->name, vmu->zonetag))
03814             break;
03815       }
03816       AST_LIST_UNLOCK(&zones);
03817    }
03818    ast_localtime(&t, tm, z ? z->timezone : NULL);
03819    return tm;
03820 }
03821 
03822 /*!\brief Check if the string would need encoding within the MIME standard, to
03823  * avoid confusing certain mail software that expects messages to be 7-bit
03824  * clean.
03825  */
03826 static int check_mime(const char *str)
03827 {
03828    for (; *str; str++) {
03829       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03830          return 1;
03831       }
03832    }
03833    return 0;
03834 }
03835 
03836 /*!\brief Encode a string according to the MIME rules for encoding strings
03837  * that are not 7-bit clean or contain control characters.
03838  *
03839  * Additionally, if the encoded string would exceed the MIME limit of 76
03840  * characters per line, then the encoding will be broken up into multiple
03841  * sections, separated by a space character, in order to facilitate
03842  * breaking up the associated header across multiple lines.
03843  *
03844  * \param start A string to be encoded
03845  * \param end An expandable buffer for holding the result
03846  * \param preamble The length of the first line already used for this string,
03847  * to ensure that each line maintains a maximum length of 76 chars.
03848  * \param postamble the length of any additional characters appended to the
03849  * line, used to ensure proper field wrapping.
03850  * \retval The encoded string.
03851  */
03852 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03853 {
03854    char tmp[80];
03855    int first_section = 1;
03856    size_t endlen = 0, tmplen = 0;
03857    *end = '\0';
03858 
03859    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03860    for (; *start; start++) {
03861       int need_encoding = 0;
03862       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03863          need_encoding = 1;
03864       }
03865       if ((first_section && need_encoding && preamble + tmplen > 70) ||
03866          (first_section && !need_encoding && preamble + tmplen > 72) ||
03867          (!first_section && need_encoding && tmplen > 70) ||
03868          (!first_section && !need_encoding && tmplen > 72)) {
03869          /* Start new line */
03870          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
03871          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03872          first_section = 0;
03873       }
03874       if (need_encoding && *start == ' ') {
03875          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
03876       } else if (need_encoding) {
03877          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
03878       } else {
03879          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
03880       }
03881    }
03882    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
03883    return end;
03884 }
03885 
03886 /*!
03887  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
03888  * \param p The output file to generate the email contents into.
03889  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
03890  * \param vmu The voicemail user who is sending the voicemail.
03891  * \param msgnum The message index in the mailbox folder.
03892  * \param context 
03893  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
03894  * \param cidnum The caller ID number.
03895  * \param cidname The caller ID name.
03896  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
03897  * \param format The message sound file format. i.e. .wav
03898  * \param duration The time of the message content, in seconds.
03899  * \param attach_user_voicemail if 1, the sound file is attached to the email.
03900  * \param chan
03901  * \param category
03902  * \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.
03903  *
03904  * 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.
03905  */
03906 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)
03907 {
03908    char date[256];
03909    char host[MAXHOSTNAMELEN] = "";
03910    char who[256];
03911    char bound[256];
03912    char dur[256];
03913    struct ast_tm tm;
03914    char enc_cidnum[256] = "", enc_cidname[256] = "";
03915    char *passdata = NULL, *passdata2;
03916    size_t len_passdata = 0, len_passdata2, tmplen;
03917    char *greeting_attachment; 
03918    char filename[256];
03919 
03920 #ifdef IMAP_STORAGE
03921 #define ENDL "\r\n"
03922 #else
03923 #define ENDL "\n"
03924 #endif
03925 
03926    /* One alloca for multiple fields */
03927    len_passdata2 = strlen(vmu->fullname);
03928    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
03929       len_passdata2 = tmplen;
03930    }
03931    if ((tmplen = strlen(fromstring)) > len_passdata2) {
03932       len_passdata2 = tmplen;
03933    }
03934    len_passdata2 = len_passdata2 * 3 + 200;
03935    passdata2 = alloca(len_passdata2);
03936 
03937    if (cidnum) {
03938       strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
03939    }
03940    if (cidname) {
03941       strip_control(cidname, enc_cidname, sizeof(enc_cidname));
03942    }
03943    gethostname(host, sizeof(host) - 1);
03944 
03945    if (strchr(srcemail, '@'))
03946       ast_copy_string(who, srcemail, sizeof(who));
03947    else 
03948       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03949    
03950    greeting_attachment = strrchr(ast_strdupa(attach), '/');
03951    if (greeting_attachment)
03952       *greeting_attachment++ = '\0';
03953 
03954    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03955    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03956    fprintf(p, "Date: %s" ENDL, date);
03957 
03958    /* Set date format for voicemail mail */
03959    ast_strftime(date, sizeof(date), emaildateformat, &tm);
03960 
03961    if (!ast_strlen_zero(fromstring)) {
03962       struct ast_channel *ast;
03963       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03964          char *ptr;
03965          memset(passdata2, 0, len_passdata2);
03966          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
03967          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
03968          len_passdata = strlen(passdata2) * 3 + 300;
03969          passdata = alloca(len_passdata);
03970          if (check_mime(passdata2)) {
03971             int first_line = 1;
03972             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
03973             while ((ptr = strchr(passdata, ' '))) {
03974                *ptr = '\0';
03975                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
03976                first_line = 0;
03977                passdata = ptr + 1;
03978             }
03979             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
03980          } else {
03981             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
03982          }
03983          ast_channel_free(ast);
03984       } else {
03985          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03986       }
03987    } else {
03988       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
03989    }
03990 
03991    if (check_mime(vmu->fullname)) {
03992       int first_line = 1;
03993       char *ptr;
03994       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
03995       while ((ptr = strchr(passdata2, ' '))) {
03996          *ptr = '\0';
03997          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
03998          first_line = 0;
03999          passdata2 = ptr + 1;
04000       }
04001       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
04002    } else {
04003       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
04004    }
04005    if (!ast_strlen_zero(emailsubject)) {
04006       struct ast_channel *ast;
04007       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04008          int vmlen = strlen(emailsubject) * 3 + 200;
04009          /* Only allocate more space if the previous was not large enough */
04010          if (vmlen > len_passdata) {
04011             passdata = alloca(vmlen);
04012             len_passdata = vmlen;
04013          }
04014 
04015          memset(passdata, 0, len_passdata);
04016          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
04017          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
04018          if (check_mime(passdata)) {
04019             int first_line = 1;
04020             char *ptr;
04021             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
04022             while ((ptr = strchr(passdata2, ' '))) {
04023                *ptr = '\0';
04024                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04025                first_line = 0;
04026                passdata2 = ptr + 1;
04027             }
04028             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
04029          } else {
04030             fprintf(p, "Subject: %s" ENDL, passdata);
04031          }
04032          ast_channel_free(ast);
04033       } else {
04034          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04035       }
04036    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
04037       if (ast_strlen_zero(flag)) {
04038          fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04039       } else {
04040          fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04041       }
04042    } else {
04043       if (ast_strlen_zero(flag)) {
04044          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
04045       } else {
04046          fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
04047       }
04048    }
04049 
04050    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
04051    if (imap) {
04052       /* additional information needed for IMAP searching */
04053       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
04054       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
04055       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
04056       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
04057 #ifdef IMAP_STORAGE
04058       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
04059 #else
04060       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
04061 #endif
04062       /* flag added for Urgent */
04063       fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
04064       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
04065       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
04066       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
04067       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
04068       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
04069       if (!ast_strlen_zero(category)) {
04070          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
04071       } else {
04072          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
04073       }
04074       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
04075       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
04076       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
04077    }
04078    if (!ast_strlen_zero(cidnum)) {
04079       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
04080    }
04081    if (!ast_strlen_zero(cidname)) {
04082       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
04083    }
04084    fprintf(p, "MIME-Version: 1.0" ENDL);
04085    if (attach_user_voicemail) {
04086       /* Something unique. */
04087       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
04088 
04089       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
04090       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
04091       fprintf(p, "--%s" ENDL, bound);
04092    }
04093    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
04094    if (emailbody) {
04095       struct ast_channel *ast;
04096       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04097          char *passdata;
04098          int vmlen = strlen(emailbody)*3 + 200;
04099          passdata = alloca(vmlen);
04100          memset(passdata, 0, vmlen);
04101          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04102          pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
04103          fprintf(p, "%s" ENDL, passdata);
04104          ast_channel_free(ast);
04105       } else
04106          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04107    } else if (msgnum > -1) {
04108       if (strcmp(vmu->mailbox, mailbox)) {
04109          /* Forwarded type */
04110          struct ast_config *msg_cfg;
04111          const char *v;
04112          int inttime;
04113          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
04114          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
04115          /* Retrieve info from VM attribute file */
04116          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
04117          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
04118          if (strlen(fromfile) < sizeof(fromfile) - 5) {
04119             strcat(fromfile, ".txt");
04120          }
04121          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
04122             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
04123                ast_copy_string(origcallerid, v, sizeof(origcallerid));
04124             }
04125 
04126             /* You might be tempted to do origdate, except that a) it's in the wrong
04127              * format, and b) it's missing for IMAP recordings. */
04128             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%d", &inttime) == 1) {
04129                struct timeval tv = { inttime, };
04130                struct ast_tm tm;
04131                ast_localtime(&tv, &tm, NULL);
04132                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
04133             }
04134             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
04135                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
04136                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
04137                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
04138                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
04139                date, origcallerid, origdate);
04140             ast_config_destroy(msg_cfg);
04141          } else {
04142             goto plain_message;
04143          }
04144       } else {
04145 plain_message:
04146          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
04147             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
04148             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
04149             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
04150             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
04151       }
04152    } else {
04153       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
04154             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
04155    }
04156 
04157    if (imap || attach_user_voicemail) {
04158       if (!ast_strlen_zero(attach2)) {
04159          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04160          ast_debug(5, "creating second attachment filename %s\n", filename);
04161          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
04162          snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
04163          ast_debug(5, "creating attachment filename %s\n", filename);
04164          add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04165       } else {
04166          snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
04167          ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
04168          add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
04169       }
04170    }
04171 }
04172 
04173 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)
04174 {
04175    char tmpdir[256], newtmp[256];
04176    char fname[256];
04177    char tmpcmd[256];
04178    int tmpfd = -1;
04179 
04180    /* Eww. We want formats to tell us their own MIME type */
04181    char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
04182 
04183    if (vmu->volgain < -.001 || vmu->volgain > .001) {
04184       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
04185       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
04186       tmpfd = mkstemp(newtmp);
04187       chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
04188       ast_debug(3, "newtmp: %s\n", newtmp);
04189       if (tmpfd > -1) {
04190          int soxstatus;
04191          snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
04192          if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
04193             attach = newtmp;
04194             ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
04195          } else {
04196             ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
04197                soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
04198             ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
04199          }
04200       }
04201    }
04202    fprintf(p, "--%s" ENDL, bound);
04203    if (msgnum > -1)
04204       fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
04205    else
04206       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
04207    fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
04208    fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
04209    if (msgnum > -1)
04210       fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
04211    else
04212       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
04213    snprintf(fname, sizeof(fname), "%s.%s", attach, format);
04214    base_encode(fname, p);
04215    if (last)
04216       fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
04217    if (tmpfd > -1) {
04218       unlink(fname);
04219       close(tmpfd);
04220       unlink(newtmp);
04221    }
04222    return 0;
04223 }
04224 #undef ENDL
04225 
04226 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)
04227 {
04228    FILE *p=NULL;
04229    char tmp[80] = "/tmp/astmail-XXXXXX";
04230    char tmp2[256];
04231 
04232    if (vmu && ast_strlen_zero(vmu->email)) {
04233       ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
04234       return(0);
04235    }
04236    if (!strcmp(format, "wav49"))
04237       format = "WAV";
04238    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));
04239    /* Make a temporary file instead of piping directly to sendmail, in case the mail
04240       command hangs */
04241    if ((p = vm_mkftemp(tmp)) == NULL) {
04242       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04243       return -1;
04244    } else {
04245       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
04246       fclose(p);
04247       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04248       ast_safe_system(tmp2);
04249       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
04250    }
04251    return 0;
04252 }
04253 
04254 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)
04255 {
04256    char date[256];
04257    char host[MAXHOSTNAMELEN] = "";
04258    char who[256];
04259    char dur[PATH_MAX];
04260    char tmp[80] = "/tmp/astmail-XXXXXX";
04261    char tmp2[PATH_MAX];
04262    struct ast_tm tm;
04263    FILE *p;
04264 
04265    if ((p = vm_mkftemp(tmp)) == NULL) {
04266       ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
04267       return -1;
04268    }
04269    gethostname(host, sizeof(host)-1);
04270    if (strchr(srcemail, '@'))
04271       ast_copy_string(who, srcemail, sizeof(who));
04272    else 
04273       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
04274    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
04275    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
04276    fprintf(p, "Date: %s\n", date);
04277 
04278    if (*pagerfromstring) {
04279       struct ast_channel *ast;
04280       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04281          char *passdata;
04282          int vmlen = strlen(fromstring)*3 + 200;
04283          passdata = alloca(vmlen);
04284          memset(passdata, 0, vmlen);
04285          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04286          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
04287          fprintf(p, "From: %s <%s>\n", passdata, who);
04288          ast_channel_free(ast);
04289       } else 
04290          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04291    } else
04292       fprintf(p, "From: Asterisk PBX <%s>\n", who);
04293    fprintf(p, "To: %s\n", pager);
04294    if (pagersubject) {
04295       struct ast_channel *ast;
04296       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04297          char *passdata;
04298          int vmlen = strlen(pagersubject) * 3 + 200;
04299          passdata = alloca(vmlen);
04300          memset(passdata, 0, vmlen);
04301          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04302          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
04303          fprintf(p, "Subject: %s\n\n", passdata);
04304          ast_channel_free(ast);
04305       } else
04306          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04307    } else {
04308       if (ast_strlen_zero(flag)) {
04309          fprintf(p, "Subject: New VM\n\n");
04310       } else {
04311          fprintf(p, "Subject: New %s VM\n\n", flag);
04312       }
04313    }
04314 
04315    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
04316    if (pagerbody) {
04317       struct ast_channel *ast;
04318       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
04319          char *passdata;
04320          int vmlen = strlen(pagerbody) * 3 + 200;
04321          passdata = alloca(vmlen);
04322          memset(passdata, 0, vmlen);
04323          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
04324          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
04325          fprintf(p, "%s\n", passdata);
04326          ast_channel_free(ast);
04327       } else
04328          ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
04329    } else {
04330       fprintf(p, "New %s long %s msg in box %s\n"
04331             "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
04332    }
04333    fclose(p);
04334    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
04335    ast_safe_system(tmp2);
04336    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
04337    return 0;
04338 }
04339 
04340 /*!
04341  * \brief Gets the current date and time, as formatted string.
04342  * \param s The buffer to hold the output formatted date.
04343  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
04344  * 
04345  * The date format string used is "%a %b %e %r UTC %Y".
04346  * 
04347  * \return zero on success, -1 on error.
04348  */
04349 static int get_date(char *s, int len)
04350 {
04351    struct ast_tm tm;
04352    struct timeval t = ast_tvnow();
04353    
04354    ast_localtime(&t, &tm, "UTC");
04355 
04356    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
04357 }
04358 
04359 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
04360 {
04361    int res;
04362    char fn[PATH_MAX];
04363    char dest[PATH_MAX];
04364 
04365    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
04366 
04367    if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
04368       ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
04369       return -1;
04370    }
04371 
04372    RETRIEVE(fn, -1, ext, context);
04373    if (ast_fileexists(fn, NULL, NULL) > 0) {
04374       res = ast_stream_and_wait(chan, fn, ecodes);
04375       if (res) {
04376          DISPOSE(fn, -1);
04377          return res;
04378       }
04379    } else {
04380       /* Dispose just in case */
04381       DISPOSE(fn, -1);
04382       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
04383       if (res)
04384          return res;
04385       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
04386       if (res)
04387          return res;
04388    }
04389    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
04390    return res;
04391 }
04392 
04393 static void free_zone(struct vm_zone *z)
04394 {
04395    ast_free(z);
04396 }
04397 
04398 #ifdef ODBC_STORAGE
04399 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
04400 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04401 {
04402    int x = -1;
04403    int res;
04404    SQLHSTMT stmt;
04405    char sql[PATH_MAX];
04406    char rowdata[20];
04407    char tmp[PATH_MAX] = "";
04408    struct odbc_obj *obj;
04409    char *context;
04410    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04411 
04412    if (newmsgs)
04413       *newmsgs = 0;
04414    if (oldmsgs)
04415       *oldmsgs = 0;
04416    if (urgentmsgs)
04417       *urgentmsgs = 0;
04418 
04419    /* If no mailbox, return immediately */
04420    if (ast_strlen_zero(mailbox))
04421       return 0;
04422 
04423    ast_copy_string(tmp, mailbox, sizeof(tmp));
04424    
04425    context = strchr(tmp, '@');
04426    if (context) {
04427       *context = '\0';
04428       context++;
04429    } else
04430       context = "default";
04431    
04432    obj = ast_odbc_request_obj(odbc_database, 0);
04433    if (obj) {
04434       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
04435       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04436       if (!stmt) {
04437          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04438          ast_odbc_release_obj(obj);
04439          goto yuck;
04440       }
04441       res = SQLFetch(stmt);
04442       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04443          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04444          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04445          ast_odbc_release_obj(obj);
04446          goto yuck;
04447       }
04448       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04449       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04450          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04451          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04452          ast_odbc_release_obj(obj);
04453          goto yuck;
04454       }
04455       *newmsgs = atoi(rowdata);
04456       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04457 
04458       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
04459       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04460       if (!stmt) {
04461          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04462          ast_odbc_release_obj(obj);
04463          goto yuck;
04464       }
04465       res = SQLFetch(stmt);
04466       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04467          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04468          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04469          ast_odbc_release_obj(obj);
04470          goto yuck;
04471       }
04472       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04473       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04474          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04475          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04476          ast_odbc_release_obj(obj);
04477          goto yuck;
04478       }
04479       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04480       *oldmsgs = atoi(rowdata);
04481 
04482       if (!urgentmsgs) {
04483          x = 0;
04484          ast_odbc_release_obj(obj);
04485          goto yuck;
04486       }
04487 
04488       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
04489       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04490       if (!stmt) {
04491          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04492          ast_odbc_release_obj(obj);
04493          goto yuck;
04494       }
04495       res = SQLFetch(stmt);
04496       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04497          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04498          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04499          ast_odbc_release_obj(obj);
04500          goto yuck;
04501       }
04502       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04503       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04504          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04505          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04506          ast_odbc_release_obj(obj);
04507          goto yuck;
04508       }
04509       *urgentmsgs = atoi(rowdata);
04510       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04511       ast_odbc_release_obj(obj);
04512       x = 0;
04513    } else
04514       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04515       
04516 yuck: 
04517    return x;
04518 }
04519 
04520 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04521 {
04522    return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
04523 }
04524 
04525 /*!
04526  * \brief Gets the number of messages that exist in a mailbox folder.
04527  * \param context
04528  * \param mailbox
04529  * \param folder
04530  * 
04531  * This method is used when ODBC backend is used.
04532  * \return The number of messages in this mailbox folder (zero or more).
04533  */
04534 static int messagecount(const char *context, const char *mailbox, const char *folder)
04535 {
04536    struct odbc_obj *obj = NULL;
04537    int nummsgs = 0;
04538    int res;
04539    SQLHSTMT stmt = NULL;
04540    char sql[PATH_MAX];
04541    char rowdata[20];
04542    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04543    if (!folder)
04544       folder = "INBOX";
04545    /* If no mailbox, return immediately */
04546    if (ast_strlen_zero(mailbox))
04547       return 0;
04548 
04549    obj = ast_odbc_request_obj(odbc_database, 0);
04550    if (obj) {
04551       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04552       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04553       if (!stmt) {
04554          ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04555          goto yuck;
04556       }
04557       res = SQLFetch(stmt);
04558       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04559          ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04560          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04561          goto yuck;
04562       }
04563       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04564       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04565          ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04566          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04567          goto yuck;
04568       }
04569       nummsgs = atoi(rowdata);
04570       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04571    } else
04572       ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04573 
04574 yuck:
04575    if (obj)
04576       ast_odbc_release_obj(obj);
04577    return nummsgs;
04578 }
04579 
04580 /** 
04581  * \brief Determines if the given folder has messages.
04582  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04583  * 
04584  * This function is used when the mailbox is stored in an ODBC back end.
04585  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04586  * \return 1 if the folder has one or more messages. zero otherwise.
04587  */
04588 static int has_voicemail(const char *mailbox, const char *folder)
04589 {
04590    char tmp[256], *tmp2 = tmp, *box, *context;
04591    ast_copy_string(tmp, mailbox, sizeof(tmp));
04592    while ((context = box = strsep(&tmp2, ","))) {
04593       strsep(&context, "@");
04594       if (ast_strlen_zero(context))
04595          context = "default";
04596       if (messagecount(context, box, folder))
04597          return 1;
04598    }
04599    return 0;
04600 }
04601 #endif
04602 #ifndef IMAP_STORAGE
04603 /*! 
04604  * \brief Copies a message from one mailbox to another.
04605  * \param chan
04606  * \param vmu
04607  * \param imbox
04608  * \param msgnum
04609  * \param duration
04610  * \param recip
04611  * \param fmt
04612  * \param dir
04613  *
04614  * This is only used by file storage based mailboxes.
04615  *
04616  * \return zero on success, -1 on error.
04617  */
04618 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)
04619 {
04620    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04621    const char *frombox = mbox(imbox);
04622    int recipmsgnum;
04623 
04624    ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04625 
04626    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
04627       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
04628    } else {
04629       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04630    }
04631    
04632    if (!dir)
04633       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04634    else
04635       ast_copy_string(fromdir, dir, sizeof(fromdir));
04636 
04637    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04638    make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04639 
04640    if (vm_lock_path(todir))
04641       return ERROR_LOCK_PATH;
04642 
04643    recipmsgnum = last_message_index(recip, todir) + 1;
04644    if (recipmsgnum < recip->maxmsg) {
04645       make_file(topath, sizeof(topath), todir, recipmsgnum);
04646       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04647          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04648       } else {
04649          /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
04650           * copy will fail. Instead, we need to create a local copy, store it, and delete the local
04651           * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
04652           * much worse problem happening and IMAP storage doesn't call this function
04653           */
04654          copy_plain_file(frompath, topath);
04655          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
04656          vm_delete(topath);
04657       }
04658    } else {
04659       ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04660    }
04661    ast_unlock_path(todir);
04662    notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
04663    
04664    return 0;
04665 }
04666 #endif
04667 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04668 
04669 static int messagecount(const char *context, const char *mailbox, const char *folder)
04670 {
04671    return __has_voicemail(context, mailbox, folder, 0);
04672 }
04673 
04674 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04675 {
04676    DIR *dir;
04677    struct dirent *de;
04678    char fn[256];
04679    int ret = 0;
04680 
04681    /* If no mailbox, return immediately */
04682    if (ast_strlen_zero(mailbox))
04683       return 0;
04684 
04685    if (ast_strlen_zero(folder))
04686       folder = "INBOX";
04687    if (ast_strlen_zero(context))
04688       context = "default";
04689 
04690    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04691 
04692    if (!(dir = opendir(fn)))
04693       return 0;
04694 
04695    while ((de = readdir(dir))) {
04696       if (!strncasecmp(de->d_name, "msg", 3)) {
04697          if (shortcircuit) {
04698             ret = 1;
04699             break;
04700          } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
04701             if (shortcircuit) return 1;
04702             ret++;
04703          }
04704       }
04705    }
04706 
04707    closedir(dir);
04708 
04709    /* If we are checking INBOX, we should check Urgent as well */
04710    if (strcmp(folder, "INBOX") == 0) {
04711       return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
04712    } else {
04713       return ret;
04714    }
04715 }
04716 
04717 /** 
04718  * \brief Determines if the given folder has messages.
04719  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
04720  * \param folder the folder to look in
04721  *
04722  * This function is used when the mailbox is stored in a filesystem back end.
04723  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
04724  * \return 1 if the folder has one or more messages. zero otherwise.
04725  */
04726 static int has_voicemail(const char *mailbox, const char *folder)
04727 {
04728    char tmp[256], *tmp2 = tmp, *box, *context;
04729    ast_copy_string(tmp, mailbox, sizeof(tmp));
04730    while ((box = strsep(&tmp2, ","))) {
04731       if ((context = strchr(box, '@')))
04732          *context++ = '\0';
04733       else
04734          context = "default";
04735       if (__has_voicemail(context, box, folder, 1))
04736          return 1;
04737    }
04738    return 0;
04739 }
04740 
04741 
04742 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
04743 {
04744    char tmp[256];
04745    char *context;
04746 
04747    /* If no mailbox, return immediately */
04748    if (ast_strlen_zero(mailbox))
04749       return 0;
04750 
04751    if (newmsgs)
04752       *newmsgs = 0;
04753    if (oldmsgs)
04754       *oldmsgs = 0;
04755    if (urgentmsgs)
04756       *urgentmsgs = 0;
04757 
04758    if (strchr(mailbox, ',')) {
04759       int tmpnew, tmpold, tmpurgent;
04760       char *mb, *cur;
04761 
04762       ast_copy_string(tmp, mailbox, sizeof(tmp));
04763       mb = tmp;
04764       while ((cur = strsep(&mb, ", "))) {
04765          if (!ast_strlen_zero(cur)) {
04766             if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04767                return -1;
04768             else {
04769                if (newmsgs)
04770                   *newmsgs += tmpnew; 
04771                if (oldmsgs)
04772                   *oldmsgs += tmpold;
04773                if (urgentmsgs)
04774                   *urgentmsgs += tmpurgent;
04775             }
04776          }
04777       }
04778       return 0;
04779    }
04780 
04781    ast_copy_string(tmp, mailbox, sizeof(tmp));
04782    
04783    if ((context = strchr(tmp, '@')))
04784       *context++ = '\0';
04785    else
04786       context = "default";
04787 
04788    if (newmsgs)
04789       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04790    if (oldmsgs)
04791       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04792    if (urgentmsgs)
04793       *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
04794 
04795    return 0;
04796 }
04797 
04798 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04799 {
04800    return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
04801 }
04802 
04803 #endif
04804 
04805 static void run_externnotify(char *context, char *extension, const char *flag)
04806 {
04807    char arguments[255];
04808    char ext_context[256] = "";
04809    int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
04810    struct ast_smdi_mwi_message *mwi_msg;
04811 
04812    if (!ast_strlen_zero(context))
04813       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04814    else
04815       ast_copy_string(ext_context, extension, sizeof(ext_context));
04816 
04817    if (smdi_iface) {
04818       if (ast_app_has_voicemail(ext_context, NULL)) 
04819          ast_smdi_mwi_set(smdi_iface, extension);
04820       else
04821          ast_smdi_mwi_unset(smdi_iface, extension);
04822 
04823       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04824          ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04825          if (!strncmp(mwi_msg->cause, "INV", 3))
04826             ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04827          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04828             ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04829          ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04830          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04831       } else {
04832          ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
04833       }
04834    }
04835 
04836    if (!ast_strlen_zero(externnotify)) {
04837       if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
04838          ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04839       } else {
04840          snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
04841          ast_debug(1, "Executing %s\n", arguments);
04842          ast_safe_system(arguments);
04843       }
04844    }
04845 }
04846 
04847 /*!
04848  * \brief Variables used for saving a voicemail.
04849  *
04850  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
04851  */
04852 struct leave_vm_options {
04853    unsigned int flags;
04854    signed char record_gain;
04855    char *exitcontext;
04856 };
04857 
04858 /*!
04859  * \brief Prompts the user and records a voicemail to a mailbox.
04860  * \param chan
04861  * \param ext
04862  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
04863  * 
04864  * 
04865  * 
04866  * \return zero on success, -1 on error.
04867  */
04868 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04869 {
04870 #ifdef IMAP_STORAGE
04871    int newmsgs, oldmsgs;
04872 #else
04873    char urgdir[PATH_MAX];
04874 #endif
04875    char txtfile[PATH_MAX];
04876    char tmptxtfile[PATH_MAX];
04877    struct vm_state *vms = NULL;
04878    char callerid[256];
04879    FILE *txt;
04880    char date[256];
04881    int txtdes;
04882    int res = 0;
04883    int msgnum;
04884    int duration = 0;
04885    int ausemacro = 0;
04886    int ousemacro = 0;
04887    int ouseexten = 0;
04888    int rtmsgid = 0;
04889    char tmpid[16];
04890    char tmpdur[16];
04891    char priority[16];
04892    char origtime[16];
04893    char dir[PATH_MAX];
04894    char tmpdir[PATH_MAX];
04895    char fn[PATH_MAX];
04896    char prefile[PATH_MAX] = "";
04897    char tempfile[PATH_MAX] = "";
04898    char ext_context[256] = "";
04899    char fmt[80];
04900    char *context;
04901    char ecodes[17] = "#";
04902    char tmp[1024] = "";
04903    char *tmpptr;
04904    struct ast_vm_user *vmu;
04905    struct ast_vm_user svm;
04906    const char *category = NULL;
04907    const char *code;
04908    const char *alldtmf = "0123456789ABCD*#";
04909    char flag[80];
04910 
04911    ast_copy_string(tmp, ext, sizeof(tmp));
04912    ext = tmp;
04913    if ((context = strchr(tmp, '@'))) {
04914       *context++ = '\0';
04915       tmpptr = strchr(context, '&');
04916    } else {
04917       tmpptr = strchr(ext, '&');
04918    }
04919 
04920    if (tmpptr)
04921       *tmpptr++ = '\0';
04922 
04923    ast_channel_lock(chan);
04924    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
04925       category = ast_strdupa(category);
04926    }
04927    ast_channel_unlock(chan);
04928 
04929    if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
04930       ast_copy_string(flag, "Urgent", sizeof(flag));
04931    } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
04932       ast_copy_string(flag, "PRIORITY", sizeof(flag));
04933    } else {
04934       flag[0] = '\0';
04935    }
04936 
04937    ast_debug(3, "Before find_user\n");
04938    if (!(vmu = find_user(&svm, context, ext))) {
04939       ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
04940       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04941       return res;
04942    }
04943    /* Setup pre-file if appropriate */
04944    if (strcmp(vmu->context, "default"))
04945       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
04946    else
04947       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
04948 
04949    /* Set the path to the prefile. Will be one of 
04950       VM_SPOOL_DIRcontext/ext/busy
04951       VM_SPOOL_DIRcontext/ext/unavail
04952       Depending on the flag set in options.
04953    */
04954    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
04955       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
04956    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
04957       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
04958    }
04959    /* Set the path to the tmpfile as
04960       VM_SPOOL_DIR/context/ext/temp
04961       and attempt to create the folder structure.
04962    */
04963    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
04964    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
04965       ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
04966       return -1;
04967    }
04968    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
04969    if (ast_fileexists(tempfile, NULL, NULL) > 0)
04970       ast_copy_string(prefile, tempfile, sizeof(prefile));
04971 
04972    DISPOSE(tempfile, -1);
04973    /* It's easier just to try to make it than to check for its existence */
04974    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
04975 
04976    /* Check current or macro-calling context for special extensions */
04977    if (ast_test_flag(vmu, VM_OPERATOR)) {
04978       if (!ast_strlen_zero(vmu->exit)) {
04979          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
04980             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04981             ouseexten = 1;
04982          }
04983       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
04984          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04985          ouseexten = 1;
04986       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
04987          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04988          ousemacro = 1;
04989       }
04990    }
04991 
04992    if (!ast_strlen_zero(vmu->exit)) {
04993       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
04994          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04995    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
04996       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04997    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
04998       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04999       ausemacro = 1;
05000    }
05001 
05002    if (ast_test_flag(options, OPT_DTMFEXIT)) {
05003       for (code = alldtmf; *code; code++) {
05004          char e[2] = "";
05005          e[0] = *code;
05006          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
05007             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
05008       }
05009    }
05010 
05011    /* Play the beginning intro if desired */
05012    if (!ast_strlen_zero(prefile)) {
05013 #ifdef ODBC_STORAGE
05014       int success = 
05015 #endif
05016          RETRIEVE(prefile, -1, ext, context);
05017       if (ast_fileexists(prefile, NULL, NULL) > 0) {
05018          if (ast_streamfile(chan, prefile, chan->language) > -1) 
05019             res = ast_waitstream(chan, ecodes);
05020 #ifdef ODBC_STORAGE
05021          if (success == -1) {
05022             /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
05023             ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
05024             store_file(prefile, vmu->mailbox, vmu->context, -1);
05025          }
05026 #endif
05027       } else {
05028          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
05029          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
05030       }
05031       DISPOSE(prefile, -1);
05032       if (res < 0) {
05033          ast_debug(1, "Hang up during prefile playback\n");
05034          free_user(vmu);
05035          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05036          return -1;
05037       }
05038    }
05039    if (res == '#') {
05040       /* On a '#' we skip the instructions */
05041       ast_set_flag(options, OPT_SILENT);
05042       res = 0;
05043    }
05044    if (!res && !ast_test_flag(options, OPT_SILENT)) {
05045       res = ast_stream_and_wait(chan, INTRO, ecodes);
05046       if (res == '#') {
05047          ast_set_flag(options, OPT_SILENT);
05048          res = 0;
05049       }
05050    }
05051    if (res > 0)
05052       ast_stopstream(chan);
05053    /* Check for a '*' here in case the caller wants to escape from voicemail to something
05054     other than the operator -- an automated attendant or mailbox login for example */
05055    if (res == '*') {
05056       chan->exten[0] = 'a';
05057       chan->exten[1] = '\0';
05058       if (!ast_strlen_zero(vmu->exit)) {
05059          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05060       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
05061          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05062       }
05063       chan->priority = 0;
05064       free_user(vmu);
05065       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05066       return 0;
05067    }
05068 
05069    /* Check for a '0' here */
05070    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
05071    transfer:
05072       if (ouseexten || ousemacro) {
05073          chan->exten[0] = 'o';
05074          chan->exten[1] = '\0';
05075          if (!ast_strlen_zero(vmu->exit)) {
05076             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
05077          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
05078             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
05079          }
05080          ast_play_and_wait(chan, "transfer");
05081          chan->priority = 0;
05082          free_user(vmu);
05083          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05084       }
05085       return 0;
05086    }
05087 
05088    /* Allow all other digits to exit Voicemail and return to the dialplan */
05089    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
05090       if (!ast_strlen_zero(options->exitcontext))
05091          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
05092       free_user(vmu);
05093       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
05094       return res;
05095    }
05096 
05097    if (res < 0) {
05098       free_user(vmu);
05099       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05100       return -1;
05101    }
05102    /* The meat of recording the message...  All the announcements and beeps have been played*/
05103    ast_copy_string(fmt, vmfmts, sizeof(fmt));
05104    if (!ast_strlen_zero(fmt)) {
05105       msgnum = 0;
05106 
05107 #ifdef IMAP_STORAGE
05108       /* Is ext a mailbox? */
05109       /* must open stream for this user to get info! */
05110       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
05111       if (res < 0) {
05112          ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
05113          return -1;
05114       }
05115       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
05116       /* It is possible under certain circumstances that inboxcount did not
05117        * create a vm_state when it was needed. This is a catchall which will
05118        * rarely be used.
05119        */
05120          if (!(vms = create_vm_state_from_user(vmu))) {
05121             ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
05122             return -1;
05123          }
05124       }
05125       vms->newmessages++;
05126       
05127       /* here is a big difference! We add one to it later */
05128       msgnum = newmsgs + oldmsgs;
05129       ast_debug(3, "Messagecount set to %d\n",msgnum);
05130       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
05131       /* set variable for compatibility */
05132       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05133 
05134       /* Check if mailbox is full */
05135       check_quota(vms, imapfolder);
05136       if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
05137          ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
05138          ast_play_and_wait(chan, "vm-mailboxfull");
05139          return -1;
05140       }
05141       
05142       /* Check if we have exceeded maxmsg */
05143       if (msgnum >= vmu->maxmsg) {
05144          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);
05145          ast_play_and_wait(chan, "vm-mailboxfull");
05146          return -1;
05147       }
05148 #else
05149       if (count_messages(vmu, dir) >= vmu->maxmsg) {
05150          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05151          if (!res)
05152             res = ast_waitstream(chan, "");
05153          ast_log(AST_LOG_WARNING, "No more messages possible\n");
05154          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05155          goto leave_vm_out;
05156       }
05157 
05158 #endif
05159       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
05160       txtdes = mkstemp(tmptxtfile);
05161       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
05162       if (txtdes < 0) {
05163          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
05164          if (!res)
05165             res = ast_waitstream(chan, "");
05166          ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
05167          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05168          goto leave_vm_out;
05169       }
05170 
05171       /* Now play the beep once we have the message number for our next message. */
05172       if (res >= 0) {
05173          /* Unless we're *really* silent, try to send the beep */
05174          res = ast_stream_and_wait(chan, "beep", "");
05175       }
05176             
05177       /* Store information in real-time storage */
05178       if (ast_check_realtime("voicemail_data")) {
05179          snprintf(priority, sizeof(priority), "%d", chan->priority);
05180          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
05181          get_date(date, sizeof(date));
05182          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);
05183       }
05184 
05185       /* Store information */
05186       txt = fdopen(txtdes, "w+");
05187       if (txt) {
05188          get_date(date, sizeof(date));
05189          fprintf(txt, 
05190             ";\n"
05191             "; Message Information file\n"
05192             ";\n"
05193             "[message]\n"
05194             "origmailbox=%s\n"
05195             "context=%s\n"
05196             "macrocontext=%s\n"
05197             "exten=%s\n"
05198             "priority=%d\n"
05199             "callerchan=%s\n"
05200             "callerid=%s\n"
05201             "origdate=%s\n"
05202             "origtime=%ld\n"
05203             "category=%s\n",
05204             ext,
05205             chan->context,
05206             chan->macrocontext, 
05207             chan->exten,
05208             chan->priority,
05209             chan->name,
05210             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
05211             date, (long)time(NULL),
05212             category ? category : "");
05213       } else
05214          ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
05215       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
05216 
05217       if (txt) {
05218          fprintf(txt, "flag=%s\n", flag);
05219          if (duration < vmminsecs) {
05220             fclose(txt);
05221             if (option_verbose > 2) 
05222                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
05223             ast_filedelete(tmptxtfile, NULL);
05224             unlink(tmptxtfile);
05225             if (ast_check_realtime("voicemail_data")) {
05226                snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05227                ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05228             }
05229          } else {
05230             fprintf(txt, "duration=%d\n", duration);
05231             fclose(txt);
05232             if (vm_lock_path(dir)) {
05233                ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
05234                /* Delete files */
05235                ast_filedelete(tmptxtfile, NULL);
05236                unlink(tmptxtfile);
05237             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
05238                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
05239                unlink(tmptxtfile);
05240                ast_unlock_path(dir);
05241                if (ast_check_realtime("voicemail_data")) {
05242                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05243                   ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
05244                }
05245             } else {
05246 #ifndef IMAP_STORAGE
05247                msgnum = last_message_index(vmu, dir) + 1;
05248 #endif
05249                make_file(fn, sizeof(fn), dir, msgnum);
05250 
05251                /* assign a variable with the name of the voicemail file */ 
05252 #ifndef IMAP_STORAGE
05253                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
05254 #else
05255                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
05256 #endif
05257 
05258                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
05259                ast_filerename(tmptxtfile, fn, NULL);
05260                rename(tmptxtfile, txtfile);
05261 
05262                /* Properly set permissions on voicemail text descriptor file.
05263                   Unfortunately mkstemp() makes this file 0600 on most unix systems. */
05264                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
05265                   ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
05266 
05267                ast_unlock_path(dir);
05268                if (ast_check_realtime("voicemail_data")) {
05269                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
05270                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
05271                   ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
05272                }
05273                /* We must store the file first, before copying the message, because
05274                 * ODBC storage does the entire copy with SQL.
05275                 */
05276                if (ast_fileexists(fn, NULL, NULL) > 0) {
05277                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
05278                }
05279 
05280                /* Are there to be more recipients of this message? */
05281                while (tmpptr) {
05282                   struct ast_vm_user recipu, *recip;
05283                   char *exten, *cntx;
05284                
05285                   exten = strsep(&tmpptr, "&");
05286                   cntx = strchr(exten, '@');
05287                   if (cntx) {
05288                      *cntx = '\0';
05289                      cntx++;
05290                   }
05291                   if ((recip = find_user(&recipu, cntx, exten))) {
05292                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
05293                      free_user(recip);
05294                   }
05295                }
05296 #ifndef IMAP_STORAGE
05297                if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
05298                   /* Move the message from INBOX to Urgent folder if this is urgent! */
05299                   char sfn[PATH_MAX];
05300                   char dfn[PATH_MAX];
05301                   int x;
05302                   /* It's easier just to try to make it than to check for its existence */
05303                   create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
05304                   ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n",sfn,dfn);
05305                   x = last_message_index(vmu, urgdir) + 1;
05306                   make_file(sfn, sizeof(sfn), dir, msgnum);
05307                   make_file(dfn, sizeof(dfn), urgdir, x);
05308                   RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
05309                }
05310 #endif
05311                /* Notification needs to happen after the copy, though. */
05312                if (ast_fileexists(fn, NULL, NULL)) {
05313 #ifdef IMAP_STORAGE
05314                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05315 #else
05316                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
05317 #endif
05318                }
05319 
05320                /* Disposal needs to happen after the optional move and copy */
05321                if (ast_fileexists(fn, NULL, NULL)) {
05322                   DISPOSE(dir, msgnum);
05323                }
05324             }
05325          }
05326       }
05327       if (res == '0') {
05328          goto transfer;
05329       } else if (res > 0)
05330          res = 0;
05331 
05332       if (duration < vmminsecs)
05333          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
05334          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05335       else
05336          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
05337    } else
05338       ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
05339 leave_vm_out:
05340    free_user(vmu);
05341 
05342 #ifdef IMAP_STORAGE
05343    /* expunge message - use UID Expunge if supported on IMAP server*/
05344    ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
05345    if (expungeonhangup == 1) {
05346       ast_mutex_lock(&vms->lock);
05347 #ifdef HAVE_IMAP_TK2006
05348       if (LEVELUIDPLUS (vms->mailstream)) {
05349          mail_expunge_full(vms->mailstream,NIL,EX_UID);
05350       } else 
05351 #endif
05352          mail_expunge(vms->mailstream);
05353       ast_mutex_unlock(&vms->lock);
05354    }
05355 #endif
05356    
05357    return res;
05358 }
05359 
05360 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
05361 {
05362    int d;
05363    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
05364    return d;
05365 }
05366 
05367 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
05368 {
05369 #ifdef IMAP_STORAGE
05370    /* we must use mbox(x) folder names, and copy the message there */
05371    /* simple. huh? */
05372    char sequence[10];
05373    char mailbox[256];
05374    int res;
05375 
05376    /* get the real IMAP message number for this message */
05377    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
05378    
05379    ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
05380    ast_mutex_lock(&vms->lock);
05381    /* if save to Old folder, put in INBOX as read */
05382    if (box == OLD_FOLDER) {
05383       mail_setflag(vms->mailstream, sequence, "\\Seen");
05384       mail_clearflag(vms->mailstream, sequence, "\\Unseen");
05385    } else if (box == NEW_FOLDER) {
05386       mail_setflag(vms->mailstream, sequence, "\\Unseen");
05387       mail_clearflag(vms->mailstream, sequence, "\\Seen");
05388    }
05389    if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
05390       ast_mutex_unlock(&vms->lock);
05391       return 0;
05392    }
05393    /* Create the folder if it don't exist */
05394    imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
05395    ast_debug(5, "Checking if folder exists: %s\n",mailbox);
05396    if (mail_create(vms->mailstream, mailbox) == NIL) 
05397       ast_debug(5, "Folder exists.\n");
05398    else
05399       ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
05400    res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
05401    ast_mutex_unlock(&vms->lock);
05402    return res;
05403 #else
05404    char *dir = vms->curdir;
05405    char *username = vms->username;
05406    char *context = vmu->context;
05407    char sfn[PATH_MAX];
05408    char dfn[PATH_MAX];
05409    char ddir[PATH_MAX];
05410    const char *dbox = mbox(box);
05411    int x, i;
05412    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
05413 
05414    if (vm_lock_path(ddir))
05415       return ERROR_LOCK_PATH;
05416 
05417    x = last_message_index(vmu, ddir) + 1;
05418 
05419    if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
05420       x--;
05421       for (i = 1; i <= x; i++) {
05422          /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
05423          make_file(sfn, sizeof(sfn), ddir, i);
05424          make_file(dfn, sizeof(dfn), ddir, i - 1);
05425          if (EXISTS(ddir, i, sfn, NULL)) {
05426             RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
05427          } else
05428             break;
05429       }
05430    } else {
05431       if (x >= vmu->maxmsg) {
05432          ast_unlock_path(ddir);
05433          return -1;
05434       }
05435    }
05436    make_file(sfn, sizeof(sfn), dir, msg);
05437    make_file(dfn, sizeof(dfn), ddir, x);
05438    if (strcmp(sfn, dfn)) {
05439       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
05440    }
05441    ast_unlock_path(ddir);
05442 #endif
05443    return 0;
05444 }
05445 
05446 static int adsi_logo(unsigned char *buf)
05447 {
05448    int bytes = 0;
05449    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
05450    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
05451    return bytes;
05452 }
05453 
05454 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
05455 {
05456    unsigned char buf[256];
05457    int bytes=0;
05458    int x;
05459    char num[5];
05460 
05461    *useadsi = 0;
05462    bytes += ast_adsi_data_mode(buf + bytes);
05463    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05464 
05465    bytes = 0;
05466    bytes += adsi_logo(buf);
05467    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05468 #ifdef DISPLAY
05469    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
05470 #endif
05471    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05472    bytes += ast_adsi_data_mode(buf + bytes);
05473    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05474 
05475    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
05476       bytes = 0;
05477       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
05478       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05479       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05480       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05481       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05482       return 0;
05483    }
05484 
05485 #ifdef DISPLAY
05486    /* Add a dot */
05487    bytes = 0;
05488    bytes += ast_adsi_logo(buf);
05489    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
05490    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
05491    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05492    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05493 #endif
05494    bytes = 0;
05495    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
05496    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
05497    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
05498    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
05499    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
05500    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
05501    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05502 
05503 #ifdef DISPLAY
05504    /* Add another dot */
05505    bytes = 0;
05506    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
05507    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05508 
05509    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05510    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05511 #endif
05512 
05513    bytes = 0;
05514    /* These buttons we load but don't use yet */
05515    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
05516    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
05517    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
05518    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
05519    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
05520    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
05521    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05522 
05523 #ifdef DISPLAY
05524    /* Add another dot */
05525    bytes = 0;
05526    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
05527    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05528    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05529 #endif
05530 
05531    bytes = 0;
05532    for (x=0;x<5;x++) {
05533       snprintf(num, sizeof(num), "%d", x);
05534       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
05535    }
05536    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
05537    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05538 
05539 #ifdef DISPLAY
05540    /* Add another dot */
05541    bytes = 0;
05542    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
05543    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05544    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05545 #endif
05546 
05547    if (ast_adsi_end_download(chan)) {
05548       bytes = 0;
05549       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
05550       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
05551       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05552       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05553       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05554       return 0;
05555    }
05556    bytes = 0;
05557    bytes += ast_adsi_download_disconnect(buf + bytes);
05558    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05559    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
05560 
05561    ast_debug(1, "Done downloading scripts...\n");
05562 
05563 #ifdef DISPLAY
05564    /* Add last dot */
05565    bytes = 0;
05566    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
05567    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05568 #endif
05569    ast_debug(1, "Restarting session...\n");
05570 
05571    bytes = 0;
05572    /* Load the session now */
05573    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
05574       *useadsi = 1;
05575       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
05576    } else
05577       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
05578 
05579    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05580    return 0;
05581 }
05582 
05583 static void adsi_begin(struct ast_channel *chan, int *useadsi)
05584 {
05585    int x;
05586    if (!ast_adsi_available(chan))
05587       return;
05588    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
05589    if (x < 0)
05590       return;
05591    if (!x) {
05592       if (adsi_load_vmail(chan, useadsi)) {
05593          ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
05594          return;
05595       }
05596    } else
05597       *useadsi = 1;
05598 }
05599 
05600 static void adsi_login(struct ast_channel *chan)
05601 {
05602    unsigned char buf[256];
05603    int bytes=0;
05604    unsigned char keys[8];
05605    int x;
05606    if (!ast_adsi_available(chan))
05607       return;
05608 
05609    for (x=0;x<8;x++)
05610       keys[x] = 0;
05611    /* Set one key for next */
05612    keys[3] = ADSI_KEY_APPS + 3;
05613 
05614    bytes += adsi_logo(buf + bytes);
05615    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
05616    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
05617    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05618    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
05619    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05620    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05621    bytes += ast_adsi_set_keys(buf + bytes, keys);
05622    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05623    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05624 }
05625 
05626 static void adsi_password(struct ast_channel *chan)
05627 {
05628    unsigned char buf[256];
05629    int bytes=0;
05630    unsigned char keys[8];
05631    int x;
05632    if (!ast_adsi_available(chan))
05633       return;
05634 
05635    for (x=0;x<8;x++)
05636       keys[x] = 0;
05637    /* Set one key for next */
05638    keys[3] = ADSI_KEY_APPS + 3;
05639 
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, "Password: ******", "");
05642    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05643    bytes += ast_adsi_set_keys(buf + bytes, keys);
05644    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05645    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05646 }
05647 
05648 static void adsi_folders(struct ast_channel *chan, int start, char *label)
05649 {
05650    unsigned char buf[256];
05651    int bytes=0;
05652    unsigned char keys[8];
05653    int x,y;
05654 
05655    if (!ast_adsi_available(chan))
05656       return;
05657 
05658    for (x=0;x<5;x++) {
05659       y = ADSI_KEY_APPS + 12 + start + x;
05660       if (y > ADSI_KEY_APPS + 12 + 4)
05661          y = 0;
05662       keys[x] = ADSI_KEY_SKT | y;
05663    }
05664    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05665    keys[6] = 0;
05666    keys[7] = 0;
05667 
05668    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05669    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
05670    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05671    bytes += ast_adsi_set_keys(buf + bytes, keys);
05672    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05673 
05674    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05675 }
05676 
05677 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
05678 {
05679    int bytes=0;
05680    unsigned char buf[256]; 
05681    char buf1[256], buf2[256];
05682    char fn2[PATH_MAX];
05683 
05684    char cid[256]="";
05685    char *val;
05686    char *name, *num;
05687    char datetime[21]="";
05688    FILE *f;
05689 
05690    unsigned char keys[8];
05691 
05692    int x;
05693 
05694    if (!ast_adsi_available(chan))
05695       return;
05696 
05697    /* Retrieve important info */
05698    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
05699    f = fopen(fn2, "r");
05700    if (f) {
05701       while (!feof(f)) {   
05702          if (!fgets((char *)buf, sizeof(buf), f)) {
05703             continue;
05704          }
05705          if (!feof(f)) {
05706             char *stringp=NULL;
05707             stringp = (char *)buf;
05708             strsep(&stringp, "=");
05709             val = strsep(&stringp, "=");
05710             if (!ast_strlen_zero(val)) {
05711                if (!strcmp((char *)buf, "callerid"))
05712                   ast_copy_string(cid, val, sizeof(cid));
05713                if (!strcmp((char *)buf, "origdate"))
05714                   ast_copy_string(datetime, val, sizeof(datetime));
05715             }
05716          }
05717       }
05718       fclose(f);
05719    }
05720    /* New meaning for keys */
05721    for (x=0;x<5;x++)
05722       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05723    keys[6] = 0x0;
05724    keys[7] = 0x0;
05725 
05726    if (!vms->curmsg) {
05727       /* No prev key, provide "Folder" instead */
05728       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05729    }
05730    if (vms->curmsg >= vms->lastmsg) {
05731       /* If last message ... */
05732       if (vms->curmsg) {
05733          /* but not only message, provide "Folder" instead */
05734          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05735          bytes += ast_adsi_voice_mode(buf + bytes, 0);
05736 
05737       } else {
05738          /* Otherwise if only message, leave blank */
05739          keys[3] = 1;
05740       }
05741    }
05742 
05743    if (!ast_strlen_zero(cid)) {
05744       ast_callerid_parse(cid, &name, &num);
05745       if (!name)
05746          name = num;
05747    } else
05748       name = "Unknown Caller";
05749 
05750    /* If deleted, show "undeleted" */
05751 
05752    if (vms->deleted[vms->curmsg])
05753       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05754 
05755    /* Except "Exit" */
05756    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05757    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05758       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05759    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05760 
05761    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05762    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05763    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05764    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05765    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05766    bytes += ast_adsi_set_keys(buf + bytes, keys);
05767    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05768 
05769    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05770 }
05771 
05772 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05773 {
05774    int bytes=0;
05775    unsigned char buf[256];
05776    unsigned char keys[8];
05777 
05778    int x;
05779 
05780    if (!ast_adsi_available(chan))
05781       return;
05782 
05783    /* New meaning for keys */
05784    for (x=0;x<5;x++)
05785       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05786 
05787    keys[6] = 0x0;
05788    keys[7] = 0x0;
05789 
05790    if (!vms->curmsg) {
05791       /* No prev key, provide "Folder" instead */
05792       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05793    }
05794    if (vms->curmsg >= vms->lastmsg) {
05795       /* If last message ... */
05796       if (vms->curmsg) {
05797          /* but not only message, provide "Folder" instead */
05798          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05799       } else {
05800          /* Otherwise if only message, leave blank */
05801          keys[3] = 1;
05802       }
05803    }
05804 
05805    /* If deleted, show "undeleted" */
05806    if (vms->deleted[vms->curmsg]) 
05807       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05808 
05809    /* Except "Exit" */
05810    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05811    bytes += ast_adsi_set_keys(buf + bytes, keys);
05812    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05813 
05814    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05815 }
05816 
05817 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05818 {
05819    unsigned char buf[256] = "";
05820    char buf1[256] = "", buf2[256] = "";
05821    int bytes=0;
05822    unsigned char keys[8];
05823    int x;
05824 
05825    char *newm = (vms->newmessages == 1) ? "message" : "messages";
05826    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05827    if (!ast_adsi_available(chan))
05828       return;
05829    if (vms->newmessages) {
05830       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05831       if (vms->oldmessages) {
05832          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05833          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05834       } else {
05835          snprintf(buf2, sizeof(buf2), "%s.", newm);
05836       }
05837    } else if (vms->oldmessages) {
05838       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05839       snprintf(buf2, sizeof(buf2), "%s.", oldm);
05840    } else {
05841       strcpy(buf1, "You have no messages.");
05842       buf2[0] = ' ';
05843       buf2[1] = '\0';
05844    }
05845    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05846    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05847    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05848 
05849    for (x=0;x<6;x++)
05850       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05851    keys[6] = 0;
05852    keys[7] = 0;
05853 
05854    /* Don't let them listen if there are none */
05855    if (vms->lastmsg < 0)
05856       keys[0] = 1;
05857    bytes += ast_adsi_set_keys(buf + bytes, keys);
05858 
05859    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05860 
05861    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05862 }
05863 
05864 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05865 {
05866    unsigned char buf[256] = "";
05867    char buf1[256] = "", buf2[256] = "";
05868    int bytes=0;
05869    unsigned char keys[8];
05870    int x;
05871 
05872    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05873 
05874    if (!ast_adsi_available(chan))
05875       return;
05876 
05877    /* Original command keys */
05878    for (x=0;x<6;x++)
05879       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05880 
05881    keys[6] = 0;
05882    keys[7] = 0;
05883 
05884    if ((vms->lastmsg + 1) < 1)
05885       keys[0] = 0;
05886 
05887    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05888       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05889 
05890    if (vms->lastmsg + 1)
05891       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05892    else
05893       strcpy(buf2, "no messages.");
05894    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05895    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05896    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05897    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05898    bytes += ast_adsi_set_keys(buf + bytes, keys);
05899 
05900    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05901 
05902    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05903    
05904 }
05905 
05906 /*
05907 static void adsi_clear(struct ast_channel *chan)
05908 {
05909    char buf[256];
05910    int bytes=0;
05911    if (!ast_adsi_available(chan))
05912       return;
05913    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05914    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05915 
05916    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05917 }
05918 */
05919 
05920 static void adsi_goodbye(struct ast_channel *chan)
05921 {
05922    unsigned char buf[256];
05923    int bytes=0;
05924 
05925    if (!ast_adsi_available(chan))
05926       return;
05927    bytes += adsi_logo(buf + bytes);
05928    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
05929    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
05930    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05931    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05932 
05933    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05934 }
05935 
05936 /*!\brief get_folder: Folder menu
05937  * Plays "press 1 for INBOX messages" etc.
05938  * Should possibly be internationalized
05939  */
05940 static int get_folder(struct ast_channel *chan, int start)
05941 {
05942    int x;
05943    int d;
05944    char fn[PATH_MAX];
05945    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
05946    if (d)
05947       return d;
05948    for (x = start; x< 5; x++) {  /* For all folders */
05949       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
05950          return d;
05951       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
05952       if (d)
05953          return d;
05954       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
05955       d = vm_play_folder_name(chan, fn);
05956       if (d)
05957          return d;
05958       d = ast_waitfordigit(chan, 500);
05959       if (d)
05960          return d;
05961    }
05962    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
05963    if (d)
05964       return d;
05965    d = ast_waitfordigit(chan, 4000);
05966    return d;
05967 }
05968 
05969 /*!
05970  * \brief plays a prompt and waits for a keypress.
05971  * \param chan
05972  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
05973  * \param start Does not appear to be used at this time.
05974  *
05975  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
05976  * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
05977  * prompting for the number inputs that correspond to the available folders.
05978  * 
05979  * \return zero on success, or -1 on error.
05980  */
05981 static int get_folder2(struct ast_channel *chan, char *fn, int start)
05982 {
05983    int res = 0;
05984    res = ast_play_and_wait(chan, fn);  /* Folder name */
05985    while (((res < '0') || (res > '9')) &&
05986          (res != '#') && (res >= 0)) {
05987       res = get_folder(chan, 0);
05988    }
05989    return res;
05990 }
05991 
05992 /*!
05993  * \brief presents the option to prepend to an existing message when forwarding it.
05994  * \param chan
05995  * \param vmu
05996  * \param curdir
05997  * \param curmsg
05998  * \param vmfmts
05999  * \param context
06000  * \param record_gain
06001  * \param duration
06002  * \param vms
06003  *
06004  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
06005  *
06006  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
06007  * \return zero on success, -1 on error.
06008  */
06009 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
06010          char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
06011 {
06012 #ifdef IMAP_STORAGE
06013    int res;
06014 #endif
06015    int cmd = 0;
06016    int retries = 0, prepend_duration = 0, already_recorded = 0;
06017    char msgfile[PATH_MAX], backup[PATH_MAX];
06018    char textfile[PATH_MAX];
06019    struct ast_config *msg_cfg;
06020    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06021 #ifndef IMAP_STORAGE
06022    signed char zero_gain = 0;
06023 #endif
06024    const char *duration_str;
06025 
06026    /* Must always populate duration correctly */
06027    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06028    strcpy(textfile, msgfile);
06029    strcpy(backup, msgfile);
06030    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
06031    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
06032 
06033    if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
06034       *duration = atoi(duration_str);
06035    } else {
06036       *duration = 0;
06037    }
06038 
06039    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
06040       if (cmd)
06041          retries = 0;
06042       switch (cmd) {
06043       case '1': 
06044 
06045 #ifdef IMAP_STORAGE
06046          /* Record new intro file */
06047          make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
06048          strncat(vms->introfn, "intro", sizeof(vms->introfn));
06049          res = ast_play_and_wait(chan, INTRO);
06050          res = ast_play_and_wait(chan, "beep");
06051          res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
06052          cmd = 't';
06053 #else
06054 
06055          /* prepend a message to the current message, update the metadata and return */
06056 
06057          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
06058          strcpy(textfile, msgfile);
06059          strncat(textfile, ".txt", sizeof(textfile) - 1);
06060          *duration = 0;
06061 
06062          /* if we can't read the message metadata, stop now */
06063          if (!msg_cfg) {
06064             cmd = 0;
06065             break;
06066          }
06067          
06068          /* Back up the original file, so we can retry the prepend */
06069          if (already_recorded)
06070             ast_filecopy(backup, msgfile, NULL);
06071          else
06072             ast_filecopy(msgfile, backup, NULL);
06073          already_recorded = 1;
06074 
06075          if (record_gain)
06076             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06077 
06078          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
06079          if (record_gain)
06080             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06081 
06082          
06083          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
06084             *duration = atoi(duration_str);
06085 
06086          if (prepend_duration) {
06087             struct ast_category *msg_cat;
06088             /* need enough space for a maximum-length message duration */
06089             char duration_buf[12];
06090 
06091             *duration += prepend_duration;
06092             msg_cat = ast_category_get(msg_cfg, "message");
06093             snprintf(duration_buf, 11, "%ld", *duration);
06094             if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
06095                config_text_file_save(textfile, msg_cfg, "app_voicemail");
06096             }
06097          }
06098 
06099 #endif
06100          break;
06101       case '2': 
06102          /* NULL out introfile so we know there is no intro! */
06103 #ifdef IMAP_STORAGE
06104          *vms->introfn = '\0';
06105 #endif
06106          cmd = 't';
06107          break;
06108       case '*':
06109          cmd = '*';
06110          break;
06111       default: 
06112          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
06113             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
06114          if (!cmd)
06115             cmd = ast_play_and_wait(chan,"vm-starmain");
06116             /* "press star to return to the main menu" */
06117          if (!cmd)
06118             cmd = ast_waitfordigit(chan,6000);
06119          if (!cmd)
06120             retries++;
06121          if (retries > 3)
06122             cmd = 't';
06123       }
06124    }
06125 
06126    if (msg_cfg)
06127       ast_config_destroy(msg_cfg);
06128    if (already_recorded)
06129       ast_filedelete(backup, NULL);
06130    if (prepend_duration)
06131       *duration = prepend_duration;
06132 
06133    if (cmd == 't' || cmd == 'S')
06134       cmd = 0;
06135    return cmd;
06136 }
06137 
06138 static void queue_mwi_event(const char *box, int urgent, int new, int old)
06139 {
06140    struct ast_event *event;
06141    char *mailbox, *context;
06142 
06143    /* Strip off @default */
06144    context = mailbox = ast_strdupa(box);
06145    strsep(&context, "@");
06146    if (ast_strlen_zero(context))
06147       context = "default";
06148 
06149    if (!(event = ast_event_new(AST_EVENT_MWI,
06150          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
06151          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
06152          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
06153          AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
06154          AST_EVENT_IE_END))) {
06155       return;
06156    }
06157 
06158    ast_event_queue_and_cache(event);
06159 }
06160 
06161 /*!
06162  * \brief Sends email notification that a user has a new voicemail waiting for them.
06163  * \param chan
06164  * \param vmu
06165  * \param vms
06166  * \param msgnum
06167  * \param duration
06168  * \param fmt
06169  * \param cidnum The Caller ID phone number value.
06170  * \param cidname The Caller ID name value.
06171  *
06172  * \return zero on success, -1 on error.
06173  */
06174 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)
06175 {
06176    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
06177    int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
06178    const char *category;
06179    char *myserveremail = serveremail;
06180 
06181    ast_channel_lock(chan);
06182    if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
06183       category = ast_strdupa(category);
06184    }
06185    ast_channel_unlock(chan);
06186 
06187    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
06188    make_file(fn, sizeof(fn), todir, msgnum);
06189    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
06190 
06191    if (!ast_strlen_zero(vmu->attachfmt)) {
06192       if (strstr(fmt, vmu->attachfmt))
06193          fmt = vmu->attachfmt;
06194       else
06195          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);
06196    }
06197 
06198    /* Attach only the first format */
06199    fmt = ast_strdupa(fmt);
06200    stringp = fmt;
06201    strsep(&stringp, "|");
06202 
06203    if (!ast_strlen_zero(vmu->serveremail))
06204       myserveremail = vmu->serveremail;
06205 
06206    if (!ast_strlen_zero(vmu->email)) {
06207       int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
06208       if (!attach_user_voicemail)
06209          attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
06210 
06211       if (attach_user_voicemail)
06212          RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
06213 
06214       /* XXX possible imap issue, should category be NULL XXX */
06215       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
06216 
06217       if (attach_user_voicemail)
06218          DISPOSE(todir, msgnum);
06219    }
06220 
06221    if (!ast_strlen_zero(vmu->pager)) {
06222       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
06223    }
06224 
06225    if (ast_test_flag(vmu, VM_DELETE))
06226       DELETE(todir, msgnum, fn, vmu);
06227 
06228    /* Leave voicemail for someone */
06229    if (ast_app_has_voicemail(ext_context, NULL)) 
06230       ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
06231 
06232    queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
06233 
06234    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);
06235    run_externnotify(vmu->context, vmu->mailbox, flag);
06236 
06237 #ifdef IMAP_STORAGE
06238    vm_delete(fn);  /* Delete the file, but not the IMAP message */
06239    if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
06240       vm_imap_delete(vms->curmsg, vmu);
06241       vms->newmessages--;  /* Fix new message count */
06242    }
06243 #endif
06244 
06245    return 0;
06246 }
06247 
06248 /*!
06249  * \brief Sends a voicemail message to a mailbox recipient.
06250  * \param ast_channel
06251  * \param context
06252  * \param vms
06253  * \param sender
06254  * \param fmt
06255  * \param is_new_message Used to indicate the mode for which this method was invoked. 
06256  *             Will be 0 when called to forward an existing message (option 8)
06257  *             Will be 1 when called to leave a message (option 3->5)
06258  * \param record_gain 
06259  *
06260  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
06261  * 
06262  * When in the leave message mode (is_new_message == 1):
06263  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
06264  *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
06265  *
06266  * When in the forward message mode (is_new_message == 0):
06267  *   - retreives the current message to be forwarded
06268  *   - copies the original message to a temporary file, so updates to the envelope can be done.
06269  *   - determines the target mailbox and folders
06270  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
06271  *
06272  * \return zero on success, -1 on error.
06273  */
06274 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)
06275 {
06276 #ifdef IMAP_STORAGE
06277    int todircount=0;
06278    struct vm_state *dstvms;
06279 #endif
06280    char username[70]="";
06281    char fn[PATH_MAX]; /* for playback of name greeting */
06282    char ecodes[16] = "#";
06283    int res = 0, cmd = 0;
06284    struct ast_vm_user *receiver = NULL, *vmtmp;
06285    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
06286    char *stringp;
06287    const char *s;
06288    int saved_messages = 0, found = 0;
06289    int valid_extensions = 0;
06290    char *dir;
06291    int curmsg;
06292    char urgent_str[7] = "";
06293    char tmptxtfile[PATH_MAX];
06294 
06295    if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
06296       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
06297    }
06298 
06299    if (vms == NULL) return -1;
06300    dir = vms->curdir;
06301    curmsg = vms->curmsg;
06302 
06303    tmptxtfile[0] = '\0';
06304    while (!res && !valid_extensions) {
06305       int use_directory = 0;
06306       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
06307          int done = 0;
06308          int retries = 0;
06309          cmd=0;
06310          while ((cmd >= 0) && !done ){
06311             if (cmd)
06312                retries = 0;
06313             switch (cmd) {
06314             case '1': 
06315                use_directory = 0;
06316                done = 1;
06317                break;
06318             case '2': 
06319                use_directory = 1;
06320                done=1;
06321                break;
06322             case '*': 
06323                cmd = 't';
06324                done = 1;
06325                break;
06326             default: 
06327                /* Press 1 to enter an extension press 2 to use the directory */
06328                cmd = ast_play_and_wait(chan,"vm-forward");
06329                if (!cmd)
06330                   cmd = ast_waitfordigit(chan,3000);
06331                if (!cmd)
06332                   retries++;
06333                if (retries > 3) {
06334                   cmd = 't';
06335                   done = 1;
06336                }
06337                
06338             }
06339          }
06340          if (cmd < 0 || cmd == 't')
06341             break;
06342       }
06343       
06344       if (use_directory) {
06345          /* use app_directory */
06346          
06347          char old_context[sizeof(chan->context)];
06348          char old_exten[sizeof(chan->exten)];
06349          int old_priority;
06350          struct ast_app* directory_app;
06351 
06352          directory_app = pbx_findapp("Directory");
06353          if (directory_app) {
06354             char vmcontext[256];
06355             /* make backup copies */
06356             memcpy(old_context, chan->context, sizeof(chan->context));
06357             memcpy(old_exten, chan->exten, sizeof(chan->exten));
06358             old_priority = chan->priority;
06359             
06360             /* call the the Directory, changes the channel */
06361             snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
06362             res = pbx_exec(chan, directory_app, vmcontext);
06363             
06364             ast_copy_string(username, chan->exten, sizeof(username));
06365             
06366             /* restore the old context, exten, and priority */
06367             memcpy(chan->context, old_context, sizeof(chan->context));
06368             memcpy(chan->exten, old_exten, sizeof(chan->exten));
06369             chan->priority = old_priority;
06370          } else {
06371             ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
06372             ast_clear_flag((&globalflags), VM_DIRECFORWARD);
06373          }
06374       } else {
06375          /* Ask for an extension */
06376          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
06377          if (res)
06378             break;
06379          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
06380             break;
06381       }
06382       
06383       /* start all over if no username */
06384       if (ast_strlen_zero(username))
06385          continue;
06386       stringp = username;
06387       s = strsep(&stringp, "*");
06388       /* start optimistic */
06389       valid_extensions = 1;
06390       while (s) {
06391          if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
06392             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
06393             found++;
06394          } else {
06395             /* XXX Optimization for the future.  When we encounter a single bad extension,
06396              * bailing out on all of the extensions may not be the way to go.  We should
06397              * probably just bail on that single extension, then allow the user to enter
06398              * several more. XXX
06399              */
06400             while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
06401                free_user(receiver);
06402             }
06403             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
06404             valid_extensions = 0;
06405             break;
06406          }
06407 
06408          /* play name if available, else play extension number */
06409          snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
06410          RETRIEVE(fn, -1, s, receiver->context);
06411          if (ast_fileexists(fn, NULL, NULL) > 0) {
06412             res = ast_stream_and_wait(chan, fn, ecodes);
06413             if (res) {
06414                DISPOSE(fn, -1);
06415                return res;
06416             }
06417          } else {
06418             res = ast_say_digit_str(chan, s, ecodes, chan->language);
06419          }
06420          DISPOSE(fn, -1);
06421 
06422          s = strsep(&stringp, "*");
06423       }
06424       /* break from the loop of reading the extensions */
06425       if (valid_extensions)
06426          break;
06427       /* "I am sorry, that's not a valid extension.  Please try again." */
06428       res = ast_play_and_wait(chan, "pbx-invalid");
06429    }
06430    /* check if we're clear to proceed */
06431    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
06432       return res;
06433    if (is_new_message == 1) {
06434       struct leave_vm_options leave_options;
06435       char mailbox[AST_MAX_EXTENSION * 2 + 2];
06436       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
06437 
06438       /* Send VoiceMail */
06439       memset(&leave_options, 0, sizeof(leave_options));
06440       leave_options.record_gain = record_gain;
06441       cmd = leave_voicemail(chan, mailbox, &leave_options);
06442    } else {
06443       /* Forward VoiceMail */
06444       long duration = 0;
06445       struct vm_state vmstmp;
06446       memcpy(&vmstmp, vms, sizeof(vmstmp));
06447 
06448       RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
06449 
06450       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
06451       if (!cmd) {
06452          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
06453 #ifdef IMAP_STORAGE
06454             int attach_user_voicemail;
06455             char *myserveremail = serveremail;
06456             
06457             /* get destination mailbox */
06458             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
06459             if (!dstvms) {
06460                dstvms = create_vm_state_from_user(vmtmp);
06461             }
06462             if (dstvms) {
06463                init_mailstream(dstvms, 0);
06464                if (!dstvms->mailstream) {
06465                   ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
06466                } else {
06467                   STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
06468                   run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
06469                }
06470             } else {
06471                ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
06472             }
06473             if (!ast_strlen_zero(vmtmp->serveremail))
06474                myserveremail = vmtmp->serveremail;
06475             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
06476             /* NULL category for IMAP storage */
06477             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);
06478 #else
06479             copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
06480 #endif
06481             saved_messages++;
06482             AST_LIST_REMOVE_CURRENT(list);
06483             free_user(vmtmp);
06484             if (res)
06485                break;
06486          }
06487          AST_LIST_TRAVERSE_SAFE_END;
06488          if (saved_messages > 0) {
06489             /* give confirmation that the message was saved */
06490             /* commented out since we can't forward batches yet
06491             if (saved_messages == 1)
06492                res = ast_play_and_wait(chan, "vm-message");
06493             else
06494                res = ast_play_and_wait(chan, "vm-messages");
06495             if (!res)
06496                res = ast_play_and_wait(chan, "vm-saved"); */
06497 #ifdef IMAP_STORAGE
06498             /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
06499             if (ast_strlen_zero(vmstmp.introfn))
06500 #endif
06501             res = ast_play_and_wait(chan, "vm-msgsaved");
06502          }  
06503       }
06504       DISPOSE(dir, curmsg);
06505    }
06506 
06507    /* If anything failed above, we still have this list to free */
06508    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
06509       free_user(vmtmp);
06510    return res ? res : cmd;
06511 }
06512 
06513 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
06514 {
06515    int res;
06516    if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
06517       ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
06518    return res;
06519 }
06520 
06521 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
06522 {
06523    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);
06524 }
06525 
06526 static int play_message_category(struct ast_channel *chan, const char *category)
06527 {
06528    int res = 0;
06529 
06530    if (!ast_strlen_zero(category))
06531       res = ast_play_and_wait(chan, category);
06532 
06533    if (res) {
06534       ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
06535       res = 0;
06536    }
06537 
06538    return res;
06539 }
06540 
06541 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
06542 {
06543    int res = 0;
06544    struct vm_zone *the_zone = NULL;
06545    time_t t;
06546 
06547    if (ast_get_time_t(origtime, &t, 0, NULL)) {
06548       ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
06549       return 0;
06550    }
06551 
06552    /* Does this user have a timezone specified? */
06553    if (!ast_strlen_zero(vmu->zonetag)) {
06554       /* Find the zone in the list */
06555       struct vm_zone *z;
06556       AST_LIST_LOCK(&zones);
06557       AST_LIST_TRAVERSE(&zones, z, list) {
06558          if (!strcmp(z->name, vmu->zonetag)) {
06559             the_zone = z;
06560             break;
06561          }
06562       }
06563       AST_LIST_UNLOCK(&zones);
06564    }
06565 
06566 /* No internal variable parsing for now, so we'll comment it out for the time being */
06567 #if 0
06568    /* Set the DIFF_* variables */
06569    ast_localtime(&t, &time_now, NULL);
06570    tv_now = ast_tvnow();
06571    ast_localtime(&tv_now, &time_then, NULL);
06572 
06573    /* Day difference */
06574    if (time_now.tm_year == time_then.tm_year)
06575       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
06576    else
06577       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
06578    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
06579 
06580    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
06581 #endif
06582    if (the_zone) {
06583       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
06584    }
06585    else if (!strcasecmp(chan->language,"pl"))       /* POLISH syntax */
06586       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
06587    else if (!strcasecmp(chan->language,"se"))       /* SWEDISH syntax */
06588       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
06589    else if (!strcasecmp(chan->language,"no"))       /* NORWEGIAN syntax */
06590       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06591    else if (!strcasecmp(chan->language,"de"))       /* GERMAN syntax */
06592       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
06593    else if (!strcasecmp(chan->language,"nl"))      /* DUTCH syntax */
06594       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
06595    else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
06596       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);
06597    else if (!strcasecmp(chan->language,"gr"))
06598       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
06599    else if (!strcasecmp(chan->language,"pt_BR"))
06600       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);
06601    else if (!strcasecmp(chan->language,"tw"))      /* CHINESE (Taiwan) syntax */
06602       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);     
06603    else {
06604       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
06605    }
06606 #if 0
06607    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
06608 #endif
06609    return res;
06610 }
06611 
06612 
06613 
06614 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
06615 {
06616    int res = 0;
06617    int i;
06618    char *callerid, *name;
06619    char prefile[PATH_MAX] = "";
06620    
06621 
06622    /* If voicemail cid is not enabled, or we didn't get cid or context from
06623     * the attribute file, leave now.
06624     *
06625     * TODO Still need to change this so that if this function is called by the
06626     * message envelope (and someone is explicitly requesting to hear the CID),
06627     * it does not check to see if CID is enabled in the config file.
06628     */
06629    if ((cid == NULL)||(context == NULL))
06630       return res;
06631 
06632    /* Strip off caller ID number from name */
06633    ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
06634    ast_callerid_parse(cid, &name, &callerid);
06635    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
06636       /* Check for internal contexts and only */
06637       /* say extension when the call didn't come from an internal context in the list */
06638       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
06639          ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
06640          if ((strcmp(cidinternalcontexts[i], context) == 0))
06641             break;
06642       }
06643       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
06644          if (!res) {
06645             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
06646             if (!ast_strlen_zero(prefile)) {
06647             /* See if we can find a recorded name for this person instead of their extension number */
06648                if (ast_fileexists(prefile, NULL, NULL) > 0) {
06649                   ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
06650                   if (!callback)
06651                      res = wait_file2(chan, vms, "vm-from");
06652                   res = ast_stream_and_wait(chan, prefile, "");
06653                } else {
06654                   ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
06655                   /* Say "from extension" as one saying to sound smoother */
06656                   if (!callback)
06657                      res = wait_file2(chan, vms, "vm-from-extension");
06658                   res = ast_say_digit_str(chan, callerid, "", chan->language);
06659                }
06660             }
06661          }
06662       } else if (!res) {
06663          ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
06664          /* Since this is all nicely figured out, why not say "from phone number" in this case? */
06665          if (!callback)
06666             res = wait_file2(chan, vms, "vm-from-phonenumber");
06667          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
06668       }
06669    } else {
06670       /* Number unknown */
06671       ast_debug(1, "VM-CID: From an unknown number\n");
06672       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
06673       res = wait_file2(chan, vms, "vm-unknown-caller");
06674    }
06675    return res;
06676 }
06677 
06678 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
06679 {
06680    int res = 0;
06681    int durationm;
06682    int durations;
06683    /* Verify that we have a duration for the message */
06684    if (duration == NULL)
06685       return res;
06686 
06687    /* Convert from seconds to minutes */
06688    durations=atoi(duration);
06689    durationm=(durations / 60);
06690 
06691    ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
06692 
06693    if ((!res) && (durationm >= minduration)) {
06694       res = wait_file2(chan, vms, "vm-duration");
06695 
06696       /* POLISH syntax */
06697       if (!strcasecmp(chan->language, "pl")) {
06698          div_t num = div(durationm, 10);
06699 
06700          if (durationm == 1) {
06701             res = ast_play_and_wait(chan, "digits/1z");
06702             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
06703          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06704             if (num.rem == 2) {
06705                if (!num.quot) {
06706                   res = ast_play_and_wait(chan, "digits/2-ie");
06707                } else {
06708                   res = say_and_wait(chan, durationm - 2 , chan->language);
06709                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06710                }
06711             } else {
06712                res = say_and_wait(chan, durationm, chan->language);
06713             }
06714             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
06715          } else {
06716             res = say_and_wait(chan, durationm, chan->language);
06717             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
06718          }
06719       /* DEFAULT syntax */
06720       } else {
06721          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
06722          res = wait_file2(chan, vms, "vm-minutes");
06723       }
06724    }
06725    return res;
06726 }
06727 
06728 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
06729 {
06730    int res = 0;
06731    char filename[256], *cid;
06732    const char *origtime, *context, *category, *duration, *flag;
06733    struct ast_config *msg_cfg;
06734    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06735 
06736    vms->starting = 0; 
06737    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06738    adsi_message(chan, vms);
06739    if (!vms->curmsg)
06740       res = wait_file2(chan, vms, "vm-first");  /* "First" */
06741    else if (vms->curmsg == vms->lastmsg)
06742       res = wait_file2(chan, vms, "vm-last");      /* "last" */
06743 
06744    snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
06745    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
06746    msg_cfg = ast_config_load(filename, config_flags);
06747    if (!msg_cfg) {
06748       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06749       return 0;
06750    }
06751    flag = ast_variable_retrieve(msg_cfg, "message", "flag");
06752 
06753    /* Play the word urgent if we are listening to urgent messages */
06754    if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
06755       res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
06756    }
06757 
06758    if (!res) {
06759       /* POLISH syntax */
06760       if (!strcasecmp(chan->language, "pl")) { 
06761          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06762             int ten, one;
06763             char nextmsg[256];
06764             ten = (vms->curmsg + 1) / 10;
06765             one = (vms->curmsg + 1) % 10;
06766             
06767             if (vms->curmsg < 20) {
06768                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
06769                res = wait_file2(chan, vms, nextmsg);
06770             } else {
06771                snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
06772                res = wait_file2(chan, vms, nextmsg);
06773                if (one > 0) {
06774                   if (!res) {
06775                      snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
06776                      res = wait_file2(chan, vms, nextmsg);
06777                   }
06778                }
06779             }
06780          }
06781          if (!res)
06782             res = wait_file2(chan, vms, "vm-message");
06783       /* HEBREW syntax */
06784       } else if (!strcasecmp(chan->language, "he")) {
06785          if (!vms->curmsg) {
06786             res = wait_file2(chan, vms, "vm-message");
06787             res = wait_file2(chan, vms, "vm-first");
06788          } else if (vms->curmsg == vms->lastmsg) {
06789             res = wait_file2(chan, vms, "vm-message");
06790             res = wait_file2(chan, vms, "vm-last");
06791          } else {
06792             res = wait_file2(chan, vms, "vm-message");
06793             res = wait_file2(chan, vms, "vm-number");
06794             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
06795          }
06796       } else {
06797          if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
06798             res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
06799          else /* DEFAULT syntax */ {
06800             res = wait_file2(chan, vms, "vm-message");
06801          }
06802          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06803             if (!res) {
06804                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06805             }
06806          }
06807       }
06808    }
06809 
06810    if (!msg_cfg) {
06811       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06812       return 0;
06813    }
06814 
06815    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06816       ast_log(AST_LOG_WARNING, "No origtime?!\n");
06817       DISPOSE(vms->curdir, vms->curmsg);
06818       ast_config_destroy(msg_cfg);
06819       return 0;
06820    }
06821 
06822    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06823    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06824    category = ast_variable_retrieve(msg_cfg, "message", "category");
06825 
06826    context = ast_variable_retrieve(msg_cfg, "message", "context");
06827    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06828       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06829    if (!res) {
06830       res = play_message_category(chan, category);
06831    }
06832    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06833       res = play_message_datetime(chan, vmu, origtime, filename);
06834    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06835       res = play_message_callerid(chan, vms, cid, context, 0);
06836    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06837       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06838    /* Allow pressing '1' to skip envelope / callerid */
06839    if (res == '1')
06840       res = 0;
06841    ast_config_destroy(msg_cfg);
06842 
06843    if (!res) {
06844       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06845       vms->heard[vms->curmsg] = 1;
06846 #ifdef IMAP_STORAGE
06847       /*IMAP storage stores any prepended message from a forward
06848        * as a separate file from the rest of the message
06849        */
06850       if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
06851          wait_file(chan, vms, vms->introfn);
06852       }
06853 #endif
06854       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06855          ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06856          res = 0;
06857       }
06858    }
06859    DISPOSE(vms->curdir, vms->curmsg);
06860    return res;
06861 }
06862 
06863 #ifdef IMAP_STORAGE
06864 static int imap_remove_file(char *dir, int msgnum)
06865 {
06866    char fn[PATH_MAX];
06867    char full_fn[PATH_MAX];
06868    char intro[PATH_MAX] = {0,};
06869    
06870    if (msgnum > -1) {
06871       make_file(fn, sizeof(fn), dir, msgnum);
06872       snprintf(intro, sizeof(intro), "%sintro", fn);
06873    } else
06874       ast_copy_string(fn, dir, sizeof(fn));
06875    
06876    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
06877       ast_filedelete(fn, NULL);
06878       if (!ast_strlen_zero(intro)) {
06879          ast_filedelete(intro, NULL);
06880       }
06881       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
06882       unlink(full_fn);
06883    }
06884    return 0;
06885 }
06886 
06887 
06888 
06889 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
06890 {
06891    char *file, *filename;
06892    char *attachment;
06893    char arg[10];
06894    int i;
06895    BODY* body;
06896 
06897    
06898    file = strrchr(ast_strdupa(dir), '/');
06899    if (file)
06900       *file++ = '\0';
06901    else {
06902       ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
06903       return -1;
06904    }
06905 
06906    ast_mutex_lock(&vms->lock);
06907    for (i = 0; i < vms->mailstream->nmsgs; i++) {
06908       mail_fetchstructure(vms->mailstream, i + 1, &body);
06909       /* We have the body, now we extract the file name of the first attachment. */
06910       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
06911          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
06912       } else {
06913          ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
06914          ast_mutex_unlock(&vms->lock);
06915          return -1;
06916       }
06917       filename = strsep(&attachment, ".");
06918       if (!strcmp(filename, file)) {
06919          sprintf (arg,"%d", i+1);
06920          mail_setflag (vms->mailstream,arg,"\\DELETED");
06921       }
06922    }
06923    mail_expunge(vms->mailstream);
06924    ast_mutex_unlock(&vms->lock);
06925    return 0;
06926 }
06927 
06928 #else
06929 #ifndef IMAP_STORAGE
06930 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
06931 {
06932    int count_msg, last_msg;
06933 
06934    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
06935    
06936    /* Rename the member vmbox HERE so that we don't try to return before
06937     * we know what's going on.
06938     */
06939    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
06940    
06941    /* Faster to make the directory than to check if it exists. */
06942    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
06943 
06944    count_msg = count_messages(vmu, vms->curdir);
06945    if (count_msg < 0)
06946       return count_msg;
06947    else
06948       vms->lastmsg = count_msg - 1;
06949 
06950    /*
06951    The following test is needed in case sequencing gets messed up.
06952    There appears to be more than one way to mess up sequence, so
06953    we will not try to find all of the root causes--just fix it when
06954    detected.
06955    */
06956 
06957    if (vm_lock_path(vms->curdir)) {
06958       ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
06959       return -1;
06960    }
06961 
06962    last_msg = last_message_index(vmu, vms->curdir);
06963    ast_unlock_path(vms->curdir);
06964 
06965    if (last_msg < 0) 
06966       return last_msg;
06967 
06968    return 0;
06969 }
06970 #endif
06971 #endif
06972 
06973 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
06974 {
06975    int x = 0;
06976 #ifndef IMAP_STORAGE
06977    int res = 0, nummsg;
06978    char fn2[PATH_MAX];
06979 #endif
06980 
06981    if (vms->lastmsg <= -1)
06982       goto done;
06983 
06984    vms->curmsg = -1; 
06985 #ifndef IMAP_STORAGE
06986    /* Get the deleted messages fixed */ 
06987    if (vm_lock_path(vms->curdir))
06988       return ERROR_LOCK_PATH;
06989 
06990    for (x = 0; x < vmu->maxmsg; x++) { 
06991       if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
06992          /* Save this message.  It's not in INBOX or hasn't been heard */ 
06993          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
06994          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
06995             break;
06996          vms->curmsg++; 
06997          make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg); 
06998          if (strcmp(vms->fn, fn2)) { 
06999             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
07000          } 
07001       } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
07002          /* Move to old folder before deleting */ 
07003          res = save_to_folder(vmu, vms, x, 1);
07004          if (res == ERROR_LOCK_PATH) {
07005             /* If save failed do not delete the message */
07006             ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
07007             vms->deleted[x] = 0;
07008             vms->heard[x] = 0;
07009             --x;
07010          }
07011       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
07012          /* Move to deleted folder */ 
07013          res = save_to_folder(vmu, vms, x, 10);
07014          if (res == ERROR_LOCK_PATH) {
07015             /* If save failed do not delete the message */
07016             vms->deleted[x] = 0;
07017             vms->heard[x] = 0;
07018             --x;
07019          }
07020       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
07021          /* If realtime storage enabled - we should explicitly delete this message,
07022          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
07023          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07024          if (EXISTS(vms->curdir, x, vms->fn, NULL))
07025             DELETE(vms->curdir, x, vms->fn, vmu);
07026       }
07027    } 
07028 
07029    /* Delete ALL remaining messages */
07030    nummsg = x - 1;
07031    for (x = vms->curmsg + 1; x <= nummsg; x++) {
07032       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
07033       if (EXISTS(vms->curdir, x, vms->fn, NULL))
07034          DELETE(vms->curdir, x, vms->fn, vmu);
07035    }
07036    ast_unlock_path(vms->curdir);
07037 #else
07038    if (vms->deleted) {
07039       for (x=0;x < vmu->maxmsg;x++) { 
07040          if (vms->deleted[x]) { 
07041             ast_debug(3,"IMAP delete of %d\n",x);
07042             DELETE(vms->curdir, x, vms->fn, vmu);
07043          }
07044       }
07045    }
07046 #endif
07047 
07048 done:
07049    if (vms->deleted)
07050       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
07051    if (vms->heard)
07052       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
07053 
07054    return 0;
07055 }
07056 
07057 /* In Greek even though we CAN use a syntax like "friends messages"
07058  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
07059  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
07060  * syntax for the above three categories which is more elegant. 
07061  */
07062 
07063 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
07064 {
07065    int cmd;
07066    char *buf;
07067 
07068    buf = alloca(strlen(box)+2); 
07069    strcpy(buf, box);
07070    strcat(buf,"s");
07071 
07072    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
07073       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
07074       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07075    } else {
07076       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
07077       return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
07078    }
07079 }
07080 
07081 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
07082 {
07083    int cmd;
07084 
07085    if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
07086       if (!strcasecmp(box, "vm-INBOX"))
07087          cmd = ast_play_and_wait(chan, "vm-new-e");
07088       else
07089          cmd = ast_play_and_wait(chan, "vm-old-e");
07090       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07091    } else {
07092       cmd = ast_play_and_wait(chan, "vm-messages");
07093       return cmd ? cmd : ast_play_and_wait(chan, box);
07094    }
07095 }
07096 
07097 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
07098 {
07099    int cmd;
07100 
07101    if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
07102       cmd = ast_play_and_wait(chan, "vm-messages");
07103       return cmd ? cmd : ast_play_and_wait(chan, box);
07104    } else {
07105       cmd = ast_play_and_wait(chan, box);
07106       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
07107    }
07108 }
07109 
07110 static int vm_play_folder_name(struct ast_channel *chan, char *box)
07111 {
07112    int cmd;
07113 
07114    if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
07115       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
07116       return cmd ? cmd : ast_play_and_wait(chan, box);
07117    } else if (!strcasecmp(chan->language, "gr")){
07118       return vm_play_folder_name_gr(chan, box);
07119    } else if (!strcasecmp(chan->language, "pl")){
07120       return vm_play_folder_name_pl(chan, box);
07121    } else if (!strcasecmp(chan->language, "ua")){  /* Ukrainian syntax */
07122       return vm_play_folder_name_ua(chan, box);
07123    } else {  /* Default English */
07124       cmd = ast_play_and_wait(chan, box);
07125       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
07126    }
07127 }
07128 
07129 /* GREEK SYNTAX 
07130    In greek the plural for old/new is
07131    different so we need the following files
07132    We also need vm-denExeteMynhmata because 
07133    this syntax is different.
07134    
07135    -> vm-Olds.wav : "Palia"
07136    -> vm-INBOXs.wav : "Nea"
07137    -> vm-denExeteMynhmata : "den exete mynhmata"
07138 */
07139                
07140    
07141 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
07142 {
07143    int res = 0;
07144 
07145    if (vms->newmessages) {
07146       res = ast_play_and_wait(chan, "vm-youhave");
07147       if (!res) 
07148          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
07149       if (!res) {
07150          if ((vms->newmessages == 1)) {
07151             res = ast_play_and_wait(chan, "vm-INBOX");
07152             if (!res)
07153                res = ast_play_and_wait(chan, "vm-message");
07154          } else {
07155             res = ast_play_and_wait(chan, "vm-INBOXs");
07156             if (!res)
07157                res = ast_play_and_wait(chan, "vm-messages");
07158          }
07159       }
07160    } else if (vms->oldmessages){
07161       res = ast_play_and_wait(chan, "vm-youhave");
07162       if (!res)
07163          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
07164       if ((vms->oldmessages == 1)){
07165          res = ast_play_and_wait(chan, "vm-Old");
07166          if (!res)
07167             res = ast_play_and_wait(chan, "vm-message");
07168       } else {
07169          res = ast_play_and_wait(chan, "vm-Olds");
07170          if (!res)
07171             res = ast_play_and_wait(chan, "vm-messages");
07172       }
07173    } else if (!vms->oldmessages && !vms->newmessages) 
07174       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
07175    return res;
07176 }
07177 
07178 /* Version of vm_intro() designed to work for many languages.
07179  *
07180  * It is hoped that this function can prevent the proliferation of 
07181  * language-specific vm_intro() functions and in time replace the language-
07182  * specific functions which already exist.  An examination of the language-
07183  * specific functions revealed that they all corrected the same deficiencies
07184  * in vm_intro_en() (which was the default function). Namely:
07185  *
07186  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
07187  *     wording of the voicemail greeting hides this problem.  For example,
07188  *     vm-INBOX contains only the word "new".  This means that both of these
07189  *     sequences produce valid utterances:
07190  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
07191  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
07192  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
07193  *     in many languages) the first utterance becomes "you have 1 the new message".
07194  *  2) The function contains hardcoded rules for pluralizing the word "message".
07195  *     These rules are correct for English, but not for many other languages.
07196  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
07197  *     required in many languages.
07198  *  4) The gender of the word for "message" is not specified. This is a problem
07199  *     because in many languages the gender of the number in phrases such
07200  *     as "you have one new message" must match the gender of the word
07201  *     meaning "message".
07202  *
07203  * Fixing these problems for each new language has meant duplication of effort.
07204  * This new function solves the problems in the following general ways:
07205  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
07206  *     and vm-Old respectively for those languages where it makes sense.
07207  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
07208  *     on vm-message.
07209  *  3) Call ast_say_counted_adjective() to put the proper gender and number
07210  *     prefix on vm-new and vm-old (none for English).
07211  *  4) Pass the gender of the language's word for "message" as an agument to
07212  *     this function which is can in turn pass on to the functions which 
07213  *     say numbers and put endings on nounds and adjectives.
07214  *
07215  * All languages require these messages:
07216  *  vm-youhave    "You have..."
07217  *  vm-and     "and"
07218  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
07219  *
07220  * To use it for English, you will need these additional sound files:
07221  *  vm-new     "new"
07222  *  vm-message    "message", singular
07223  *  vm-messages      "messages", plural
07224  *
07225  * If you use it for Russian and other slavic languages, you will need these additional sound files:
07226  *
07227  *  vm-newn    "novoye" (singular, neuter)
07228  *  vm-newx    "novikh" (counting plural form, genative plural)
07229  *  vm-message    "sobsheniye" (singular form)
07230  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
07231  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
07232  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
07233  *  digits/2n     "dva" (neuter singular)
07234  */
07235 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
07236 {
07237    int res;
07238    int lastnum = 0;
07239 
07240    res = ast_play_and_wait(chan, "vm-youhave");
07241 
07242    if (!res && vms->newmessages) {
07243       lastnum = vms->newmessages;
07244 
07245       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07246          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
07247       }
07248 
07249       if (!res && vms->oldmessages) {
07250          res = ast_play_and_wait(chan, "vm-and");
07251       }
07252    }
07253 
07254    if (!res && vms->oldmessages) {
07255       lastnum = vms->oldmessages;
07256 
07257       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
07258          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
07259       }
07260    }
07261 
07262    if (!res) {
07263       if (lastnum == 0) {
07264          res = ast_play_and_wait(chan, "vm-no");
07265       }
07266       if (!res) {
07267          res = ast_say_counted_noun(chan, lastnum, "vm-message");
07268       }
07269    }
07270 
07271    return res;
07272 }
07273 
07274 /* Default Hebrew syntax */
07275 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
07276 {
07277    int res = 0;
07278 
07279    /* Introduce messages they have */
07280    if (!res) {
07281       if ((vms->newmessages) || (vms->oldmessages)) {
07282          res = ast_play_and_wait(chan, "vm-youhave");
07283       }
07284       /*
07285        * The word "shtei" refers to the number 2 in hebrew when performing a count
07286        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
07287        * an element, this is one of them.
07288        */
07289       if (vms->newmessages) {
07290          if (!res) {
07291             if (vms->newmessages == 1) {
07292                res = ast_play_and_wait(chan, "vm-INBOX1");
07293             } else {
07294                if (vms->newmessages == 2) {
07295                   res = ast_play_and_wait(chan, "vm-shtei");
07296                } else {
07297                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07298                }
07299                res = ast_play_and_wait(chan, "vm-INBOX");
07300             }
07301          }
07302          if (vms->oldmessages && !res) {
07303             res = ast_play_and_wait(chan, "vm-and");
07304             if (vms->oldmessages == 1) {
07305                res = ast_play_and_wait(chan, "vm-Old1");
07306             } else {
07307                if (vms->oldmessages == 2) {
07308                   res = ast_play_and_wait(chan, "vm-shtei");
07309                } else {
07310                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07311                }
07312                res = ast_play_and_wait(chan, "vm-Old");
07313             }
07314          }
07315       }
07316       if (!res && vms->oldmessages && !vms->newmessages) {
07317          if (!res) {
07318             if (vms->oldmessages == 1) {
07319                res = ast_play_and_wait(chan, "vm-Old1");
07320             } else {
07321                if (vms->oldmessages == 2) {
07322                   res = ast_play_and_wait(chan, "vm-shtei");
07323                } else {
07324                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
07325                }
07326                res = ast_play_and_wait(chan, "vm-Old");
07327             }
07328          }
07329       }
07330       if (!res) {
07331          if (!vms->oldmessages && !vms->newmessages) {
07332             if (!res) {
07333                res = ast_play_and_wait(chan, "vm-nomessages");
07334             }
07335          }
07336       }
07337    }
07338    return res;
07339 }
07340    
07341 /* Default English syntax */
07342 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
07343 {
07344    int res;
07345 
07346    /* Introduce messages they have */
07347    res = ast_play_and_wait(chan, "vm-youhave");
07348    if (!res) {
07349       if (vms->urgentmessages) {
07350          res = say_and_wait(chan, vms->urgentmessages, chan->language);
07351          if (!res)
07352             res = ast_play_and_wait(chan, "vm-Urgent");
07353          if ((vms->oldmessages || vms->newmessages) && !res) {
07354             res = ast_play_and_wait(chan, "vm-and");
07355          } else if (!res) {
07356             if ((vms->urgentmessages == 1))
07357                res = ast_play_and_wait(chan, "vm-message");
07358             else
07359                res = ast_play_and_wait(chan, "vm-messages");
07360          }
07361       }
07362       if (vms->newmessages) {
07363          res = say_and_wait(chan, vms->newmessages, chan->language);
07364          if (!res)
07365             res = ast_play_and_wait(chan, "vm-INBOX");
07366          if (vms->oldmessages && !res)
07367             res = ast_play_and_wait(chan, "vm-and");
07368          else if (!res) {
07369             if ((vms->newmessages == 1))
07370                res = ast_play_and_wait(chan, "vm-message");
07371             else
07372                res = ast_play_and_wait(chan, "vm-messages");
07373          }
07374             
07375       }
07376       if (!res && vms->oldmessages) {
07377          res = say_and_wait(chan, vms->oldmessages, chan->language);
07378          if (!res)
07379             res = ast_play_and_wait(chan, "vm-Old");
07380          if (!res) {
07381             if (vms->oldmessages == 1)
07382                res = ast_play_and_wait(chan, "vm-message");
07383             else
07384                res = ast_play_and_wait(chan, "vm-messages");
07385          }
07386       }
07387       if (!res) {
07388          if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
07389             res = ast_play_and_wait(chan, "vm-no");
07390             if (!res)
07391                res = ast_play_and_wait(chan, "vm-messages");
07392          }
07393       }
07394    }
07395    return res;
07396 }
07397 
07398 /* ITALIAN syntax */
07399 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
07400 {
07401    /* Introduce messages they have */
07402    int res;
07403    if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
07404       res = ast_play_and_wait(chan, "vm-no") ||
07405          ast_play_and_wait(chan, "vm-message");
07406    else
07407       res = ast_play_and_wait(chan, "vm-youhave");
07408    if (!res && vms->newmessages) {
07409       res = (vms->newmessages == 1) ?
07410          ast_play_and_wait(chan, "digits/un") ||
07411          ast_play_and_wait(chan, "vm-nuovo") ||
07412          ast_play_and_wait(chan, "vm-message") :
07413          /* 2 or more new messages */
07414          say_and_wait(chan, vms->newmessages, chan->language) ||
07415          ast_play_and_wait(chan, "vm-nuovi") ||
07416          ast_play_and_wait(chan, "vm-messages");
07417       if (!res && vms->oldmessages)
07418          res = ast_play_and_wait(chan, "vm-and");
07419    }
07420    if (!res && vms->oldmessages) {
07421       res = (vms->oldmessages == 1) ?
07422          ast_play_and_wait(chan, "digits/un") ||
07423          ast_play_and_wait(chan, "vm-vecchio") ||
07424          ast_play_and_wait(chan, "vm-message") :
07425          /* 2 or more old messages */
07426          say_and_wait(chan, vms->oldmessages, chan->language) ||
07427          ast_play_and_wait(chan, "vm-vecchi") ||
07428          ast_play_and_wait(chan, "vm-messages");
07429    }
07430    return res;
07431 }
07432 
07433 /* POLISH syntax */
07434 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
07435 {
07436    /* Introduce messages they have */
07437    int res;
07438    div_t num;
07439 
07440    if (!vms->oldmessages && !vms->newmessages) {
07441       res = ast_play_and_wait(chan, "vm-no");
07442       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07443       return res;
07444    } else {
07445       res = ast_play_and_wait(chan, "vm-youhave");
07446    }
07447 
07448    if (vms->newmessages) {
07449       num = div(vms->newmessages, 10);
07450       if (vms->newmessages == 1) {
07451          res = ast_play_and_wait(chan, "digits/1-a");
07452          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
07453          res = res ? res : ast_play_and_wait(chan, "vm-message");
07454       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07455          if (num.rem == 2) {
07456             if (!num.quot) {
07457                res = ast_play_and_wait(chan, "digits/2-ie");
07458             } else {
07459                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
07460                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07461             }
07462          } else {
07463             res = say_and_wait(chan, vms->newmessages, chan->language);
07464          }
07465          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
07466          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07467       } else {
07468          res = say_and_wait(chan, vms->newmessages, chan->language);
07469          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
07470          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07471       }
07472       if (!res && vms->oldmessages)
07473          res = ast_play_and_wait(chan, "vm-and");
07474    }
07475    if (!res && vms->oldmessages) {
07476       num = div(vms->oldmessages, 10);
07477       if (vms->oldmessages == 1) {
07478          res = ast_play_and_wait(chan, "digits/1-a");
07479          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
07480          res = res ? res : ast_play_and_wait(chan, "vm-message");
07481       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
07482          if (num.rem == 2) {
07483             if (!num.quot) {
07484                res = ast_play_and_wait(chan, "digits/2-ie");
07485             } else {
07486                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
07487                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
07488             }
07489          } else {
07490             res = say_and_wait(chan, vms->oldmessages, chan->language);
07491          }
07492          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
07493          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07494       } else {
07495          res = say_and_wait(chan, vms->oldmessages, chan->language);
07496          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
07497          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07498       }
07499    }
07500 
07501    return res;
07502 }
07503 
07504 /* SWEDISH syntax */
07505 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
07506 {
07507    /* Introduce messages they have */
07508    int res;
07509 
07510    res = ast_play_and_wait(chan, "vm-youhave");
07511    if (res)
07512       return res;
07513 
07514    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07515       res = ast_play_and_wait(chan, "vm-no");
07516       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07517       return res;
07518    }
07519 
07520    if (vms->newmessages) {
07521       if ((vms->newmessages == 1)) {
07522          res = ast_play_and_wait(chan, "digits/ett");
07523          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
07524          res = res ? res : ast_play_and_wait(chan, "vm-message");
07525       } else {
07526          res = say_and_wait(chan, vms->newmessages, chan->language);
07527          res = res ? res : ast_play_and_wait(chan, "vm-nya");
07528          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07529       }
07530       if (!res && vms->oldmessages)
07531          res = ast_play_and_wait(chan, "vm-and");
07532    }
07533    if (!res && vms->oldmessages) {
07534       if (vms->oldmessages == 1) {
07535          res = ast_play_and_wait(chan, "digits/ett");
07536          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
07537          res = res ? res : ast_play_and_wait(chan, "vm-message");
07538       } else {
07539          res = say_and_wait(chan, vms->oldmessages, chan->language);
07540          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
07541          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07542       }
07543    }
07544 
07545    return res;
07546 }
07547 
07548 /* NORWEGIAN syntax */
07549 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
07550 {
07551    /* Introduce messages they have */
07552    int res;
07553 
07554    res = ast_play_and_wait(chan, "vm-youhave");
07555    if (res)
07556       return res;
07557 
07558    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07559       res = ast_play_and_wait(chan, "vm-no");
07560       res = res ? res : ast_play_and_wait(chan, "vm-messages");
07561       return res;
07562    }
07563 
07564    if (vms->newmessages) {
07565       if ((vms->newmessages == 1)) {
07566          res = ast_play_and_wait(chan, "digits/1");
07567          res = res ? res : ast_play_and_wait(chan, "vm-ny");
07568          res = res ? res : ast_play_and_wait(chan, "vm-message");
07569       } else {
07570          res = say_and_wait(chan, vms->newmessages, chan->language);
07571          res = res ? res : ast_play_and_wait(chan, "vm-nye");
07572          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07573       }
07574       if (!res && vms->oldmessages)
07575          res = ast_play_and_wait(chan, "vm-and");
07576    }
07577    if (!res && vms->oldmessages) {
07578       if (vms->oldmessages == 1) {
07579          res = ast_play_and_wait(chan, "digits/1");
07580          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
07581          res = res ? res : ast_play_and_wait(chan, "vm-message");
07582       } else {
07583          res = say_and_wait(chan, vms->oldmessages, chan->language);
07584          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
07585          res = res ? res : ast_play_and_wait(chan, "vm-messages");
07586       }
07587    }
07588 
07589    return res;
07590 }
07591 
07592 /* GERMAN syntax */
07593 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
07594 {
07595    /* Introduce messages they have */
07596    int res;
07597    res = ast_play_and_wait(chan, "vm-youhave");
07598    if (!res) {
07599       if (vms->newmessages) {
07600          if ((vms->newmessages == 1))
07601             res = ast_play_and_wait(chan, "digits/1F");
07602          else
07603             res = say_and_wait(chan, vms->newmessages, chan->language);
07604          if (!res)
07605             res = ast_play_and_wait(chan, "vm-INBOX");
07606          if (vms->oldmessages && !res)
07607             res = ast_play_and_wait(chan, "vm-and");
07608          else if (!res) {
07609             if ((vms->newmessages == 1))
07610                res = ast_play_and_wait(chan, "vm-message");
07611             else
07612                res = ast_play_and_wait(chan, "vm-messages");
07613          }
07614             
07615       }
07616       if (!res && vms->oldmessages) {
07617          if (vms->oldmessages == 1)
07618             res = ast_play_and_wait(chan, "digits/1F");
07619          else
07620             res = say_and_wait(chan, vms->oldmessages, chan->language);
07621          if (!res)
07622             res = ast_play_and_wait(chan, "vm-Old");
07623          if (!res) {
07624             if (vms->oldmessages == 1)
07625                res = ast_play_and_wait(chan, "vm-message");
07626             else
07627                res = ast_play_and_wait(chan, "vm-messages");
07628          }
07629       }
07630       if (!res) {
07631          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07632             res = ast_play_and_wait(chan, "vm-no");
07633             if (!res)
07634                res = ast_play_and_wait(chan, "vm-messages");
07635          }
07636       }
07637    }
07638    return res;
07639 }
07640 
07641 /* SPANISH syntax */
07642 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
07643 {
07644    /* Introduce messages they have */
07645    int res;
07646    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07647       res = ast_play_and_wait(chan, "vm-youhaveno");
07648       if (!res)
07649          res = ast_play_and_wait(chan, "vm-messages");
07650    } else {
07651       res = ast_play_and_wait(chan, "vm-youhave");
07652    }
07653    if (!res) {
07654       if (vms->newmessages) {
07655          if (!res) {
07656             if ((vms->newmessages == 1)) {
07657                res = ast_play_and_wait(chan, "digits/1M");
07658                if (!res)
07659                   res = ast_play_and_wait(chan, "vm-message");
07660                if (!res)
07661                   res = ast_play_and_wait(chan, "vm-INBOXs");
07662             } else {
07663                res = say_and_wait(chan, vms->newmessages, chan->language);
07664                if (!res)
07665                   res = ast_play_and_wait(chan, "vm-messages");
07666                if (!res)
07667                   res = ast_play_and_wait(chan, "vm-INBOX");
07668             }
07669          }
07670          if (vms->oldmessages && !res)
07671             res = ast_play_and_wait(chan, "vm-and");
07672       }
07673       if (vms->oldmessages) {
07674          if (!res) {
07675             if (vms->oldmessages == 1) {
07676                res = ast_play_and_wait(chan, "digits/1M");
07677                if (!res)
07678                   res = ast_play_and_wait(chan, "vm-message");
07679                if (!res)
07680                   res = ast_play_and_wait(chan, "vm-Olds");
07681             } else {
07682                res = say_and_wait(chan, vms->oldmessages, chan->language);
07683                if (!res)
07684                   res = ast_play_and_wait(chan, "vm-messages");
07685                if (!res)
07686                   res = ast_play_and_wait(chan, "vm-Old");
07687             }
07688          }
07689       }
07690    }
07691 return res;
07692 }
07693 
07694 /* BRAZILIAN PORTUGUESE syntax */
07695 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
07696    /* Introduce messages they have */
07697    int res;
07698    if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07699       res = ast_play_and_wait(chan, "vm-nomessages");
07700       return res;
07701    } else {
07702       res = ast_play_and_wait(chan, "vm-youhave");
07703    }
07704    if (vms->newmessages) {
07705       if (!res)
07706          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07707       if ((vms->newmessages == 1)) {
07708          if (!res)
07709             res = ast_play_and_wait(chan, "vm-message");
07710          if (!res)
07711             res = ast_play_and_wait(chan, "vm-INBOXs");
07712       } else {
07713          if (!res)
07714             res = ast_play_and_wait(chan, "vm-messages");
07715          if (!res)
07716             res = ast_play_and_wait(chan, "vm-INBOX");
07717       }
07718       if (vms->oldmessages && !res)
07719          res = ast_play_and_wait(chan, "vm-and");
07720    }
07721    if (vms->oldmessages) {
07722       if (!res)
07723          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07724       if (vms->oldmessages == 1) {
07725          if (!res)
07726             res = ast_play_and_wait(chan, "vm-message");
07727          if (!res)
07728             res = ast_play_and_wait(chan, "vm-Olds");
07729       } else {
07730          if (!res)
07731             res = ast_play_and_wait(chan, "vm-messages");
07732          if (!res)
07733             res = ast_play_and_wait(chan, "vm-Old");
07734       }
07735    }
07736    return res;
07737 }
07738 
07739 /* FRENCH syntax */
07740 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
07741 {
07742    /* Introduce messages they have */
07743    int res;
07744    res = ast_play_and_wait(chan, "vm-youhave");
07745    if (!res) {
07746       if (vms->newmessages) {
07747          res = say_and_wait(chan, vms->newmessages, chan->language);
07748          if (!res)
07749             res = ast_play_and_wait(chan, "vm-INBOX");
07750          if (vms->oldmessages && !res)
07751             res = ast_play_and_wait(chan, "vm-and");
07752          else if (!res) {
07753             if ((vms->newmessages == 1))
07754                res = ast_play_and_wait(chan, "vm-message");
07755             else
07756                res = ast_play_and_wait(chan, "vm-messages");
07757          }
07758             
07759       }
07760       if (!res && vms->oldmessages) {
07761          res = say_and_wait(chan, vms->oldmessages, chan->language);
07762          if (!res)
07763             res = ast_play_and_wait(chan, "vm-Old");
07764          if (!res) {
07765             if (vms->oldmessages == 1)
07766                res = ast_play_and_wait(chan, "vm-message");
07767             else
07768                res = ast_play_and_wait(chan, "vm-messages");
07769          }
07770       }
07771       if (!res) {
07772          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07773             res = ast_play_and_wait(chan, "vm-no");
07774             if (!res)
07775                res = ast_play_and_wait(chan, "vm-messages");
07776          }
07777       }
07778    }
07779    return res;
07780 }
07781 
07782 /* DUTCH syntax */
07783 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
07784 {
07785    /* Introduce messages they have */
07786    int res;
07787    res = ast_play_and_wait(chan, "vm-youhave");
07788    if (!res) {
07789       if (vms->newmessages) {
07790          res = say_and_wait(chan, vms->newmessages, chan->language);
07791          if (!res) {
07792             if (vms->newmessages == 1)
07793                res = ast_play_and_wait(chan, "vm-INBOXs");
07794             else
07795                res = ast_play_and_wait(chan, "vm-INBOX");
07796          }
07797          if (vms->oldmessages && !res)
07798             res = ast_play_and_wait(chan, "vm-and");
07799          else if (!res) {
07800             if ((vms->newmessages == 1))
07801                res = ast_play_and_wait(chan, "vm-message");
07802             else
07803                res = ast_play_and_wait(chan, "vm-messages");
07804          }
07805             
07806       }
07807       if (!res && vms->oldmessages) {
07808          res = say_and_wait(chan, vms->oldmessages, chan->language);
07809          if (!res) {
07810             if (vms->oldmessages == 1)
07811                res = ast_play_and_wait(chan, "vm-Olds");
07812             else
07813                res = ast_play_and_wait(chan, "vm-Old");
07814          }
07815          if (!res) {
07816             if (vms->oldmessages == 1)
07817                res = ast_play_and_wait(chan, "vm-message");
07818             else
07819                res = ast_play_and_wait(chan, "vm-messages");
07820          }
07821       }
07822       if (!res) {
07823          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07824             res = ast_play_and_wait(chan, "vm-no");
07825             if (!res)
07826                res = ast_play_and_wait(chan, "vm-messages");
07827          }
07828       }
07829    }
07830    return res;
07831 }
07832 
07833 /* PORTUGUESE syntax */
07834 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
07835 {
07836    /* Introduce messages they have */
07837    int res;
07838    res = ast_play_and_wait(chan, "vm-youhave");
07839    if (!res) {
07840       if (vms->newmessages) {
07841          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07842          if (!res) {
07843             if ((vms->newmessages == 1)) {
07844                res = ast_play_and_wait(chan, "vm-message");
07845                if (!res)
07846                   res = ast_play_and_wait(chan, "vm-INBOXs");
07847             } else {
07848                res = ast_play_and_wait(chan, "vm-messages");
07849                if (!res)
07850                   res = ast_play_and_wait(chan, "vm-INBOX");
07851             }
07852          }
07853          if (vms->oldmessages && !res)
07854             res = ast_play_and_wait(chan, "vm-and");
07855       }
07856       if (!res && vms->oldmessages) {
07857          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07858          if (!res) {
07859             if (vms->oldmessages == 1) {
07860                res = ast_play_and_wait(chan, "vm-message");
07861                if (!res)
07862                   res = ast_play_and_wait(chan, "vm-Olds");
07863             } else {
07864                res = ast_play_and_wait(chan, "vm-messages");
07865                if (!res)
07866                   res = ast_play_and_wait(chan, "vm-Old");
07867             }
07868          }
07869       }
07870       if (!res) {
07871          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07872             res = ast_play_and_wait(chan, "vm-no");
07873             if (!res)
07874                res = ast_play_and_wait(chan, "vm-messages");
07875          }
07876       }
07877    }
07878    return res;
07879 }
07880 
07881 
07882 /* CZECH syntax */
07883 /* in czech there must be declension of word new and message
07884  * czech        : english        : czech      : english
07885  * --------------------------------------------------------
07886  * vm-youhave   : you have 
07887  * vm-novou     : one new        : vm-zpravu  : message
07888  * vm-nove      : 2-4 new        : vm-zpravy  : messages
07889  * vm-novych    : 5-infinite new : vm-zprav   : messages
07890  * vm-starou   : one old
07891  * vm-stare     : 2-4 old 
07892  * vm-starych   : 5-infinite old
07893  * jednu        : one   - falling 4. 
07894  * vm-no        : no  ( no messages )
07895  */
07896 
07897 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
07898 {
07899    int res;
07900    res = ast_play_and_wait(chan, "vm-youhave");
07901    if (!res) {
07902       if (vms->newmessages) {
07903          if (vms->newmessages == 1) {
07904             res = ast_play_and_wait(chan, "digits/jednu");
07905          } else {
07906             res = say_and_wait(chan, vms->newmessages, chan->language);
07907          }
07908          if (!res) {
07909             if ((vms->newmessages == 1))
07910                res = ast_play_and_wait(chan, "vm-novou");
07911             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07912                res = ast_play_and_wait(chan, "vm-nove");
07913             if (vms->newmessages > 4)
07914                res = ast_play_and_wait(chan, "vm-novych");
07915          }
07916          if (vms->oldmessages && !res)
07917             res = ast_play_and_wait(chan, "vm-and");
07918          else if (!res) {
07919             if ((vms->newmessages == 1))
07920                res = ast_play_and_wait(chan, "vm-zpravu");
07921             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07922                res = ast_play_and_wait(chan, "vm-zpravy");
07923             if (vms->newmessages > 4)
07924                res = ast_play_and_wait(chan, "vm-zprav");
07925          }
07926       }
07927       if (!res && vms->oldmessages) {
07928          res = say_and_wait(chan, vms->oldmessages, chan->language);
07929          if (!res) {
07930             if ((vms->oldmessages == 1))
07931                res = ast_play_and_wait(chan, "vm-starou");
07932             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07933                res = ast_play_and_wait(chan, "vm-stare");
07934             if (vms->oldmessages > 4)
07935                res = ast_play_and_wait(chan, "vm-starych");
07936          }
07937          if (!res) {
07938             if ((vms->oldmessages == 1))
07939                res = ast_play_and_wait(chan, "vm-zpravu");
07940             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07941                res = ast_play_and_wait(chan, "vm-zpravy");
07942             if (vms->oldmessages > 4)
07943                res = ast_play_and_wait(chan, "vm-zprav");
07944          }
07945       }
07946       if (!res) {
07947          if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
07948             res = ast_play_and_wait(chan, "vm-no");
07949             if (!res)
07950                res = ast_play_and_wait(chan, "vm-zpravy");
07951          }
07952       }
07953    }
07954    return res;
07955 }
07956 
07957 /* CHINESE (Taiwan) syntax */
07958 static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
07959 {
07960    int res;
07961    /* Introduce messages they have */
07962    res = ast_play_and_wait(chan, "vm-you");
07963 
07964    if (!res && vms->newmessages) {
07965       res = ast_play_and_wait(chan, "vm-have");
07966       if (!res)
07967          res = say_and_wait(chan, vms->newmessages, chan->language);
07968       if (!res)
07969          res = ast_play_and_wait(chan, "vm-tong");
07970       if (!res)
07971          res = ast_play_and_wait(chan, "vm-INBOX");
07972       if (vms->oldmessages && !res)
07973          res = ast_play_and_wait(chan, "vm-and");
07974       else if (!res) 
07975          res = ast_play_and_wait(chan, "vm-messages");
07976    }
07977    if (!res && vms->oldmessages) {
07978       res = ast_play_and_wait(chan, "vm-have");
07979       if (!res)
07980          res = say_and_wait(chan, vms->oldmessages, chan->language);
07981       if (!res)
07982          res = ast_play_and_wait(chan, "vm-tong");
07983       if (!res)
07984          res = ast_play_and_wait(chan, "vm-Old");
07985       if (!res)
07986          res = ast_play_and_wait(chan, "vm-messages");
07987    }
07988    if (!res && !vms->oldmessages && !vms->newmessages) {
07989       res = ast_play_and_wait(chan, "vm-haveno");
07990       if (!res)
07991          res = ast_play_and_wait(chan, "vm-messages");
07992    }
07993    return res;
07994 }
07995 
07996 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07997 {
07998    char prefile[256];
07999    
08000    /* Notify the user that the temp greeting is set and give them the option to remove it */
08001    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08002    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
08003       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08004       if (ast_fileexists(prefile, NULL, NULL) > 0) {
08005          ast_play_and_wait(chan, "vm-tempgreetactive");
08006       }
08007       DISPOSE(prefile, -1);
08008    }
08009 
08010    /* Play voicemail intro - syntax is different for different languages */
08011    if (!strcasecmp(chan->language, "de")) {  /* GERMAN syntax */
08012       return vm_intro_de(chan, vms);
08013    } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
08014       return vm_intro_es(chan, vms);
08015    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
08016       return vm_intro_it(chan, vms);
08017    } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
08018       return vm_intro_fr(chan, vms);
08019    } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
08020       return vm_intro_nl(chan, vms);
08021    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
08022       return vm_intro_pt(chan, vms);
08023    } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
08024       return vm_intro_pt_BR(chan, vms);
08025    } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
08026       return vm_intro_cz(chan, vms);
08027    } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
08028       return vm_intro_gr(chan, vms);
08029    } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
08030       return vm_intro_pl(chan, vms);
08031    } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
08032       return vm_intro_se(chan, vms);
08033    } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
08034       return vm_intro_no(chan, vms);
08035    } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
08036       return vm_intro_multilang(chan, vms, "n");
08037    } else if (!strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
08038       return vm_intro_tw(chan, vms);
08039    } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
08040       return vm_intro_multilang(chan, vms, "n");
08041    } else if (!strcasecmp(chan->language, "he")) { /* HEBREW syntax */
08042        return vm_intro_he(chan, vms);
08043    } else {             /* Default to ENGLISH */
08044       return vm_intro_en(chan, vms);
08045    }
08046 }
08047 
08048 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08049 {
08050    int res = 0;
08051    /* Play instructions and wait for new command */
08052    while (!res) {
08053       if (vms->starting) {
08054          if (vms->lastmsg > -1) {
08055             if (skipadvanced)
08056                res = ast_play_and_wait(chan, "vm-onefor-full");
08057             else
08058                res = ast_play_and_wait(chan, "vm-onefor");
08059             if (!res)
08060                res = vm_play_folder_name(chan, vms->vmbox);
08061          }
08062          if (!res) {
08063             if (skipadvanced)
08064                res = ast_play_and_wait(chan, "vm-opts-full");
08065             else
08066                res = ast_play_and_wait(chan, "vm-opts");
08067          }
08068       } else {
08069          /* Added for additional help */
08070          if (skipadvanced) {
08071             res = ast_play_and_wait(chan, "vm-onefor-full");
08072             if (!res)
08073                res = vm_play_folder_name(chan, vms->vmbox);
08074             res = ast_play_and_wait(chan, "vm-opts-full");
08075          }
08076          /* Logic:
08077           * If the current message is not the first OR
08078           * if we're listening to the first new message and there are
08079           * also urgent messages, then prompt for navigation to the
08080           * previous message
08081           */
08082          if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
08083             res = ast_play_and_wait(chan, "vm-prev");
08084          }
08085          if (!res && !skipadvanced)
08086             res = ast_play_and_wait(chan, "vm-advopts");
08087          if (!res)
08088             res = ast_play_and_wait(chan, "vm-repeat");
08089          /* Logic:
08090           * If we're not listening to the last message OR
08091           * we're listening to the last urgent message and there are
08092           * also new non-urgent messages, then prompt for navigation
08093           * to the next message
08094           */
08095          if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
08096             (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
08097             res = ast_play_and_wait(chan, "vm-next");
08098          }
08099          if (!res) {
08100             if (!vms->deleted[vms->curmsg])
08101                res = ast_play_and_wait(chan, "vm-delete");
08102             else
08103                res = ast_play_and_wait(chan, "vm-undelete");
08104             if (!res)
08105                res = ast_play_and_wait(chan, "vm-toforward");
08106             if (!res)
08107                res = ast_play_and_wait(chan, "vm-savemessage");
08108          }
08109       }
08110       if (!res) {
08111          if (skipadvanced)
08112             res = ast_play_and_wait(chan, "vm-helpexit-full");
08113          else
08114             res = ast_play_and_wait(chan, "vm-helpexit");
08115       }
08116       if (!res)
08117          res = ast_waitfordigit(chan, 6000);
08118       if (!res) {
08119          vms->repeats++;
08120          if (vms->repeats > 2) {
08121             res = 't';
08122          }
08123       }
08124    }
08125    return res;
08126 }
08127 
08128 static int vm_instructions_tw(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
08129 {
08130    int res = 0;
08131    /* Play instructions and wait for new command */
08132    while (!res) {
08133       if (vms->lastmsg > -1) {
08134          res = ast_play_and_wait(chan, "vm-listen");
08135          if (!res)
08136             res = vm_play_folder_name(chan, vms->vmbox);
08137          if (!res)
08138             res = ast_play_and_wait(chan, "press");
08139          if (!res)
08140             res = ast_play_and_wait(chan, "digits/1");
08141       }
08142       if (!res)
08143          res = ast_play_and_wait(chan, "vm-opts");
08144       if (!res) {
08145          vms->starting = 0;
08146          return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08147       }
08148    }
08149    return res;
08150 }
08151 
08152 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
08153 {
08154    if (vms->starting && !strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
08155       return vm_instructions_tw(chan, vmu, vms, skipadvanced, in_urgent);
08156    } else {             /* Default to ENGLISH */
08157       return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
08158    }
08159 }
08160 
08161 
08162 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08163 {
08164    int cmd = 0;
08165    int duration = 0;
08166    int tries = 0;
08167    char newpassword[80] = "";
08168    char newpassword2[80] = "";
08169    char prefile[PATH_MAX] = "";
08170    unsigned char buf[256];
08171    int bytes=0;
08172 
08173    if (ast_adsi_available(chan)) {
08174       bytes += adsi_logo(buf + bytes);
08175       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
08176       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08177       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08178       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08179       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08180    }
08181 
08182    /* First, have the user change their password 
08183       so they won't get here again */
08184    for (;;) {
08185       newpassword[1] = '\0';
08186       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08187       if (cmd == '#')
08188          newpassword[0] = '\0';
08189       if (cmd < 0 || cmd == 't' || cmd == '#')
08190          return cmd;
08191       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
08192       if (cmd < 0 || cmd == 't' || cmd == '#')
08193          return cmd;
08194       cmd = check_password(vmu, newpassword); /* perform password validation */
08195       if (cmd != 0) {
08196          ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08197          cmd = ast_play_and_wait(chan, vm_invalid_password);
08198       } else {
08199          newpassword2[1] = '\0';
08200          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08201          if (cmd == '#')
08202             newpassword2[0] = '\0';
08203          if (cmd < 0 || cmd == 't' || cmd == '#')
08204             return cmd;
08205          cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
08206          if (cmd < 0 || cmd == 't' || cmd == '#')
08207             return cmd;
08208          if (!strcmp(newpassword, newpassword2))
08209             break;
08210          ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08211          cmd = ast_play_and_wait(chan, vm_mismatch);
08212       }
08213       if (++tries == 3)
08214          return -1;
08215    }
08216    if (pwdchange & PWDCHANGE_INTERNAL)
08217       vm_change_password(vmu, newpassword);
08218    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08219       vm_change_password_shell(vmu, newpassword);
08220 
08221    ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08222    cmd = ast_play_and_wait(chan, vm_passchanged);
08223 
08224    /* If forcename is set, have the user record their name */  
08225    if (ast_test_flag(vmu, VM_FORCENAME)) {
08226       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08227       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08228          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08229          if (cmd < 0 || cmd == 't' || cmd == '#')
08230             return cmd;
08231       }
08232    }
08233 
08234    /* If forcegreetings is set, have the user record their greetings */
08235    if (ast_test_flag(vmu, VM_FORCEGREET)) {
08236       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08237       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08238          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08239          if (cmd < 0 || cmd == 't' || cmd == '#')
08240             return cmd;
08241       }
08242 
08243       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08244       if (ast_fileexists(prefile, NULL, NULL) < 1) {
08245          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08246          if (cmd < 0 || cmd == 't' || cmd == '#')
08247             return cmd;
08248       }
08249    }
08250 
08251    return cmd;
08252 }
08253 
08254 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08255 {
08256    int cmd = 0;
08257    int retries = 0;
08258    int duration = 0;
08259    char newpassword[80] = "";
08260    char newpassword2[80] = "";
08261    char prefile[PATH_MAX] = "";
08262    unsigned char buf[256];
08263    int bytes=0;
08264 
08265    if (ast_adsi_available(chan)) {
08266       bytes += adsi_logo(buf + bytes);
08267       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
08268       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08269       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08270       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08271       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08272    }
08273    while ((cmd >= 0) && (cmd != 't')) {
08274       if (cmd)
08275          retries = 0;
08276       switch (cmd) {
08277       case '1': /* Record your unavailable message */
08278          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
08279          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08280          break;
08281       case '2':  /* Record your busy message */
08282          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
08283          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08284          break;
08285       case '3': /* Record greeting */
08286          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
08287          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08288          break;
08289       case '4':  /* manage the temporary greeting */
08290          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
08291          break;
08292       case '5': /* change password */
08293          if (vmu->password[0] == '-') {
08294             cmd = ast_play_and_wait(chan, "vm-no");
08295             break;
08296          }
08297          newpassword[1] = '\0';
08298          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
08299          if (cmd == '#')
08300             newpassword[0] = '\0';
08301          else {
08302             if (cmd < 0)
08303                break;
08304             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
08305                break;
08306             }
08307          }
08308          cmd = check_password(vmu, newpassword); /* perform password validation */
08309          if (cmd != 0) {
08310             ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
08311             cmd = ast_play_and_wait(chan, vm_invalid_password);
08312             break;
08313          }
08314          newpassword2[1] = '\0';
08315          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
08316          if (cmd == '#')
08317             newpassword2[0] = '\0';
08318          else {
08319             if (cmd < 0)
08320                break;
08321 
08322             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
08323                break;
08324             }
08325          }
08326          if (strcmp(newpassword, newpassword2)) {
08327             ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
08328             cmd = ast_play_and_wait(chan, vm_mismatch);
08329             break;
08330          }
08331          if (pwdchange & PWDCHANGE_INTERNAL)
08332             vm_change_password(vmu, newpassword);
08333          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
08334             vm_change_password_shell(vmu, newpassword);
08335 
08336          ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
08337          cmd = ast_play_and_wait(chan, vm_passchanged);
08338          break;
08339       case '*': 
08340          cmd = 't';
08341          break;
08342       default: 
08343          cmd = 0;
08344          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08345          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08346          if (ast_fileexists(prefile, NULL, NULL)) {
08347             cmd = ast_play_and_wait(chan, "vm-tmpexists");
08348          }
08349          DISPOSE(prefile, -1);
08350          if (!cmd) {
08351             cmd = ast_play_and_wait(chan, "vm-options");
08352          }
08353          if (!cmd) {
08354             cmd = ast_waitfordigit(chan,6000);
08355          }
08356          if (!cmd) {
08357             retries++;
08358          }
08359          if (retries > 3) {
08360             cmd = 't';
08361          }
08362       }
08363    }
08364    if (cmd == 't')
08365       cmd = 0;
08366    return cmd;
08367 }
08368 
08369 /*!
08370  * \brief The handler for 'record a temporary greeting'. 
08371  * \param chan
08372  * \param vmu
08373  * \param vms
08374  * \param fmtc
08375  * \param record_gain
08376  *
08377  * This is option 4 from the mailbox options menu.
08378  * This function manages the following promptings:
08379  * 1: play / record / review the temporary greeting. : invokes play_record_review().
08380  * 2: remove (delete) the temporary greeting.
08381  * *: return to the main menu.
08382  *
08383  * \return zero on success, -1 on error.
08384  */
08385 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
08386 {
08387    int cmd = 0;
08388    int retries = 0;
08389    int duration = 0;
08390    char prefile[PATH_MAX] = "";
08391    unsigned char buf[256];
08392    int bytes = 0;
08393 
08394    if (ast_adsi_available(chan)) {
08395       bytes += adsi_logo(buf + bytes);
08396       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
08397       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
08398       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
08399       bytes += ast_adsi_voice_mode(buf + bytes, 0);
08400       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
08401    }
08402 
08403    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
08404    while ((cmd >= 0) && (cmd != 't')) {
08405       if (cmd)
08406          retries = 0;
08407       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
08408       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
08409          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08410          cmd = 't';  
08411       } else {
08412          switch (cmd) {
08413          case '1':
08414             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
08415             break;
08416          case '2':
08417             DELETE(prefile, -1, prefile, vmu);
08418             ast_play_and_wait(chan, "vm-tempremoved");
08419             cmd = 't';  
08420             break;
08421          case '*': 
08422             cmd = 't';
08423             break;
08424          default:
08425             cmd = ast_play_and_wait(chan,
08426                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
08427                   "vm-tempgreeting2" : "vm-tempgreeting");
08428             if (!cmd)
08429                cmd = ast_waitfordigit(chan,6000);
08430             if (!cmd)
08431                retries++;
08432             if (retries > 3)
08433                cmd = 't';
08434          }
08435       }
08436       DISPOSE(prefile, -1);
08437    }
08438    if (cmd == 't')
08439       cmd = 0;
08440    return cmd;
08441 }
08442 
08443 /*!
08444  * \brief Greek syntax for 'You have N messages' greeting.
08445  * \param chan
08446  * \param vms
08447  * \param vmu
08448  *
08449  * \return zero on success, -1 on error.
08450  */   
08451 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08452 {
08453    int cmd=0;
08454 
08455    if (vms->lastmsg > -1) {
08456       cmd = play_message(chan, vmu, vms);
08457    } else {
08458       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08459       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
08460          if (!cmd) {
08461             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
08462             cmd = ast_play_and_wait(chan, vms->fn);
08463          }
08464          if (!cmd)
08465             cmd = ast_play_and_wait(chan, "vm-messages");
08466       } else {
08467          if (!cmd)
08468             cmd = ast_play_and_wait(chan, "vm-messages");
08469          if (!cmd) {
08470             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08471             cmd = ast_play_and_wait(chan, vms->fn);
08472          }
08473       }
08474    } 
08475    return cmd;
08476 }
08477 
08478 /* Hebrew Syntax */
08479 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08480 {
08481    int cmd = 0;
08482 
08483    if (vms->lastmsg > -1) {
08484       cmd = play_message(chan, vmu, vms);
08485    } else {
08486       if (!strcasecmp(vms->fn, "INBOX")) {
08487          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
08488       } else {
08489          cmd = ast_play_and_wait(chan, "vm-nomessages");
08490       }
08491    }
08492    return cmd;
08493 }
08494 
08495 /*! 
08496  * \brief Default English syntax for 'You have N messages' greeting.
08497  * \param chan
08498  * \param vms
08499  * \param vmu
08500  *
08501  * \return zero on success, -1 on error.
08502  */
08503 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08504 {
08505    int cmd=0;
08506 
08507    if (vms->lastmsg > -1) {
08508       cmd = play_message(chan, vmu, vms);
08509    } else {
08510       cmd = ast_play_and_wait(chan, "vm-youhave");
08511       if (!cmd) 
08512          cmd = ast_play_and_wait(chan, "vm-no");
08513       if (!cmd) {
08514          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08515          cmd = ast_play_and_wait(chan, vms->fn);
08516       }
08517       if (!cmd)
08518          cmd = ast_play_and_wait(chan, "vm-messages");
08519    }
08520    return cmd;
08521 }
08522 
08523 /*! 
08524  *\brief Italian syntax for 'You have N messages' greeting.
08525  * \param chan
08526  * \param vms
08527  * \param vmu
08528  *
08529  * \return zero on success, -1 on error.
08530  */
08531 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08532 {
08533    int cmd=0;
08534 
08535    if (vms->lastmsg > -1) {
08536       cmd = play_message(chan, vmu, vms);
08537    } else {
08538       cmd = ast_play_and_wait(chan, "vm-no");
08539       if (!cmd)
08540          cmd = ast_play_and_wait(chan, "vm-message");
08541       if (!cmd) {
08542          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08543          cmd = ast_play_and_wait(chan, vms->fn);
08544       }
08545    }
08546    return cmd;
08547 }
08548 
08549 /*! 
08550  * \brief Spanish syntax for 'You have N messages' greeting.
08551  * \param chan
08552  * \param vms
08553  * \param vmu
08554  *
08555  * \return zero on success, -1 on error.
08556  */
08557 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08558 {
08559    int cmd=0;
08560 
08561    if (vms->lastmsg > -1) {
08562       cmd = play_message(chan, vmu, vms);
08563    } else {
08564       cmd = ast_play_and_wait(chan, "vm-youhaveno");
08565       if (!cmd)
08566          cmd = ast_play_and_wait(chan, "vm-messages");
08567       if (!cmd) {
08568          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08569          cmd = ast_play_and_wait(chan, vms->fn);
08570       }
08571    }
08572    return cmd;
08573 }
08574 
08575 /*! 
08576  * \brief Portuguese syntax for 'You have N messages' greeting.
08577  * \param chan
08578  * \param vms
08579  * \param vmu
08580  *
08581  * \return zero on success, -1 on error.
08582  */
08583 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08584 {
08585    int cmd=0;
08586 
08587    if (vms->lastmsg > -1) {
08588       cmd = play_message(chan, vmu, vms);
08589    } else {
08590       cmd = ast_play_and_wait(chan, "vm-no");
08591       if (!cmd) {
08592          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08593          cmd = ast_play_and_wait(chan, vms->fn);
08594       }
08595       if (!cmd)
08596          cmd = ast_play_and_wait(chan, "vm-messages");
08597    }
08598    return cmd;
08599 }
08600 
08601 /*! 
08602  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
08603  * \param chan
08604  * \param vms
08605  * \param vmu
08606  *
08607  * \return zero on success, -1 on error.
08608  */
08609 static int vm_browse_messages_tw(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08610 {
08611    int cmd=0;
08612 
08613    if (vms->lastmsg > -1) {
08614       cmd = play_message(chan, vmu, vms);
08615    } else {
08616       cmd = ast_play_and_wait(chan, "vm-you");
08617       if (!cmd) 
08618          cmd = ast_play_and_wait(chan, "vm-haveno");
08619       if (!cmd)
08620          cmd = ast_play_and_wait(chan, "vm-messages");
08621       if (!cmd) {
08622          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
08623          cmd = ast_play_and_wait(chan, vms->fn);
08624       }
08625    }
08626    return cmd;
08627 }
08628 
08629 /*!
08630  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
08631  * \param chan The channel for the current user. We read the language property from this.
08632  * \param vms passed into the language-specific vm_browse_messages function.
08633  * \param vmu passed into the language-specific vm_browse_messages function.
08634  * 
08635  * The method to be invoked is determined by the value of language code property in the user's channel.
08636  * The default (when unable to match) is to use english.
08637  *
08638  * \return zero on success, -1 on error.
08639  */
08640 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
08641 {
08642    if (!strcasecmp(chan->language, "es")) {  /* SPANISH */
08643       return vm_browse_messages_es(chan, vms, vmu);
08644    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
08645       return vm_browse_messages_it(chan, vms, vmu);
08646    } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) {   /* PORTUGUESE */
08647       return vm_browse_messages_pt(chan, vms, vmu);
08648    } else if (!strcasecmp(chan->language, "gr")){
08649       return vm_browse_messages_gr(chan, vms, vmu);   /* GREEK */
08650    } else if (!strcasecmp(chan->language, "tw")){
08651       return vm_browse_messages_tw(chan, vms, vmu);   /* CHINESE (Taiwan) */
08652    } else if (!strcasecmp(chan->language, "he")) {
08653       return vm_browse_messages_he(chan, vms, vmu);   /* HEBREW */
08654    } else { /* Default to English syntax */
08655       return vm_browse_messages_en(chan, vms, vmu);
08656    }
08657 }
08658 
08659 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
08660          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
08661          int skipuser, int max_logins, int silent)
08662 {
08663    int useadsi=0, valid=0, logretries=0;
08664    char password[AST_MAX_EXTENSION]="", *passptr;
08665    struct ast_vm_user vmus, *vmu = NULL;
08666 
08667    /* If ADSI is supported, setup login screen */
08668    adsi_begin(chan, &useadsi);
08669    if (!skipuser && useadsi)
08670       adsi_login(chan);
08671    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
08672       ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
08673       return -1;
08674    }
08675    
08676    /* Authenticate them and get their mailbox/password */
08677    
08678    while (!valid && (logretries < max_logins)) {
08679       /* Prompt for, and read in the username */
08680       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
08681          ast_log(AST_LOG_WARNING, "Couldn't read username\n");
08682          return -1;
08683       }
08684       if (ast_strlen_zero(mailbox)) {
08685          if (chan->cid.cid_num) {
08686             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
08687          } else {
08688             ast_verb(3,"Username not entered\n");  
08689             return -1;
08690          }
08691       }
08692       if (useadsi)
08693          adsi_password(chan);
08694 
08695       if (!ast_strlen_zero(prefix)) {
08696          char fullusername[80] = "";
08697          ast_copy_string(fullusername, prefix, sizeof(fullusername));
08698          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
08699          ast_copy_string(mailbox, fullusername, mailbox_size);
08700       }
08701 
08702       ast_debug(1, "Before find user for mailbox %s\n",mailbox);
08703       vmu = find_user(&vmus, context, mailbox);
08704       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
08705          /* saved password is blank, so don't bother asking */
08706          password[0] = '\0';
08707       } else {
08708          if (ast_streamfile(chan, vm_password, chan->language)) {
08709             ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
08710             return -1;
08711          }
08712          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
08713             ast_log(AST_LOG_WARNING, "Unable to read password\n");
08714             return -1;
08715          }
08716       }
08717 
08718       if (vmu) {
08719          passptr = vmu->password;
08720          if (passptr[0] == '-') passptr++;
08721       }
08722       if (vmu && !strcmp(passptr, password))
08723          valid++;
08724       else {
08725          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
08726          if (!ast_strlen_zero(prefix))
08727             mailbox[0] = '\0';
08728       }
08729       logretries++;
08730       if (!valid) {
08731          if (skipuser || logretries >= max_logins) {
08732             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
08733                ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
08734                return -1;
08735             }
08736          } else {
08737             if (useadsi)
08738                adsi_login(chan);
08739             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
08740                ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
08741                return -1;
08742             }
08743          }
08744          if (ast_waitstream(chan, "")) /* Channel is hung up */
08745             return -1;
08746       }
08747    }
08748    if (!valid && (logretries >= max_logins)) {
08749       ast_stopstream(chan);
08750       ast_play_and_wait(chan, "vm-goodbye");
08751       return -1;
08752    }
08753    if (vmu && !skipuser) {
08754       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
08755    }
08756    return 0;
08757 }
08758 
08759 static int vm_execmain(struct ast_channel *chan, void *data)
08760 {
08761    /* XXX This is, admittedly, some pretty horrendous code.  For some
08762       reason it just seemed a lot easier to do with GOTO's.  I feel
08763       like I'm back in my GWBASIC days. XXX */
08764    int res=-1;
08765    int cmd=0;
08766    int valid = 0;
08767    char prefixstr[80] ="";
08768    char ext_context[256]="";
08769    int box;
08770    int useadsi = 0;
08771    int skipuser = 0;
08772    struct vm_state vms;
08773    struct ast_vm_user *vmu = NULL, vmus;
08774    char *context=NULL;
08775    int silentexit = 0;
08776    struct ast_flags flags = { 0 };
08777    signed char record_gain = 0;
08778    int play_auto = 0;
08779    int play_folder = 0;
08780    int in_urgent = 0;
08781 #ifdef IMAP_STORAGE
08782    int deleted = 0;
08783 #endif
08784 
08785    /* Add the vm_state to the active list and keep it active */
08786    memset(&vms, 0, sizeof(vms));
08787 
08788    vms.lastmsg = -1;
08789 
08790    memset(&vmus, 0, sizeof(vmus));
08791 
08792    if (chan->_state != AST_STATE_UP) {
08793       ast_debug(1, "Before ast_answer\n");
08794       ast_answer(chan);
08795    }
08796 
08797    if (!ast_strlen_zero(data)) {
08798       char *opts[OPT_ARG_ARRAY_SIZE];
08799       char *parse;
08800       AST_DECLARE_APP_ARGS(args,
08801          AST_APP_ARG(argv0);
08802          AST_APP_ARG(argv1);
08803       );
08804 
08805       parse = ast_strdupa(data);
08806 
08807       AST_STANDARD_APP_ARGS(args, parse);
08808 
08809       if (args.argc == 2) {
08810          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
08811             return -1;
08812          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
08813             int gain;
08814             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
08815                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
08816                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
08817                   return -1;
08818                } else {
08819                   record_gain = (signed char) gain;
08820                }
08821             } else {
08822                ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
08823             }
08824          }
08825          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
08826             play_auto = 1;
08827             if (opts[OPT_ARG_PLAYFOLDER]) {
08828                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
08829                   ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
08830                }
08831             } else {
08832                ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
08833             }  
08834             if ( play_folder > 9 || play_folder < 0) {
08835                ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
08836                play_folder = 0;
08837             }
08838          }
08839       } else {
08840          /* old style options parsing */
08841          while (*(args.argv0)) {
08842             if (*(args.argv0) == 's')
08843                ast_set_flag(&flags, OPT_SILENT);
08844             else if (*(args.argv0) == 'p')
08845                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
08846             else 
08847                break;
08848             (args.argv0)++;
08849          }
08850 
08851       }
08852 
08853       valid = ast_test_flag(&flags, OPT_SILENT);
08854 
08855       if ((context = strchr(args.argv0, '@')))
08856          *context++ = '\0';
08857 
08858       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
08859          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
08860       else
08861          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
08862 
08863       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
08864          skipuser++;
08865       else
08866          valid = 0;
08867    }
08868 
08869    if (!valid)
08870       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
08871 
08872    ast_debug(1, "After vm_authenticate\n");
08873    if (!res) {
08874       valid = 1;
08875       if (!skipuser)
08876          vmu = &vmus;
08877    } else {
08878       res = 0;
08879    }
08880 
08881    /* If ADSI is supported, setup login screen */
08882    adsi_begin(chan, &useadsi);
08883 
08884 #ifdef IMAP_STORAGE
08885    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
08886    pthread_setspecific(ts_vmstate.key, &vms);
08887 
08888    vms.interactive = 1;
08889    vms.updated = 1;
08890    if (vmu)
08891       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
08892    vmstate_insert(&vms);
08893    init_vm_state(&vms);
08894 #endif
08895    if (!valid)
08896       goto out;
08897 
08898    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
08899       ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
08900       cmd = ast_play_and_wait(chan, "an-error-has-occured");
08901       return -1;
08902    }
08903    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
08904       ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
08905       cmd = ast_play_and_wait(chan, "an-error-has-occured");
08906       return -1;
08907    }
08908    
08909    /* Set language from config to override channel language */
08910    if (!ast_strlen_zero(vmu->language))
08911       ast_string_field_set(chan, language, vmu->language);
08912 
08913    /* Retrieve urgent, old and new message counts */
08914    ast_debug(1, "Before open_mailbox\n");
08915    res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
08916    if (res == ERROR_LOCK_PATH)
08917       goto out;
08918    vms.oldmessages = vms.lastmsg + 1;
08919    ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
08920    /* check INBOX */
08921    res = open_mailbox(&vms, vmu, NEW_FOLDER);
08922    if (res == ERROR_LOCK_PATH)
08923       goto out;
08924    vms.newmessages = vms.lastmsg + 1;
08925    ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
08926    /* Start in Urgent */
08927    in_urgent = 1;
08928    res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
08929    if (res == ERROR_LOCK_PATH)
08930       goto out;
08931    vms.urgentmessages = vms.lastmsg + 1;
08932    ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
08933 
08934    /* Select proper mailbox FIRST!! */
08935    if (play_auto) {
08936       if (vms.urgentmessages) {
08937          in_urgent = 1;
08938          res = open_mailbox(&vms, vmu, 11);
08939       } else {
08940          in_urgent = 0;
08941          res = open_mailbox(&vms, vmu, play_folder);
08942       }
08943       if (res == ERROR_LOCK_PATH)
08944          goto out;
08945 
08946       /* If there are no new messages, inform the user and hangup */
08947       if (vms.lastmsg == -1) {
08948          in_urgent = 0;
08949          cmd = vm_browse_messages(chan, &vms, vmu);
08950          res = 0;
08951          goto out;
08952       }
08953    } else {
08954       if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
08955          /* If we only have old messages start here */
08956          res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
08957          in_urgent = 0;
08958          play_folder = 1;
08959          if (res == ERROR_LOCK_PATH)
08960             goto out;
08961       } else if (!vms.urgentmessages && vms.newmessages) {
08962          /* If we have new messages but none are urgent */
08963          in_urgent = 0;
08964          res = open_mailbox(&vms, vmu, NEW_FOLDER);
08965          if (res == ERROR_LOCK_PATH)
08966             goto out;
08967       }
08968    }
08969 
08970    if (useadsi)
08971       adsi_status(chan, &vms);
08972    res = 0;
08973 
08974    /* Check to see if this is a new user */
08975    if (!strcasecmp(vmu->mailbox, vmu->password) && 
08976       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
08977       if (ast_play_and_wait(chan, "vm-newuser") == -1)
08978          ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
08979       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
08980       if ((cmd == 't') || (cmd == '#')) {
08981          /* Timeout */
08982          res = 0;
08983          goto out;
08984       } else if (cmd < 0) {
08985          /* Hangup */
08986          res = -1;
08987          goto out;
08988       }
08989    }
08990 #ifdef IMAP_STORAGE
08991       ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
08992       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
08993          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
08994          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
08995       }
08996       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
08997       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
08998          ast_log(AST_LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
08999          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09000       }
09001 #endif
09002    if (play_auto) {
09003       cmd = '1';
09004    } else {
09005       cmd = vm_intro(chan, vmu, &vms);
09006    }
09007 
09008    vms.repeats = 0;
09009    vms.starting = 1;
09010    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09011       /* Run main menu */
09012       switch (cmd) {
09013       case '1': /* First message */
09014          vms.curmsg = 0;
09015          /* Fall through */
09016       case '5': /* Play current message */
09017          cmd = vm_browse_messages(chan, &vms, vmu);
09018          break;
09019       case '2': /* Change folders */
09020          if (useadsi)
09021             adsi_folders(chan, 0, "Change to folder...");
09022          cmd = get_folder2(chan, "vm-changeto", 0);
09023          if (cmd == '#') {
09024             cmd = 0;
09025          } else if (cmd > 0) {
09026             cmd = cmd - '0';
09027             res = close_mailbox(&vms, vmu);
09028             if (res == ERROR_LOCK_PATH)
09029                goto out;
09030             /* If folder is not urgent, set in_urgent to zero! */
09031             if (cmd != 11) in_urgent = 0;
09032             res = open_mailbox(&vms, vmu, cmd);
09033             if (res == ERROR_LOCK_PATH)
09034                goto out;
09035             play_folder = cmd;
09036             cmd = 0;
09037          }
09038          if (useadsi)
09039             adsi_status2(chan, &vms);
09040             
09041          if (!cmd)
09042             cmd = vm_play_folder_name(chan, vms.vmbox);
09043 
09044          vms.starting = 1;
09045          break;
09046       case '3': /* Advanced options */
09047          cmd = 0;
09048          vms.repeats = 0;
09049          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
09050             switch (cmd) {
09051             case '1': /* Reply */
09052                if (vms.lastmsg > -1 && !vms.starting) {
09053                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
09054                   if (cmd == ERROR_LOCK_PATH) {
09055                      res = cmd;
09056                      goto out;
09057                   }
09058                } else
09059                   cmd = ast_play_and_wait(chan, "vm-sorry");
09060                cmd = 't';
09061                break;
09062             case '2': /* Callback */
09063                if (!vms.starting)
09064                   ast_verb(3, "Callback Requested\n");
09065                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
09066                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
09067                   if (cmd == 9) {
09068                      silentexit = 1;
09069                      goto out;
09070                   } else if (cmd == ERROR_LOCK_PATH) {
09071                      res = cmd;
09072                      goto out;
09073                   }
09074                } else 
09075                   cmd = ast_play_and_wait(chan, "vm-sorry");
09076                cmd = 't';
09077                break;
09078             case '3': /* Envelope */
09079                if (vms.lastmsg > -1 && !vms.starting) {
09080                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
09081                   if (cmd == ERROR_LOCK_PATH) {
09082                      res = cmd;
09083                      goto out;
09084                   }
09085                } else
09086                   cmd = ast_play_and_wait(chan, "vm-sorry");
09087                cmd = 't';
09088                break;
09089             case '4': /* Dialout */
09090                if (!ast_strlen_zero(vmu->dialout)) {
09091                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
09092                   if (cmd == 9) {
09093                      silentexit = 1;
09094                      goto out;
09095                   }
09096                } else 
09097                   cmd = ast_play_and_wait(chan, "vm-sorry");
09098                cmd = 't';
09099                break;
09100 
09101             case '5': /* Leave VoiceMail */
09102                if (ast_test_flag(vmu, VM_SVMAIL)) {
09103                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
09104                   if (cmd == ERROR_LOCK_PATH) {
09105                      res = cmd;
09106                      ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
09107                      goto out;
09108                   }
09109                } else
09110                   cmd = ast_play_and_wait(chan,"vm-sorry");
09111                cmd='t';
09112                break;
09113                
09114             case '*': /* Return to main menu */
09115                cmd = 't';
09116                break;
09117 
09118             default:
09119                cmd = 0;
09120                if (!vms.starting) {
09121                   cmd = ast_play_and_wait(chan, "vm-toreply");
09122                }
09123                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
09124                   cmd = ast_play_and_wait(chan, "vm-tocallback");
09125                }
09126                if (!cmd && !vms.starting) {
09127                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
09128                }
09129                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
09130                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
09131                }
09132                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
09133                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
09134                if (!cmd)
09135                   cmd = ast_play_and_wait(chan, "vm-starmain");
09136                if (!cmd)
09137                   cmd = ast_waitfordigit(chan,6000);
09138                if (!cmd)
09139                   vms.repeats++;
09140                if (vms.repeats > 3)
09141                   cmd = 't';
09142             }
09143          }
09144          if (cmd == 't') {
09145             cmd = 0;
09146             vms.repeats = 0;
09147          }
09148          break;
09149       case '4': /* Go to the previous message */
09150          if (vms.curmsg > 0) {
09151             vms.curmsg--;
09152             cmd = play_message(chan, vmu, &vms);
09153          } else {
09154             /* Check if we were listening to new
09155                messages.  If so, go to Urgent messages
09156                instead of saying "no more messages"
09157             */
09158             if (in_urgent == 0 && vms.urgentmessages > 0) {
09159                /* Check for Urgent messages */
09160                in_urgent = 1;
09161                res = close_mailbox(&vms, vmu);
09162                if (res == ERROR_LOCK_PATH)
09163                   goto out;
09164                res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
09165                if (res == ERROR_LOCK_PATH)
09166                   goto out;
09167                ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
09168                vms.curmsg = vms.lastmsg;
09169                if (vms.lastmsg < 0)
09170                   cmd = ast_play_and_wait(chan, "vm-nomore");
09171             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09172                vms.curmsg = vms.lastmsg;
09173                cmd = play_message(chan, vmu, &vms);
09174             } else {
09175                cmd = ast_play_and_wait(chan, "vm-nomore");
09176             }
09177          }
09178          break;
09179       case '6': /* Go to the next message */
09180          if (vms.curmsg < vms.lastmsg) {
09181             vms.curmsg++;
09182             cmd = play_message(chan, vmu, &vms);
09183          } else {
09184             if (in_urgent && vms.newmessages > 0) {
09185                /* Check if we were listening to urgent
09186                 * messages.  If so, go to regular new messages
09187                 * instead of saying "no more messages"
09188                 */
09189                in_urgent = 0;
09190                res = close_mailbox(&vms, vmu);
09191                if (res == ERROR_LOCK_PATH)
09192                   goto out;
09193                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09194                if (res == ERROR_LOCK_PATH)
09195                   goto out;
09196                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09197                vms.curmsg = -1;
09198                if (vms.lastmsg < 0) {
09199                   cmd = ast_play_and_wait(chan, "vm-nomore");
09200                }
09201             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09202                vms.curmsg = 0;
09203                cmd = play_message(chan, vmu, &vms);
09204             } else {
09205                cmd = ast_play_and_wait(chan, "vm-nomore");
09206             }
09207          }
09208          break;
09209       case '7': /* Delete the current message */
09210          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
09211             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
09212             if (useadsi)
09213                adsi_delete(chan, &vms);
09214             if (vms.deleted[vms.curmsg]) {
09215                if (play_folder == 0) {
09216                   if (in_urgent) {
09217                      vms.urgentmessages--;
09218                   } else {
09219                      vms.newmessages--;
09220                   }
09221                }
09222                else if (play_folder == 1)
09223                   vms.oldmessages--;
09224                cmd = ast_play_and_wait(chan, "vm-deleted");
09225             } else {
09226                if (play_folder == 0) {
09227                   if (in_urgent) {
09228                      vms.urgentmessages++;
09229                   } else {
09230                      vms.newmessages++;
09231                   }
09232                }
09233                else if (play_folder == 1)
09234                   vms.oldmessages++;
09235                cmd = ast_play_and_wait(chan, "vm-undeleted");
09236             }
09237             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09238                if (vms.curmsg < vms.lastmsg) {
09239                   vms.curmsg++;
09240                   cmd = play_message(chan, vmu, &vms);
09241                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09242                   vms.curmsg = 0;
09243                   cmd = play_message(chan, vmu, &vms);
09244                } else {
09245                   /* Check if we were listening to urgent
09246                      messages.  If so, go to regular new messages
09247                      instead of saying "no more messages"
09248                   */
09249                   if (in_urgent == 1) {
09250                      /* Check for new messages */
09251                      in_urgent = 0;
09252                      res = close_mailbox(&vms, vmu);
09253                      if (res == ERROR_LOCK_PATH)
09254                         goto out;
09255                      res = open_mailbox(&vms, vmu, NEW_FOLDER);
09256                      if (res == ERROR_LOCK_PATH)
09257                         goto out;
09258                      ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09259                      vms.curmsg = -1;
09260                      if (vms.lastmsg < 0)
09261                         cmd = ast_play_and_wait(chan, "vm-nomore");
09262                   } else {
09263                      cmd = ast_play_and_wait(chan, "vm-nomore");
09264                   }
09265                }
09266             }
09267          } else /* Delete not valid if we haven't selected a message */
09268             cmd = 0;
09269 #ifdef IMAP_STORAGE
09270          deleted = 1;
09271 #endif
09272          break;
09273    
09274       case '8': /* Forward the current messgae */
09275          if (vms.lastmsg > -1) {
09276             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
09277             if (cmd == ERROR_LOCK_PATH) {
09278                res = cmd;
09279                goto out;
09280             }
09281          } else {
09282             /* Check if we were listening to urgent
09283                messages.  If so, go to regular new messages
09284                instead of saying "no more messages"
09285             */
09286             if (in_urgent == 1 && vms.newmessages > 0) {
09287                /* Check for new messages */
09288                in_urgent = 0;
09289                res = close_mailbox(&vms, vmu);
09290                if (res == ERROR_LOCK_PATH)
09291                   goto out;
09292                res = open_mailbox(&vms, vmu, NEW_FOLDER);
09293                if (res == ERROR_LOCK_PATH)
09294                   goto out;
09295                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09296                vms.curmsg = -1;
09297                if (vms.lastmsg < 0)
09298                   cmd = ast_play_and_wait(chan, "vm-nomore");
09299             } else {
09300                cmd = ast_play_and_wait(chan, "vm-nomore");
09301             }
09302          }
09303          break;
09304       case '9': /* Save message to folder */
09305          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
09306             /* No message selected */
09307             cmd = 0;
09308             break;
09309          }
09310          if (useadsi)
09311             adsi_folders(chan, 1, "Save to folder...");
09312          cmd = get_folder2(chan, "vm-savefolder", 1);
09313          box = 0; /* Shut up compiler */
09314          if (cmd == '#') {
09315             cmd = 0;
09316             break;
09317          } else if (cmd > 0) {
09318             box = cmd = cmd - '0';
09319             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
09320             if (cmd == ERROR_LOCK_PATH) {
09321                res = cmd;
09322                goto out;
09323 #ifndef IMAP_STORAGE
09324             } else if (!cmd) {
09325                vms.deleted[vms.curmsg] = 1;
09326 #endif
09327             } else {
09328                vms.deleted[vms.curmsg] = 0;
09329                vms.heard[vms.curmsg] = 0;
09330             }
09331          }
09332          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
09333          if (useadsi)
09334             adsi_message(chan, &vms);
09335          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
09336          if (!cmd) {
09337             cmd = ast_play_and_wait(chan, "vm-message");
09338             if (!cmd) 
09339                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
09340             if (!cmd)
09341                cmd = ast_play_and_wait(chan, "vm-savedto");
09342             if (!cmd)
09343                cmd = vm_play_folder_name(chan, vms.fn);
09344          } else {
09345             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
09346          }
09347          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
09348             if (vms.curmsg < vms.lastmsg) {
09349                vms.curmsg++;
09350                cmd = play_message(chan, vmu, &vms);
09351             } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
09352                vms.curmsg = 0;
09353                cmd = play_message(chan, vmu, &vms);
09354             } else {
09355                /* Check if we were listening to urgent
09356                   messages.  If so, go to regular new messages
09357                   instead of saying "no more messages"
09358                */
09359                if (in_urgent == 1 && vms.newmessages > 0) {
09360                   /* Check for new messages */
09361                   in_urgent = 0;
09362                   res = close_mailbox(&vms, vmu);
09363                   if (res == ERROR_LOCK_PATH)
09364                      goto out;
09365                   res = open_mailbox(&vms, vmu, NEW_FOLDER);
09366                   if (res == ERROR_LOCK_PATH)
09367                      goto out;
09368                   ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
09369                   vms.curmsg = -1;
09370                   if (vms.lastmsg < 0)
09371                      cmd = ast_play_and_wait(chan, "vm-nomore");
09372                } else {
09373                   cmd = ast_play_and_wait(chan, "vm-nomore");
09374                }
09375             }
09376          }
09377          break;
09378       case '*': /* Help */
09379          if (!vms.starting) {
09380             cmd = ast_play_and_wait(chan, "vm-onefor");
09381             if (!cmd)
09382                cmd = vm_play_folder_name(chan, vms.vmbox);
09383             if (!cmd)
09384                cmd = ast_play_and_wait(chan, "vm-opts");
09385             if (!cmd)
09386                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
09387          } else
09388             cmd = 0;
09389          break;
09390       case '0': /* Mailbox options */
09391          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
09392          if (useadsi)
09393             adsi_status(chan, &vms);
09394          break;
09395       default: /* Nothing */
09396          cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
09397          break;
09398       }
09399    }
09400    if ((cmd == 't') || (cmd == '#')) {
09401       /* Timeout */
09402       res = 0;
09403    } else {
09404       /* Hangup */
09405       res = -1;
09406    }
09407 
09408 out:
09409    if (res > -1) {
09410       ast_stopstream(chan);
09411       adsi_goodbye(chan);
09412       if (valid) {
09413          if (silentexit)
09414             res = ast_play_and_wait(chan, "vm-dialout");
09415          else 
09416             res = ast_play_and_wait(chan, "vm-goodbye");
09417          if (res > 0)
09418             res = 0;
09419       }
09420       if (useadsi)
09421          ast_adsi_unload_session(chan);
09422    }
09423    if (vmu)
09424       close_mailbox(&vms, vmu);
09425    if (valid) {
09426       int new = 0, old = 0, urgent = 0;
09427       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
09428       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
09429       /* Urgent flag not passwd to externnotify here */
09430       run_externnotify(vmu->context, vmu->mailbox, NULL);
09431       ast_app_inboxcount2(ext_context, &urgent, &new, &old);
09432       queue_mwi_event(ext_context, urgent, new, old);
09433    }
09434 #ifdef IMAP_STORAGE
09435    /* expunge message - use UID Expunge if supported on IMAP server*/
09436    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
09437    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
09438       ast_mutex_lock(&vms.lock);
09439 #ifdef HAVE_IMAP_TK2006
09440       if (LEVELUIDPLUS (vms.mailstream)) {
09441          mail_expunge_full(vms.mailstream,NIL,EX_UID);
09442       } else 
09443 #endif
09444          mail_expunge(vms.mailstream);
09445       ast_mutex_unlock(&vms.lock);
09446    }
09447    /*  before we delete the state, we should copy pertinent info
09448     *  back to the persistent model */
09449    vmstate_delete(&vms);
09450 #endif
09451    if (vmu)
09452       free_user(vmu);
09453    if (vms.deleted)
09454       ast_free(vms.deleted);
09455    if (vms.heard)
09456       ast_free(vms.heard);
09457 
09458 #ifdef IMAP_STORAGE
09459    pthread_setspecific(ts_vmstate.key, NULL);
09460 #endif
09461    return res;
09462 }
09463 
09464 static int vm_exec(struct ast_channel *chan, void *data)
09465 {
09466    int res = 0;
09467    char *tmp;
09468    struct leave_vm_options leave_options;
09469    struct ast_flags flags = { 0 };
09470    char *opts[OPT_ARG_ARRAY_SIZE];
09471    AST_DECLARE_APP_ARGS(args,
09472       AST_APP_ARG(argv0);
09473       AST_APP_ARG(argv1);
09474    );
09475    
09476    memset(&leave_options, 0, sizeof(leave_options));
09477 
09478    if (chan->_state != AST_STATE_UP)
09479       ast_answer(chan);
09480 
09481    if (!ast_strlen_zero(data)) {
09482       tmp = ast_strdupa(data);
09483       AST_STANDARD_APP_ARGS(args, tmp);
09484       if (args.argc == 2) {
09485          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
09486             return -1;
09487          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
09488          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
09489             int gain;
09490 
09491             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
09492                ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
09493                return -1;
09494             } else {
09495                leave_options.record_gain = (signed char) gain;
09496             }
09497          }
09498          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
09499             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
09500                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
09501          }
09502       }
09503    } else {
09504       char temp[256];
09505       res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
09506       if (res < 0)
09507          return res;
09508       if (ast_strlen_zero(temp))
09509          return 0;
09510       args.argv0 = ast_strdupa(temp);
09511    }
09512 
09513    res = leave_voicemail(chan, args.argv0, &leave_options);
09514 
09515    if (res == ERROR_LOCK_PATH) {
09516       ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
09517       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
09518       res = 0;
09519    }
09520 
09521    return res;
09522 }
09523 
09524 static struct ast_vm_user *find_or_create(const char *context, const char *box)
09525 {
09526    struct ast_vm_user *vmu;
09527 
09528    AST_LIST_TRAVERSE(&users, vmu, list) {
09529       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
09530          if (strcasecmp(vmu->context, context)) {
09531             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
09532                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
09533                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
09534                   \n\tamend your voicemail.conf file to avoid this situation.\n", box);
09535          }
09536          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
09537          return NULL;
09538       }
09539       if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
09540          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
09541          return NULL;
09542       }
09543    }
09544    
09545    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
09546       return NULL;
09547    
09548    ast_copy_string(vmu->context, context, sizeof(vmu->context));
09549    ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
09550 
09551    AST_LIST_INSERT_TAIL(&users, vmu, list);
09552    
09553    return vmu;
09554 }
09555 
09556 static int append_mailbox(const char *context, const char *box, const char *data)
09557 {
09558    /* Assumes lock is already held */
09559    char *tmp;
09560    char *stringp;
09561    char *s;
09562    struct ast_vm_user *vmu;
09563    char *mailbox_full;
09564    int new = 0, old = 0, urgent = 0;
09565 
09566    tmp = ast_strdupa(data);
09567 
09568    if (!(vmu = find_or_create(context, box)))
09569       return -1;
09570    
09571    populate_defaults(vmu);
09572 
09573    stringp = tmp;
09574    if ((s = strsep(&stringp, ","))) 
09575       ast_copy_string(vmu->password, s, sizeof(vmu->password));
09576    if (stringp && (s = strsep(&stringp, ","))) 
09577       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
09578    if (stringp && (s = strsep(&stringp, ","))) 
09579       ast_copy_string(vmu->email, s, sizeof(vmu->email));
09580    if (stringp && (s = strsep(&stringp, ","))) 
09581       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
09582    if (stringp && (s = strsep(&stringp, ","))) 
09583       apply_options(vmu, s);
09584 
09585    mailbox_full = alloca(strlen(box) + strlen(context) + 1);
09586    strcpy(mailbox_full, box);
09587    strcat(mailbox_full, "@");
09588    strcat(mailbox_full, context);
09589 
09590    inboxcount2(mailbox_full, &urgent, &new, &old);
09591    queue_mwi_event(mailbox_full, urgent, new, old);
09592 
09593    return 0;
09594 }
09595 
09596 static int vm_box_exists(struct ast_channel *chan, void *data) 
09597 {
09598    struct ast_vm_user svm;
09599    char *context, *box;
09600    AST_DECLARE_APP_ARGS(args,
09601       AST_APP_ARG(mbox);
09602       AST_APP_ARG(options);
09603    );
09604    static int dep_warning = 0;
09605 
09606    if (ast_strlen_zero(data)) {
09607       ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
09608       return -1;
09609    }
09610 
09611    if (!dep_warning) {
09612       dep_warning = 1;
09613       ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
09614    }
09615 
09616    box = ast_strdupa(data);
09617 
09618    AST_STANDARD_APP_ARGS(args, box);
09619 
09620    if (args.options) {
09621    }
09622 
09623    if ((context = strchr(args.mbox, '@'))) {
09624       *context = '\0';
09625       context++;
09626    }
09627 
09628    if (find_user(&svm, context, args.mbox)) {
09629       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
09630    } else
09631       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
09632 
09633    return 0;
09634 }
09635 
09636 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
09637 {
09638    struct ast_vm_user svm;
09639    AST_DECLARE_APP_ARGS(arg,
09640       AST_APP_ARG(mbox);
09641       AST_APP_ARG(context);
09642    );
09643 
09644    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
09645 
09646    if (ast_strlen_zero(arg.mbox)) {
09647       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
09648       return -1;
09649    }
09650 
09651    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
09652    return 0;
09653 }
09654 
09655 static struct ast_custom_function mailbox_exists_acf = {
09656    .name = "MAILBOX_EXISTS",
09657    .synopsis = "Tell if a mailbox is configured",
09658    .desc =
09659 "Returns a boolean of whether the corresponding mailbox exists.  If context\n"
09660 "is not specified, defaults to the \"default\" context.\n",
09661    .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
09662    .read = acf_mailbox_exists,
09663 };
09664 
09665 static int vmauthenticate(struct ast_channel *chan, void *data)
09666 {
09667    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
09668    struct ast_vm_user vmus;
09669    char *options = NULL;
09670    int silent = 0, skipuser = 0;
09671    int res = -1;
09672    
09673    if (s) {
09674       s = ast_strdupa(s);
09675       user = strsep(&s, ",");
09676       options = strsep(&s, ",");
09677       if (user) {
09678          s = user;
09679          user = strsep(&s, "@");
09680          context = strsep(&s, "");
09681          if (!ast_strlen_zero(user))
09682             skipuser++;
09683          ast_copy_string(mailbox, user, sizeof(mailbox));
09684       }
09685    }
09686 
09687    if (options) {
09688       silent = (strchr(options, 's')) != NULL;
09689    }
09690 
09691    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
09692       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
09693       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
09694       ast_play_and_wait(chan, "auth-thankyou");
09695       res = 0;
09696    }
09697 
09698    return res;
09699 }
09700 
09701 static char *show_users_realtime(int fd, const char *context)
09702 {
09703    struct ast_config *cfg;
09704    const char *cat = NULL;
09705 
09706    if (!(cfg = ast_load_realtime_multientry("voicemail", 
09707       "context", context, SENTINEL))) {
09708       return CLI_FAILURE;
09709    }
09710 
09711    ast_cli(fd,
09712       "\n"
09713       "=============================================================\n"
09714       "=== Configured Voicemail Users ==============================\n"
09715       "=============================================================\n"
09716       "===\n");
09717 
09718    while ((cat = ast_category_browse(cfg, cat))) {
09719       struct ast_variable *var = NULL;
09720       ast_cli(fd,
09721          "=== Mailbox ...\n"
09722          "===\n");
09723       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
09724          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
09725       ast_cli(fd,
09726          "===\n"
09727          "=== ---------------------------------------------------------\n"
09728          "===\n");
09729    }
09730 
09731    ast_cli(fd,
09732       "=============================================================\n"
09733       "\n");
09734 
09735    ast_config_destroy(cfg);
09736 
09737    return CLI_SUCCESS;
09738 }
09739 
09740 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
09741 {
09742    int which = 0;
09743    int wordlen;
09744    struct ast_vm_user *vmu;
09745    const char *context = "";
09746 
09747    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
09748    if (pos > 4)
09749       return NULL;
09750    if (pos == 3)
09751       return (state == 0) ? ast_strdup("for") : NULL;
09752    wordlen = strlen(word);
09753    AST_LIST_TRAVERSE(&users, vmu, list) {
09754       if (!strncasecmp(word, vmu->context, wordlen)) {
09755          if (context && strcmp(context, vmu->context) && ++which > state)
09756             return ast_strdup(vmu->context);
09757          /* ignore repeated contexts ? */
09758          context = vmu->context;
09759       }
09760    }
09761    return NULL;
09762 }
09763 
09764 /*! \brief Show a list of voicemail users in the CLI */
09765 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09766 {
09767    struct ast_vm_user *vmu;
09768 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
09769    const char *context = NULL;
09770    int users_counter = 0;
09771 
09772    switch (cmd) {
09773    case CLI_INIT:
09774       e->command = "voicemail show users";
09775       e->usage =
09776          "Usage: voicemail show users [for <context>]\n"
09777          "       Lists all mailboxes currently set up\n";
09778       return NULL;
09779    case CLI_GENERATE:
09780       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
09781    }  
09782 
09783    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
09784       return CLI_SHOWUSAGE;
09785    if (a->argc == 5) {
09786       if (strcmp(a->argv[3],"for"))
09787          return CLI_SHOWUSAGE;
09788       context = a->argv[4];
09789    }
09790 
09791    if (ast_check_realtime("voicemail")) {
09792       if (!context) {
09793          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
09794          return CLI_SHOWUSAGE;
09795       }
09796       return show_users_realtime(a->fd, context);
09797    }
09798 
09799    AST_LIST_LOCK(&users);
09800    if (AST_LIST_EMPTY(&users)) {
09801       ast_cli(a->fd, "There are no voicemail users currently defined\n");
09802       AST_LIST_UNLOCK(&users);
09803       return CLI_FAILURE;
09804    }
09805    if (a->argc == 3)
09806       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
09807    else {
09808       int count = 0;
09809       AST_LIST_TRAVERSE(&users, vmu, list) {
09810          if (!strcmp(context, vmu->context))
09811             count++;
09812       }
09813       if (count) {
09814          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
09815       } else {
09816          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
09817          AST_LIST_UNLOCK(&users);
09818          return CLI_FAILURE;
09819       }
09820    }
09821    AST_LIST_TRAVERSE(&users, vmu, list) {
09822       int newmsgs = 0, oldmsgs = 0;
09823       char count[12], tmp[256] = "";
09824 
09825       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
09826          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
09827          inboxcount(tmp, &newmsgs, &oldmsgs);
09828          snprintf(count, sizeof(count), "%d", newmsgs);
09829          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
09830          users_counter++;
09831       }
09832    }
09833    AST_LIST_UNLOCK(&users);
09834    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
09835    return CLI_SUCCESS;
09836 }
09837 
09838 /*! \brief Show a list of voicemail zones in the CLI */
09839 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09840 {
09841    struct vm_zone *zone;
09842 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
09843    char *res = CLI_SUCCESS;
09844 
09845    switch (cmd) {
09846    case CLI_INIT:
09847       e->command = "voicemail show zones";
09848       e->usage =
09849          "Usage: voicemail show zones\n"
09850          "       Lists zone message formats\n";
09851       return NULL;
09852    case CLI_GENERATE:
09853       return NULL;
09854    }
09855 
09856    if (a->argc != 3)
09857       return CLI_SHOWUSAGE;
09858 
09859    AST_LIST_LOCK(&zones);
09860    if (!AST_LIST_EMPTY(&zones)) {
09861       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
09862       AST_LIST_TRAVERSE(&zones, zone, list) {
09863          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
09864       }
09865    } else {
09866       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
09867       res = CLI_FAILURE;
09868    }
09869    AST_LIST_UNLOCK(&zones);
09870 
09871    return res;
09872 }
09873 
09874 /*! \brief Reload voicemail configuration from the CLI */
09875 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
09876 {
09877    switch (cmd) {
09878    case CLI_INIT:
09879       e->command = "voicemail reload";
09880       e->usage =
09881          "Usage: voicemail reload\n"
09882          "       Reload voicemail configuration\n";
09883       return NULL;
09884    case CLI_GENERATE:
09885       return NULL;
09886    }
09887 
09888    if (a->argc != 2)
09889       return CLI_SHOWUSAGE;
09890 
09891    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
09892    load_config(1);
09893    
09894    return CLI_SUCCESS;
09895 }
09896 
09897 static struct ast_cli_entry cli_voicemail[] = {
09898    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
09899    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
09900    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
09901 };
09902 
09903 static void poll_subscribed_mailboxes(void)
09904 {
09905    struct mwi_sub *mwi_sub;
09906 
09907    AST_RWLIST_RDLOCK(&mwi_subs);
09908    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
09909       int new = 0, old = 0, urgent = 0;
09910 
09911       if (ast_strlen_zero(mwi_sub->mailbox))
09912          continue;
09913 
09914       inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
09915 
09916       if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
09917          mwi_sub->old_urgent = urgent;
09918          mwi_sub->old_new = new;
09919          mwi_sub->old_old = old;
09920          queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
09921       }
09922    }
09923    AST_RWLIST_UNLOCK(&mwi_subs);
09924 }
09925 
09926 static void *mb_poll_thread(void *data)
09927 {
09928    while (poll_thread_run) {
09929       struct timespec ts = { 0, };
09930       struct timeval wait;
09931 
09932       wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
09933       ts.tv_sec = wait.tv_sec;
09934       ts.tv_nsec = wait.tv_usec * 1000;
09935 
09936       ast_mutex_lock(&poll_lock);
09937       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
09938       ast_mutex_unlock(&poll_lock);
09939 
09940       if (!poll_thread_run)
09941          break;
09942 
09943       poll_subscribed_mailboxes();
09944    }
09945 
09946    return NULL;
09947 }
09948 
09949 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
09950 {
09951    ast_free(mwi_sub);
09952 }
09953 
09954 static int handle_unsubscribe(void *datap)
09955 {
09956    struct mwi_sub *mwi_sub;
09957    uint32_t *uniqueid = datap;
09958    
09959    AST_RWLIST_WRLOCK(&mwi_subs);
09960    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
09961       if (mwi_sub->uniqueid == *uniqueid) {
09962          AST_LIST_REMOVE_CURRENT(entry);
09963          break;
09964       }
09965    }
09966    AST_RWLIST_TRAVERSE_SAFE_END
09967    AST_RWLIST_UNLOCK(&mwi_subs);
09968 
09969    if (mwi_sub)
09970       mwi_sub_destroy(mwi_sub);
09971 
09972    ast_free(uniqueid);  
09973    return 0;
09974 }
09975 
09976 static int handle_subscribe(void *datap)
09977 {
09978    unsigned int len;
09979    struct mwi_sub *mwi_sub;
09980    struct mwi_sub_task *p = datap;
09981 
09982    len = sizeof(*mwi_sub);
09983    if (!ast_strlen_zero(p->mailbox))
09984       len += strlen(p->mailbox);
09985 
09986    if (!ast_strlen_zero(p->context))
09987       len += strlen(p->context) + 1; /* Allow for seperator */
09988 
09989    if (!(mwi_sub = ast_calloc(1, len)))
09990       return -1;
09991 
09992    mwi_sub->uniqueid = p->uniqueid;
09993    if (!ast_strlen_zero(p->mailbox))
09994       strcpy(mwi_sub->mailbox, p->mailbox);
09995 
09996    if (!ast_strlen_zero(p->context)) {
09997       strcat(mwi_sub->mailbox, "@");
09998       strcat(mwi_sub->mailbox, p->context);
09999    }
10000 
10001    AST_RWLIST_WRLOCK(&mwi_subs);
10002    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10003    AST_RWLIST_UNLOCK(&mwi_subs);
10004    ast_free((void *) p->mailbox);
10005    ast_free((void *) p->context);
10006    ast_free(p);   
10007    return 0;
10008 }
10009 
10010 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10011 {
10012    uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10013    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10014       return;
10015 
10016    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10017       return;
10018 
10019    u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10020    *uniqueid = u;
10021    if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10022       ast_free(uniqueid);
10023    }
10024 }
10025 
10026 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10027 {
10028    struct mwi_sub_task *mwist;
10029    
10030    if (ast_event_get_type(event) != AST_EVENT_SUB)
10031       return;
10032 
10033    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10034       return;
10035 
10036    if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10037       ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10038       return;
10039    }
10040    mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10041    mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10042    mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10043    
10044    if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10045       ast_free(mwist);
10046    }
10047 }
10048 
10049 static void start_poll_thread(void)
10050 {
10051    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10052       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10053       AST_EVENT_IE_END);
10054 
10055    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10056       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10057       AST_EVENT_IE_END);
10058 
10059    if (mwi_sub_sub)
10060       ast_event_report_subs(mwi_sub_sub);
10061 
10062    poll_thread_run = 1;
10063 
10064    ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10065 }
10066 
10067 static void stop_poll_thread(void)
10068 {
10069    poll_thread_run = 0;
10070 
10071    if (mwi_sub_sub) {
10072       ast_event_unsubscribe(mwi_sub_sub);
10073       mwi_sub_sub = NULL;
10074    }
10075 
10076    if (mwi_unsub_sub) {
10077       ast_event_unsubscribe(mwi_unsub_sub);
10078       mwi_unsub_sub = NULL;
10079    }
10080 
10081    ast_mutex_lock(&poll_lock);
10082    ast_cond_signal(&poll_cond);
10083    ast_mutex_unlock(&poll_lock);
10084 
10085    pthread_join(poll_thread, NULL);
10086 
10087    poll_thread = AST_PTHREADT_NULL;
10088 }
10089 
10090 /*! \brief Manager list voicemail users command */
10091 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10092 {
10093    struct ast_vm_user *vmu = NULL;
10094    const char *id = astman_get_header(m, "ActionID");
10095    char actionid[128] = "";
10096 
10097    if (!ast_strlen_zero(id))
10098       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10099 
10100    AST_LIST_LOCK(&users);
10101 
10102    if (AST_LIST_EMPTY(&users)) {
10103       astman_send_ack(s, m, "There are no voicemail users currently defined.");
10104       AST_LIST_UNLOCK(&users);
10105       return RESULT_SUCCESS;
10106    }
10107    
10108    astman_send_ack(s, m, "Voicemail user list will follow");
10109    
10110    AST_LIST_TRAVERSE(&users, vmu, list) {
10111       char dirname[256];
10112 
10113 #ifdef IMAP_STORAGE
10114       int new, old;
10115       inboxcount(vmu->mailbox, &new, &old);
10116 #endif
10117       
10118       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10119       astman_append(s,
10120          "%s"
10121          "Event: VoicemailUserEntry\r\n"
10122          "VMContext: %s\r\n"
10123          "VoiceMailbox: %s\r\n"
10124          "Fullname: %s\r\n"
10125          "Email: %s\r\n"
10126          "Pager: %s\r\n"
10127          "ServerEmail: %s\r\n"
10128          "MailCommand: %s\r\n"
10129          "Language: %s\r\n"
10130          "TimeZone: %s\r\n"
10131          "Callback: %s\r\n"
10132          "Dialout: %s\r\n"
10133          "UniqueID: %s\r\n"
10134          "ExitContext: %s\r\n"
10135          "SayDurationMinimum: %d\r\n"
10136          "SayEnvelope: %s\r\n"
10137          "SayCID: %s\r\n"
10138          "AttachMessage: %s\r\n"
10139          "AttachmentFormat: %s\r\n"
10140          "DeleteMessage: %s\r\n"
10141          "VolumeGain: %.2f\r\n"
10142          "CanReview: %s\r\n"
10143          "CallOperator: %s\r\n"
10144          "MaxMessageCount: %d\r\n"
10145          "MaxMessageLength: %d\r\n"
10146          "NewMessageCount: %d\r\n"
10147 #ifdef IMAP_STORAGE
10148          "OldMessageCount: %d\r\n"
10149          "IMAPUser: %s\r\n"
10150 #endif
10151          "\r\n",
10152          actionid,
10153          vmu->context,
10154          vmu->mailbox,
10155          vmu->fullname,
10156          vmu->email,
10157          vmu->pager,
10158          vmu->serveremail,
10159          vmu->mailcmd,
10160          vmu->language,
10161          vmu->zonetag,
10162          vmu->callback,
10163          vmu->dialout,
10164          vmu->uniqueid,
10165          vmu->exit,
10166          vmu->saydurationm,
10167          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10168          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10169          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10170          vmu->attachfmt,
10171          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10172          vmu->volgain,
10173          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10174          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10175          vmu->maxmsg,
10176          vmu->maxsecs,
10177 #ifdef IMAP_STORAGE
10178          new, old, vmu->imapuser
10179 #else
10180          count_messages(vmu, dirname)
10181 #endif
10182          );
10183    }     
10184    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10185 
10186    AST_LIST_UNLOCK(&users);
10187 
10188    return RESULT_SUCCESS;
10189 }
10190 
10191 /*! \brief Free the users structure. */
10192 static void free_vm_users(void) 
10193 {
10194    struct ast_vm_user *current;
10195    AST_LIST_LOCK(&users);
10196    while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10197       ast_set_flag(current, VM_ALLOCED);
10198       free_user(current);
10199    }
10200    AST_LIST_UNLOCK(&users);
10201 }
10202 
10203 /*! \brief Free the zones structure. */
10204 static void free_vm_zones(void)
10205 {
10206    struct vm_zone *zcur;
10207    AST_LIST_LOCK(&zones);
10208    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10209       free_zone(zcur);
10210    AST_LIST_UNLOCK(&zones);
10211 }
10212 
10213 static char *substitute_escapes(const char *value)
10214 {
10215    char *current, *result;
10216 
10217    /* Add 16 for fudge factor */
10218    struct ast_str *str = ast_str_create(strlen(value) + 16);
10219 
10220    /* Substitute strings \r, \n, and \t into the appropriate characters */
10221    for (current = (char *) value; *current; current++) {
10222       if (*current == '\\') {
10223          current++;
10224          if (!*current) {
10225             ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10226             break;
10227          }
10228          switch (*current) {
10229          case 'r':
10230             ast_str_append(&str, 0, "\r");
10231             break;
10232          case 'n':
10233 #ifdef IMAP_STORAGE
10234             if (!str->used || str->str[str->used - 1] != '\r') {
10235                ast_str_append(&str, 0, "\r");
10236             }
10237 #endif
10238             ast_str_append(&str, 0, "\n");
10239             break;
10240          case 't':
10241             ast_str_append(&str, 0, "\t");
10242             break;
10243          default:
10244             ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10245             break;
10246          }
10247       } else {
10248          ast_str_append(&str, 0, "%c", *current);
10249       }
10250    }
10251 
10252    result = ast_strdup(str->str);
10253    ast_free(str);
10254 
10255    return result;
10256 }
10257 
10258 static int load_config(int reload)
10259 {
10260    struct ast_vm_user *current;
10261    struct ast_config *cfg, *ucfg;
10262    char *cat;
10263    struct ast_variable *var;
10264    const char *val;
10265    char *q, *stringp;
10266    int x;
10267    int tmpadsi[4];
10268    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10269 
10270    ast_unload_realtime("voicemail");
10271    ast_unload_realtime("voicemail_data");
10272 
10273    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10274       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
10275          return 0;
10276       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10277       cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
10278    } else {
10279       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10280       ucfg = ast_config_load("users.conf", config_flags);
10281    }
10282 #ifdef IMAP_STORAGE
10283    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10284 #endif
10285    /* set audio control prompts */
10286    strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10287    strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10288    strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10289    strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10290    strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10291 
10292    /* Free all the users structure */  
10293    free_vm_users();
10294 
10295    /* Free all the zones structure */
10296    free_vm_zones();
10297 
10298    AST_LIST_LOCK(&users);  
10299 
10300    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10301    memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10302 
10303    if (cfg) {
10304       /* General settings */
10305 
10306       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10307          val = "default";
10308       ast_copy_string(userscontext, val, sizeof(userscontext));
10309       /* Attach voice message to mail message ? */
10310       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
10311          val = "yes";
10312       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
10313 
10314       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10315          val = "no";
10316       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10317 
10318       volgain = 0.0;
10319       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10320          sscanf(val, "%lf", &volgain);
10321 
10322 #ifdef ODBC_STORAGE
10323       strcpy(odbc_database, "asterisk");
10324       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10325          ast_copy_string(odbc_database, val, sizeof(odbc_database));
10326       }
10327       strcpy(odbc_table, "voicemessages");
10328       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10329          ast_copy_string(odbc_table, val, sizeof(odbc_table));
10330       }
10331 #endif      
10332       /* Mail command */
10333       strcpy(mailcmd, SENDMAIL);
10334       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10335          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10336 
10337       maxsilence = 0;
10338       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10339          maxsilence = atoi(val);
10340          if (maxsilence > 0)
10341             maxsilence *= 1000;
10342       }
10343       
10344       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10345          maxmsg = MAXMSG;
10346       } else {
10347          maxmsg = atoi(val);
10348          if (maxmsg <= 0) {
10349             ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10350             maxmsg = MAXMSG;
10351          } else if (maxmsg > MAXMSGLIMIT) {
10352             ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10353             maxmsg = MAXMSGLIMIT;
10354          }
10355       }
10356 
10357       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10358          maxdeletedmsg = 0;
10359       } else {
10360          if (sscanf(val, "%d", &x) == 1)
10361             maxdeletedmsg = x;
10362          else if (ast_true(val))
10363             maxdeletedmsg = MAXMSG;
10364          else
10365             maxdeletedmsg = 0;
10366 
10367          if (maxdeletedmsg < 0) {
10368             ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10369             maxdeletedmsg = MAXMSG;
10370          } else if (maxdeletedmsg > MAXMSGLIMIT) {
10371             ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10372             maxdeletedmsg = MAXMSGLIMIT;
10373          }
10374       }
10375 
10376       /* Load date format config for voicemail mail */
10377       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10378          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10379       }
10380 
10381       /* External password changing command */
10382       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10383          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10384          pwdchange = PWDCHANGE_EXTERNAL;
10385       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10386          ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10387          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10388       }
10389  
10390       /* External password validation command */
10391       if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10392          ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10393          ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10394       }
10395 
10396 #ifdef IMAP_STORAGE
10397       /* IMAP server address */
10398       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10399          ast_copy_string(imapserver, val, sizeof(imapserver));
10400       } else {
10401          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10402       }
10403       /* IMAP server port */
10404       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10405          ast_copy_string(imapport, val, sizeof(imapport));
10406       } else {
10407          ast_copy_string(imapport,"143", sizeof(imapport));
10408       }
10409       /* IMAP server flags */
10410       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10411          ast_copy_string(imapflags, val, sizeof(imapflags));
10412       }
10413       /* IMAP server master username */
10414       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10415          ast_copy_string(authuser, val, sizeof(authuser));
10416       }
10417       /* IMAP server master password */
10418       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10419          ast_copy_string(authpassword, val, sizeof(authpassword));
10420       }
10421       /* Expunge on exit */
10422       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10423          if (ast_false(val))
10424             expungeonhangup = 0;
10425          else
10426             expungeonhangup = 1;
10427       } else {
10428          expungeonhangup = 1;
10429       }
10430       /* IMAP voicemail folder */
10431       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10432          ast_copy_string(imapfolder, val, sizeof(imapfolder));
10433       } else {
10434          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10435       }
10436       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10437          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10438       }
10439       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10440          imapgreetings = ast_true(val);
10441       } else {
10442          imapgreetings = 0;
10443       }
10444       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10445          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10446       } else {
10447          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10448       }
10449 
10450       /* There is some very unorthodox casting done here. This is due
10451        * to the way c-client handles the argument passed in. It expects a 
10452        * void pointer and casts the pointer directly to a long without
10453        * first dereferencing it. */
10454       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10455          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10456       } else {
10457          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10458       }
10459 
10460       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10461          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10462       } else {
10463          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10464       }
10465 
10466       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10467          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10468       } else {
10469          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10470       }
10471 
10472       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10473          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10474       } else {
10475          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10476       }
10477 
10478 #endif
10479       /* External voicemail notify application */
10480       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10481          ast_copy_string(externnotify, val, sizeof(externnotify));
10482          ast_debug(1, "found externnotify: %s\n", externnotify);
10483       } else {
10484          externnotify[0] = '\0';
10485       }
10486 
10487       /* SMDI voicemail notification */
10488       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10489          ast_debug(1, "Enabled SMDI voicemail notification\n");
10490          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10491             smdi_iface = ast_smdi_interface_find(val);
10492          } else {
10493             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10494             smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
10495          }
10496          if (!smdi_iface) {
10497             ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10498          } 
10499       }
10500 
10501       /* Silence treshold */
10502       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10503       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10504          silencethreshold = atoi(val);
10505       
10506       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
10507          val = ASTERISK_USERNAME;
10508       ast_copy_string(serveremail, val, sizeof(serveremail));
10509       
10510       vmmaxsecs = 0;
10511       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10512          if (sscanf(val, "%d", &x) == 1) {
10513             vmmaxsecs = x;
10514          } else {
10515             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10516          }
10517       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10518          static int maxmessage_deprecate = 0;
10519          if (maxmessage_deprecate == 0) {
10520             maxmessage_deprecate = 1;
10521             ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10522          }
10523          if (sscanf(val, "%d", &x) == 1) {
10524             vmmaxsecs = x;
10525          } else {
10526             ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10527          }
10528       }
10529 
10530       vmminsecs = 0;
10531       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10532          if (sscanf(val, "%d", &x) == 1) {
10533             vmminsecs = x;
10534             if (maxsilence <= vmminsecs)
10535                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10536          } else {
10537             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10538          }
10539       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10540          static int maxmessage_deprecate = 0;
10541          if (maxmessage_deprecate == 0) {
10542             maxmessage_deprecate = 1;
10543             ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10544          }
10545          if (sscanf(val, "%d", &x) == 1) {
10546             vmminsecs = x;
10547             if (maxsilence <= vmminsecs)
10548                ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10549          } else {
10550             ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10551          }
10552       }
10553 
10554       val = ast_variable_retrieve(cfg, "general", "format");
10555       if (!val)
10556          val = "wav";   
10557       ast_copy_string(vmfmts, val, sizeof(vmfmts));
10558 
10559       skipms = 3000;
10560       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10561          if (sscanf(val, "%d", &x) == 1) {
10562             maxgreet = x;
10563          } else {
10564             ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10565          }
10566       }
10567 
10568       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10569          if (sscanf(val, "%d", &x) == 1) {
10570             skipms = x;
10571          } else {
10572             ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10573          }
10574       }
10575 
10576       maxlogins = 3;
10577       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10578          if (sscanf(val, "%d", &x) == 1) {
10579             maxlogins = x;
10580          } else {
10581             ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10582          }
10583       }
10584 
10585       minpassword = MINPASSWORD;
10586       if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
10587          if (sscanf(val, "%d", &x) == 1) {
10588             minpassword = x;
10589          } else {
10590             ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
10591          }
10592       }
10593 
10594       /* Force new user to record name ? */
10595       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
10596          val = "no";
10597       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
10598 
10599       /* Force new user to record greetings ? */
10600       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
10601          val = "no";
10602       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
10603 
10604       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
10605          ast_debug(1, "VM_CID Internal context string: %s\n", val);
10606          stringp = ast_strdupa(val);
10607          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
10608             if (!ast_strlen_zero(stringp)) {
10609                q = strsep(&stringp, ",");
10610                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
10611                   q++;
10612                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
10613                ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
10614             } else {
10615                cidinternalcontexts[x][0] = '\0';
10616             }
10617          }
10618       }
10619       if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
10620          ast_debug(1,"VM Review Option disabled globally\n");
10621          val = "no";
10622       }
10623       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
10624 
10625       /* Temporary greeting reminder */
10626       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
10627          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
10628          val = "no";
10629       } else {
10630          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
10631       }
10632       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
10633       if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
10634          ast_debug(1, "VM next message wrap disabled globally\n");
10635          val = "no";
10636       }
10637       ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);  
10638 
10639       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
10640          ast_debug(1,"VM Operator break disabled globally\n");
10641          val = "no";
10642       }
10643       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
10644 
10645       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
10646          ast_debug(1,"VM CID Info before msg disabled globally\n");
10647          val = "no";
10648       } 
10649       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
10650 
10651       if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
10652          ast_debug(1,"Send Voicemail msg disabled globally\n");
10653          val = "no";
10654       }
10655       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
10656    
10657       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
10658          ast_debug(1,"ENVELOPE before msg enabled globally\n");
10659          val = "yes";
10660       }
10661       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
10662 
10663       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
10664          ast_debug(1,"Move Heard enabled globally\n");
10665          val = "yes";
10666       }
10667       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
10668 
10669       if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
10670          ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
10671          val = "no";
10672       }
10673       ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);   
10674 
10675       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
10676          ast_debug(1,"Duration info before msg enabled globally\n");
10677          val = "yes";
10678       }
10679       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
10680 
10681       saydurationminfo = 2;
10682       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
10683          if (sscanf(val, "%d", &x) == 1) {
10684             saydurationminfo = x;
10685          } else {
10686             ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
10687          }
10688       }
10689 
10690       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
10691          ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
10692          val = "no";
10693       }
10694       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
10695 
10696       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
10697          ast_copy_string(dialcontext, val, sizeof(dialcontext));
10698          ast_debug(1, "found dialout context: %s\n", dialcontext);
10699       } else {
10700          dialcontext[0] = '\0';  
10701       }
10702       
10703       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
10704          ast_copy_string(callcontext, val, sizeof(callcontext));
10705          ast_debug(1, "found callback context: %s\n", callcontext);
10706       } else {
10707          callcontext[0] = '\0';
10708       }
10709 
10710       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
10711          ast_copy_string(exitcontext, val, sizeof(exitcontext));
10712          ast_debug(1, "found operator context: %s\n", exitcontext);
10713       } else {
10714          exitcontext[0] = '\0';
10715       }
10716       
10717       /* load password sounds configuration */
10718       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
10719          ast_copy_string(vm_password, val, sizeof(vm_password));
10720       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
10721          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
10722       if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
10723          ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
10724       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
10725          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
10726       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
10727          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
10728       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
10729          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
10730       /* load configurable audio prompts */
10731       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
10732          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
10733       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
10734          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
10735       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
10736          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
10737       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
10738          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
10739       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
10740          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
10741 
10742       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
10743          val = "no";
10744       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
10745 
10746       poll_freq = DEFAULT_POLL_FREQ;
10747       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
10748          if (sscanf(val, "%u", &poll_freq) != 1) {
10749             poll_freq = DEFAULT_POLL_FREQ;
10750             ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
10751          }
10752       }
10753 
10754       poll_mailboxes = 0;
10755       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
10756          poll_mailboxes = ast_true(val);
10757 
10758       if (ucfg) { 
10759          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
10760             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
10761                continue;
10762             if ((current = find_or_create(userscontext, cat))) {
10763                populate_defaults(current);
10764                apply_options_full(current, ast_variable_browse(ucfg, cat));
10765                ast_copy_string(current->context, userscontext, sizeof(current->context));
10766             }
10767          }
10768          ast_config_destroy(ucfg);
10769       }
10770       cat = ast_category_browse(cfg, NULL);
10771       while (cat) {
10772          if (strcasecmp(cat, "general")) {
10773             var = ast_variable_browse(cfg, cat);
10774             if (strcasecmp(cat, "zonemessages")) {
10775                /* Process mailboxes in this context */
10776                while (var) {
10777                   append_mailbox(cat, var->name, var->value);
10778                   var = var->next;
10779                }
10780             } else {
10781                /* Timezones in this context */
10782                while (var) {
10783                   struct vm_zone *z;
10784                   if ((z = ast_malloc(sizeof(*z)))) {
10785                      char *msg_format, *tzone;
10786                      msg_format = ast_strdupa(var->value);
10787                      tzone = strsep(&msg_format, "|");
10788                      if (msg_format) {
10789                         ast_copy_string(z->name, var->name, sizeof(z->name));
10790                         ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
10791                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
10792                         AST_LIST_LOCK(&zones);
10793                         AST_LIST_INSERT_HEAD(&zones, z, list);
10794                         AST_LIST_UNLOCK(&zones);
10795                      } else {
10796                         ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
10797                         ast_free(z);
10798                      }
10799                   } else {
10800                      AST_LIST_UNLOCK(&users);
10801                      ast_config_destroy(cfg);
10802                      return -1;
10803                   }
10804                   var = var->next;
10805                }
10806             }
10807          }
10808          cat = ast_category_browse(cfg, cat);
10809       }
10810       memset(fromstring, 0, sizeof(fromstring));
10811       memset(pagerfromstring, 0, sizeof(pagerfromstring));
10812       strcpy(charset, "ISO-8859-1");
10813       if (emailbody) {
10814          ast_free(emailbody);
10815          emailbody = NULL;
10816       }
10817       if (emailsubject) {
10818          ast_free(emailsubject);
10819          emailsubject = NULL;
10820       }
10821       if (pagerbody) {
10822          ast_free(pagerbody);
10823          pagerbody = NULL;
10824       }
10825       if (pagersubject) {
10826          ast_free(pagersubject);
10827          pagersubject = NULL;
10828       }
10829       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
10830          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
10831       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
10832          ast_copy_string(fromstring, val, sizeof(fromstring));
10833       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
10834          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
10835       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
10836          ast_copy_string(charset, val, sizeof(charset));
10837       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
10838          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
10839          for (x = 0; x < 4; x++) {
10840             memcpy(&adsifdn[x], &tmpadsi[x], 1);
10841          }
10842       }
10843       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
10844          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
10845          for (x = 0; x < 4; x++) {
10846             memcpy(&adsisec[x], &tmpadsi[x], 1);
10847          }
10848       }
10849       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
10850          if (atoi(val)) {
10851             adsiver = atoi(val);
10852          }
10853       }
10854       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
10855          ast_copy_string(zonetag, val, sizeof(zonetag));
10856       }
10857       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
10858          emailsubject = ast_strdup(val);
10859       }
10860       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
10861          emailbody = substitute_escapes(val);
10862       }
10863       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
10864          pagersubject = ast_strdup(val);
10865       }
10866       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
10867          pagerbody = substitute_escapes(val);
10868       }
10869       AST_LIST_UNLOCK(&users);
10870       ast_config_destroy(cfg);
10871 
10872       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
10873          start_poll_thread();
10874       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
10875          stop_poll_thread();;
10876 
10877       return 0;
10878    } else {
10879       AST_LIST_UNLOCK(&users);
10880       ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
10881       if (ucfg)
10882          ast_config_destroy(ucfg);
10883       return 0;
10884    }
10885 }
10886 
10887 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
10888 {
10889    int res = -1;
10890    char dir[PATH_MAX];
10891    snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
10892    ast_debug(2, "About to try retrieving name file %s\n", dir);
10893    RETRIEVE(dir, -1, mailbox, context);
10894    if (ast_fileexists(dir, NULL, NULL)) {
10895       res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
10896    }
10897    DISPOSE(dir, -1);
10898    return res;
10899 }
10900 
10901 static int reload(void)
10902 {
10903    return load_config(1);
10904 }
10905 
10906 static int unload_module(void)
10907 {
10908    int res;
10909 
10910    res = ast_unregister_application(app);
10911    res |= ast_unregister_application(app2);
10912    res |= ast_unregister_application(app3);
10913    res |= ast_unregister_application(app4);
10914    res |= ast_custom_function_unregister(&mailbox_exists_acf);
10915    res |= ast_manager_unregister("VoicemailUsersList");
10916    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
10917    ast_uninstall_vm_functions();
10918 
10919    if (poll_thread != AST_PTHREADT_NULL)
10920       stop_poll_thread();
10921 
10922    mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
10923    ast_unload_realtime("voicemail");
10924    ast_unload_realtime("voicemail_data");
10925 
10926    free_vm_users();
10927    free_vm_zones();
10928    return res;
10929 }
10930 
10931 static int load_module(void)
10932 {
10933    int res;
10934    my_umask = umask(0);
10935    umask(my_umask);
10936 
10937    /* compute the location of the voicemail spool directory */
10938    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
10939    
10940    if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
10941       ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
10942    }
10943 
10944    if ((res = load_config(0)))
10945       return res;
10946 
10947    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
10948    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
10949    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
10950    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
10951    res |= ast_custom_function_register(&mailbox_exists_acf);
10952    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
10953    if (res)
10954       return res;
10955 
10956    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
10957 
10958    ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
10959    ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
10960    ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
10961 
10962    return res;
10963 }
10964 
10965 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
10966 {
10967    int cmd = 0;
10968    char destination[80] = "";
10969    int retries = 0;
10970 
10971    if (!num) {
10972       ast_verb(3, "Destination number will be entered manually\n");
10973       while (retries < 3 && cmd != 't') {
10974          destination[1] = '\0';
10975          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
10976          if (!cmd)
10977             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
10978          if (!cmd)
10979             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
10980          if (!cmd) {
10981             cmd = ast_waitfordigit(chan, 6000);
10982             if (cmd)
10983                destination[0] = cmd;
10984          }
10985          if (!cmd) {
10986             retries++;
10987          } else {
10988 
10989             if (cmd < 0)
10990                return 0;
10991             if (cmd == '*') {
10992                ast_verb(3, "User hit '*' to cancel outgoing call\n");
10993                return 0;
10994             }
10995             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
10996                retries++;
10997             else
10998                cmd = 't';
10999          }
11000       }
11001       if (retries >= 3) {
11002          return 0;
11003       }
11004       
11005    } else {
11006       if (option_verbose > 2)
11007          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11008       ast_copy_string(destination, num, sizeof(destination));
11009    }
11010 
11011    if (!ast_strlen_zero(destination)) {
11012       if (destination[strlen(destination) -1 ] == '*')
11013          return 0; 
11014       if (option_verbose > 2)
11015          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11016       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11017       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11018       chan->priority = 0;
11019       return 9;
11020    }
11021    return 0;
11022 }
11023 
11024 /*!
11025  * \brief The advanced options within a message.
11026  * \param chan
11027  * \param vmu 
11028  * \param vms
11029  * \param msg
11030  * \param option
11031  * \param record_gain
11032  *
11033  * Provides handling for the play message envelope, call the person back, or reply to message. 
11034  *
11035  * \return zero on success, -1 on error.
11036  */
11037 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)
11038 {
11039    int res = 0;
11040    char filename[PATH_MAX];
11041    struct ast_config *msg_cfg = NULL;
11042    const char *origtime, *context;
11043    char *name, *num;
11044    int retries = 0;
11045    char *cid;
11046    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11047 
11048    vms->starting = 0; 
11049 
11050    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11051 
11052    /* Retrieve info from VM attribute file */
11053    snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11054    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11055    msg_cfg = ast_config_load(filename, config_flags);
11056    DISPOSE(vms->curdir, vms->curmsg);
11057    if (!msg_cfg) {
11058       ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11059       return 0;
11060    }
11061 
11062    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11063       ast_config_destroy(msg_cfg);
11064       return 0;
11065    }
11066 
11067    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11068 
11069    context = ast_variable_retrieve(msg_cfg, "message", "context");
11070    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11071       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11072    switch (option) {
11073    case 3: /* Play message envelope */
11074       if (!res)
11075          res = play_message_datetime(chan, vmu, origtime, filename);
11076       if (!res)
11077          res = play_message_callerid(chan, vms, cid, context, 0);
11078 
11079       res = 't';
11080       break;
11081 
11082    case 2:  /* Call back */
11083 
11084       if (ast_strlen_zero(cid))
11085          break;
11086 
11087       ast_callerid_parse(cid, &name, &num);
11088       while ((res > -1) && (res != 't')) {
11089          switch (res) {
11090          case '1':
11091             if (num) {
11092                /* Dial the CID number */
11093                res = dialout(chan, vmu, num, vmu->callback);
11094                if (res) {
11095                   ast_config_destroy(msg_cfg);
11096                   return 9;
11097                }
11098             } else {
11099                res = '2';
11100             }
11101             break;
11102 
11103          case '2':
11104             /* Want to enter a different number, can only do this if there's a dialout context for this user */
11105             if (!ast_strlen_zero(vmu->dialout)) {
11106                res = dialout(chan, vmu, NULL, vmu->dialout);
11107                if (res) {
11108                   ast_config_destroy(msg_cfg);
11109                   return 9;
11110                }
11111             } else {
11112                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11113                res = ast_play_and_wait(chan, "vm-sorry");
11114             }
11115             ast_config_destroy(msg_cfg);
11116             return res;
11117          case '*':
11118             res = 't';
11119             break;
11120          case '3':
11121          case '4':
11122          case '5':
11123          case '6':
11124          case '7':
11125          case '8':
11126          case '9':
11127          case '0':
11128 
11129             res = ast_play_and_wait(chan, "vm-sorry");
11130             retries++;
11131             break;
11132          default:
11133             if (num) {
11134                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11135                res = ast_play_and_wait(chan, "vm-num-i-have");
11136                if (!res)
11137                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
11138                if (!res)
11139                   res = ast_play_and_wait(chan, "vm-tocallnum");
11140                /* Only prompt for a caller-specified number if there is a dialout context specified */
11141                if (!ast_strlen_zero(vmu->dialout)) {
11142                   if (!res)
11143                      res = ast_play_and_wait(chan, "vm-calldiffnum");
11144                }
11145             } else {
11146                res = ast_play_and_wait(chan, "vm-nonumber");
11147                if (!ast_strlen_zero(vmu->dialout)) {
11148                   if (!res)
11149                      res = ast_play_and_wait(chan, "vm-toenternumber");
11150                }
11151             }
11152             if (!res)
11153                res = ast_play_and_wait(chan, "vm-star-cancel");
11154             if (!res)
11155                res = ast_waitfordigit(chan, 6000);
11156             if (!res) {
11157                retries++;
11158                if (retries > 3)
11159                   res = 't';
11160             }
11161             break; 
11162             
11163          }
11164          if (res == 't')
11165             res = 0;
11166          else if (res == '*')
11167             res = -1;
11168       }
11169       break;
11170       
11171    case 1:  /* Reply */
11172       /* Send reply directly to sender */
11173       if (ast_strlen_zero(cid))
11174          break;
11175 
11176       ast_callerid_parse(cid, &name, &num);
11177       if (!num) {
11178          ast_verb(3, "No CID number available, no reply sent\n");
11179          if (!res)
11180             res = ast_play_and_wait(chan, "vm-nonumber");
11181          ast_config_destroy(msg_cfg);
11182          return res;
11183       } else {
11184          struct ast_vm_user vmu2;
11185          if (find_user(&vmu2, vmu->context, num)) {
11186             struct leave_vm_options leave_options;
11187             char mailbox[AST_MAX_EXTENSION * 2 + 2];
11188             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11189 
11190             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11191             
11192             memset(&leave_options, 0, sizeof(leave_options));
11193             leave_options.record_gain = record_gain;
11194             res = leave_voicemail(chan, mailbox, &leave_options);
11195             if (!res)
11196                res = 't';
11197             ast_config_destroy(msg_cfg);
11198             return res;
11199          } else {
11200             /* Sender has no mailbox, can't reply */
11201             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11202             ast_play_and_wait(chan, "vm-nobox");
11203             res = 't';
11204             ast_config_destroy(msg_cfg);
11205             return res;
11206          }
11207       } 
11208       res = 0;
11209 
11210       break;
11211    }
11212 
11213 #ifndef IMAP_STORAGE
11214    ast_config_destroy(msg_cfg);
11215 
11216    if (!res) {
11217       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11218       vms->heard[msg] = 1;
11219       res = wait_file(chan, vms, vms->fn);
11220    }
11221 #endif
11222    return res;
11223 }
11224 
11225 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11226          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11227          signed char record_gain, struct vm_state *vms, char *flag)
11228 {
11229    /* Record message & let caller review or re-record it, or set options if applicable */
11230    int res = 0;
11231    int cmd = 0;
11232    int max_attempts = 3;
11233    int attempts = 0;
11234    int recorded = 0;
11235    int msg_exists = 0;
11236    signed char zero_gain = 0;
11237    char tempfile[PATH_MAX];
11238    char *acceptdtmf = "#";
11239    char *canceldtmf = "";
11240 
11241    /* Note that urgent and private are for flagging messages as such in the future */
11242 
11243    /* barf if no pointer passed to store duration in */
11244    if (duration == NULL) {
11245       ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11246       return -1;
11247    }
11248 
11249    if (!outsidecaller)
11250       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11251    else
11252       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11253 
11254    cmd = '3';  /* Want to start by recording */
11255 
11256    while ((cmd >= 0) && (cmd != 't')) {
11257       switch (cmd) {
11258       case '1':
11259          if (!msg_exists) {
11260             /* In this case, 1 is to record a message */
11261             cmd = '3';
11262             break;
11263          } else {
11264             /* Otherwise 1 is to save the existing message */
11265             ast_verb(3, "Saving message as is\n");
11266             if (!outsidecaller) 
11267                ast_filerename(tempfile, recordfile, NULL);
11268             ast_stream_and_wait(chan, "vm-msgsaved", "");
11269             if (!outsidecaller) {
11270                /* Saves to IMAP server - but SHOULD save to filesystem ONLY if recording greetings! */
11271 #ifndef IMAP_STORAGE
11272                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11273                DISPOSE(recordfile, -1);
11274 #endif
11275             }
11276             cmd = 't';
11277             return res;
11278          }
11279       case '2':
11280          /* Review */
11281          ast_verb(3, "Reviewing the message\n");
11282          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11283          break;
11284       case '3':
11285          msg_exists = 0;
11286          /* Record */
11287          if (recorded == 1) 
11288             ast_verb(3, "Re-recording the message\n");
11289          else  
11290             ast_verb(3, "Recording the message\n");
11291          
11292          if (recorded && outsidecaller) {
11293             cmd = ast_play_and_wait(chan, INTRO);
11294             cmd = ast_play_and_wait(chan, "beep");
11295          }
11296          recorded = 1;
11297          /* 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 */
11298          if (record_gain)
11299             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11300          if (ast_test_flag(vmu, VM_OPERATOR))
11301             canceldtmf = "0";
11302          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11303          if (record_gain)
11304             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11305          if (cmd == -1) {
11306             /* User has hung up, no options to give */
11307             if (!outsidecaller) {
11308                /* user was recording a greeting and they hung up, so let's delete the recording. */
11309                ast_filedelete(tempfile, NULL);
11310             }     
11311             return cmd;
11312          }
11313          if (cmd == '0') {
11314             break;
11315          } else if (cmd == '*') {
11316             break;
11317 #if 0
11318          } else if (vmu->review && (*duration < 5)) {
11319             /* Message is too short */
11320             ast_verb(3, "Message too short\n");
11321             cmd = ast_play_and_wait(chan, "vm-tooshort");
11322             cmd = ast_filedelete(tempfile, NULL);
11323             break;
11324          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11325             /* Message is all silence */
11326             ast_verb(3, "Nothing recorded\n");
11327             cmd = ast_filedelete(tempfile, NULL);
11328             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11329             if (!cmd)
11330                cmd = ast_play_and_wait(chan, "vm-speakup");
11331             break;
11332 #endif
11333          } else {
11334             /* If all is well, a message exists */
11335             msg_exists = 1;
11336             cmd = 0;
11337          }
11338          break;
11339       case '4':
11340          if (outsidecaller) {  /* only mark vm messages */
11341             /* Mark Urgent */
11342             if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11343                ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11344                ast_debug(1000, "This message is too urgent!\n");
11345                res = ast_play_and_wait(chan, "vm-marked-urgent");
11346                strcpy(flag, "Urgent");
11347             } else if (flag) {
11348                ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11349                res = ast_play_and_wait(chan, "vm-urgent-removed");
11350                strcpy(flag, "");
11351             } else {
11352                ast_play_and_wait(chan, "vm-sorry");
11353             }
11354             cmd = 0;
11355          } else {
11356             cmd = ast_play_and_wait(chan, "vm-sorry");
11357          }
11358          break;
11359       case '5':
11360       case '6':
11361       case '7':
11362       case '8':
11363       case '9':
11364       case '*':
11365       case '#':
11366          cmd = ast_play_and_wait(chan, "vm-sorry");
11367          break;
11368 #if 0 
11369 /*  XXX Commented out for the moment because of the dangers of deleting
11370     a message while recording (can put the message numbers out of sync) */
11371       case '*':
11372          /* Cancel recording, delete message, offer to take another message*/
11373          cmd = ast_play_and_wait(chan, "vm-deleted");
11374          cmd = ast_filedelete(tempfile, NULL);
11375          if (outsidecaller) {
11376             res = vm_exec(chan, NULL);
11377             return res;
11378          }
11379          else
11380             return 1;
11381 #endif
11382       case '0':
11383          if (!ast_test_flag(vmu, VM_OPERATOR)) {
11384             cmd = ast_play_and_wait(chan, "vm-sorry");
11385             break;
11386          }
11387          if (msg_exists || recorded) {
11388             cmd = ast_play_and_wait(chan, "vm-saveoper");
11389             if (!cmd)
11390                cmd = ast_waitfordigit(chan, 3000);
11391             if (cmd == '1') {
11392                ast_play_and_wait(chan, "vm-msgsaved");
11393                cmd = '0';
11394             } else if (cmd == '4') {
11395                if (flag) {
11396                   ast_play_and_wait(chan, "vm-marked-urgent");
11397                   strcpy(flag, "Urgent");
11398                }
11399                ast_play_and_wait(chan, "vm-msgsaved");
11400                cmd = '0';
11401             } else {
11402                ast_play_and_wait(chan, "vm-deleted");
11403                DELETE(recordfile, -1, recordfile, vmu);
11404                cmd = '0';
11405             }
11406          }
11407          return cmd;
11408       default:
11409          /* If the caller is an ouside caller, and the review option is enabled,
11410             allow them to review the message, but let the owner of the box review
11411             their OGM's */
11412          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11413             return cmd;
11414          if (msg_exists) {
11415             cmd = ast_play_and_wait(chan, "vm-review");
11416             if (!cmd && outsidecaller) {
11417                if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11418                   cmd = ast_play_and_wait(chan, "vm-review-urgent");
11419                } else if (flag) {
11420                   cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11421                }
11422             }
11423          } else {
11424             cmd = ast_play_and_wait(chan, "vm-torerecord");
11425             if (!cmd)
11426                cmd = ast_waitfordigit(chan, 600);
11427          }
11428          
11429          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11430             cmd = ast_play_and_wait(chan, "vm-reachoper");
11431             if (!cmd)
11432                cmd = ast_waitfordigit(chan, 600);
11433          }
11434 #if 0
11435          if (!cmd)
11436             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11437 #endif
11438          if (!cmd)
11439             cmd = ast_waitfordigit(chan, 6000);
11440          if (!cmd) {
11441             attempts++;
11442          }
11443          if (attempts > max_attempts) {
11444             cmd = 't';
11445          }
11446       }
11447    }
11448    if (outsidecaller)
11449       ast_play_and_wait(chan, "vm-goodbye");
11450    if (cmd == 't')
11451       cmd = 0;
11452    return cmd;
11453 }
11454 
11455 /* This is a workaround so that menuselect displays a proper description
11456  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11457  */
11458 
11459 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11460       .load = load_module,
11461       .unload = unload_module,
11462       .reload = reload,
11463       );

Generated on Fri Jun 19 12:09:30 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7