Sat Aug 6 00:39:21 2011

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  * \par See also
00026  * \arg \ref Config_vm
00027  * \ingroup applications
00028  * \note This module requires res_adsi to load.
00029  */
00030 
00031 /*** MODULEINFO
00032    <depend>res_adsi</depend>
00033    <depend>res_smdi</depend>
00034  ***/
00035 
00036 /*** MAKEOPTS
00037 <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">
00038    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00039       <depend>unixodbc</depend>
00040       <depend>ltdl</depend>
00041       <conflict>IMAP_STORAGE</conflict>
00042       <defaultenabled>no</defaultenabled>
00043    </member>
00044    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00045       <depend>imap_tk</depend>
00046       <conflict>ODBC_STORAGE</conflict>
00047       <use>ssl</use>
00048       <defaultenabled>no</defaultenabled>
00049    </member>
00050 </category>
00051  ***/
00052 
00053 #include "asterisk.h"
00054 
00055 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 316707 $")
00056 
00057 #include <stdlib.h>
00058 #include <errno.h>
00059 #include <unistd.h>
00060 #include <string.h>
00061 #include <stdlib.h>
00062 #include <stdio.h>
00063 #include <sys/time.h>
00064 #include <sys/stat.h>
00065 #include <sys/types.h>
00066 #include <sys/mman.h>
00067 #include <time.h>
00068 #include <dirent.h>
00069 #ifdef IMAP_STORAGE
00070 #include <ctype.h>
00071 #include <signal.h>
00072 #include <pwd.h>
00073 #ifdef USE_SYSTEM_IMAP
00074 #include <imap/c-client.h>
00075 #include <imap/imap4r1.h>
00076 #include <imap/linkage.h>
00077 #elif defined (USE_SYSTEM_CCLIENT)
00078 #include <c-client/c-client.h>
00079 #include <c-client/imap4r1.h>
00080 #include <c-client/linkage.h>
00081 #else
00082 #include "c-client.h"
00083 #include "imap4r1.h"
00084 #include "linkage.h"
00085 #endif
00086 #endif
00087 #include "asterisk/lock.h"
00088 #include "asterisk/file.h"
00089 #include "asterisk/logger.h"
00090 #include "asterisk/channel.h"
00091 #include "asterisk/pbx.h"
00092 #include "asterisk/options.h"
00093 #include "asterisk/config.h"
00094 #include "asterisk/say.h"
00095 #include "asterisk/module.h"
00096 #include "asterisk/adsi.h"
00097 #include "asterisk/app.h"
00098 #include "asterisk/manager.h"
00099 #include "asterisk/dsp.h"
00100 #include "asterisk/localtime.h"
00101 #include "asterisk/cli.h"
00102 #include "asterisk/utils.h"
00103 #include "asterisk/stringfields.h"
00104 #include "asterisk/smdi.h"
00105 #include "asterisk/astobj2.h"
00106 #ifdef ODBC_STORAGE
00107 #include "asterisk/res_odbc.h"
00108 #endif
00109 
00110 #ifdef IMAP_STORAGE
00111 #include "asterisk/threadstorage.h"
00112 
00113 AST_MUTEX_DEFINE_STATIC(imaptemp_lock);
00114 static char imaptemp[1024];
00115 static char imapserver[48];
00116 static char imapport[8];
00117 static char imapflags[128];
00118 static char imapfolder[64];
00119 static char authuser[32];
00120 static char authpassword[42];
00121 static int imapversion = 1;
00122 
00123 static int expungeonhangup = 1;
00124 static char delimiter = '\0';
00125 static const long DEFAULT_IMAP_TCP_TIMEOUT = 60L;
00126 
00127 struct vm_state;
00128 struct ast_vm_user;
00129 
00130 AST_THREADSTORAGE(ts_vmstate, ts_vmstate_init);
00131 
00132 static int init_mailstream (struct vm_state *vms, int box);
00133 static void write_file (char *filename, char *buffer, unsigned long len);
00134 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
00135 static char *get_header_by_tag(char *header, char *tag);
00136 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
00137 static char *get_user_by_mailbox(char *mailbox);
00138 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
00139 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00140 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00141 static void vmstate_insert(struct vm_state *vms);
00142 static void vmstate_delete(struct vm_state *vms);
00143 static void set_update(MAILSTREAM * stream);
00144 static void init_vm_state(struct vm_state *vms);
00145 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
00146 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
00147 static void get_mailbox_delimiter(MAILSTREAM *stream);
00148 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00149 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00150 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);
00151 static void check_quota(struct vm_state *vms, char *mailbox);
00152 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
00153 struct vmstate {
00154    struct vm_state *vms;
00155    struct vmstate *next;
00156 };
00157 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
00158 static struct vmstate *vmstates = NULL;
00159 #endif
00160 
00161 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00162 
00163 #define COMMAND_TIMEOUT 5000
00164 /* Don't modify these here; set your umask at runtime instead */
00165 #define  VOICEMAIL_DIR_MODE   0777
00166 #define  VOICEMAIL_FILE_MODE  0666
00167 #define  CHUNKSIZE   65536
00168 
00169 #define VOICEMAIL_CONFIG "voicemail.conf"
00170 #define ASTERISK_USERNAME "asterisk"
00171 
00172 /* Default mail command to mail voicemail. Change it with the
00173     mailcmd= command in voicemail.conf */
00174 #define SENDMAIL "/usr/sbin/sendmail -t"
00175 
00176 #define INTRO "vm-intro"
00177 
00178 #define MAXMSG 100
00179 #ifndef IMAP_STORAGE
00180 #define MAXMSGLIMIT 9999
00181 #else
00182 #define MAXMSGLIMIT 255
00183 #endif
00184 
00185 #define BASEMAXINLINE 256
00186 #define BASELINELEN 72
00187 #define BASEMAXINLINE 256
00188 #ifdef IMAP_STORAGE
00189 #define ENDL "\r\n"
00190 #else
00191 #define ENDL "\n"
00192 #endif
00193 
00194 #define MAX_DATETIME_FORMAT   512
00195 #define MAX_NUM_CID_CONTEXTS 10
00196 
00197 #define VM_REVIEW        (1 << 0)
00198 #define VM_OPERATOR      (1 << 1)
00199 #define VM_SAYCID        (1 << 2)
00200 #define VM_SVMAIL        (1 << 3)
00201 #define VM_ENVELOPE      (1 << 4)
00202 #define VM_SAYDURATION   (1 << 5)
00203 #define VM_SKIPAFTERCMD  (1 << 6)
00204 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00205 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00206 #define VM_PBXSKIP       (1 << 9)
00207 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
00208 #define VM_ATTACH        (1 << 11)
00209 #define VM_DELETE        (1 << 12)
00210 #define VM_ALLOCED       (1 << 13)
00211 #define VM_SEARCH        (1 << 14)
00212 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00213 #define ERROR_LOCK_PATH  -100
00214 #define ERROR_MAILBOX_FULL -200
00215 #define OPERATOR_EXIT      300
00216 
00217 
00218 enum {
00219    OPT_SILENT =           (1 << 0),
00220    OPT_BUSY_GREETING =    (1 << 1),
00221    OPT_UNAVAIL_GREETING = (1 << 2),
00222    OPT_RECORDGAIN =       (1 << 3),
00223    OPT_PREPEND_MAILBOX =  (1 << 4),
00224    OPT_PRIORITY_JUMP =    (1 << 5),
00225    OPT_AUTOPLAY =         (1 << 6),
00226 } vm_option_flags;
00227 
00228 enum {
00229    OPT_ARG_RECORDGAIN = 0,
00230    OPT_ARG_PLAYFOLDER = 1,
00231    /* This *must* be the last value in this enum! */
00232    OPT_ARG_ARRAY_SIZE = 2,
00233 } vm_option_args;
00234 
00235 AST_APP_OPTIONS(vm_app_options, {
00236    AST_APP_OPTION('s', OPT_SILENT),
00237    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00238    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00239    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00240    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00241    AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
00242    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00243 });
00244 
00245 static int load_config(void);
00246 
00247 /*! \page vmlang Voicemail Language Syntaxes Supported
00248 
00249    \par Syntaxes supported, not really language codes.
00250    \arg \b en - English
00251    \arg \b de - German
00252    \arg \b es - Spanish
00253    \arg \b fr - French
00254    \arg \b it = Italian
00255    \arg \b nl - Dutch
00256    \arg \b pt - Polish
00257    \arg \b pt - Portuguese
00258    \arg \b pt_BR - Portuguese (Brazil)
00259    \arg \b gr - Greek
00260    \arg \b no - Norwegian
00261    \arg \b se - Swedish
00262    \arg \b ua - Ukrainian
00263    \arg \b he - Hebrew
00264 
00265 German requires the following additional soundfile:
00266 \arg \b 1F  einE (feminine)
00267 
00268 Spanish requires the following additional soundfile:
00269 \arg \b 1M      un (masculine)
00270 
00271 Dutch, Greek, Portuguese & Spanish require the following additional soundfiles:
00272 \arg \b vm-INBOXs singular of 'new'
00273 \arg \b vm-Olds      singular of 'old/heard/read'
00274 
00275 NB these are plural:
00276 \arg \b vm-INBOX  nieuwe (nl)
00277 \arg \b vm-Old    oude (nl)
00278 
00279 Polish uses:
00280 \arg \b vm-new-a  'new', feminine singular accusative
00281 \arg \b vm-new-e  'new', feminine plural accusative
00282 \arg \b vm-new-ych   'new', feminine plural genitive
00283 \arg \b vm-old-a  'old', feminine singular accusative
00284 \arg \b vm-old-e  'old', feminine plural accusative
00285 \arg \b vm-old-ych   'old', feminine plural genitive
00286 \arg \b digits/1-a   'one', not always same as 'digits/1'
00287 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00288 
00289 Swedish uses:
00290 \arg \b vm-nytt      singular of 'new'
00291 \arg \b vm-nya    plural of 'new'
00292 \arg \b vm-gammalt   singular of 'old'
00293 \arg \b vm-gamla  plural of 'old'
00294 \arg \b digits/ett   'one', not always same as 'digits/1'
00295 
00296 Norwegian uses:
00297 \arg \b vm-ny     singular of 'new'
00298 \arg \b vm-nye    plural of 'new'
00299 \arg \b vm-gammel singular of 'old'
00300 \arg \b vm-gamle  plural of 'old'
00301 
00302 Dutch also uses:
00303 \arg \b nl-om     'at'?
00304 
00305 Greek, Portuguese & Spanish also uses:
00306 \arg \b vm-youhaveno
00307 
00308 Italian requires the following additional soundfile:
00309 
00310 For vm_intro_it:
00311 \arg \b vm-nuovo  new
00312 \arg \b vm-nuovi  new plural
00313 \arg \b vm-vecchio   old
00314 \arg \b vm-vecchi old plural
00315 
00316 Hebrew also uses:
00317 \arg \b vm-INBOX1 '1 new message'
00318 \arg \b vm-OLD1   '1 old message'
00319 \arg \b vm-shtei  'shtei'
00320 \arg \b vm-nomessages 'you have no new messages'
00321 
00322 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00323 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00324 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00325 
00326 */
00327 
00328 struct baseio {
00329    int iocp;
00330    int iolen;
00331    int linelength;
00332    int ateof;
00333    unsigned char iobuf[BASEMAXINLINE];
00334 };
00335 
00336 /*! Structure for linked list of users */
00337 struct ast_vm_user {
00338    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00339    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00340    char password[80];               /*!< Secret pin code, numbers only */
00341    char fullname[80];               /*!< Full name, for directory app */
00342    char email[80];                  /*!< E-mail address */
00343    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00344    char serveremail[80];            /*!< From: Mail address */
00345    char mailcmd[160];               /*!< Configurable mail command */
00346    char preprocesscmd[160];          /*!< Configurable pre-attach command */
00347    char preprocessfmt[20];          /*!< Configurable pre-attach command */
00348    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00349    char zonetag[80];                /*!< Time zone */
00350    char callback[80];
00351    char dialout[80];
00352    char uniqueid[80];               /*!< Unique integer identifier */
00353    char exit[80];
00354    char attachfmt[20];              /*!< Attachment format */
00355    unsigned int flags;              /*!< VM_ flags */ 
00356    int saydurationm;
00357    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00358 #ifdef IMAP_STORAGE
00359    char imapuser[80];   /* IMAP server login */
00360    char imappassword[80];  /* IMAP server password if authpassword not defined */
00361    char imapvmshareid[80]; /* Shared mailbox ID to use rather than the dialed one */
00362    int imapversion;                 /*!< If configuration changes, use the new values */
00363 #endif
00364    double volgain;      /*!< Volume gain for voicemails sent via email */
00365    AST_LIST_ENTRY(ast_vm_user) list;
00366 };
00367 
00368 struct vm_zone {
00369    AST_LIST_ENTRY(vm_zone) list;
00370    char name[80];
00371    char timezone[80];
00372    char msg_format[512];
00373 };
00374 
00375 struct vm_state {
00376    char curbox[80];
00377    char username[80];
00378    char context[80];
00379    char curdir[PATH_MAX];
00380    char vmbox[PATH_MAX];
00381    char fn[PATH_MAX];
00382    char fn2[PATH_MAX];
00383    int *deleted;
00384    int *heard;
00385    int dh_arraysize; /* used for deleted / heard allocation */
00386    int curmsg;
00387    int lastmsg;
00388    int newmessages;
00389    int oldmessages;
00390    int starting;
00391    int repeats;
00392 #ifdef IMAP_STORAGE
00393    ast_mutex_t lock;
00394    int updated; /* decremented on each mail check until 1 -allows delay */
00395    long msgArray[256];
00396    MAILSTREAM *mailstream;
00397    int vmArrayIndex;
00398    char imapuser[80]; /* IMAP server login */
00399    int imapversion;
00400    int interactive;
00401    unsigned int quota_limit;
00402    unsigned int quota_usage;
00403    struct vm_state *persist_vms;
00404 #endif
00405 };
00406 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);
00407 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00408 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00409          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00410          signed char record_gain, struct vm_state *vms);
00411 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00412 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00413 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
00414 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *folder, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
00415 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00416 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00417 #endif
00418 static void apply_options(struct ast_vm_user *vmu, const char *options);
00419 
00420 struct ao2_container *inprocess_container;
00421 
00422 struct inprocess {
00423    int count;
00424    char *context;
00425    char mailbox[0];
00426 };
00427 
00428 static int inprocess_hash_fn(const void *obj, const int flags)
00429 {
00430    const struct inprocess *i = obj;
00431    return atoi(i->mailbox);
00432 }
00433 
00434 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00435 {
00436    struct inprocess *i = obj, *j = arg;
00437    if (strcmp(i->mailbox, j->mailbox)) {
00438       return 0;
00439    }
00440    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00441 }
00442 
00443 static int inprocess_count(const char *context, const char *mailbox, int delta)
00444 {
00445    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00446    arg->context = arg->mailbox + strlen(mailbox) + 1;
00447    strcpy(arg->mailbox, mailbox); /* SAFE */
00448    strcpy(arg->context, context); /* SAFE */
00449    ao2_lock(inprocess_container);
00450    if ((i = ao2_find(inprocess_container, arg, 0))) {
00451       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00452       ao2_unlock(inprocess_container);
00453       ao2_ref(i, -1);
00454       return ret;
00455    }
00456    if (delta < 0) {
00457       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00458    }
00459    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00460       ao2_unlock(inprocess_container);
00461       return 0;
00462    }
00463    i->context = i->mailbox + strlen(mailbox) + 1;
00464    strcpy(i->mailbox, mailbox); /* SAFE */
00465    strcpy(i->context, context); /* SAFE */
00466    i->count = delta;
00467    ao2_link(inprocess_container, i);
00468    ao2_unlock(inprocess_container);
00469    ao2_ref(i, -1);
00470    return 0;
00471 }
00472 
00473 #ifdef ODBC_STORAGE
00474 static char odbc_database[80];
00475 static char odbc_table[80];
00476 #define RETRIEVE(a,b,c) retrieve_file(a,b)
00477 #define DISPOSE(a,b) remove_file(a,b)
00478 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
00479 #define EXISTS(a,b,c,d) (message_exists(a,b))
00480 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00481 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00482 #define DELETE(a,b,c,d) (delete_file(a,b))
00483 #else
00484 #ifdef IMAP_STORAGE
00485 #define RETRIEVE(a,b,c) imap_retrieve_file(a,b,c)
00486 #define DISPOSE(a,b) remove_file(a,b)
00487 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
00488 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00489 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00490 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00491 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00492 #else
00493 #define RETRIEVE(a,b,c)
00494 #define DISPOSE(a,b)
00495 #define STORE(a,b,c,d,e,f,g,h,i)
00496 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00497 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00498 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
00499 #define DELETE(a,b,c,d) (vm_delete(c))
00500 #endif
00501 #endif
00502 
00503 static char VM_SPOOL_DIR[PATH_MAX];
00504 
00505 static char ext_pass_cmd[128];
00506 
00507 static int my_umask;
00508 
00509 #if ODBC_STORAGE
00510 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00511 #elif IMAP_STORAGE
00512 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00513 #else
00514 #define tdesc "Comedian Mail (Voicemail System)"
00515 #endif
00516 
00517 static char userscontext[AST_MAX_EXTENSION] = "default";
00518 
00519 static char *addesc = "Comedian Mail";
00520 
00521 static char *synopsis_vm =
00522 "Leave a Voicemail message";
00523 
00524 static char *descrip_vm =
00525 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
00526 "application allows the calling party to leave a message for the specified\n"
00527 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00528 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00529 "specified mailbox does not exist.\n"
00530 "  The Voicemail application will exit if any of the following DTMF digits are\n"
00531 "received:\n"
00532 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00533 "    * - Jump to the 'a' extension in the current dialplan context.\n"
00534 "  This application will set the following channel variable upon completion:\n"
00535 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00536 "               application. The possible values are:\n"
00537 "               SUCCESS | USEREXIT | FAILED\n\n"
00538 "  Options:\n"
00539 "    b    - Play the 'busy' greeting to the calling party.\n"
00540 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00541 "           message. The units are whole-number decibels (dB).\n"
00542 "           Only works on supported technologies, which is Zap only.\n"
00543 "    s    - Skip the playback of instructions for leaving a message to the\n"
00544 "           calling party.\n"
00545 "    u    - Play the 'unavailable' greeting.\n"
00546 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
00547 "           error occurs.\n";
00548 
00549 static char *synopsis_vmain =
00550 "Check Voicemail messages";
00551 
00552 static char *descrip_vmain =
00553 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
00554 "calling party to check voicemail messages. A specific mailbox, and optional\n"
00555 "corresponding context, may be specified. If a mailbox is not provided, the\n"
00556 "calling party will be prompted to enter one. If a context is not specified,\n"
00557 "the 'default' context will be used.\n\n"
00558 "  Options:\n"
00559 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00560 "           is entered by the caller.\n"
00561 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00562 "           message. The units are whole-number decibels (dB).\n"
00563 "    s    - Skip checking the passcode for the mailbox.\n"
00564 "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00565 "           Defaults to INBOX\n";
00566 
00567 static char *synopsis_vm_box_exists =
00568 "Check to see if Voicemail mailbox exists";
00569 
00570 static char *descrip_vm_box_exists =
00571 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
00572 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00573 "will be used.\n"
00574 "  This application will set the following channel variable upon completion:\n"
00575 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00576 "                        MailboxExists application. Possible values include:\n"
00577 "                        SUCCESS | FAILED\n\n"
00578 "  Options:\n"
00579 "    j - Jump to priority n+101 if the mailbox is found.\n";
00580 
00581 static char *synopsis_vmauthenticate =
00582 "Authenticate with Voicemail passwords";
00583 
00584 static char *descrip_vmauthenticate =
00585 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
00586 "same way as the Authenticate application, but the passwords are taken from\n"
00587 "voicemail.conf.\n"
00588 "  If the mailbox is specified, only that mailbox's password will be considered\n"
00589 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00590 "be set with the authenticated mailbox.\n\n"
00591 "  Options:\n"
00592 "    s - Skip playing the initial prompts.\n";
00593 
00594 /* Leave a message */
00595 static char *app = "VoiceMail";
00596 
00597 /* Check mail, control, etc */
00598 static char *app2 = "VoiceMailMain";
00599 
00600 static char *app3 = "MailboxExists";
00601 static char *app4 = "VMAuthenticate";
00602 
00603 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00604 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00605 static char zonetag[80];
00606 static int maxsilence;
00607 static int maxmsg;
00608 static int silencethreshold = 128;
00609 static char serveremail[80];
00610 static char mailcmd[160];  /* Configurable mail cmd */
00611 static char preprocesscmd[160];  /* Configurable pre-attach cmd */
00612 static char preprocessfmt[20];       /* Resulting attach format*/
00613 static char externnotify[160]; 
00614 static struct ast_smdi_interface *smdi_iface = NULL;
00615 static char vmfmts[80];
00616 static double volgain;
00617 static int vmminmessage;
00618 static int vmmaxmessage;
00619 static int maxgreet;
00620 static int skipms;
00621 static int maxlogins;
00622 
00623 static struct ast_flags globalflags = {0};
00624 
00625 static int saydurationminfo;
00626 
00627 static char dialcontext[AST_MAX_CONTEXT];
00628 static char callcontext[AST_MAX_CONTEXT];
00629 static char exitcontext[AST_MAX_CONTEXT];
00630 
00631 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00632 
00633 
00634 static char *emailbody = NULL;
00635 static char *emailsubject = NULL;
00636 static char *pagerbody = NULL;
00637 static char *pagersubject = NULL;
00638 static char fromstring[100];
00639 static char pagerfromstring[100];
00640 static char emailtitle[100];
00641 static char charset[32] = "ISO-8859-1";
00642 
00643 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00644 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00645 static int adsiver = 1;
00646 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00647 
00648 
00649 /*!
00650  * \brief Strips control and non 7-bit clean characters from input string.
00651  *
00652  * \note To map control and none 7-bit characters to a 7-bit clean characters
00653  *  please use ast_str_encode_mine().
00654  */
00655 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00656 {
00657    char *bufptr = buf;
00658    for (; *input; input++) {
00659       if (*input < 32) {
00660          continue;
00661       }
00662       *bufptr++ = *input;
00663       if (bufptr == buf + buflen - 1) {
00664          break;
00665       }
00666    }
00667    *bufptr = '\0';
00668    return buf;
00669 }
00670 
00671 static void populate_defaults(struct ast_vm_user *vmu)
00672 {
00673    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00674    if (saydurationminfo)
00675       vmu->saydurationm = saydurationminfo;
00676    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00677    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00678    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00679    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00680    if (maxmsg)
00681       vmu->maxmsg = maxmsg;
00682    vmu->volgain = volgain;
00683 }
00684 
00685 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00686 {
00687    int x;
00688    if (!strcasecmp(var, "attach")) {
00689       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00690    } else if (!strcasecmp(var, "attachfmt")) {
00691       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00692    } else if (!strcasecmp(var, "preprocesscmd")) {
00693       ast_copy_string(vmu->preprocesscmd, value, sizeof(vmu->preprocesscmd));
00694    } else if (!strcasecmp(var, "preprocessfmt")) {
00695       ast_copy_string(vmu->preprocessfmt, value, sizeof(vmu->preprocessfmt));
00696    } else if (!strcasecmp(var, "serveremail")) {
00697       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00698    } else if (!strcasecmp(var, "language")) {
00699       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00700    } else if (!strcasecmp(var, "tz")) {
00701       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00702 #ifdef IMAP_STORAGE
00703    } else if (!strcasecmp(var, "imapuser")) {
00704       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00705       vmu->imapversion = imapversion;
00706    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00707       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00708       vmu->imapversion = imapversion;
00709    } else if (!strcasecmp(var, "imapvmshareid")) {
00710       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00711       vmu->imapversion = imapversion;
00712 #endif
00713    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00714       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00715    } else if (!strcasecmp(var, "saycid")){
00716       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00717    } else if (!strcasecmp(var,"sendvoicemail")){
00718       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00719    } else if (!strcasecmp(var, "review")){
00720       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00721    } else if (!strcasecmp(var, "tempgreetwarn")){
00722       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00723    } else if (!strcasecmp(var, "operator")){
00724       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00725    } else if (!strcasecmp(var, "envelope")){
00726       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00727    } else if (!strcasecmp(var, "sayduration")){
00728       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00729    } else if (!strcasecmp(var, "saydurationm")){
00730       if (sscanf(value, "%30d", &x) == 1) {
00731          vmu->saydurationm = x;
00732       } else {
00733          ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
00734       }
00735    } else if (!strcasecmp(var, "forcename")){
00736       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00737    } else if (!strcasecmp(var, "forcegreetings")){
00738       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00739    } else if (!strcasecmp(var, "callback")) {
00740       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00741    } else if (!strcasecmp(var, "dialout")) {
00742       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00743    } else if (!strcasecmp(var, "exitcontext")) {
00744       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00745    } else if (!strcasecmp(var, "maxmsg")) {
00746       vmu->maxmsg = atoi(value);
00747       if (vmu->maxmsg <= 0) {
00748          ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
00749          vmu->maxmsg = MAXMSG;
00750       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00751          ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00752          vmu->maxmsg = MAXMSGLIMIT;
00753       }
00754    } else if (!strcasecmp(var, "volgain")) {
00755       sscanf(value, "%30lf", &vmu->volgain);
00756    } else if (!strcasecmp(var, "options")) {
00757       apply_options(vmu, value);
00758    }
00759 }
00760 
00761 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00762 {
00763    int res = -1;
00764    if (!strcmp(vmu->password, password)) {
00765       /* No change (but an update would return 0 rows updated, so we opt out here) */
00766       res = 0;
00767    } else if (!ast_strlen_zero(vmu->uniqueid)) {
00768       if (ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL) > 0) {
00769          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00770          res = 0;
00771       }
00772    }
00773    return res;
00774 }
00775 
00776 static void apply_options(struct ast_vm_user *vmu, const char *options)
00777 {  /* Destructively Parse options and apply */
00778    char *stringp;
00779    char *s;
00780    char *var, *value;
00781    stringp = ast_strdupa(options);
00782    while ((s = strsep(&stringp, "|"))) {
00783       value = s;
00784       if ((var = strsep(&value, "=")) && value) {
00785          apply_option(vmu, var, value);
00786       }
00787    }  
00788 }
00789 
00790 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
00791 {
00792    struct ast_variable *tmp;
00793    tmp = var;
00794    while (tmp) {
00795       if (!strcasecmp(tmp->name, "vmsecret")) {
00796          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00797       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
00798          if (ast_strlen_zero(retval->password))
00799             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00800       } else if (!strcasecmp(tmp->name, "uniqueid")) {
00801          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00802       } else if (!strcasecmp(tmp->name, "pager")) {
00803          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
00804       } else if (!strcasecmp(tmp->name, "email")) {
00805          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
00806       } else if (!strcasecmp(tmp->name, "fullname")) {
00807          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
00808       } else if (!strcasecmp(tmp->name, "context")) {
00809          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
00810 #ifdef IMAP_STORAGE
00811       } else if (!strcasecmp(tmp->name, "imapuser")) {
00812          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
00813          retval->imapversion = imapversion;
00814       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
00815          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
00816          retval->imapversion = imapversion;
00817       } else if (!strcasecmp(tmp->name, "imapvmshareid")) {
00818          ast_copy_string(retval->imapvmshareid, tmp->value, sizeof(retval->imapvmshareid));
00819          retval->imapversion = imapversion;
00820 #endif
00821       } else
00822          apply_option(retval, tmp->name, tmp->value);
00823       tmp = tmp->next;
00824    } 
00825 }
00826 
00827 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00828 {
00829    struct ast_variable *var;
00830    struct ast_vm_user *retval;
00831 
00832    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
00833       if (!ivm)
00834          ast_set_flag(retval, VM_ALLOCED);   
00835       else
00836          memset(retval, 0, sizeof(*retval));
00837       if (mailbox) 
00838          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
00839       populate_defaults(retval);
00840       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
00841          var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
00842       else
00843          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
00844       if (var) {
00845          apply_options_full(retval, var);
00846          ast_variables_destroy(var);
00847       } else { 
00848          if (!ivm) 
00849             free(retval);
00850          retval = NULL;
00851       }  
00852    } 
00853    return retval;
00854 }
00855 
00856 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00857 {
00858    /* This function could be made to generate one from a database, too */
00859    struct ast_vm_user *vmu=NULL, *cur;
00860    AST_LIST_LOCK(&users);
00861 
00862    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
00863       context = "default";
00864 
00865    AST_LIST_TRAVERSE(&users, cur, list) {
00866 #ifdef IMAP_STORAGE
00867       if (cur->imapversion != imapversion) {
00868          continue;
00869       }
00870 #endif
00871       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
00872          break;
00873       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
00874          break;
00875    }
00876    if (cur) {
00877       /* Make a copy, so that on a reload, we have no race */
00878       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
00879          memcpy(vmu, cur, sizeof(*vmu));
00880          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
00881          AST_LIST_NEXT(vmu, list) = NULL;
00882       }
00883    } else
00884       vmu = find_user_realtime(ivm, context, mailbox);
00885    AST_LIST_UNLOCK(&users);
00886    return vmu;
00887 }
00888 
00889 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
00890 {
00891    /* This function could be made to generate one from a database, too */
00892    struct ast_vm_user *cur;
00893    int res = -1;
00894    AST_LIST_LOCK(&users);
00895    AST_LIST_TRAVERSE(&users, cur, list) {
00896       if ((!context || !strcasecmp(context, cur->context)) &&
00897          (!strcasecmp(mailbox, cur->mailbox)))
00898             break;
00899    }
00900    if (cur) {
00901       ast_copy_string(cur->password, newpass, sizeof(cur->password));
00902       res = 0;
00903    }
00904    AST_LIST_UNLOCK(&users);
00905    return res;
00906 }
00907 
00908 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
00909 {
00910    struct ast_config   *cfg=NULL;
00911    struct ast_variable *var=NULL;
00912    struct ast_category *cat=NULL;
00913    char *category=NULL, *value=NULL, *new=NULL;
00914    const char *tmp=NULL;
00915                
00916    if (!change_password_realtime(vmu, newpassword))
00917       return;
00918 
00919    /* check voicemail.conf */
00920    if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
00921       while ((category = ast_category_browse(cfg, category))) {
00922          if (!strcasecmp(category, vmu->context)) {
00923             tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
00924             if (!tmp) {
00925                ast_log(LOG_WARNING, "We could not find the mailbox.\n");
00926                break;
00927             }
00928             value = strstr(tmp,",");
00929             if (!value) {
00930                new = alloca(strlen(newpassword)+1);
00931                sprintf(new, "%s", newpassword);
00932             } else {
00933                new = alloca((strlen(value)+strlen(newpassword)+1));
00934                sprintf(new,"%s%s", newpassword, value);
00935             }
00936             if (!(cat = ast_category_get(cfg, category))) {
00937                ast_log(LOG_WARNING, "Failed to get category structure.\n");
00938                break;
00939             }
00940             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
00941          }
00942       }
00943       /* save the results */
00944       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00945       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00946       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
00947    }
00948    category = NULL;
00949    var = NULL;
00950    /* check users.conf and update the password stored for the mailbox*/
00951    /* if no vmsecret entry exists create one. */
00952    if ((cfg = ast_config_load_with_comments("users.conf"))) {
00953       if (option_debug > 3)
00954          ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
00955       while ((category = ast_category_browse(cfg, category))) {
00956          if (option_debug > 3)
00957             ast_log(LOG_DEBUG, "users.conf: %s\n", category);
00958          if (!strcasecmp(category, vmu->mailbox)) {
00959             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
00960                if (option_debug > 3)
00961                   ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
00962                var = ast_variable_new("vmsecret", newpassword);
00963             } 
00964             new = alloca(strlen(newpassword)+1);
00965             sprintf(new, "%s", newpassword);
00966             if (!(cat = ast_category_get(cfg, category))) {
00967                if (option_debug > 3)
00968                   ast_log(LOG_DEBUG, "failed to get category!\n");
00969                break;
00970             }
00971             if (!var)      
00972                ast_variable_update(cat, "vmsecret", new, NULL, 0);
00973             else
00974                ast_variable_append(cat, var);
00975          }
00976       }
00977       /* save the results and clean things up */
00978       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
00979       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00980       config_text_file_save("users.conf", cfg, "AppVoicemail");
00981    }
00982 }
00983 
00984 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
00985 {
00986    char buf[255];
00987    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
00988    if (!ast_safe_system(buf)) {
00989       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00990       /* Reset the password in memory, too */
00991       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00992    }
00993 }
00994 
00995 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
00996 {
00997    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
00998 }
00999 
01000 static int make_file(char *dest, const int len, const char *dir, const int num)
01001 {
01002    return snprintf(dest, len, "%s/msg%04d", dir, num);
01003 }
01004 
01005 /* same as mkstemp, but return a FILE * */
01006 static FILE *vm_mkftemp(char *template)
01007 {
01008    FILE *p = NULL;
01009    int pfd = mkstemp(template);
01010    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01011    if (pfd > -1) {
01012       p = fdopen(pfd, "w+");
01013       if (!p) {
01014          close(pfd);
01015          pfd = -1;
01016       }
01017    }
01018    return p;
01019 }
01020 
01021 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01022  * \param dest    String. base directory.
01023  * \param len     Length of dest.
01024  * \param context String. Ignored if is null or empty string.
01025  * \param ext     String. Ignored if is null or empty string.
01026  * \param folder  String. Ignored if is null or empty string. 
01027  * \return -1 on failure, 0 on success.
01028  */
01029 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01030 {
01031    mode_t   mode = VOICEMAIL_DIR_MODE;
01032 
01033    if (!ast_strlen_zero(context)) {
01034       make_dir(dest, len, context, "", "");
01035       if (mkdir(dest, mode) && errno != EEXIST) {
01036          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
01037          return -1;
01038       }
01039    }
01040    if (!ast_strlen_zero(ext)) {
01041       make_dir(dest, len, context, ext, "");
01042       if (mkdir(dest, mode) && errno != EEXIST) {
01043          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
01044          return -1;
01045       }
01046    }
01047    if (!ast_strlen_zero(folder)) {
01048       make_dir(dest, len, context, ext, folder);
01049       if (mkdir(dest, mode) && errno != EEXIST) {
01050          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
01051          return -1;
01052       }
01053    }
01054    return 0;
01055 }
01056 
01057 static char *mbox(int id)
01058 {
01059    static char *msgs[] = {
01060       "INBOX",
01061       "Old",
01062       "Work",
01063       "Family",
01064       "Friends",
01065       "Cust1",
01066       "Cust2",
01067       "Cust3",
01068       "Cust4",
01069       "Cust5",
01070    };
01071    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
01072 }
01073 
01074 static void free_user(struct ast_vm_user *vmu)
01075 {
01076    if (ast_test_flag(vmu, VM_ALLOCED))
01077       free(vmu);
01078 }
01079 
01080 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01081 
01082    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01083    if (!vms->dh_arraysize) {
01084       /* initial allocation */
01085       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01086          return -1;
01087       }
01088       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01089          return -1;
01090       }
01091       vms->dh_arraysize = arraysize;
01092    } else if (vms->dh_arraysize < arraysize) {
01093       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01094          return -1;
01095       }
01096       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01097          return -1;
01098       }
01099       memset(vms->deleted, 0, arraysize * sizeof(int));
01100       memset(vms->heard, 0, arraysize * sizeof(int));
01101       vms->dh_arraysize = arraysize;
01102    }
01103 
01104    return 0;
01105 }
01106 
01107 /* All IMAP-specific functions should go in this block. This
01108  * keeps them from being spread out all over the code */
01109 #ifdef IMAP_STORAGE
01110 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01111 {
01112    char arg[10];
01113    struct vm_state *vms;
01114    unsigned long messageNum;
01115 
01116    /* Greetings aren't stored in IMAP, so we delete them from disk */
01117    if (msgnum < 0) {
01118       ast_filedelete(file, NULL);
01119       return;
01120    }
01121 
01122    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01123       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);
01124       return;
01125    }
01126 
01127    /* find real message number based on msgnum */
01128    /* this may be an index into vms->msgArray based on the msgnum. */
01129    messageNum = vms->msgArray[msgnum];
01130    if (messageNum == 0) {
01131       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01132       return;
01133    }
01134    if (option_debug > 2)
01135       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01136    /* delete message */
01137    snprintf (arg, sizeof(arg), "%lu",messageNum);
01138    ast_mutex_lock(&vms->lock);
01139    mail_setflag (vms->mailstream,arg,"\\DELETED");
01140    mail_expunge(vms->mailstream);
01141    ast_mutex_unlock(&vms->lock);
01142 }
01143 
01144 static int imap_retrieve_file(const char *dir, const int msgnum, const struct ast_vm_user *vmu)
01145 {
01146    BODY *body;
01147    char *header_content;
01148    char *attachedfilefmt;
01149    const char *cid_num;
01150    const char *cid_name;
01151    const char *duration;
01152    const char *context;
01153    const char *category;
01154    const char *origtime;
01155    struct vm_state *vms;
01156    char text_file[PATH_MAX];
01157    FILE *text_file_ptr;
01158 
01159    /* Greetings are not stored on the IMAP server, so we should not
01160     * attempt to retrieve them.
01161     */
01162    if (msgnum < 0) {
01163       return 0;
01164    }
01165    
01166    /* Before anything can happen, we need a vm_state so that we can
01167     * actually access the imap server through the vms->mailstream
01168     */
01169    if(!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01170       /* This should not happen. If it does, then I guess we'd
01171        * need to create the vm_state, extract which mailbox to
01172        * open, and then set up the msgArray so that the correct
01173        * IMAP message could be accessed. If I have seen correctly
01174        * though, the vms should be obtainable from the vmstates list
01175        * and should have its msgArray properly set up.
01176        */
01177       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01178       return -1;
01179    }
01180    
01181    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01182 
01183    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01184    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01185       return 0;
01186    }
01187 
01188    if (option_debug > 2)
01189       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01190    if (vms->msgArray[msgnum] == 0) {
01191       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01192       return -1;
01193    }
01194 
01195    /* This will only work for new messages... */
01196    ast_mutex_lock(&vms->lock);
01197    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01198    ast_mutex_unlock(&vms->lock);
01199    /* empty string means no valid header */
01200    if (ast_strlen_zero(header_content)) {
01201       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01202       return -1;
01203    }
01204 
01205    ast_mutex_lock(&vms->lock);
01206    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01207    ast_mutex_unlock(&vms->lock);
01208 
01209    /* We have the body, now we extract the file name of the first attachment. */
01210    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01211       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01212    } else {
01213       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01214       return -1;
01215    }
01216    
01217    /* Find the format of the attached file */
01218 
01219    strsep(&attachedfilefmt, ".");
01220    if (!attachedfilefmt) {
01221       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01222       return -1;
01223    }
01224    
01225    save_body(body, vms, "2", attachedfilefmt);
01226 
01227    /* Get info from headers!! */
01228    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01229 
01230    if (!(text_file_ptr = fopen(text_file, "w"))) {
01231       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01232    }
01233 
01234    fprintf(text_file_ptr, "%s\n", "[message]");
01235 
01236    cid_name = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:");
01237    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(cid_name, ""));
01238    cid_num = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
01239    fprintf(text_file_ptr, "<%s>\n", S_OR(cid_num, ""));
01240    context = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
01241    fprintf(text_file_ptr, "context=%s\n", S_OR(context, ""));
01242    origtime = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
01243    fprintf(text_file_ptr, "origtime=%s\n", S_OR(origtime, ""));
01244    duration = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
01245    fprintf(text_file_ptr, "duration=%s\n", S_OR(origtime, ""));
01246    category = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
01247    fprintf(text_file_ptr, "category=%s\n", S_OR(category, ""));
01248    
01249    fclose(text_file_ptr);
01250    return 0;
01251 }
01252 
01253 static int folder_int(const char *folder)
01254 {
01255    /*assume a NULL folder means INBOX*/
01256    if (!folder)
01257       return 0;
01258    if (!strcasecmp(folder, "INBOX"))
01259       return 0;
01260    else if (!strcasecmp(folder, "Old"))
01261       return 1;
01262    else if (!strcasecmp(folder, "Work"))
01263       return 2;
01264    else if (!strcasecmp(folder, "Family"))
01265       return 3;
01266    else if (!strcasecmp(folder, "Friends"))
01267       return 4;
01268    else if (!strcasecmp(folder, "Cust1"))
01269       return 5;
01270    else if (!strcasecmp(folder, "Cust2"))
01271       return 6;
01272    else if (!strcasecmp(folder, "Cust3"))
01273       return 7;
01274    else if (!strcasecmp(folder, "Cust4"))
01275       return 8;
01276    else if (!strcasecmp(folder, "Cust5"))
01277       return 9;
01278    else /*assume they meant INBOX if folder is not found otherwise*/
01279       return 0;
01280 }
01281 
01282 static int messagecount(const char *context, const char *mailbox, const char *folder)
01283 {
01284    SEARCHPGM *pgm;
01285    SEARCHHEADER *hdr;
01286 
01287    struct ast_vm_user *vmu, vmus;
01288    struct vm_state *vms_p;
01289    int ret = 0;
01290    int fold = folder_int(folder);
01291    
01292    if (ast_strlen_zero(mailbox))
01293       return 0;
01294 
01295    /* We have to get the user before we can open the stream! */
01296    /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
01297    vmu = find_user(&vmus, context, mailbox);
01298    if (!vmu) {
01299       ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
01300       return -1;
01301    } else {
01302       /* No IMAP account available */
01303       if (vmu->imapuser[0] == '\0') {
01304          ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
01305          return -1;
01306       }
01307    }
01308 
01309    /* check if someone is accessing this box right now... */
01310    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01311    if (!vms_p) {
01312       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01313    }
01314    if (vms_p) {
01315       if (option_debug > 2)
01316          ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
01317       if (fold == 0) {/*INBOX*/
01318          return vms_p->newmessages;
01319       }
01320       if (fold == 1) {/*Old messages*/
01321          return vms_p->oldmessages;
01322       }
01323    }
01324 
01325    /* add one if not there... */
01326    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01327    if (!vms_p) {
01328       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01329    }
01330 
01331    if (!vms_p) {
01332       if (!(vms_p = create_vm_state_from_user(vmu))) {
01333          ast_log(LOG_WARNING, "Unable to allocate space for new vm_state!\n");
01334          return -1;
01335       }
01336    }
01337    ret = init_mailstream(vms_p, fold);
01338    if (!vms_p->mailstream) {
01339       ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
01340       return -1;
01341    }
01342    if (ret == 0) {
01343       ast_mutex_lock(&vms_p->lock);
01344       pgm = mail_newsearchpgm ();
01345       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01346       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01347       pgm->header = hdr;
01348       if (fold != 1) {
01349          pgm->unseen = 1;
01350          pgm->seen = 0;
01351       }
01352       /* In the special case where fold is 1 (old messages) we have to do things a bit
01353        * differently. Old messages are stored in the INBOX but are marked as "seen"
01354        */
01355       else {
01356          pgm->unseen = 0;
01357          pgm->seen = 1;
01358       }
01359       pgm->undeleted = 1;
01360       pgm->deleted = 0;
01361 
01362       vms_p->vmArrayIndex = 0;
01363       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01364       if (fold == 0)
01365          vms_p->newmessages = vms_p->vmArrayIndex;
01366       if (fold == 1)
01367          vms_p->oldmessages = vms_p->vmArrayIndex;
01368       /*Freeing the searchpgm also frees the searchhdr*/
01369       mail_free_searchpgm(&pgm);
01370       ast_mutex_unlock(&vms_p->lock);
01371       vms_p->updated = 0;
01372       return vms_p->vmArrayIndex;
01373    } else {
01374       ast_mutex_lock(&vms_p->lock);
01375       mail_ping(vms_p->mailstream);
01376       ast_mutex_unlock(&vms_p->lock);
01377    }
01378    return 0;
01379 }
01380 
01381 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
01382 {
01383    /* Check if mailbox is full */
01384    check_quota(vms, imapfolder);
01385    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
01386       if (option_debug)
01387          ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
01388       ast_play_and_wait(chan, "vm-mailboxfull");
01389       return -1;
01390    }
01391 
01392    /* Check if we have exceeded maxmsg */
01393    if (option_debug > 2)
01394       ast_log(LOG_DEBUG, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
01395    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
01396       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
01397       ast_play_and_wait(chan, "vm-mailboxfull");
01398       inprocess_count(vmu->mailbox, vmu->context, -1);
01399       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
01400       return -1;
01401    }
01402    return 0;
01403 }
01404 
01405 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)
01406 {
01407    char *myserveremail = serveremail;
01408    char fn[PATH_MAX];
01409    char mailbox[256];
01410    char *stringp;
01411    FILE *p=NULL;
01412    char tmp[80] = "/tmp/astmail-XXXXXX";
01413    long len;
01414    void *buf;
01415    int tempcopy = 0;
01416    STRING str;
01417    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
01418 
01419    /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
01420    if (msgnum < 0)
01421       return 0;
01422 
01423    if (imap_check_limits(chan, vms, vmu, msgcount)) {
01424       return -1;
01425    }
01426 
01427    /* Attach only the first format */
01428    fmt = ast_strdupa(fmt);
01429    stringp = fmt;
01430    strsep(&stringp, "|");
01431 
01432    if (!ast_strlen_zero(vmu->serveremail))
01433       myserveremail = vmu->serveremail;
01434 
01435    make_file(fn, sizeof(fn), dir, msgnum);
01436 
01437    if (ast_strlen_zero(vmu->email)) {
01438       /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
01439        * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
01440        * string if tempcopy is 1
01441        */
01442       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01443       tempcopy = 1;
01444    }
01445 
01446    if (!strcmp(fmt, "wav49"))
01447       fmt = "WAV";
01448    if (option_debug > 2)
01449       ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
01450    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01451       command hangs */
01452    if ((p = vm_mkftemp(tmp)) == NULL) {
01453       ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01454       if (tempcopy)
01455          *(vmu->email) = '\0';
01456       return -1;
01457    } else {
01458       make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, vms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
01459       /* read mail file to memory */
01460       len = ftell(p);
01461       rewind(p);
01462       if ((buf = ast_malloc(len+1)) == NIL) {
01463          ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
01464          fclose(p);
01465          return -1;
01466       }
01467       if (fread(buf, len, 1, p) != 1) {
01468          ast_log(LOG_WARNING, "Short read: %s\n", strerror(errno));
01469       }
01470       ((char *)buf)[len] = '\0';
01471       INIT(&str, mail_string, buf, len);
01472       init_mailstream(vms, 0);
01473       imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
01474       ast_mutex_lock(&vms->lock);
01475       if (!mail_append(vms->mailstream, mailbox, &str))
01476          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01477       ast_mutex_unlock(&vms->lock);
01478       fclose(p);
01479       unlink(tmp);
01480       ast_free(buf);
01481       if (option_debug > 2)
01482          ast_log(LOG_DEBUG, "%s stored\n", fn);
01483       /* Using messagecount to populate the last place in the msgArray
01484        * is less than optimal, but it's the only way given the current setup
01485        */
01486       messagecount(vmu->context, vmu->mailbox, "INBOX");
01487    }
01488    if (tempcopy)
01489       *(vmu->email) = '\0';
01490    inprocess_count(vmu->mailbox, vmu->context, -1);
01491    return 0;
01492 
01493 }
01494 
01495 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
01496 {
01497    char tmp[PATH_MAX] = "";
01498    char *mailboxnc;  
01499    char *context;
01500    char *mb;
01501    char *cur;
01502    if (newmsgs)
01503       *newmsgs = 0;
01504    if (oldmsgs)
01505       *oldmsgs = 0;
01506 
01507    if (option_debug > 2)
01508       ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
01509    /* If no mailbox, return immediately */
01510    if (ast_strlen_zero(mailbox_context))
01511       return 0;
01512    
01513    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01514    context = strchr(tmp, '@');
01515    if (strchr(mailbox_context, ',')) {
01516       int tmpnew, tmpold;
01517       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01518       mb = tmp;
01519       while ((cur = strsep(&mb, ", "))) {
01520          if (!ast_strlen_zero(cur)) {
01521             if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01522                return -1;
01523             else {
01524                if (newmsgs)
01525                   *newmsgs += tmpnew; 
01526                if (oldmsgs)
01527                   *oldmsgs += tmpold;
01528             }
01529          }
01530       }
01531       return 0;
01532    }
01533    if (context) {
01534       *context = '\0';
01535       mailboxnc = tmp;
01536       context++;
01537    } else {
01538       context = "default";
01539       mailboxnc = (char *)mailbox_context;
01540    }
01541    if (newmsgs) {
01542       if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
01543          return -1;
01544    }
01545    if (oldmsgs) {
01546       if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
01547          return -1;
01548    }
01549    return 0;
01550 }
01551    
01552 
01553 static int has_voicemail(const char *mailbox, const char *folder)
01554 {
01555    char tmp[256], *tmp2, *mbox, *context;
01556    ast_copy_string(tmp, mailbox, sizeof(tmp));
01557    tmp2 = tmp;
01558    if (strchr(tmp2, ',')) {
01559       while ((mbox = strsep(&tmp2, ","))) {
01560          if (!ast_strlen_zero(mbox)) {
01561             if (has_voicemail(mbox, folder))
01562                return 1;
01563          }
01564       }
01565    }
01566    if ((context= strchr(tmp, '@')))
01567       *context++ = '\0';
01568    else
01569       context = "default";
01570    return messagecount(context, tmp, folder) ? 1 : 0;
01571 }
01572 
01573 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)
01574 {
01575    struct vm_state *sendvms = NULL, *destvms = NULL;
01576    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
01577    if (msgnum >= recip->maxmsg) {
01578       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
01579       return -1;
01580    }
01581    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
01582       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
01583       return -1;
01584    }
01585    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
01586       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
01587       return -1;
01588    }
01589    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
01590    ast_mutex_lock(&sendvms->lock);
01591    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
01592       ast_mutex_unlock(&sendvms->lock);
01593       return 0;
01594    }
01595    ast_mutex_unlock(&sendvms->lock);
01596    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
01597    return -1;
01598 }
01599 
01600 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
01601 {
01602    char tmp[256], *t = tmp;
01603    size_t left = sizeof(tmp);
01604 
01605    if (box == 1) {
01606       ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
01607       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
01608    } else {
01609       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
01610       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
01611    }
01612 
01613    /* Build up server information */
01614    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
01615 
01616    /* Add authentication user if present */
01617    if (!ast_strlen_zero(authuser))
01618       ast_build_string(&t, &left, "/authuser=%s", authuser);
01619 
01620    /* Add flags if present */
01621    if (!ast_strlen_zero(imapflags))
01622       ast_build_string(&t, &left, "/%s", imapflags);
01623 
01624    /* End with username */
01625 #if 1
01626    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
01627 #else
01628    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
01629 #endif
01630 
01631    if (box == 0 || box == 1)
01632       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
01633    else
01634       snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
01635 }
01636 
01637 static int init_mailstream(struct vm_state *vms, int box)
01638 {
01639    MAILSTREAM *stream = NIL;
01640    long debug;
01641    char tmp[256];
01642    
01643    if (!vms) {
01644       ast_log (LOG_ERROR,"vm_state is NULL!\n");
01645       return -1;
01646    }
01647    if (option_debug > 2)
01648       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
01649    if (vms->mailstream == NIL || !vms->mailstream) {
01650       if (option_debug)
01651          ast_log (LOG_DEBUG,"mailstream not set.\n");
01652    } else {
01653       stream = vms->mailstream;
01654    }
01655    /* debug = T;  user wants protocol telemetry? */
01656    debug = NIL;  /* NO protocol telemetry? */
01657 
01658    if (delimiter == '\0') {      /* did not probe the server yet */
01659       char *cp;
01660 #ifdef USE_SYSTEM_IMAP
01661 #include <imap/linkage.c>
01662 #elif defined(USE_SYSTEM_CCLIENT)
01663 #include <c-client/linkage.c>
01664 #else
01665 #include "linkage.c"
01666 #endif
01667       /* Connect to INBOX first to get folders delimiter */
01668       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
01669       ast_mutex_lock(&vms->lock);
01670       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
01671       ast_mutex_unlock(&vms->lock);
01672       if (stream == NIL) {
01673          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
01674          return -1;
01675       }
01676       get_mailbox_delimiter(stream);
01677       /* update delimiter in imapfolder */
01678       for (cp = imapfolder; *cp; cp++)
01679          if (*cp == '/')
01680             *cp = delimiter;
01681    }
01682    /* Now connect to the target folder */
01683    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
01684    if (option_debug > 2)
01685       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
01686    ast_mutex_lock(&vms->lock);
01687    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
01688    ast_mutex_unlock(&vms->lock);
01689    if (vms->mailstream == NIL) {
01690       return -1;
01691    } else {
01692       return 0;
01693    }
01694 }
01695 
01696 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
01697 {
01698    SEARCHPGM *pgm;
01699    SEARCHHEADER *hdr;
01700    int ret;
01701 
01702    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
01703    vms->imapversion = vmu->imapversion;
01704 
01705    if (option_debug > 2)
01706       ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
01707    ret = init_mailstream(vms, box);
01708    if (ret != 0 || !vms->mailstream) {
01709       ast_log (LOG_ERROR,"Could not initialize mailstream\n");
01710       return -1;
01711    }
01712    
01713    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
01714 
01715    /* Check Quota */
01716    if  (box == 0)  {
01717       if (option_debug > 2)
01718          ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
01719       check_quota(vms,(char *)mbox(box));
01720    }
01721 
01722    ast_mutex_lock(&vms->lock);
01723    pgm = mail_newsearchpgm();
01724 
01725    /* Check IMAP folder for Asterisk messages only... */
01726    hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
01727    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
01728    pgm->header = hdr;
01729    pgm->deleted = 0;
01730    pgm->undeleted = 1;
01731 
01732    /* if box = 0, check for new, if box = 1, check for read */
01733    if (box == 0) {
01734       pgm->unseen = 1;
01735       pgm->seen = 0;
01736    } else if (box == 1) {
01737       pgm->seen = 1;
01738       pgm->unseen = 0;
01739    }
01740 
01741    vms->vmArrayIndex = 0;
01742    if (option_debug > 2)
01743       ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
01744    mail_search_full (vms->mailstream, NULL, pgm, NIL);
01745 
01746    vms->lastmsg = vms->vmArrayIndex - 1;
01747    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
01748     * ensure to allocate enough space to account for all of them. Warn if old messages
01749     * have not been checked first as that is required.
01750     */
01751    if (box == 0 && !vms->dh_arraysize) {
01752       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
01753    }
01754    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
01755       ast_mutex_unlock(&vms->lock);
01756       return -1;
01757    }
01758 
01759    mail_free_searchpgm(&pgm);
01760    ast_mutex_unlock(&vms->lock);
01761    return 0;
01762 }
01763 
01764 static void write_file(char *filename, char *buffer, unsigned long len)
01765 {
01766    FILE *output;
01767 
01768    output = fopen (filename, "w");
01769    if (fwrite (buffer, len, 1, output) != 1) {
01770       ast_log(LOG_WARNING, "Short write: %s\n", strerror(errno));
01771    }
01772    fclose (output);
01773 }
01774 
01775 void mm_searched(MAILSTREAM *stream, unsigned long number)
01776 {
01777    struct vm_state *vms;
01778    char *mailbox;
01779    char *user;
01780    mailbox = stream->mailbox;
01781    user = get_user_by_mailbox(mailbox);
01782    vms = get_vm_state_by_imapuser(user,2);
01783    if (!vms) {
01784       vms = get_vm_state_by_imapuser(user, 0);
01785    }
01786    if (vms) {
01787       if (option_debug > 2)
01788          ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
01789       vms->msgArray[vms->vmArrayIndex++] = number;
01790    } else {
01791       ast_log (LOG_ERROR, "No state found.\n");
01792    }
01793 }
01794 
01795 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
01796 {
01797    struct ast_variable *var;
01798    struct ast_vm_user *vmu;
01799 
01800    vmu = ast_calloc(1, sizeof *vmu);
01801    if (!vmu)
01802       return NULL;
01803    ast_set_flag(vmu, VM_ALLOCED);
01804    populate_defaults(vmu);
01805 
01806    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
01807    if (var) {
01808       apply_options_full(vmu, var);
01809       ast_variables_destroy(var);
01810       return vmu;
01811    } else {
01812       free(vmu);
01813       return NULL;
01814    }
01815 }
01816 
01817 /* Interfaces to C-client */
01818 
01819 void mm_exists(MAILSTREAM * stream, unsigned long number)
01820 {
01821    /* mail_ping will callback here if new mail! */
01822    if (option_debug > 3)
01823       ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
01824    if (number == 0) return;
01825    set_update(stream);
01826 }
01827 
01828 
01829 void mm_expunged(MAILSTREAM * stream, unsigned long number)
01830 {
01831    /* mail_ping will callback here if expunged mail! */
01832    if (option_debug > 3)
01833       ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
01834    if (number == 0) return;
01835    set_update(stream);
01836 }
01837 
01838 
01839 void mm_flags(MAILSTREAM * stream, unsigned long number)
01840 {
01841    /* mail_ping will callback here if read mail! */
01842    if (option_debug > 3)
01843       ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
01844    if (number == 0) return;
01845    set_update(stream);
01846 }
01847 
01848 
01849 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
01850 {
01851    mm_log (string, errflg);
01852 }
01853 
01854 
01855 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
01856 {
01857    if (delimiter == '\0') {
01858       delimiter = delim;
01859    }
01860    if (option_debug > 4) {
01861       ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
01862       if (attributes & LATT_NOINFERIORS)
01863          ast_log(LOG_DEBUG, "no inferiors\n");
01864       if (attributes & LATT_NOSELECT)
01865          ast_log(LOG_DEBUG, "no select\n");
01866       if (attributes & LATT_MARKED)
01867          ast_log(LOG_DEBUG, "marked\n");
01868       if (attributes & LATT_UNMARKED)
01869          ast_log(LOG_DEBUG, "unmarked\n");
01870    }
01871 }
01872 
01873 
01874 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
01875 {
01876    if (option_debug > 4) {
01877       ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
01878       if (attributes & LATT_NOINFERIORS)
01879          ast_log(LOG_DEBUG, "no inferiors\n");
01880       if (attributes & LATT_NOSELECT)
01881          ast_log(LOG_DEBUG, "no select\n");
01882       if (attributes & LATT_MARKED)
01883          ast_log(LOG_DEBUG, "marked\n");
01884       if (attributes & LATT_UNMARKED)
01885          ast_log(LOG_DEBUG, "unmarked\n");
01886    }
01887 }
01888 
01889 
01890 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
01891 {
01892    ast_log (LOG_NOTICE," Mailbox %s", mailbox);
01893    if (status->flags & SA_MESSAGES)
01894       ast_log (LOG_NOTICE,", %lu messages", status->messages);
01895    if (status->flags & SA_RECENT)
01896       ast_log (LOG_NOTICE,", %lu recent", status->recent);
01897    if (status->flags & SA_UNSEEN)
01898       ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
01899    if (status->flags & SA_UIDVALIDITY)
01900       ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
01901    if (status->flags & SA_UIDNEXT)
01902       ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
01903    ast_log (LOG_NOTICE,"\n");
01904 }
01905 
01906 
01907 void mm_log(char *string, long errflg)
01908 {
01909    switch ((short) errflg) {
01910       case NIL:
01911          if (option_debug)
01912             ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
01913          break;
01914       case PARSE:
01915       case WARN:
01916          ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
01917          break;
01918       case ERROR:
01919          ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
01920          break;
01921    }
01922 }
01923 
01924 
01925 void mm_dlog(char *string)
01926 {
01927    ast_log (LOG_NOTICE, "%s\n", string);
01928 }
01929 
01930 
01931 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
01932 {
01933    struct ast_vm_user *vmu;
01934 
01935    if (option_debug > 3)
01936       ast_log(LOG_DEBUG, "Entering callback mm_login\n");
01937 
01938    ast_copy_string(user, mb->user, MAILTMPLEN);
01939 
01940    /* We should only do this when necessary */
01941    if (!ast_strlen_zero(authpassword)) {
01942       ast_copy_string(pwd, authpassword, MAILTMPLEN);
01943    } else {
01944       AST_LIST_TRAVERSE(&users, vmu, list) {
01945          if (!strcasecmp(mb->user, vmu->imapuser)) {
01946             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
01947             break;
01948          }
01949       }
01950       if (!vmu) {
01951          if ((vmu = find_user_realtime_imapuser(mb->user))) {
01952             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
01953             free_user(vmu);
01954          }
01955       }
01956    }
01957 }
01958 
01959 
01960 void mm_critical(MAILSTREAM * stream)
01961 {
01962 }
01963 
01964 
01965 void mm_nocritical(MAILSTREAM * stream)
01966 {
01967 }
01968 
01969 
01970 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
01971 {
01972    kill (getpid (), SIGSTOP);
01973    return NIL;
01974 }
01975 
01976 
01977 void mm_fatal(char *string)
01978 {
01979    ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
01980 }
01981 
01982 /* C-client callback to handle quota */
01983 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
01984 {
01985    struct vm_state *vms;
01986    char *mailbox;
01987    char *user;
01988    unsigned long usage = 0;
01989    unsigned long limit = 0;
01990    
01991    while (pquota) {
01992       usage = pquota->usage;
01993       limit = pquota->limit;
01994       pquota = pquota->next;
01995    }
01996    
01997    mailbox = stream->mailbox;
01998    user = get_user_by_mailbox(mailbox);
01999    vms = get_vm_state_by_imapuser(user,2);
02000    if (!vms) {
02001       vms = get_vm_state_by_imapuser(user, 0);
02002    }
02003    if (vms) {
02004       if (option_debug > 2)
02005          ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
02006       vms->quota_usage = usage;
02007       vms->quota_limit = limit;
02008    } else {
02009       ast_log (LOG_ERROR, "No state found.\n");
02010    }
02011 }
02012 
02013 static char *get_header_by_tag(char *header, char *tag)
02014 {
02015    char *start;
02016    int taglen;
02017    char *eol_pnt;
02018 
02019    if (!header || !tag)
02020       return NULL;
02021 
02022    taglen = strlen(tag) + 1;
02023    if (taglen < 1)
02024       return NULL;
02025 
02026    start = strstr(header, tag);
02027    if (!start)
02028       return NULL;
02029 
02030    ast_mutex_lock(&imaptemp_lock);
02031    ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
02032    ast_mutex_unlock(&imaptemp_lock);
02033    if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
02034       *eol_pnt = '\0';
02035    return imaptemp;
02036 }
02037 
02038 static char *get_user_by_mailbox(char *mailbox)
02039 {
02040    char *start, *quote;
02041    char *eol_pnt;
02042 
02043    if (!mailbox)
02044       return NULL;
02045 
02046    start = strstr(mailbox,"/user=");
02047    if (!start)
02048       return NULL;
02049 
02050    ast_mutex_lock(&imaptemp_lock);
02051    ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
02052    ast_mutex_unlock(&imaptemp_lock);
02053 
02054    quote = strchr(imaptemp,'\"');
02055    if (!quote) {  /* if username is not in quotes */
02056       eol_pnt = strchr(imaptemp,'/');
02057       if (!eol_pnt) {
02058          eol_pnt = strchr(imaptemp,'}');
02059       }
02060       *eol_pnt = '\0';
02061       return imaptemp;
02062    } else {
02063       eol_pnt = strchr(imaptemp+1,'\"');
02064       *eol_pnt = '\0';
02065       return imaptemp+1;
02066    }
02067 }
02068 
02069 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02070 {
02071    struct vm_state *vms_p;
02072 
02073    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02074    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02075       return vms_p;
02076    }
02077    if (option_debug > 4)
02078       ast_log(LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02079    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02080       return NULL;
02081    ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
02082    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02083    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02084    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02085    vms_p->imapversion = vmu->imapversion;
02086    if (option_debug > 4)
02087       ast_log(LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02088    vms_p->updated = 1;
02089    /* set mailbox to INBOX! */
02090    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02091    init_vm_state(vms_p);
02092    vmstate_insert(vms_p);
02093    return vms_p;
02094 }
02095 
02096 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
02097 {
02098    struct vmstate *vlist = NULL;
02099 
02100    if (interactive) {
02101       struct vm_state *vms;
02102       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02103       vms = pthread_getspecific(ts_vmstate.key);
02104       return vms;
02105    }
02106 
02107    ast_mutex_lock(&vmstate_lock);
02108    vlist = vmstates;
02109    while (vlist) {
02110       if (vlist->vms && vlist->vms->imapversion == imapversion) {
02111          if (vlist->vms->imapuser) {
02112             if (!strcmp(vlist->vms->imapuser,user)) {
02113                if (interactive == 2) {
02114                   ast_mutex_unlock(&vmstate_lock);
02115                   return vlist->vms;
02116                } else if (vlist->vms->interactive == interactive) {
02117                   ast_mutex_unlock(&vmstate_lock);
02118                   return vlist->vms;
02119                }
02120             }
02121          } else {
02122             if (option_debug > 2)
02123                ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
02124          }
02125       } else {
02126          if (option_debug > 2)
02127             ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
02128       }
02129       vlist = vlist->next;
02130    }
02131    ast_mutex_unlock(&vmstate_lock);
02132    if (option_debug > 2)
02133       ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
02134    return NULL;
02135 }
02136 
02137 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02138 { 
02139    struct vmstate *vlist = NULL;
02140    const char *local_context = S_OR(context, "default");
02141 
02142    if (interactive) {
02143       struct vm_state *vms;
02144       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02145       vms = pthread_getspecific(ts_vmstate.key);
02146       return vms;
02147    }
02148 
02149    ast_mutex_lock(&vmstate_lock);
02150    vlist = vmstates;
02151    if (option_debug > 2) 
02152       ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
02153    while (vlist) {
02154       if (vlist->vms) {
02155          if (vlist->vms->username && vlist->vms->context && vlist->vms->imapversion == imapversion) {
02156             if (option_debug > 2)
02157                ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
02158             if (!strcmp(vlist->vms->username,mailbox) && !(strcmp(vlist->vms->context, local_context))) {
02159                if (option_debug > 2)
02160                   ast_log(LOG_DEBUG, " Found it!\n");
02161                ast_mutex_unlock(&vmstate_lock);
02162                return vlist->vms;
02163             }
02164          } else {
02165             if (option_debug > 2)
02166                ast_log(LOG_DEBUG, " error: username or context is NULL for %s\n",mailbox);
02167          }
02168       } else {
02169          if (option_debug > 2)
02170             ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
02171       }
02172       vlist = vlist->next;
02173    }
02174    ast_mutex_unlock(&vmstate_lock);
02175    if (option_debug > 2)
02176       ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
02177    return NULL;
02178 }
02179 
02180 static void vmstate_insert(struct vm_state *vms) 
02181 {
02182    struct vmstate *v;
02183    struct vm_state *altvms;
02184 
02185    /* If interactive, it probably already exists, and we should
02186       use the one we already have since it is more up to date.
02187       We can compare the username to find the duplicate */
02188    if (vms->interactive == 1) {
02189       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02190       if (altvms) {
02191          if (option_debug > 2)
02192             ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
02193          vms->newmessages = altvms->newmessages;
02194          vms->oldmessages = altvms->oldmessages;
02195          /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
02196          copy_msgArray(vms, altvms);
02197          vms->vmArrayIndex = altvms->vmArrayIndex;
02198          vms->lastmsg = altvms->lastmsg;
02199          vms->curmsg = altvms->curmsg;
02200          /* get a pointer to the persistent store */
02201          vms->persist_vms = altvms;
02202          /* Reuse the mailstream? */
02203 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02204          vms->mailstream = altvms->mailstream;
02205 #else
02206          vms->mailstream = NIL;
02207 #endif
02208       }
02209       return;
02210    }
02211 
02212    v = (struct vmstate *)malloc(sizeof(struct vmstate));
02213    if (!v) {
02214       ast_log(LOG_ERROR, "Out of memory\n");
02215    }
02216    if (option_debug > 2)
02217       ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02218    ast_mutex_lock(&vmstate_lock);
02219    v->vms = vms;
02220    v->next = vmstates;
02221    vmstates = v;
02222    ast_mutex_unlock(&vmstate_lock);
02223 }
02224 
02225 static void vmstate_delete(struct vm_state *vms) 
02226 {
02227    struct vmstate *vc, *vf = NULL, *vl = NULL;
02228    struct vm_state *altvms;
02229 
02230    /* If interactive, we should copy pertainent info
02231       back to the persistent state (to make update immediate) */
02232    if (vms->interactive == 1) {
02233       altvms = vms->persist_vms;
02234       if (altvms) {
02235          if (option_debug > 2)
02236             ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
02237          altvms->newmessages = vms->newmessages;
02238          altvms->oldmessages = vms->oldmessages;
02239          altvms->updated = 1;
02240       }
02241       vms->mailstream = mail_close(vms->mailstream);
02242 
02243       /* Interactive states are not stored within the persistent list */
02244       return;
02245    }
02246 
02247    ast_mutex_lock(&vmstate_lock);
02248    vc = vmstates;
02249    if (option_debug > 2)
02250       ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02251    while (vc) {
02252       if (vc->vms == vms) {
02253          vf = vc;
02254          if (vl)
02255             vl->next = vc->next;
02256          else
02257             vmstates = vc->next;
02258          break;
02259       }
02260       vl = vc;
02261       vc = vc->next;
02262    }
02263    if (!vf) {
02264       ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02265    } else {
02266       ast_mutex_destroy(&vf->vms->lock);
02267       free(vf);
02268    }
02269    ast_mutex_unlock(&vmstate_lock);
02270 }
02271 
02272 static void set_update(MAILSTREAM * stream) 
02273 {
02274    struct vm_state *vms;
02275    char *mailbox;
02276    char *user;
02277 
02278    mailbox = stream->mailbox;
02279    user = get_user_by_mailbox(mailbox);
02280    vms = get_vm_state_by_imapuser(user, 0);
02281    if (vms) {
02282       if (option_debug > 2)
02283          ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
02284       vms->updated = 1; /* set updated flag since mailbox changed */
02285    } else {
02286       if (option_debug > 2)
02287          ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
02288    }
02289 }
02290 
02291 static void init_vm_state(struct vm_state *vms) 
02292 {
02293    int x;
02294    vms->vmArrayIndex = 0;
02295    for (x = 0; x < 256; x++) {
02296       vms->msgArray[x] = 0;
02297    }
02298    ast_mutex_init(&vms->lock);
02299 }
02300 
02301 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
02302 {
02303    int x;
02304    for (x = 0; x<256; x++) {
02305       dst->msgArray[x] = src->msgArray[x];
02306    }
02307 }
02308 
02309 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format) 
02310 {
02311    char *body_content;
02312    char *body_decoded;
02313    unsigned long len;
02314    unsigned long newlen;
02315    char filename[256];
02316    
02317    if (!body || body == NIL)
02318       return -1;
02319    ast_mutex_lock(&vms->lock);
02320    body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02321    ast_mutex_unlock(&vms->lock);
02322    if (body_content != NIL) {
02323       snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
02324       /* ast_log (LOG_DEBUG,body_content); */
02325       body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
02326       write_file (filename, (char *) body_decoded, newlen);
02327    }
02328    return 0;
02329 }
02330 
02331 /* get delimiter via mm_list callback */
02332 /* MUTEX should already be held */
02333 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02334    char tmp[50];
02335    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02336    mail_list(stream, tmp, "*");
02337 }
02338 
02339 /* Check Quota for user */
02340 static void check_quota(struct vm_state *vms, char *mailbox) {
02341    ast_mutex_lock(&vms->lock);
02342    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02343    if (option_debug > 2)
02344       ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02345    if (vms && vms->mailstream != NULL) {
02346       imap_getquotaroot(vms->mailstream, mailbox);
02347    } else {
02348       ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
02349    }
02350    ast_mutex_unlock(&vms->lock);
02351 }
02352 #endif
02353 
02354 /* only return failure if ast_lock_path returns 'timeout',
02355    not if the path does not exist or any other reason
02356 */
02357 static int vm_lock_path(const char *path)
02358 {
02359    switch (ast_lock_path(path)) {
02360    case AST_LOCK_TIMEOUT:
02361       return -1;
02362    default:
02363       return 0;
02364    }
02365 }
02366 
02367 
02368 #ifdef ODBC_STORAGE
02369 struct generic_prepare_struct {
02370    char *sql;
02371    int argc;
02372    char **argv;
02373 };
02374 
02375 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02376 {
02377    struct generic_prepare_struct *gps = data;
02378    int res, i;
02379    SQLHSTMT stmt;
02380 
02381    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02382    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02383       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02384       return NULL;
02385    }
02386    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02387    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02388       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02389       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02390       return NULL;
02391    }
02392    for (i = 0; i < gps->argc; i++)
02393       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02394 
02395    return stmt;
02396 }
02397 
02398 static int retrieve_file(char *dir, int msgnum)
02399 {
02400    int x = 0;
02401    int res;
02402    int fd=-1;
02403    size_t fdlen = 0;
02404    void *fdm = MAP_FAILED;
02405    SQLSMALLINT colcount=0;
02406    SQLHSTMT stmt;
02407    char sql[PATH_MAX];
02408    char fmt[80]="";
02409    char *c;
02410    char coltitle[256];
02411    SQLSMALLINT collen;
02412    SQLSMALLINT datatype;
02413    SQLSMALLINT decimaldigits;
02414    SQLSMALLINT nullable;
02415    SQLULEN colsize;
02416    SQLLEN colsize2;
02417    FILE *f=NULL;
02418    char rowdata[80];
02419    char fn[PATH_MAX];
02420    char full_fn[PATH_MAX];
02421    char msgnums[80];
02422    char *argv[] = { dir, msgnums };
02423    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02424 
02425    struct odbc_obj *obj;
02426    obj = ast_odbc_request_obj(odbc_database, 0);
02427    if (obj) {
02428       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02429       c = strchr(fmt, '|');
02430       if (c)
02431          *c = '\0';
02432       if (!strcasecmp(fmt, "wav49"))
02433          strcpy(fmt, "WAV");
02434       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02435       if (msgnum > -1)
02436          make_file(fn, sizeof(fn), dir, msgnum);
02437       else
02438          ast_copy_string(fn, dir, sizeof(fn));
02439       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02440       
02441       if (!(f = fopen(full_fn, "w+"))) {
02442               ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02443               goto yuck;
02444       }
02445       
02446       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02447       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02448       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02449       if (!stmt) {
02450          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02451          ast_odbc_release_obj(obj);
02452          goto yuck;
02453       }
02454       res = SQLFetch(stmt);
02455       if (res == SQL_NO_DATA) {
02456          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02457          ast_odbc_release_obj(obj);
02458          goto yuck;
02459       }
02460       else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02461          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02462          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02463          ast_odbc_release_obj(obj);
02464          goto yuck;
02465       }
02466       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
02467       if (fd < 0) {
02468          ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02469          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02470          ast_odbc_release_obj(obj);
02471          goto yuck;
02472       }
02473       res = SQLNumResultCols(stmt, &colcount);
02474       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02475          ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02476          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02477          ast_odbc_release_obj(obj);
02478          goto yuck;
02479       }
02480       if (f) 
02481          fprintf(f, "[message]\n");
02482       for (x=0;x<colcount;x++) {
02483          rowdata[0] = '\0';
02484          colsize = 0;
02485          collen = sizeof(coltitle);
02486          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02487                   &datatype, &colsize, &decimaldigits, &nullable);
02488          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02489             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02490             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02491             ast_odbc_release_obj(obj);
02492             goto yuck;
02493          }
02494          if (!strcasecmp(coltitle, "recording")) {
02495             off_t offset;
02496             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02497             fdlen = colsize2;
02498             if (fd > -1) {
02499                char tmp[1]="";
02500                lseek(fd, fdlen - 1, SEEK_SET);
02501                if (write(fd, tmp, 1) != 1) {
02502                   close(fd);
02503                   fd = -1;
02504                   continue;
02505                }
02506                /* Read out in small chunks */
02507                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
02508                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
02509                      ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
02510                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02511                      ast_odbc_release_obj(obj);
02512                      goto yuck;
02513                   } else {
02514                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
02515                      munmap(fdm, CHUNKSIZE);
02516                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02517                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02518                         unlink(full_fn);
02519                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02520                         ast_odbc_release_obj(obj);
02521                         goto yuck;
02522                      }
02523                   }
02524                }
02525                if (truncate(full_fn, fdlen) < 0) {
02526                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
02527                }
02528             }
02529          } else {
02530             SQLLEN ind;
02531             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &ind);
02532             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02533                SQLINTEGER nativeerror = 0;
02534                SQLSMALLINT diagbytes = 0;
02535                unsigned char state[10], diagnostic[256];
02536                SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
02537                ast_log(LOG_WARNING, "SQL Get Data error: %s: %s!\n[%s]\n\n", state, diagnostic, sql);
02538                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02539                ast_odbc_release_obj(obj);
02540                goto yuck;
02541             }
02542             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
02543                fprintf(f, "%s=%s\n", coltitle, rowdata);
02544          }
02545       }
02546       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02547       ast_odbc_release_obj(obj);
02548    } else
02549       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02550 yuck:
02551    if (f)
02552       fclose(f);
02553    if (fd > -1)
02554       close(fd);
02555    return x - 1;
02556 }
02557 
02558 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02559 {
02560    int x = 0;
02561    int res;
02562    SQLHSTMT stmt;
02563    char sql[PATH_MAX];
02564    char rowdata[20];
02565    char *argv[] = { dir };
02566    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02567 
02568    struct odbc_obj *obj;
02569    obj = ast_odbc_request_obj(odbc_database, 0);
02570    if (obj) {
02571       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc limit 1", odbc_table);
02572 
02573       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02574       if (!stmt) {
02575          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02576          ast_odbc_release_obj(obj);
02577          goto yuck;
02578       }
02579       res = SQLFetch(stmt);
02580       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02581          if (res == SQL_NO_DATA) {
02582             ast_log(LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
02583          } else {
02584             ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02585          }
02586 
02587          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02588          ast_odbc_release_obj(obj);
02589          goto yuck;
02590       }
02591       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02592       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02593          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02594          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02595          ast_odbc_release_obj(obj);
02596          goto yuck;
02597       }
02598       if (sscanf(rowdata, "%30d", &x) != 1)
02599          ast_log(LOG_WARNING, "Failed to read message index!\n");
02600       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02601       ast_odbc_release_obj(obj);
02602       return x;
02603    } else
02604       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02605 yuck:
02606    return x - 1;
02607 }
02608 
02609 static int message_exists(char *dir, int msgnum)
02610 {
02611    int x = 0;
02612    int res;
02613    SQLHSTMT stmt;
02614    char sql[PATH_MAX];
02615    char rowdata[20];
02616    char msgnums[20];
02617    char *argv[] = { dir, msgnums };
02618    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02619 
02620    struct odbc_obj *obj;
02621    obj = ast_odbc_request_obj(odbc_database, 0);
02622    if (obj) {
02623       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
02624       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02625       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02626       if (!stmt) {
02627          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02628          ast_odbc_release_obj(obj);
02629          goto yuck;
02630       }
02631       res = SQLFetch(stmt);
02632       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02633          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02634          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02635          ast_odbc_release_obj(obj);
02636          goto yuck;
02637       }
02638       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02639       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02640          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02641          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02642          ast_odbc_release_obj(obj);
02643          goto yuck;
02644       }
02645       if (sscanf(rowdata, "%30d", &x) != 1)
02646          ast_log(LOG_WARNING, "Failed to read message count!\n");
02647       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02648       ast_odbc_release_obj(obj);
02649    } else
02650       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02651 yuck:
02652    return x;
02653 }
02654 
02655 static int count_messages(struct ast_vm_user *vmu, char *dir)
02656 {
02657    int x = 0;
02658    int res;
02659    SQLHSTMT stmt;
02660    char sql[PATH_MAX];
02661    char rowdata[20];
02662    char *argv[] = { dir };
02663    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02664 
02665    struct odbc_obj *obj;
02666    obj = ast_odbc_request_obj(odbc_database, 0);
02667    if (obj) {
02668       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
02669       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02670       if (!stmt) {
02671          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02672          ast_odbc_release_obj(obj);
02673          goto yuck;
02674       }
02675       res = SQLFetch(stmt);
02676       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02677          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02678          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02679          ast_odbc_release_obj(obj);
02680          goto yuck;
02681       }
02682       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02683       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02684          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02685          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02686          ast_odbc_release_obj(obj);
02687          goto yuck;
02688       }
02689       if (sscanf(rowdata, "%30d", &x) != 1)
02690          ast_log(LOG_WARNING, "Failed to read message count!\n");
02691       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02692       ast_odbc_release_obj(obj);
02693       return x;
02694    } else
02695       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02696 yuck:
02697    return x - 1;
02698 
02699 }
02700 
02701 static void delete_file(char *sdir, int smsg)
02702 {
02703    SQLHSTMT stmt;
02704    char sql[PATH_MAX];
02705    char msgnums[20];
02706    char *argv[] = { sdir, msgnums };
02707    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02708 
02709    struct odbc_obj *obj;
02710    obj = ast_odbc_request_obj(odbc_database, 0);
02711    if (obj) {
02712       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02713       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02714       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02715       if (!stmt)
02716          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02717       else
02718          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02719       ast_odbc_release_obj(obj);
02720    } else
02721       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02722    return;  
02723 }
02724 
02725 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
02726 {
02727    SQLHSTMT stmt;
02728    char sql[512];
02729    char msgnums[20];
02730    char msgnumd[20];
02731    struct odbc_obj *obj;
02732    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
02733    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
02734 
02735    delete_file(ddir, dmsg);
02736    obj = ast_odbc_request_obj(odbc_database, 0);
02737    if (obj) {
02738       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02739       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
02740       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
02741       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02742       if (!stmt)
02743          ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
02744       else
02745          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02746       ast_odbc_release_obj(obj);
02747    } else
02748       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02749    return;  
02750 }
02751 
02752 struct insert_cb_struct {
02753    char *dir;
02754    char *msgnum;
02755    void *recording;
02756    size_t recordinglen;
02757    SQLLEN indlen;
02758    const char *context;
02759    const char *macrocontext;
02760    const char *callerid;
02761    const char *origtime;
02762    const char *duration;
02763    char *mailboxuser;
02764    char *mailboxcontext;
02765    const char *category;
02766    char *sql;
02767 };
02768 
02769 static SQLHSTMT insert_cb(struct odbc_obj *obj, void *vd)
02770 {
02771    struct insert_cb_struct *d = vd;
02772    int res;
02773    SQLHSTMT stmt;
02774 
02775    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02776    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02777       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02778       return NULL;
02779    }
02780 
02781    res = SQLPrepare(stmt, (unsigned char *)d->sql, SQL_NTS);
02782    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02783       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", d->sql);
02784       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02785       return NULL;
02786    }
02787 
02788    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->dir), 0, (void *)d->dir, 0, NULL);
02789    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->msgnum), 0, (void *)d->msgnum, 0, NULL);
02790    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, d->recordinglen, 0, (void *)d->recording, 0, &d->indlen);
02791    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->context), 0, (void *)d->context, 0, NULL);
02792    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->macrocontext), 0, (void *)d->macrocontext, 0, NULL);
02793    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->callerid), 0, (void *)d->callerid, 0, NULL);
02794    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->origtime), 0, (void *)d->origtime, 0, NULL);
02795    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->duration), 0, (void *)d->duration, 0, NULL);
02796    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxuser), 0, (void *)d->mailboxuser, 0, NULL);
02797    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxcontext), 0, (void *)d->mailboxcontext, 0, NULL);
02798    if (!ast_strlen_zero(d->category)) {
02799       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->category), 0, (void *)d->category, 0, NULL);
02800    }
02801 
02802    return stmt;
02803 }
02804 
02805 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
02806 {
02807    int x = 0;
02808    int fd = -1;
02809    void *fdm = MAP_FAILED;
02810    size_t fdlen = -1;
02811    SQLHSTMT stmt;
02812    char sql[PATH_MAX];
02813    char msgnums[20];
02814    char fn[PATH_MAX];
02815    char full_fn[PATH_MAX];
02816    char fmt[80]="";
02817    char *c;
02818    struct insert_cb_struct d = {
02819       .dir = dir,
02820       .msgnum = msgnums,
02821       .context = "",
02822       .macrocontext = "",
02823       .callerid = "",
02824       .origtime = "",
02825       .duration = "",
02826       .mailboxuser = mailboxuser,
02827       .mailboxcontext = mailboxcontext,
02828       .category = "",
02829       .sql = sql
02830    };
02831    struct ast_config *cfg=NULL;
02832    struct odbc_obj *obj;
02833 
02834    delete_file(dir, msgnum);
02835    obj = ast_odbc_request_obj(odbc_database, 0);
02836    if (obj) {
02837       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02838       c = strchr(fmt, '|');
02839       if (c)
02840          *c = '\0';
02841       if (!strcasecmp(fmt, "wav49"))
02842          strcpy(fmt, "WAV");
02843       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02844       if (msgnum > -1)
02845          make_file(fn, sizeof(fn), dir, msgnum);
02846       else
02847          ast_copy_string(fn, dir, sizeof(fn));
02848       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02849       cfg = ast_config_load(full_fn);
02850       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02851       fd = open(full_fn, O_RDWR);
02852       if (fd < 0) {
02853          ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
02854          ast_odbc_release_obj(obj);
02855          goto yuck;
02856       }
02857       if (cfg) {
02858          d.context = ast_variable_retrieve(cfg, "message", "context");
02859          if (!d.context) d.context = "";
02860          d.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
02861          if (!d.macrocontext) d.macrocontext = "";
02862          d.callerid = ast_variable_retrieve(cfg, "message", "callerid");
02863          if (!d.callerid) d.callerid = "";
02864          d.origtime = ast_variable_retrieve(cfg, "message", "origtime");
02865          if (!d.origtime) d.origtime = "";
02866          d.duration = ast_variable_retrieve(cfg, "message", "duration");
02867          if (!d.duration) d.duration = "";
02868          d.category = ast_variable_retrieve(cfg, "message", "category");
02869          if (!d.category) d.category = "";
02870       }
02871       fdlen = lseek(fd, 0, SEEK_END);
02872       lseek(fd, 0, SEEK_SET);
02873       printf("Length is %zd\n", fdlen);
02874       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
02875       if (fdm == MAP_FAILED) {
02876          ast_log(LOG_WARNING, "Memory map failed!\n");
02877          ast_odbc_release_obj(obj);
02878          goto yuck;
02879       }
02880       d.recording = fdm;
02881       d.recordinglen = d.indlen = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
02882       if (!ast_strlen_zero(d.category)) 
02883          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
02884       else
02885          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?, ? , ?,?,?,?,?,?,?)",odbc_table);
02886       stmt = ast_odbc_prepare_and_execute(obj, insert_cb, &d);
02887       if (stmt) {
02888          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02889       }
02890       ast_odbc_release_obj(obj);
02891    } else
02892       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02893 yuck:
02894    if (cfg)
02895       ast_config_destroy(cfg);
02896    if (fdm != MAP_FAILED)
02897       munmap(fdm, fdlen);
02898    if (fd > -1)
02899       close(fd);
02900    return x;
02901 }
02902 
02903 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
02904 {
02905    SQLHSTMT stmt;
02906    char sql[PATH_MAX];
02907    char msgnums[20];
02908    char msgnumd[20];
02909    struct odbc_obj *obj;
02910    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
02911    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
02912 
02913    delete_file(ddir, dmsg);
02914    obj = ast_odbc_request_obj(odbc_database, 0);
02915    if (obj) {
02916       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02917       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
02918       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
02919       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02920       if (!stmt)
02921          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02922       else
02923          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02924       ast_odbc_release_obj(obj);
02925    } else
02926       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02927    return;  
02928 }
02929 
02930 #else
02931 #ifndef IMAP_STORAGE
02932 static int count_messages(struct ast_vm_user *vmu, char *dir)
02933 {
02934    /* Find all .txt files - even if they are not in sequence from 0000 */
02935 
02936    int vmcount = 0;
02937    DIR *vmdir = NULL;
02938    struct dirent *vment = NULL;
02939 
02940    if (vm_lock_path(dir))
02941       return ERROR_LOCK_PATH;
02942 
02943    if ((vmdir = opendir(dir))) {
02944       while ((vment = readdir(vmdir))) {
02945          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
02946             vmcount++;
02947       }
02948       closedir(vmdir);
02949    }
02950    ast_unlock_path(dir);
02951    
02952    return vmcount;
02953 }
02954 
02955 static void rename_file(char *sfn, char *dfn)
02956 {
02957    char stxt[PATH_MAX];
02958    char dtxt[PATH_MAX];
02959    ast_filerename(sfn,dfn,NULL);
02960    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
02961    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
02962    rename(stxt, dtxt);
02963 }
02964 #endif
02965 
02966 /*! 
02967  * \brief Determines the highest message number in use for a given user and mailbox folder.
02968  * \param vmu 
02969  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
02970  *
02971  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
02972  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
02973  *
02974  * \note Should always be called with a lock already set on dir.
02975  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
02976  */
02977 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
02978 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02979 {
02980    int x;
02981    unsigned char map[MAXMSGLIMIT] = "";
02982    DIR *msgdir;
02983    struct dirent *msgdirent;
02984    int msgdirint;
02985    char extension[4];
02986    int stopcount = 0;
02987 
02988    /* Reading the entire directory into a file map scales better than
02989     * doing a stat repeatedly on a predicted sequence.  I suspect this
02990     * is partially due to stat(2) internally doing a readdir(2) itself to
02991     * find each file. */
02992    if (!(msgdir = opendir(dir))) {
02993       return -1;
02994    }
02995 
02996    while ((msgdirent = readdir(msgdir))) {
02997       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
02998          map[msgdirint] = 1;
02999          stopcount++;
03000          if (option_debug > 3) {
03001             ast_log(LOG_DEBUG, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
03002          }
03003       }
03004    }
03005    closedir(msgdir);
03006 
03007    for (x = 0; x < vmu->maxmsg; x++) {
03008       if (map[x] == 1) {
03009          stopcount--;
03010       } else if (map[x] == 0 && !stopcount) {
03011          break;
03012       }
03013    }
03014 
03015    return x - 1;
03016 }
03017 #endif
03018 #endif
03019 #if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
03020 static int remove_file(char *dir, int msgnum)
03021 {
03022    char fn[PATH_MAX];
03023    char full_fn[PATH_MAX];
03024    char msgnums[80];
03025    
03026    if (msgnum > -1) {
03027       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03028       make_file(fn, sizeof(fn), dir, msgnum);
03029    } else {
03030 #ifndef IMAP_STORAGE
03031       ast_copy_string(fn, dir, sizeof(fn));
03032 #else
03033       /*IMAP stores greetings locally so it should not
03034        * try to dispose of them
03035        */
03036       return 0;
03037 #endif
03038    }
03039    ast_filedelete(fn, NULL);  
03040    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03041    unlink(full_fn);
03042    return 0;
03043 }
03044 #endif
03045 
03046 #ifndef IMAP_STORAGE
03047 static int copy(char *infile, char *outfile)
03048 {
03049    int ifd;
03050    int ofd;
03051    int res;
03052    int len;
03053    char buf[4096];
03054 
03055 #ifdef HARDLINK_WHEN_POSSIBLE
03056    /* Hard link if possible; saves disk space & is faster */
03057    if (link(infile, outfile)) {
03058 #endif
03059       if ((ifd = open(infile, O_RDONLY)) < 0) {
03060          ast_log(LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03061          return -1;
03062       }
03063       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03064          ast_log(LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03065          close(ifd);
03066          return -1;
03067       }
03068       do {
03069          len = read(ifd, buf, sizeof(buf));
03070          if (len < 0) {
03071             ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03072             close(ifd);
03073             close(ofd);
03074             unlink(outfile);
03075          }
03076          if (len) {
03077             res = write(ofd, buf, len);
03078             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03079                ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03080                close(ifd);
03081                close(ofd);
03082                unlink(outfile);
03083             }
03084          }
03085       } while (len);
03086       close(ifd);
03087       close(ofd);
03088       return 0;
03089 #ifdef HARDLINK_WHEN_POSSIBLE
03090    } else {
03091       /* Hard link succeeded */
03092       return 0;
03093    }
03094 #endif
03095 }
03096 #endif
03097 
03098 #ifndef IMAP_STORAGE
03099 static void copy_plain_file(char *frompath, char *topath)
03100 {
03101    char frompath2[PATH_MAX], topath2[PATH_MAX];
03102    ast_filecopy(frompath, topath, NULL);
03103    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03104    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03105    copy(frompath2, topath2);
03106 }
03107 #endif
03108 
03109 #ifndef IMAP_STORAGE
03110 static int vm_delete(char *file)
03111 {
03112    char *txt;
03113    int txtsize = 0;
03114 
03115    txtsize = (strlen(file) + 5)*sizeof(char);
03116    txt = alloca(txtsize);
03117    /* Sprintf here would safe because we alloca'd exactly the right length,
03118     * but trying to eliminate all sprintf's anyhow
03119     */
03120    snprintf(txt, txtsize, "%s.txt", file);
03121    unlink(txt);
03122    return ast_filedelete(file, NULL);
03123 }
03124 #endif
03125 
03126 static int inbuf(struct baseio *bio, FILE *fi)
03127 {
03128    int l;
03129 
03130    if (bio->ateof)
03131       return 0;
03132 
03133    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03134       if (ferror(fi))
03135          return -1;
03136 
03137       bio->ateof = 1;
03138       return 0;
03139    }
03140 
03141    bio->iolen= l;
03142    bio->iocp= 0;
03143 
03144    return 1;
03145 }
03146 
03147 static int inchar(struct baseio *bio, FILE *fi)
03148 {
03149    if (bio->iocp>=bio->iolen) {
03150       if (!inbuf(bio, fi))
03151          return EOF;
03152    }
03153 
03154    return bio->iobuf[bio->iocp++];
03155 }
03156 
03157 static int ochar(struct baseio *bio, int c, FILE *so)
03158 {
03159    if (bio->linelength >= BASELINELEN) {
03160       if (fputs(ENDL, so) == EOF) {
03161          return -1;
03162       }
03163 
03164       bio->linelength = 0;
03165    }
03166 
03167    if (putc(((unsigned char) c), so) == EOF) {
03168       return -1;
03169    }
03170 
03171    bio->linelength++;
03172 
03173    return 1;
03174 }
03175 
03176 static int base_encode(char *filename, FILE *so)
03177 {
03178    unsigned char dtable[BASEMAXINLINE];
03179    int i,hiteof= 0;
03180    FILE *fi;
03181    struct baseio bio;
03182 
03183    memset(&bio, 0, sizeof(bio));
03184    bio.iocp = BASEMAXINLINE;
03185 
03186    if (!(fi = fopen(filename, "rb"))) {
03187       ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03188       return -1;
03189    }
03190 
03191    for (i= 0;i<9;i++) {
03192       dtable[i]= 'A'+i;
03193       dtable[i+9]= 'J'+i;
03194       dtable[26+i]= 'a'+i;
03195       dtable[26+i+9]= 'j'+i;
03196    }
03197    for (i= 0;i<8;i++) {
03198       dtable[i+18]= 'S'+i;
03199       dtable[26+i+18]= 's'+i;
03200    }
03201    for (i= 0;i<10;i++) {
03202       dtable[52+i]= '0'+i;
03203    }
03204    dtable[62]= '+';
03205    dtable[63]= '/';
03206 
03207    while (!hiteof){
03208       unsigned char igroup[3],ogroup[4];
03209       int c,n;
03210 
03211       igroup[0]= igroup[1]= igroup[2]= 0;
03212 
03213       for (n= 0;n<3;n++) {
03214          if ((c = inchar(&bio, fi)) == EOF) {
03215             hiteof= 1;
03216             break;
03217          }
03218 
03219          igroup[n]= (unsigned char)c;
03220       }
03221 
03222       if (n> 0) {
03223          ogroup[0]= dtable[igroup[0]>>2];
03224          ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
03225          ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
03226          ogroup[3]= dtable[igroup[2]&0x3F];
03227 
03228          if (n<3) {
03229             ogroup[3]= '=';
03230 
03231             if (n<2)
03232                ogroup[2]= '=';
03233          }
03234 
03235          for (i= 0;i<4;i++)
03236             ochar(&bio, ogroup[i], so);
03237       }
03238    }
03239 
03240    fclose(fi);
03241    
03242    if (fputs(ENDL, so) == EOF) {
03243       return 0;
03244    }
03245 
03246    return 1;
03247 }
03248 
03249 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)
03250 {
03251    char callerid[256];
03252    char fromdir[256], fromfile[256];
03253    struct ast_config *msg_cfg;
03254    const char *origcallerid, *origtime;
03255    char origcidname[80], origcidnum[80], origdate[80];
03256    int inttime;
03257 
03258    /* Prepare variables for substition in email body and subject */
03259    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03260    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03261    snprintf(passdata, passdatasize, "%d", msgnum);
03262    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03263    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03264    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03265    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03266       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03267    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03268    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03269    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03270    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03271 
03272    /* Retrieve info from VM attribute file */
03273    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03274    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03275    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03276       strcat(fromfile, ".txt");
03277    }
03278    if (!(msg_cfg = ast_config_load(fromfile))) {
03279       if (option_debug > 0) {
03280          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03281       }
03282       return;
03283    }
03284 
03285    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03286       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03287       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03288       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03289       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03290    }
03291 
03292    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
03293       time_t ttime = inttime;
03294       struct tm tm;
03295       ast_localtime(&ttime, &tm, NULL);
03296       strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03297       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03298    }
03299    ast_config_destroy(msg_cfg);
03300 }
03301 
03302 static char *quote(const char *from, char *to, size_t len)
03303 {
03304    char *ptr = to;
03305    *ptr++ = '"';
03306    for (; ptr < to + len - 1; from++) {
03307       if (*from == '"')
03308          *ptr++ = '\\';
03309       else if (*from == '\0')
03310          break;
03311       *ptr++ = *from;
03312    }
03313    if (ptr < to + len - 1)
03314       *ptr++ = '"';
03315    *ptr = '\0';
03316    return to;
03317 }
03318 /*
03319  * fill in *tm for current time according to the proper timezone, if any.
03320  * Return tm so it can be used as a function argument.
03321  */
03322 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
03323 {
03324    const struct vm_zone *z = NULL;
03325    time_t t = time(NULL);
03326 
03327    /* Does this user have a timezone specified? */
03328    if (!ast_strlen_zero(vmu->zonetag)) {
03329       /* Find the zone in the list */
03330       AST_LIST_LOCK(&zones);
03331       AST_LIST_TRAVERSE(&zones, z, list) {
03332          if (!strcmp(z->name, vmu->zonetag))
03333             break;
03334       }
03335       AST_LIST_UNLOCK(&zones);
03336    }
03337    ast_localtime(&t, tm, z ? z->timezone : NULL);
03338    return tm;
03339 }
03340 
03341 /*!\brief Check if the string would need encoding within the MIME standard, to
03342  * avoid confusing certain mail software that expects messages to be 7-bit
03343  * clean.
03344  */
03345 static int check_mime(const char *str)
03346 {
03347    for (; *str; str++) {
03348       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03349          return 1;
03350       }
03351    }
03352    return 0;
03353 }
03354 
03355 /*!\brief Encode a string according to the MIME rules for encoding strings
03356  * that are not 7-bit clean or contain control characters.
03357  *
03358  * Additionally, if the encoded string would exceed the MIME limit of 76
03359  * characters per line, then the encoding will be broken up into multiple
03360  * sections, separated by a space character, in order to facilitate
03361  * breaking up the associated header across multiple lines.
03362  *
03363  * \param start A string to be encoded
03364  * \param end An expandable buffer for holding the result
03365  * \param preamble The length of the first line already used for this string,
03366  * to ensure that each line maintains a maximum length of 76 chars.
03367  * \param postamble the length of any additional characters appended to the
03368  * line, used to ensure proper field wrapping.
03369  * \retval The encoded string.
03370  */
03371 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03372 {
03373    char tmp[80];
03374    int first_section = 1;
03375    size_t endlen = 0, tmplen = 0;
03376    *end = '\0';
03377 
03378    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03379    for (; *start; start++) {
03380       int need_encoding = 0;
03381       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03382          need_encoding = 1;
03383       }
03384       if ((first_section && need_encoding && preamble + tmplen > 70) ||
03385          (first_section && !need_encoding && preamble + tmplen > 72) ||
03386          (!first_section && need_encoding && tmplen > 70) ||
03387          (!first_section && !need_encoding && tmplen > 72)) {
03388          /* Start new line */
03389          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
03390          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03391          first_section = 0;
03392       }
03393       if (need_encoding && *start == ' ') {
03394          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
03395       } else if (need_encoding) {
03396          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
03397       } else {
03398          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
03399       }
03400    }
03401    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
03402    return end;
03403 }
03404 
03405 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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
03406 {
03407    char date[256];
03408    char host[MAXHOSTNAMELEN] = "";
03409    char who[256];
03410    char bound[256];
03411    char fname[256];
03412    char dur[256];
03413    char enc_cidnum[256] = "", enc_cidname[256] = "";
03414    struct tm tm;
03415    char *passdata = NULL, *passdata2;
03416    size_t len_passdata = 0, len_passdata2, tmplen;
03417 
03418    /* One alloca for multiple fields */
03419    len_passdata2 = strlen(vmu->fullname);
03420    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
03421       len_passdata2 = tmplen;
03422    }
03423    if ((tmplen = strlen(emailtitle)) > len_passdata2) {
03424       len_passdata2 = tmplen;
03425    }
03426    if ((tmplen = strlen(fromstring)) > len_passdata2) {
03427       len_passdata2 = tmplen;
03428    }
03429    len_passdata2 = len_passdata2 * 3 + 200;
03430    passdata2 = alloca(len_passdata2);
03431 
03432    if (cidnum) {
03433       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
03434    }
03435    if (cidname) {
03436       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
03437    }
03438    gethostname(host, sizeof(host) - 1);
03439    if (strchr(srcemail, '@'))
03440       ast_copy_string(who, srcemail, sizeof(who));
03441    else {
03442       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03443    }
03444    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03445    strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03446    fprintf(p, "Date: %s" ENDL, date);
03447 
03448    /* Set date format for voicemail mail */
03449    strftime(date, sizeof(date), emaildateformat, &tm);
03450 
03451    if (*fromstring) {
03452       struct ast_channel *ast;
03453       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03454          char *ptr;
03455          memset(passdata2, 0, len_passdata2);
03456          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category);
03457          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
03458          len_passdata = strlen(passdata2) * 3 + 300;
03459          passdata = alloca(len_passdata);
03460          if (check_mime(passdata2)) {
03461             int first_line = 1;
03462             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
03463             while ((ptr = strchr(passdata, ' '))) {
03464                *ptr = '\0';
03465                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
03466                first_line = 0;
03467                passdata = ptr + 1;
03468             }
03469             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
03470          } else {
03471             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
03472          }
03473          ast_channel_free(ast);
03474       } else
03475          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03476    } else
03477       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
03478 
03479    if (check_mime(vmu->fullname)) {
03480       int first_line = 1;
03481       char *ptr;
03482       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
03483       while ((ptr = strchr(passdata2, ' '))) {
03484          *ptr = '\0';
03485          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
03486          first_line = 0;
03487          passdata2 = ptr + 1;
03488       }
03489       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
03490    } else {
03491       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
03492    }
03493    if (emailsubject) {
03494       struct ast_channel *ast;
03495       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03496          int vmlen = strlen(emailsubject) * 3 + 200;
03497          /* Only allocate more space if the previous was not large enough */
03498          if (vmlen > len_passdata) {
03499             passdata = alloca(vmlen);
03500             len_passdata = vmlen;
03501          }
03502 
03503          memset(passdata, 0, len_passdata);
03504          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category);
03505          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
03506          if (check_mime(passdata)) {
03507             int first_line = 1;
03508             char *ptr;
03509             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
03510             while ((ptr = strchr(passdata2, ' '))) {
03511                *ptr = '\0';
03512                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
03513                first_line = 0;
03514                passdata2 = ptr + 1;
03515             }
03516             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
03517          } else {
03518             fprintf(p, "Subject: %s" ENDL, passdata);
03519          }
03520          ast_channel_free(ast);
03521       } else {
03522          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03523       }
03524    } else   if (*emailtitle) {
03525       fprintf(p, emailtitle, msgnum + 1, mailbox) ;
03526       fprintf(p, ENDL) ;
03527    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
03528       fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
03529    } else {
03530       fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
03531    }
03532    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
03533    if (imap) {
03534       /* additional information needed for IMAP searching */
03535       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
03536       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
03537       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
03538       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
03539 #ifdef IMAP_STORAGE
03540       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
03541 #else
03542       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
03543 #endif
03544       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
03545       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
03546       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
03547       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
03548       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
03549       if (!ast_strlen_zero(category)) {
03550          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
03551       }
03552       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
03553       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
03554    }
03555    if (!ast_strlen_zero(cidnum)) {
03556       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
03557    }
03558    if (!ast_strlen_zero(cidname)) {
03559       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
03560    }
03561    fprintf(p, "MIME-Version: 1.0" ENDL);
03562    if (attach_user_voicemail) {
03563       /* Something unique. */
03564       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
03565 
03566       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
03567       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
03568       fprintf(p, "--%s" ENDL, bound);
03569    }
03570    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
03571    if (emailbody) {
03572       struct ast_channel *ast;
03573       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03574          char *passdata;
03575          int vmlen = strlen(emailbody)*3 + 200;
03576          if ((passdata = alloca(vmlen))) {
03577             memset(passdata, 0, vmlen);
03578             prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03579             pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
03580 #ifdef IMAP_STORAGE
03581             {
03582                /* Convert body to native line terminators for IMAP backend */
03583                char *line = passdata, *next;
03584                do {
03585                   /* Terminate line before outputting it to the file */
03586                   if ((next = strchr(line, '\n'))) {
03587                      *next++ = '\0';
03588                   }
03589                   fprintf(p, "%s" ENDL, line);
03590                   line = next;
03591                } while (!ast_strlen_zero(line));
03592             }
03593 #else
03594             fprintf(p, "%s" ENDL, passdata);
03595 #endif
03596          } else
03597             ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03598          ast_channel_free(ast);
03599       } else
03600          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03601    } else {
03602       if (strcmp(vmu->mailbox, mailbox)) {
03603          /* Forwarded type */
03604          struct ast_config *msg_cfg;
03605          const char *v;
03606          int inttime;
03607          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
03608          /* Retrieve info from VM attribute file */
03609          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03610          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
03611          if (strlen(fromfile) < sizeof(fromfile) - 5) {
03612             strcat(fromfile, ".txt");
03613          }
03614          if ((msg_cfg = ast_config_load(fromfile))) {
03615             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03616                ast_copy_string(origcallerid, v, sizeof(origcallerid));
03617             }
03618 
03619             /* You might be tempted to do origdate, except that a) it's in the wrong
03620              * format, and b) it's missing for IMAP recordings. */
03621             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
03622                time_t ttime = inttime;
03623                struct tm tm;
03624                ast_localtime(&ttime, &tm, NULL);
03625                strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03626             }
03627             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
03628                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
03629                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
03630                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
03631                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
03632                date, origcallerid, origdate);
03633             ast_config_destroy(msg_cfg);
03634          } else {
03635             goto plain_message;
03636          }
03637       } else {
03638 plain_message:
03639          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
03640             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
03641             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
03642             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
03643             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
03644       }
03645    }
03646    if (attach_user_voicemail) {
03647       char tmpdir[256], newtmp[256], tmpcmd[256], newfname[256];
03648       char newfmt[10];
03649       int tmpfd = -1;
03650       int exitstatus = 0;
03651 
03652       if (!ast_strlen_zero(vmu->preprocesscmd) || !ast_strlen_zero(preprocesscmd)) {
03653          create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
03654          snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
03655          tmpfd = mkstemp(newtmp);
03656          chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
03657          if (tmpfd > -1) {
03658             snprintf(newfmt, sizeof(newfmt), (!ast_strlen_zero(vmu->preprocesscmd)) ? (vmu->preprocessfmt ? vmu->preprocessfmt : format) : (preprocessfmt ? preprocessfmt : format));
03659             snprintf(newfname, sizeof(newfname), "%s.%s", newtmp, newfmt);
03660             snprintf(tmpcmd, sizeof(tmpcmd), "%s < %s.%s > %s", !ast_strlen_zero(vmu->preprocesscmd) ? vmu->preprocesscmd : preprocesscmd, attach, format, newfname);
03661             exitstatus = ast_safe_system(tmpcmd);
03662             if (option_debug)
03663                ast_log(LOG_DEBUG, "Executed preprocesscmd '%s', exitstatus %i\n", tmpcmd, exitstatus);
03664             if (exitstatus) {
03665                ast_log(LOG_WARNING, "Command %s exited with exitstatus %i\n", tmpcmd, exitstatus);
03666                close(tmpfd);
03667                unlink(fname);
03668             } else {
03669                attach = newtmp;
03670                strncpy(format, newfmt, 5);
03671                close(tmpfd);
03672             }
03673 
03674          }
03675       }
03676 
03677       /* Eww. We want formats to tell us their own MIME type */
03678       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
03679       int soxstatus = 0;
03680 
03681       if (vmu->volgain < -.001 || vmu->volgain > .001) {
03682          create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
03683          snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
03684          tmpfd = mkstemp(newtmp);
03685          chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
03686          if (option_debug > 2)
03687             ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
03688          if (tmpfd > -1) {
03689             snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
03690             if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
03691                attach = newtmp;
03692                if (option_debug > 2) {
03693                   ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
03694                }
03695             } else {
03696                ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
03697                   soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
03698                ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
03699             }
03700          }
03701       }
03702       fprintf(p, "--%s" ENDL, bound);
03703       if (strcasecmp(format, "mp3"))
03704          fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
03705       else
03706          fprintf(p, "Content-Type: audio/mpeg; name=\"msg%04d.%s\"" ENDL, msgnum + 1, format);
03707       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
03708       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
03709       fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
03710       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
03711       base_encode(fname, p);
03712       fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
03713       if (tmpfd > -1) {
03714          if (soxstatus == 0) {
03715             unlink(fname);
03716          }
03717          close(tmpfd);
03718          unlink(newtmp);
03719       }
03720    }
03721 }
03722 
03723 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *fromfolder, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
03724 {
03725    FILE *p=NULL;
03726    char tmp[80] = "/tmp/astmail-XXXXXX";
03727    char tmp2[256];
03728    char *stringp;
03729 
03730    if (vmu && ast_strlen_zero(vmu->email)) {
03731       ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
03732       return(0);
03733    }
03734 
03735    /* Mail only the first format */
03736    format = ast_strdupa(format);
03737    stringp = format;
03738    strsep(&stringp, "|");
03739 
03740    if (!strcmp(format, "wav49"))
03741       format = "WAV";
03742    if (option_debug > 2)
03743       ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
03744    /* Make a temporary file instead of piping directly to sendmail, in case the mail
03745       command hangs */
03746    if ((p = vm_mkftemp(tmp)) == NULL) {
03747       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03748       return -1;
03749    } else {
03750       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
03751       fclose(p);
03752       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03753       ast_safe_system(tmp2);
03754       if (option_debug > 2)
03755          ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
03756    }
03757    return 0;
03758 }
03759 
03760 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)
03761 {
03762    char date[256];
03763    char host[MAXHOSTNAMELEN] = "";
03764    char who[256];
03765    char dur[PATH_MAX];
03766    char tmp[80] = "/tmp/astmail-XXXXXX";
03767    char tmp2[PATH_MAX];
03768    struct tm tm;
03769    FILE *p;
03770 
03771    if ((p = vm_mkftemp(tmp)) == NULL) {
03772       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03773       return -1;
03774    } else {
03775       gethostname(host, sizeof(host)-1);
03776       if (strchr(srcemail, '@'))
03777          ast_copy_string(who, srcemail, sizeof(who));
03778       else {
03779          snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03780       }
03781       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03782       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03783       fprintf(p, "Date: %s\n", date);
03784 
03785       if (*pagerfromstring) {
03786          struct ast_channel *ast;
03787          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03788             char *passdata;
03789             int vmlen = strlen(fromstring)*3 + 200;
03790             if ((passdata = alloca(vmlen))) {
03791                memset(passdata, 0, vmlen);
03792                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03793                pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
03794                fprintf(p, "From: %s <%s>\n", passdata, who);
03795             } else 
03796                ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03797             ast_channel_free(ast);
03798          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03799       } else
03800          fprintf(p, "From: Asterisk PBX <%s>\n", who);
03801       fprintf(p, "To: %s\n", pager);
03802       if (pagersubject) {
03803          struct ast_channel *ast;
03804          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03805             char *passdata;
03806             int vmlen = strlen(pagersubject) * 3 + 200;
03807             if ((passdata = alloca(vmlen))) {
03808                memset(passdata, 0, vmlen);
03809                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03810                pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
03811                fprintf(p, "Subject: %s\n\n", passdata);
03812             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03813             ast_channel_free(ast);
03814          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03815       } else
03816          fprintf(p, "Subject: New VM\n\n");
03817       strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
03818       if (pagerbody) {
03819          struct ast_channel *ast;
03820          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03821             char *passdata;
03822             int vmlen = strlen(pagerbody)*3 + 200;
03823             if ((passdata = alloca(vmlen))) {
03824                memset(passdata, 0, vmlen);
03825                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03826                pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
03827                fprintf(p, "%s\n", passdata);
03828             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03829          ast_channel_free(ast);
03830          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03831       } else {
03832          fprintf(p, "New %s long msg in box %s\n"
03833                "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
03834       }
03835       fclose(p);
03836       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03837       ast_safe_system(tmp2);
03838       if (option_debug > 2)
03839          ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
03840    }
03841    return 0;
03842 }
03843 
03844 static int get_date(char *s, int len)
03845 {
03846    struct tm tm;
03847    time_t t;
03848 
03849    time(&t);
03850 
03851    ast_localtime(&t, &tm, NULL);
03852 
03853    return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
03854 }
03855 
03856 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
03857 {
03858    int res = -2;
03859 
03860 #ifdef ODBC_STORAGE
03861    int success = 
03862 #endif
03863    RETRIEVE(filename, -1, vmu);
03864    if (ast_fileexists(filename, NULL, NULL) > 0) {
03865       res = ast_streamfile(chan, filename, chan->language);
03866       if (res > -1) 
03867          res = ast_waitstream(chan, ecodes);
03868 #ifdef ODBC_STORAGE
03869       if (success == -1) {
03870          /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
03871          if (option_debug)
03872             ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
03873          store_file(filename, vmu->mailbox, vmu->context, -1);
03874       }
03875 #endif
03876    }
03877    DISPOSE(filename, -1);
03878 
03879    return res;
03880 }
03881 
03882 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
03883 {
03884    int res;
03885    char fn[PATH_MAX];
03886    char dest[PATH_MAX];
03887 
03888    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
03889 
03890    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
03891       ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
03892       return -1;
03893    }
03894 
03895    res = play_greeting(chan, vmu, fn, ecodes);
03896    if (res == -2) {
03897       /* File did not exist */
03898       res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
03899       if (res)
03900          return res;
03901       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
03902    }
03903 
03904    if (res)
03905       return res;
03906 
03907    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
03908    return res;
03909 }
03910 
03911 static void free_zone(struct vm_zone *z)
03912 {
03913    free(z);
03914 }
03915 
03916 #ifdef ODBC_STORAGE
03917 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
03918 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
03919 {
03920    int x = -1;
03921    int res;
03922    SQLHSTMT stmt;
03923    char sql[PATH_MAX];
03924    char rowdata[20];
03925    char tmp[PATH_MAX] = "";
03926    struct odbc_obj *obj;
03927    char *context;
03928    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
03929 
03930    if (newmsgs)
03931       *newmsgs = 0;
03932    if (oldmsgs)
03933       *oldmsgs = 0;
03934 
03935    /* If no mailbox, return immediately */
03936    if (ast_strlen_zero(mailbox))
03937       return 0;
03938 
03939    ast_copy_string(tmp, mailbox, sizeof(tmp));
03940    
03941    context = strchr(tmp, '@');
03942    if (context) {
03943       *context = '\0';
03944       context++;
03945    } else
03946       context = "default";
03947    
03948    obj = ast_odbc_request_obj(odbc_database, 0);
03949    if (obj) {
03950       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
03951       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03952       if (!stmt) {
03953          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03954          ast_odbc_release_obj(obj);
03955          goto yuck;
03956       }
03957       res = SQLFetch(stmt);
03958       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03959          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03960          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03961          ast_odbc_release_obj(obj);
03962          goto yuck;
03963       }
03964       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03965       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03966          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03967          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03968          ast_odbc_release_obj(obj);
03969          goto yuck;
03970       }
03971       *newmsgs = atoi(rowdata);
03972       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03973 
03974       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
03975       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03976       if (!stmt) {
03977          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03978          ast_odbc_release_obj(obj);
03979          goto yuck;
03980       }
03981       res = SQLFetch(stmt);
03982       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03983          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03984          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03985          ast_odbc_release_obj(obj);
03986          goto yuck;
03987       }
03988       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03989       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03990          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03991          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03992          ast_odbc_release_obj(obj);
03993          goto yuck;
03994       }
03995       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03996       ast_odbc_release_obj(obj);
03997       *oldmsgs = atoi(rowdata);
03998       x = 0;
03999    } else
04000       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04001       
04002 yuck:
04003    return x;
04004 }
04005 
04006 static int messagecount(const char *context, const char *mailbox, const char *folder)
04007 {
04008    struct odbc_obj *obj = NULL;
04009    int nummsgs = 0;
04010    int res;
04011    SQLHSTMT stmt = NULL;
04012    char sql[PATH_MAX];
04013    char rowdata[20];
04014    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04015    if (!folder)
04016       folder = "INBOX";
04017    /* If no mailbox, return immediately */
04018    if (ast_strlen_zero(mailbox))
04019       return 0;
04020 
04021    obj = ast_odbc_request_obj(odbc_database, 0);
04022    if (obj) {
04023       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04024       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04025       if (!stmt) {
04026          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04027          goto yuck;
04028       }
04029       res = SQLFetch(stmt);
04030       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04031          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04032          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04033          goto yuck;
04034       }
04035       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04036       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04037          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04038          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04039          goto yuck;
04040       }
04041       nummsgs = atoi(rowdata);
04042       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04043    } else
04044       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04045 
04046 yuck:
04047    if (obj)
04048       ast_odbc_release_obj(obj);
04049    return nummsgs;
04050 }
04051 
04052 static int has_voicemail(const char *mailbox, const char *folder)
04053 {
04054    char tmp[256], *tmp2 = tmp, *mbox, *context;
04055    ast_copy_string(tmp, mailbox, sizeof(tmp));
04056    while ((context = mbox = strsep(&tmp2, ","))) {
04057       strsep(&context, "@");
04058       if (ast_strlen_zero(context))
04059          context = "default";
04060       if (messagecount(context, mbox, folder))
04061          return 1;
04062    }
04063    return 0;
04064 }
04065 #endif
04066 #ifndef IMAP_STORAGE
04067 /* copy message only used by file storage */
04068 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)
04069 {
04070    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04071    const char *frombox = mbox(imbox);
04072    int recipmsgnum;
04073    int res = 0;
04074 
04075    ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04076 
04077    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04078    
04079    if (!dir)
04080       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04081    else
04082       ast_copy_string(fromdir, dir, sizeof(fromdir));
04083 
04084    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04085 
04086    if (vm_lock_path(todir))
04087       return ERROR_LOCK_PATH;
04088 
04089    recipmsgnum = 0;
04090    do {
04091       make_file(topath, sizeof(topath), todir, recipmsgnum);
04092       if (!EXISTS(todir, recipmsgnum, topath, chan->language))
04093          break;
04094       recipmsgnum++;
04095    } while (recipmsgnum < recip->maxmsg);
04096    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04097 #ifndef ODBC_STORAGE
04098       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04099          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04100       } else {
04101 #endif
04102          /* If we are prepending a message for ODBC, then the message already
04103           * exists in the database, but we want to force copying from the
04104           * filesystem (since only the FS contains the prepend). */
04105          copy_plain_file(frompath, topath);
04106          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL);
04107          vm_delete(topath);
04108 #ifndef ODBC_STORAGE
04109       }
04110 #endif
04111    } else {
04112       ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04113       res = -1;
04114    }
04115    ast_unlock_path(todir);
04116    notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04117    
04118    return res;
04119 }
04120 #endif
04121 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04122 static int messagecount(const char *context, const char *mailbox, const char *folder)
04123 {
04124    return __has_voicemail(context, mailbox, folder, 0);
04125 }
04126 
04127 
04128 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04129 {
04130    DIR *dir;
04131    struct dirent *de;
04132    char fn[256];
04133    int ret = 0;
04134    if (!folder)
04135       folder = "INBOX";
04136    /* If no mailbox, return immediately */
04137    if (ast_strlen_zero(mailbox))
04138       return 0;
04139    if (!context)
04140       context = "default";
04141    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04142    dir = opendir(fn);
04143    if (!dir)
04144       return 0;
04145    while ((de = readdir(dir))) {
04146       if (!strncasecmp(de->d_name, "msg", 3)) {
04147          if (shortcircuit) {
04148             ret = 1;
04149             break;
04150          } else if (!strncasecmp(de->d_name + 8, "txt", 3))
04151             ret++;
04152       }
04153    }
04154    closedir(dir);
04155    return ret;
04156 }
04157 
04158 
04159 static int has_voicemail(const char *mailbox, const char *folder)
04160 {
04161    char tmp[256], *tmp2 = tmp, *mbox, *context;
04162    ast_copy_string(tmp, mailbox, sizeof(tmp));
04163    while ((mbox = strsep(&tmp2, ","))) {
04164       if ((context = strchr(mbox, '@')))
04165          *context++ = '\0';
04166       else
04167          context = "default";
04168       if (__has_voicemail(context, mbox, folder, 1))
04169          return 1;
04170    }
04171    return 0;
04172 }
04173 
04174 
04175 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04176 {
04177    char tmp[256];
04178    char *context;
04179 
04180    if (newmsgs)
04181       *newmsgs = 0;
04182    if (oldmsgs)
04183       *oldmsgs = 0;
04184    /* If no mailbox, return immediately */
04185    if (ast_strlen_zero(mailbox))
04186       return 0;
04187    if (strchr(mailbox, ',')) {
04188       int tmpnew, tmpold;
04189       char *mb, *cur;
04190 
04191       ast_copy_string(tmp, mailbox, sizeof(tmp));
04192       mb = tmp;
04193       while ((cur = strsep(&mb, ", "))) {
04194          if (!ast_strlen_zero(cur)) {
04195             if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04196                return -1;
04197             else {
04198                if (newmsgs)
04199                   *newmsgs += tmpnew; 
04200                if (oldmsgs)
04201                   *oldmsgs += tmpold;
04202             }
04203          }
04204       }
04205       return 0;
04206    }
04207    ast_copy_string(tmp, mailbox, sizeof(tmp));
04208    context = strchr(tmp, '@');
04209    if (context) {
04210       *context = '\0';
04211       context++;
04212    } else
04213       context = "default";
04214    if (newmsgs)
04215       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04216    if (oldmsgs)
04217       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04218    return 0;
04219 }
04220 
04221 #endif
04222 
04223 static void run_externnotify(char *context, char *extension)
04224 {
04225    char arguments[255];
04226    char ext_context[256] = "";
04227    int newvoicemails = 0, oldvoicemails = 0;
04228    struct ast_smdi_mwi_message *mwi_msg;
04229 
04230    if (!ast_strlen_zero(context))
04231       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04232    else
04233       ast_copy_string(ext_context, extension, sizeof(ext_context));
04234 
04235    if (!strcasecmp(externnotify, "smdi")) {
04236       if (ast_app_has_voicemail(ext_context, NULL)) 
04237          ast_smdi_mwi_set(smdi_iface, extension);
04238       else
04239          ast_smdi_mwi_unset(smdi_iface, extension);
04240 
04241       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04242          ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04243          if (!strncmp(mwi_msg->cause, "INV", 3))
04244             ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04245          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04246             ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04247          ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04248          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04249       } else {
04250          if (option_debug)
04251             ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
04252       }
04253    } else if (!ast_strlen_zero(externnotify)) {
04254       if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
04255          ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04256       } else {
04257          snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
04258          if (option_debug)
04259             ast_log(LOG_DEBUG, "Executing %s\n", arguments);
04260          ast_safe_system(arguments);
04261       }
04262    }
04263 }
04264 
04265 struct leave_vm_options {
04266    unsigned int flags;
04267    signed char record_gain;
04268 };
04269 
04270 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04271 {
04272 #ifdef IMAP_STORAGE
04273    int newmsgs, oldmsgs;
04274 #endif
04275    struct vm_state *vms = NULL;
04276    char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
04277    char callerid[256];
04278    FILE *txt;
04279    char date[50];
04280    int txtdes;
04281    int res = 0;
04282    int msgnum;
04283    int duration = 0;
04284    int ausemacro = 0;
04285    int ousemacro = 0;
04286    int ouseexten = 0;
04287    char dir[PATH_MAX], tmpdir[PATH_MAX];
04288    char dest[PATH_MAX];
04289    char fn[PATH_MAX];
04290    char prefile[PATH_MAX] = "";
04291    char tempfile[PATH_MAX] = "";
04292    char ext_context[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2] = "";
04293    char fmt[80];
04294    char *context;
04295    char ecodes[16] = "#";
04296    char tmp[1324] = "", *tmpptr;
04297    struct ast_vm_user *vmu;
04298    struct ast_vm_user svm;
04299    const char *category = NULL;
04300 
04301    if (strlen(ext) > sizeof(tmp) - 1) {
04302       ast_log(LOG_WARNING, "List of extensions is too long (>%ld).  Truncating.\n", (long) sizeof(tmp) - 1);
04303    }
04304    ast_copy_string(tmp, ext, sizeof(tmp));
04305    ext = tmp;
04306    context = strchr(tmp, '@');
04307    if (context) {
04308       *context++ = '\0';
04309       tmpptr = strchr(context, '&');
04310    } else {
04311       tmpptr = strchr(ext, '&');
04312    }
04313 
04314    if (tmpptr)
04315       *tmpptr++ = '\0';
04316 
04317    category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
04318 
04319    if (option_debug > 2)
04320       ast_log(LOG_DEBUG, "Before find_user\n");
04321    if (!(vmu = find_user(&svm, context, ext))) {
04322       ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
04323       if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
04324          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04325       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04326       return res;
04327    }
04328    /* Setup pre-file if appropriate */
04329    if (strcmp(vmu->context, "default"))
04330       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
04331    else
04332       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
04333    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
04334       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
04335       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
04336    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
04337       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
04338       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
04339    }
04340    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
04341    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
04342       ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
04343       return -1;
04344    }
04345    RETRIEVE(tempfile, -1, vmu);
04346    if (ast_fileexists(tempfile, NULL, NULL) > 0)
04347       ast_copy_string(prefile, tempfile, sizeof(prefile));
04348    DISPOSE(tempfile, -1);
04349    /* It's easier just to try to make it than to check for its existence */
04350 #ifndef IMAP_STORAGE
04351    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
04352 #else
04353    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
04354    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
04355       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
04356    }
04357 #endif
04358    create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
04359 
04360    /* Check current or macro-calling context for special extensions */
04361    if (ast_test_flag(vmu, VM_OPERATOR)) {
04362       if (!ast_strlen_zero(vmu->exit)) {
04363          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
04364             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04365             ouseexten = 1;
04366          }
04367       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
04368          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04369          ouseexten = 1;
04370       }
04371       else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
04372       strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04373       ousemacro = 1;
04374       }
04375    }
04376 
04377    if (!ast_strlen_zero(vmu->exit)) {
04378       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
04379          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04380    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
04381       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04382    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
04383       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04384       ausemacro = 1;
04385    }
04386 
04387    /* Play the beginning intro if desired */
04388    if (!ast_strlen_zero(prefile)) {
04389       res = play_greeting(chan, vmu, prefile, ecodes);
04390       if (res == -2) {
04391          /* The file did not exist */
04392          if (option_debug)
04393             ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
04394          res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
04395       }
04396       if (res < 0) {
04397          if (option_debug)
04398             ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
04399          free_user(vmu);
04400          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04401          return -1;
04402       }
04403    }
04404    if (res == '#') {
04405       /* On a '#' we skip the instructions */
04406       ast_set_flag(options, OPT_SILENT);
04407       res = 0;
04408    }
04409    if (!res && !ast_test_flag(options, OPT_SILENT)) {
04410       res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
04411       if (res == '#') {
04412          ast_set_flag(options, OPT_SILENT);
04413          res = 0;
04414       }
04415    }
04416    if (res > 0)
04417       ast_stopstream(chan);
04418    /* Check for a '*' here in case the caller wants to escape from voicemail to something
04419     other than the operator -- an automated attendant or mailbox login for example */
04420    if (res == '*') {
04421       chan->exten[0] = 'a';
04422       chan->exten[1] = '\0';
04423       if (!ast_strlen_zero(vmu->exit)) {
04424          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04425       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
04426          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04427       }
04428       chan->priority = 0;
04429       free_user(vmu);
04430       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04431       return 0;
04432    }
04433 
04434    /* Check for a '0' here */
04435    if (res == '0') {
04436    transfer:
04437       if (ouseexten || ousemacro) {
04438          chan->exten[0] = 'o';
04439          chan->exten[1] = '\0';
04440          if (!ast_strlen_zero(vmu->exit)) {
04441             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04442          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
04443             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04444          }
04445          ast_play_and_wait(chan, "transfer");
04446          chan->priority = 0;
04447          free_user(vmu);
04448          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04449       }
04450       return OPERATOR_EXIT;
04451    }
04452    if (res < 0) {
04453       free_user(vmu);
04454       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04455       return -1;
04456    }
04457    /* The meat of recording the message...  All the announcements and beeps have been played*/
04458    ast_copy_string(fmt, vmfmts, sizeof(fmt));
04459    if (!ast_strlen_zero(fmt)) {
04460       msgnum = 0;
04461 
04462 #ifdef IMAP_STORAGE
04463       /* Is ext a mailbox? */
04464       /* must open stream for this user to get info! */
04465       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
04466       if (res < 0) {
04467          ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
04468          return -1;
04469       }
04470       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
04471       /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
04472        * rarely be used*/
04473          if (!(vms = create_vm_state_from_user(vmu))) {
04474             ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
04475             return -1;
04476          }
04477       }
04478       vms->newmessages++;
04479       /* here is a big difference! We add one to it later */
04480       msgnum = newmsgs + oldmsgs;
04481       if (option_debug > 2)
04482          ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
04483       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
04484       /* set variable for compatability */
04485       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04486 
04487       if (imap_check_limits(chan, vms, vmu, msgnum)) {
04488          goto leave_vm_out;
04489       }
04490 #else
04491       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
04492          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04493          if (!res)
04494             res = ast_waitstream(chan, "");
04495          ast_log(LOG_WARNING, "No more messages possible\n");
04496          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04497          inprocess_count(vmu->mailbox, vmu->context, -1);
04498          goto leave_vm_out;
04499       }
04500 
04501 #endif
04502       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
04503       txtdes = mkstemp(tmptxtfile);
04504       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
04505       if (txtdes < 0) {
04506          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04507          if (!res)
04508             res = ast_waitstream(chan, "");
04509          ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
04510          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04511          inprocess_count(vmu->mailbox, vmu->context, -1);
04512          goto leave_vm_out;
04513       }
04514 
04515       /* Now play the beep once we have the message number for our next message. */
04516       if (res >= 0) {
04517          /* Unless we're *really* silent, try to send the beep */
04518          res = ast_stream_and_wait(chan, "beep", chan->language, "");
04519       }
04520             
04521       /* Store information */
04522       txt = fdopen(txtdes, "w+");
04523       if (txt) {
04524          get_date(date, sizeof(date));
04525          fprintf(txt, 
04526             ";\n"
04527             "; Message Information file\n"
04528             ";\n"
04529             "[message]\n"
04530             "origmailbox=%s\n"
04531             "context=%s\n"
04532             "macrocontext=%s\n"
04533             "exten=%s\n"
04534             "priority=%d\n"
04535             "callerchan=%s\n"
04536             "callerid=%s\n"
04537             "origdate=%s\n"
04538             "origtime=%ld\n"
04539             "category=%s\n",
04540             ext,
04541             chan->context,
04542             chan->macrocontext, 
04543             chan->exten,
04544             chan->priority,
04545             chan->name,
04546             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
04547             date, (long)time(NULL),
04548             category ? category : ""); 
04549       } else
04550          ast_log(LOG_WARNING, "Error opening text file for output\n");
04551       res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
04552 
04553       if (txt) {
04554          if (duration < vmminmessage) {
04555             fclose(txt);
04556             if (option_verbose > 2) 
04557                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
04558             ast_filedelete(tmptxtfile, NULL);
04559             unlink(tmptxtfile);
04560             inprocess_count(vmu->mailbox, vmu->context, -1);
04561          } else {
04562             fprintf(txt, "duration=%d\n", duration);
04563             fclose(txt);
04564             if (vm_lock_path(dir)) {
04565                ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
04566                /* Delete files */
04567                ast_filedelete(tmptxtfile, NULL);
04568                unlink(tmptxtfile);
04569                inprocess_count(vmu->mailbox, vmu->context, -1);
04570             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
04571                if (option_debug) 
04572                   ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
04573                unlink(tmptxtfile);
04574                ast_unlock_path(dir);
04575                inprocess_count(vmu->mailbox, vmu->context, -1);
04576             } else {
04577 #ifndef IMAP_STORAGE
04578                msgnum = last_message_index(vmu, dir) + 1;
04579 #endif
04580                make_file(fn, sizeof(fn), dir, msgnum);
04581 
04582                /* assign a variable with the name of the voicemail file */ 
04583 #ifndef IMAP_STORAGE
04584                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
04585 #else
04586                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04587 #endif
04588 
04589                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
04590                ast_filerename(tmptxtfile, fn, NULL);
04591                rename(tmptxtfile, txtfile);
04592                inprocess_count(vmu->mailbox, vmu->context, -1);
04593 
04594                ast_unlock_path(dir);
04595                /* We must store the file first, before copying the message, because
04596                 * ODBC storage does the entire copy with SQL.
04597                 */
04598                if (ast_fileexists(fn, NULL, NULL) > 0) {
04599                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
04600                }
04601 
04602                /* Are there to be more recipients of this message? */
04603                while (tmpptr) {
04604                   struct ast_vm_user recipu, *recip;
04605                   char *exten, *context;
04606                
04607                   exten = strsep(&tmpptr, "&");
04608                   context = strchr(exten, '@');
04609                   if (context) {
04610                      *context = '\0';
04611                      context++;
04612                   }
04613                   if ((recip = find_user(&recipu, context, exten))) {
04614                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
04615                      free_user(recip);
04616                   }
04617                }
04618                /* Notification and disposal needs to happen after the copy, though. */
04619                if (ast_fileexists(fn, NULL, NULL)) {
04620                   notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04621                   DISPOSE(dir, msgnum);
04622                }
04623             }
04624          }
04625       } else {
04626          inprocess_count(vmu->mailbox, vmu->context, -1);
04627       }
04628       if (res == '0') {
04629          goto transfer;
04630       } else if (res > 0 && res != 't')
04631          res = 0;
04632 
04633       if (duration < vmminmessage)
04634          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
04635          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04636       else
04637          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
04638    } else
04639       ast_log(LOG_WARNING, "No format for saving voicemail?\n");
04640 leave_vm_out:
04641    free_user(vmu);
04642    
04643    return res;
04644 }
04645 
04646 #if !defined(IMAP_STORAGE)
04647 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
04648 {
04649    /* we know the actual number of messages, so stop process when number is hit */
04650 
04651    int x,dest;
04652    char sfn[PATH_MAX];
04653    char dfn[PATH_MAX];
04654 
04655    if (vm_lock_path(dir))
04656       return ERROR_LOCK_PATH;
04657 
04658    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
04659       make_file(sfn, sizeof(sfn), dir, x);
04660       if (EXISTS(dir, x, sfn, NULL)) {
04661          
04662          if (x != dest) {
04663             make_file(dfn, sizeof(dfn), dir, dest);
04664             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
04665          }
04666          
04667          dest++;
04668       }
04669    }
04670    ast_unlock_path(dir);
04671 
04672    return dest;
04673 }
04674 #endif
04675 
04676 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
04677 {
04678    int d;
04679    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
04680    return d;
04681 }
04682 
04683 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
04684 {
04685 #ifdef IMAP_STORAGE
04686    /* we must use mbox(x) folder names, and copy the message there */
04687    /* simple. huh? */
04688    char sequence[10];
04689    /* get the real IMAP message number for this message */
04690    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
04691    if (option_debug > 2)
04692       ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
04693    ast_mutex_lock(&vms->lock);
04694    if (box == 1) {
04695       mail_setflag(vms->mailstream, sequence, "\\Seen");
04696    } else if (box == 0) {
04697       mail_clearflag(vms->mailstream, sequence, "\\Seen");
04698    }
04699    if (!strcasecmp(mbox(0), vms->curbox) && (box == 0 || box == 1)) {
04700       ast_mutex_unlock(&vms->lock);
04701       return 0;
04702    } else {
04703       int res = !mail_copy(vms->mailstream,sequence,(char *) mbox(box)); 
04704       ast_mutex_unlock(&vms->lock);
04705       return res;
04706    }
04707 #else
04708    char *dir = vms->curdir;
04709    char *username = vms->username;
04710    char *context = vmu->context;
04711    char sfn[PATH_MAX];
04712    char dfn[PATH_MAX];
04713    char ddir[PATH_MAX];
04714    const char *dbox = mbox(box);
04715    int x;
04716    make_file(sfn, sizeof(sfn), dir, msg);
04717    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
04718 
04719    if (vm_lock_path(ddir))
04720       return ERROR_LOCK_PATH;
04721 
04722    for (x = 0; x < vmu->maxmsg; x++) {
04723       make_file(dfn, sizeof(dfn), ddir, x);
04724       if (!EXISTS(ddir, x, dfn, NULL))
04725          break;
04726    }
04727    if (x >= vmu->maxmsg) {
04728       ast_unlock_path(ddir);
04729       return ERROR_MAILBOX_FULL;
04730    }
04731    if (strcmp(sfn, dfn)) {
04732       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
04733    }
04734    ast_unlock_path(ddir);
04735 #endif
04736    return 0;
04737 }
04738 
04739 static int adsi_logo(unsigned char *buf)
04740 {
04741    int bytes = 0;
04742    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
04743    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
04744    return bytes;
04745 }
04746 
04747 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
04748 {
04749    unsigned char buf[256];
04750    int bytes=0;
04751    int x;
04752    char num[5];
04753 
04754    *useadsi = 0;
04755    bytes += ast_adsi_data_mode(buf + bytes);
04756    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04757 
04758    bytes = 0;
04759    bytes += adsi_logo(buf);
04760    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04761 #ifdef DISPLAY
04762    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
04763 #endif
04764    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04765    bytes += ast_adsi_data_mode(buf + bytes);
04766    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04767 
04768    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
04769       bytes = 0;
04770       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
04771       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04772       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04773       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04774       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04775       return 0;
04776    }
04777 
04778 #ifdef DISPLAY
04779    /* Add a dot */
04780    bytes = 0;
04781    bytes += ast_adsi_logo(buf);
04782    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04783    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
04784    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04785    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04786 #endif
04787    bytes = 0;
04788    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
04789    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
04790    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
04791    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
04792    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
04793    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
04794    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04795 
04796 #ifdef DISPLAY
04797    /* Add another dot */
04798    bytes = 0;
04799    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
04800    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04801 
04802    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04803    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04804 #endif
04805 
04806    bytes = 0;
04807    /* These buttons we load but don't use yet */
04808    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
04809    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
04810    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
04811    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
04812    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
04813    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
04814    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04815 
04816 #ifdef DISPLAY
04817    /* Add another dot */
04818    bytes = 0;
04819    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
04820    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04821    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04822 #endif
04823 
04824    bytes = 0;
04825    for (x=0;x<5;x++) {
04826       snprintf(num, sizeof(num), "%d", x);
04827       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
04828    }
04829    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
04830    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04831 
04832 #ifdef DISPLAY
04833    /* Add another dot */
04834    bytes = 0;
04835    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
04836    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04837    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04838 #endif
04839 
04840    if (ast_adsi_end_download(chan)) {
04841       bytes = 0;
04842       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
04843       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04844       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04845       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04846       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04847       return 0;
04848    }
04849    bytes = 0;
04850    bytes += ast_adsi_download_disconnect(buf + bytes);
04851    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04852    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04853 
04854    if (option_debug)
04855       ast_log(LOG_DEBUG, "Done downloading scripts...\n");
04856 
04857 #ifdef DISPLAY
04858    /* Add last dot */
04859    bytes = 0;
04860    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
04861    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04862 #endif
04863    if (option_debug)
04864       ast_log(LOG_DEBUG, "Restarting session...\n");
04865 
04866    bytes = 0;
04867    /* Load the session now */
04868    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
04869       *useadsi = 1;
04870       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
04871    } else
04872       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
04873 
04874    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04875    return 0;
04876 }
04877 
04878 static void adsi_begin(struct ast_channel *chan, int *useadsi)
04879 {
04880    int x;
04881    if (!ast_adsi_available(chan))
04882       return;
04883    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
04884    if (x < 0)
04885       return;
04886    if (!x) {
04887       if (adsi_load_vmail(chan, useadsi)) {
04888          ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
04889          return;
04890       }
04891    } else
04892       *useadsi = 1;
04893 }
04894 
04895 static void adsi_login(struct ast_channel *chan)
04896 {
04897    unsigned char buf[256];
04898    int bytes=0;
04899    unsigned char keys[8];
04900    int x;
04901    if (!ast_adsi_available(chan))
04902       return;
04903 
04904    for (x=0;x<8;x++)
04905       keys[x] = 0;
04906    /* Set one key for next */
04907    keys[3] = ADSI_KEY_APPS + 3;
04908 
04909    bytes += adsi_logo(buf + bytes);
04910    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
04911    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
04912    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04913    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
04914    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
04915    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
04916    bytes += ast_adsi_set_keys(buf + bytes, keys);
04917    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04918    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04919 }
04920 
04921 static void adsi_password(struct ast_channel *chan)
04922 {
04923    unsigned char buf[256];
04924    int bytes=0;
04925    unsigned char keys[8];
04926    int x;
04927    if (!ast_adsi_available(chan))
04928       return;
04929 
04930    for (x=0;x<8;x++)
04931       keys[x] = 0;
04932    /* Set one key for next */
04933    keys[3] = ADSI_KEY_APPS + 3;
04934 
04935    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04936    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
04937    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
04938    bytes += ast_adsi_set_keys(buf + bytes, keys);
04939    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04940    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04941 }
04942 
04943 static void adsi_folders(struct ast_channel *chan, int start, char *label)
04944 {
04945    unsigned char buf[256];
04946    int bytes=0;
04947    unsigned char keys[8];
04948    int x,y;
04949 
04950    if (!ast_adsi_available(chan))
04951       return;
04952 
04953    for (x=0;x<5;x++) {
04954       y = ADSI_KEY_APPS + 12 + start + x;
04955       if (y > ADSI_KEY_APPS + 12 + 4)
04956          y = 0;
04957       keys[x] = ADSI_KEY_SKT | y;
04958    }
04959    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
04960    keys[6] = 0;
04961    keys[7] = 0;
04962 
04963    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
04964    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
04965    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04966    bytes += ast_adsi_set_keys(buf + bytes, keys);
04967    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04968 
04969    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04970 }
04971 
04972 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
04973 {
04974    int bytes=0;
04975    unsigned char buf[256]; 
04976    char buf1[256], buf2[256];
04977    char fn2[PATH_MAX];
04978 
04979    char cid[256]="";
04980    char *val;
04981    char *name, *num;
04982    char datetime[21]="";
04983    FILE *f;
04984 
04985    unsigned char keys[8];
04986 
04987    int x;
04988 
04989    if (!ast_adsi_available(chan))
04990       return;
04991 
04992    /* Retrieve important info */
04993    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
04994    f = fopen(fn2, "r");
04995    if (f) {
04996       while (!feof(f)) {   
04997          if (!fgets((char *)buf, sizeof(buf), f)) {
04998             continue;
04999          }
05000          if (!feof(f)) {
05001             char *stringp=NULL;
05002             stringp = (char *)buf;
05003             strsep(&stringp, "=");
05004             val = strsep(&stringp, "=");
05005             if (!ast_strlen_zero(val)) {
05006                if (!strcmp((char *)buf, "callerid"))
05007                   ast_copy_string(cid, val, sizeof(cid));
05008                if (!strcmp((char *)buf, "origdate"))
05009                   ast_copy_string(datetime, val, sizeof(datetime));
05010             }
05011          }
05012       }
05013       fclose(f);
05014    }
05015    /* New meaning for keys */
05016    for (x=0;x<5;x++)
05017       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05018    keys[6] = 0x0;
05019    keys[7] = 0x0;
05020 
05021    if (!vms->curmsg) {
05022       /* No prev key, provide "Folder" instead */
05023       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05024    }
05025    if (vms->curmsg >= vms->lastmsg) {
05026       /* If last message ... */
05027       if (vms->curmsg) {
05028          /* but not only message, provide "Folder" instead */
05029          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05030          bytes += ast_adsi_voice_mode(buf + bytes, 0);
05031 
05032       } else {
05033          /* Otherwise if only message, leave blank */
05034          keys[3] = 1;
05035       }
05036    }
05037 
05038    if (!ast_strlen_zero(cid)) {
05039       ast_callerid_parse(cid, &name, &num);
05040       if (!name)
05041          name = num;
05042    } else
05043       name = "Unknown Caller";
05044 
05045    /* If deleted, show "undeleted" */
05046 
05047    if (vms->deleted[vms->curmsg])
05048       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05049 
05050    /* Except "Exit" */
05051    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05052    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05053       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05054    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05055 
05056    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05057    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05058    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05059    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05060    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05061    bytes += ast_adsi_set_keys(buf + bytes, keys);
05062    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05063 
05064    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05065 }
05066 
05067 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05068 {
05069    int bytes=0;
05070    unsigned char buf[256];
05071    unsigned char keys[8];
05072 
05073    int x;
05074 
05075    if (!ast_adsi_available(chan))
05076       return;
05077 
05078    /* New meaning for keys */
05079    for (x=0;x<5;x++)
05080       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05081 
05082    keys[6] = 0x0;
05083    keys[7] = 0x0;
05084 
05085    if (!vms->curmsg) {
05086       /* No prev key, provide "Folder" instead */
05087       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05088    }
05089    if (vms->curmsg >= vms->lastmsg) {
05090       /* If last message ... */
05091       if (vms->curmsg) {
05092          /* but not only message, provide "Folder" instead */
05093          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05094       } else {
05095          /* Otherwise if only message, leave blank */
05096          keys[3] = 1;
05097       }
05098    }
05099 
05100    /* If deleted, show "undeleted" */
05101    if (vms->deleted[vms->curmsg]) 
05102       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05103 
05104    /* Except "Exit" */
05105    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05106    bytes += ast_adsi_set_keys(buf + bytes, keys);
05107    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05108 
05109    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05110 }
05111 
05112 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05113 {
05114    unsigned char buf[256] = "";
05115    char buf1[256] = "", buf2[256] = "";
05116    int bytes=0;
05117    unsigned char keys[8];
05118    int x;
05119 
05120    char *newm = (vms->newmessages == 1) ? "message" : "messages";
05121    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05122    if (!ast_adsi_available(chan))
05123       return;
05124    if (vms->newmessages) {
05125       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05126       if (vms->oldmessages) {
05127          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05128          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05129       } else {
05130          snprintf(buf2, sizeof(buf2), "%s.", newm);
05131       }
05132    } else if (vms->oldmessages) {
05133       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05134       snprintf(buf2, sizeof(buf2), "%s.", oldm);
05135    } else {
05136       strcpy(buf1, "You have no messages.");
05137       buf2[0] = ' ';
05138       buf2[1] = '\0';
05139    }
05140    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05141    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05142    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05143 
05144    for (x=0;x<6;x++)
05145       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05146    keys[6] = 0;
05147    keys[7] = 0;
05148 
05149    /* Don't let them listen if there are none */
05150    if (vms->lastmsg < 0)
05151       keys[0] = 1;
05152    bytes += ast_adsi_set_keys(buf + bytes, keys);
05153 
05154    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05155 
05156    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05157 }
05158 
05159 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05160 {
05161    unsigned char buf[256] = "";
05162    char buf1[256] = "", buf2[256] = "";
05163    int bytes=0;
05164    unsigned char keys[8];
05165    int x;
05166 
05167    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05168 
05169    if (!ast_adsi_available(chan))
05170       return;
05171 
05172    /* Original command keys */
05173    for (x=0;x<6;x++)
05174       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05175 
05176    keys[6] = 0;
05177    keys[7] = 0;
05178 
05179    if ((vms->lastmsg + 1) < 1)
05180       keys[0] = 0;
05181 
05182    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05183       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05184 
05185    if (vms->lastmsg + 1)
05186       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05187    else
05188       strcpy(buf2, "no messages.");
05189    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05190    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05191    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05192    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05193    bytes += ast_adsi_set_keys(buf + bytes, keys);
05194 
05195    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05196 
05197    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05198    
05199 }
05200 
05201 /*
05202 static void adsi_clear(struct ast_channel *chan)
05203 {
05204    char buf[256];
05205    int bytes=0;
05206    if (!ast_adsi_available(chan))
05207       return;
05208    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05209    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05210 
05211    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05212 }
05213 */
05214 
05215 static void adsi_goodbye(struct ast_channel *chan)
05216 {
05217    unsigned char buf[256];
05218    int bytes=0;
05219 
05220    if (!ast_adsi_available(chan))
05221       return;
05222    bytes += adsi_logo(buf + bytes);
05223    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
05224    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
05225    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05226    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05227 
05228    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05229 }
05230 
05231 /*--- get_folder: Folder menu ---*/
05232 /* Plays "press 1 for INBOX messages" etc
05233    Should possibly be internationalized
05234  */
05235 static int get_folder(struct ast_channel *chan, int start)
05236 {
05237    int x;
05238    int d;
05239    char fn[PATH_MAX];
05240    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
05241    if (d)
05242       return d;
05243    for (x = start; x< 5; x++) {  /* For all folders */
05244       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
05245          return d;
05246       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
05247       if (d)
05248          return d;
05249       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
05250       d = vm_play_folder_name(chan, fn);
05251       if (d)
05252          return d;
05253       d = ast_waitfordigit(chan, 500);
05254       if (d)
05255          return d;
05256    }
05257    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
05258    if (d)
05259       return d;
05260    d = ast_waitfordigit(chan, 4000);
05261    return d;
05262 }
05263 
05264 static int get_folder2(struct ast_channel *chan, char *fn, int start)
05265 {
05266    int res = 0;
05267    int loops = 0;
05268    res = ast_play_and_wait(chan, fn);  /* Folder name */
05269    while (((res < '0') || (res > '9')) &&
05270          (res != '#') && (res >= 0) &&
05271          loops < 4) {
05272       res = get_folder(chan, 0);
05273       loops++;
05274    }
05275    if (loops == 4) { /* give up */
05276       return '#';
05277    }
05278    return res;
05279 }
05280 
05281 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
05282               char *context, signed char record_gain, long *duration, struct vm_state *vms)
05283 {
05284    int cmd = 0;
05285    int retries = 0, prepend_duration = 0, already_recorded = 0;
05286    signed char zero_gain = 0;
05287    struct ast_config *msg_cfg;
05288    const char *duration_cstr;
05289    char msgfile[PATH_MAX], backup[PATH_MAX];
05290    char textfile[PATH_MAX];
05291    char backup_textfile[PATH_MAX];
05292    struct ast_category *msg_cat;
05293    char duration_str[12] = "";
05294 
05295    ast_log(LOG_NOTICE, "curdir=%s\n", curdir);
05296    /* Must always populate duration correctly */
05297    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
05298    strcpy(textfile, msgfile);
05299    strcpy(backup, msgfile);
05300    strcpy(backup_textfile, msgfile);
05301    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05302    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05303    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05304 
05305    if (!(msg_cfg = ast_config_load(textfile))) {
05306       return -1;
05307    }
05308 
05309    *duration = 0;
05310    if ((duration_cstr = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
05311       *duration = atoi(duration_cstr);
05312    }
05313 
05314    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
05315       if (cmd)
05316          retries = 0;
05317       switch (cmd) {
05318       case '1': 
05319          /* prepend a message to the current message, update the metadata and return */
05320       {
05321          prepend_duration = 0;
05322 
05323          /* Back up the original file, so we can retry the prepend */
05324 #ifndef IMAP_STORAGE
05325          if (already_recorded) {
05326             ast_filecopy(backup, msgfile, NULL);
05327             copy(textfile, backup_textfile);
05328          } else {
05329             ast_filecopy(msgfile, backup, NULL);
05330             copy(textfile, backup_textfile);
05331          }
05332 #endif
05333          already_recorded = 1;
05334 
05335          if (record_gain)
05336             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
05337 
05338          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
05339          if (cmd == 'S') {
05340             ast_filerename(backup, msgfile, NULL);
05341          }
05342 
05343          if (record_gain)
05344             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
05345 
05346          if (prepend_duration) {
05347             prepend_duration += *duration;
05348          }
05349 
05350          break;
05351       }
05352       case '2': 
05353          cmd = 't';
05354          break;
05355       case '*':
05356          cmd = '*';
05357          break;
05358       default: 
05359          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
05360             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
05361          if (!cmd)
05362             cmd = ast_play_and_wait(chan,"vm-starmain");
05363             /* "press star to return to the main menu" */
05364          if (!cmd)
05365             cmd = ast_waitfordigit(chan,6000);
05366          if (!cmd)
05367             retries++;
05368          if (retries > 3)
05369             cmd = 't';
05370       }
05371    }
05372 
05373    if (already_recorded && cmd == -1) {
05374       /* Restore original files, if operation cancelled */
05375       ast_filerename(backup, msgfile, NULL);
05376       if (duration_cstr) {
05377          ast_copy_string(duration_str, duration_cstr, sizeof(duration_str));
05378       }
05379    } else if (prepend_duration) {
05380       *duration = prepend_duration;
05381       snprintf(duration_str, sizeof(duration_str), "%d", prepend_duration);
05382    }
05383 
05384    msg_cat = ast_category_get(msg_cfg, "message");
05385    if (!ast_strlen_zero(duration_str) && !ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
05386       config_text_file_save(textfile, msg_cfg, "app_voicemail");
05387    }
05388    ast_config_destroy(msg_cfg);
05389 
05390    if (cmd == 't' || cmd == 'S')
05391       cmd = 0;
05392    return cmd;
05393 }
05394 
05395 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
05396 {
05397    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
05398    int newmsgs = 0, oldmsgs = 0;
05399    const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
05400 
05401 #ifndef IMAP_STORAGE
05402    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
05403 #else
05404    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
05405 #endif
05406    make_file(fn, sizeof(fn), todir, msgnum);
05407    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
05408 
05409    if (!ast_strlen_zero(vmu->attachfmt)) {
05410       if (strstr(fmt, vmu->attachfmt)) {
05411          fmt = vmu->attachfmt;
05412       } else {
05413          ast_log(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);
05414       }
05415    }
05416 
05417    /* Attach only the first format */
05418    fmt = ast_strdupa(fmt);
05419    stringp = fmt;
05420    strsep(&stringp, "|");
05421 
05422    if (!ast_strlen_zero(vmu->email)) {
05423       int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
05424       char *myserveremail = serveremail;
05425       attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
05426       if (!ast_strlen_zero(vmu->serveremail))
05427          myserveremail = vmu->serveremail;
05428       
05429       if (attach_user_voicemail)
05430          RETRIEVE(todir, msgnum, vmu);
05431 
05432       /*XXX possible imap issue, should category be NULL XXX*/
05433       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
05434 
05435       if (attach_user_voicemail)
05436          DISPOSE(todir, msgnum);
05437    }
05438 
05439    if (!ast_strlen_zero(vmu->pager)) {
05440       char *myserveremail = serveremail;
05441       if (!ast_strlen_zero(vmu->serveremail))
05442          myserveremail = vmu->serveremail;
05443       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category);
05444    }
05445 
05446    if (ast_test_flag(vmu, VM_DELETE)) {
05447       DELETE(todir, msgnum, fn, vmu);
05448    }
05449 
05450    /* Leave voicemail for someone */
05451    if (ast_app_has_voicemail(ext_context, NULL)) {
05452       ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
05453    }
05454    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);
05455    run_externnotify(vmu->context, vmu->mailbox);
05456    return 0;
05457 }
05458 
05459 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)
05460 {
05461 #ifdef IMAP_STORAGE
05462    int todircount=0;
05463    struct vm_state *dstvms;
05464 #else
05465    char textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
05466 #endif
05467    char username[70]="";
05468    int res = 0, cmd = 0;
05469    struct ast_vm_user *receiver = NULL, *vmtmp;
05470    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
05471    char *stringp;
05472    const char *s;
05473    int saved_messages = 0;
05474    int valid_extensions = 0;
05475    char *dir;
05476    int curmsg;
05477    int prompt_played = 0;
05478 
05479    if (vms == NULL) return -1;
05480    dir = vms->curdir;
05481    curmsg = vms->curmsg;
05482    
05483    while (!res && !valid_extensions) {
05484       int use_directory = 0;
05485       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
05486          int done = 0;
05487          int retries = 0;
05488          cmd=0;
05489          while ((cmd >= 0) && !done ){
05490             if (cmd)
05491                retries = 0;
05492             switch (cmd) {
05493             case '1': 
05494                use_directory = 0;
05495                done = 1;
05496                break;
05497             case '2': 
05498                use_directory = 1;
05499                done=1;
05500                break;
05501             case '*': 
05502                cmd = 't';
05503                done = 1;
05504                break;
05505             default: 
05506                /* Press 1 to enter an extension press 2 to use the directory */
05507                cmd = ast_play_and_wait(chan,"vm-forward");
05508                if (!cmd)
05509                   cmd = ast_waitfordigit(chan,3000);
05510                if (!cmd)
05511                   retries++;
05512                if (retries > 3)
05513                {
05514                   cmd = 't';
05515                   done = 1;
05516                }
05517                
05518             }
05519          }
05520          if (cmd < 0 || cmd == 't')
05521             break;
05522       }
05523       
05524       if (use_directory) {
05525          /* use app_directory */
05526          
05527          char old_context[sizeof(chan->context)];
05528          char old_exten[sizeof(chan->exten)];
05529          int old_priority;
05530          struct ast_app* app;
05531 
05532          
05533          app = pbx_findapp("Directory");
05534          if (app) {
05535             char vmcontext[256];
05536             /* make backup copies */
05537             memcpy(old_context, chan->context, sizeof(chan->context));
05538             memcpy(old_exten, chan->exten, sizeof(chan->exten));
05539             old_priority = chan->priority;
05540             
05541             /* call the the Directory, changes the channel */
05542             snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
05543             res = pbx_exec(chan, app, vmcontext);
05544             
05545             ast_copy_string(username, chan->exten, sizeof(username));
05546             
05547             /* restore the old context, exten, and priority */
05548             memcpy(chan->context, old_context, sizeof(chan->context));
05549             memcpy(chan->exten, old_exten, sizeof(chan->exten));
05550             chan->priority = old_priority;
05551             
05552          } else {
05553             ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
05554             ast_clear_flag((&globalflags), VM_DIRECFORWARD);   
05555          }
05556       } else {
05557          /* Ask for an extension */
05558          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
05559          prompt_played++;
05560          if (res || prompt_played > 4)
05561             break;
05562          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
05563             break;
05564       }
05565       
05566       /* start all over if no username */
05567       if (ast_strlen_zero(username))
05568          continue;
05569       stringp = username;
05570       s = strsep(&stringp, "*");
05571       /* start optimistic */
05572       valid_extensions = 1;
05573       while (s) {
05574          /* Don't forward to ourselves but allow leaving a message for ourselves (is_new_message == 1).  find_user is going to malloc since we have a NULL as first argument */
05575          if ((is_new_message == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
05576             int oldmsgs;
05577             int newmsgs;
05578             int capacity;
05579             if (inboxcount(s, &newmsgs, &oldmsgs)) {
05580                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
05581                /* Shouldn't happen, but allow trying another extension if it does */
05582                res = ast_play_and_wait(chan, "pbx-invalid");
05583                valid_extensions = 0;
05584                break;
05585             }
05586             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
05587             if ((newmsgs + oldmsgs) >= capacity) {
05588                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
05589                res = ast_play_and_wait(chan, "vm-mailboxfull");
05590                valid_extensions = 0;
05591                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05592                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05593                   free_user(vmtmp);
05594                }
05595                inprocess_count(receiver->mailbox, receiver->context, -1);
05596                break;
05597             }
05598             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
05599          } else {
05600             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
05601             /* "I am sorry, that's not a valid extension.  Please try again." */
05602             res = ast_play_and_wait(chan, "pbx-invalid");
05603             valid_extensions = 0;
05604             break;
05605          }
05606          s = strsep(&stringp, "*");
05607       }
05608       /* break from the loop of reading the extensions */
05609       if (valid_extensions)
05610          break;
05611    }
05612    /* check if we're clear to proceed */
05613    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
05614       return res;
05615    if (is_new_message == 1) {
05616       struct leave_vm_options leave_options;
05617       char mailbox[AST_MAX_EXTENSION * 2 + 2];
05618       /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
05619       if (context)
05620          snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
05621       else
05622          ast_copy_string(mailbox, username, sizeof(mailbox));
05623 
05624       /* Send VoiceMail */
05625       memset(&leave_options, 0, sizeof(leave_options));
05626       leave_options.record_gain = record_gain;
05627       cmd = leave_voicemail(chan, mailbox, &leave_options);
05628    } else {
05629       /* Forward VoiceMail */
05630       long duration = 0;
05631       struct vm_state vmstmp;
05632 #ifndef IMAP_STORAGE
05633       char msgfile[PATH_MAX];
05634 #endif
05635       int copy_msg_result = 0;
05636 
05637       memcpy(&vmstmp, vms, sizeof(vmstmp));
05638 
05639       RETRIEVE(dir, curmsg, sender);
05640 
05641       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
05642       if (!cmd) {
05643          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
05644 #ifdef IMAP_STORAGE
05645             char *myserveremail;
05646             int attach_user_voicemail;
05647             /* get destination mailbox */
05648             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
05649             if (!dstvms) {
05650                dstvms = create_vm_state_from_user(vmtmp);
05651             }
05652             if (dstvms) {
05653                init_mailstream(dstvms, 0);
05654                if (!dstvms->mailstream) {
05655                   ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
05656                } else {
05657                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
05658                   run_externnotify(vmtmp->context, vmtmp->mailbox); 
05659                }
05660             } else {
05661                ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
05662             }
05663             myserveremail = serveremail;
05664             if (!ast_strlen_zero(vmtmp->serveremail))
05665                myserveremail = vmtmp->serveremail;
05666             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
05667             /* NULL category for IMAP storage */
05668             sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, dstvms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
05669 #else
05670             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
05671 #endif
05672             saved_messages++;
05673             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05674             AST_LIST_REMOVE_CURRENT(&extensions, list);
05675             free_user(vmtmp);
05676             if (res)
05677                break;
05678          }
05679          AST_LIST_TRAVERSE_SAFE_END;
05680          if (saved_messages > 0 && !copy_msg_result) {
05681             /* give confirmation that the message was saved */
05682             /* commented out since we can't forward batches yet
05683             if (saved_messages == 1)
05684                res = ast_play_and_wait(chan, "vm-message");
05685             else
05686                res = ast_play_and_wait(chan, "vm-messages");
05687             if (!res)
05688                res = ast_play_and_wait(chan, "vm-saved"); */
05689             res = ast_play_and_wait(chan, "vm-msgsaved");
05690          }
05691 #ifndef IMAP_STORAGE
05692          else {
05693             /* with IMAP, mailbox full warning played by imap_check_limits */
05694             res = ast_play_and_wait(chan, "vm-mailboxfull");
05695          }
05696          /* Restore original message without prepended message if backup exists */
05697          make_file(msgfile, sizeof(msgfile), dir, curmsg);
05698          strcpy(textfile, msgfile);
05699          strcpy(backup, msgfile);
05700          strcpy(backup_textfile, msgfile);
05701          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05702          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05703          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05704          if (ast_fileexists(backup, NULL, NULL) > 0) {
05705             ast_filerename(backup, msgfile, NULL);
05706             rename(backup_textfile, textfile);
05707          }
05708 #endif
05709       }
05710       DISPOSE(dir, curmsg);
05711 #ifndef IMAP_STORAGE
05712       if (cmd) { /* assuming hangup, cleanup backup file */
05713          make_file(msgfile, sizeof(msgfile), dir, curmsg);
05714          strcpy(textfile, msgfile);
05715          strcpy(backup_textfile, msgfile);
05716          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05717          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05718          rename(backup_textfile, textfile);
05719       }
05720 #endif
05721    }
05722 
05723    /* If anything failed above, we still have this list to free */
05724    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05725       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05726       free_user(vmtmp);
05727    }
05728    return res ? res : cmd;
05729 }
05730 
05731 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
05732 {
05733    int res;
05734    if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0) 
05735       ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
05736    return res;
05737 }
05738 
05739 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
05740 {
05741    return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
05742 }
05743 
05744 static int play_message_category(struct ast_channel *chan, const char *category)
05745 {
05746    int res = 0;
05747 
05748    if (!ast_strlen_zero(category))
05749       res = ast_play_and_wait(chan, category);
05750 
05751    if (res) {
05752       ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
05753       res = 0;
05754    }
05755 
05756    return res;
05757 }
05758 
05759 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
05760 {
05761    int res = 0;
05762    struct vm_zone *the_zone = NULL;
05763    time_t t;
05764 
05765    if (ast_get_time_t(origtime, &t, 0, NULL)) {
05766       ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
05767       return 0;
05768    }
05769 
05770    /* Does this user have a timezone specified? */
05771    if (!ast_strlen_zero(vmu->zonetag)) {
05772       /* Find the zone in the list */
05773       struct vm_zone *z;
05774       AST_LIST_LOCK(&zones);
05775       AST_LIST_TRAVERSE(&zones, z, list) {
05776          if (!strcmp(z->name, vmu->zonetag)) {
05777             the_zone = z;
05778             break;
05779          }
05780       }
05781       AST_LIST_UNLOCK(&zones);
05782    }
05783 
05784 /* No internal variable parsing for now, so we'll comment it out for the time being */
05785 #if 0
05786    /* Set the DIFF_* variables */
05787    ast_localtime(&t, &time_now, NULL);
05788    tv_now = ast_tvnow();
05789    tnow = tv_now.tv_sec;
05790    ast_localtime(&tnow, &time_then, NULL);
05791 
05792    /* Day difference */
05793    if (time_now.tm_year == time_then.tm_year)
05794       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
05795    else
05796       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
05797    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
05798 
05799    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
05800 #endif
05801    if (the_zone) {
05802       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
05803    } else if (!strncasecmp(chan->language, "de", 2)) {    /* GERMAN syntax */
05804       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05805    } else if (!strncasecmp(chan->language, "gr", 2)) {    /* GREEK syntax */
05806       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
05807    } else if (!strncasecmp(chan->language, "he", 2)) {    /* HEBREW syntax */
05808       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'at2' kM", NULL);
05809    } else if (!strncasecmp(chan->language, "it", 2)) {    /* ITALIAN syntax */
05810       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);
05811    } else if (!strncasecmp(chan->language, "nl", 2)) {    /* DUTCH syntax */
05812       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
05813    } else if (!strncasecmp(chan->language, "no", 2)) {    /* NORWEGIAN syntax */
05814       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05815    } else if (!strncasecmp(chan->language, "pl", 2)) {    /* POLISH syntax */
05816       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
05817    } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* PORTUGUESE syntax */
05818       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);
05819    } else if (!strncasecmp(chan->language, "se", 2)) {    /* SWEDISH syntax */
05820       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
05821    } else {
05822       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
05823    }
05824 #if 0
05825    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
05826 #endif
05827    return res;
05828 }
05829 
05830 
05831 
05832 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
05833 {
05834    int res = 0;
05835    int i;
05836    char *callerid, *name;
05837    char prefile[PATH_MAX] = "";
05838    
05839 
05840    /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
05841    /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
05842    if ((cid == NULL)||(context == NULL))
05843       return res;
05844 
05845    /* Strip off caller ID number from name */
05846    if (option_debug > 2)
05847       ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
05848    ast_callerid_parse(cid, &name, &callerid);
05849    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
05850       /* Check for internal contexts and only */
05851       /* say extension when the call didn't come from an internal context in the list */
05852       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
05853          if (option_debug > 2)
05854             ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
05855          if ((strcmp(cidinternalcontexts[i], context) == 0))
05856             break;
05857       }
05858       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
05859          if (!res) {
05860             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
05861             if (!ast_strlen_zero(prefile)) {
05862             /* See if we can find a recorded name for this person instead of their extension number */
05863                if (ast_fileexists(prefile, NULL, NULL) > 0) {
05864                   if (option_verbose > 2)
05865                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
05866                   if (!callback)
05867                      res = wait_file2(chan, vms, "vm-from");
05868                   res = ast_stream_and_wait(chan, prefile, chan->language, "");
05869                } else {
05870                   if (option_verbose > 2)
05871                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
05872                   /* BB: Say "from extension" as one saying to sound smoother */
05873                   if (!callback)
05874                      res = wait_file2(chan, vms, "vm-from-extension");
05875                   res = ast_say_digit_str(chan, callerid, "", chan->language);
05876                }
05877             }
05878          }
05879       }
05880 
05881       else if (!res){
05882          if (option_debug > 2)
05883             ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
05884          /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
05885          if (!callback)
05886             res = wait_file2(chan, vms, "vm-from-phonenumber");
05887          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
05888       }
05889    } else {
05890       /* Number unknown */
05891       if (option_debug)
05892          ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
05893       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
05894       res = wait_file2(chan, vms, "vm-unknown-caller");
05895    }
05896    return res;
05897 }
05898 
05899 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
05900 {
05901    int res = 0;
05902    int durationm;
05903    int durations;
05904    /* Verify that we have a duration for the message */
05905    if (duration == NULL)
05906       return res;
05907 
05908    /* Convert from seconds to minutes */
05909    durations=atoi(duration);
05910    durationm=(durations / 60);
05911 
05912    if (option_debug > 2)
05913       ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
05914 
05915    if ((!res) && (durationm >= minduration)) {
05916       res = wait_file2(chan, vms, "vm-duration");
05917 
05918       /* POLISH syntax */
05919       if (!strncasecmp(chan->language, "pl", 2)) {
05920          div_t num = div(durationm, 10);
05921 
05922          if (durationm == 1) {
05923             res = ast_play_and_wait(chan, "digits/1z");
05924             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
05925          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
05926             if (num.rem == 2) {
05927                if (!num.quot) {
05928                   res = ast_play_and_wait(chan, "digits/2-ie");
05929                } else {
05930                   res = say_and_wait(chan, durationm - 2 , chan->language);
05931                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
05932                }
05933             } else {
05934                res = say_and_wait(chan, durationm, chan->language);
05935             }
05936             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
05937          } else {
05938             res = say_and_wait(chan, durationm, chan->language);
05939             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
05940          }
05941       /* DEFAULT syntax */
05942       } else {
05943          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
05944          res = wait_file2(chan, vms, "vm-minutes");
05945       }
05946    }
05947    return res;
05948 }
05949 
05950 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
05951 {
05952    int res = 0;
05953    char filename[256], *cid;
05954    const char *origtime, *context, *category, *duration;
05955    struct ast_config *msg_cfg;
05956 
05957    vms->starting = 0; 
05958    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
05959    adsi_message(chan, vms);
05960    
05961    if (!strncasecmp(chan->language, "he", 2)) {        /* HEBREW FORMAT */
05962       /*
05963        * The syntax in hebrew for counting the number of message is up side down
05964        * in comparison to english.
05965        */
05966       if (!vms->curmsg) {
05967          res = wait_file2(chan, vms, "vm-message");
05968          res = wait_file2(chan, vms, "vm-first");    /* "First" */
05969       } else if (vms->curmsg == vms->lastmsg) {
05970          res = wait_file2(chan, vms, "vm-message");
05971          res = wait_file2(chan, vms, "vm-last");     /* "last" */
05972       } else {
05973          res = wait_file2(chan, vms, "vm-message");  /* "message" */
05974          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05975             ast_log(LOG_DEBUG, "curmsg: %d\n", vms->curmsg);
05976             ast_log(LOG_DEBUG, "lagmsg: %d\n", vms->lastmsg);
05977             if (!res) {
05978                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
05979             }
05980          }
05981       }
05982 
05983    } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH FORMAT */
05984       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05985          int ten, one;
05986          char nextmsg[256];
05987          ten = (vms->curmsg + 1) / 10;
05988          one = (vms->curmsg + 1) % 10;
05989 
05990          if (vms->curmsg < 20) {
05991             snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
05992             res = wait_file2(chan, vms, nextmsg);
05993          } else {
05994             snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
05995             res = wait_file2(chan, vms, nextmsg);
05996             if (one > 0) {
05997                if (!res) {
05998                   snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
05999                   res = wait_file2(chan, vms, nextmsg);
06000                }
06001             }
06002          }
06003       }
06004       if (!res)
06005          res = wait_file2(chan, vms, "vm-message");         
06006 
06007    } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH FORMAT */
06008       if (!vms->curmsg)
06009          res = wait_file2(chan, vms, "vm-first");  /* "First" */
06010       else if (vms->curmsg == vms->lastmsg)
06011          res = wait_file2(chan, vms, "vm-last");      /* "last" */      
06012       res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
06013       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06014          if (!res)
06015             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06016       }
06017       /* We know that the difference between English and Swedish
06018        * is very small, however, we differ the two for standartization
06019        * purposes, and possible changes to either of these in the 
06020        * future
06021        */
06022    } else {
06023       if (!vms->curmsg)                      /* Default syntax */
06024          res = wait_file2(chan, vms, "vm-first");  /* "First" */
06025       else if (vms->curmsg == vms->lastmsg)
06026          res = wait_file2(chan, vms, "vm-last");      /* "last" */      
06027       res = wait_file2(chan, vms, "vm-message");
06028       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06029          if (!res)
06030             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06031       }
06032    }
06033 
06034    /* Retrieve info from VM attribute file */
06035    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
06036    snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
06037    RETRIEVE(vms->curdir, vms->curmsg, vmu);
06038    msg_cfg = ast_config_load(filename);
06039    if (!msg_cfg) {
06040       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06041       return 0;
06042    }
06043 
06044    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06045       ast_log(LOG_WARNING, "No origtime?!\n");
06046       DISPOSE(vms->curdir, vms->curmsg);
06047       ast_config_destroy(msg_cfg);
06048       return 0;
06049    }
06050 
06051    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06052    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06053    category = ast_variable_retrieve(msg_cfg, "message", "category");
06054 
06055    context = ast_variable_retrieve(msg_cfg, "message", "context");
06056    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06057       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06058    if (!res)
06059       res = play_message_category(chan, category);
06060    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06061       res = play_message_datetime(chan, vmu, origtime, filename);
06062    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06063       res = play_message_callerid(chan, vms, cid, context, 0);
06064    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06065       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06066    /* Allow pressing '1' to skip envelope / callerid */
06067    if (res == '1')
06068       res = 0;
06069    ast_config_destroy(msg_cfg);
06070 
06071    if (!res) {
06072       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06073       vms->heard[vms->curmsg] = 1;
06074       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06075          ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06076          res = 0;
06077       }
06078    }
06079    DISPOSE(vms->curdir, vms->curmsg);
06080    return res;
06081 }
06082 
06083 #ifndef IMAP_STORAGE
06084 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
06085 {
06086    int count_msg, last_msg;
06087 
06088    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
06089    
06090    /* Rename the member vmbox HERE so that we don't try to return before
06091     * we know what's going on.
06092     */
06093    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
06094    
06095    /* Faster to make the directory than to check if it exists. */
06096    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
06097 
06098    /* traverses directory using readdir (or select query for ODBC) */
06099    count_msg = count_messages(vmu, vms->curdir);
06100    if (count_msg < 0)
06101       return count_msg;
06102    else
06103       vms->lastmsg = count_msg - 1;
06104 
06105    if (vm_allocate_dh(vms, vmu, count_msg)) {
06106       return -1;
06107    }
06108 
06109    /*
06110    The following test is needed in case sequencing gets messed up.
06111    There appears to be more than one way to mess up sequence, so
06112    we will not try to find all of the root causes--just fix it when
06113    detected.
06114    */
06115 
06116    /* for local storage, checks directory for messages up to maxmsg limit */
06117    last_msg = last_message_index(vmu, vms->curdir);
06118 
06119    if (last_msg < -1) {
06120       return last_msg;
06121    } else if (vms->lastmsg != last_msg) {
06122       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
06123       resequence_mailbox(vmu, vms->curdir, count_msg);
06124    }
06125 
06126    return 0;
06127 }
06128 #endif
06129 
06130 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
06131 {
06132    int x = 0;
06133 #ifndef IMAP_STORAGE
06134    int last_msg_index;
06135    int res = 0, nummsg;
06136 #endif
06137 
06138    if (vms->lastmsg <= -1)
06139       goto done;
06140 
06141    vms->curmsg = -1; 
06142 #ifndef IMAP_STORAGE
06143    /* Get the deleted messages fixed */ 
06144    if (vm_lock_path(vms->curdir))
06145       return ERROR_LOCK_PATH;
06146 
06147    last_msg_index = last_message_index(vmu, vms->curdir);
06148    if (last_msg_index !=  vms->lastmsg) {
06149       ast_log(LOG_NOTICE, "%d messages arrived while mailbox was open\n", last_msg_index - vms->lastmsg);
06150    }
06151  
06152    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
06153    for (x = 0; x < last_msg_index + 1; x++) { 
06154       if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
06155          /* Save this message.  It's not in INBOX or hasn't been heard */ 
06156          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
06157          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
06158             break;
06159          vms->curmsg++; 
06160          make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
06161          if (strcmp(vms->fn, vms->fn2)) { 
06162             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
06163          } 
06164       } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
06165          /* Move to old folder before deleting */ 
06166          res = save_to_folder(vmu, vms, x, 1);
06167          if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
06168             /* If save failed do not delete the message */
06169             ast_log(LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
06170             vms->deleted[x] = 0;
06171             vms->heard[x] = 0;
06172             --x;
06173          } 
06174       } 
06175    } 
06176 
06177    /* Delete ALL remaining messages */
06178    nummsg = x - 1;
06179    for (x = vms->curmsg + 1; x <= nummsg; x++) {
06180       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
06181       if (EXISTS(vms->curdir, x, vms->fn, NULL))
06182          DELETE(vms->curdir, x, vms->fn, vmu);
06183    }
06184    ast_unlock_path(vms->curdir);
06185 #else /* defined(IMAP_STORAGE) */
06186    if (vms->deleted) {
06187       /* Since we now expunge after each delete, deleting in reverse order
06188        * ensures that no reordering occurs between each step. */
06189       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
06190          if (vms->deleted[x]) {
06191             if (option_debug > 2) {
06192                ast_log(LOG_DEBUG, "IMAP delete of %d\n", x);
06193             }
06194             DELETE(vms->curdir, x, vms->fn, vmu);
06195          }
06196       }
06197    }
06198 #endif
06199 
06200 done:
06201    if (vms->deleted)
06202       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int)); 
06203    if (vms->heard)
06204       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int)); 
06205 
06206    return 0;
06207 }
06208 
06209 /* In Greek even though we CAN use a syntax like "friends messages"
06210  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
06211  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
06212  * syntax for the above three categories which is more elegant. 
06213  */
06214 
06215 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
06216 {
06217    int cmd;
06218    char *buf;
06219 
06220    buf = alloca(strlen(mbox)+2); 
06221    strcpy(buf, mbox);
06222    strcat(buf,"s");
06223 
06224    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
06225       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
06226       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06227    } else {
06228       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06229       return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
06230    }
06231 }
06232 
06233 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
06234 {
06235    int cmd;
06236 
06237    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
06238       if (!strcasecmp(mbox, "vm-INBOX"))
06239          cmd = ast_play_and_wait(chan, "vm-new-e");
06240       else
06241          cmd = ast_play_and_wait(chan, "vm-old-e");
06242       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06243    } else {
06244       cmd = ast_play_and_wait(chan, "vm-messages");
06245       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06246    }
06247 }
06248 
06249 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
06250 {
06251    int cmd;
06252 
06253    if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
06254       cmd = ast_play_and_wait(chan, "vm-messages");
06255       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06256    } else {
06257       cmd = ast_play_and_wait(chan, mbox);
06258       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06259    }
06260 }
06261 
06262 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
06263 {
06264    int cmd;
06265 
06266    if (  !strncasecmp(chan->language, "it", 2) ||
06267         !strncasecmp(chan->language, "es", 2) ||
06268         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
06269       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
06270       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06271    } else if (!strncasecmp(chan->language, "gr", 2)) {
06272       return vm_play_folder_name_gr(chan, mbox);
06273    } else if (!strncasecmp(chan->language, "pl", 2)) {
06274       return vm_play_folder_name_pl(chan, mbox);
06275    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
06276       return vm_play_folder_name_ua(chan, mbox);
06277    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
06278       cmd = ast_play_and_wait(chan, mbox);
06279       return cmd;
06280    } else {  /* Default English */
06281       cmd = ast_play_and_wait(chan, mbox);
06282       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
06283    }
06284 }
06285 
06286 /* GREEK SYNTAX 
06287    In greek the plural for old/new is
06288    different so we need the following files
06289    We also need vm-denExeteMynhmata because 
06290    this syntax is different.
06291    
06292    -> vm-Olds.wav : "Palia"
06293    -> vm-INBOXs.wav : "Nea"
06294    -> vm-denExeteMynhmata : "den exete mynhmata"
06295 */
06296                
06297    
06298 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
06299 {
06300    int res = 0;
06301 
06302    if (vms->newmessages) {
06303       res = ast_play_and_wait(chan, "vm-youhave");
06304       if (!res) 
06305          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
06306       if (!res) {
06307          if ((vms->newmessages == 1)) {
06308             res = ast_play_and_wait(chan, "vm-INBOX");
06309             if (!res)
06310                res = ast_play_and_wait(chan, "vm-message");
06311          } else {
06312             res = ast_play_and_wait(chan, "vm-INBOXs");
06313             if (!res)
06314                res = ast_play_and_wait(chan, "vm-messages");
06315          }
06316       }
06317    } else if (vms->oldmessages){
06318       res = ast_play_and_wait(chan, "vm-youhave");
06319       if (!res)
06320          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
06321       if ((vms->oldmessages == 1)){
06322          res = ast_play_and_wait(chan, "vm-Old");
06323          if (!res)
06324             res = ast_play_and_wait(chan, "vm-message");
06325       } else {
06326          res = ast_play_and_wait(chan, "vm-Olds");
06327          if (!res)
06328             res = ast_play_and_wait(chan, "vm-messages");
06329       }
06330    } else if (!vms->oldmessages && !vms->newmessages) 
06331       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
06332    return res;
06333 }
06334 
06335 /* Default English syntax */
06336 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
06337 {
06338    int res;
06339 
06340    /* Introduce messages they have */
06341    res = ast_play_and_wait(chan, "vm-youhave");
06342    if (!res) {
06343       if (vms->newmessages) {
06344          res = say_and_wait(chan, vms->newmessages, chan->language);
06345          if (!res)
06346             res = ast_play_and_wait(chan, "vm-INBOX");
06347          if (vms->oldmessages && !res)
06348             res = ast_play_and_wait(chan, "vm-and");
06349          else if (!res) {
06350             if ((vms->newmessages == 1))
06351                res = ast_play_and_wait(chan, "vm-message");
06352             else
06353                res = ast_play_and_wait(chan, "vm-messages");
06354          }
06355             
06356       }
06357       if (!res && vms->oldmessages) {
06358          res = say_and_wait(chan, vms->oldmessages, chan->language);
06359          if (!res)
06360             res = ast_play_and_wait(chan, "vm-Old");
06361          if (!res) {
06362             if (vms->oldmessages == 1)
06363                res = ast_play_and_wait(chan, "vm-message");
06364             else
06365                res = ast_play_and_wait(chan, "vm-messages");
06366          }
06367       }
06368       if (!res) {
06369          if (!vms->oldmessages && !vms->newmessages) {
06370             res = ast_play_and_wait(chan, "vm-no");
06371             if (!res)
06372                res = ast_play_and_wait(chan, "vm-messages");
06373          }
06374       }
06375    }
06376    return res;
06377 }
06378 
06379 /* Version of vm_intro() designed to work for many languages.
06380  *
06381  * It is hoped that this function can prevent the proliferation of 
06382  * language-specific vm_intro() functions and in time replace the language-
06383  * specific functions which already exist.  An examination of the language-
06384  * specific functions revealed that they all corrected the same deficiencies
06385  * in vm_intro_en() (which was the default function). Namely:
06386  *
06387  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
06388  *     wording of the voicemail greeting hides this problem.  For example,
06389  *     vm-INBOX contains only the word "new".  This means that both of these
06390  *     sequences produce valid utterances:
06391  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
06392  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
06393  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
06394  *     in many languages) the first utterance becomes "you have 1 the new message".
06395  *  2) The function contains hardcoded rules for pluralizing the word "message".
06396  *     These rules are correct for English, but not for many other languages.
06397  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
06398  *     required in many languages.
06399  *  4) The gender of the word for "message" is not specified. This is a problem
06400  *     because in many languages the gender of the number in phrases such
06401  *     as "you have one new message" must match the gender of the word
06402  *     meaning "message".
06403  *
06404  * Fixing these problems for each new language has meant duplication of effort.
06405  * This new function solves the problems in the following general ways:
06406  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
06407  *     and vm-Old respectively for those languages where it makes sense.
06408  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
06409  *     on vm-message.
06410  *  3) Call ast_say_counted_adjective() to put the proper gender and number
06411  *     prefix on vm-new and vm-old (none for English).
06412  *  4) Pass the gender of the language's word for "message" as an agument to
06413  *     this function which is can in turn pass on to the functions which 
06414  *     say numbers and put endings on nounds and adjectives.
06415  *
06416  * All languages require these messages:
06417  *  vm-youhave    "You have..."
06418  *  vm-and     "and"
06419  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
06420  *
06421  * To use it for English, you will need these additional sound files:
06422  *  vm-new     "new"
06423  *  vm-message    "message", singular
06424  *  vm-messages      "messages", plural
06425  *
06426  * If you use it for Russian and other slavic languages, you will need these additional sound files:
06427  *
06428  *  vm-newn    "novoye" (singular, neuter)
06429  *  vm-newx    "novikh" (counting plural form, genative plural)
06430  *  vm-message    "sobsheniye" (singular form)
06431  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
06432  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
06433  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
06434  *  digits/2n     "dva" (neuter singular)
06435  */
06436 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
06437 {
06438    int res;
06439    int lastnum = 0;
06440 
06441    res = ast_play_and_wait(chan, "vm-youhave");
06442 
06443    if (!res && vms->newmessages) {
06444       lastnum = vms->newmessages;
06445 
06446       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06447          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
06448       }
06449 
06450       if (!res && vms->oldmessages) {
06451          res = ast_play_and_wait(chan, "vm-and");
06452       }
06453    }
06454 
06455    if (!res && vms->oldmessages) {
06456       lastnum = vms->oldmessages;
06457 
06458       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06459          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
06460       }
06461    }
06462 
06463    if (!res) {
06464       if (lastnum == 0) {
06465          res = ast_play_and_wait(chan, "vm-no");
06466       }
06467       if (!res) {
06468          res = ast_say_counted_noun(chan, lastnum, "vm-message");
06469       }
06470    }
06471 
06472    return res;
06473 }
06474 
06475 /* Default Hebrew syntax */
06476 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
06477 {
06478    int res=0;
06479 
06480    /* Introduce messages they have */
06481    if (!res) {
06482       if ((vms->newmessages) || (vms->oldmessages)) {
06483          res = ast_play_and_wait(chan, "vm-youhave");
06484       }
06485       /*
06486        * The word "shtei" refers to the number 2 in hebrew when performing a count
06487        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
06488        * an element, this is one of them.
06489        */
06490       if (vms->newmessages) {
06491          if (!res) {
06492             if (vms->newmessages == 1) {
06493                res = ast_play_and_wait(chan, "vm-INBOX1");
06494             } else {
06495                if (vms->newmessages == 2) {
06496                   res = ast_play_and_wait(chan, "vm-shtei");
06497                } else {
06498                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06499                }
06500                res = ast_play_and_wait(chan, "vm-INBOX");
06501             }
06502          }
06503          if (vms->oldmessages && !res) {
06504             res = ast_play_and_wait(chan, "vm-and");
06505             if (vms->oldmessages == 1) {
06506                res = ast_play_and_wait(chan, "vm-Old1");
06507             } else {
06508                if (vms->oldmessages == 2) {
06509                   res = ast_play_and_wait(chan, "vm-shtei");
06510                } else {
06511                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06512                }
06513                res = ast_play_and_wait(chan, "vm-Old");
06514             }
06515          }
06516       }
06517       if (!res && vms->oldmessages && !vms->newmessages) {
06518          if (!res) {
06519             if (vms->oldmessages == 1) {
06520                res = ast_play_and_wait(chan, "vm-Old1");
06521             } else {
06522                if (vms->oldmessages == 2) {
06523                   res = ast_play_and_wait(chan, "vm-shtei");
06524                } else {
06525                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
06526                }
06527                res = ast_play_and_wait(chan, "vm-Old");
06528             }
06529          }
06530       }
06531       if (!res) {
06532          if (!vms->oldmessages && !vms->newmessages) {
06533             if (!res) {
06534                res = ast_play_and_wait(chan, "vm-nomessages");
06535             }
06536          }
06537       }
06538    }
06539    return res;
06540 }
06541 
06542 
06543 /* ITALIAN syntax */
06544 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
06545 {
06546    /* Introduce messages they have */
06547    int res;
06548    if (!vms->oldmessages && !vms->newmessages)
06549       res = ast_play_and_wait(chan, "vm-no") ||
06550          ast_play_and_wait(chan, "vm-message");
06551    else
06552       res = ast_play_and_wait(chan, "vm-youhave");
06553    if (!res && vms->newmessages) {
06554       res = (vms->newmessages == 1) ?
06555          ast_play_and_wait(chan, "digits/un") ||
06556          ast_play_and_wait(chan, "vm-nuovo") ||
06557          ast_play_and_wait(chan, "vm-message") :
06558          /* 2 or more new messages */
06559          say_and_wait(chan, vms->newmessages, chan->language) ||
06560          ast_play_and_wait(chan, "vm-nuovi") ||
06561          ast_play_and_wait(chan, "vm-messages");
06562       if (!res && vms->oldmessages)
06563          res = ast_play_and_wait(chan, "vm-and");
06564    }
06565    if (!res && vms->oldmessages) {
06566       res = (vms->oldmessages == 1) ?
06567          ast_play_and_wait(chan, "digits/un") ||
06568          ast_play_and_wait(chan, "vm-vecchio") ||
06569          ast_play_and_wait(chan, "vm-message") :
06570          /* 2 or more old messages */
06571          say_and_wait(chan, vms->oldmessages, chan->language) ||
06572          ast_play_and_wait(chan, "vm-vecchi") ||
06573          ast_play_and_wait(chan, "vm-messages");
06574    }
06575    return res;
06576 }
06577 
06578 /* POLISH syntax */
06579 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
06580 {
06581    /* Introduce messages they have */
06582    int res;
06583    div_t num;
06584 
06585    if (!vms->oldmessages && !vms->newmessages) {
06586       res = ast_play_and_wait(chan, "vm-no");
06587       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06588       return res;
06589    } else {
06590       res = ast_play_and_wait(chan, "vm-youhave");
06591    }
06592 
06593    if (vms->newmessages) {
06594       num = div(vms->newmessages, 10);
06595       if (vms->newmessages == 1) {
06596          res = ast_play_and_wait(chan, "digits/1-a");
06597          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
06598          res = res ? res : ast_play_and_wait(chan, "vm-message");
06599       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06600          if (num.rem == 2) {
06601             if (!num.quot) {
06602                res = ast_play_and_wait(chan, "digits/2-ie");
06603             } else {
06604                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
06605                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06606             }
06607          } else {
06608             res = say_and_wait(chan, vms->newmessages, chan->language);
06609          }
06610          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
06611          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06612       } else {
06613          res = say_and_wait(chan, vms->newmessages, chan->language);
06614          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
06615          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06616       }
06617       if (!res && vms->oldmessages)
06618          res = ast_play_and_wait(chan, "vm-and");
06619    }
06620    if (!res && vms->oldmessages) {
06621       num = div(vms->oldmessages, 10);
06622       if (vms->oldmessages == 1) {
06623          res = ast_play_and_wait(chan, "digits/1-a");
06624          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
06625          res = res ? res : ast_play_and_wait(chan, "vm-message");
06626       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06627          if (num.rem == 2) {
06628             if (!num.quot) {
06629                res = ast_play_and_wait(chan, "digits/2-ie");
06630             } else {
06631                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
06632                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06633             }
06634          } else {
06635             res = say_and_wait(chan, vms->oldmessages, chan->language);
06636          }
06637          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
06638          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06639       } else {
06640          res = say_and_wait(chan, vms->oldmessages, chan->language);
06641          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
06642          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06643       }
06644    }
06645 
06646    return res;
06647 }
06648 
06649 /* SWEDISH syntax */
06650 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
06651 {
06652    /* Introduce messages they have */
06653    int res;
06654 
06655    res = ast_play_and_wait(chan, "vm-youhave");
06656    if (res)
06657       return res;
06658 
06659    if (!vms->oldmessages && !vms->newmessages) {
06660       res = ast_play_and_wait(chan, "vm-no");
06661       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06662       return res;
06663    }
06664 
06665    if (vms->newmessages) {
06666       if ((vms->newmessages == 1)) {
06667          res = ast_play_and_wait(chan, "digits/ett");
06668          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
06669          res = res ? res : ast_play_and_wait(chan, "vm-message");
06670       } else {
06671          res = say_and_wait(chan, vms->newmessages, chan->language);
06672          res = res ? res : ast_play_and_wait(chan, "vm-nya");
06673          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06674       }
06675       if (!res && vms->oldmessages)
06676          res = ast_play_and_wait(chan, "vm-and");
06677    }
06678    if (!res && vms->oldmessages) {
06679       if (vms->oldmessages == 1) {
06680          res = ast_play_and_wait(chan, "digits/ett");
06681          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
06682          res = res ? res : ast_play_and_wait(chan, "vm-message");
06683       } else {
06684          res = say_and_wait(chan, vms->oldmessages, chan->language);
06685          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
06686          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06687       }
06688    }
06689 
06690    return res;
06691 }
06692 
06693 /* NORWEGIAN syntax */
06694 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
06695 {
06696    /* Introduce messages they have */
06697    int res;
06698 
06699    res = ast_play_and_wait(chan, "vm-youhave");
06700    if (res)
06701       return res;
06702 
06703    if (!vms->oldmessages && !vms->newmessages) {
06704       res = ast_play_and_wait(chan, "vm-no");
06705       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06706       return res;
06707    }
06708 
06709    if (vms->newmessages) {
06710       if ((vms->newmessages == 1)) {
06711          res = ast_play_and_wait(chan, "digits/1");
06712          res = res ? res : ast_play_and_wait(chan, "vm-ny");
06713          res = res ? res : ast_play_and_wait(chan, "vm-message");
06714       } else {
06715          res = say_and_wait(chan, vms->newmessages, chan->language);
06716          res = res ? res : ast_play_and_wait(chan, "vm-nye");
06717          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06718       }
06719       if (!res && vms->oldmessages)
06720          res = ast_play_and_wait(chan, "vm-and");
06721    }
06722    if (!res && vms->oldmessages) {
06723       if (vms->oldmessages == 1) {
06724          res = ast_play_and_wait(chan, "digits/1");
06725          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
06726          res = res ? res : ast_play_and_wait(chan, "vm-message");
06727       } else {
06728          res = say_and_wait(chan, vms->oldmessages, chan->language);
06729          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
06730          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06731       }
06732    }
06733 
06734    return res;
06735 }
06736 
06737 /* GERMAN syntax */
06738 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
06739 {
06740    /* Introduce messages they have */
06741    int res;
06742    res = ast_play_and_wait(chan, "vm-youhave");
06743    if (!res) {
06744       if (vms->newmessages) {
06745          if ((vms->newmessages == 1))
06746             res = ast_play_and_wait(chan, "digits/1F");
06747          else
06748             res = say_and_wait(chan, vms->newmessages, chan->language);
06749          if (!res)
06750             res = ast_play_and_wait(chan, "vm-INBOX");
06751          if (vms->oldmessages && !res)
06752             res = ast_play_and_wait(chan, "vm-and");
06753          else if (!res) {
06754             if ((vms->newmessages == 1))
06755                res = ast_play_and_wait(chan, "vm-message");
06756             else
06757                res = ast_play_and_wait(chan, "vm-messages");
06758          }
06759             
06760       }
06761       if (!res && vms->oldmessages) {
06762          if (vms->oldmessages == 1)
06763             res = ast_play_and_wait(chan, "digits/1F");
06764          else
06765             res = say_and_wait(chan, vms->oldmessages, chan->language);
06766          if (!res)
06767             res = ast_play_and_wait(chan, "vm-Old");
06768          if (!res) {
06769             if (vms->oldmessages == 1)
06770                res = ast_play_and_wait(chan, "vm-message");
06771             else
06772                res = ast_play_and_wait(chan, "vm-messages");
06773          }
06774       }
06775       if (!res) {
06776          if (!vms->oldmessages && !vms->newmessages) {
06777             res = ast_play_and_wait(chan, "vm-no");
06778             if (!res)
06779                res = ast_play_and_wait(chan, "vm-messages");
06780          }
06781       }
06782    }
06783    return res;
06784 }
06785 
06786 /* SPANISH syntax */
06787 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
06788 {
06789    /* Introduce messages they have */
06790    int res;
06791    if (!vms->oldmessages && !vms->newmessages) {
06792       res = ast_play_and_wait(chan, "vm-youhaveno");
06793       if (!res)
06794          res = ast_play_and_wait(chan, "vm-messages");
06795    } else {
06796       res = ast_play_and_wait(chan, "vm-youhave");
06797    }
06798    if (!res) {
06799       if (vms->newmessages) {
06800          if (!res) {
06801             if ((vms->newmessages == 1)) {
06802                res = ast_play_and_wait(chan, "digits/1");
06803                if (!res)
06804                   res = ast_play_and_wait(chan, "vm-message");
06805                if (!res)
06806                   res = ast_play_and_wait(chan, "vm-INBOXs");
06807             } else {
06808                res = say_and_wait(chan, vms->newmessages, chan->language);
06809                if (!res)
06810                   res = ast_play_and_wait(chan, "vm-messages");
06811                if (!res)
06812                   res = ast_play_and_wait(chan, "vm-INBOX");
06813             }
06814          }
06815          if (vms->oldmessages && !res)
06816             res = ast_play_and_wait(chan, "vm-and");
06817       }
06818       if (vms->oldmessages) {
06819          if (!res) {
06820             if (vms->oldmessages == 1) {
06821                res = ast_play_and_wait(chan, "digits/1");
06822                if (!res)
06823                   res = ast_play_and_wait(chan, "vm-message");
06824                if (!res)
06825                   res = ast_play_and_wait(chan, "vm-Olds");
06826             } else {
06827                res = say_and_wait(chan, vms->oldmessages, chan->language);
06828                if (!res)
06829                   res = ast_play_and_wait(chan, "vm-messages");
06830                if (!res)
06831                   res = ast_play_and_wait(chan, "vm-Old");
06832             }
06833          }
06834       }
06835    }
06836 return res;
06837 }
06838 
06839 /* BRAZILIAN PORTUGUESE syntax */
06840 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
06841    /* Introduce messages they have */
06842    int res;
06843    if (!vms->oldmessages && !vms->newmessages) {
06844       res = ast_play_and_wait(chan, "vm-nomessages");
06845       return res;
06846    }
06847    else {
06848       res = ast_play_and_wait(chan, "vm-youhave");
06849    }
06850    if (vms->newmessages) {
06851       if (!res)
06852          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06853       if ((vms->newmessages == 1)) {
06854          if (!res)
06855             res = ast_play_and_wait(chan, "vm-message");
06856          if (!res)
06857             res = ast_play_and_wait(chan, "vm-INBOXs");
06858       }
06859       else {
06860          if (!res)
06861             res = ast_play_and_wait(chan, "vm-messages");
06862          if (!res)
06863             res = ast_play_and_wait(chan, "vm-INBOX");
06864       }
06865       if (vms->oldmessages && !res)
06866          res = ast_play_and_wait(chan, "vm-and");
06867    }
06868    if (vms->oldmessages) {
06869       if (!res)
06870          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06871       if (vms->oldmessages == 1) {
06872          if (!res)
06873             res = ast_play_and_wait(chan, "vm-message");
06874          if (!res)
06875             res = ast_play_and_wait(chan, "vm-Olds");
06876       }
06877       else {
06878          if (!res)
06879       res = ast_play_and_wait(chan, "vm-messages");
06880          if (!res)
06881             res = ast_play_and_wait(chan, "vm-Old");
06882       }
06883    }
06884    return res;
06885 }
06886 
06887 /* FRENCH syntax */
06888 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
06889 {
06890    /* Introduce messages they have */
06891    int res;
06892    res = ast_play_and_wait(chan, "vm-youhave");
06893    if (!res) {
06894       if (vms->newmessages) {
06895          res = say_and_wait(chan, vms->newmessages, chan->language);
06896          if (!res)
06897             res = ast_play_and_wait(chan, "vm-INBOX");
06898          if (vms->oldmessages && !res)
06899             res = ast_play_and_wait(chan, "vm-and");
06900          else if (!res) {
06901             if ((vms->newmessages == 1))
06902                res = ast_play_and_wait(chan, "vm-message");
06903             else
06904                res = ast_play_and_wait(chan, "vm-messages");
06905          }
06906             
06907       }
06908       if (!res && vms->oldmessages) {
06909          res = say_and_wait(chan, vms->oldmessages, chan->language);
06910          if (!res)
06911             res = ast_play_and_wait(chan, "vm-Old");
06912          if (!res) {
06913             if (vms->oldmessages == 1)
06914                res = ast_play_and_wait(chan, "vm-message");
06915             else
06916                res = ast_play_and_wait(chan, "vm-messages");
06917          }
06918       }
06919       if (!res) {
06920          if (!vms->oldmessages && !vms->newmessages) {
06921             res = ast_play_and_wait(chan, "vm-no");
06922             if (!res)
06923                res = ast_play_and_wait(chan, "vm-messages");
06924          }
06925       }
06926    }
06927    return res;
06928 }
06929 
06930 /* DUTCH syntax */
06931 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
06932 {
06933    /* Introduce messages they have */
06934    int res;
06935    res = ast_play_and_wait(chan, "vm-youhave");
06936    if (!res) {
06937       if (vms->newmessages) {
06938          res = say_and_wait(chan, vms->newmessages, chan->language);
06939          if (!res) {
06940             if (vms->newmessages == 1)
06941                res = ast_play_and_wait(chan, "vm-INBOXs");
06942             else
06943                res = ast_play_and_wait(chan, "vm-INBOX");
06944          }
06945          if (vms->oldmessages && !res)
06946             res = ast_play_and_wait(chan, "vm-and");
06947          else if (!res) {
06948             if ((vms->newmessages == 1))
06949                res = ast_play_and_wait(chan, "vm-message");
06950             else
06951                res = ast_play_and_wait(chan, "vm-messages");
06952          }
06953             
06954       }
06955       if (!res && vms->oldmessages) {
06956          res = say_and_wait(chan, vms->oldmessages, chan->language);
06957          if (!res) {
06958             if (vms->oldmessages == 1)
06959                res = ast_play_and_wait(chan, "vm-Olds");
06960             else
06961                res = ast_play_and_wait(chan, "vm-Old");
06962          }
06963          if (!res) {
06964             if (vms->oldmessages == 1)
06965                res = ast_play_and_wait(chan, "vm-message");
06966             else
06967                res = ast_play_and_wait(chan, "vm-messages");
06968          }
06969       }
06970       if (!res) {
06971          if (!vms->oldmessages && !vms->newmessages) {
06972             res = ast_play_and_wait(chan, "vm-no");
06973             if (!res)
06974                res = ast_play_and_wait(chan, "vm-messages");
06975          }
06976       }
06977    }
06978    return res;
06979 }
06980 
06981 /* PORTUGUESE syntax */
06982 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
06983 {
06984    /* Introduce messages they have */
06985    int res;
06986    res = ast_play_and_wait(chan, "vm-youhave");
06987    if (!res) {
06988       if (vms->newmessages) {
06989          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06990          if (!res) {
06991             if ((vms->newmessages == 1)) {
06992                res = ast_play_and_wait(chan, "vm-message");
06993                if (!res)
06994                   res = ast_play_and_wait(chan, "vm-INBOXs");
06995             } else {
06996                res = ast_play_and_wait(chan, "vm-messages");
06997                if (!res)
06998                   res = ast_play_and_wait(chan, "vm-INBOX");
06999             }
07000          }
07001          if (vms->oldmessages && !res)
07002             res = ast_play_and_wait(chan, "vm-and");
07003       }
07004       if (!res && vms->oldmessages) {
07005          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07006          if (!res) {
07007             if (vms->oldmessages == 1) {
07008                res = ast_play_and_wait(chan, "vm-message");
07009                if (!res)
07010                   res = ast_play_and_wait(chan, "vm-Olds");
07011             } else {
07012                res = ast_play_and_wait(chan, "vm-messages");
07013                if (!res)
07014                   res = ast_play_and_wait(chan, "vm-Old");
07015             }
07016          }
07017       }
07018       if (!res) {
07019          if (!vms->oldmessages && !vms->newmessages) {
07020             res = ast_play_and_wait(chan, "vm-no");
07021             if (!res)
07022                res = ast_play_and_wait(chan, "vm-messages");
07023          }
07024       }
07025    }
07026    return res;
07027 }
07028 
07029 
07030 /* CZECH syntax */
07031 /* in czech there must be declension of word new and message
07032  * czech        : english        : czech      : english
07033  * --------------------------------------------------------
07034  * vm-youhave   : you have 
07035  * vm-novou     : one new        : vm-zpravu  : message
07036  * vm-nove      : 2-4 new        : vm-zpravy  : messages
07037  * vm-novych    : 5-infinite new : vm-zprav   : messages
07038  * vm-starou   : one old
07039  * vm-stare     : 2-4 old 
07040  * vm-starych   : 5-infinite old
07041  * jednu        : one   - falling 4. 
07042  * vm-no        : no  ( no messages )
07043  */
07044 
07045 static int vm_intro_cs(struct ast_channel *chan,struct vm_state *vms)
07046 {
07047    int res;
07048    res = ast_play_and_wait(chan, "vm-youhave");
07049    if (!res) {
07050       if (vms->newmessages) {
07051          if (vms->newmessages == 1) {
07052             res = ast_play_and_wait(chan, "digits/jednu");
07053          } else {
07054             res = say_and_wait(chan, vms->newmessages, chan->language);
07055          }
07056          if (!res) {
07057             if ((vms->newmessages == 1))
07058                res = ast_play_and_wait(chan, "vm-novou");
07059             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07060                res = ast_play_and_wait(chan, "vm-nove");
07061             if (vms->newmessages > 4)
07062                res = ast_play_and_wait(chan, "vm-novych");
07063          }
07064          if (vms->oldmessages && !res)
07065             res = ast_play_and_wait(chan, "vm-and");
07066          else if (!res) {
07067             if ((vms->newmessages == 1))
07068                res = ast_play_and_wait(chan, "vm-zpravu");
07069             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07070                res = ast_play_and_wait(chan, "vm-zpravy");
07071             if (vms->newmessages > 4)
07072                res = ast_play_and_wait(chan, "vm-zprav");
07073          }
07074       }
07075       if (!res && vms->oldmessages) {
07076          res = say_and_wait(chan, vms->oldmessages, chan->language);
07077          if (!res) {
07078             if ((vms->oldmessages == 1))
07079                res = ast_play_and_wait(chan, "vm-starou");
07080             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07081                res = ast_play_and_wait(chan, "vm-stare");
07082             if (vms->oldmessages > 4)
07083                res = ast_play_and_wait(chan, "vm-starych");
07084          }
07085          if (!res) {
07086             if ((vms->oldmessages == 1))
07087                res = ast_play_and_wait(chan, "vm-zpravu");
07088             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07089                res = ast_play_and_wait(chan, "vm-zpravy");
07090             if (vms->oldmessages > 4)
07091                res = ast_play_and_wait(chan, "vm-zprav");
07092          }
07093       }
07094       if (!res) {
07095          if (!vms->oldmessages && !vms->newmessages) {
07096             res = ast_play_and_wait(chan, "vm-no");
07097             if (!res)
07098                res = ast_play_and_wait(chan, "vm-zpravy");
07099          }
07100       }
07101    }
07102    return res;
07103 }
07104 
07105 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07106 {
07107    char prefile[256];
07108    
07109    /* Notify the user that the temp greeting is set and give them the option to remove it */
07110    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07111    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
07112       RETRIEVE(prefile, -1, vmu);
07113       if (ast_fileexists(prefile, NULL, NULL) > 0)
07114          ast_play_and_wait(chan, "vm-tempgreetactive");
07115       DISPOSE(prefile, -1);
07116    }
07117 
07118    /* Play voicemail intro - syntax is different for different languages */
07119    if (0) {
07120       return 0;
07121    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
07122       return vm_intro_cs(chan, vms);
07123    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
07124       static int deprecation_warning = 0;
07125       if (deprecation_warning++ % 10 == 0) {
07126          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
07127       }
07128       return vm_intro_cs(chan, vms);
07129    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
07130       return vm_intro_de(chan, vms);
07131    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
07132       return vm_intro_es(chan, vms);
07133    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
07134       return vm_intro_fr(chan, vms);
07135    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
07136       return vm_intro_gr(chan, vms);
07137    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
07138       return vm_intro_he(chan, vms);
07139    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
07140       return vm_intro_it(chan, vms);
07141    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
07142       return vm_intro_nl(chan, vms);
07143    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
07144       return vm_intro_no(chan, vms);
07145    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
07146       return vm_intro_pl(chan, vms);
07147    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
07148       return vm_intro_pt_BR(chan, vms);
07149    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
07150       return vm_intro_pt(chan, vms);
07151    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
07152       return vm_intro_multilang(chan, vms, "n");
07153    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
07154       return vm_intro_se(chan, vms);
07155    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
07156       return vm_intro_multilang(chan, vms, "n");
07157    } else {                                             /* Default to ENGLISH */
07158       return vm_intro_en(chan, vms);
07159    }
07160 }
07161 
07162 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
07163 {
07164    int res = 0;
07165    /* Play instructions and wait for new command */
07166    while (!res) {
07167       if (vms->starting) {
07168          if (vms->lastmsg > -1) {
07169             res = ast_play_and_wait(chan, "vm-onefor");
07170             if (!strncasecmp(chan->language, "he", 2)) {
07171                res = ast_play_and_wait(chan, "vm-for");
07172             }
07173             if (!res)
07174                res = vm_play_folder_name(chan, vms->vmbox);
07175          }
07176          if (!res)
07177             res = ast_play_and_wait(chan, "vm-opts");
07178       } else {
07179          if (vms->curmsg)
07180             res = ast_play_and_wait(chan, "vm-prev");
07181          if (!res && !skipadvanced)
07182             res = ast_play_and_wait(chan, "vm-advopts");
07183          if (!res)
07184             res = ast_play_and_wait(chan, "vm-repeat");
07185          if (!res && (vms->curmsg != vms->lastmsg))
07186             res = ast_play_and_wait(chan, "vm-next");
07187          if (!res) {
07188             if (!vms->deleted[vms->curmsg])
07189                res = ast_play_and_wait(chan, "vm-delete");
07190             else
07191                res = ast_play_and_wait(chan, "vm-undelete");
07192             if (!res)
07193                res = ast_play_and_wait(chan, "vm-toforward");
07194             if (!res)
07195                res = ast_play_and_wait(chan, "vm-savemessage");
07196          }
07197       }
07198       if (!res)
07199          res = ast_play_and_wait(chan, "vm-helpexit");
07200       if (!res)
07201          res = ast_waitfordigit(chan, 6000);
07202       if (!res) {
07203          vms->repeats++;
07204          if (vms->repeats > 2) {
07205             res = 't';
07206          }
07207       }
07208    }
07209    return res;
07210 }
07211 
07212 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07213 {
07214    int cmd = 0;
07215    int duration = 0;
07216    int tries = 0;
07217    char newpassword[80] = "";
07218    char newpassword2[80] = "";
07219    char prefile[PATH_MAX] = "";
07220    unsigned char buf[256];
07221    int bytes=0;
07222 
07223    if (ast_adsi_available(chan)) {
07224       bytes += adsi_logo(buf + bytes);
07225       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
07226       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07227       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07228       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07229       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07230    }
07231 
07232    /* First, have the user change their password 
07233       so they won't get here again */
07234    for (;;) {
07235       newpassword[1] = '\0';
07236       newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
07237       if (cmd == '#')
07238          newpassword[0] = '\0';
07239       if (cmd < 0 || cmd == 't' || cmd == '#')
07240          return cmd;
07241       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
07242       if (cmd < 0 || cmd == 't' || cmd == '#')
07243          return cmd;
07244       newpassword2[1] = '\0';
07245       newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
07246       if (cmd == '#')
07247          newpassword2[0] = '\0';
07248       if (cmd < 0 || cmd == 't' || cmd == '#')
07249          return cmd;
07250       cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
07251       if (cmd < 0 || cmd == 't' || cmd == '#')
07252          return cmd;
07253       if (!strcmp(newpassword, newpassword2))
07254          break;
07255       ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07256       cmd = ast_play_and_wait(chan, "vm-mismatch");
07257       if (++tries == 3)
07258          return -1;
07259       if (cmd == 0) {
07260          cmd = ast_play_and_wait(chan, "vm-pls-try-again");
07261       }
07262    }
07263    if (ast_strlen_zero(ext_pass_cmd)) 
07264       vm_change_password(vmu,newpassword);
07265    else 
07266       vm_change_password_shell(vmu,newpassword);
07267    if (option_debug > 2)
07268       ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
07269    cmd = ast_play_and_wait(chan,"vm-passchanged");
07270 
07271    /* If forcename is set, have the user record their name */  
07272    if (ast_test_flag(vmu, VM_FORCENAME)) {
07273       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07274       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07275          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07276          if (cmd < 0 || cmd == 't' || cmd == '#')
07277             return cmd;
07278       }
07279    }
07280 
07281    /* If forcegreetings is set, have the user record their greetings */
07282    if (ast_test_flag(vmu, VM_FORCEGREET)) {
07283       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07284       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07285          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07286          if (cmd < 0 || cmd == 't' || cmd == '#')
07287             return cmd;
07288       }
07289 
07290       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07291       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07292          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07293          if (cmd < 0 || cmd == 't' || cmd == '#')
07294             return cmd;
07295       }
07296    }
07297 
07298    return cmd;
07299 }
07300 
07301 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07302 {
07303    int cmd = 0;
07304    int retries = 0;
07305    int duration = 0;
07306    char newpassword[80] = "";
07307    char newpassword2[80] = "";
07308    char prefile[PATH_MAX] = "";
07309    unsigned char buf[256];
07310    int bytes=0;
07311 
07312    if (ast_adsi_available(chan))
07313    {
07314       bytes += adsi_logo(buf + bytes);
07315       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
07316       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07317       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07318       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07319       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07320    }
07321    while ((cmd >= 0) && (cmd != 't')) {
07322       if (cmd)
07323          retries = 0;
07324       switch (cmd) {
07325       case '1':
07326          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07327          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07328          break;
07329       case '2': 
07330          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07331          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07332          break;
07333       case '3': 
07334          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07335          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07336          break;
07337       case '4': 
07338          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
07339          break;
07340       case '5':
07341          if (vmu->password[0] == '-') {
07342             cmd = ast_play_and_wait(chan, "vm-no");
07343             break;
07344          }
07345          newpassword[1] = '\0';
07346          newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
07347          if (cmd == '#')
07348             newpassword[0] = '\0';
07349          else {
07350             if (cmd < 0)
07351                break;
07352             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
07353                break;
07354             }
07355          }
07356          newpassword2[1] = '\0';
07357          newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
07358          if (cmd == '#')
07359             newpassword2[0] = '\0';
07360          else {
07361             if (cmd < 0)
07362                break;
07363 
07364             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
07365                break;
07366             }
07367          }
07368          if (strcmp(newpassword, newpassword2)) {
07369             ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07370             cmd = ast_play_and_wait(chan, "vm-mismatch");
07371             if (!cmd) {
07372                cmd = ast_play_and_wait(chan, "vm-pls-try-again");
07373             }
07374             break;
07375          }
07376          if (ast_strlen_zero(ext_pass_cmd)) 
07377             vm_change_password(vmu,newpassword);
07378          else 
07379             vm_change_password_shell(vmu,newpassword);
07380          if (option_debug > 2)
07381             ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
07382          cmd = ast_play_and_wait(chan,"vm-passchanged");
07383          break;
07384       case '*': 
07385          cmd = 't';
07386          break;
07387       default: 
07388          cmd = 0;
07389          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07390          RETRIEVE(prefile, -1, vmu);
07391          if (ast_fileexists(prefile, NULL, NULL))
07392             cmd = ast_play_and_wait(chan, "vm-tmpexists");
07393          DISPOSE(prefile, -1);
07394          if (!cmd)
07395             cmd = ast_play_and_wait(chan, "vm-options");
07396          if (!cmd)
07397             cmd = ast_waitfordigit(chan,6000);
07398          if (!cmd)
07399             retries++;
07400          if (retries > 3)
07401             cmd = 't';
07402       }
07403    }
07404    if (cmd == 't')
07405       cmd = 0;
07406    return cmd;
07407 }
07408 
07409 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07410 {
07411    int res;
07412    int cmd = 0;
07413    int retries = 0;
07414    int duration = 0;
07415    char prefile[PATH_MAX] = "";
07416    unsigned char buf[256];
07417    char dest[PATH_MAX];
07418    int bytes = 0;
07419 
07420    if (ast_adsi_available(chan)) {
07421       bytes += adsi_logo(buf + bytes);
07422       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
07423       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07424       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07425       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07426       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07427    }
07428 
07429    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07430    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
07431       ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
07432       return -1;
07433    }
07434    while ((cmd >= 0) && (cmd != 't')) {
07435       if (cmd)
07436          retries = 0;
07437       RETRIEVE(prefile, -1, vmu);
07438       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
07439          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07440          cmd = 't';  
07441       } else {
07442          switch (cmd) {
07443          case '1':
07444             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07445             break;
07446          case '2':
07447             DELETE(prefile, -1, prefile, vmu);
07448             ast_play_and_wait(chan, "vm-tempremoved");
07449             cmd = 't';  
07450             break;
07451          case '*': 
07452             cmd = 't';
07453             break;
07454          default:
07455             cmd = ast_play_and_wait(chan,
07456                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
07457                   "vm-tempgreeting2" : "vm-tempgreeting");
07458             if (!cmd)
07459                cmd = ast_waitfordigit(chan,6000);
07460             if (!cmd)
07461                retries++;
07462             if (retries > 3)
07463                cmd = 't';
07464          }
07465       }
07466       DISPOSE(prefile, -1);
07467    }
07468    if (cmd == 't')
07469       cmd = 0;
07470    return cmd;
07471 }
07472 
07473 /* Default English syntax */
07474 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07475 {
07476    int cmd=0;
07477 
07478    if (vms->lastmsg > -1) {
07479       cmd = play_message(chan, vmu, vms);
07480    } else {
07481       cmd = ast_play_and_wait(chan, "vm-youhave");
07482       if (!cmd) 
07483          cmd = ast_play_and_wait(chan, "vm-no");
07484       if (!cmd) {
07485          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07486          cmd = ast_play_and_wait(chan, vms->fn);
07487       }
07488       if (!cmd)
07489          cmd = ast_play_and_wait(chan, "vm-messages");
07490    }
07491    return cmd;
07492 }
07493 
07494 /* Hebrew Syntax */
07495 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07496 {
07497    int cmd = 0;
07498 
07499    if (vms->lastmsg > -1) {
07500       cmd = play_message(chan, vmu, vms);
07501    } else {
07502       if (!strcasecmp(vms->fn, "INBOX")) {
07503          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
07504       } else {
07505          cmd = ast_play_and_wait(chan, "vm-nomessages");
07506       }
07507    }
07508    return cmd;
07509 }
07510 
07511 /* Common LATIN languages syntax */
07512 static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07513 {
07514    int cmd=0;
07515 
07516    if (vms->lastmsg > -1) {
07517       cmd = play_message(chan, vmu, vms);
07518    } else {
07519       cmd = ast_play_and_wait(chan, "vm-youhaveno");
07520       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
07521          if (!cmd) {
07522             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
07523             cmd = ast_play_and_wait(chan, vms->fn);
07524          }
07525          if (!cmd)
07526             cmd = ast_play_and_wait(chan, "vm-messages");
07527       } else {
07528          if (!cmd)
07529             cmd = ast_play_and_wait(chan, "vm-messages");
07530          if (!cmd) {
07531             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07532             cmd = ast_play_and_wait(chan, vms->fn);
07533          }
07534       }
07535    }
07536    return cmd;
07537 }
07538 
07539 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07540 {
07541    if (!strncasecmp(chan->language, "es", 2) ||
07542          !strncasecmp(chan->language, "it", 2) ||
07543          !strncasecmp(chan->language, "pt", 2) ||
07544          !strncasecmp(chan->language, "gr", 2)) { /* SPANISH, ITALIAN, PORTUGUESE or GREEK */
07545       return vm_browse_messages_latin(chan, vms, vmu);
07546    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
07547       return vm_browse_messages_he(chan, vms, vmu);
07548    } else {                                             /* Default to English syntax */
07549       return vm_browse_messages_en(chan, vms, vmu);
07550    }
07551 }
07552 
07553 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
07554          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
07555          int skipuser, int maxlogins, int silent)
07556 {
07557    int useadsi=0, valid=0, logretries=0;
07558    char password[AST_MAX_EXTENSION]="", *passptr;
07559    struct ast_vm_user vmus, *vmu = NULL;
07560 
07561    /* If ADSI is supported, setup login screen */
07562    adsi_begin(chan, &useadsi);
07563    if (!skipuser && useadsi)
07564       adsi_login(chan);
07565    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
07566       ast_log(LOG_WARNING, "Couldn't stream login file\n");
07567       return -1;
07568    }
07569    
07570    /* Authenticate them and get their mailbox/password */
07571    
07572    while (!valid && (logretries < maxlogins)) {
07573       /* Prompt for, and read in the username */
07574       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
07575          ast_log(LOG_WARNING, "Couldn't read username\n");
07576          return -1;
07577       }
07578       if (ast_strlen_zero(mailbox)) {
07579          if (chan->cid.cid_num) {
07580             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
07581          } else {
07582             if (option_verbose > 2)
07583                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");  
07584             return -1;
07585          }
07586       }
07587       if (useadsi)
07588          adsi_password(chan);
07589 
07590       if (!ast_strlen_zero(prefix)) {
07591          char fullusername[80] = "";
07592          ast_copy_string(fullusername, prefix, sizeof(fullusername));
07593          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
07594          ast_copy_string(mailbox, fullusername, mailbox_size);
07595       }
07596 
07597       if (option_debug)
07598          ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
07599       vmu = find_user(&vmus, context, mailbox);
07600       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
07601          /* saved password is blank, so don't bother asking */
07602          password[0] = '\0';
07603       } else {
07604          if (ast_streamfile(chan, "vm-password", chan->language)) {
07605             ast_log(LOG_WARNING, "Unable to stream password file\n");
07606             return -1;
07607          }
07608          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
07609             ast_log(LOG_WARNING, "Unable to read password\n");
07610             return -1;
07611          }
07612       }
07613 
07614       if (vmu) {
07615          passptr = vmu->password;
07616          if (passptr[0] == '-') passptr++;
07617       }
07618       if (vmu && !strcmp(passptr, password))
07619          valid++;
07620       else {
07621          if (option_verbose > 2)
07622             ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
07623          if (!ast_strlen_zero(prefix))
07624             mailbox[0] = '\0';
07625       }
07626       logretries++;
07627       if (!valid) {
07628          if (skipuser || logretries >= maxlogins) {
07629             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
07630                ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
07631                return -1;
07632             }
07633          } else {
07634             if (useadsi)
07635                adsi_login(chan);
07636             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
07637                ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
07638                return -1;
07639             }
07640          }
07641          if (ast_waitstream(chan, "")) /* Channel is hung up */
07642             return -1;
07643       }
07644    }
07645    if (!valid && (logretries >= maxlogins)) {
07646       ast_stopstream(chan);
07647       ast_play_and_wait(chan, "vm-goodbye");
07648       return -1;
07649    }
07650    if (vmu && !skipuser) {
07651       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
07652    }
07653    return 0;
07654 }
07655 
07656 static int vm_execmain(struct ast_channel *chan, void *data)
07657 {
07658    /* XXX This is, admittedly, some pretty horrendus code.  For some
07659       reason it just seemed a lot easier to do with GOTO's.  I feel
07660       like I'm back in my GWBASIC days. XXX */
07661    int res=-1;
07662    int cmd=0;
07663    int valid = 0;
07664    struct ast_module_user *u;
07665    char prefixstr[80] ="";
07666    char ext_context[256]="";
07667    int box;
07668    int useadsi = 0;
07669    int skipuser = 0;
07670    struct vm_state vms;
07671    struct ast_vm_user *vmu = NULL, vmus;
07672    char *context=NULL;
07673    int silentexit = 0;
07674    struct ast_flags flags = { 0 };
07675    signed char record_gain = 0;
07676    int play_auto = 0;
07677    int play_folder = 0;
07678 #ifdef IMAP_STORAGE
07679    int deleted = 0;
07680 #endif
07681    u = ast_module_user_add(chan);
07682 
07683    /* Add the vm_state to the active list and keep it active */
07684    memset(&vms, 0, sizeof(vms));
07685    vms.lastmsg = -1;
07686 
07687    memset(&vmus, 0, sizeof(vmus));
07688 
07689    if (chan->_state != AST_STATE_UP) {
07690       if (option_debug)
07691          ast_log(LOG_DEBUG, "Before ast_answer\n");
07692       ast_answer(chan);
07693    }
07694 
07695    if (!ast_strlen_zero(data)) {
07696       char *opts[OPT_ARG_ARRAY_SIZE];
07697       char *parse;
07698       AST_DECLARE_APP_ARGS(args,
07699          AST_APP_ARG(argv0);
07700          AST_APP_ARG(argv1);
07701       );
07702 
07703       parse = ast_strdupa(data);
07704 
07705       AST_STANDARD_APP_ARGS(args, parse);
07706 
07707       if (args.argc == 2) {
07708          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
07709             ast_module_user_remove(u);
07710             return -1;
07711          }
07712          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
07713             int gain;
07714             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
07715                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
07716                   ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
07717                   ast_module_user_remove(u);
07718                   return -1;
07719                } else {
07720                   record_gain = (signed char) gain;
07721                }
07722             } else {
07723                ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
07724             }
07725          }
07726          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
07727             play_auto = 1;
07728             if (opts[OPT_ARG_PLAYFOLDER]) {
07729                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
07730                   ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
07731                }
07732             } else {
07733                ast_log(LOG_WARNING, "Invalid folder set with option a\n");
07734             }  
07735             if ( play_folder > 9 || play_folder < 0) {
07736                ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
07737                play_folder = 0;
07738             }
07739          }
07740       } else {
07741          /* old style options parsing */
07742          while (*(args.argv0)) {
07743             if (*(args.argv0) == 's')
07744                ast_set_flag(&flags, OPT_SILENT);
07745             else if (*(args.argv0) == 'p')
07746                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
07747             else 
07748                break;
07749             (args.argv0)++;
07750          }
07751 
07752       }
07753 
07754       valid = ast_test_flag(&flags, OPT_SILENT);
07755 
07756       if ((context = strchr(args.argv0, '@')))
07757          *context++ = '\0';
07758 
07759       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
07760          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
07761       else
07762          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
07763 
07764       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
07765          skipuser++;
07766       else
07767          valid = 0;
07768    }
07769 
07770    if (!valid)
07771       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
07772 
07773    if (option_debug)
07774       ast_log(LOG_DEBUG, "After vm_authenticate\n");
07775    if (!res) {
07776       valid = 1;
07777       if (!skipuser)
07778          vmu = &vmus;
07779    } else {
07780       res = 0;
07781    }
07782 
07783    /* If ADSI is supported, setup login screen */
07784    adsi_begin(chan, &useadsi);
07785 
07786    if (!valid) {
07787       goto out;
07788    }
07789 
07790 #ifdef IMAP_STORAGE
07791    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
07792    pthread_setspecific(ts_vmstate.key, &vms);
07793 
07794    vms.interactive = 1;
07795    vms.updated = 1;
07796    if (vmu)
07797       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
07798    vmstate_insert(&vms);
07799    init_vm_state(&vms);
07800 #endif
07801 
07802    /* Set language from config to override channel language */
07803    if (!ast_strlen_zero(vmu->language))
07804       ast_string_field_set(chan, language, vmu->language);
07805    create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
07806    /* Retrieve old and new message counts */
07807    if (option_debug)
07808       ast_log(LOG_DEBUG, "Before open_mailbox\n");
07809    res = open_mailbox(&vms, vmu, 1);
07810    if (res < 0)
07811       goto out;
07812    vms.oldmessages = vms.lastmsg + 1;
07813    if (option_debug > 2)
07814       ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
07815    /* Start in INBOX */
07816    res = open_mailbox(&vms, vmu, 0);
07817    if (res < 0)
07818       goto out;
07819    vms.newmessages = vms.lastmsg + 1;
07820    if (option_debug > 2)
07821       ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
07822       
07823    /* Select proper mailbox FIRST!! */
07824    if (play_auto) {
07825       res = open_mailbox(&vms, vmu, play_folder);
07826       if (res < 0)
07827          goto out;
07828 
07829       /* If there are no new messages, inform the user and hangup */
07830       if (vms.lastmsg == -1) {
07831          cmd = vm_browse_messages(chan, &vms, vmu);
07832          res = 0;
07833          goto out;
07834       }
07835    } else {
07836       if (!vms.newmessages && vms.oldmessages) {
07837          /* If we only have old messages start here */
07838          res = open_mailbox(&vms, vmu, 1);
07839          play_folder = 1;
07840          if (res < 0)
07841             goto out;
07842       }
07843    }
07844 
07845    if (useadsi)
07846       adsi_status(chan, &vms);
07847    res = 0;
07848 
07849    /* Check to see if this is a new user */
07850    if (!strcasecmp(vmu->mailbox, vmu->password) && 
07851       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
07852       if (ast_play_and_wait(chan, "vm-newuser") == -1)
07853          ast_log(LOG_WARNING, "Couldn't stream new user file\n");
07854       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
07855       if ((cmd == 't') || (cmd == '#')) {
07856          /* Timeout */
07857          res = 0;
07858          goto out;
07859       } else if (cmd < 0) {
07860          /* Hangup */
07861          res = -1;
07862          goto out;
07863       }
07864    }
07865 #ifdef IMAP_STORAGE
07866       if (option_debug > 2)
07867          ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
07868       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
07869          if (option_debug)
07870             ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
07871          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
07872       }
07873       if (option_debug > 2)
07874          ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
07875       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
07876          ast_log(LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
07877          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
07878       }
07879 #endif
07880    if (play_auto) {
07881       cmd = '1';
07882    } else {
07883       cmd = vm_intro(chan, vmu, &vms);
07884    }
07885 
07886    vms.repeats = 0;
07887    vms.starting = 1;
07888    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
07889       /* Run main menu */
07890       switch (cmd) {
07891       case '1':
07892          vms.curmsg = 0;
07893          /* Fall through */
07894       case '5':
07895          cmd = vm_browse_messages(chan, &vms, vmu);
07896          break;
07897       case '2': /* Change folders */
07898          if (useadsi)
07899             adsi_folders(chan, 0, "Change to folder...");
07900          cmd = get_folder2(chan, "vm-changeto", 0);
07901          if (cmd == '#') {
07902             cmd = 0;
07903          } else if (cmd > 0) {
07904             cmd = cmd - '0';
07905             res = close_mailbox(&vms, vmu);
07906             if (res == ERROR_LOCK_PATH)
07907                goto out;
07908             res = open_mailbox(&vms, vmu, cmd);
07909             if (res < 0)
07910                goto out;
07911             play_folder = cmd;
07912             cmd = 0;
07913          }
07914          if (useadsi)
07915             adsi_status2(chan, &vms);
07916             
07917          if (!cmd)
07918             cmd = vm_play_folder_name(chan, vms.vmbox);
07919 
07920          vms.starting = 1;
07921          break;
07922       case '3': /* Advanced options */
07923          cmd = 0;
07924          vms.repeats = 0;
07925          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
07926             switch (cmd) {
07927             case '1': /* Reply */
07928                if (vms.lastmsg > -1 && !vms.starting) {
07929                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
07930                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
07931                      res = cmd;
07932                      goto out;
07933                   }
07934                } else
07935                   cmd = ast_play_and_wait(chan, "vm-sorry");
07936                cmd = 't';
07937                break;
07938             case '2': /* Callback */
07939                if (option_verbose > 2 && !vms.starting)
07940                   ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
07941                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
07942                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
07943                   if (cmd == 9) {
07944                      silentexit = 1;
07945                      goto out;
07946                   } else if (cmd == ERROR_LOCK_PATH) {
07947                      res = cmd;
07948                      goto out;
07949                   }
07950                }
07951                else 
07952                   cmd = ast_play_and_wait(chan, "vm-sorry");
07953                cmd = 't';
07954                break;
07955             case '3': /* Envelope */
07956                if (vms.lastmsg > -1 && !vms.starting) {
07957                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
07958                   if (cmd == ERROR_LOCK_PATH) {
07959                      res = cmd;
07960                      goto out;
07961                   }
07962                } else
07963                   cmd = ast_play_and_wait(chan, "vm-sorry");
07964                cmd = 't';
07965                break;
07966             case '4': /* Dialout */
07967                if (!ast_strlen_zero(vmu->dialout)) {
07968                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
07969                   if (cmd == 9) {
07970                      silentexit = 1;
07971                      goto out;
07972                   }
07973                }
07974                else 
07975                   cmd = ast_play_and_wait(chan, "vm-sorry");
07976                cmd = 't';
07977                break;
07978 
07979             case '5': /* Leave VoiceMail */
07980                if (ast_test_flag(vmu, VM_SVMAIL)) {
07981                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
07982                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
07983                      res = cmd;
07984                      goto out;
07985                   }
07986                } else
07987                   cmd = ast_play_and_wait(chan,"vm-sorry");
07988                cmd='t';
07989                break;
07990                
07991             case '*': /* Return to main menu */
07992                cmd = 't';
07993                break;
07994 
07995             default:
07996                cmd = 0;
07997                if (!vms.starting) {
07998                   cmd = ast_play_and_wait(chan, "vm-toreply");
07999                }
08000                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
08001                   cmd = ast_play_and_wait(chan, "vm-tocallback");
08002                }
08003                if (!cmd && !vms.starting) {
08004                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
08005                }
08006                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
08007                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
08008                }
08009                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
08010                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
08011                if (!cmd)
08012                   cmd = ast_play_and_wait(chan, "vm-starmain");
08013                if (!cmd)
08014                   cmd = ast_waitfordigit(chan,6000);
08015                if (!cmd)
08016                   vms.repeats++;
08017                if (vms.repeats > 3)
08018                   cmd = 't';
08019             }
08020          }
08021          if (cmd == 't') {
08022             cmd = 0;
08023             vms.repeats = 0;
08024          }
08025          break;
08026       case '4':
08027          if (vms.curmsg > 0) {
08028             vms.curmsg--;
08029             cmd = play_message(chan, vmu, &vms);
08030          } else {
08031             cmd = ast_play_and_wait(chan, "vm-nomore");
08032          }
08033          break;
08034       case '6':
08035          if (vms.curmsg < vms.lastmsg) {
08036             vms.curmsg++;
08037             cmd = play_message(chan, vmu, &vms);
08038          } else {
08039             cmd = ast_play_and_wait(chan, "vm-nomore");
08040          }
08041          break;
08042       case '7':
08043          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
08044             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
08045             if (useadsi)
08046                adsi_delete(chan, &vms);
08047             if (vms.deleted[vms.curmsg]) {
08048                if (play_folder == 0)
08049                   vms.newmessages--;
08050                else if (play_folder == 1)
08051                   vms.oldmessages--;
08052                cmd = ast_play_and_wait(chan, "vm-deleted");
08053             }
08054             else {
08055                if (play_folder == 0)
08056                   vms.newmessages++;
08057                else if (play_folder == 1)
08058                   vms.oldmessages++;
08059                cmd = ast_play_and_wait(chan, "vm-undeleted");
08060             }
08061             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
08062                if (vms.curmsg < vms.lastmsg) {
08063                   vms.curmsg++;
08064                   cmd = play_message(chan, vmu, &vms);
08065                } else {
08066                   cmd = ast_play_and_wait(chan, "vm-nomore");
08067                }
08068             }
08069          } else /* Delete not valid if we haven't selected a message */
08070             cmd = 0;
08071 #ifdef IMAP_STORAGE
08072          deleted = 1;
08073 #endif
08074          break;
08075    
08076       case '8':
08077          if (vms.lastmsg > -1) {
08078             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
08079             if (cmd == ERROR_LOCK_PATH) {
08080                res = cmd;
08081                goto out;
08082             }
08083          } else
08084             cmd = ast_play_and_wait(chan, "vm-nomore");
08085          break;
08086       case '9':
08087          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
08088             /* No message selected */
08089             cmd = 0;
08090             break;
08091          }
08092          if (useadsi)
08093             adsi_folders(chan, 1, "Save to folder...");
08094          cmd = get_folder2(chan, "vm-savefolder", 1);
08095          box = 0; /* Shut up compiler */
08096          if (cmd == '#') {
08097             cmd = 0;
08098             break;
08099          } else if (cmd > 0) {
08100             box = cmd = cmd - '0';
08101             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
08102             if (cmd == ERROR_LOCK_PATH) {
08103                res = cmd;
08104                goto out;
08105 #ifndef IMAP_STORAGE
08106             } else if (!cmd) {
08107                vms.deleted[vms.curmsg] = 1;
08108 #endif
08109             } else {
08110                vms.deleted[vms.curmsg] = 0;
08111                vms.heard[vms.curmsg] = 0;
08112             }
08113          }
08114          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
08115          if (useadsi)
08116             adsi_message(chan, &vms);
08117          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
08118          if (!cmd) {
08119             cmd = ast_play_and_wait(chan, "vm-message");
08120             if (!cmd)
08121                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
08122             if (!cmd)
08123                cmd = ast_play_and_wait(chan, "vm-savedto");
08124             if (!cmd)
08125                cmd = vm_play_folder_name(chan, vms.fn);
08126          } else {
08127             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
08128          }
08129          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
08130             if (vms.curmsg < vms.lastmsg) {
08131                vms.curmsg++;
08132                cmd = play_message(chan, vmu, &vms);
08133             } else {
08134                cmd = ast_play_and_wait(chan, "vm-nomore");
08135             }
08136          }
08137          break;
08138       case '*':
08139          if (!vms.starting) {
08140             cmd = ast_play_and_wait(chan, "vm-onefor");
08141             if (!strncasecmp(chan->language, "he", 2)) {
08142                cmd = ast_play_and_wait(chan, "vm-for");
08143             }
08144             if (!cmd)
08145                cmd = vm_play_folder_name(chan, vms.vmbox);
08146             if (!cmd)
08147                cmd = ast_play_and_wait(chan, "vm-opts");
08148             if (!cmd)
08149                cmd = vm_instructions(chan, &vms, 1);
08150          } else
08151             cmd = 0;
08152          break;
08153       case '0':
08154          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
08155          if (useadsi)
08156             adsi_status(chan, &vms);
08157          break;
08158       default: /* Nothing */
08159          cmd = vm_instructions(chan, &vms, 0);
08160          break;
08161       }
08162    }
08163    if ((cmd == 't') || (cmd == '#')) {
08164       /* Timeout */
08165       res = 0;
08166    } else {
08167       /* Hangup */
08168       res = -1;
08169    }
08170 
08171 out:
08172    if (res > -1) {
08173       ast_stopstream(chan);
08174       adsi_goodbye(chan);
08175       if (valid && res != OPERATOR_EXIT) {
08176          if (silentexit)
08177             res = ast_play_and_wait(chan, "vm-dialout");
08178          else 
08179             res = ast_play_and_wait(chan, "vm-goodbye");
08180       }
08181       if ((valid && res > 0) || res == OPERATOR_EXIT) {
08182          res = 0;
08183       }
08184       if (useadsi)
08185          ast_adsi_unload_session(chan);
08186    }
08187    if (vmu)
08188       close_mailbox(&vms, vmu);
08189    if (valid) {
08190       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
08191       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
08192       run_externnotify(vmu->context, vmu->mailbox);
08193    }
08194 #ifdef IMAP_STORAGE
08195    /* expunge message - use UID Expunge if supported on IMAP server*/
08196    if (option_debug > 2)
08197       ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
08198    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
08199       ast_mutex_lock(&vms.lock);
08200 #ifdef HAVE_IMAP_TK2006
08201       if (LEVELUIDPLUS (vms.mailstream)) {
08202          mail_expunge_full(vms.mailstream,NIL,EX_UID);
08203       } else 
08204 #endif
08205          mail_expunge(vms.mailstream);
08206       ast_mutex_unlock(&vms.lock);
08207    }
08208    /*  before we delete the state, we should copy pertinent info
08209     *  back to the persistent model */
08210    if (vmu) {
08211       vmstate_delete(&vms);
08212    }
08213 #endif
08214    if (vmu)
08215       free_user(vmu);
08216    if (vms.deleted)
08217       free(vms.deleted);
08218    if (vms.heard)
08219       free(vms.heard);
08220 
08221 #ifdef IMAP_STORAGE
08222    pthread_setspecific(ts_vmstate.key, NULL);
08223 #endif
08224    ast_module_user_remove(u);
08225    return res;
08226 }
08227 
08228 static int vm_exec(struct ast_channel *chan, void *data)
08229 {
08230    int res = 0;
08231    struct ast_module_user *u;
08232    char *tmp;
08233    struct leave_vm_options leave_options;
08234    struct ast_flags flags = { 0 };
08235    static int deprecate_warning = 0;
08236    char *opts[OPT_ARG_ARRAY_SIZE];
08237    AST_DECLARE_APP_ARGS(args,
08238       AST_APP_ARG(argv0);
08239       AST_APP_ARG(argv1);
08240    );
08241 
08242    u = ast_module_user_add(chan);
08243    
08244    memset(&leave_options, 0, sizeof(leave_options));
08245 
08246    if (chan->_state != AST_STATE_UP)
08247       ast_answer(chan);
08248 
08249    if (!ast_strlen_zero(data)) {
08250       tmp = ast_strdupa(data);
08251       AST_STANDARD_APP_ARGS(args, tmp);
08252       if (args.argc == 2) {
08253          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
08254             ast_module_user_remove(u);
08255             return -1;
08256          }
08257          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
08258          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
08259             int gain;
08260 
08261             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
08262                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
08263                ast_module_user_remove(u);
08264                return -1;
08265             } else {
08266                leave_options.record_gain = (signed char) gain;
08267             }
08268          }
08269       } else {
08270          /* old style options parsing */
08271          int old = 0;
08272          char *orig_argv0 = args.argv0;
08273          while (*(args.argv0)) {
08274             if (*(args.argv0) == 's') {
08275                old = 1;
08276                ast_set_flag(&leave_options, OPT_SILENT);
08277             } else if (*(args.argv0) == 'b') {
08278                old = 1;
08279                ast_set_flag(&leave_options, OPT_BUSY_GREETING);
08280             } else if (*(args.argv0) == 'u') {
08281                old = 1;
08282                ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
08283             } else if (*(args.argv0) == 'j') {
08284                old = 1;
08285                ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
08286             } else
08287                break;
08288             (args.argv0)++;
08289          }
08290          if (!deprecate_warning && old) {
08291             deprecate_warning = 1;
08292             ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
08293             ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
08294          }
08295       }
08296    } else {
08297       char tmp[256];
08298       res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
08299       if (res < 0) {
08300          ast_module_user_remove(u);
08301          return res;
08302       }
08303       if (ast_strlen_zero(tmp)) {
08304          ast_module_user_remove(u);
08305          return 0;
08306       }
08307       args.argv0 = ast_strdupa(tmp);
08308    }
08309 
08310    res = leave_voicemail(chan, args.argv0, &leave_options);
08311    if (res == 't') {
08312       ast_play_and_wait(chan, "vm-goodbye");
08313       res = 0;
08314    }
08315 
08316    if (res == OPERATOR_EXIT) {
08317       res = 0;
08318    }
08319 
08320    if (res == ERROR_LOCK_PATH) {
08321       ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
08322       /*Send the call to n+101 priority, where n is the current priority*/
08323       if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
08324          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
08325             ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
08326       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
08327       res = 0;
08328    }
08329    
08330    ast_module_user_remove(u);
08331 
08332    return res;
08333 }
08334 
08335 static struct ast_vm_user *find_or_create(char *context, char *mbox)
08336 {
08337    struct ast_vm_user *vmu;
08338    AST_LIST_TRAVERSE(&users, vmu, list) {
08339       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox)) {
08340          if (strcasecmp(vmu->context, context)) {
08341             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
08342                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
08343                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
08344                   \n\tamend your voicemail.conf file to avoid this situation.\n", mbox);
08345          }
08346          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", mbox);
08347          return NULL;
08348       }
08349       if (!strcasecmp(context, vmu->context) && !strcasecmp(mbox, vmu->mailbox)) {
08350          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", mbox, context);
08351          return NULL;
08352       }
08353    }
08354    
08355    if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
08356       ast_copy_string(vmu->context, context, sizeof(vmu->context));
08357       ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
08358       AST_LIST_INSERT_TAIL(&users, vmu, list);
08359    }
08360    return vmu;
08361 }
08362 
08363 static int append_mailbox(char *context, char *mbox, char *data)
08364 {
08365    /* Assumes lock is already held */
08366    char *tmp;
08367    char *stringp;
08368    char *s;
08369    struct ast_vm_user *vmu;
08370 
08371    tmp = ast_strdupa(data);
08372 
08373    if ((vmu = find_or_create(context, mbox))) {
08374       populate_defaults(vmu);
08375 
08376       stringp = tmp;
08377       if ((s = strsep(&stringp, ","))) 
08378          ast_copy_string(vmu->password, s, sizeof(vmu->password));
08379       if (stringp && (s = strsep(&stringp, ","))) 
08380          ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
08381       if (stringp && (s = strsep(&stringp, ","))) 
08382          ast_copy_string(vmu->email, s, sizeof(vmu->email));
08383       if (stringp && (s = strsep(&stringp, ","))) 
08384          ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
08385       if (stringp && (s = strsep(&stringp, ","))) 
08386          apply_options(vmu, s);
08387    }
08388    return 0;
08389 }
08390 
08391 static int vm_box_exists(struct ast_channel *chan, void *data) 
08392 {
08393    struct ast_module_user *u;
08394    struct ast_vm_user svm;
08395    char *context, *box;
08396    int priority_jump = 0;
08397    AST_DECLARE_APP_ARGS(args,
08398       AST_APP_ARG(mbox);
08399       AST_APP_ARG(options);
08400    );
08401 
08402    if (ast_strlen_zero(data)) {
08403       ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
08404       return -1;
08405    }
08406 
08407    u = ast_module_user_add(chan);
08408 
08409    box = ast_strdupa(data);
08410 
08411    AST_STANDARD_APP_ARGS(args, box);
08412 
08413    if (args.options) {
08414       if (strchr(args.options, 'j'))
08415          priority_jump = 1;
08416    }
08417 
08418    if ((context = strchr(args.mbox, '@'))) {
08419       *context = '\0';
08420       context++;
08421    }
08422 
08423    if (find_user(&svm, context, args.mbox)) {
08424       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
08425       if (priority_jump || ast_opt_priority_jumping)
08426          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) 
08427             ast_log(LOG_WARNING, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box, context, chan->exten, chan->priority + 101);
08428    } else
08429       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
08430    ast_module_user_remove(u);
08431    return 0;
08432 }
08433 
08434 static int vmauthenticate(struct ast_channel *chan, void *data)
08435 {
08436    struct ast_module_user *u;
08437    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
08438    struct ast_vm_user vmus;
08439    char *options = NULL;
08440    int silent = 0, skipuser = 0;
08441    int res = -1;
08442 
08443    u = ast_module_user_add(chan);
08444    
08445    if (s) {
08446       s = ast_strdupa(s);
08447       user = strsep(&s, "|");
08448       options = strsep(&s, "|");
08449       if (user) {
08450          s = user;
08451          user = strsep(&s, "@");
08452          context = strsep(&s, "");
08453          if (!ast_strlen_zero(user))
08454             skipuser++;
08455          ast_copy_string(mailbox, user, sizeof(mailbox));
08456       }
08457    }
08458 
08459    if (options) {
08460       silent = (strchr(options, 's')) != NULL;
08461    }
08462 
08463    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
08464       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
08465       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
08466       ast_play_and_wait(chan, "auth-thankyou");
08467       res = 0;
08468    }
08469 
08470    ast_module_user_remove(u);
08471    return res;
08472 }
08473 
08474 static char voicemail_show_users_help[] =
08475 "Usage: voicemail show users [for <context>]\n"
08476 "       Lists all mailboxes currently set up\n";
08477 
08478 static char voicemail_show_zones_help[] =
08479 "Usage: voicemail show zones\n"
08480 "       Lists zone message formats\n";
08481 
08482 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
08483 {
08484    struct ast_vm_user *vmu;
08485    char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
08486 
08487    if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
08488    else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
08489 
08490    AST_LIST_LOCK(&users);
08491    if (!AST_LIST_EMPTY(&users)) {
08492       if (argc == 3)
08493          ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
08494       else {
08495          int count = 0;
08496          AST_LIST_TRAVERSE(&users, vmu, list) {
08497             if (!strcmp(argv[4],vmu->context))
08498                count++;
08499          }
08500          if (count) {
08501             ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
08502          } else {
08503             ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
08504             AST_LIST_UNLOCK(&users);
08505             return RESULT_FAILURE;
08506          }
08507       }
08508       AST_LIST_TRAVERSE(&users, vmu, list) {
08509          int newmsgs = 0, oldmsgs = 0;
08510          char count[12], tmp[256] = "";
08511 
08512          if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
08513             snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
08514             inboxcount(tmp, &newmsgs, &oldmsgs);
08515             snprintf(count,sizeof(count),"%d",newmsgs);
08516             ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
08517          }
08518       }
08519    } else {
08520       ast_cli(fd, "There are no voicemail users currently defined\n");
08521       AST_LIST_UNLOCK(&users);
08522       return RESULT_FAILURE;
08523    }
08524    AST_LIST_UNLOCK(&users);
08525    return RESULT_SUCCESS;
08526 }
08527 
08528 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
08529 {
08530    struct vm_zone *zone;
08531    char *output_format = "%-15s %-20s %-45s\n";
08532    int res = RESULT_SUCCESS;
08533 
08534    if (argc != 3)
08535       return RESULT_SHOWUSAGE;
08536 
08537    AST_LIST_LOCK(&zones);
08538    if (!AST_LIST_EMPTY(&zones)) {
08539       ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
08540       AST_LIST_TRAVERSE(&zones, zone, list) {
08541          ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
08542       }
08543    } else {
08544       ast_cli(fd, "There are no voicemail zones currently defined\n");
08545       res = RESULT_FAILURE;
08546    }
08547    AST_LIST_UNLOCK(&zones);
08548 
08549    return res;
08550 }
08551 
08552 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
08553 {
08554    int which = 0;
08555    int wordlen;
08556    struct ast_vm_user *vmu;
08557    const char *context = "";
08558 
08559    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
08560    if (pos > 4)
08561       return NULL;
08562    if (pos == 3)
08563       return (state == 0) ? ast_strdup("for") : NULL;
08564    wordlen = strlen(word);
08565    AST_LIST_TRAVERSE(&users, vmu, list) {
08566       if (!strncasecmp(word, vmu->context, wordlen)) {
08567          if (context && strcmp(context, vmu->context) && ++which > state)
08568             return ast_strdup(vmu->context);
08569          /* ignore repeated contexts ? */
08570          context = vmu->context;
08571       }
08572    }
08573    return NULL;
08574 }
08575 
08576 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
08577    { "show", "voicemail", "users", NULL },
08578    handle_voicemail_show_users, NULL,
08579    NULL, complete_voicemail_show_users };
08580 
08581 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
08582    { "show", "voicemail", "zones", NULL },
08583    handle_voicemail_show_zones, NULL,
08584    NULL, NULL };
08585 
08586 static struct ast_cli_entry cli_voicemail[] = {
08587    { { "voicemail", "show", "users", NULL },
08588    handle_voicemail_show_users, "List defined voicemail boxes",
08589    voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
08590 
08591    { { "voicemail", "show", "zones", NULL },
08592    handle_voicemail_show_zones, "List zone message formats",
08593    voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
08594 };
08595 
08596 static void free_vm_users(void)
08597 {
08598    struct ast_vm_user *cur;
08599    struct vm_zone *zcur;
08600 
08601    AST_LIST_LOCK(&users);
08602    while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
08603       ast_set_flag(cur, VM_ALLOCED);
08604       free_user(cur);
08605    }
08606    AST_LIST_UNLOCK(&users);
08607 
08608    AST_LIST_LOCK(&zones);
08609    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list))) {
08610       free_zone(zcur);
08611    }
08612    AST_LIST_UNLOCK(&zones);
08613 }
08614 
08615 static int load_config(void)
08616 {
08617    struct ast_vm_user *cur;
08618    struct ast_config *cfg, *ucfg;
08619    char *cat;
08620    struct ast_variable *var;
08621    const char *notifystr = NULL;
08622    const char *smdistr = NULL;
08623    const char *astattach;
08624    const char *astsearch;
08625    const char *astsaycid;
08626    const char *send_voicemail;
08627 #ifdef IMAP_STORAGE
08628    const char *imap_server;
08629    const char *imap_port;
08630    const char *imap_flags;
08631    const char *imap_folder;
08632    const char *auth_user;
08633    const char *auth_password;
08634    const char *expunge_on_hangup;
08635    const char *imap_timeout;
08636 #endif
08637    const char *astcallop;
08638    const char *astreview;
08639    const char *asttempgreetwarn;
08640    const char *astskipcmd;
08641    const char *asthearenv;
08642    const char *astsaydurationinfo;
08643    const char *astsaydurationminfo;
08644    const char *silencestr;
08645    const char *maxmsgstr;
08646    const char *astdirfwd;
08647    const char *thresholdstr;
08648    const char *fmt;
08649    const char *astemail;
08650    const char *ucontext;
08651    const char *astmailcmd = SENDMAIL;
08652    const char *astpreprocesscmd;
08653    const char *astpreprocessfmt;
08654    const char *astforcename;
08655    const char *astforcegreet;
08656    const char *s;
08657    char *q,*stringp, *tmp;
08658    const char *dialoutcxt = NULL;
08659    const char *callbackcxt = NULL;  
08660    const char *exitcxt = NULL;   
08661    const char *extpc;
08662    const char *emaildateformatstr;
08663    const char *volgainstr;
08664    int x;
08665    int tmpadsi[4];
08666 
08667    cfg = ast_config_load(VOICEMAIL_CONFIG);
08668 
08669    free_vm_users();
08670 
08671    AST_LIST_LOCK(&users);
08672 
08673    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
08674 
08675    if (cfg) {
08676       /* General settings */
08677 
08678       if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
08679          ucontext = "default";
08680       ast_copy_string(userscontext, ucontext, sizeof(userscontext));
08681       /* Attach voice message to mail message ? */
08682       if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
08683          astattach = "yes";
08684       ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH); 
08685 
08686       if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
08687          astsearch = "no";
08688       ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
08689 
08690       volgain = 0.0;
08691       if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
08692          sscanf(volgainstr, "%30lf", &volgain);
08693 
08694 #ifdef ODBC_STORAGE
08695       strcpy(odbc_database, "asterisk");
08696       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
08697          ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
08698       }
08699       strcpy(odbc_table, "voicemessages");
08700       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
08701          ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
08702       }
08703 #endif      
08704       /* Mail command */
08705       strcpy(mailcmd, SENDMAIL);
08706       if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
08707          ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
08708 
08709       if ((astpreprocesscmd = ast_variable_retrieve(cfg, "general", "preprocesscmd")))
08710          ast_copy_string(preprocesscmd, astpreprocesscmd, sizeof(preprocesscmd)); /* User setting */
08711 
08712       if ((astpreprocessfmt = ast_variable_retrieve(cfg, "general", "preprocessfmt")))
08713          ast_copy_string(preprocessfmt, astpreprocessfmt, sizeof(preprocessfmt)); /* User setting */
08714 
08715       maxsilence = 0;
08716       if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
08717          maxsilence = atoi(silencestr);
08718          if (maxsilence > 0)
08719             maxsilence *= 1000;
08720       }
08721       
08722       if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
08723          maxmsg = MAXMSG;
08724       } else {
08725          maxmsg = atoi(maxmsgstr);
08726          if (maxmsg <= 0) {
08727             ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
08728             maxmsg = MAXMSG;
08729          } else if (maxmsg > MAXMSGLIMIT) {
08730             ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
08731             maxmsg = MAXMSGLIMIT;
08732          }
08733       }
08734 
08735       /* Load date format config for voicemail mail */
08736       if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
08737          ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
08738       }
08739 
08740       /* External password changing command */
08741       if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
08742          ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
08743       }
08744 #ifdef IMAP_STORAGE
08745       /* IMAP server address */
08746       if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
08747          ast_copy_string(imapserver, imap_server, sizeof(imapserver));
08748       } else {
08749          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
08750       }
08751       /* IMAP server port */
08752       if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
08753          ast_copy_string(imapport, imap_port, sizeof(imapport));
08754       } else {
08755          ast_copy_string(imapport,"143", sizeof(imapport));
08756       }
08757       /* IMAP server flags */
08758       if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
08759          ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
08760       }
08761       /* IMAP server master username */
08762       if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
08763          ast_copy_string(authuser, auth_user, sizeof(authuser));
08764       }
08765       /* IMAP server master password */
08766       if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
08767          ast_copy_string(authpassword, auth_password, sizeof(authpassword));
08768       }
08769       /* Expunge on exit */
08770       if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
08771          if (ast_false(expunge_on_hangup))
08772             expungeonhangup = 0;
08773          else
08774             expungeonhangup = 1;
08775       } else {
08776          expungeonhangup = 1;
08777       }
08778       /* IMAP voicemail folder */
08779       if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
08780          ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
08781       } else {
08782          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
08783       }
08784 
08785       /* There is some very unorthodox casting done here. This is due
08786        * to the way c-client handles the argument passed in. It expects a 
08787        * void pointer and casts the pointer directly to a long without
08788        * first dereferencing it. */
08789 
08790       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
08791          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(imap_timeout)));
08792       } else {
08793          mail_parameters(NIL, SET_READTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08794       }
08795 
08796       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
08797          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(imap_timeout)));
08798       } else {
08799          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08800       }
08801 
08802       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
08803          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(imap_timeout)));
08804       } else {
08805          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08806       }
08807 
08808       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
08809          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(imap_timeout)));
08810       } else {
08811          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08812       }
08813 
08814       /* Increment configuration version */
08815       imapversion++;
08816 #endif
08817       /* External voicemail notify application */
08818       
08819       if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
08820          ast_copy_string(externnotify, notifystr, sizeof(externnotify));
08821          if (option_debug > 2)
08822             ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
08823          if (!strcasecmp(externnotify, "smdi")) {
08824             if (option_debug)
08825                ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
08826             if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
08827                smdi_iface = ast_smdi_interface_find(smdistr);
08828             } else {
08829                if (option_debug)
08830                   ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
08831                smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
08832             }
08833 
08834             if (!smdi_iface) {
08835                ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
08836                externnotify[0] = '\0';
08837             }
08838          }
08839       } else {
08840          externnotify[0] = '\0';
08841       }
08842 
08843       /* Silence treshold */
08844       silencethreshold = 256;
08845       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
08846          silencethreshold = atoi(thresholdstr);
08847       
08848       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
08849          astemail = ASTERISK_USERNAME;
08850       ast_copy_string(serveremail, astemail, sizeof(serveremail));
08851       
08852       vmmaxmessage = 0;
08853       if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
08854          if (sscanf(s, "%30d", &x) == 1) {
08855             vmmaxmessage = x;
08856          } else {
08857             ast_log(LOG_WARNING, "Invalid max message time length\n");
08858          }
08859       }
08860 
08861       vmminmessage = 0;
08862       if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
08863          if (sscanf(s, "%30d", &x) == 1) {
08864             vmminmessage = x;
08865             if (maxsilence / 1000 >= vmminmessage)
08866                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
08867          } else {
08868             ast_log(LOG_WARNING, "Invalid min message time length\n");
08869          }
08870       }
08871       fmt = ast_variable_retrieve(cfg, "general", "format");
08872       if (!fmt) {
08873          fmt = "wav";   
08874       } else {
08875          tmp = ast_strdupa(fmt);
08876          fmt = ast_format_str_reduce(tmp);
08877          if (!fmt) {
08878             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
08879             fmt = "wav";
08880          }
08881       }
08882 
08883       ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
08884 
08885       skipms = 3000;
08886       if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
08887          if (sscanf(s, "%30d", &x) == 1) {
08888             maxgreet = x;
08889          } else {
08890             ast_log(LOG_WARNING, "Invalid max message greeting length\n");
08891          }
08892       }
08893 
08894       if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
08895          if (sscanf(s, "%30d", &x) == 1) {
08896             skipms = x;
08897          } else {
08898             ast_log(LOG_WARNING, "Invalid skipms value\n");
08899          }
08900       }
08901 
08902       maxlogins = 3;
08903       if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
08904          if (sscanf(s, "%30d", &x) == 1) {
08905             maxlogins = x;
08906          } else {
08907             ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
08908          }
08909       }
08910 
08911       /* Force new user to record name ? */
08912       if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename"))) 
08913          astforcename = "no";
08914       ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
08915 
08916       /* Force new user to record greetings ? */
08917       if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
08918          astforcegreet = "no";
08919       ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
08920 
08921       if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
08922          if (option_debug > 2)
08923             ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
08924          stringp = ast_strdupa(s);
08925          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
08926             if (!ast_strlen_zero(stringp)) {
08927                q = strsep(&stringp,",");
08928                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
08929                   q++;
08930                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
08931                if (option_debug > 2)
08932                   ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
08933             } else {
08934                cidinternalcontexts[x][0] = '\0';
08935             }
08936          }
08937       }
08938       if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
08939          if (option_debug)
08940             ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
08941          astreview = "no";
08942       }
08943       ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW); 
08944 
08945       /*Temperary greeting reminder */
08946       if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
08947          if (option_debug)
08948             ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
08949          asttempgreetwarn = "no";
08950       } else {
08951          if (option_debug)
08952             ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
08953       }
08954       ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
08955 
08956       if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
08957          if (option_debug)
08958             ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
08959          astcallop = "no";
08960       }
08961       ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);  
08962 
08963       if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
08964          if (option_debug)
08965             ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
08966          astsaycid = "no";
08967       } 
08968       ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID); 
08969 
08970       if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
08971          if (option_debug)
08972             ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
08973          send_voicemail = "no";
08974       }
08975       ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
08976    
08977       if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
08978          if (option_debug)
08979             ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
08980          asthearenv = "yes";
08981       }
08982       ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE); 
08983 
08984       if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
08985          if (option_debug)
08986             ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
08987          astsaydurationinfo = "yes";
08988       }
08989       ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);  
08990 
08991       saydurationminfo = 2;
08992       if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
08993          if (sscanf(astsaydurationminfo, "%30d", &x) == 1) {
08994             saydurationminfo = x;
08995          } else {
08996             ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
08997          }
08998       }
08999 
09000       if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
09001          if (option_debug)
09002             ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
09003          astskipcmd = "no";
09004       }
09005       ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
09006 
09007       if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
09008          ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
09009          if (option_debug)
09010             ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
09011       } else {
09012          dialcontext[0] = '\0';  
09013       }
09014       
09015       if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
09016          ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
09017          if (option_debug)
09018             ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
09019       } else {
09020          callcontext[0] = '\0';
09021       }
09022 
09023       if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
09024          ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
09025          if (option_debug)
09026             ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
09027       } else {
09028          exitcontext[0] = '\0';
09029       }
09030 
09031       if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
09032          astdirfwd = "no";
09033       ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD); 
09034       if ((ucfg = ast_config_load("users.conf"))) {   
09035          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
09036             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
09037                continue;
09038             if ((cur = find_or_create(userscontext, cat))) {
09039                populate_defaults(cur);
09040                apply_options_full(cur, ast_variable_browse(ucfg, cat));
09041                ast_copy_string(cur->context, userscontext, sizeof(cur->context));
09042             }
09043          }
09044          ast_config_destroy(ucfg);
09045       }
09046       cat = ast_category_browse(cfg, NULL);
09047       while (cat) {
09048          if (strcasecmp(cat, "general")) {
09049             var = ast_variable_browse(cfg, cat);
09050             if (strcasecmp(cat, "zonemessages")) {
09051                /* Process mailboxes in this context */
09052                while (var) {
09053                   append_mailbox(cat, var->name, var->value);
09054                   var = var->next;
09055                }
09056             } else {
09057                /* Timezones in this context */
09058                while (var) {
09059                   struct vm_zone *z;
09060                   if ((z = ast_malloc(sizeof(*z)))) {
09061                      char *msg_format, *timezone;
09062                      msg_format = ast_strdupa(var->value);
09063                      timezone = strsep(&msg_format, "|");
09064                      if (msg_format) {
09065                         ast_copy_string(z->name, var->name, sizeof(z->name));
09066                         ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
09067                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
09068                         AST_LIST_LOCK(&zones);
09069                         AST_LIST_INSERT_HEAD(&zones, z, list);
09070                         AST_LIST_UNLOCK(&zones);
09071                      } else {
09072                         ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
09073                         free(z);
09074                      }
09075                   } else {
09076                      free(z);
09077                      AST_LIST_UNLOCK(&users);
09078                      ast_config_destroy(cfg);
09079                      return -1;
09080                   }
09081                   var = var->next;
09082                }
09083             }
09084          }
09085          cat = ast_category_browse(cfg, cat);
09086       }
09087       memset(fromstring,0,sizeof(fromstring));
09088       memset(pagerfromstring,0,sizeof(pagerfromstring));
09089       memset(emailtitle,0,sizeof(emailtitle));
09090       strcpy(charset, "ISO-8859-1");
09091       if (emailbody) {
09092          free(emailbody);
09093          emailbody = NULL;
09094       }
09095       if (emailsubject) {
09096          free(emailsubject);
09097          emailsubject = NULL;
09098       }
09099       if (pagerbody) {
09100          free(pagerbody);
09101          pagerbody = NULL;
09102       }
09103       if (pagersubject) {
09104          free(pagersubject);
09105          pagersubject = NULL;
09106       }
09107       if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
09108          ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
09109       if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
09110          ast_copy_string(fromstring,s,sizeof(fromstring));
09111       if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
09112          ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
09113       if ((s = ast_variable_retrieve(cfg, "general", "charset")))
09114          ast_copy_string(charset,s,sizeof(charset));
09115       if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
09116          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
09117          for (x = 0; x < 4; x++) {
09118             memcpy(&adsifdn[x], &tmpadsi[x], 1);
09119          }
09120       }
09121       if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
09122          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
09123          for (x = 0; x < 4; x++) {
09124             memcpy(&adsisec[x], &tmpadsi[x], 1);
09125          }
09126       }
09127       if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
09128          if (atoi(s)) {
09129             adsiver = atoi(s);
09130          }
09131       if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
09132          ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
09133          ast_copy_string(emailtitle,s,sizeof(emailtitle));
09134       }
09135       if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
09136          emailsubject = ast_strdup(s);
09137       if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
09138          char *tmpread, *tmpwrite;
09139          emailbody = ast_strdup(s);
09140 
09141          /* substitute strings \t and \n into the appropriate characters */
09142          tmpread = tmpwrite = emailbody;
09143          while ((tmpwrite = strchr(tmpread,'\\'))) {
09144             switch (tmpwrite[1]) {
09145             case 'r':
09146                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09147                *tmpwrite = '\r';
09148                break;
09149             case 'n':
09150                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09151                *tmpwrite = '\n';
09152                break;
09153             case 't':
09154                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09155                *tmpwrite = '\t';
09156                break;
09157             default:
09158                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
09159             }
09160             tmpread = tmpwrite + 1;
09161          }
09162       }
09163       if ((s = ast_variable_retrieve(cfg, "general", "tz"))) {
09164          ast_copy_string(zonetag, s, sizeof(zonetag));
09165       }
09166       if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
09167          pagersubject = ast_strdup(s);
09168       if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
09169          char *tmpread, *tmpwrite;
09170          pagerbody = ast_strdup(s);
09171 
09172          /* substitute strings \t and \n into the appropriate characters */
09173          tmpread = tmpwrite = pagerbody;
09174          while ((tmpwrite = strchr(tmpread, '\\'))) {
09175             switch (tmpwrite[1]) {
09176             case 'r':
09177                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09178                *tmpwrite = '\r';
09179                break;
09180             case 'n':
09181                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09182                *tmpwrite = '\n';
09183                break;
09184             case 't':
09185                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09186                *tmpwrite = '\t';
09187                break;
09188             default:
09189                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
09190             }
09191             tmpread = tmpwrite + 1;
09192          }
09193       }
09194       AST_LIST_UNLOCK(&users);
09195       ast_config_destroy(cfg);
09196       return 0;
09197    } else {
09198       AST_LIST_UNLOCK(&users);
09199       ast_log(LOG_WARNING, "Failed to load configuration file.\n");
09200       return 0;
09201    }
09202 }
09203 
09204 static int reload(void)
09205 {
09206    return(load_config());
09207 }
09208 
09209 static int unload_module(void)
09210 {
09211    int res;
09212    
09213    res = ast_unregister_application(app);
09214    res |= ast_unregister_application(app2);
09215    res |= ast_unregister_application(app3);
09216    res |= ast_unregister_application(app4);
09217    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
09218    ast_uninstall_vm_functions();
09219    ao2_ref(inprocess_container, -1);
09220    
09221    ast_module_user_hangup_all();
09222 
09223    return res;
09224 }
09225 
09226 static int load_module(void)
09227 {
09228    int res;
09229    char *adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
09230    char *smdi_loaded = ast_module_helper("", "res_smdi", 0, 0, 0, 0);
09231    free(adsi_loaded);
09232    free(smdi_loaded);
09233 
09234    if (!adsi_loaded) {
09235       ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
09236       return AST_MODULE_LOAD_DECLINE;
09237    }
09238 
09239    if (!smdi_loaded) {
09240       ast_log(LOG_ERROR, "app_voicemail.so depends upon res_smdi.so\n");
09241       return AST_MODULE_LOAD_DECLINE;
09242    }
09243 
09244    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
09245       return AST_MODULE_LOAD_DECLINE;
09246    }
09247 
09248    my_umask = umask(0);
09249    umask(my_umask);
09250    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
09251    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
09252    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
09253    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
09254    if (res)
09255       return(res);
09256 
09257    if ((res=load_config())) {
09258       return(res);
09259    }
09260 
09261    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
09262 
09263    /* compute the location of the voicemail spool directory */
09264    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
09265 
09266    ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
09267 
09268    return res;
09269 }
09270 
09271 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
09272 {
09273    int cmd = 0;
09274    char destination[80] = "";
09275    int retries = 0;
09276 
09277    if (!num) {
09278       if (option_verbose > 2)
09279          ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
09280       while (retries < 3 && cmd != 't') {
09281          destination[1] = '\0';
09282          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
09283          if (!cmd)
09284             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
09285          if (!cmd)
09286             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
09287          if (!cmd) {
09288             cmd = ast_waitfordigit(chan, 6000);
09289             if (cmd)
09290                destination[0] = cmd;
09291          }
09292          if (!cmd) {
09293             retries++;
09294          } else {
09295 
09296             if (cmd < 0)
09297                return 0;
09298             if (cmd == '*') {
09299                if (option_verbose > 2)
09300                   ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
09301                return 0;
09302             }
09303             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
09304                retries++;
09305             else
09306                cmd = 't';
09307          }
09308       }
09309       if (retries >= 3) {
09310          return 0;
09311       }
09312       
09313    } else {
09314       if (option_verbose > 2)
09315          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
09316       ast_copy_string(destination, num, sizeof(destination));
09317    }
09318 
09319    if (!ast_strlen_zero(destination)) {
09320       if (destination[strlen(destination) -1 ] == '*')
09321          return 0; 
09322       if (option_verbose > 2)
09323          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
09324       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
09325       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
09326       chan->priority = 0;
09327       return 9;
09328    }
09329    return 0;
09330 }
09331 
09332 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)
09333 {
09334    int res = 0;
09335    char filename[PATH_MAX];
09336    struct ast_config *msg_cfg = NULL;
09337    const char *origtime, *context;
09338    char *cid, *name, *num;
09339    int retries = 0;
09340 
09341    vms->starting = 0; 
09342    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
09343 
09344    /* Retrieve info from VM attribute file */
09345    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
09346    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
09347    RETRIEVE(vms->curdir, vms->curmsg, vmu);
09348    msg_cfg = ast_config_load(filename);
09349    DISPOSE(vms->curdir, vms->curmsg);
09350    if (!msg_cfg) {
09351       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
09352       return 0;
09353    }
09354 
09355    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
09356       ast_config_destroy(msg_cfg);
09357       return 0;
09358    }
09359 
09360    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
09361 
09362    context = ast_variable_retrieve(msg_cfg, "message", "context");
09363    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
09364       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
09365    switch (option) {
09366    case 3:
09367       if (!res)
09368          res = play_message_datetime(chan, vmu, origtime, filename);
09369       if (!res)
09370          res = play_message_callerid(chan, vms, cid, context, 0);
09371 
09372       res = 't';
09373       break;
09374 
09375    case 2:  /* Call back */
09376 
09377       if (ast_strlen_zero(cid))
09378          break;
09379 
09380       ast_callerid_parse(cid, &name, &num);
09381       while ((res > -1) && (res != 't')) {
09382          switch (res) {
09383          case '1':
09384             if (num) {
09385                /* Dial the CID number */
09386                res = dialout(chan, vmu, num, vmu->callback);
09387                if (res) {
09388                   ast_config_destroy(msg_cfg);
09389                   return 9;
09390                }
09391             } else {
09392                res = '2';
09393             }
09394             break;
09395 
09396          case '2':
09397             /* Want to enter a different number, can only do this if there's a dialout context for this user */
09398             if (!ast_strlen_zero(vmu->dialout)) {
09399                res = dialout(chan, vmu, NULL, vmu->dialout);
09400                if (res) {
09401                   ast_config_destroy(msg_cfg);
09402                   return 9;
09403                }
09404             } else {
09405                if (option_verbose > 2)
09406                   ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
09407                res = ast_play_and_wait(chan, "vm-sorry");
09408             }
09409             ast_config_destroy(msg_cfg);
09410             return res;
09411          case '*':
09412             res = 't';
09413             break;
09414          case '3':
09415          case '4':
09416          case '5':
09417          case '6':
09418          case '7':
09419          case '8':
09420          case '9':
09421          case '0':
09422 
09423             res = ast_play_and_wait(chan, "vm-sorry");
09424             retries++;
09425             break;
09426          default:
09427             if (num) {
09428                if (option_verbose > 2)
09429                   ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
09430                res = ast_play_and_wait(chan, "vm-num-i-have");
09431                if (!res)
09432                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
09433                if (!res)
09434                   res = ast_play_and_wait(chan, "vm-tocallnum");
09435                /* Only prompt for a caller-specified number if there is a dialout context specified */
09436                if (!ast_strlen_zero(vmu->dialout)) {
09437                   if (!res)
09438                      res = ast_play_and_wait(chan, "vm-calldiffnum");
09439                }
09440             } else {
09441                res = ast_play_and_wait(chan, "vm-nonumber");
09442                if (!ast_strlen_zero(vmu->dialout)) {
09443                   if (!res)
09444                      res = ast_play_and_wait(chan, "vm-toenternumber");
09445                }
09446             }
09447             if (!res)
09448                res = ast_play_and_wait(chan, "vm-star-cancel");
09449             if (!res)
09450                res = ast_waitfordigit(chan, 6000);
09451             if (!res) {
09452                retries++;
09453                if (retries > 3)
09454                   res = 't';
09455             }
09456             break; 
09457             
09458          }
09459          if (res == 't')
09460             res = 0;
09461          else if (res == '*')
09462             res = -1;
09463       }
09464       break;
09465       
09466    case 1:  /* Reply */
09467       /* Send reply directly to sender */
09468       if (ast_strlen_zero(cid))
09469          break;
09470 
09471       ast_callerid_parse(cid, &name, &num);
09472       if (!num) {
09473          if (option_verbose > 2)
09474             ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
09475          if (!res)
09476             res = ast_play_and_wait(chan, "vm-nonumber");
09477          ast_config_destroy(msg_cfg);
09478          return res;
09479       } else {
09480          struct ast_vm_user vmu2;
09481          if (find_user(&vmu2, vmu->context, num)) {
09482             struct leave_vm_options leave_options;
09483             char mailbox[AST_MAX_EXTENSION * 2 + 2];
09484             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
09485 
09486             if (option_verbose > 2)
09487                ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
09488             
09489             memset(&leave_options, 0, sizeof(leave_options));
09490             leave_options.record_gain = record_gain;
09491             res = leave_voicemail(chan, mailbox, &leave_options);
09492             if (!res)
09493                res = 't';
09494             ast_config_destroy(msg_cfg);
09495             return res;
09496          } else {
09497             /* Sender has no mailbox, can't reply */
09498             if (option_verbose > 2)
09499                ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
09500             ast_play_and_wait(chan, "vm-nobox");
09501             res = 't';
09502             ast_config_destroy(msg_cfg);
09503             return res;
09504          }
09505       } 
09506       res = 0;
09507 
09508       break;
09509    }
09510 
09511 #ifndef IMAP_STORAGE
09512    ast_config_destroy(msg_cfg);
09513 
09514    if (!res) {
09515       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
09516       vms->heard[msg] = 1;
09517       res = wait_file(chan, vms, vms->fn);
09518    }
09519 #endif
09520    return res;
09521 }
09522 
09523 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
09524          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
09525          signed char record_gain, struct vm_state *vms)
09526 {
09527    /* Record message & let caller review or re-record it, or set options if applicable */
09528    int res = 0;
09529    int cmd = 0;
09530    int max_attempts = 3;
09531    int attempts = 0;
09532    int recorded = 0;
09533    int message_exists = 0;
09534    signed char zero_gain = 0;
09535    char tempfile[PATH_MAX];
09536    char *acceptdtmf = "#";
09537    char *canceldtmf = "";
09538    int canceleddtmf = 0;
09539 
09540    /* Note that urgent and private are for flagging messages as such in the future */
09541 
09542    /* barf if no pointer passed to store duration in */
09543    if (duration == NULL) {
09544       ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
09545       return -1;
09546    }
09547 
09548    if (!outsidecaller)
09549       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
09550    else
09551       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
09552 
09553    cmd = '3';  /* Want to start by recording */
09554 
09555    while ((cmd >= 0) && (cmd != 't')) {
09556       switch (cmd) {
09557       case '1':
09558          if (!message_exists) {
09559             /* In this case, 1 is to record a message */
09560             cmd = '3';
09561             break;
09562          } else {
09563             /* Otherwise 1 is to save the existing message */
09564             if (option_verbose > 2)
09565                ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
09566             if (!outsidecaller)
09567                ast_filerename(tempfile, recordfile, NULL);
09568             ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
09569             if (!outsidecaller) {
09570                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
09571                DISPOSE(recordfile, -1);
09572             }
09573             cmd = 't';
09574             return res;
09575          }
09576       case '2':
09577          /* Review */
09578          if (option_verbose > 2)
09579             ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
09580          cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
09581          break;
09582       case '3':
09583          message_exists = 0;
09584          /* Record */
09585          if (recorded == 1) {
09586             if (option_verbose > 2)
09587                ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
09588          } else { 
09589             if (option_verbose > 2)
09590                ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
09591          }
09592          if (recorded && outsidecaller) {
09593             cmd = ast_play_and_wait(chan, INTRO);
09594             cmd = ast_play_and_wait(chan, "beep");
09595          }
09596          recorded = 1;
09597          /* 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 */
09598          if (record_gain)
09599             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
09600          if (ast_test_flag(vmu, VM_OPERATOR))
09601             canceldtmf = "0";
09602          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
09603          if (strchr(canceldtmf, cmd)) {
09604          /* need this flag here to distinguish between pressing '0' during message recording or after */
09605             canceleddtmf = 1;
09606          }
09607          if (record_gain)
09608             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
09609          if (cmd == -1) {
09610             /* User has hung up, no options to give */
09611             if (!outsidecaller) {
09612                /* user was recording a greeting and they hung up, so let's delete the recording. */
09613                ast_filedelete(tempfile, NULL);
09614             }
09615             return cmd;
09616          }
09617          if (cmd == '0') {
09618             break;
09619          } else if (cmd == '*') {
09620             break;
09621          } 
09622 #if 0       
09623          else if (vmu->review && (*duration < 5)) {
09624             /* Message is too short */
09625             if (option_verbose > 2)
09626                ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
09627             cmd = ast_play_and_wait(chan, "vm-tooshort");
09628             cmd = ast_filedelete(tempfile, NULL);
09629             break;
09630          }
09631          else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
09632             /* Message is all silence */
09633             if (option_verbose > 2)
09634                ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
09635             cmd = ast_filedelete(tempfile, NULL);
09636             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
09637             if (!cmd)
09638                cmd = ast_play_and_wait(chan, "vm-speakup");
09639             break;
09640          }
09641 #endif
09642          else {
09643             /* If all is well, a message exists */
09644             message_exists = 1;
09645             cmd = 0;
09646          }
09647          break;
09648       case '4':
09649       case '5':
09650       case '6':
09651       case '7':
09652       case '8':
09653       case '9':
09654       case '*':
09655       case '#':
09656          cmd = ast_play_and_wait(chan, "vm-sorry");
09657          break;
09658 #if 0 
09659 /*  XXX Commented out for the moment because of the dangers of deleting
09660     a message while recording (can put the message numbers out of sync) */
09661       case '*':
09662          /* Cancel recording, delete message, offer to take another message*/
09663          cmd = ast_play_and_wait(chan, "vm-deleted");
09664          cmd = ast_filedelete(tempfile, NULL);
09665          if (outsidecaller) {
09666             res = vm_exec(chan, NULL);
09667             return res;
09668          }
09669          else
09670             return 1;
09671 #endif
09672       case '0':
09673          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
09674             cmd = ast_play_and_wait(chan, "vm-sorry");
09675             break;
09676          }
09677          if (message_exists || recorded) {
09678             cmd = ast_play_and_wait(chan, "vm-saveoper");
09679             if (!cmd)
09680                cmd = ast_waitfordigit(chan, 3000);
09681             if (cmd == '1') {
09682                ast_filerename(tempfile, recordfile, NULL);
09683                ast_play_and_wait(chan, "vm-msgsaved");
09684                cmd = '0';
09685             } else {
09686                ast_play_and_wait(chan, "vm-deleted");
09687                DELETE(tempfile, -1, tempfile, vmu);
09688                cmd = '0';
09689             }
09690          }
09691          return cmd;
09692       default:
09693          /* If the caller is an ouside caller, and the review option is enabled,
09694             allow them to review the message, but let the owner of the box review
09695             their OGM's */
09696          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
09697             return cmd;
09698          if (message_exists) {
09699             cmd = ast_play_and_wait(chan, "vm-review");
09700          } else {
09701             cmd = ast_play_and_wait(chan, "vm-torerecord");
09702             if (!cmd)
09703                cmd = ast_waitfordigit(chan, 600);
09704          }
09705          
09706          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
09707             cmd = ast_play_and_wait(chan, "vm-reachoper");
09708             if (!cmd)
09709                cmd = ast_waitfordigit(chan, 600);
09710          }
09711 #if 0
09712          if (!cmd)
09713             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
09714 #endif
09715          if (!cmd)
09716             cmd = ast_waitfordigit(chan, 6000);
09717          if (!cmd) {
09718             attempts++;
09719          }
09720          if (attempts > max_attempts) {
09721             cmd = 't';
09722          }
09723       }
09724    }
09725    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
09726       /* Hang up or timeout, so delete the recording. */
09727       ast_filedelete(tempfile, NULL);
09728    }
09729 
09730    if (cmd != 't' && outsidecaller)
09731       ast_play_and_wait(chan, "vm-goodbye");
09732 
09733    return cmd;
09734 }
09735 
09736 /* This is a workaround so that menuselect displays a proper description
09737  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
09738  */
09739  
09740 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
09741       .load = load_module,
09742       .unload = unload_module,
09743       .reload = reload,
09744       );

Generated on Sat Aug 6 00:39:22 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7