Mon Mar 31 18:15:51 2014

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)) ? 
03659                 ((!ast_strlen_zero(vmu->preprocessfmt)) ? vmu->preprocessfmt : format) : 
03660                 ((!ast_strlen_zero(preprocessfmt) ? preprocessfmt : format))
03661                );
03662             snprintf(newfname, sizeof(newfname), "%s.%s", newtmp, newfmt);
03663             snprintf(tmpcmd, sizeof(tmpcmd), "%s < %s.%s > %s", !ast_strlen_zero(vmu->preprocesscmd) ? vmu->preprocesscmd : preprocesscmd, attach, format, newfname);
03664             exitstatus = ast_safe_system(tmpcmd);
03665             if (option_debug)
03666                ast_log(LOG_DEBUG, "Executed preprocesscmd '%s', exitstatus %i\n", tmpcmd, exitstatus);
03667             if (exitstatus) {
03668                ast_log(LOG_WARNING, "Command %s exited with exitstatus %i\n", tmpcmd, exitstatus);
03669                close(tmpfd);
03670                unlink(fname);
03671             } else {
03672                attach = newtmp;
03673                strncpy(format, newfmt, 5);
03674                close(tmpfd);
03675             }
03676 
03677          }
03678       }
03679 
03680       /* Eww. We want formats to tell us their own MIME type */
03681       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
03682       int soxstatus = 0;
03683 
03684       if (vmu->volgain < -.001 || vmu->volgain > .001) {
03685          create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
03686          snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
03687          tmpfd = mkstemp(newtmp);
03688          chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
03689          if (option_debug > 2)
03690             ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
03691          if (tmpfd > -1) {
03692             snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
03693             if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
03694                attach = newtmp;
03695                if (option_debug > 2) {
03696                   ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
03697                }
03698             } else {
03699                ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
03700                   soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
03701                ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
03702             }
03703          }
03704       }
03705       fprintf(p, "--%s" ENDL, bound);
03706       if (strcasecmp(format, "mp3"))
03707          fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
03708       else
03709          fprintf(p, "Content-Type: audio/mpeg; name=\"msg%04d.%s\"" ENDL, msgnum + 1, format);
03710       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
03711       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
03712       fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
03713       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
03714       base_encode(fname, p);
03715       fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
03716       if (tmpfd > -1) {
03717          if (soxstatus == 0) {
03718             unlink(fname);
03719          }
03720          close(tmpfd);
03721          unlink(newtmp);
03722       }
03723    }
03724 }
03725 
03726 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)
03727 {
03728    FILE *p=NULL;
03729    char tmp[80] = "/tmp/astmail-XXXXXX";
03730    char tmp2[256];
03731    char *stringp;
03732 
03733    if (vmu && ast_strlen_zero(vmu->email)) {
03734       ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
03735       return(0);
03736    }
03737 
03738    /* Mail only the first format */
03739    format = ast_strdupa(format);
03740    stringp = format;
03741    strsep(&stringp, "|");
03742 
03743    if (!strcmp(format, "wav49"))
03744       format = "WAV";
03745    if (option_debug > 2)
03746       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));
03747    /* Make a temporary file instead of piping directly to sendmail, in case the mail
03748       command hangs */
03749    if ((p = vm_mkftemp(tmp)) == NULL) {
03750       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03751       return -1;
03752    } else {
03753       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
03754       fclose(p);
03755       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03756       ast_safe_system(tmp2);
03757       if (option_debug > 2)
03758          ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
03759    }
03760    return 0;
03761 }
03762 
03763 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)
03764 {
03765    char date[256];
03766    char host[MAXHOSTNAMELEN] = "";
03767    char who[256];
03768    char dur[PATH_MAX];
03769    char tmp[80] = "/tmp/astmail-XXXXXX";
03770    char tmp2[PATH_MAX];
03771    struct tm tm;
03772    FILE *p;
03773 
03774    if ((p = vm_mkftemp(tmp)) == NULL) {
03775       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03776       return -1;
03777    } else {
03778       gethostname(host, sizeof(host)-1);
03779       if (strchr(srcemail, '@'))
03780          ast_copy_string(who, srcemail, sizeof(who));
03781       else {
03782          snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03783       }
03784       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03785       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03786       fprintf(p, "Date: %s\n", date);
03787 
03788       if (*pagerfromstring) {
03789          struct ast_channel *ast;
03790          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03791             char *passdata;
03792             int vmlen = strlen(fromstring)*3 + 200;
03793             if ((passdata = alloca(vmlen))) {
03794                memset(passdata, 0, vmlen);
03795                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03796                pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
03797                fprintf(p, "From: %s <%s>\n", passdata, who);
03798             } else 
03799                ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03800             ast_channel_free(ast);
03801          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03802       } else
03803          fprintf(p, "From: Asterisk PBX <%s>\n", who);
03804       fprintf(p, "To: %s\n", pager);
03805       if (pagersubject) {
03806          struct ast_channel *ast;
03807          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03808             char *passdata;
03809             int vmlen = strlen(pagersubject) * 3 + 200;
03810             if ((passdata = alloca(vmlen))) {
03811                memset(passdata, 0, vmlen);
03812                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03813                pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
03814                fprintf(p, "Subject: %s\n\n", passdata);
03815             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03816             ast_channel_free(ast);
03817          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03818       } else
03819          fprintf(p, "Subject: New VM\n\n");
03820       strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
03821       if (pagerbody) {
03822          struct ast_channel *ast;
03823          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03824             char *passdata;
03825             int vmlen = strlen(pagerbody)*3 + 200;
03826             if ((passdata = alloca(vmlen))) {
03827                memset(passdata, 0, vmlen);
03828                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03829                pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
03830                fprintf(p, "%s\n", passdata);
03831             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03832          ast_channel_free(ast);
03833          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03834       } else {
03835          fprintf(p, "New %s long msg in box %s\n"
03836                "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
03837       }
03838       fclose(p);
03839       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03840       ast_safe_system(tmp2);
03841       if (option_debug > 2)
03842          ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
03843    }
03844    return 0;
03845 }
03846 
03847 static int get_date(char *s, int len)
03848 {
03849    struct tm tm;
03850    time_t t;
03851 
03852    time(&t);
03853 
03854    ast_localtime(&t, &tm, NULL);
03855 
03856    return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
03857 }
03858 
03859 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
03860 {
03861    int res = -2;
03862 
03863 #ifdef ODBC_STORAGE
03864    int success = 
03865 #endif
03866    RETRIEVE(filename, -1, vmu);
03867    if (ast_fileexists(filename, NULL, NULL) > 0) {
03868       res = ast_streamfile(chan, filename, chan->language);
03869       if (res > -1) 
03870          res = ast_waitstream(chan, ecodes);
03871 #ifdef ODBC_STORAGE
03872       if (success == -1) {
03873          /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
03874          if (option_debug)
03875             ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
03876          store_file(filename, vmu->mailbox, vmu->context, -1);
03877       }
03878 #endif
03879    }
03880    DISPOSE(filename, -1);
03881 
03882    return res;
03883 }
03884 
03885 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
03886 {
03887    int res;
03888    char fn[PATH_MAX];
03889    char dest[PATH_MAX];
03890 
03891    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
03892 
03893    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
03894       ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
03895       return -1;
03896    }
03897 
03898    res = play_greeting(chan, vmu, fn, ecodes);
03899    if (res == -2) {
03900       /* File did not exist */
03901       res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
03902       if (res)
03903          return res;
03904       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
03905    }
03906 
03907    if (res)
03908       return res;
03909 
03910    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
03911    return res;
03912 }
03913 
03914 static void free_zone(struct vm_zone *z)
03915 {
03916    free(z);
03917 }
03918 
03919 #ifdef ODBC_STORAGE
03920 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
03921 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
03922 {
03923    int x = -1;
03924    int res;
03925    SQLHSTMT stmt;
03926    char sql[PATH_MAX];
03927    char rowdata[20];
03928    char tmp[PATH_MAX] = "";
03929    struct odbc_obj *obj;
03930    char *context;
03931    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
03932 
03933    if (newmsgs)
03934       *newmsgs = 0;
03935    if (oldmsgs)
03936       *oldmsgs = 0;
03937 
03938    /* If no mailbox, return immediately */
03939    if (ast_strlen_zero(mailbox))
03940       return 0;
03941 
03942    ast_copy_string(tmp, mailbox, sizeof(tmp));
03943    
03944    context = strchr(tmp, '@');
03945    if (context) {
03946       *context = '\0';
03947       context++;
03948    } else
03949       context = "default";
03950    
03951    obj = ast_odbc_request_obj(odbc_database, 0);
03952    if (obj) {
03953       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
03954       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03955       if (!stmt) {
03956          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03957          ast_odbc_release_obj(obj);
03958          goto yuck;
03959       }
03960       res = SQLFetch(stmt);
03961       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03962          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03963          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03964          ast_odbc_release_obj(obj);
03965          goto yuck;
03966       }
03967       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03968       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03969          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03970          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03971          ast_odbc_release_obj(obj);
03972          goto yuck;
03973       }
03974       *newmsgs = atoi(rowdata);
03975       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03976 
03977       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
03978       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03979       if (!stmt) {
03980          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03981          ast_odbc_release_obj(obj);
03982          goto yuck;
03983       }
03984       res = SQLFetch(stmt);
03985       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03986          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03987          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03988          ast_odbc_release_obj(obj);
03989          goto yuck;
03990       }
03991       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03992       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03993          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03994          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03995          ast_odbc_release_obj(obj);
03996          goto yuck;
03997       }
03998       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03999       ast_odbc_release_obj(obj);
04000       *oldmsgs = atoi(rowdata);
04001       x = 0;
04002    } else
04003       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04004       
04005 yuck:
04006    return x;
04007 }
04008 
04009 static int messagecount(const char *context, const char *mailbox, const char *folder)
04010 {
04011    struct odbc_obj *obj = NULL;
04012    int nummsgs = 0;
04013    int res;
04014    SQLHSTMT stmt = NULL;
04015    char sql[PATH_MAX];
04016    char rowdata[20];
04017    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04018    if (!folder)
04019       folder = "INBOX";
04020    /* If no mailbox, return immediately */
04021    if (ast_strlen_zero(mailbox))
04022       return 0;
04023 
04024    obj = ast_odbc_request_obj(odbc_database, 0);
04025    if (obj) {
04026       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04027       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04028       if (!stmt) {
04029          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04030          goto yuck;
04031       }
04032       res = SQLFetch(stmt);
04033       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04034          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04035          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04036          goto yuck;
04037       }
04038       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04039       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04040          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04041          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04042          goto yuck;
04043       }
04044       nummsgs = atoi(rowdata);
04045       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04046    } else
04047       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04048 
04049 yuck:
04050    if (obj)
04051       ast_odbc_release_obj(obj);
04052    return nummsgs;
04053 }
04054 
04055 static int has_voicemail(const char *mailbox, const char *folder)
04056 {
04057    char tmp[256], *tmp2 = tmp, *mbox, *context;
04058    ast_copy_string(tmp, mailbox, sizeof(tmp));
04059    while ((context = mbox = strsep(&tmp2, ","))) {
04060       strsep(&context, "@");
04061       if (ast_strlen_zero(context))
04062          context = "default";
04063       if (messagecount(context, mbox, folder))
04064          return 1;
04065    }
04066    return 0;
04067 }
04068 #endif
04069 #ifndef IMAP_STORAGE
04070 /* copy message only used by file storage */
04071 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)
04072 {
04073    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04074    const char *frombox = mbox(imbox);
04075    int recipmsgnum;
04076    int res = 0;
04077 
04078    ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04079 
04080    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04081    
04082    if (!dir)
04083       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04084    else
04085       ast_copy_string(fromdir, dir, sizeof(fromdir));
04086 
04087    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04088 
04089    if (vm_lock_path(todir))
04090       return ERROR_LOCK_PATH;
04091 
04092    recipmsgnum = 0;
04093    do {
04094       make_file(topath, sizeof(topath), todir, recipmsgnum);
04095       if (!EXISTS(todir, recipmsgnum, topath, chan->language))
04096          break;
04097       recipmsgnum++;
04098    } while (recipmsgnum < recip->maxmsg);
04099    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04100 #ifndef ODBC_STORAGE
04101       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04102          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04103       } else {
04104 #endif
04105          /* If we are prepending a message for ODBC, then the message already
04106           * exists in the database, but we want to force copying from the
04107           * filesystem (since only the FS contains the prepend). */
04108          copy_plain_file(frompath, topath);
04109          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL);
04110          vm_delete(topath);
04111 #ifndef ODBC_STORAGE
04112       }
04113 #endif
04114    } else {
04115       ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04116       res = -1;
04117    }
04118    ast_unlock_path(todir);
04119    notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04120    
04121    return res;
04122 }
04123 #endif
04124 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04125 static int messagecount(const char *context, const char *mailbox, const char *folder)
04126 {
04127    return __has_voicemail(context, mailbox, folder, 0);
04128 }
04129 
04130 
04131 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04132 {
04133    DIR *dir;
04134    struct dirent *de;
04135    char fn[256];
04136    int ret = 0;
04137    if (!folder)
04138       folder = "INBOX";
04139    /* If no mailbox, return immediately */
04140    if (ast_strlen_zero(mailbox))
04141       return 0;
04142    if (!context)
04143       context = "default";
04144    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04145    dir = opendir(fn);
04146    if (!dir)
04147       return 0;
04148    while ((de = readdir(dir))) {
04149       if (!strncasecmp(de->d_name, "msg", 3)) {
04150          if (shortcircuit) {
04151             ret = 1;
04152             break;
04153          } else if (!strncasecmp(de->d_name + 8, "txt", 3))
04154             ret++;
04155       }
04156    }
04157    closedir(dir);
04158    return ret;
04159 }
04160 
04161 
04162 static int has_voicemail(const char *mailbox, const char *folder)
04163 {
04164    char tmp[256], *tmp2 = tmp, *mbox, *context;
04165    ast_copy_string(tmp, mailbox, sizeof(tmp));
04166    while ((mbox = strsep(&tmp2, ","))) {
04167       if ((context = strchr(mbox, '@')))
04168          *context++ = '\0';
04169       else
04170          context = "default";
04171       if (__has_voicemail(context, mbox, folder, 1))
04172          return 1;
04173    }
04174    return 0;
04175 }
04176 
04177 
04178 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04179 {
04180    char tmp[256];
04181    char *context;
04182 
04183    if (newmsgs)
04184       *newmsgs = 0;
04185    if (oldmsgs)
04186       *oldmsgs = 0;
04187    /* If no mailbox, return immediately */
04188    if (ast_strlen_zero(mailbox))
04189       return 0;
04190    if (strchr(mailbox, ',')) {
04191       int tmpnew, tmpold;
04192       char *mb, *cur;
04193 
04194       ast_copy_string(tmp, mailbox, sizeof(tmp));
04195       mb = tmp;
04196       while ((cur = strsep(&mb, ", "))) {
04197          if (!ast_strlen_zero(cur)) {
04198             if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04199                return -1;
04200             else {
04201                if (newmsgs)
04202                   *newmsgs += tmpnew; 
04203                if (oldmsgs)
04204                   *oldmsgs += tmpold;
04205             }
04206          }
04207       }
04208       return 0;
04209    }
04210    ast_copy_string(tmp, mailbox, sizeof(tmp));
04211    context = strchr(tmp, '@');
04212    if (context) {
04213       *context = '\0';
04214       context++;
04215    } else
04216       context = "default";
04217    if (newmsgs)
04218       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04219    if (oldmsgs)
04220       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04221    return 0;
04222 }
04223 
04224 #endif
04225 
04226 static void run_externnotify(char *context, char *extension)
04227 {
04228    char arguments[255];
04229    char ext_context[256] = "";
04230    int newvoicemails = 0, oldvoicemails = 0;
04231    struct ast_smdi_mwi_message *mwi_msg;
04232 
04233    if (!ast_strlen_zero(context))
04234       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04235    else
04236       ast_copy_string(ext_context, extension, sizeof(ext_context));
04237 
04238    if (!strcasecmp(externnotify, "smdi")) {
04239       if (ast_app_has_voicemail(ext_context, NULL)) 
04240          ast_smdi_mwi_set(smdi_iface, extension);
04241       else
04242          ast_smdi_mwi_unset(smdi_iface, extension);
04243 
04244       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04245          ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04246          if (!strncmp(mwi_msg->cause, "INV", 3))
04247             ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04248          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04249             ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04250          ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04251          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04252       } else {
04253          if (option_debug)
04254             ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
04255       }
04256    } else if (!ast_strlen_zero(externnotify)) {
04257       if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
04258          ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04259       } else {
04260          snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
04261          if (option_debug)
04262             ast_log(LOG_DEBUG, "Executing %s\n", arguments);
04263          ast_safe_system(arguments);
04264       }
04265    }
04266 }
04267 
04268 struct leave_vm_options {
04269    unsigned int flags;
04270    signed char record_gain;
04271 };
04272 
04273 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04274 {
04275 #ifdef IMAP_STORAGE
04276    int newmsgs, oldmsgs;
04277 #endif
04278    struct vm_state *vms = NULL;
04279    char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
04280    char callerid[256];
04281    FILE *txt;
04282    char date[50];
04283    int txtdes;
04284    int res = 0;
04285    int msgnum;
04286    int duration = 0;
04287    int ausemacro = 0;
04288    int ousemacro = 0;
04289    int ouseexten = 0;
04290    char dir[PATH_MAX], tmpdir[PATH_MAX];
04291    char dest[PATH_MAX];
04292    char fn[PATH_MAX];
04293    char prefile[PATH_MAX] = "";
04294    char tempfile[PATH_MAX] = "";
04295    char ext_context[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2] = "";
04296    char fmt[80];
04297    char *context;
04298    char ecodes[16] = "#";
04299    char tmp[1324] = "", *tmpptr;
04300    struct ast_vm_user *vmu;
04301    struct ast_vm_user svm;
04302    const char *category = NULL;
04303 
04304    if (strlen(ext) > sizeof(tmp) - 1) {
04305       ast_log(LOG_WARNING, "List of extensions is too long (>%ld).  Truncating.\n", (long) sizeof(tmp) - 1);
04306    }
04307    ast_copy_string(tmp, ext, sizeof(tmp));
04308    ext = tmp;
04309    context = strchr(tmp, '@');
04310    if (context) {
04311       *context++ = '\0';
04312       tmpptr = strchr(context, '&');
04313    } else {
04314       tmpptr = strchr(ext, '&');
04315    }
04316 
04317    if (tmpptr)
04318       *tmpptr++ = '\0';
04319 
04320    category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
04321 
04322    if (option_debug > 2)
04323       ast_log(LOG_DEBUG, "Before find_user\n");
04324    if (!(vmu = find_user(&svm, context, ext))) {
04325       ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
04326       if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
04327          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04328       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04329       return res;
04330    }
04331    /* Setup pre-file if appropriate */
04332    if (strcmp(vmu->context, "default"))
04333       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
04334    else
04335       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
04336    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
04337       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
04338       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
04339    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
04340       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
04341       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
04342    }
04343    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
04344    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
04345       ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
04346       return -1;
04347    }
04348    RETRIEVE(tempfile, -1, vmu);
04349    if (ast_fileexists(tempfile, NULL, NULL) > 0)
04350       ast_copy_string(prefile, tempfile, sizeof(prefile));
04351    DISPOSE(tempfile, -1);
04352    /* It's easier just to try to make it than to check for its existence */
04353 #ifndef IMAP_STORAGE
04354    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
04355 #else
04356    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
04357    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
04358       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
04359    }
04360 #endif
04361    create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
04362 
04363    /* Check current or macro-calling context for special extensions */
04364    if (ast_test_flag(vmu, VM_OPERATOR)) {
04365       if (!ast_strlen_zero(vmu->exit)) {
04366          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
04367             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04368             ouseexten = 1;
04369          }
04370       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
04371          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04372          ouseexten = 1;
04373       }
04374       else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
04375       strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04376       ousemacro = 1;
04377       }
04378    }
04379 
04380    if (!ast_strlen_zero(vmu->exit)) {
04381       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
04382          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04383    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
04384       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04385    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
04386       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04387       ausemacro = 1;
04388    }
04389 
04390    /* Play the beginning intro if desired */
04391    if (!ast_strlen_zero(prefile)) {
04392       res = play_greeting(chan, vmu, prefile, ecodes);
04393       if (res == -2) {
04394          /* The file did not exist */
04395          if (option_debug)
04396             ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
04397          res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
04398       }
04399       if (res < 0) {
04400          if (option_debug)
04401             ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
04402          free_user(vmu);
04403          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04404          return -1;
04405       }
04406    }
04407    if (res == '#') {
04408       /* On a '#' we skip the instructions */
04409       ast_set_flag(options, OPT_SILENT);
04410       res = 0;
04411    }
04412    if (!res && !ast_test_flag(options, OPT_SILENT)) {
04413       res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
04414       if (res == '#') {
04415          ast_set_flag(options, OPT_SILENT);
04416          res = 0;
04417       }
04418    }
04419    if (res > 0)
04420       ast_stopstream(chan);
04421    /* Check for a '*' here in case the caller wants to escape from voicemail to something
04422     other than the operator -- an automated attendant or mailbox login for example */
04423    if (res == '*') {
04424       chan->exten[0] = 'a';
04425       chan->exten[1] = '\0';
04426       if (!ast_strlen_zero(vmu->exit)) {
04427          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04428       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
04429          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04430       }
04431       chan->priority = 0;
04432       free_user(vmu);
04433       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04434       return 0;
04435    }
04436 
04437    /* Check for a '0' here */
04438    if (res == '0') {
04439    transfer:
04440       if (ouseexten || ousemacro) {
04441          chan->exten[0] = 'o';
04442          chan->exten[1] = '\0';
04443          if (!ast_strlen_zero(vmu->exit)) {
04444             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04445          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
04446             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04447          }
04448          ast_play_and_wait(chan, "transfer");
04449          chan->priority = 0;
04450          free_user(vmu);
04451          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04452       }
04453       return OPERATOR_EXIT;
04454    }
04455    if (res < 0) {
04456       free_user(vmu);
04457       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04458       return -1;
04459    }
04460    /* The meat of recording the message...  All the announcements and beeps have been played*/
04461    ast_copy_string(fmt, vmfmts, sizeof(fmt));
04462    if (!ast_strlen_zero(fmt)) {
04463       msgnum = 0;
04464 
04465 #ifdef IMAP_STORAGE
04466       /* Is ext a mailbox? */
04467       /* must open stream for this user to get info! */
04468       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
04469       if (res < 0) {
04470          ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
04471          return -1;
04472       }
04473       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
04474       /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
04475        * rarely be used*/
04476          if (!(vms = create_vm_state_from_user(vmu))) {
04477             ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
04478             return -1;
04479          }
04480       }
04481       vms->newmessages++;
04482       /* here is a big difference! We add one to it later */
04483       msgnum = newmsgs + oldmsgs;
04484       if (option_debug > 2)
04485          ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
04486       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
04487       /* set variable for compatability */
04488       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04489 
04490       if (imap_check_limits(chan, vms, vmu, msgnum)) {
04491          goto leave_vm_out;
04492       }
04493 #else
04494       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
04495          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04496          if (!res)
04497             res = ast_waitstream(chan, "");
04498          ast_log(LOG_WARNING, "No more messages possible\n");
04499          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04500          inprocess_count(vmu->mailbox, vmu->context, -1);
04501          goto leave_vm_out;
04502       }
04503 
04504 #endif
04505       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
04506       txtdes = mkstemp(tmptxtfile);
04507       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
04508       if (txtdes < 0) {
04509          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04510          if (!res)
04511             res = ast_waitstream(chan, "");
04512          ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
04513          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04514          inprocess_count(vmu->mailbox, vmu->context, -1);
04515          goto leave_vm_out;
04516       }
04517 
04518       /* Now play the beep once we have the message number for our next message. */
04519       if (res >= 0) {
04520          /* Unless we're *really* silent, try to send the beep */
04521          res = ast_stream_and_wait(chan, "beep", chan->language, "");
04522       }
04523             
04524       /* Store information */
04525       txt = fdopen(txtdes, "w+");
04526       if (txt) {
04527          get_date(date, sizeof(date));
04528          fprintf(txt, 
04529             ";\n"
04530             "; Message Information file\n"
04531             ";\n"
04532             "[message]\n"
04533             "origmailbox=%s\n"
04534             "context=%s\n"
04535             "macrocontext=%s\n"
04536             "exten=%s\n"
04537             "priority=%d\n"
04538             "callerchan=%s\n"
04539             "callerid=%s\n"
04540             "origdate=%s\n"
04541             "origtime=%ld\n"
04542             "category=%s\n",
04543             ext,
04544             chan->context,
04545             chan->macrocontext, 
04546             chan->exten,
04547             chan->priority,
04548             chan->name,
04549             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
04550             date, (long)time(NULL),
04551             category ? category : ""); 
04552       } else
04553          ast_log(LOG_WARNING, "Error opening text file for output\n");
04554       res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
04555 
04556       if (txt) {
04557          if (duration < vmminmessage) {
04558             fclose(txt);
04559             if (option_verbose > 2) 
04560                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
04561             ast_filedelete(tmptxtfile, NULL);
04562             unlink(tmptxtfile);
04563             inprocess_count(vmu->mailbox, vmu->context, -1);
04564          } else {
04565             fprintf(txt, "duration=%d\n", duration);
04566             fclose(txt);
04567             if (vm_lock_path(dir)) {
04568                ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
04569                /* Delete files */
04570                ast_filedelete(tmptxtfile, NULL);
04571                unlink(tmptxtfile);
04572                inprocess_count(vmu->mailbox, vmu->context, -1);
04573             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
04574                if (option_debug) 
04575                   ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
04576                unlink(tmptxtfile);
04577                ast_unlock_path(dir);
04578                inprocess_count(vmu->mailbox, vmu->context, -1);
04579             } else {
04580 #ifndef IMAP_STORAGE
04581                msgnum = last_message_index(vmu, dir) + 1;
04582 #endif
04583                make_file(fn, sizeof(fn), dir, msgnum);
04584 
04585                /* assign a variable with the name of the voicemail file */ 
04586 #ifndef IMAP_STORAGE
04587                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
04588 #else
04589                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04590 #endif
04591 
04592                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
04593                ast_filerename(tmptxtfile, fn, NULL);
04594                rename(tmptxtfile, txtfile);
04595                inprocess_count(vmu->mailbox, vmu->context, -1);
04596 
04597                ast_unlock_path(dir);
04598                /* We must store the file first, before copying the message, because
04599                 * ODBC storage does the entire copy with SQL.
04600                 */
04601                if (ast_fileexists(fn, NULL, NULL) > 0) {
04602                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
04603                }
04604 
04605                /* Are there to be more recipients of this message? */
04606                while (tmpptr) {
04607                   struct ast_vm_user recipu, *recip;
04608                   char *exten, *context;
04609                
04610                   exten = strsep(&tmpptr, "&");
04611                   context = strchr(exten, '@');
04612                   if (context) {
04613                      *context = '\0';
04614                      context++;
04615                   }
04616                   if ((recip = find_user(&recipu, context, exten))) {
04617                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
04618                      free_user(recip);
04619                   }
04620                }
04621                /* Notification and disposal needs to happen after the copy, though. */
04622                if (ast_fileexists(fn, NULL, NULL)) {
04623                   notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04624                   DISPOSE(dir, msgnum);
04625                }
04626             }
04627          }
04628       } else {
04629          inprocess_count(vmu->mailbox, vmu->context, -1);
04630       }
04631       if (res == '0') {
04632          goto transfer;
04633       } else if (res > 0 && res != 't')
04634          res = 0;
04635 
04636       if (duration < vmminmessage)
04637          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
04638          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04639       else
04640          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
04641    } else
04642       ast_log(LOG_WARNING, "No format for saving voicemail?\n");
04643 leave_vm_out:
04644    free_user(vmu);
04645    
04646    return res;
04647 }
04648 
04649 #if !defined(IMAP_STORAGE)
04650 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
04651 {
04652    /* we know the actual number of messages, so stop process when number is hit */
04653 
04654    int x,dest;
04655    char sfn[PATH_MAX];
04656    char dfn[PATH_MAX];
04657 
04658    if (vm_lock_path(dir))
04659       return ERROR_LOCK_PATH;
04660 
04661    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
04662       make_file(sfn, sizeof(sfn), dir, x);
04663       if (EXISTS(dir, x, sfn, NULL)) {
04664          
04665          if (x != dest) {
04666             make_file(dfn, sizeof(dfn), dir, dest);
04667             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
04668          }
04669          
04670          dest++;
04671       }
04672    }
04673    ast_unlock_path(dir);
04674 
04675    return dest;
04676 }
04677 #endif
04678 
04679 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
04680 {
04681    int d;
04682    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
04683    return d;
04684 }
04685 
04686 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
04687 {
04688 #ifdef IMAP_STORAGE
04689    /* we must use mbox(x) folder names, and copy the message there */
04690    /* simple. huh? */
04691    char sequence[10];
04692    /* get the real IMAP message number for this message */
04693    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
04694    if (option_debug > 2)
04695       ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
04696    ast_mutex_lock(&vms->lock);
04697    if (box == 1) {
04698       mail_setflag(vms->mailstream, sequence, "\\Seen");
04699    } else if (box == 0) {
04700       mail_clearflag(vms->mailstream, sequence, "\\Seen");
04701    }
04702    if (!strcasecmp(mbox(0), vms->curbox) && (box == 0 || box == 1)) {
04703       ast_mutex_unlock(&vms->lock);
04704       return 0;
04705    } else {
04706       int res = !mail_copy(vms->mailstream,sequence,(char *) mbox(box)); 
04707       ast_mutex_unlock(&vms->lock);
04708       return res;
04709    }
04710 #else
04711    char *dir = vms->curdir;
04712    char *username = vms->username;
04713    char *context = vmu->context;
04714    char sfn[PATH_MAX];
04715    char dfn[PATH_MAX];
04716    char ddir[PATH_MAX];
04717    const char *dbox = mbox(box);
04718    int x;
04719    make_file(sfn, sizeof(sfn), dir, msg);
04720    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
04721 
04722    if (vm_lock_path(ddir))
04723       return ERROR_LOCK_PATH;
04724 
04725    for (x = 0; x < vmu->maxmsg; x++) {
04726       make_file(dfn, sizeof(dfn), ddir, x);
04727       if (!EXISTS(ddir, x, dfn, NULL))
04728          break;
04729    }
04730    if (x >= vmu->maxmsg) {
04731       ast_unlock_path(ddir);
04732       return ERROR_MAILBOX_FULL;
04733    }
04734    if (strcmp(sfn, dfn)) {
04735       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
04736    }
04737    ast_unlock_path(ddir);
04738 #endif
04739    return 0;
04740 }
04741 
04742 static int adsi_logo(unsigned char *buf)
04743 {
04744    int bytes = 0;
04745    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
04746    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
04747    return bytes;
04748 }
04749 
04750 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
04751 {
04752    unsigned char buf[256];
04753    int bytes=0;
04754    int x;
04755    char num[5];
04756 
04757    *useadsi = 0;
04758    bytes += ast_adsi_data_mode(buf + bytes);
04759    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04760 
04761    bytes = 0;
04762    bytes += adsi_logo(buf);
04763    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04764 #ifdef DISPLAY
04765    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
04766 #endif
04767    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04768    bytes += ast_adsi_data_mode(buf + bytes);
04769    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04770 
04771    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
04772       bytes = 0;
04773       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
04774       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04775       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04776       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04777       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04778       return 0;
04779    }
04780 
04781 #ifdef DISPLAY
04782    /* Add a dot */
04783    bytes = 0;
04784    bytes += ast_adsi_logo(buf);
04785    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04786    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
04787    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04788    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04789 #endif
04790    bytes = 0;
04791    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
04792    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
04793    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
04794    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
04795    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
04796    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
04797    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04798 
04799 #ifdef DISPLAY
04800    /* Add another dot */
04801    bytes = 0;
04802    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
04803    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04804 
04805    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04806    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04807 #endif
04808 
04809    bytes = 0;
04810    /* These buttons we load but don't use yet */
04811    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
04812    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
04813    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
04814    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
04815    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
04816    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
04817    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04818 
04819 #ifdef DISPLAY
04820    /* Add another dot */
04821    bytes = 0;
04822    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
04823    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04824    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04825 #endif
04826 
04827    bytes = 0;
04828    for (x=0;x<5;x++) {
04829       snprintf(num, sizeof(num), "%d", x);
04830       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
04831    }
04832    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
04833    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04834 
04835 #ifdef DISPLAY
04836    /* Add another dot */
04837    bytes = 0;
04838    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
04839    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04840    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04841 #endif
04842 
04843    if (ast_adsi_end_download(chan)) {
04844       bytes = 0;
04845       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
04846       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04847       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04848       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04849       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04850       return 0;
04851    }
04852    bytes = 0;
04853    bytes += ast_adsi_download_disconnect(buf + bytes);
04854    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04855    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04856 
04857    if (option_debug)
04858       ast_log(LOG_DEBUG, "Done downloading scripts...\n");
04859 
04860 #ifdef DISPLAY
04861    /* Add last dot */
04862    bytes = 0;
04863    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
04864    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04865 #endif
04866    if (option_debug)
04867       ast_log(LOG_DEBUG, "Restarting session...\n");
04868 
04869    bytes = 0;
04870    /* Load the session now */
04871    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
04872       *useadsi = 1;
04873       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
04874    } else
04875       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
04876 
04877    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04878    return 0;
04879 }
04880 
04881 static void adsi_begin(struct ast_channel *chan, int *useadsi)
04882 {
04883    int x;
04884    if (!ast_adsi_available(chan))
04885       return;
04886    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
04887    if (x < 0)
04888       return;
04889    if (!x) {
04890       if (adsi_load_vmail(chan, useadsi)) {
04891          ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
04892          return;
04893       }
04894    } else
04895       *useadsi = 1;
04896 }
04897 
04898 static void adsi_login(struct ast_channel *chan)
04899 {
04900    unsigned char buf[256];
04901    int bytes=0;
04902    unsigned char keys[8];
04903    int x;
04904    if (!ast_adsi_available(chan))
04905       return;
04906 
04907    for (x=0;x<8;x++)
04908       keys[x] = 0;
04909    /* Set one key for next */
04910    keys[3] = ADSI_KEY_APPS + 3;
04911 
04912    bytes += adsi_logo(buf + bytes);
04913    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
04914    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
04915    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04916    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
04917    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
04918    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
04919    bytes += ast_adsi_set_keys(buf + bytes, keys);
04920    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04921    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04922 }
04923 
04924 static void adsi_password(struct ast_channel *chan)
04925 {
04926    unsigned char buf[256];
04927    int bytes=0;
04928    unsigned char keys[8];
04929    int x;
04930    if (!ast_adsi_available(chan))
04931       return;
04932 
04933    for (x=0;x<8;x++)
04934       keys[x] = 0;
04935    /* Set one key for next */
04936    keys[3] = ADSI_KEY_APPS + 3;
04937 
04938    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04939    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
04940    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
04941    bytes += ast_adsi_set_keys(buf + bytes, keys);
04942    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04943    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04944 }
04945 
04946 static void adsi_folders(struct ast_channel *chan, int start, char *label)
04947 {
04948    unsigned char buf[256];
04949    int bytes=0;
04950    unsigned char keys[8];
04951    int x,y;
04952 
04953    if (!ast_adsi_available(chan))
04954       return;
04955 
04956    for (x=0;x<5;x++) {
04957       y = ADSI_KEY_APPS + 12 + start + x;
04958       if (y > ADSI_KEY_APPS + 12 + 4)
04959          y = 0;
04960       keys[x] = ADSI_KEY_SKT | y;
04961    }
04962    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
04963    keys[6] = 0;
04964    keys[7] = 0;
04965 
04966    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
04967    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
04968    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04969    bytes += ast_adsi_set_keys(buf + bytes, keys);
04970    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04971 
04972    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04973 }
04974 
04975 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
04976 {
04977    int bytes=0;
04978    unsigned char buf[256]; 
04979    char buf1[256], buf2[256];
04980    char fn2[PATH_MAX];
04981 
04982    char cid[256]="";
04983    char *val;
04984    char *name, *num;
04985    char datetime[21]="";
04986    FILE *f;
04987 
04988    unsigned char keys[8];
04989 
04990    int x;
04991 
04992    if (!ast_adsi_available(chan))
04993       return;
04994 
04995    /* Retrieve important info */
04996    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
04997    f = fopen(fn2, "r");
04998    if (f) {
04999       while (!feof(f)) {   
05000          if (!fgets((char *)buf, sizeof(buf), f)) {
05001             continue;
05002          }
05003          if (!feof(f)) {
05004             char *stringp=NULL;
05005             stringp = (char *)buf;
05006             strsep(&stringp, "=");
05007             val = strsep(&stringp, "=");
05008             if (!ast_strlen_zero(val)) {
05009                if (!strcmp((char *)buf, "callerid"))
05010                   ast_copy_string(cid, val, sizeof(cid));
05011                if (!strcmp((char *)buf, "origdate"))
05012                   ast_copy_string(datetime, val, sizeof(datetime));
05013             }
05014          }
05015       }
05016       fclose(f);
05017    }
05018    /* New meaning for keys */
05019    for (x=0;x<5;x++)
05020       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05021    keys[6] = 0x0;
05022    keys[7] = 0x0;
05023 
05024    if (!vms->curmsg) {
05025       /* No prev key, provide "Folder" instead */
05026       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05027    }
05028    if (vms->curmsg >= vms->lastmsg) {
05029       /* If last message ... */
05030       if (vms->curmsg) {
05031          /* but not only message, provide "Folder" instead */
05032          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05033          bytes += ast_adsi_voice_mode(buf + bytes, 0);
05034 
05035       } else {
05036          /* Otherwise if only message, leave blank */
05037          keys[3] = 1;
05038       }
05039    }
05040 
05041    if (!ast_strlen_zero(cid)) {
05042       ast_callerid_parse(cid, &name, &num);
05043       if (!name)
05044          name = num;
05045    } else
05046       name = "Unknown Caller";
05047 
05048    /* If deleted, show "undeleted" */
05049 
05050    if (vms->deleted[vms->curmsg])
05051       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05052 
05053    /* Except "Exit" */
05054    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05055    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05056       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05057    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05058 
05059    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05060    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05061    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05062    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05063    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05064    bytes += ast_adsi_set_keys(buf + bytes, keys);
05065    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05066 
05067    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05068 }
05069 
05070 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05071 {
05072    int bytes=0;
05073    unsigned char buf[256];
05074    unsigned char keys[8];
05075 
05076    int x;
05077 
05078    if (!ast_adsi_available(chan))
05079       return;
05080 
05081    /* New meaning for keys */
05082    for (x=0;x<5;x++)
05083       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05084 
05085    keys[6] = 0x0;
05086    keys[7] = 0x0;
05087 
05088    if (!vms->curmsg) {
05089       /* No prev key, provide "Folder" instead */
05090       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05091    }
05092    if (vms->curmsg >= vms->lastmsg) {
05093       /* If last message ... */
05094       if (vms->curmsg) {
05095          /* but not only message, provide "Folder" instead */
05096          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05097       } else {
05098          /* Otherwise if only message, leave blank */
05099          keys[3] = 1;
05100       }
05101    }
05102 
05103    /* If deleted, show "undeleted" */
05104    if (vms->deleted[vms->curmsg]) 
05105       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05106 
05107    /* Except "Exit" */
05108    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05109    bytes += ast_adsi_set_keys(buf + bytes, keys);
05110    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05111 
05112    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05113 }
05114 
05115 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05116 {
05117    unsigned char buf[256] = "";
05118    char buf1[256] = "", buf2[256] = "";
05119    int bytes=0;
05120    unsigned char keys[8];
05121    int x;
05122 
05123    char *newm = (vms->newmessages == 1) ? "message" : "messages";
05124    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05125    if (!ast_adsi_available(chan))
05126       return;
05127    if (vms->newmessages) {
05128       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05129       if (vms->oldmessages) {
05130          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05131          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05132       } else {
05133          snprintf(buf2, sizeof(buf2), "%s.", newm);
05134       }
05135    } else if (vms->oldmessages) {
05136       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05137       snprintf(buf2, sizeof(buf2), "%s.", oldm);
05138    } else {
05139       strcpy(buf1, "You have no messages.");
05140       buf2[0] = ' ';
05141       buf2[1] = '\0';
05142    }
05143    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05144    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05145    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05146 
05147    for (x=0;x<6;x++)
05148       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05149    keys[6] = 0;
05150    keys[7] = 0;
05151 
05152    /* Don't let them listen if there are none */
05153    if (vms->lastmsg < 0)
05154       keys[0] = 1;
05155    bytes += ast_adsi_set_keys(buf + bytes, keys);
05156 
05157    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05158 
05159    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05160 }
05161 
05162 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05163 {
05164    unsigned char buf[256] = "";
05165    char buf1[256] = "", buf2[256] = "";
05166    int bytes=0;
05167    unsigned char keys[8];
05168    int x;
05169 
05170    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05171 
05172    if (!ast_adsi_available(chan))
05173       return;
05174 
05175    /* Original command keys */
05176    for (x=0;x<6;x++)
05177       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05178 
05179    keys[6] = 0;
05180    keys[7] = 0;
05181 
05182    if ((vms->lastmsg + 1) < 1)
05183       keys[0] = 0;
05184 
05185    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05186       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05187 
05188    if (vms->lastmsg + 1)
05189       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05190    else
05191       strcpy(buf2, "no messages.");
05192    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05193    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05194    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05195    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05196    bytes += ast_adsi_set_keys(buf + bytes, keys);
05197 
05198    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05199 
05200    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05201    
05202 }
05203 
05204 /*
05205 static void adsi_clear(struct ast_channel *chan)
05206 {
05207    char buf[256];
05208    int bytes=0;
05209    if (!ast_adsi_available(chan))
05210       return;
05211    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05212    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05213 
05214    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05215 }
05216 */
05217 
05218 static void adsi_goodbye(struct ast_channel *chan)
05219 {
05220    unsigned char buf[256];
05221    int bytes=0;
05222 
05223    if (!ast_adsi_available(chan))
05224       return;
05225    bytes += adsi_logo(buf + bytes);
05226    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
05227    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
05228    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05229    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05230 
05231    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05232 }
05233 
05234 /*--- get_folder: Folder menu ---*/
05235 /* Plays "press 1 for INBOX messages" etc
05236    Should possibly be internationalized
05237  */
05238 static int get_folder(struct ast_channel *chan, int start)
05239 {
05240    int x;
05241    int d;
05242    char fn[PATH_MAX];
05243    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
05244    if (d)
05245       return d;
05246    for (x = start; x< 5; x++) {  /* For all folders */
05247       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
05248          return d;
05249       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
05250       if (d)
05251          return d;
05252       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
05253       d = vm_play_folder_name(chan, fn);
05254       if (d)
05255          return d;
05256       d = ast_waitfordigit(chan, 500);
05257       if (d)
05258          return d;
05259    }
05260    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
05261    if (d)
05262       return d;
05263    d = ast_waitfordigit(chan, 4000);
05264    return d;
05265 }
05266 
05267 static int get_folder2(struct ast_channel *chan, char *fn, int start)
05268 {
05269    int res = 0;
05270    int loops = 0;
05271    res = ast_play_and_wait(chan, fn);  /* Folder name */
05272    while (((res < '0') || (res > '9')) &&
05273          (res != '#') && (res >= 0) &&
05274          loops < 4) {
05275       res = get_folder(chan, 0);
05276       loops++;
05277    }
05278    if (loops == 4) { /* give up */
05279       return '#';
05280    }
05281    return res;
05282 }
05283 
05284 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
05285               char *context, signed char record_gain, long *duration, struct vm_state *vms)
05286 {
05287    int cmd = 0;
05288    int retries = 0, prepend_duration = 0, already_recorded = 0;
05289    signed char zero_gain = 0;
05290    struct ast_config *msg_cfg;
05291    const char *duration_cstr;
05292    char msgfile[PATH_MAX], backup[PATH_MAX];
05293    char textfile[PATH_MAX];
05294    char backup_textfile[PATH_MAX];
05295    struct ast_category *msg_cat;
05296    char duration_str[12] = "";
05297 
05298    ast_log(LOG_NOTICE, "curdir=%s\n", curdir);
05299    /* Must always populate duration correctly */
05300    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
05301    strcpy(textfile, msgfile);
05302    strcpy(backup, msgfile);
05303    strcpy(backup_textfile, msgfile);
05304    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05305    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05306    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05307 
05308    if (!(msg_cfg = ast_config_load(textfile))) {
05309       return -1;
05310    }
05311 
05312    *duration = 0;
05313    if ((duration_cstr = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
05314       *duration = atoi(duration_cstr);
05315    }
05316 
05317    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
05318       if (cmd)
05319          retries = 0;
05320       switch (cmd) {
05321       case '1': 
05322          /* prepend a message to the current message, update the metadata and return */
05323       {
05324          prepend_duration = 0;
05325 
05326          /* Back up the original file, so we can retry the prepend */
05327 #ifndef IMAP_STORAGE
05328          if (already_recorded) {
05329             ast_filecopy(backup, msgfile, NULL);
05330             copy(textfile, backup_textfile);
05331          } else {
05332             ast_filecopy(msgfile, backup, NULL);
05333             copy(textfile, backup_textfile);
05334          }
05335 #endif
05336          already_recorded = 1;
05337 
05338          if (record_gain)
05339             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
05340 
05341          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
05342          if (cmd == 'S') {
05343             ast_filerename(backup, msgfile, NULL);
05344          }
05345 
05346          if (record_gain)
05347             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
05348 
05349          if (prepend_duration) {
05350             prepend_duration += *duration;
05351          }
05352 
05353          break;
05354       }
05355       case '2': 
05356          cmd = 't';
05357          break;
05358       case '*':
05359          cmd = '*';
05360          break;
05361       default: 
05362          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
05363             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
05364          if (!cmd)
05365             cmd = ast_play_and_wait(chan,"vm-starmain");
05366             /* "press star to return to the main menu" */
05367          if (!cmd)
05368             cmd = ast_waitfordigit(chan,6000);
05369          if (!cmd)
05370             retries++;
05371          if (retries > 3)
05372             cmd = 't';
05373       }
05374    }
05375 
05376    if (already_recorded && cmd == -1) {
05377       /* Restore original files, if operation cancelled */
05378       ast_filerename(backup, msgfile, NULL);
05379       if (duration_cstr) {
05380          ast_copy_string(duration_str, duration_cstr, sizeof(duration_str));
05381       }
05382    } else if (prepend_duration) {
05383       *duration = prepend_duration;
05384       snprintf(duration_str, sizeof(duration_str), "%d", prepend_duration);
05385    }
05386 
05387    msg_cat = ast_category_get(msg_cfg, "message");
05388    if (!ast_strlen_zero(duration_str) && !ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
05389       config_text_file_save(textfile, msg_cfg, "app_voicemail");
05390    }
05391    ast_config_destroy(msg_cfg);
05392 
05393    if (cmd == 't' || cmd == 'S')
05394       cmd = 0;
05395    return cmd;
05396 }
05397 
05398 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
05399 {
05400    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
05401    int newmsgs = 0, oldmsgs = 0;
05402    const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
05403 
05404 #ifndef IMAP_STORAGE
05405    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
05406 #else
05407    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
05408 #endif
05409    make_file(fn, sizeof(fn), todir, msgnum);
05410    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
05411 
05412    if (!ast_strlen_zero(vmu->attachfmt)) {
05413       if (strstr(fmt, vmu->attachfmt)) {
05414          fmt = vmu->attachfmt;
05415       } else {
05416          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);
05417       }
05418    }
05419 
05420    /* Attach only the first format */
05421    fmt = ast_strdupa(fmt);
05422    stringp = fmt;
05423    strsep(&stringp, "|");
05424 
05425    if (!ast_strlen_zero(vmu->email)) {
05426       int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
05427       char *myserveremail = serveremail;
05428       attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
05429       if (!ast_strlen_zero(vmu->serveremail))
05430          myserveremail = vmu->serveremail;
05431       
05432       if (attach_user_voicemail)
05433          RETRIEVE(todir, msgnum, vmu);
05434 
05435       /*XXX possible imap issue, should category be NULL XXX*/
05436       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
05437 
05438       if (attach_user_voicemail)
05439          DISPOSE(todir, msgnum);
05440    }
05441 
05442    if (!ast_strlen_zero(vmu->pager)) {
05443       char *myserveremail = serveremail;
05444       if (!ast_strlen_zero(vmu->serveremail))
05445          myserveremail = vmu->serveremail;
05446       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category);
05447    }
05448 
05449    if (ast_test_flag(vmu, VM_DELETE)) {
05450       DELETE(todir, msgnum, fn, vmu);
05451    }
05452 
05453    /* Leave voicemail for someone */
05454    if (ast_app_has_voicemail(ext_context, NULL)) {
05455       ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
05456    }
05457    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);
05458    run_externnotify(vmu->context, vmu->mailbox);
05459    return 0;
05460 }
05461 
05462 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)
05463 {
05464 #ifdef IMAP_STORAGE
05465    int todircount=0;
05466    struct vm_state *dstvms;
05467 #else
05468    char textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
05469 #endif
05470    char username[70]="";
05471    int res = 0, cmd = 0;
05472    struct ast_vm_user *receiver = NULL, *vmtmp;
05473    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
05474    char *stringp;
05475    const char *s;
05476    int saved_messages = 0;
05477    int valid_extensions = 0;
05478    char *dir;
05479    int curmsg;
05480    int prompt_played = 0;
05481 
05482    if (vms == NULL) return -1;
05483    dir = vms->curdir;
05484    curmsg = vms->curmsg;
05485    
05486    while (!res && !valid_extensions) {
05487       int use_directory = 0;
05488       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
05489          int done = 0;
05490          int retries = 0;
05491          cmd=0;
05492          while ((cmd >= 0) && !done ){
05493             if (cmd)
05494                retries = 0;
05495             switch (cmd) {
05496             case '1': 
05497                use_directory = 0;
05498                done = 1;
05499                break;
05500             case '2': 
05501                use_directory = 1;
05502                done=1;
05503                break;
05504             case '*': 
05505                cmd = 't';
05506                done = 1;
05507                break;
05508             default: 
05509                /* Press 1 to enter an extension press 2 to use the directory */
05510                cmd = ast_play_and_wait(chan,"vm-forward");
05511                if (!cmd)
05512                   cmd = ast_waitfordigit(chan,3000);
05513                if (!cmd)
05514                   retries++;
05515                if (retries > 3)
05516                {
05517                   cmd = 't';
05518                   done = 1;
05519                }
05520                
05521             }
05522          }
05523          if (cmd < 0 || cmd == 't')
05524             break;
05525       }
05526       
05527       if (use_directory) {
05528          /* use app_directory */
05529          
05530          char old_context[sizeof(chan->context)];
05531          char old_exten[sizeof(chan->exten)];
05532          int old_priority;
05533          struct ast_app* app;
05534 
05535          
05536          app = pbx_findapp("Directory");
05537          if (app) {
05538             char vmcontext[256];
05539             /* make backup copies */
05540             memcpy(old_context, chan->context, sizeof(chan->context));
05541             memcpy(old_exten, chan->exten, sizeof(chan->exten));
05542             old_priority = chan->priority;
05543             
05544             /* call the the Directory, changes the channel */
05545             snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
05546             res = pbx_exec(chan, app, vmcontext);
05547             
05548             ast_copy_string(username, chan->exten, sizeof(username));
05549             
05550             /* restore the old context, exten, and priority */
05551             memcpy(chan->context, old_context, sizeof(chan->context));
05552             memcpy(chan->exten, old_exten, sizeof(chan->exten));
05553             chan->priority = old_priority;
05554             
05555          } else {
05556             ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
05557             ast_clear_flag((&globalflags), VM_DIRECFORWARD);   
05558          }
05559       } else {
05560          /* Ask for an extension */
05561          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
05562          prompt_played++;
05563          if (res || prompt_played > 4)
05564             break;
05565          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
05566             break;
05567       }
05568       
05569       /* start all over if no username */
05570       if (ast_strlen_zero(username))
05571          continue;
05572       stringp = username;
05573       s = strsep(&stringp, "*");
05574       /* start optimistic */
05575       valid_extensions = 1;
05576       while (s) {
05577          /* 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 */
05578          if ((is_new_message == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
05579             int oldmsgs;
05580             int newmsgs;
05581             int capacity;
05582             if (inboxcount(s, &newmsgs, &oldmsgs)) {
05583                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
05584                /* Shouldn't happen, but allow trying another extension if it does */
05585                res = ast_play_and_wait(chan, "pbx-invalid");
05586                valid_extensions = 0;
05587                break;
05588             }
05589             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
05590             if ((newmsgs + oldmsgs) >= capacity) {
05591                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
05592                res = ast_play_and_wait(chan, "vm-mailboxfull");
05593                valid_extensions = 0;
05594                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05595                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05596                   free_user(vmtmp);
05597                }
05598                inprocess_count(receiver->mailbox, receiver->context, -1);
05599                break;
05600             }
05601             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
05602          } else {
05603             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
05604             /* "I am sorry, that's not a valid extension.  Please try again." */
05605             res = ast_play_and_wait(chan, "pbx-invalid");
05606             valid_extensions = 0;
05607             break;
05608          }
05609          s = strsep(&stringp, "*");
05610       }
05611       /* break from the loop of reading the extensions */
05612       if (valid_extensions)
05613          break;
05614    }
05615    /* check if we're clear to proceed */
05616    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
05617       return res;
05618    if (is_new_message == 1) {
05619       struct leave_vm_options leave_options;
05620       char mailbox[AST_MAX_EXTENSION * 2 + 2];
05621       /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
05622       if (context)
05623          snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
05624       else
05625          ast_copy_string(mailbox, username, sizeof(mailbox));
05626 
05627       /* Send VoiceMail */
05628       memset(&leave_options, 0, sizeof(leave_options));
05629       leave_options.record_gain = record_gain;
05630       cmd = leave_voicemail(chan, mailbox, &leave_options);
05631    } else {
05632       /* Forward VoiceMail */
05633       long duration = 0;
05634       struct vm_state vmstmp;
05635 #ifndef IMAP_STORAGE
05636       char msgfile[PATH_MAX];
05637 #endif
05638       int copy_msg_result = 0;
05639 
05640       memcpy(&vmstmp, vms, sizeof(vmstmp));
05641 
05642       RETRIEVE(dir, curmsg, sender);
05643 
05644       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
05645       if (!cmd) {
05646          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
05647 #ifdef IMAP_STORAGE
05648             char *myserveremail;
05649             int attach_user_voicemail;
05650             /* get destination mailbox */
05651             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
05652             if (!dstvms) {
05653                dstvms = create_vm_state_from_user(vmtmp);
05654             }
05655             if (dstvms) {
05656                init_mailstream(dstvms, 0);
05657                if (!dstvms->mailstream) {
05658                   ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
05659                } else {
05660                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
05661                   run_externnotify(vmtmp->context, vmtmp->mailbox); 
05662                }
05663             } else {
05664                ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
05665             }
05666             myserveremail = serveremail;
05667             if (!ast_strlen_zero(vmtmp->serveremail))
05668                myserveremail = vmtmp->serveremail;
05669             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
05670             /* NULL category for IMAP storage */
05671             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);
05672 #else
05673             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
05674 #endif
05675             saved_messages++;
05676             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05677             AST_LIST_REMOVE_CURRENT(&extensions, list);
05678             free_user(vmtmp);
05679             if (res)
05680                break;
05681          }
05682          AST_LIST_TRAVERSE_SAFE_END;
05683          if (saved_messages > 0 && !copy_msg_result) {
05684             /* give confirmation that the message was saved */
05685             /* commented out since we can't forward batches yet
05686             if (saved_messages == 1)
05687                res = ast_play_and_wait(chan, "vm-message");
05688             else
05689                res = ast_play_and_wait(chan, "vm-messages");
05690             if (!res)
05691                res = ast_play_and_wait(chan, "vm-saved"); */
05692             res = ast_play_and_wait(chan, "vm-msgsaved");
05693          }
05694 #ifndef IMAP_STORAGE
05695          else {
05696             /* with IMAP, mailbox full warning played by imap_check_limits */
05697             res = ast_play_and_wait(chan, "vm-mailboxfull");
05698          }
05699          /* Restore original message without prepended message if backup exists */
05700          make_file(msgfile, sizeof(msgfile), dir, curmsg);
05701          strcpy(textfile, msgfile);
05702          strcpy(backup, msgfile);
05703          strcpy(backup_textfile, msgfile);
05704          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05705          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05706          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05707          if (ast_fileexists(backup, NULL, NULL) > 0) {
05708             ast_filerename(backup, msgfile, NULL);
05709             rename(backup_textfile, textfile);
05710          }
05711 #endif
05712       }
05713       DISPOSE(dir, curmsg);
05714 #ifndef IMAP_STORAGE
05715       if (cmd) { /* assuming hangup, cleanup backup file */
05716          make_file(msgfile, sizeof(msgfile), dir, curmsg);
05717          strcpy(textfile, msgfile);
05718          strcpy(backup_textfile, msgfile);
05719          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05720          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05721          rename(backup_textfile, textfile);
05722       }
05723 #endif
05724    }
05725 
05726    /* If anything failed above, we still have this list to free */
05727    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05728       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05729       free_user(vmtmp);
05730    }
05731    return res ? res : cmd;
05732 }
05733 
05734 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
05735 {
05736    int res;
05737    if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0) 
05738       ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
05739    return res;
05740 }
05741 
05742 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
05743 {
05744    return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
05745 }
05746 
05747 static int play_message_category(struct ast_channel *chan, const char *category)
05748 {
05749    int res = 0;
05750 
05751    if (!ast_strlen_zero(category))
05752       res = ast_play_and_wait(chan, category);
05753 
05754    if (res) {
05755       ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
05756       res = 0;
05757    }
05758 
05759    return res;
05760 }
05761 
05762 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
05763 {
05764    int res = 0;
05765    struct vm_zone *the_zone = NULL;
05766    time_t t;
05767 
05768    if (ast_get_time_t(origtime, &t, 0, NULL)) {
05769       ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
05770       return 0;
05771    }
05772 
05773    /* Does this user have a timezone specified? */
05774    if (!ast_strlen_zero(vmu->zonetag)) {
05775       /* Find the zone in the list */
05776       struct vm_zone *z;
05777       AST_LIST_LOCK(&zones);
05778       AST_LIST_TRAVERSE(&zones, z, list) {
05779          if (!strcmp(z->name, vmu->zonetag)) {
05780             the_zone = z;
05781             break;
05782          }
05783       }
05784       AST_LIST_UNLOCK(&zones);
05785    }
05786 
05787 /* No internal variable parsing for now, so we'll comment it out for the time being */
05788 #if 0
05789    /* Set the DIFF_* variables */
05790    ast_localtime(&t, &time_now, NULL);
05791    tv_now = ast_tvnow();
05792    tnow = tv_now.tv_sec;
05793    ast_localtime(&tnow, &time_then, NULL);
05794 
05795    /* Day difference */
05796    if (time_now.tm_year == time_then.tm_year)
05797       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
05798    else
05799       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
05800    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
05801 
05802    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
05803 #endif
05804    if (the_zone) {
05805       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
05806    } else if (!strncasecmp(chan->language, "de", 2)) {    /* GERMAN syntax */
05807       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05808    } else if (!strncasecmp(chan->language, "gr", 2)) {    /* GREEK syntax */
05809       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
05810    } else if (!strncasecmp(chan->language, "he", 2)) {    /* HEBREW syntax */
05811       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'at2' kM", NULL);
05812    } else if (!strncasecmp(chan->language, "it", 2)) {    /* ITALIAN syntax */
05813       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);
05814    } else if (!strncasecmp(chan->language, "nl", 2)) {    /* DUTCH syntax */
05815       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
05816    } else if (!strncasecmp(chan->language, "no", 2)) {    /* NORWEGIAN syntax */
05817       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05818    } else if (!strncasecmp(chan->language, "pl", 2)) {    /* POLISH syntax */
05819       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
05820    } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* PORTUGUESE syntax */
05821       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);
05822    } else if (!strncasecmp(chan->language, "se", 2)) {    /* SWEDISH syntax */
05823       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
05824    } else {
05825       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
05826    }
05827 #if 0
05828    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
05829 #endif
05830    return res;
05831 }
05832 
05833 
05834 
05835 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
05836 {
05837    int res = 0;
05838    int i;
05839    char *callerid, *name;
05840    char prefile[PATH_MAX] = "";
05841    
05842 
05843    /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
05844    /* 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 */
05845    if ((cid == NULL)||(context == NULL))
05846       return res;
05847 
05848    /* Strip off caller ID number from name */
05849    if (option_debug > 2)
05850       ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
05851    ast_callerid_parse(cid, &name, &callerid);
05852    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
05853       /* Check for internal contexts and only */
05854       /* say extension when the call didn't come from an internal context in the list */
05855       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
05856          if (option_debug > 2)
05857             ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
05858          if ((strcmp(cidinternalcontexts[i], context) == 0))
05859             break;
05860       }
05861       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
05862          if (!res) {
05863             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
05864             if (!ast_strlen_zero(prefile)) {
05865             /* See if we can find a recorded name for this person instead of their extension number */
05866                if (ast_fileexists(prefile, NULL, NULL) > 0) {
05867                   if (option_verbose > 2)
05868                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
05869                   if (!callback)
05870                      res = wait_file2(chan, vms, "vm-from");
05871                   res = ast_stream_and_wait(chan, prefile, chan->language, "");
05872                } else {
05873                   if (option_verbose > 2)
05874                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
05875                   /* BB: Say "from extension" as one saying to sound smoother */
05876                   if (!callback)
05877                      res = wait_file2(chan, vms, "vm-from-extension");
05878                   res = ast_say_digit_str(chan, callerid, "", chan->language);
05879                }
05880             }
05881          }
05882       }
05883 
05884       else if (!res){
05885          if (option_debug > 2)
05886             ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
05887          /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
05888          if (!callback)
05889             res = wait_file2(chan, vms, "vm-from-phonenumber");
05890          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
05891       }
05892    } else {
05893       /* Number unknown */
05894       if (option_debug)
05895          ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
05896       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
05897       res = wait_file2(chan, vms, "vm-unknown-caller");
05898    }
05899    return res;
05900 }
05901 
05902 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
05903 {
05904    int res = 0;
05905    int durationm;
05906    int durations;
05907    /* Verify that we have a duration for the message */
05908    if (duration == NULL)
05909       return res;
05910 
05911    /* Convert from seconds to minutes */
05912    durations=atoi(duration);
05913    durationm=(durations / 60);
05914 
05915    if (option_debug > 2)
05916       ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
05917 
05918    if ((!res) && (durationm >= minduration)) {
05919       res = wait_file2(chan, vms, "vm-duration");
05920 
05921       /* POLISH syntax */
05922       if (!strncasecmp(chan->language, "pl", 2)) {
05923          div_t num = div(durationm, 10);
05924 
05925          if (durationm == 1) {
05926             res = ast_play_and_wait(chan, "digits/1z");
05927             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
05928          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
05929             if (num.rem == 2) {
05930                if (!num.quot) {
05931                   res = ast_play_and_wait(chan, "digits/2-ie");
05932                } else {
05933                   res = say_and_wait(chan, durationm - 2 , chan->language);
05934                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
05935                }
05936             } else {
05937                res = say_and_wait(chan, durationm, chan->language);
05938             }
05939             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
05940          } else {
05941             res = say_and_wait(chan, durationm, chan->language);
05942             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
05943          }
05944       /* DEFAULT syntax */
05945       } else {
05946          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
05947          res = wait_file2(chan, vms, "vm-minutes");
05948       }
05949    }
05950    return res;
05951 }
05952 
05953 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
05954 {
05955    int res = 0;
05956    char filename[256], *cid;
05957    const char *origtime, *context, *category, *duration;
05958    struct ast_config *msg_cfg;
05959 
05960    vms->starting = 0; 
05961    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
05962    adsi_message(chan, vms);
05963    
05964    if (!strncasecmp(chan->language, "he", 2)) {        /* HEBREW FORMAT */
05965       /*
05966        * The syntax in hebrew for counting the number of message is up side down
05967        * in comparison to english.
05968        */
05969       if (!vms->curmsg) {
05970          res = wait_file2(chan, vms, "vm-message");
05971          res = wait_file2(chan, vms, "vm-first");    /* "First" */
05972       } else if (vms->curmsg == vms->lastmsg) {
05973          res = wait_file2(chan, vms, "vm-message");
05974          res = wait_file2(chan, vms, "vm-last");     /* "last" */
05975       } else {
05976          res = wait_file2(chan, vms, "vm-message");  /* "message" */
05977          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05978             ast_log(LOG_DEBUG, "curmsg: %d\n", vms->curmsg);
05979             ast_log(LOG_DEBUG, "lagmsg: %d\n", vms->lastmsg);
05980             if (!res) {
05981                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
05982             }
05983          }
05984       }
05985 
05986    } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH FORMAT */
05987       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05988          int ten, one;
05989          char nextmsg[256];
05990          ten = (vms->curmsg + 1) / 10;
05991          one = (vms->curmsg + 1) % 10;
05992 
05993          if (vms->curmsg < 20) {
05994             snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
05995             res = wait_file2(chan, vms, nextmsg);
05996          } else {
05997             snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
05998             res = wait_file2(chan, vms, nextmsg);
05999             if (one > 0) {
06000                if (!res) {
06001                   snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
06002                   res = wait_file2(chan, vms, nextmsg);
06003                }
06004             }
06005          }
06006       }
06007       if (!res)
06008          res = wait_file2(chan, vms, "vm-message");         
06009 
06010    } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH FORMAT */
06011       if (!vms->curmsg)
06012          res = wait_file2(chan, vms, "vm-first");  /* "First" */
06013       else if (vms->curmsg == vms->lastmsg)
06014          res = wait_file2(chan, vms, "vm-last");      /* "last" */      
06015       res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
06016       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06017          if (!res)
06018             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06019       }
06020       /* We know that the difference between English and Swedish
06021        * is very small, however, we differ the two for standartization
06022        * purposes, and possible changes to either of these in the 
06023        * future
06024        */
06025    } else {
06026       if (!vms->curmsg)                      /* Default syntax */
06027          res = wait_file2(chan, vms, "vm-first");  /* "First" */
06028       else if (vms->curmsg == vms->lastmsg)
06029          res = wait_file2(chan, vms, "vm-last");      /* "last" */      
06030       res = wait_file2(chan, vms, "vm-message");
06031       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06032          if (!res)
06033             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06034       }
06035    }
06036 
06037    /* Retrieve info from VM attribute file */
06038    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
06039    snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
06040    RETRIEVE(vms->curdir, vms->curmsg, vmu);
06041    msg_cfg = ast_config_load(filename);
06042    if (!msg_cfg) {
06043       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06044       return 0;
06045    }
06046 
06047    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06048       ast_log(LOG_WARNING, "No origtime?!\n");
06049       DISPOSE(vms->curdir, vms->curmsg);
06050       ast_config_destroy(msg_cfg);
06051       return 0;
06052    }
06053 
06054    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06055    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06056    category = ast_variable_retrieve(msg_cfg, "message", "category");
06057 
06058    context = ast_variable_retrieve(msg_cfg, "message", "context");
06059    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06060       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06061    if (!res)
06062       res = play_message_category(chan, category);
06063    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06064       res = play_message_datetime(chan, vmu, origtime, filename);
06065    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06066       res = play_message_callerid(chan, vms, cid, context, 0);
06067    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06068       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06069    /* Allow pressing '1' to skip envelope / callerid */
06070    if (res == '1')
06071       res = 0;
06072    ast_config_destroy(msg_cfg);
06073 
06074    if (!res) {
06075       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06076       vms->heard[vms->curmsg] = 1;
06077       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06078          ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06079          res = 0;
06080       }
06081    }
06082    DISPOSE(vms->curdir, vms->curmsg);
06083    return res;
06084 }
06085 
06086 #ifndef IMAP_STORAGE
06087 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
06088 {
06089    int count_msg, last_msg;
06090 
06091    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
06092    
06093    /* Rename the member vmbox HERE so that we don't try to return before
06094     * we know what's going on.
06095     */
06096    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
06097    
06098    /* Faster to make the directory than to check if it exists. */
06099    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
06100 
06101    /* traverses directory using readdir (or select query for ODBC) */
06102    count_msg = count_messages(vmu, vms->curdir);
06103    if (count_msg < 0)
06104       return count_msg;
06105    else
06106       vms->lastmsg = count_msg - 1;
06107 
06108    if (vm_allocate_dh(vms, vmu, count_msg)) {
06109       return -1;
06110    }
06111 
06112    /*
06113    The following test is needed in case sequencing gets messed up.
06114    There appears to be more than one way to mess up sequence, so
06115    we will not try to find all of the root causes--just fix it when
06116    detected.
06117    */
06118 
06119    /* for local storage, checks directory for messages up to maxmsg limit */
06120    last_msg = last_message_index(vmu, vms->curdir);
06121 
06122    if (last_msg < -1) {
06123       return last_msg;
06124    } else if (vms->lastmsg != last_msg) {
06125       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);
06126       resequence_mailbox(vmu, vms->curdir, count_msg);
06127    }
06128 
06129    return 0;
06130 }
06131 #endif
06132 
06133 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
06134 {
06135    int x = 0;
06136 #ifndef IMAP_STORAGE
06137    int last_msg_index;
06138    int res = 0, nummsg;
06139 #endif
06140 
06141    if (vms->lastmsg <= -1)
06142       goto done;
06143 
06144    vms->curmsg = -1; 
06145 #ifndef IMAP_STORAGE
06146    /* Get the deleted messages fixed */ 
06147    if (vm_lock_path(vms->curdir))
06148       return ERROR_LOCK_PATH;
06149 
06150    last_msg_index = last_message_index(vmu, vms->curdir);
06151    if (last_msg_index !=  vms->lastmsg) {
06152       ast_log(LOG_NOTICE, "%d messages arrived while mailbox was open\n", last_msg_index - vms->lastmsg);
06153    }
06154  
06155    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
06156    for (x = 0; x < last_msg_index + 1; x++) { 
06157       if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
06158          /* Save this message.  It's not in INBOX or hasn't been heard */ 
06159          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
06160          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
06161             break;
06162          vms->curmsg++; 
06163          make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
06164          if (strcmp(vms->fn, vms->fn2)) { 
06165             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
06166          } 
06167       } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
06168          /* Move to old folder before deleting */ 
06169          res = save_to_folder(vmu, vms, x, 1);
06170          if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
06171             /* If save failed do not delete the message */
06172             ast_log(LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
06173             vms->deleted[x] = 0;
06174             vms->heard[x] = 0;
06175             --x;
06176          } 
06177       } 
06178    } 
06179 
06180    /* Delete ALL remaining messages */
06181    nummsg = x - 1;
06182    for (x = vms->curmsg + 1; x <= nummsg; x++) {
06183       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
06184       if (EXISTS(vms->curdir, x, vms->fn, NULL))
06185          DELETE(vms->curdir, x, vms->fn, vmu);
06186    }
06187    ast_unlock_path(vms->curdir);
06188 #else /* defined(IMAP_STORAGE) */
06189    if (vms->deleted) {
06190       /* Since we now expunge after each delete, deleting in reverse order
06191        * ensures that no reordering occurs between each step. */
06192       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
06193          if (vms->deleted[x]) {
06194             if (option_debug > 2) {
06195                ast_log(LOG_DEBUG, "IMAP delete of %d\n", x);
06196             }
06197             DELETE(vms->curdir, x, vms->fn, vmu);
06198          }
06199       }
06200    }
06201 #endif
06202 
06203 done:
06204    if (vms->deleted)
06205       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int)); 
06206    if (vms->heard)
06207       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int)); 
06208 
06209    return 0;
06210 }
06211 
06212 /* In Greek even though we CAN use a syntax like "friends messages"
06213  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
06214  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
06215  * syntax for the above three categories which is more elegant. 
06216  */
06217 
06218 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
06219 {
06220    int cmd;
06221    char *buf;
06222 
06223    buf = alloca(strlen(mbox)+2); 
06224    strcpy(buf, mbox);
06225    strcat(buf,"s");
06226 
06227    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
06228       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
06229       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06230    } else {
06231       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06232       return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
06233    }
06234 }
06235 
06236 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
06237 {
06238    int cmd;
06239 
06240    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
06241       if (!strcasecmp(mbox, "vm-INBOX"))
06242          cmd = ast_play_and_wait(chan, "vm-new-e");
06243       else
06244          cmd = ast_play_and_wait(chan, "vm-old-e");
06245       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06246    } else {
06247       cmd = ast_play_and_wait(chan, "vm-messages");
06248       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06249    }
06250 }
06251 
06252 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
06253 {
06254    int cmd;
06255 
06256    if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
06257       cmd = ast_play_and_wait(chan, "vm-messages");
06258       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06259    } else {
06260       cmd = ast_play_and_wait(chan, mbox);
06261       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06262    }
06263 }
06264 
06265 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
06266 {
06267    int cmd;
06268 
06269    if (  !strncasecmp(chan->language, "it", 2) ||
06270         !strncasecmp(chan->language, "es", 2) ||
06271         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
06272       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
06273       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06274    } else if (!strncasecmp(chan->language, "gr", 2)) {
06275       return vm_play_folder_name_gr(chan, mbox);
06276    } else if (!strncasecmp(chan->language, "pl", 2)) {
06277       return vm_play_folder_name_pl(chan, mbox);
06278    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
06279       return vm_play_folder_name_ua(chan, mbox);
06280    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
06281       cmd = ast_play_and_wait(chan, mbox);
06282       return cmd;
06283    } else {  /* Default English */
06284       cmd = ast_play_and_wait(chan, mbox);
06285       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
06286    }
06287 }
06288 
06289 /* GREEK SYNTAX 
06290    In greek the plural for old/new is
06291    different so we need the following files
06292    We also need vm-denExeteMynhmata because 
06293    this syntax is different.
06294    
06295    -> vm-Olds.wav : "Palia"
06296    -> vm-INBOXs.wav : "Nea"
06297    -> vm-denExeteMynhmata : "den exete mynhmata"
06298 */
06299                
06300    
06301 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
06302 {
06303    int res = 0;
06304 
06305    if (vms->newmessages) {
06306       res = ast_play_and_wait(chan, "vm-youhave");
06307       if (!res) 
06308          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
06309       if (!res) {
06310          if ((vms->newmessages == 1)) {
06311             res = ast_play_and_wait(chan, "vm-INBOX");
06312             if (!res)
06313                res = ast_play_and_wait(chan, "vm-message");
06314          } else {
06315             res = ast_play_and_wait(chan, "vm-INBOXs");
06316             if (!res)
06317                res = ast_play_and_wait(chan, "vm-messages");
06318          }
06319       }
06320    } else if (vms->oldmessages){
06321       res = ast_play_and_wait(chan, "vm-youhave");
06322       if (!res)
06323          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
06324       if ((vms->oldmessages == 1)){
06325          res = ast_play_and_wait(chan, "vm-Old");
06326          if (!res)
06327             res = ast_play_and_wait(chan, "vm-message");
06328       } else {
06329          res = ast_play_and_wait(chan, "vm-Olds");
06330          if (!res)
06331             res = ast_play_and_wait(chan, "vm-messages");
06332       }
06333    } else if (!vms->oldmessages && !vms->newmessages) 
06334       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
06335    return res;
06336 }
06337 
06338 /* Default English syntax */
06339 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
06340 {
06341    int res;
06342 
06343    /* Introduce messages they have */
06344    res = ast_play_and_wait(chan, "vm-youhave");
06345    if (!res) {
06346       if (vms->newmessages) {
06347          res = say_and_wait(chan, vms->newmessages, chan->language);
06348          if (!res)
06349             res = ast_play_and_wait(chan, "vm-INBOX");
06350          if (vms->oldmessages && !res)
06351             res = ast_play_and_wait(chan, "vm-and");
06352          else if (!res) {
06353             if ((vms->newmessages == 1))
06354                res = ast_play_and_wait(chan, "vm-message");
06355             else
06356                res = ast_play_and_wait(chan, "vm-messages");
06357          }
06358             
06359       }
06360       if (!res && vms->oldmessages) {
06361          res = say_and_wait(chan, vms->oldmessages, chan->language);
06362          if (!res)
06363             res = ast_play_and_wait(chan, "vm-Old");
06364          if (!res) {
06365             if (vms->oldmessages == 1)
06366                res = ast_play_and_wait(chan, "vm-message");
06367             else
06368                res = ast_play_and_wait(chan, "vm-messages");
06369          }
06370       }
06371       if (!res) {
06372          if (!vms->oldmessages && !vms->newmessages) {
06373             res = ast_play_and_wait(chan, "vm-no");
06374             if (!res)
06375                res = ast_play_and_wait(chan, "vm-messages");
06376          }
06377       }
06378    }
06379    return res;
06380 }
06381 
06382 /* Version of vm_intro() designed to work for many languages.
06383  *
06384  * It is hoped that this function can prevent the proliferation of 
06385  * language-specific vm_intro() functions and in time replace the language-
06386  * specific functions which already exist.  An examination of the language-
06387  * specific functions revealed that they all corrected the same deficiencies
06388  * in vm_intro_en() (which was the default function). Namely:
06389  *
06390  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
06391  *     wording of the voicemail greeting hides this problem.  For example,
06392  *     vm-INBOX contains only the word "new".  This means that both of these
06393  *     sequences produce valid utterances:
06394  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
06395  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
06396  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
06397  *     in many languages) the first utterance becomes "you have 1 the new message".
06398  *  2) The function contains hardcoded rules for pluralizing the word "message".
06399  *     These rules are correct for English, but not for many other languages.
06400  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
06401  *     required in many languages.
06402  *  4) The gender of the word for "message" is not specified. This is a problem
06403  *     because in many languages the gender of the number in phrases such
06404  *     as "you have one new message" must match the gender of the word
06405  *     meaning "message".
06406  *
06407  * Fixing these problems for each new language has meant duplication of effort.
06408  * This new function solves the problems in the following general ways:
06409  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
06410  *     and vm-Old respectively for those languages where it makes sense.
06411  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
06412  *     on vm-message.
06413  *  3) Call ast_say_counted_adjective() to put the proper gender and number
06414  *     prefix on vm-new and vm-old (none for English).
06415  *  4) Pass the gender of the language's word for "message" as an agument to
06416  *     this function which is can in turn pass on to the functions which 
06417  *     say numbers and put endings on nounds and adjectives.
06418  *
06419  * All languages require these messages:
06420  *  vm-youhave    "You have..."
06421  *  vm-and     "and"
06422  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
06423  *
06424  * To use it for English, you will need these additional sound files:
06425  *  vm-new     "new"
06426  *  vm-message    "message", singular
06427  *  vm-messages      "messages", plural
06428  *
06429  * If you use it for Russian and other slavic languages, you will need these additional sound files:
06430  *
06431  *  vm-newn    "novoye" (singular, neuter)
06432  *  vm-newx    "novikh" (counting plural form, genative plural)
06433  *  vm-message    "sobsheniye" (singular form)
06434  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
06435  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
06436  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
06437  *  digits/2n     "dva" (neuter singular)
06438  */
06439 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
06440 {
06441    int res;
06442    int lastnum = 0;
06443 
06444    res = ast_play_and_wait(chan, "vm-youhave");
06445 
06446    if (!res && vms->newmessages) {
06447       lastnum = vms->newmessages;
06448 
06449       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06450          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
06451       }
06452 
06453       if (!res && vms->oldmessages) {
06454          res = ast_play_and_wait(chan, "vm-and");
06455       }
06456    }
06457 
06458    if (!res && vms->oldmessages) {
06459       lastnum = vms->oldmessages;
06460 
06461       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06462          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
06463       }
06464    }
06465 
06466    if (!res) {
06467       if (lastnum == 0) {
06468          res = ast_play_and_wait(chan, "vm-no");
06469       }
06470       if (!res) {
06471          res = ast_say_counted_noun(chan, lastnum, "vm-message");
06472       }
06473    }
06474 
06475    return res;
06476 }
06477 
06478 /* Default Hebrew syntax */
06479 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
06480 {
06481    int res=0;
06482 
06483    /* Introduce messages they have */
06484    if (!res) {
06485       if ((vms->newmessages) || (vms->oldmessages)) {
06486          res = ast_play_and_wait(chan, "vm-youhave");
06487       }
06488       /*
06489        * The word "shtei" refers to the number 2 in hebrew when performing a count
06490        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
06491        * an element, this is one of them.
06492        */
06493       if (vms->newmessages) {
06494          if (!res) {
06495             if (vms->newmessages == 1) {
06496                res = ast_play_and_wait(chan, "vm-INBOX1");
06497             } else {
06498                if (vms->newmessages == 2) {
06499                   res = ast_play_and_wait(chan, "vm-shtei");
06500                } else {
06501                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06502                }
06503                res = ast_play_and_wait(chan, "vm-INBOX");
06504             }
06505          }
06506          if (vms->oldmessages && !res) {
06507             res = ast_play_and_wait(chan, "vm-and");
06508             if (vms->oldmessages == 1) {
06509                res = ast_play_and_wait(chan, "vm-Old1");
06510             } else {
06511                if (vms->oldmessages == 2) {
06512                   res = ast_play_and_wait(chan, "vm-shtei");
06513                } else {
06514                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06515                }
06516                res = ast_play_and_wait(chan, "vm-Old");
06517             }
06518          }
06519       }
06520       if (!res && vms->oldmessages && !vms->newmessages) {
06521          if (!res) {
06522             if (vms->oldmessages == 1) {
06523                res = ast_play_and_wait(chan, "vm-Old1");
06524             } else {
06525                if (vms->oldmessages == 2) {
06526                   res = ast_play_and_wait(chan, "vm-shtei");
06527                } else {
06528                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
06529                }
06530                res = ast_play_and_wait(chan, "vm-Old");
06531             }
06532          }
06533       }
06534       if (!res) {
06535          if (!vms->oldmessages && !vms->newmessages) {
06536             if (!res) {
06537                res = ast_play_and_wait(chan, "vm-nomessages");
06538             }
06539          }
06540       }
06541    }
06542    return res;
06543 }
06544 
06545 
06546 /* ITALIAN syntax */
06547 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
06548 {
06549    /* Introduce messages they have */
06550    int res;
06551    if (!vms->oldmessages && !vms->newmessages)
06552       res = ast_play_and_wait(chan, "vm-no") ||
06553          ast_play_and_wait(chan, "vm-message");
06554    else
06555       res = ast_play_and_wait(chan, "vm-youhave");
06556    if (!res && vms->newmessages) {
06557       res = (vms->newmessages == 1) ?
06558          ast_play_and_wait(chan, "digits/un") ||
06559          ast_play_and_wait(chan, "vm-nuovo") ||
06560          ast_play_and_wait(chan, "vm-message") :
06561          /* 2 or more new messages */
06562          say_and_wait(chan, vms->newmessages, chan->language) ||
06563          ast_play_and_wait(chan, "vm-nuovi") ||
06564          ast_play_and_wait(chan, "vm-messages");
06565       if (!res && vms->oldmessages)
06566          res = ast_play_and_wait(chan, "vm-and");
06567    }
06568    if (!res && vms->oldmessages) {
06569       res = (vms->oldmessages == 1) ?
06570          ast_play_and_wait(chan, "digits/un") ||
06571          ast_play_and_wait(chan, "vm-vecchio") ||
06572          ast_play_and_wait(chan, "vm-message") :
06573          /* 2 or more old messages */
06574          say_and_wait(chan, vms->oldmessages, chan->language) ||
06575          ast_play_and_wait(chan, "vm-vecchi") ||
06576          ast_play_and_wait(chan, "vm-messages");
06577    }
06578    return res;
06579 }
06580 
06581 /* POLISH syntax */
06582 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
06583 {
06584    /* Introduce messages they have */
06585    int res;
06586    div_t num;
06587 
06588    if (!vms->oldmessages && !vms->newmessages) {
06589       res = ast_play_and_wait(chan, "vm-no");
06590       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06591       return res;
06592    } else {
06593       res = ast_play_and_wait(chan, "vm-youhave");
06594    }
06595 
06596    if (vms->newmessages) {
06597       num = div(vms->newmessages, 10);
06598       if (vms->newmessages == 1) {
06599          res = ast_play_and_wait(chan, "digits/1-a");
06600          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
06601          res = res ? res : ast_play_and_wait(chan, "vm-message");
06602       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06603          if (num.rem == 2) {
06604             if (!num.quot) {
06605                res = ast_play_and_wait(chan, "digits/2-ie");
06606             } else {
06607                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
06608                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06609             }
06610          } else {
06611             res = say_and_wait(chan, vms->newmessages, chan->language);
06612          }
06613          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
06614          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06615       } else {
06616          res = say_and_wait(chan, vms->newmessages, chan->language);
06617          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
06618          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06619       }
06620       if (!res && vms->oldmessages)
06621          res = ast_play_and_wait(chan, "vm-and");
06622    }
06623    if (!res && vms->oldmessages) {
06624       num = div(vms->oldmessages, 10);
06625       if (vms->oldmessages == 1) {
06626          res = ast_play_and_wait(chan, "digits/1-a");
06627          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
06628          res = res ? res : ast_play_and_wait(chan, "vm-message");
06629       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06630          if (num.rem == 2) {
06631             if (!num.quot) {
06632                res = ast_play_and_wait(chan, "digits/2-ie");
06633             } else {
06634                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
06635                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06636             }
06637          } else {
06638             res = say_and_wait(chan, vms->oldmessages, chan->language);
06639          }
06640          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
06641          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06642       } else {
06643          res = say_and_wait(chan, vms->oldmessages, chan->language);
06644          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
06645          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06646       }
06647    }
06648 
06649    return res;
06650 }
06651 
06652 /* SWEDISH syntax */
06653 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
06654 {
06655    /* Introduce messages they have */
06656    int res;
06657 
06658    res = ast_play_and_wait(chan, "vm-youhave");
06659    if (res)
06660       return res;
06661 
06662    if (!vms->oldmessages && !vms->newmessages) {
06663       res = ast_play_and_wait(chan, "vm-no");
06664       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06665       return res;
06666    }
06667 
06668    if (vms->newmessages) {
06669       if ((vms->newmessages == 1)) {
06670          res = ast_play_and_wait(chan, "digits/ett");
06671          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
06672          res = res ? res : ast_play_and_wait(chan, "vm-message");
06673       } else {
06674          res = say_and_wait(chan, vms->newmessages, chan->language);
06675          res = res ? res : ast_play_and_wait(chan, "vm-nya");
06676          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06677       }
06678       if (!res && vms->oldmessages)
06679          res = ast_play_and_wait(chan, "vm-and");
06680    }
06681    if (!res && vms->oldmessages) {
06682       if (vms->oldmessages == 1) {
06683          res = ast_play_and_wait(chan, "digits/ett");
06684          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
06685          res = res ? res : ast_play_and_wait(chan, "vm-message");
06686       } else {
06687          res = say_and_wait(chan, vms->oldmessages, chan->language);
06688          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
06689          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06690       }
06691    }
06692 
06693    return res;
06694 }
06695 
06696 /* NORWEGIAN syntax */
06697 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
06698 {
06699    /* Introduce messages they have */
06700    int res;
06701 
06702    res = ast_play_and_wait(chan, "vm-youhave");
06703    if (res)
06704       return res;
06705 
06706    if (!vms->oldmessages && !vms->newmessages) {
06707       res = ast_play_and_wait(chan, "vm-no");
06708       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06709       return res;
06710    }
06711 
06712    if (vms->newmessages) {
06713       if ((vms->newmessages == 1)) {
06714          res = ast_play_and_wait(chan, "digits/1");
06715          res = res ? res : ast_play_and_wait(chan, "vm-ny");
06716          res = res ? res : ast_play_and_wait(chan, "vm-message");
06717       } else {
06718          res = say_and_wait(chan, vms->newmessages, chan->language);
06719          res = res ? res : ast_play_and_wait(chan, "vm-nye");
06720          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06721       }
06722       if (!res && vms->oldmessages)
06723          res = ast_play_and_wait(chan, "vm-and");
06724    }
06725    if (!res && vms->oldmessages) {
06726       if (vms->oldmessages == 1) {
06727          res = ast_play_and_wait(chan, "digits/1");
06728          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
06729          res = res ? res : ast_play_and_wait(chan, "vm-message");
06730       } else {
06731          res = say_and_wait(chan, vms->oldmessages, chan->language);
06732          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
06733          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06734       }
06735    }
06736 
06737    return res;
06738 }
06739 
06740 /* GERMAN syntax */
06741 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
06742 {
06743    /* Introduce messages they have */
06744    int res;
06745    res = ast_play_and_wait(chan, "vm-youhave");
06746    if (!res) {
06747       if (vms->newmessages) {
06748          if ((vms->newmessages == 1))
06749             res = ast_play_and_wait(chan, "digits/1F");
06750          else
06751             res = say_and_wait(chan, vms->newmessages, chan->language);
06752          if (!res)
06753             res = ast_play_and_wait(chan, "vm-INBOX");
06754          if (vms->oldmessages && !res)
06755             res = ast_play_and_wait(chan, "vm-and");
06756          else if (!res) {
06757             if ((vms->newmessages == 1))
06758                res = ast_play_and_wait(chan, "vm-message");
06759             else
06760                res = ast_play_and_wait(chan, "vm-messages");
06761          }
06762             
06763       }
06764       if (!res && vms->oldmessages) {
06765          if (vms->oldmessages == 1)
06766             res = ast_play_and_wait(chan, "digits/1F");
06767          else
06768             res = say_and_wait(chan, vms->oldmessages, chan->language);
06769          if (!res)
06770             res = ast_play_and_wait(chan, "vm-Old");
06771          if (!res) {
06772             if (vms->oldmessages == 1)
06773                res = ast_play_and_wait(chan, "vm-message");
06774             else
06775                res = ast_play_and_wait(chan, "vm-messages");
06776          }
06777       }
06778       if (!res) {
06779          if (!vms->oldmessages && !vms->newmessages) {
06780             res = ast_play_and_wait(chan, "vm-no");
06781             if (!res)
06782                res = ast_play_and_wait(chan, "vm-messages");
06783          }
06784       }
06785    }
06786    return res;
06787 }
06788 
06789 /* SPANISH syntax */
06790 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
06791 {
06792    /* Introduce messages they have */
06793    int res;
06794    if (!vms->oldmessages && !vms->newmessages) {
06795       res = ast_play_and_wait(chan, "vm-youhaveno");
06796       if (!res)
06797          res = ast_play_and_wait(chan, "vm-messages");
06798    } else {
06799       res = ast_play_and_wait(chan, "vm-youhave");
06800    }
06801    if (!res) {
06802       if (vms->newmessages) {
06803          if (!res) {
06804             if ((vms->newmessages == 1)) {
06805                res = ast_play_and_wait(chan, "digits/1");
06806                if (!res)
06807                   res = ast_play_and_wait(chan, "vm-message");
06808                if (!res)
06809                   res = ast_play_and_wait(chan, "vm-INBOXs");
06810             } else {
06811                res = say_and_wait(chan, vms->newmessages, chan->language);
06812                if (!res)
06813                   res = ast_play_and_wait(chan, "vm-messages");
06814                if (!res)
06815                   res = ast_play_and_wait(chan, "vm-INBOX");
06816             }
06817          }
06818          if (vms->oldmessages && !res)
06819             res = ast_play_and_wait(chan, "vm-and");
06820       }
06821       if (vms->oldmessages) {
06822          if (!res) {
06823             if (vms->oldmessages == 1) {
06824                res = ast_play_and_wait(chan, "digits/1");
06825                if (!res)
06826                   res = ast_play_and_wait(chan, "vm-message");
06827                if (!res)
06828                   res = ast_play_and_wait(chan, "vm-Olds");
06829             } else {
06830                res = say_and_wait(chan, vms->oldmessages, chan->language);
06831                if (!res)
06832                   res = ast_play_and_wait(chan, "vm-messages");
06833                if (!res)
06834                   res = ast_play_and_wait(chan, "vm-Old");
06835             }
06836          }
06837       }
06838    }
06839 return res;
06840 }
06841 
06842 /* BRAZILIAN PORTUGUESE syntax */
06843 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
06844    /* Introduce messages they have */
06845    int res;
06846    if (!vms->oldmessages && !vms->newmessages) {
06847       res = ast_play_and_wait(chan, "vm-nomessages");
06848       return res;
06849    }
06850    else {
06851       res = ast_play_and_wait(chan, "vm-youhave");
06852    }
06853    if (vms->newmessages) {
06854       if (!res)
06855          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06856       if ((vms->newmessages == 1)) {
06857          if (!res)
06858             res = ast_play_and_wait(chan, "vm-message");
06859          if (!res)
06860             res = ast_play_and_wait(chan, "vm-INBOXs");
06861       }
06862       else {
06863          if (!res)
06864             res = ast_play_and_wait(chan, "vm-messages");
06865          if (!res)
06866             res = ast_play_and_wait(chan, "vm-INBOX");
06867       }
06868       if (vms->oldmessages && !res)
06869          res = ast_play_and_wait(chan, "vm-and");
06870    }
06871    if (vms->oldmessages) {
06872       if (!res)
06873          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06874       if (vms->oldmessages == 1) {
06875          if (!res)
06876             res = ast_play_and_wait(chan, "vm-message");
06877          if (!res)
06878             res = ast_play_and_wait(chan, "vm-Olds");
06879       }
06880       else {
06881          if (!res)
06882       res = ast_play_and_wait(chan, "vm-messages");
06883          if (!res)
06884             res = ast_play_and_wait(chan, "vm-Old");
06885       }
06886    }
06887    return res;
06888 }
06889 
06890 /* FRENCH syntax */
06891 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
06892 {
06893    /* Introduce messages they have */
06894    int res;
06895    res = ast_play_and_wait(chan, "vm-youhave");
06896    if (!res) {
06897       if (vms->newmessages) {
06898          res = say_and_wait(chan, vms->newmessages, chan->language);
06899          if (!res)
06900             res = ast_play_and_wait(chan, "vm-INBOX");
06901          if (vms->oldmessages && !res)
06902             res = ast_play_and_wait(chan, "vm-and");
06903          else if (!res) {
06904             if ((vms->newmessages == 1))
06905                res = ast_play_and_wait(chan, "vm-message");
06906             else
06907                res = ast_play_and_wait(chan, "vm-messages");
06908          }
06909             
06910       }
06911       if (!res && vms->oldmessages) {
06912          res = say_and_wait(chan, vms->oldmessages, chan->language);
06913          if (!res)
06914             res = ast_play_and_wait(chan, "vm-Old");
06915          if (!res) {
06916             if (vms->oldmessages == 1)
06917                res = ast_play_and_wait(chan, "vm-message");
06918             else
06919                res = ast_play_and_wait(chan, "vm-messages");
06920          }
06921       }
06922       if (!res) {
06923          if (!vms->oldmessages && !vms->newmessages) {
06924             res = ast_play_and_wait(chan, "vm-no");
06925             if (!res)
06926                res = ast_play_and_wait(chan, "vm-messages");
06927          }
06928       }
06929    }
06930    return res;
06931 }
06932 
06933 /* DUTCH syntax */
06934 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
06935 {
06936    /* Introduce messages they have */
06937    int res;
06938    res = ast_play_and_wait(chan, "vm-youhave");
06939    if (!res) {
06940       if (vms->newmessages) {
06941          res = say_and_wait(chan, vms->newmessages, chan->language);
06942          if (!res) {
06943             if (vms->newmessages == 1)
06944                res = ast_play_and_wait(chan, "vm-INBOXs");
06945             else
06946                res = ast_play_and_wait(chan, "vm-INBOX");
06947          }
06948          if (vms->oldmessages && !res)
06949             res = ast_play_and_wait(chan, "vm-and");
06950          else if (!res) {
06951             if ((vms->newmessages == 1))
06952                res = ast_play_and_wait(chan, "vm-message");
06953             else
06954                res = ast_play_and_wait(chan, "vm-messages");
06955          }
06956             
06957       }
06958       if (!res && vms->oldmessages) {
06959          res = say_and_wait(chan, vms->oldmessages, chan->language);
06960          if (!res) {
06961             if (vms->oldmessages == 1)
06962                res = ast_play_and_wait(chan, "vm-Olds");
06963             else
06964                res = ast_play_and_wait(chan, "vm-Old");
06965          }
06966          if (!res) {
06967             if (vms->oldmessages == 1)
06968                res = ast_play_and_wait(chan, "vm-message");
06969             else
06970                res = ast_play_and_wait(chan, "vm-messages");
06971          }
06972       }
06973       if (!res) {
06974          if (!vms->oldmessages && !vms->newmessages) {
06975             res = ast_play_and_wait(chan, "vm-no");
06976             if (!res)
06977                res = ast_play_and_wait(chan, "vm-messages");
06978          }
06979       }
06980    }
06981    return res;
06982 }
06983 
06984 /* PORTUGUESE syntax */
06985 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
06986 {
06987    /* Introduce messages they have */
06988    int res;
06989    res = ast_play_and_wait(chan, "vm-youhave");
06990    if (!res) {
06991       if (vms->newmessages) {
06992          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06993          if (!res) {
06994             if ((vms->newmessages == 1)) {
06995                res = ast_play_and_wait(chan, "vm-message");
06996                if (!res)
06997                   res = ast_play_and_wait(chan, "vm-INBOXs");
06998             } else {
06999                res = ast_play_and_wait(chan, "vm-messages");
07000                if (!res)
07001                   res = ast_play_and_wait(chan, "vm-INBOX");
07002             }
07003          }
07004          if (vms->oldmessages && !res)
07005             res = ast_play_and_wait(chan, "vm-and");
07006       }
07007       if (!res && vms->oldmessages) {
07008          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07009          if (!res) {
07010             if (vms->oldmessages == 1) {
07011                res = ast_play_and_wait(chan, "vm-message");
07012                if (!res)
07013                   res = ast_play_and_wait(chan, "vm-Olds");
07014             } else {
07015                res = ast_play_and_wait(chan, "vm-messages");
07016                if (!res)
07017                   res = ast_play_and_wait(chan, "vm-Old");
07018             }
07019          }
07020       }
07021       if (!res) {
07022          if (!vms->oldmessages && !vms->newmessages) {
07023             res = ast_play_and_wait(chan, "vm-no");
07024             if (!res)
07025                res = ast_play_and_wait(chan, "vm-messages");
07026          }
07027       }
07028    }
07029    return res;
07030 }
07031 
07032 
07033 /* CZECH syntax */
07034 /* in czech there must be declension of word new and message
07035  * czech        : english        : czech      : english
07036  * --------------------------------------------------------
07037  * vm-youhave   : you have 
07038  * vm-novou     : one new        : vm-zpravu  : message
07039  * vm-nove      : 2-4 new        : vm-zpravy  : messages
07040  * vm-novych    : 5-infinite new : vm-zprav   : messages
07041  * vm-starou   : one old
07042  * vm-stare     : 2-4 old 
07043  * vm-starych   : 5-infinite old
07044  * jednu        : one   - falling 4. 
07045  * vm-no        : no  ( no messages )
07046  */
07047 
07048 static int vm_intro_cs(struct ast_channel *chan,struct vm_state *vms)
07049 {
07050    int res;
07051    res = ast_play_and_wait(chan, "vm-youhave");
07052    if (!res) {
07053       if (vms->newmessages) {
07054          if (vms->newmessages == 1) {
07055             res = ast_play_and_wait(chan, "digits/jednu");
07056          } else {
07057             res = say_and_wait(chan, vms->newmessages, chan->language);
07058          }
07059          if (!res) {
07060             if ((vms->newmessages == 1))
07061                res = ast_play_and_wait(chan, "vm-novou");
07062             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07063                res = ast_play_and_wait(chan, "vm-nove");
07064             if (vms->newmessages > 4)
07065                res = ast_play_and_wait(chan, "vm-novych");
07066          }
07067          if (vms->oldmessages && !res)
07068             res = ast_play_and_wait(chan, "vm-and");
07069          else if (!res) {
07070             if ((vms->newmessages == 1))
07071                res = ast_play_and_wait(chan, "vm-zpravu");
07072             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07073                res = ast_play_and_wait(chan, "vm-zpravy");
07074             if (vms->newmessages > 4)
07075                res = ast_play_and_wait(chan, "vm-zprav");
07076          }
07077       }
07078       if (!res && vms->oldmessages) {
07079          res = say_and_wait(chan, vms->oldmessages, chan->language);
07080          if (!res) {
07081             if ((vms->oldmessages == 1))
07082                res = ast_play_and_wait(chan, "vm-starou");
07083             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07084                res = ast_play_and_wait(chan, "vm-stare");
07085             if (vms->oldmessages > 4)
07086                res = ast_play_and_wait(chan, "vm-starych");
07087          }
07088          if (!res) {
07089             if ((vms->oldmessages == 1))
07090                res = ast_play_and_wait(chan, "vm-zpravu");
07091             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07092                res = ast_play_and_wait(chan, "vm-zpravy");
07093             if (vms->oldmessages > 4)
07094                res = ast_play_and_wait(chan, "vm-zprav");
07095          }
07096       }
07097       if (!res) {
07098          if (!vms->oldmessages && !vms->newmessages) {
07099             res = ast_play_and_wait(chan, "vm-no");
07100             if (!res)
07101                res = ast_play_and_wait(chan, "vm-zpravy");
07102          }
07103       }
07104    }
07105    return res;
07106 }
07107 
07108 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07109 {
07110    char prefile[256];
07111    
07112    /* Notify the user that the temp greeting is set and give them the option to remove it */
07113    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07114    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
07115       RETRIEVE(prefile, -1, vmu);
07116       if (ast_fileexists(prefile, NULL, NULL) > 0)
07117          ast_play_and_wait(chan, "vm-tempgreetactive");
07118       DISPOSE(prefile, -1);
07119    }
07120 
07121    /* Play voicemail intro - syntax is different for different languages */
07122    if (0) {
07123       return 0;
07124    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
07125       return vm_intro_cs(chan, vms);
07126    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
07127       static int deprecation_warning = 0;
07128       if (deprecation_warning++ % 10 == 0) {
07129          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
07130       }
07131       return vm_intro_cs(chan, vms);
07132    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
07133       return vm_intro_de(chan, vms);
07134    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
07135       return vm_intro_es(chan, vms);
07136    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
07137       return vm_intro_fr(chan, vms);
07138    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
07139       return vm_intro_gr(chan, vms);
07140    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
07141       return vm_intro_he(chan, vms);
07142    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
07143       return vm_intro_it(chan, vms);
07144    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
07145       return vm_intro_nl(chan, vms);
07146    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
07147       return vm_intro_no(chan, vms);
07148    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
07149       return vm_intro_pl(chan, vms);
07150    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
07151       return vm_intro_pt_BR(chan, vms);
07152    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
07153       return vm_intro_pt(chan, vms);
07154    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
07155       return vm_intro_multilang(chan, vms, "n");
07156    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
07157       return vm_intro_se(chan, vms);
07158    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
07159       return vm_intro_multilang(chan, vms, "n");
07160    } else {                                             /* Default to ENGLISH */
07161       return vm_intro_en(chan, vms);
07162    }
07163 }
07164 
07165 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
07166 {
07167    int res = 0;
07168    /* Play instructions and wait for new command */
07169    while (!res) {
07170       if (vms->starting) {
07171          if (vms->lastmsg > -1) {
07172             res = ast_play_and_wait(chan, "vm-onefor");
07173             if (!strncasecmp(chan->language, "he", 2)) {
07174                res = ast_play_and_wait(chan, "vm-for");
07175             }
07176             if (!res)
07177                res = vm_play_folder_name(chan, vms->vmbox);
07178          }
07179          if (!res)
07180             res = ast_play_and_wait(chan, "vm-opts");
07181       } else {
07182          if (vms->curmsg)
07183             res = ast_play_and_wait(chan, "vm-prev");
07184          if (!res && !skipadvanced)
07185             res = ast_play_and_wait(chan, "vm-advopts");
07186          if (!res)
07187             res = ast_play_and_wait(chan, "vm-repeat");
07188          if (!res && (vms->curmsg != vms->lastmsg))
07189             res = ast_play_and_wait(chan, "vm-next");
07190          if (!res) {
07191             if (!vms->deleted[vms->curmsg])
07192                res = ast_play_and_wait(chan, "vm-delete");
07193             else
07194                res = ast_play_and_wait(chan, "vm-undelete");
07195             if (!res)
07196                res = ast_play_and_wait(chan, "vm-toforward");
07197             if (!res)
07198                res = ast_play_and_wait(chan, "vm-savemessage");
07199          }
07200       }
07201       if (!res)
07202          res = ast_play_and_wait(chan, "vm-helpexit");
07203       if (!res)
07204          res = ast_waitfordigit(chan, 6000);
07205       if (!res) {
07206          vms->repeats++;
07207          if (vms->repeats > 2) {
07208             res = 't';
07209          }
07210       }
07211    }
07212    return res;
07213 }
07214 
07215 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07216 {
07217    int cmd = 0;
07218    int duration = 0;
07219    int tries = 0;
07220    char newpassword[80] = "";
07221    char newpassword2[80] = "";
07222    char prefile[PATH_MAX] = "";
07223    unsigned char buf[256];
07224    int bytes=0;
07225 
07226    if (ast_adsi_available(chan)) {
07227       bytes += adsi_logo(buf + bytes);
07228       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
07229       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07230       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07231       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07232       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07233    }
07234 
07235    /* First, have the user change their password 
07236       so they won't get here again */
07237    for (;;) {
07238       newpassword[1] = '\0';
07239       newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
07240       if (cmd == '#')
07241          newpassword[0] = '\0';
07242       if (cmd < 0 || cmd == 't' || cmd == '#')
07243          return cmd;
07244       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
07245       if (cmd < 0 || cmd == 't' || cmd == '#')
07246          return cmd;
07247       newpassword2[1] = '\0';
07248       newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
07249       if (cmd == '#')
07250          newpassword2[0] = '\0';
07251       if (cmd < 0 || cmd == 't' || cmd == '#')
07252          return cmd;
07253       cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
07254       if (cmd < 0 || cmd == 't' || cmd == '#')
07255          return cmd;
07256       if (!strcmp(newpassword, newpassword2))
07257          break;
07258       ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07259       cmd = ast_play_and_wait(chan, "vm-mismatch");
07260       if (++tries == 3)
07261          return -1;
07262       if (cmd == 0) {
07263          cmd = ast_play_and_wait(chan, "vm-pls-try-again");
07264       }
07265    }
07266    if (ast_strlen_zero(ext_pass_cmd)) 
07267       vm_change_password(vmu,newpassword);
07268    else 
07269       vm_change_password_shell(vmu,newpassword);
07270    if (option_debug > 2)
07271       ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
07272    cmd = ast_play_and_wait(chan,"vm-passchanged");
07273 
07274    /* If forcename is set, have the user record their name */  
07275    if (ast_test_flag(vmu, VM_FORCENAME)) {
07276       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07277       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07278          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07279          if (cmd < 0 || cmd == 't' || cmd == '#')
07280             return cmd;
07281       }
07282    }
07283 
07284    /* If forcegreetings is set, have the user record their greetings */
07285    if (ast_test_flag(vmu, VM_FORCEGREET)) {
07286       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07287       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07288          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07289          if (cmd < 0 || cmd == 't' || cmd == '#')
07290             return cmd;
07291       }
07292 
07293       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07294       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07295          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07296          if (cmd < 0 || cmd == 't' || cmd == '#')
07297             return cmd;
07298       }
07299    }
07300 
07301    return cmd;
07302 }
07303 
07304 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07305 {
07306    int cmd = 0;
07307    int retries = 0;
07308    int duration = 0;
07309    char newpassword[80] = "";
07310    char newpassword2[80] = "";
07311    char prefile[PATH_MAX] = "";
07312    unsigned char buf[256];
07313    int bytes=0;
07314 
07315    if (ast_adsi_available(chan))
07316    {
07317       bytes += adsi_logo(buf + bytes);
07318       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
07319       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07320       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07321       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07322       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07323    }
07324    while ((cmd >= 0) && (cmd != 't')) {
07325       if (cmd)
07326          retries = 0;
07327       switch (cmd) {
07328       case '1':
07329          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07330          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07331          break;
07332       case '2': 
07333          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07334          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07335          break;
07336       case '3': 
07337          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07338          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07339          break;
07340       case '4': 
07341          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
07342          break;
07343       case '5':
07344          if (vmu->password[0] == '-') {
07345             cmd = ast_play_and_wait(chan, "vm-no");
07346             break;
07347          }
07348          newpassword[1] = '\0';
07349          newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
07350          if (cmd == '#')
07351             newpassword[0] = '\0';
07352          else {
07353             if (cmd < 0)
07354                break;
07355             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
07356                break;
07357             }
07358          }
07359          newpassword2[1] = '\0';
07360          newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
07361          if (cmd == '#')
07362             newpassword2[0] = '\0';
07363          else {
07364             if (cmd < 0)
07365                break;
07366 
07367             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
07368                break;
07369             }
07370          }
07371          if (strcmp(newpassword, newpassword2)) {
07372             ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07373             cmd = ast_play_and_wait(chan, "vm-mismatch");
07374             if (!cmd) {
07375                cmd = ast_play_and_wait(chan, "vm-pls-try-again");
07376             }
07377             break;
07378          }
07379          if (ast_strlen_zero(ext_pass_cmd)) 
07380             vm_change_password(vmu,newpassword);
07381          else 
07382             vm_change_password_shell(vmu,newpassword);
07383          if (option_debug > 2)
07384             ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
07385          cmd = ast_play_and_wait(chan,"vm-passchanged");
07386          break;
07387       case '*': 
07388          cmd = 't';
07389          break;
07390       default: 
07391          cmd = 0;
07392          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07393          RETRIEVE(prefile, -1, vmu);
07394          if (ast_fileexists(prefile, NULL, NULL))
07395             cmd = ast_play_and_wait(chan, "vm-tmpexists");
07396          DISPOSE(prefile, -1);
07397          if (!cmd)
07398             cmd = ast_play_and_wait(chan, "vm-options");
07399          if (!cmd)
07400             cmd = ast_waitfordigit(chan,6000);
07401          if (!cmd)
07402             retries++;
07403          if (retries > 3)
07404             cmd = 't';
07405       }
07406    }
07407    if (cmd == 't')
07408       cmd = 0;
07409    return cmd;
07410 }
07411 
07412 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07413 {
07414    int res;
07415    int cmd = 0;
07416    int retries = 0;
07417    int duration = 0;
07418    char prefile[PATH_MAX] = "";
07419    unsigned char buf[256];
07420    char dest[PATH_MAX];
07421    int bytes = 0;
07422 
07423    if (ast_adsi_available(chan)) {
07424       bytes += adsi_logo(buf + bytes);
07425       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
07426       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07427       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07428       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07429       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07430    }
07431 
07432    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07433    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
07434       ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
07435       return -1;
07436    }
07437    while ((cmd >= 0) && (cmd != 't')) {
07438       if (cmd)
07439          retries = 0;
07440       RETRIEVE(prefile, -1, vmu);
07441       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
07442          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07443          cmd = 't';  
07444       } else {
07445          switch (cmd) {
07446          case '1':
07447             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07448             break;
07449          case '2':
07450             DELETE(prefile, -1, prefile, vmu);
07451             ast_play_and_wait(chan, "vm-tempremoved");
07452             cmd = 't';  
07453             break;
07454          case '*': 
07455             cmd = 't';
07456             break;
07457          default:
07458             cmd = ast_play_and_wait(chan,
07459                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
07460                   "vm-tempgreeting2" : "vm-tempgreeting");
07461             if (!cmd)
07462                cmd = ast_waitfordigit(chan,6000);
07463             if (!cmd)
07464                retries++;
07465             if (retries > 3)
07466                cmd = 't';
07467          }
07468       }
07469       DISPOSE(prefile, -1);
07470    }
07471    if (cmd == 't')
07472       cmd = 0;
07473    return cmd;
07474 }
07475 
07476 /* Default English syntax */
07477 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07478 {
07479    int cmd=0;
07480 
07481    if (vms->lastmsg > -1) {
07482       cmd = play_message(chan, vmu, vms);
07483    } else {
07484       cmd = ast_play_and_wait(chan, "vm-youhave");
07485       if (!cmd) 
07486          cmd = ast_play_and_wait(chan, "vm-no");
07487       if (!cmd) {
07488          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07489          cmd = ast_play_and_wait(chan, vms->fn);
07490       }
07491       if (!cmd)
07492          cmd = ast_play_and_wait(chan, "vm-messages");
07493    }
07494    return cmd;
07495 }
07496 
07497 /* Hebrew Syntax */
07498 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07499 {
07500    int cmd = 0;
07501 
07502    if (vms->lastmsg > -1) {
07503       cmd = play_message(chan, vmu, vms);
07504    } else {
07505       if (!strcasecmp(vms->fn, "INBOX")) {
07506          cmd = ast_play_and_wait(chan, "vm-nonewmessages");
07507       } else {
07508          cmd = ast_play_and_wait(chan, "vm-nomessages");
07509       }
07510    }
07511    return cmd;
07512 }
07513 
07514 /* Common LATIN languages syntax */
07515 static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07516 {
07517    int cmd=0;
07518 
07519    if (vms->lastmsg > -1) {
07520       cmd = play_message(chan, vmu, vms);
07521    } else {
07522       cmd = ast_play_and_wait(chan, "vm-youhaveno");
07523       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
07524          if (!cmd) {
07525             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
07526             cmd = ast_play_and_wait(chan, vms->fn);
07527          }
07528          if (!cmd)
07529             cmd = ast_play_and_wait(chan, "vm-messages");
07530       } else {
07531          if (!cmd)
07532             cmd = ast_play_and_wait(chan, "vm-messages");
07533          if (!cmd) {
07534             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07535             cmd = ast_play_and_wait(chan, vms->fn);
07536          }
07537       }
07538    }
07539    return cmd;
07540 }
07541 
07542 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07543 {
07544    if (!strncasecmp(chan->language, "es", 2) ||
07545          !strncasecmp(chan->language, "it", 2) ||
07546          !strncasecmp(chan->language, "pt", 2) ||
07547          !strncasecmp(chan->language, "gr", 2)) { /* SPANISH, ITALIAN, PORTUGUESE or GREEK */
07548       return vm_browse_messages_latin(chan, vms, vmu);
07549    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW */
07550       return vm_browse_messages_he(chan, vms, vmu);
07551    } else {                                             /* Default to English syntax */
07552       return vm_browse_messages_en(chan, vms, vmu);
07553    }
07554 }
07555 
07556 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
07557          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
07558          int skipuser, int maxlogins, int silent)
07559 {
07560    int useadsi=0, valid=0, logretries=0;
07561    char password[AST_MAX_EXTENSION]="", *passptr;
07562    struct ast_vm_user vmus, *vmu = NULL;
07563 
07564    /* If ADSI is supported, setup login screen */
07565    adsi_begin(chan, &useadsi);
07566    if (!skipuser && useadsi)
07567       adsi_login(chan);
07568    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
07569       ast_log(LOG_WARNING, "Couldn't stream login file\n");
07570       return -1;
07571    }
07572    
07573    /* Authenticate them and get their mailbox/password */
07574    
07575    while (!valid && (logretries < maxlogins)) {
07576       /* Prompt for, and read in the username */
07577       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
07578          ast_log(LOG_WARNING, "Couldn't read username\n");
07579          return -1;
07580       }
07581       if (ast_strlen_zero(mailbox)) {
07582          if (chan->cid.cid_num) {
07583             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
07584          } else {
07585             if (option_verbose > 2)
07586                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");  
07587             return -1;
07588          }
07589       }
07590       if (useadsi)
07591          adsi_password(chan);
07592 
07593       if (!ast_strlen_zero(prefix)) {
07594          char fullusername[80] = "";
07595          ast_copy_string(fullusername, prefix, sizeof(fullusername));
07596          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
07597          ast_copy_string(mailbox, fullusername, mailbox_size);
07598       }
07599 
07600       if (option_debug)
07601          ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
07602       vmu = find_user(&vmus, context, mailbox);
07603       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
07604          /* saved password is blank, so don't bother asking */
07605          password[0] = '\0';
07606       } else {
07607          if (ast_streamfile(chan, "vm-password", chan->language)) {
07608             ast_log(LOG_WARNING, "Unable to stream password file\n");
07609             return -1;
07610          }
07611          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
07612             ast_log(LOG_WARNING, "Unable to read password\n");
07613             return -1;
07614          }
07615       }
07616 
07617       if (vmu) {
07618          passptr = vmu->password;
07619          if (passptr[0] == '-') passptr++;
07620       }
07621       if (vmu && !strcmp(passptr, password))
07622          valid++;
07623       else {
07624          if (option_verbose > 2)
07625             ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
07626          if (!ast_strlen_zero(prefix))
07627             mailbox[0] = '\0';
07628       }
07629       logretries++;
07630       if (!valid) {
07631          if (skipuser || logretries >= maxlogins) {
07632             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
07633                ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
07634                return -1;
07635             }
07636          } else {
07637             if (useadsi)
07638                adsi_login(chan);
07639             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
07640                ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
07641                return -1;
07642             }
07643          }
07644          if (ast_waitstream(chan, "")) /* Channel is hung up */
07645             return -1;
07646       }
07647    }
07648    if (!valid && (logretries >= maxlogins)) {
07649       ast_stopstream(chan);
07650       ast_play_and_wait(chan, "vm-goodbye");
07651       return -1;
07652    }
07653    if (vmu && !skipuser) {
07654       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
07655    }
07656    return 0;
07657 }
07658 
07659 static int vm_execmain(struct ast_channel *chan, void *data)
07660 {
07661    /* XXX This is, admittedly, some pretty horrendus code.  For some
07662       reason it just seemed a lot easier to do with GOTO's.  I feel
07663       like I'm back in my GWBASIC days. XXX */
07664    int res=-1;
07665    int cmd=0;
07666    int valid = 0;
07667    struct ast_module_user *u;
07668    char prefixstr[80] ="";
07669    char ext_context[256]="";
07670    int box;
07671    int useadsi = 0;
07672    int skipuser = 0;
07673    struct vm_state vms;
07674    struct ast_vm_user *vmu = NULL, vmus;
07675    char *context=NULL;
07676    int silentexit = 0;
07677    struct ast_flags flags = { 0 };
07678    signed char record_gain = 0;
07679    int play_auto = 0;
07680    int play_folder = 0;
07681 #ifdef IMAP_STORAGE
07682    int deleted = 0;
07683 #endif
07684    u = ast_module_user_add(chan);
07685 
07686    /* Add the vm_state to the active list and keep it active */
07687    memset(&vms, 0, sizeof(vms));
07688    vms.lastmsg = -1;
07689 
07690    memset(&vmus, 0, sizeof(vmus));
07691 
07692    if (chan->_state != AST_STATE_UP) {
07693       if (option_debug)
07694          ast_log(LOG_DEBUG, "Before ast_answer\n");
07695       ast_answer(chan);
07696    }
07697 
07698    if (!ast_strlen_zero(data)) {
07699       char *opts[OPT_ARG_ARRAY_SIZE];
07700       char *parse;
07701       AST_DECLARE_APP_ARGS(args,
07702          AST_APP_ARG(argv0);
07703          AST_APP_ARG(argv1);
07704       );
07705 
07706       parse = ast_strdupa(data);
07707 
07708       AST_STANDARD_APP_ARGS(args, parse);
07709 
07710       if (args.argc == 2) {
07711          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
07712             ast_module_user_remove(u);
07713             return -1;
07714          }
07715          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
07716             int gain;
07717             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
07718                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
07719                   ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
07720                   ast_module_user_remove(u);
07721                   return -1;
07722                } else {
07723                   record_gain = (signed char) gain;
07724                }
07725             } else {
07726                ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
07727             }
07728          }
07729          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
07730             play_auto = 1;
07731             if (opts[OPT_ARG_PLAYFOLDER]) {
07732                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
07733                   ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
07734                }
07735             } else {
07736                ast_log(LOG_WARNING, "Invalid folder set with option a\n");
07737             }  
07738             if ( play_folder > 9 || play_folder < 0) {
07739                ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
07740                play_folder = 0;
07741             }
07742          }
07743       } else {
07744          /* old style options parsing */
07745          while (*(args.argv0)) {
07746             if (*(args.argv0) == 's')
07747                ast_set_flag(&flags, OPT_SILENT);
07748             else if (*(args.argv0) == 'p')
07749                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
07750             else 
07751                break;
07752             (args.argv0)++;
07753          }
07754 
07755       }
07756 
07757       valid = ast_test_flag(&flags, OPT_SILENT);
07758 
07759       if ((context = strchr(args.argv0, '@')))
07760          *context++ = '\0';
07761 
07762       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
07763          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
07764       else
07765          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
07766 
07767       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
07768          skipuser++;
07769       else
07770          valid = 0;
07771    }
07772 
07773    if (!valid)
07774       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
07775 
07776    if (option_debug)
07777       ast_log(LOG_DEBUG, "After vm_authenticate\n");
07778    if (!res) {
07779       valid = 1;
07780       if (!skipuser)
07781          vmu = &vmus;
07782    } else {
07783       res = 0;
07784    }
07785 
07786    /* If ADSI is supported, setup login screen */
07787    adsi_begin(chan, &useadsi);
07788 
07789    if (!valid) {
07790       goto out;
07791    }
07792 
07793 #ifdef IMAP_STORAGE
07794    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
07795    pthread_setspecific(ts_vmstate.key, &vms);
07796 
07797    vms.interactive = 1;
07798    vms.updated = 1;
07799    if (vmu)
07800       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
07801    vmstate_insert(&vms);
07802    init_vm_state(&vms);
07803 #endif
07804 
07805    /* Set language from config to override channel language */
07806    if (!ast_strlen_zero(vmu->language))
07807       ast_string_field_set(chan, language, vmu->language);
07808    create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
07809    /* Retrieve old and new message counts */
07810    if (option_debug)
07811       ast_log(LOG_DEBUG, "Before open_mailbox\n");
07812    res = open_mailbox(&vms, vmu, 1);
07813    if (res < 0)
07814       goto out;
07815    vms.oldmessages = vms.lastmsg + 1;
07816    if (option_debug > 2)
07817       ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
07818    /* Start in INBOX */
07819    res = open_mailbox(&vms, vmu, 0);
07820    if (res < 0)
07821       goto out;
07822    vms.newmessages = vms.lastmsg + 1;
07823    if (option_debug > 2)
07824       ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
07825       
07826    /* Select proper mailbox FIRST!! */
07827    if (play_auto) {
07828       res = open_mailbox(&vms, vmu, play_folder);
07829       if (res < 0)
07830          goto out;
07831 
07832       /* If there are no new messages, inform the user and hangup */
07833       if (vms.lastmsg == -1) {
07834          cmd = vm_browse_messages(chan, &vms, vmu);
07835          res = 0;
07836          goto out;
07837       }
07838    } else {
07839       if (!vms.newmessages && vms.oldmessages) {
07840          /* If we only have old messages start here */
07841          res = open_mailbox(&vms, vmu, 1);
07842          play_folder = 1;
07843          if (res < 0)
07844             goto out;
07845       }
07846    }
07847 
07848    if (useadsi)
07849       adsi_status(chan, &vms);
07850    res = 0;
07851 
07852    /* Check to see if this is a new user */
07853    if (!strcasecmp(vmu->mailbox, vmu->password) && 
07854       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
07855       if (ast_play_and_wait(chan, "vm-newuser") == -1)
07856          ast_log(LOG_WARNING, "Couldn't stream new user file\n");
07857       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
07858       if ((cmd == 't') || (cmd == '#')) {
07859          /* Timeout */
07860          res = 0;
07861          goto out;
07862       } else if (cmd < 0) {
07863          /* Hangup */
07864          res = -1;
07865          goto out;
07866       }
07867    }
07868 #ifdef IMAP_STORAGE
07869       if (option_debug > 2)
07870          ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
07871       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
07872          if (option_debug)
07873             ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
07874          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
07875       }
07876       if (option_debug > 2)
07877          ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
07878       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
07879          ast_log(LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
07880          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
07881       }
07882 #endif
07883    if (play_auto) {
07884       cmd = '1';
07885    } else {
07886       cmd = vm_intro(chan, vmu, &vms);
07887    }
07888 
07889    vms.repeats = 0;
07890    vms.starting = 1;
07891    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
07892       /* Run main menu */
07893       switch (cmd) {
07894       case '1':
07895          vms.curmsg = 0;
07896          /* Fall through */
07897       case '5':
07898          cmd = vm_browse_messages(chan, &vms, vmu);
07899          break;
07900       case '2': /* Change folders */
07901          if (useadsi)
07902             adsi_folders(chan, 0, "Change to folder...");
07903          cmd = get_folder2(chan, "vm-changeto", 0);
07904          if (cmd == '#') {
07905             cmd = 0;
07906          } else if (cmd > 0) {
07907             cmd = cmd - '0';
07908             res = close_mailbox(&vms, vmu);
07909             if (res == ERROR_LOCK_PATH)
07910                goto out;
07911             res = open_mailbox(&vms, vmu, cmd);
07912             if (res < 0)
07913                goto out;
07914             play_folder = cmd;
07915             cmd = 0;
07916          }
07917          if (useadsi)
07918             adsi_status2(chan, &vms);
07919             
07920          if (!cmd)
07921             cmd = vm_play_folder_name(chan, vms.vmbox);
07922 
07923          vms.starting = 1;
07924          break;
07925       case '3': /* Advanced options */
07926          cmd = 0;
07927          vms.repeats = 0;
07928          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
07929             switch (cmd) {
07930             case '1': /* Reply */
07931                if (vms.lastmsg > -1 && !vms.starting) {
07932                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
07933                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
07934                      res = cmd;
07935                      goto out;
07936                   }
07937                } else
07938                   cmd = ast_play_and_wait(chan, "vm-sorry");
07939                cmd = 't';
07940                break;
07941             case '2': /* Callback */
07942                if (option_verbose > 2 && !vms.starting)
07943                   ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
07944                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
07945                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
07946                   if (cmd == 9) {
07947                      silentexit = 1;
07948                      goto out;
07949                   } else if (cmd == ERROR_LOCK_PATH) {
07950                      res = cmd;
07951                      goto out;
07952                   }
07953                }
07954                else 
07955                   cmd = ast_play_and_wait(chan, "vm-sorry");
07956                cmd = 't';
07957                break;
07958             case '3': /* Envelope */
07959                if (vms.lastmsg > -1 && !vms.starting) {
07960                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
07961                   if (cmd == ERROR_LOCK_PATH) {
07962                      res = cmd;
07963                      goto out;
07964                   }
07965                } else
07966                   cmd = ast_play_and_wait(chan, "vm-sorry");
07967                cmd = 't';
07968                break;
07969             case '4': /* Dialout */
07970                if (!ast_strlen_zero(vmu->dialout)) {
07971                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
07972                   if (cmd == 9) {
07973                      silentexit = 1;
07974                      goto out;
07975                   }
07976                }
07977                else 
07978                   cmd = ast_play_and_wait(chan, "vm-sorry");
07979                cmd = 't';
07980                break;
07981 
07982             case '5': /* Leave VoiceMail */
07983                if (ast_test_flag(vmu, VM_SVMAIL)) {
07984                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
07985                   if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
07986                      res = cmd;
07987                      goto out;
07988                   }
07989                } else
07990                   cmd = ast_play_and_wait(chan,"vm-sorry");
07991                cmd='t';
07992                break;
07993                
07994             case '*': /* Return to main menu */
07995                cmd = 't';
07996                break;
07997 
07998             default:
07999                cmd = 0;
08000                if (!vms.starting) {
08001                   cmd = ast_play_and_wait(chan, "vm-toreply");
08002                }
08003                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
08004                   cmd = ast_play_and_wait(chan, "vm-tocallback");
08005                }
08006                if (!cmd && !vms.starting) {
08007                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
08008                }
08009                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
08010                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
08011                }
08012                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
08013                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
08014                if (!cmd)
08015                   cmd = ast_play_and_wait(chan, "vm-starmain");
08016                if (!cmd)
08017                   cmd = ast_waitfordigit(chan,6000);
08018                if (!cmd)
08019                   vms.repeats++;
08020                if (vms.repeats > 3)
08021                   cmd = 't';
08022             }
08023          }
08024          if (cmd == 't') {
08025             cmd = 0;
08026             vms.repeats = 0;
08027          }
08028          break;
08029       case '4':
08030          if (vms.curmsg > 0) {
08031             vms.curmsg--;
08032             cmd = play_message(chan, vmu, &vms);
08033          } else {
08034             cmd = ast_play_and_wait(chan, "vm-nomore");
08035          }
08036          break;
08037       case '6':
08038          if (vms.curmsg < vms.lastmsg) {
08039             vms.curmsg++;
08040             cmd = play_message(chan, vmu, &vms);
08041          } else {
08042             cmd = ast_play_and_wait(chan, "vm-nomore");
08043          }
08044          break;
08045       case '7':
08046          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
08047             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
08048             if (useadsi)
08049                adsi_delete(chan, &vms);
08050             if (vms.deleted[vms.curmsg]) {
08051                if (play_folder == 0)
08052                   vms.newmessages--;
08053                else if (play_folder == 1)
08054                   vms.oldmessages--;
08055                cmd = ast_play_and_wait(chan, "vm-deleted");
08056             }
08057             else {
08058                if (play_folder == 0)
08059                   vms.newmessages++;
08060                else if (play_folder == 1)
08061                   vms.oldmessages++;
08062                cmd = ast_play_and_wait(chan, "vm-undeleted");
08063             }
08064             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
08065                if (vms.curmsg < vms.lastmsg) {
08066                   vms.curmsg++;
08067                   cmd = play_message(chan, vmu, &vms);
08068                } else {
08069                   cmd = ast_play_and_wait(chan, "vm-nomore");
08070                }
08071             }
08072          } else /* Delete not valid if we haven't selected a message */
08073             cmd = 0;
08074 #ifdef IMAP_STORAGE
08075          deleted = 1;
08076 #endif
08077          break;
08078    
08079       case '8':
08080          if (vms.lastmsg > -1) {
08081             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
08082             if (cmd == ERROR_LOCK_PATH) {
08083                res = cmd;
08084                goto out;
08085             }
08086          } else
08087             cmd = ast_play_and_wait(chan, "vm-nomore");
08088          break;
08089       case '9':
08090          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
08091             /* No message selected */
08092             cmd = 0;
08093             break;
08094          }
08095          if (useadsi)
08096             adsi_folders(chan, 1, "Save to folder...");
08097          cmd = get_folder2(chan, "vm-savefolder", 1);
08098          box = 0; /* Shut up compiler */
08099          if (cmd == '#') {
08100             cmd = 0;
08101             break;
08102          } else if (cmd > 0) {
08103             box = cmd = cmd - '0';
08104             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
08105             if (cmd == ERROR_LOCK_PATH) {
08106                res = cmd;
08107                goto out;
08108 #ifndef IMAP_STORAGE
08109             } else if (!cmd) {
08110                vms.deleted[vms.curmsg] = 1;
08111 #endif
08112             } else {
08113                vms.deleted[vms.curmsg] = 0;
08114                vms.heard[vms.curmsg] = 0;
08115             }
08116          }
08117          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
08118          if (useadsi)
08119             adsi_message(chan, &vms);
08120          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
08121          if (!cmd) {
08122             cmd = ast_play_and_wait(chan, "vm-message");
08123             if (!cmd)
08124                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
08125             if (!cmd)
08126                cmd = ast_play_and_wait(chan, "vm-savedto");
08127             if (!cmd)
08128                cmd = vm_play_folder_name(chan, vms.fn);
08129          } else {
08130             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
08131          }
08132          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
08133             if (vms.curmsg < vms.lastmsg) {
08134                vms.curmsg++;
08135                cmd = play_message(chan, vmu, &vms);
08136             } else {
08137                cmd = ast_play_and_wait(chan, "vm-nomore");
08138             }
08139          }
08140          break;
08141       case '*':
08142          if (!vms.starting) {
08143             cmd = ast_play_and_wait(chan, "vm-onefor");
08144             if (!strncasecmp(chan->language, "he", 2)) {
08145                cmd = ast_play_and_wait(chan, "vm-for");
08146             }
08147             if (!cmd)
08148                cmd = vm_play_folder_name(chan, vms.vmbox);
08149             if (!cmd)
08150                cmd = ast_play_and_wait(chan, "vm-opts");
08151             if (!cmd)
08152                cmd = vm_instructions(chan, &vms, 1);
08153          } else
08154             cmd = 0;
08155          break;
08156       case '0':
08157          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
08158          if (useadsi)
08159             adsi_status(chan, &vms);
08160          break;
08161       default: /* Nothing */
08162          cmd = vm_instructions(chan, &vms, 0);
08163          break;
08164       }
08165    }
08166    if ((cmd == 't') || (cmd == '#')) {
08167       /* Timeout */
08168       res = 0;
08169    } else {
08170       /* Hangup */
08171       res = -1;
08172    }
08173 
08174 out:
08175    if (res > -1) {
08176       ast_stopstream(chan);
08177       adsi_goodbye(chan);
08178       if (valid && res != OPERATOR_EXIT) {
08179          if (silentexit)
08180             res = ast_play_and_wait(chan, "vm-dialout");
08181          else 
08182             res = ast_play_and_wait(chan, "vm-goodbye");
08183       }
08184       if ((valid && res > 0) || res == OPERATOR_EXIT) {
08185          res = 0;
08186       }
08187       if (useadsi)
08188          ast_adsi_unload_session(chan);
08189    }
08190    if (vmu)
08191       close_mailbox(&vms, vmu);
08192    if (valid) {
08193       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
08194       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
08195       run_externnotify(vmu->context, vmu->mailbox);
08196    }
08197 #ifdef IMAP_STORAGE
08198    /* expunge message - use UID Expunge if supported on IMAP server*/
08199    if (option_debug > 2)
08200       ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
08201    if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
08202       ast_mutex_lock(&vms.lock);
08203 #ifdef HAVE_IMAP_TK2006
08204       if (LEVELUIDPLUS (vms.mailstream)) {
08205          mail_expunge_full(vms.mailstream,NIL,EX_UID);
08206       } else 
08207 #endif
08208          mail_expunge(vms.mailstream);
08209       ast_mutex_unlock(&vms.lock);
08210    }
08211    /*  before we delete the state, we should copy pertinent info
08212     *  back to the persistent model */
08213    if (vmu) {
08214       vmstate_delete(&vms);
08215    }
08216 #endif
08217    if (vmu)
08218       free_user(vmu);
08219    if (vms.deleted)
08220       free(vms.deleted);
08221    if (vms.heard)
08222       free(vms.heard);
08223 
08224 #ifdef IMAP_STORAGE
08225    pthread_setspecific(ts_vmstate.key, NULL);
08226 #endif
08227    ast_module_user_remove(u);
08228    return res;
08229 }
08230 
08231 static int vm_exec(struct ast_channel *chan, void *data)
08232 {
08233    int res = 0;
08234    struct ast_module_user *u;
08235    char *tmp;
08236    struct leave_vm_options leave_options;
08237    struct ast_flags flags = { 0 };
08238    static int deprecate_warning = 0;
08239    char *opts[OPT_ARG_ARRAY_SIZE];
08240    AST_DECLARE_APP_ARGS(args,
08241       AST_APP_ARG(argv0);
08242       AST_APP_ARG(argv1);
08243    );
08244 
08245    u = ast_module_user_add(chan);
08246    
08247    memset(&leave_options, 0, sizeof(leave_options));
08248 
08249    if (chan->_state != AST_STATE_UP)
08250       ast_answer(chan);
08251 
08252    if (!ast_strlen_zero(data)) {
08253       tmp = ast_strdupa(data);
08254       AST_STANDARD_APP_ARGS(args, tmp);
08255       if (args.argc == 2) {
08256          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
08257             ast_module_user_remove(u);
08258             return -1;
08259          }
08260          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
08261          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
08262             int gain;
08263 
08264             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
08265                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
08266                ast_module_user_remove(u);
08267                return -1;
08268             } else {
08269                leave_options.record_gain = (signed char) gain;
08270             }
08271          }
08272       } else {
08273          /* old style options parsing */
08274          int old = 0;
08275          char *orig_argv0 = args.argv0;
08276          while (*(args.argv0)) {
08277             if (*(args.argv0) == 's') {
08278                old = 1;
08279                ast_set_flag(&leave_options, OPT_SILENT);
08280             } else if (*(args.argv0) == 'b') {
08281                old = 1;
08282                ast_set_flag(&leave_options, OPT_BUSY_GREETING);
08283             } else if (*(args.argv0) == 'u') {
08284                old = 1;
08285                ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
08286             } else if (*(args.argv0) == 'j') {
08287                old = 1;
08288                ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
08289             } else
08290                break;
08291             (args.argv0)++;
08292          }
08293          if (!deprecate_warning && old) {
08294             deprecate_warning = 1;
08295             ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
08296             ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
08297          }
08298       }
08299    } else {
08300       char tmp[256];
08301       res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
08302       if (res < 0) {
08303          ast_module_user_remove(u);
08304          return res;
08305       }
08306       if (ast_strlen_zero(tmp)) {
08307          ast_module_user_remove(u);
08308          return 0;
08309       }
08310       args.argv0 = ast_strdupa(tmp);
08311    }
08312 
08313    res = leave_voicemail(chan, args.argv0, &leave_options);
08314    if (res == 't') {
08315       ast_play_and_wait(chan, "vm-goodbye");
08316       res = 0;
08317    }
08318 
08319    if (res == OPERATOR_EXIT) {
08320       res = 0;
08321    }
08322 
08323    if (res == ERROR_LOCK_PATH) {
08324       ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
08325       /*Send the call to n+101 priority, where n is the current priority*/
08326       if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
08327          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
08328             ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
08329       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
08330       res = 0;
08331    }
08332    
08333    ast_module_user_remove(u);
08334 
08335    return res;
08336 }
08337 
08338 static struct ast_vm_user *find_or_create(char *context, char *mbox)
08339 {
08340    struct ast_vm_user *vmu;
08341    AST_LIST_TRAVERSE(&users, vmu, list) {
08342       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox)) {
08343          if (strcasecmp(vmu->context, context)) {
08344             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
08345                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
08346                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
08347                   \n\tamend your voicemail.conf file to avoid this situation.\n", mbox);
08348          }
08349          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", mbox);
08350          return NULL;
08351       }
08352       if (!strcasecmp(context, vmu->context) && !strcasecmp(mbox, vmu->mailbox)) {
08353          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", mbox, context);
08354          return NULL;
08355       }
08356    }
08357    
08358    if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
08359       ast_copy_string(vmu->context, context, sizeof(vmu->context));
08360       ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
08361       AST_LIST_INSERT_TAIL(&users, vmu, list);
08362    }
08363    return vmu;
08364 }
08365 
08366 static int append_mailbox(char *context, char *mbox, char *data)
08367 {
08368    /* Assumes lock is already held */
08369    char *tmp;
08370    char *stringp;
08371    char *s;
08372    struct ast_vm_user *vmu;
08373 
08374    tmp = ast_strdupa(data);
08375 
08376    if ((vmu = find_or_create(context, mbox))) {
08377       populate_defaults(vmu);
08378 
08379       stringp = tmp;
08380       if ((s = strsep(&stringp, ","))) 
08381          ast_copy_string(vmu->password, s, sizeof(vmu->password));
08382       if (stringp && (s = strsep(&stringp, ","))) 
08383          ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
08384       if (stringp && (s = strsep(&stringp, ","))) 
08385          ast_copy_string(vmu->email, s, sizeof(vmu->email));
08386       if (stringp && (s = strsep(&stringp, ","))) 
08387          ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
08388       if (stringp && (s = strsep(&stringp, ","))) 
08389          apply_options(vmu, s);
08390    }
08391    return 0;
08392 }
08393 
08394 static int vm_box_exists(struct ast_channel *chan, void *data) 
08395 {
08396    struct ast_module_user *u;
08397    struct ast_vm_user svm;
08398    char *context, *box;
08399    int priority_jump = 0;
08400    AST_DECLARE_APP_ARGS(args,
08401       AST_APP_ARG(mbox);
08402       AST_APP_ARG(options);
08403    );
08404 
08405    if (ast_strlen_zero(data)) {
08406       ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
08407       return -1;
08408    }
08409 
08410    u = ast_module_user_add(chan);
08411 
08412    box = ast_strdupa(data);
08413 
08414    AST_STANDARD_APP_ARGS(args, box);
08415 
08416    if (args.options) {
08417       if (strchr(args.options, 'j'))
08418          priority_jump = 1;
08419    }
08420 
08421    if ((context = strchr(args.mbox, '@'))) {
08422       *context = '\0';
08423       context++;
08424    }
08425 
08426    if (find_user(&svm, context, args.mbox)) {
08427       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
08428       if (priority_jump || ast_opt_priority_jumping)
08429          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) 
08430             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);
08431    } else
08432       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
08433    ast_module_user_remove(u);
08434    return 0;
08435 }
08436 
08437 static int vmauthenticate(struct ast_channel *chan, void *data)
08438 {
08439    struct ast_module_user *u;
08440    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
08441    struct ast_vm_user vmus;
08442    char *options = NULL;
08443    int silent = 0, skipuser = 0;
08444    int res = -1;
08445 
08446    u = ast_module_user_add(chan);
08447    
08448    if (s) {
08449       s = ast_strdupa(s);
08450       user = strsep(&s, "|");
08451       options = strsep(&s, "|");
08452       if (user) {
08453          s = user;
08454          user = strsep(&s, "@");
08455          context = strsep(&s, "");
08456          if (!ast_strlen_zero(user))
08457             skipuser++;
08458          ast_copy_string(mailbox, user, sizeof(mailbox));
08459       }
08460    }
08461 
08462    if (options) {
08463       silent = (strchr(options, 's')) != NULL;
08464    }
08465 
08466    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
08467       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
08468       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
08469       ast_play_and_wait(chan, "auth-thankyou");
08470       res = 0;
08471    }
08472 
08473    ast_module_user_remove(u);
08474    return res;
08475 }
08476 
08477 static char voicemail_show_users_help[] =
08478 "Usage: voicemail show users [for <context>]\n"
08479 "       Lists all mailboxes currently set up\n";
08480 
08481 static char voicemail_show_zones_help[] =
08482 "Usage: voicemail show zones\n"
08483 "       Lists zone message formats\n";
08484 
08485 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
08486 {
08487    struct ast_vm_user *vmu;
08488    char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
08489 
08490    if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
08491    else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
08492 
08493    AST_LIST_LOCK(&users);
08494    if (!AST_LIST_EMPTY(&users)) {
08495       if (argc == 3)
08496          ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
08497       else {
08498          int count = 0;
08499          AST_LIST_TRAVERSE(&users, vmu, list) {
08500             if (!strcmp(argv[4],vmu->context))
08501                count++;
08502          }
08503          if (count) {
08504             ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
08505          } else {
08506             ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
08507             AST_LIST_UNLOCK(&users);
08508             return RESULT_FAILURE;
08509          }
08510       }
08511       AST_LIST_TRAVERSE(&users, vmu, list) {
08512          int newmsgs = 0, oldmsgs = 0;
08513          char count[12], tmp[256] = "";
08514 
08515          if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
08516             snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
08517             inboxcount(tmp, &newmsgs, &oldmsgs);
08518             snprintf(count,sizeof(count),"%d",newmsgs);
08519             ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
08520          }
08521       }
08522    } else {
08523       ast_cli(fd, "There are no voicemail users currently defined\n");
08524       AST_LIST_UNLOCK(&users);
08525       return RESULT_FAILURE;
08526    }
08527    AST_LIST_UNLOCK(&users);
08528    return RESULT_SUCCESS;
08529 }
08530 
08531 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
08532 {
08533    struct vm_zone *zone;
08534    char *output_format = "%-15s %-20s %-45s\n";
08535    int res = RESULT_SUCCESS;
08536 
08537    if (argc != 3)
08538       return RESULT_SHOWUSAGE;
08539 
08540    AST_LIST_LOCK(&zones);
08541    if (!AST_LIST_EMPTY(&zones)) {
08542       ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
08543       AST_LIST_TRAVERSE(&zones, zone, list) {
08544          ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
08545       }
08546    } else {
08547       ast_cli(fd, "There are no voicemail zones currently defined\n");
08548       res = RESULT_FAILURE;
08549    }
08550    AST_LIST_UNLOCK(&zones);
08551 
08552    return res;
08553 }
08554 
08555 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
08556 {
08557    int which = 0;
08558    int wordlen;
08559    struct ast_vm_user *vmu;
08560    const char *context = "";
08561 
08562    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
08563    if (pos > 4)
08564       return NULL;
08565    if (pos == 3)
08566       return (state == 0) ? ast_strdup("for") : NULL;
08567    wordlen = strlen(word);
08568    AST_LIST_TRAVERSE(&users, vmu, list) {
08569       if (!strncasecmp(word, vmu->context, wordlen)) {
08570          if (context && strcmp(context, vmu->context) && ++which > state)
08571             return ast_strdup(vmu->context);
08572          /* ignore repeated contexts ? */
08573          context = vmu->context;
08574       }
08575    }
08576    return NULL;
08577 }
08578 
08579 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
08580    { "show", "voicemail", "users", NULL },
08581    handle_voicemail_show_users, NULL,
08582    NULL, complete_voicemail_show_users };
08583 
08584 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
08585    { "show", "voicemail", "zones", NULL },
08586    handle_voicemail_show_zones, NULL,
08587    NULL, NULL };
08588 
08589 static struct ast_cli_entry cli_voicemail[] = {
08590    { { "voicemail", "show", "users", NULL },
08591    handle_voicemail_show_users, "List defined voicemail boxes",
08592    voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
08593 
08594    { { "voicemail", "show", "zones", NULL },
08595    handle_voicemail_show_zones, "List zone message formats",
08596    voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
08597 };
08598 
08599 static void free_vm_users(void)
08600 {
08601    struct ast_vm_user *cur;
08602    struct vm_zone *zcur;
08603 
08604    AST_LIST_LOCK(&users);
08605    while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
08606       ast_set_flag(cur, VM_ALLOCED);
08607       free_user(cur);
08608    }
08609    AST_LIST_UNLOCK(&users);
08610 
08611    AST_LIST_LOCK(&zones);
08612    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list))) {
08613       free_zone(zcur);
08614    }
08615    AST_LIST_UNLOCK(&zones);
08616 }
08617 
08618 static int load_config(void)
08619 {
08620    struct ast_vm_user *cur;
08621    struct ast_config *cfg, *ucfg;
08622    char *cat;
08623    struct ast_variable *var;
08624    const char *notifystr = NULL;
08625    const char *smdistr = NULL;
08626    const char *astattach;
08627    const char *astsearch;
08628    const char *astsaycid;
08629    const char *send_voicemail;
08630 #ifdef IMAP_STORAGE
08631    const char *imap_server;
08632    const char *imap_port;
08633    const char *imap_flags;
08634    const char *imap_folder;
08635    const char *auth_user;
08636    const char *auth_password;
08637    const char *expunge_on_hangup;
08638    const char *imap_timeout;
08639 #endif
08640    const char *astcallop;
08641    const char *astreview;
08642    const char *asttempgreetwarn;
08643    const char *astskipcmd;
08644    const char *asthearenv;
08645    const char *astsaydurationinfo;
08646    const char *astsaydurationminfo;
08647    const char *silencestr;
08648    const char *maxmsgstr;
08649    const char *astdirfwd;
08650    const char *thresholdstr;
08651    const char *fmt;
08652    const char *astemail;
08653    const char *ucontext;
08654    const char *astmailcmd = SENDMAIL;
08655    const char *astpreprocesscmd;
08656    const char *astpreprocessfmt;
08657    const char *astforcename;
08658    const char *astforcegreet;
08659    const char *s;
08660    char *q,*stringp, *tmp;
08661    const char *dialoutcxt = NULL;
08662    const char *callbackcxt = NULL;  
08663    const char *exitcxt = NULL;   
08664    const char *extpc;
08665    const char *emaildateformatstr;
08666    const char *volgainstr;
08667    int x;
08668    int tmpadsi[4];
08669 
08670    cfg = ast_config_load(VOICEMAIL_CONFIG);
08671 
08672    free_vm_users();
08673 
08674    AST_LIST_LOCK(&users);
08675 
08676    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
08677 
08678    if (cfg) {
08679       /* General settings */
08680 
08681       if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
08682          ucontext = "default";
08683       ast_copy_string(userscontext, ucontext, sizeof(userscontext));
08684       /* Attach voice message to mail message ? */
08685       if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
08686          astattach = "yes";
08687       ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH); 
08688 
08689       if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
08690          astsearch = "no";
08691       ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
08692 
08693       volgain = 0.0;
08694       if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
08695          sscanf(volgainstr, "%30lf", &volgain);
08696 
08697 #ifdef ODBC_STORAGE
08698       strcpy(odbc_database, "asterisk");
08699       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
08700          ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
08701       }
08702       strcpy(odbc_table, "voicemessages");
08703       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
08704          ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
08705       }
08706 #endif      
08707       /* Mail command */
08708       strcpy(mailcmd, SENDMAIL);
08709       if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
08710          ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
08711 
08712       /* Preprocess file before attaching by email */
08713       if ((astpreprocesscmd = ast_variable_retrieve(cfg, "general", "preprocesscmd"))) {
08714          ast_copy_string(preprocesscmd, astpreprocesscmd, sizeof(preprocesscmd));
08715          if ((astpreprocessfmt = ast_variable_retrieve(cfg, "general", "preprocessfmt")))
08716             ast_copy_string(preprocessfmt, astpreprocessfmt, sizeof(preprocessfmt));
08717          else
08718             ast_log(LOG_WARNING, "Empty 'preprocessfmt'. Using default format value\n");
08719       }
08720 
08721       maxsilence = 0;
08722       if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
08723          maxsilence = atoi(silencestr);
08724          if (maxsilence > 0)
08725             maxsilence *= 1000;
08726       }
08727       
08728       if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
08729          maxmsg = MAXMSG;
08730       } else {
08731          maxmsg = atoi(maxmsgstr);
08732          if (maxmsg <= 0) {
08733             ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
08734             maxmsg = MAXMSG;
08735          } else if (maxmsg > MAXMSGLIMIT) {
08736             ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
08737             maxmsg = MAXMSGLIMIT;
08738          }
08739       }
08740 
08741       /* Load date format config for voicemail mail */
08742       if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
08743          ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
08744       }
08745 
08746       /* External password changing command */
08747       if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
08748          ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
08749       }
08750 #ifdef IMAP_STORAGE
08751       /* IMAP server address */
08752       if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
08753          ast_copy_string(imapserver, imap_server, sizeof(imapserver));
08754       } else {
08755          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
08756       }
08757       /* IMAP server port */
08758       if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
08759          ast_copy_string(imapport, imap_port, sizeof(imapport));
08760       } else {
08761          ast_copy_string(imapport,"143", sizeof(imapport));
08762       }
08763       /* IMAP server flags */
08764       if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
08765          ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
08766       }
08767       /* IMAP server master username */
08768       if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
08769          ast_copy_string(authuser, auth_user, sizeof(authuser));
08770       }
08771       /* IMAP server master password */
08772       if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
08773          ast_copy_string(authpassword, auth_password, sizeof(authpassword));
08774       }
08775       /* Expunge on exit */
08776       if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
08777          if (ast_false(expunge_on_hangup))
08778             expungeonhangup = 0;
08779          else
08780             expungeonhangup = 1;
08781       } else {
08782          expungeonhangup = 1;
08783       }
08784       /* IMAP voicemail folder */
08785       if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
08786          ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
08787       } else {
08788          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
08789       }
08790 
08791       /* There is some very unorthodox casting done here. This is due
08792        * to the way c-client handles the argument passed in. It expects a 
08793        * void pointer and casts the pointer directly to a long without
08794        * first dereferencing it. */
08795 
08796       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
08797          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(imap_timeout)));
08798       } else {
08799          mail_parameters(NIL, SET_READTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08800       }
08801 
08802       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
08803          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(imap_timeout)));
08804       } else {
08805          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08806       }
08807 
08808       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
08809          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(imap_timeout)));
08810       } else {
08811          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08812       }
08813 
08814       if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
08815          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(imap_timeout)));
08816       } else {
08817          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
08818       }
08819 
08820       /* Increment configuration version */
08821       imapversion++;
08822 #endif
08823       /* External voicemail notify application */
08824       
08825       if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
08826          ast_copy_string(externnotify, notifystr, sizeof(externnotify));
08827          if (option_debug > 2)
08828             ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
08829          if (!strcasecmp(externnotify, "smdi")) {
08830             if (option_debug)
08831                ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
08832             if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
08833                smdi_iface = ast_smdi_interface_find(smdistr);
08834             } else {
08835                if (option_debug)
08836                   ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
08837                smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
08838             }
08839 
08840             if (!smdi_iface) {
08841                ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
08842                externnotify[0] = '\0';
08843             }
08844          }
08845       } else {
08846          externnotify[0] = '\0';
08847       }
08848 
08849       /* Silence treshold */
08850       silencethreshold = 256;
08851       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
08852          silencethreshold = atoi(thresholdstr);
08853       
08854       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
08855          astemail = ASTERISK_USERNAME;
08856       ast_copy_string(serveremail, astemail, sizeof(serveremail));
08857       
08858       vmmaxmessage = 0;
08859       if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
08860          if (sscanf(s, "%30d", &x) == 1) {
08861             vmmaxmessage = x;
08862          } else {
08863             ast_log(LOG_WARNING, "Invalid max message time length\n");
08864          }
08865       }
08866 
08867       vmminmessage = 0;
08868       if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
08869          if (sscanf(s, "%30d", &x) == 1) {
08870             vmminmessage = x;
08871             if (maxsilence / 1000 >= vmminmessage)
08872                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
08873          } else {
08874             ast_log(LOG_WARNING, "Invalid min message time length\n");
08875          }
08876       }
08877       fmt = ast_variable_retrieve(cfg, "general", "format");
08878       if (!fmt) {
08879          fmt = "wav";   
08880       } else {
08881          tmp = ast_strdupa(fmt);
08882          fmt = ast_format_str_reduce(tmp);
08883          if (!fmt) {
08884             ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
08885             fmt = "wav";
08886          }
08887       }
08888 
08889       ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
08890 
08891       skipms = 3000;
08892       if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
08893          if (sscanf(s, "%30d", &x) == 1) {
08894             maxgreet = x;
08895          } else {
08896             ast_log(LOG_WARNING, "Invalid max message greeting length\n");
08897          }
08898       }
08899 
08900       if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
08901          if (sscanf(s, "%30d", &x) == 1) {
08902             skipms = x;
08903          } else {
08904             ast_log(LOG_WARNING, "Invalid skipms value\n");
08905          }
08906       }
08907 
08908       maxlogins = 3;
08909       if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
08910          if (sscanf(s, "%30d", &x) == 1) {
08911             maxlogins = x;
08912          } else {
08913             ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
08914          }
08915       }
08916 
08917       /* Force new user to record name ? */
08918       if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename"))) 
08919          astforcename = "no";
08920       ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
08921 
08922       /* Force new user to record greetings ? */
08923       if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
08924          astforcegreet = "no";
08925       ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
08926 
08927       if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
08928          if (option_debug > 2)
08929             ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
08930          stringp = ast_strdupa(s);
08931          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
08932             if (!ast_strlen_zero(stringp)) {
08933                q = strsep(&stringp,",");
08934                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
08935                   q++;
08936                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
08937                if (option_debug > 2)
08938                   ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
08939             } else {
08940                cidinternalcontexts[x][0] = '\0';
08941             }
08942          }
08943       }
08944       if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
08945          if (option_debug)
08946             ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
08947          astreview = "no";
08948       }
08949       ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW); 
08950 
08951       /*Temperary greeting reminder */
08952       if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
08953          if (option_debug)
08954             ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
08955          asttempgreetwarn = "no";
08956       } else {
08957          if (option_debug)
08958             ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
08959       }
08960       ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
08961 
08962       if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
08963          if (option_debug)
08964             ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
08965          astcallop = "no";
08966       }
08967       ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);  
08968 
08969       if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
08970          if (option_debug)
08971             ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
08972          astsaycid = "no";
08973       } 
08974       ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID); 
08975 
08976       if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
08977          if (option_debug)
08978             ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
08979          send_voicemail = "no";
08980       }
08981       ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
08982    
08983       if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
08984          if (option_debug)
08985             ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
08986          asthearenv = "yes";
08987       }
08988       ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE); 
08989 
08990       if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
08991          if (option_debug)
08992             ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
08993          astsaydurationinfo = "yes";
08994       }
08995       ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);  
08996 
08997       saydurationminfo = 2;
08998       if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
08999          if (sscanf(astsaydurationminfo, "%30d", &x) == 1) {
09000             saydurationminfo = x;
09001          } else {
09002             ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
09003          }
09004       }
09005 
09006       if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
09007          if (option_debug)
09008             ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
09009          astskipcmd = "no";
09010       }
09011       ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
09012 
09013       if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
09014          ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
09015          if (option_debug)
09016             ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
09017       } else {
09018          dialcontext[0] = '\0';  
09019       }
09020       
09021       if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
09022          ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
09023          if (option_debug)
09024             ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
09025       } else {
09026          callcontext[0] = '\0';
09027       }
09028 
09029       if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
09030          ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
09031          if (option_debug)
09032             ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
09033       } else {
09034          exitcontext[0] = '\0';
09035       }
09036 
09037       if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
09038          astdirfwd = "no";
09039       ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD); 
09040       if ((ucfg = ast_config_load("users.conf"))) {   
09041          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
09042             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
09043                continue;
09044             if ((cur = find_or_create(userscontext, cat))) {
09045                populate_defaults(cur);
09046                apply_options_full(cur, ast_variable_browse(ucfg, cat));
09047                ast_copy_string(cur->context, userscontext, sizeof(cur->context));
09048             }
09049          }
09050          ast_config_destroy(ucfg);
09051       }
09052       cat = ast_category_browse(cfg, NULL);
09053       while (cat) {
09054          if (strcasecmp(cat, "general")) {
09055             var = ast_variable_browse(cfg, cat);
09056             if (strcasecmp(cat, "zonemessages")) {
09057                /* Process mailboxes in this context */
09058                while (var) {
09059                   append_mailbox(cat, var->name, var->value);
09060                   var = var->next;
09061                }
09062             } else {
09063                /* Timezones in this context */
09064                while (var) {
09065                   struct vm_zone *z;
09066                   if ((z = ast_malloc(sizeof(*z)))) {
09067                      char *msg_format, *timezone;
09068                      msg_format = ast_strdupa(var->value);
09069                      timezone = strsep(&msg_format, "|");
09070                      if (msg_format) {
09071                         ast_copy_string(z->name, var->name, sizeof(z->name));
09072                         ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
09073                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
09074                         AST_LIST_LOCK(&zones);
09075                         AST_LIST_INSERT_HEAD(&zones, z, list);
09076                         AST_LIST_UNLOCK(&zones);
09077                      } else {
09078                         ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
09079                         free(z);
09080                      }
09081                   } else {
09082                      free(z);
09083                      AST_LIST_UNLOCK(&users);
09084                      ast_config_destroy(cfg);
09085                      return -1;
09086                   }
09087                   var = var->next;
09088                }
09089             }
09090          }
09091          cat = ast_category_browse(cfg, cat);
09092       }
09093       memset(fromstring,0,sizeof(fromstring));
09094       memset(pagerfromstring,0,sizeof(pagerfromstring));
09095       memset(emailtitle,0,sizeof(emailtitle));
09096       strcpy(charset, "ISO-8859-1");
09097       if (emailbody) {
09098          free(emailbody);
09099          emailbody = NULL;
09100       }
09101       if (emailsubject) {
09102          free(emailsubject);
09103          emailsubject = NULL;
09104       }
09105       if (pagerbody) {
09106          free(pagerbody);
09107          pagerbody = NULL;
09108       }
09109       if (pagersubject) {
09110          free(pagersubject);
09111          pagersubject = NULL;
09112       }
09113       if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
09114          ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
09115       if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
09116          ast_copy_string(fromstring,s,sizeof(fromstring));
09117       if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
09118          ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
09119       if ((s = ast_variable_retrieve(cfg, "general", "charset")))
09120          ast_copy_string(charset,s,sizeof(charset));
09121       if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
09122          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
09123          for (x = 0; x < 4; x++) {
09124             memcpy(&adsifdn[x], &tmpadsi[x], 1);
09125          }
09126       }
09127       if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
09128          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
09129          for (x = 0; x < 4; x++) {
09130             memcpy(&adsisec[x], &tmpadsi[x], 1);
09131          }
09132       }
09133       if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
09134          if (atoi(s)) {
09135             adsiver = atoi(s);
09136          }
09137       if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
09138          ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
09139          ast_copy_string(emailtitle,s,sizeof(emailtitle));
09140       }
09141       if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
09142          emailsubject = ast_strdup(s);
09143       if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
09144          char *tmpread, *tmpwrite;
09145          emailbody = ast_strdup(s);
09146 
09147          /* substitute strings \t and \n into the appropriate characters */
09148          tmpread = tmpwrite = emailbody;
09149          while ((tmpwrite = strchr(tmpread,'\\'))) {
09150             switch (tmpwrite[1]) {
09151             case 'r':
09152                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09153                *tmpwrite = '\r';
09154                break;
09155             case 'n':
09156                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09157                *tmpwrite = '\n';
09158                break;
09159             case 't':
09160                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09161                *tmpwrite = '\t';
09162                break;
09163             default:
09164                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
09165             }
09166             tmpread = tmpwrite + 1;
09167          }
09168       }
09169       if ((s = ast_variable_retrieve(cfg, "general", "tz"))) {
09170          ast_copy_string(zonetag, s, sizeof(zonetag));
09171       }
09172       if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
09173          pagersubject = ast_strdup(s);
09174       if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
09175          char *tmpread, *tmpwrite;
09176          pagerbody = ast_strdup(s);
09177 
09178          /* substitute strings \t and \n into the appropriate characters */
09179          tmpread = tmpwrite = pagerbody;
09180          while ((tmpwrite = strchr(tmpread, '\\'))) {
09181             switch (tmpwrite[1]) {
09182             case 'r':
09183                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09184                *tmpwrite = '\r';
09185                break;
09186             case 'n':
09187                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09188                *tmpwrite = '\n';
09189                break;
09190             case 't':
09191                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
09192                *tmpwrite = '\t';
09193                break;
09194             default:
09195                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
09196             }
09197             tmpread = tmpwrite + 1;
09198          }
09199       }
09200       AST_LIST_UNLOCK(&users);
09201       ast_config_destroy(cfg);
09202       return 0;
09203    } else {
09204       AST_LIST_UNLOCK(&users);
09205       ast_log(LOG_WARNING, "Failed to load configuration file.\n");
09206       return 0;
09207    }
09208 }
09209 
09210 static int reload(void)
09211 {
09212    return(load_config());
09213 }
09214 
09215 static int unload_module(void)
09216 {
09217    int res;
09218    
09219    res = ast_unregister_application(app);
09220    res |= ast_unregister_application(app2);
09221    res |= ast_unregister_application(app3);
09222    res |= ast_unregister_application(app4);
09223    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
09224    ast_uninstall_vm_functions();
09225    ao2_ref(inprocess_container, -1);
09226    
09227    ast_module_user_hangup_all();
09228 
09229    return res;
09230 }
09231 
09232 static int load_module(void)
09233 {
09234    int res;
09235    char *adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
09236    char *smdi_loaded = ast_module_helper("", "res_smdi", 0, 0, 0, 0);
09237    free(adsi_loaded);
09238    free(smdi_loaded);
09239 
09240    if (!adsi_loaded) {
09241       ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
09242       return AST_MODULE_LOAD_DECLINE;
09243    }
09244 
09245    if (!smdi_loaded) {
09246       ast_log(LOG_ERROR, "app_voicemail.so depends upon res_smdi.so\n");
09247       return AST_MODULE_LOAD_DECLINE;
09248    }
09249 
09250    if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
09251       return AST_MODULE_LOAD_DECLINE;
09252    }
09253 
09254    my_umask = umask(0);
09255    umask(my_umask);
09256    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
09257    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
09258    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
09259    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
09260    if (res)
09261       return(res);
09262 
09263    if ((res=load_config())) {
09264       return(res);
09265    }
09266 
09267    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
09268 
09269    /* compute the location of the voicemail spool directory */
09270    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
09271 
09272    ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
09273 
09274    return res;
09275 }
09276 
09277 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
09278 {
09279    int cmd = 0;
09280    char destination[80] = "";
09281    int retries = 0;
09282 
09283    if (!num) {
09284       if (option_verbose > 2)
09285          ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
09286       while (retries < 3 && cmd != 't') {
09287          destination[1] = '\0';
09288          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
09289          if (!cmd)
09290             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
09291          if (!cmd)
09292             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
09293          if (!cmd) {
09294             cmd = ast_waitfordigit(chan, 6000);
09295             if (cmd)
09296                destination[0] = cmd;
09297          }
09298          if (!cmd) {
09299             retries++;
09300          } else {
09301 
09302             if (cmd < 0)
09303                return 0;
09304             if (cmd == '*') {
09305                if (option_verbose > 2)
09306                   ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
09307                return 0;
09308             }
09309             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
09310                retries++;
09311             else
09312                cmd = 't';
09313          }
09314       }
09315       if (retries >= 3) {
09316          return 0;
09317       }
09318       
09319    } else {
09320       if (option_verbose > 2)
09321          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
09322       ast_copy_string(destination, num, sizeof(destination));
09323    }
09324 
09325    if (!ast_strlen_zero(destination)) {
09326       if (destination[strlen(destination) -1 ] == '*')
09327          return 0; 
09328       if (option_verbose > 2)
09329          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
09330       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
09331       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
09332       chan->priority = 0;
09333       return 9;
09334    }
09335    return 0;
09336 }
09337 
09338 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)
09339 {
09340    int res = 0;
09341    char filename[PATH_MAX];
09342    struct ast_config *msg_cfg = NULL;
09343    const char *origtime, *context;
09344    char *cid, *name, *num;
09345    int retries = 0;
09346 
09347    vms->starting = 0; 
09348    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
09349 
09350    /* Retrieve info from VM attribute file */
09351    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
09352    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
09353    RETRIEVE(vms->curdir, vms->curmsg, vmu);
09354    msg_cfg = ast_config_load(filename);
09355    DISPOSE(vms->curdir, vms->curmsg);
09356    if (!msg_cfg) {
09357       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
09358       return 0;
09359    }
09360 
09361    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
09362       ast_config_destroy(msg_cfg);
09363       return 0;
09364    }
09365 
09366    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
09367 
09368    context = ast_variable_retrieve(msg_cfg, "message", "context");
09369    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
09370       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
09371    switch (option) {
09372    case 3:
09373       if (!res)
09374          res = play_message_datetime(chan, vmu, origtime, filename);
09375       if (!res)
09376          res = play_message_callerid(chan, vms, cid, context, 0);
09377 
09378       res = 't';
09379       break;
09380 
09381    case 2:  /* Call back */
09382 
09383       if (ast_strlen_zero(cid))
09384          break;
09385 
09386       ast_callerid_parse(cid, &name, &num);
09387       while ((res > -1) && (res != 't')) {
09388          switch (res) {
09389          case '1':
09390             if (num) {
09391                /* Dial the CID number */
09392                res = dialout(chan, vmu, num, vmu->callback);
09393                if (res) {
09394                   ast_config_destroy(msg_cfg);
09395                   return 9;
09396                }
09397             } else {
09398                res = '2';
09399             }
09400             break;
09401 
09402          case '2':
09403             /* Want to enter a different number, can only do this if there's a dialout context for this user */
09404             if (!ast_strlen_zero(vmu->dialout)) {
09405                res = dialout(chan, vmu, NULL, vmu->dialout);
09406                if (res) {
09407                   ast_config_destroy(msg_cfg);
09408                   return 9;
09409                }
09410             } else {
09411                if (option_verbose > 2)
09412                   ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
09413                res = ast_play_and_wait(chan, "vm-sorry");
09414             }
09415             ast_config_destroy(msg_cfg);
09416             return res;
09417          case '*':
09418             res = 't';
09419             break;
09420          case '3':
09421          case '4':
09422          case '5':
09423          case '6':
09424          case '7':
09425          case '8':
09426          case '9':
09427          case '0':
09428 
09429             res = ast_play_and_wait(chan, "vm-sorry");
09430             retries++;
09431             break;
09432          default:
09433             if (num) {
09434                if (option_verbose > 2)
09435                   ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
09436                res = ast_play_and_wait(chan, "vm-num-i-have");
09437                if (!res)
09438                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
09439                if (!res)
09440                   res = ast_play_and_wait(chan, "vm-tocallnum");
09441                /* Only prompt for a caller-specified number if there is a dialout context specified */
09442                if (!ast_strlen_zero(vmu->dialout)) {
09443                   if (!res)
09444                      res = ast_play_and_wait(chan, "vm-calldiffnum");
09445                }
09446             } else {
09447                res = ast_play_and_wait(chan, "vm-nonumber");
09448                if (!ast_strlen_zero(vmu->dialout)) {
09449                   if (!res)
09450                      res = ast_play_and_wait(chan, "vm-toenternumber");
09451                }
09452             }
09453             if (!res)
09454                res = ast_play_and_wait(chan, "vm-star-cancel");
09455             if (!res)
09456                res = ast_waitfordigit(chan, 6000);
09457             if (!res) {
09458                retries++;
09459                if (retries > 3)
09460                   res = 't';
09461             }
09462             break; 
09463             
09464          }
09465          if (res == 't')
09466             res = 0;
09467          else if (res == '*')
09468             res = -1;
09469       }
09470       break;
09471       
09472    case 1:  /* Reply */
09473       /* Send reply directly to sender */
09474       if (ast_strlen_zero(cid))
09475          break;
09476 
09477       ast_callerid_parse(cid, &name, &num);
09478       if (!num) {
09479          if (option_verbose > 2)
09480             ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
09481          if (!res)
09482             res = ast_play_and_wait(chan, "vm-nonumber");
09483          ast_config_destroy(msg_cfg);
09484          return res;
09485       } else {
09486          struct ast_vm_user vmu2;
09487          if (find_user(&vmu2, vmu->context, num)) {
09488             struct leave_vm_options leave_options;
09489             char mailbox[AST_MAX_EXTENSION * 2 + 2];
09490             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
09491 
09492             if (option_verbose > 2)
09493                ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
09494             
09495             memset(&leave_options, 0, sizeof(leave_options));
09496             leave_options.record_gain = record_gain;
09497             res = leave_voicemail(chan, mailbox, &leave_options);
09498             if (!res)
09499                res = 't';
09500             ast_config_destroy(msg_cfg);
09501             return res;
09502          } else {
09503             /* Sender has no mailbox, can't reply */
09504             if (option_verbose > 2)
09505                ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
09506             ast_play_and_wait(chan, "vm-nobox");
09507             res = 't';
09508             ast_config_destroy(msg_cfg);
09509             return res;
09510          }
09511       } 
09512       res = 0;
09513 
09514       break;
09515    }
09516 
09517 #ifndef IMAP_STORAGE
09518    ast_config_destroy(msg_cfg);
09519 
09520    if (!res) {
09521       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
09522       vms->heard[msg] = 1;
09523       res = wait_file(chan, vms, vms->fn);
09524    }
09525 #endif
09526    return res;
09527 }
09528 
09529 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
09530          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
09531          signed char record_gain, struct vm_state *vms)
09532 {
09533    /* Record message & let caller review or re-record it, or set options if applicable */
09534    int res = 0;
09535    int cmd = 0;
09536    int max_attempts = 3;
09537    int attempts = 0;
09538    int recorded = 0;
09539    int message_exists = 0;
09540    signed char zero_gain = 0;
09541    char tempfile[PATH_MAX];
09542    char *acceptdtmf = "#";
09543    char *canceldtmf = "";
09544    int canceleddtmf = 0;
09545 
09546    /* Note that urgent and private are for flagging messages as such in the future */
09547 
09548    /* barf if no pointer passed to store duration in */
09549    if (duration == NULL) {
09550       ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
09551       return -1;
09552    }
09553 
09554    if (!outsidecaller)
09555       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
09556    else
09557       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
09558 
09559    cmd = '3';  /* Want to start by recording */
09560 
09561    while ((cmd >= 0) && (cmd != 't')) {
09562       switch (cmd) {
09563       case '1':
09564          if (!message_exists) {
09565             /* In this case, 1 is to record a message */
09566             cmd = '3';
09567             break;
09568          } else {
09569             /* Otherwise 1 is to save the existing message */
09570             if (option_verbose > 2)
09571                ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
09572             if (!outsidecaller)
09573                ast_filerename(tempfile, recordfile, NULL);
09574             ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
09575             if (!outsidecaller) {
09576                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
09577                DISPOSE(recordfile, -1);
09578             }
09579             cmd = 't';
09580             return res;
09581          }
09582       case '2':
09583          /* Review */
09584          if (option_verbose > 2)
09585             ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
09586          cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
09587          break;
09588       case '3':
09589          message_exists = 0;
09590          /* Record */
09591          if (recorded == 1) {
09592             if (option_verbose > 2)
09593                ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
09594          } else { 
09595             if (option_verbose > 2)
09596                ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
09597          }
09598          if (recorded && outsidecaller) {
09599             cmd = ast_play_and_wait(chan, INTRO);
09600             cmd = ast_play_and_wait(chan, "beep");
09601          }
09602          recorded = 1;
09603          /* 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 */
09604          if (record_gain)
09605             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
09606          if (ast_test_flag(vmu, VM_OPERATOR))
09607             canceldtmf = "0";
09608          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
09609          if (strchr(canceldtmf, cmd)) {
09610          /* need this flag here to distinguish between pressing '0' during message recording or after */
09611             canceleddtmf = 1;
09612          }
09613          if (record_gain)
09614             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
09615          if (cmd == -1) {
09616             /* User has hung up, no options to give */
09617             if (!outsidecaller) {
09618                /* user was recording a greeting and they hung up, so let's delete the recording. */
09619                ast_filedelete(tempfile, NULL);
09620             }
09621             return cmd;
09622          }
09623          if (cmd == '0') {
09624             break;
09625          } else if (cmd == '*') {
09626             break;
09627          } 
09628 #if 0       
09629          else if (vmu->review && (*duration < 5)) {
09630             /* Message is too short */
09631             if (option_verbose > 2)
09632                ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
09633             cmd = ast_play_and_wait(chan, "vm-tooshort");
09634             cmd = ast_filedelete(tempfile, NULL);
09635             break;
09636          }
09637          else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
09638             /* Message is all silence */
09639             if (option_verbose > 2)
09640                ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
09641             cmd = ast_filedelete(tempfile, NULL);
09642             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
09643             if (!cmd)
09644                cmd = ast_play_and_wait(chan, "vm-speakup");
09645             break;
09646          }
09647 #endif
09648          else {
09649             /* If all is well, a message exists */
09650             message_exists = 1;
09651             cmd = 0;
09652          }
09653          break;
09654       case '4':
09655       case '5':
09656       case '6':
09657       case '7':
09658       case '8':
09659       case '9':
09660       case '*':
09661       case '#':
09662          cmd = ast_play_and_wait(chan, "vm-sorry");
09663          break;
09664 #if 0 
09665 /*  XXX Commented out for the moment because of the dangers of deleting
09666     a message while recording (can put the message numbers out of sync) */
09667       case '*':
09668          /* Cancel recording, delete message, offer to take another message*/
09669          cmd = ast_play_and_wait(chan, "vm-deleted");
09670          cmd = ast_filedelete(tempfile, NULL);
09671          if (outsidecaller) {
09672             res = vm_exec(chan, NULL);
09673             return res;
09674          }
09675          else
09676             return 1;
09677 #endif
09678       case '0':
09679          if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
09680             cmd = ast_play_and_wait(chan, "vm-sorry");
09681             break;
09682          }
09683          if (message_exists || recorded) {
09684             cmd = ast_play_and_wait(chan, "vm-saveoper");
09685             if (!cmd)
09686                cmd = ast_waitfordigit(chan, 3000);
09687             if (cmd == '1') {
09688                ast_filerename(tempfile, recordfile, NULL);
09689                ast_play_and_wait(chan, "vm-msgsaved");
09690                cmd = '0';
09691             } else {
09692                ast_play_and_wait(chan, "vm-deleted");
09693                DELETE(tempfile, -1, tempfile, vmu);
09694                cmd = '0';
09695             }
09696          }
09697          return cmd;
09698       default:
09699          /* If the caller is an ouside caller, and the review option is enabled,
09700             allow them to review the message, but let the owner of the box review
09701             their OGM's */
09702          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
09703             return cmd;
09704          if (message_exists) {
09705             cmd = ast_play_and_wait(chan, "vm-review");
09706          } else {
09707             cmd = ast_play_and_wait(chan, "vm-torerecord");
09708             if (!cmd)
09709                cmd = ast_waitfordigit(chan, 600);
09710          }
09711          
09712          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
09713             cmd = ast_play_and_wait(chan, "vm-reachoper");
09714             if (!cmd)
09715                cmd = ast_waitfordigit(chan, 600);
09716          }
09717 #if 0
09718          if (!cmd)
09719             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
09720 #endif
09721          if (!cmd)
09722             cmd = ast_waitfordigit(chan, 6000);
09723          if (!cmd) {
09724             attempts++;
09725          }
09726          if (attempts > max_attempts) {
09727             cmd = 't';
09728          }
09729       }
09730    }
09731    if (!outsidecaller && (cmd == -1 || cmd == 't')) {
09732       /* Hang up or timeout, so delete the recording. */
09733       ast_filedelete(tempfile, NULL);
09734    }
09735 
09736    if (cmd != 't' && outsidecaller)
09737       ast_play_and_wait(chan, "vm-goodbye");
09738 
09739    return cmd;
09740 }
09741 
09742 /* This is a workaround so that menuselect displays a proper description
09743  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
09744  */
09745  
09746 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
09747       .load = load_module,
09748       .unload = unload_module,
09749       .reload = reload,
09750       );

Generated on Mon Mar 31 18:15:51 2014 for Asterisk - the Open Source PBX by  doxygen 1.4.7