Thu Oct 11 06:42:00 2012

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, 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 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 language[MAX_LANGUAGE];     /*!< Config: Language setting */
00347    char zonetag[80];                /*!< Time zone */
00348    char callback[80];
00349    char dialout[80];
00350    char uniqueid[80];               /*!< Unique integer identifier */
00351    char exit[80];
00352    char attachfmt[20];              /*!< Attachment format */
00353    unsigned int flags;              /*!< VM_ flags */ 
00354    int saydurationm;
00355    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00356 #ifdef IMAP_STORAGE
00357    char imapuser[80];   /* IMAP server login */
00358    char imappassword[80];  /* IMAP server password if authpassword not defined */
00359    char imapvmshareid[80]; /* Shared mailbox ID to use rather than the dialed one */
00360    int imapversion;                 /*!< If configuration changes, use the new values */
00361 #endif
00362    double volgain;      /*!< Volume gain for voicemails sent via email */
00363    AST_LIST_ENTRY(ast_vm_user) list;
00364 };
00365 
00366 struct vm_zone {
00367    AST_LIST_ENTRY(vm_zone) list;
00368    char name[80];
00369    char timezone[80];
00370    char msg_format[512];
00371 };
00372 
00373 struct vm_state {
00374    char curbox[80];
00375    char username[80];
00376    char context[80];
00377    char curdir[PATH_MAX];
00378    char vmbox[PATH_MAX];
00379    char fn[PATH_MAX];
00380    char fn2[PATH_MAX];
00381    int *deleted;
00382    int *heard;
00383    int dh_arraysize; /* used for deleted / heard allocation */
00384    int curmsg;
00385    int lastmsg;
00386    int newmessages;
00387    int oldmessages;
00388    int starting;
00389    int repeats;
00390 #ifdef IMAP_STORAGE
00391    ast_mutex_t lock;
00392    int updated; /* decremented on each mail check until 1 -allows delay */
00393    long msgArray[256];
00394    MAILSTREAM *mailstream;
00395    int vmArrayIndex;
00396    char imapuser[80]; /* IMAP server login */
00397    int imapversion;
00398    int interactive;
00399    unsigned int quota_limit;
00400    unsigned int quota_usage;
00401    struct vm_state *persist_vms;
00402 #endif
00403 };
00404 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);
00405 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00406 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00407          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00408          signed char record_gain, struct vm_state *vms);
00409 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00410 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00411 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
00412 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);
00413 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00414 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00415 #endif
00416 static void apply_options(struct ast_vm_user *vmu, const char *options);
00417 
00418 struct ao2_container *inprocess_container;
00419 
00420 struct inprocess {
00421    int count;
00422    char *context;
00423    char mailbox[0];
00424 };
00425 
00426 static int inprocess_hash_fn(const void *obj, const int flags)
00427 {
00428    const struct inprocess *i = obj;
00429    return atoi(i->mailbox);
00430 }
00431 
00432 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
00433 {
00434    struct inprocess *i = obj, *j = arg;
00435    if (strcmp(i->mailbox, j->mailbox)) {
00436       return 0;
00437    }
00438    return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
00439 }
00440 
00441 static int inprocess_count(const char *context, const char *mailbox, int delta)
00442 {
00443    struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
00444    arg->context = arg->mailbox + strlen(mailbox) + 1;
00445    strcpy(arg->mailbox, mailbox); /* SAFE */
00446    strcpy(arg->context, context); /* SAFE */
00447    ao2_lock(inprocess_container);
00448    if ((i = ao2_find(inprocess_container, arg, 0))) {
00449       int ret = ast_atomic_fetchadd_int(&i->count, delta);
00450       ao2_unlock(inprocess_container);
00451       ao2_ref(i, -1);
00452       return ret;
00453    }
00454    if (delta < 0) {
00455       ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
00456    }
00457    if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
00458       ao2_unlock(inprocess_container);
00459       return 0;
00460    }
00461    i->context = i->mailbox + strlen(mailbox) + 1;
00462    strcpy(i->mailbox, mailbox); /* SAFE */
00463    strcpy(i->context, context); /* SAFE */
00464    i->count = delta;
00465    ao2_link(inprocess_container, i);
00466    ao2_unlock(inprocess_container);
00467    ao2_ref(i, -1);
00468    return 0;
00469 }
00470 
00471 #ifdef ODBC_STORAGE
00472 static char odbc_database[80];
00473 static char odbc_table[80];
00474 #define RETRIEVE(a,b,c) retrieve_file(a,b)
00475 #define DISPOSE(a,b) remove_file(a,b)
00476 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
00477 #define EXISTS(a,b,c,d) (message_exists(a,b))
00478 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00479 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00480 #define DELETE(a,b,c,d) (delete_file(a,b))
00481 #else
00482 #ifdef IMAP_STORAGE
00483 #define RETRIEVE(a,b,c) imap_retrieve_file(a,b,c)
00484 #define DISPOSE(a,b) remove_file(a,b)
00485 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
00486 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00487 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00488 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00489 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
00490 #else
00491 #define RETRIEVE(a,b,c)
00492 #define DISPOSE(a,b)
00493 #define STORE(a,b,c,d,e,f,g,h,i)
00494 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00495 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00496 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
00497 #define DELETE(a,b,c,d) (vm_delete(c))
00498 #endif
00499 #endif
00500 
00501 static char VM_SPOOL_DIR[PATH_MAX];
00502 
00503 static char ext_pass_cmd[128];
00504 
00505 static int my_umask;
00506 
00507 #if ODBC_STORAGE
00508 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00509 #elif IMAP_STORAGE
00510 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00511 #else
00512 #define tdesc "Comedian Mail (Voicemail System)"
00513 #endif
00514 
00515 static char userscontext[AST_MAX_EXTENSION] = "default";
00516 
00517 static char *addesc = "Comedian Mail";
00518 
00519 static char *synopsis_vm =
00520 "Leave a Voicemail message";
00521 
00522 static char *descrip_vm =
00523 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
00524 "application allows the calling party to leave a message for the specified\n"
00525 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00526 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00527 "specified mailbox does not exist.\n"
00528 "  The Voicemail application will exit if any of the following DTMF digits are\n"
00529 "received:\n"
00530 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00531 "    * - Jump to the 'a' extension in the current dialplan context.\n"
00532 "  This application will set the following channel variable upon completion:\n"
00533 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00534 "               application. The possible values are:\n"
00535 "               SUCCESS | USEREXIT | FAILED\n\n"
00536 "  Options:\n"
00537 "    b    - Play the 'busy' greeting to the calling party.\n"
00538 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00539 "           message. The units are whole-number decibels (dB).\n"
00540 "           Only works on supported technologies, which is Zap only.\n"
00541 "    s    - Skip the playback of instructions for leaving a message to the\n"
00542 "           calling party.\n"
00543 "    u    - Play the 'unavailable' greeting.\n"
00544 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
00545 "           error occurs.\n";
00546 
00547 static char *synopsis_vmain =
00548 "Check Voicemail messages";
00549 
00550 static char *descrip_vmain =
00551 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
00552 "calling party to check voicemail messages. A specific mailbox, and optional\n"
00553 "corresponding context, may be specified. If a mailbox is not provided, the\n"
00554 "calling party will be prompted to enter one. If a context is not specified,\n"
00555 "the 'default' context will be used.\n\n"
00556 "  Options:\n"
00557 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00558 "           is entered by the caller.\n"
00559 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00560 "           message. The units are whole-number decibels (dB).\n"
00561 "    s    - Skip checking the passcode for the mailbox.\n"
00562 "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00563 "           Defaults to INBOX\n";
00564 
00565 static char *synopsis_vm_box_exists =
00566 "Check to see if Voicemail mailbox exists";
00567 
00568 static char *descrip_vm_box_exists =
00569 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
00570 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00571 "will be used.\n"
00572 "  This application will set the following channel variable upon completion:\n"
00573 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00574 "                        MailboxExists application. Possible values include:\n"
00575 "                        SUCCESS | FAILED\n\n"
00576 "  Options:\n"
00577 "    j - Jump to priority n+101 if the mailbox is found.\n";
00578 
00579 static char *synopsis_vmauthenticate =
00580 "Authenticate with Voicemail passwords";
00581 
00582 static char *descrip_vmauthenticate =
00583 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
00584 "same way as the Authenticate application, but the passwords are taken from\n"
00585 "voicemail.conf.\n"
00586 "  If the mailbox is specified, only that mailbox's password will be considered\n"
00587 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00588 "be set with the authenticated mailbox.\n\n"
00589 "  Options:\n"
00590 "    s - Skip playing the initial prompts.\n";
00591 
00592 /* Leave a message */
00593 static char *app = "VoiceMail";
00594 
00595 /* Check mail, control, etc */
00596 static char *app2 = "VoiceMailMain";
00597 
00598 static char *app3 = "MailboxExists";
00599 static char *app4 = "VMAuthenticate";
00600 
00601 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00602 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00603 static char zonetag[80];
00604 static int maxsilence;
00605 static int maxmsg;
00606 static int silencethreshold = 128;
00607 static char serveremail[80];
00608 static char mailcmd[160];  /* Configurable mail cmd */
00609 static char externnotify[160]; 
00610 static struct ast_smdi_interface *smdi_iface = NULL;
00611 static char vmfmts[80];
00612 static double volgain;
00613 static int vmminmessage;
00614 static int vmmaxmessage;
00615 static int maxgreet;
00616 static int skipms;
00617 static int maxlogins;
00618 
00619 static struct ast_flags globalflags = {0};
00620 
00621 static int saydurationminfo;
00622 
00623 static char dialcontext[AST_MAX_CONTEXT];
00624 static char callcontext[AST_MAX_CONTEXT];
00625 static char exitcontext[AST_MAX_CONTEXT];
00626 
00627 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00628 
00629 
00630 static char *emailbody = NULL;
00631 static char *emailsubject = NULL;
00632 static char *pagerbody = NULL;
00633 static char *pagersubject = NULL;
00634 static char fromstring[100];
00635 static char pagerfromstring[100];
00636 static char emailtitle[100];
00637 static char charset[32] = "ISO-8859-1";
00638 
00639 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00640 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00641 static int adsiver = 1;
00642 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00643 
00644 
00645 /*!
00646  * \brief Strips control and non 7-bit clean characters from input string.
00647  *
00648  * \note To map control and none 7-bit characters to a 7-bit clean characters
00649  *  please use ast_str_encode_mine().
00650  */
00651 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
00652 {
00653    char *bufptr = buf;
00654    for (; *input; input++) {
00655       if (*input < 32) {
00656          continue;
00657       }
00658       *bufptr++ = *input;
00659       if (bufptr == buf + buflen - 1) {
00660          break;
00661       }
00662    }
00663    *bufptr = '\0';
00664    return buf;
00665 }
00666 
00667 static void populate_defaults(struct ast_vm_user *vmu)
00668 {
00669    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00670    if (saydurationminfo)
00671       vmu->saydurationm = saydurationminfo;
00672    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00673    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00674    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00675    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00676    if (maxmsg)
00677       vmu->maxmsg = maxmsg;
00678    vmu->volgain = volgain;
00679 }
00680 
00681 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00682 {
00683    int x;
00684    if (!strcasecmp(var, "attach")) {
00685       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00686    } else if (!strcasecmp(var, "attachfmt")) {
00687       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00688    } else if (!strcasecmp(var, "serveremail")) {
00689       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00690    } else if (!strcasecmp(var, "language")) {
00691       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00692    } else if (!strcasecmp(var, "tz")) {
00693       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00694 #ifdef IMAP_STORAGE
00695    } else if (!strcasecmp(var, "imapuser")) {
00696       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00697       vmu->imapversion = imapversion;
00698    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00699       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00700       vmu->imapversion = imapversion;
00701    } else if (!strcasecmp(var, "imapvmshareid")) {
00702       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00703       vmu->imapversion = imapversion;
00704 #endif
00705    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00706       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00707    } else if (!strcasecmp(var, "saycid")){
00708       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00709    } else if (!strcasecmp(var,"sendvoicemail")){
00710       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00711    } else if (!strcasecmp(var, "review")){
00712       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00713    } else if (!strcasecmp(var, "tempgreetwarn")){
00714       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00715    } else if (!strcasecmp(var, "operator")){
00716       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00717    } else if (!strcasecmp(var, "envelope")){
00718       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00719    } else if (!strcasecmp(var, "sayduration")){
00720       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00721    } else if (!strcasecmp(var, "saydurationm")){
00722       if (sscanf(value, "%30d", &x) == 1) {
00723          vmu->saydurationm = x;
00724       } else {
00725          ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
00726       }
00727    } else if (!strcasecmp(var, "forcename")){
00728       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00729    } else if (!strcasecmp(var, "forcegreetings")){
00730       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00731    } else if (!strcasecmp(var, "callback")) {
00732       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00733    } else if (!strcasecmp(var, "dialout")) {
00734       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00735    } else if (!strcasecmp(var, "exitcontext")) {
00736       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00737    } else if (!strcasecmp(var, "maxmsg")) {
00738       vmu->maxmsg = atoi(value);
00739       if (vmu->maxmsg <= 0) {
00740          ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
00741          vmu->maxmsg = MAXMSG;
00742       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00743          ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00744          vmu->maxmsg = MAXMSGLIMIT;
00745       }
00746    } else if (!strcasecmp(var, "volgain")) {
00747       sscanf(value, "%30lf", &vmu->volgain);
00748    } else if (!strcasecmp(var, "options")) {
00749       apply_options(vmu, value);
00750    }
00751 }
00752 
00753 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00754 {
00755    int res = -1;
00756    if (!strcmp(vmu->password, password)) {
00757       /* No change (but an update would return 0 rows updated, so we opt out here) */
00758       res = 0;
00759    } else if (!ast_strlen_zero(vmu->uniqueid)) {
00760       if (ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL) > 0) {
00761          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00762          res = 0;
00763       }
00764    }
00765    return res;
00766 }
00767 
00768 static void apply_options(struct ast_vm_user *vmu, const char *options)
00769 {  /* Destructively Parse options and apply */
00770    char *stringp;
00771    char *s;
00772    char *var, *value;
00773    stringp = ast_strdupa(options);
00774    while ((s = strsep(&stringp, "|"))) {
00775       value = s;
00776       if ((var = strsep(&value, "=")) && value) {
00777          apply_option(vmu, var, value);
00778       }
00779    }  
00780 }
00781 
00782 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
00783 {
00784    struct ast_variable *tmp;
00785    tmp = var;
00786    while (tmp) {
00787       if (!strcasecmp(tmp->name, "vmsecret")) {
00788          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00789       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
00790          if (ast_strlen_zero(retval->password))
00791             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00792       } else if (!strcasecmp(tmp->name, "uniqueid")) {
00793          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00794       } else if (!strcasecmp(tmp->name, "pager")) {
00795          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
00796       } else if (!strcasecmp(tmp->name, "email")) {
00797          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
00798       } else if (!strcasecmp(tmp->name, "fullname")) {
00799          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
00800       } else if (!strcasecmp(tmp->name, "context")) {
00801          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
00802 #ifdef IMAP_STORAGE
00803       } else if (!strcasecmp(tmp->name, "imapuser")) {
00804          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
00805          retval->imapversion = imapversion;
00806       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
00807          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
00808          retval->imapversion = imapversion;
00809       } else if (!strcasecmp(tmp->name, "imapvmshareid")) {
00810          ast_copy_string(retval->imapvmshareid, tmp->value, sizeof(retval->imapvmshareid));
00811          retval->imapversion = imapversion;
00812 #endif
00813       } else
00814          apply_option(retval, tmp->name, tmp->value);
00815       tmp = tmp->next;
00816    } 
00817 }
00818 
00819 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00820 {
00821    struct ast_variable *var;
00822    struct ast_vm_user *retval;
00823 
00824    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
00825       if (!ivm)
00826          ast_set_flag(retval, VM_ALLOCED);   
00827       else
00828          memset(retval, 0, sizeof(*retval));
00829       if (mailbox) 
00830          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
00831       populate_defaults(retval);
00832       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
00833          var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
00834       else
00835          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
00836       if (var) {
00837          apply_options_full(retval, var);
00838          ast_variables_destroy(var);
00839       } else { 
00840          if (!ivm) 
00841             free(retval);
00842          retval = NULL;
00843       }  
00844    } 
00845    return retval;
00846 }
00847 
00848 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00849 {
00850    /* This function could be made to generate one from a database, too */
00851    struct ast_vm_user *vmu=NULL, *cur;
00852    AST_LIST_LOCK(&users);
00853 
00854    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
00855       context = "default";
00856 
00857    AST_LIST_TRAVERSE(&users, cur, list) {
00858 #ifdef IMAP_STORAGE
00859       if (cur->imapversion != imapversion) {
00860          continue;
00861       }
00862 #endif
00863       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
00864          break;
00865       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
00866          break;
00867    }
00868    if (cur) {
00869       /* Make a copy, so that on a reload, we have no race */
00870       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
00871          memcpy(vmu, cur, sizeof(*vmu));
00872          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
00873          AST_LIST_NEXT(vmu, list) = NULL;
00874       }
00875    } else
00876       vmu = find_user_realtime(ivm, context, mailbox);
00877    AST_LIST_UNLOCK(&users);
00878    return vmu;
00879 }
00880 
00881 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
00882 {
00883    /* This function could be made to generate one from a database, too */
00884    struct ast_vm_user *cur;
00885    int res = -1;
00886    AST_LIST_LOCK(&users);
00887    AST_LIST_TRAVERSE(&users, cur, list) {
00888       if ((!context || !strcasecmp(context, cur->context)) &&
00889          (!strcasecmp(mailbox, cur->mailbox)))
00890             break;
00891    }
00892    if (cur) {
00893       ast_copy_string(cur->password, newpass, sizeof(cur->password));
00894       res = 0;
00895    }
00896    AST_LIST_UNLOCK(&users);
00897    return res;
00898 }
00899 
00900 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
00901 {
00902    struct ast_config   *cfg=NULL;
00903    struct ast_variable *var=NULL;
00904    struct ast_category *cat=NULL;
00905    char *category=NULL, *value=NULL, *new=NULL;
00906    const char *tmp=NULL;
00907                
00908    if (!change_password_realtime(vmu, newpassword))
00909       return;
00910 
00911    /* check voicemail.conf */
00912    if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
00913       while ((category = ast_category_browse(cfg, category))) {
00914          if (!strcasecmp(category, vmu->context)) {
00915             tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
00916             if (!tmp) {
00917                ast_log(LOG_WARNING, "We could not find the mailbox.\n");
00918                break;
00919             }
00920             value = strstr(tmp,",");
00921             if (!value) {
00922                new = alloca(strlen(newpassword)+1);
00923                sprintf(new, "%s", newpassword);
00924             } else {
00925                new = alloca((strlen(value)+strlen(newpassword)+1));
00926                sprintf(new,"%s%s", newpassword, value);
00927             }
00928             if (!(cat = ast_category_get(cfg, category))) {
00929                ast_log(LOG_WARNING, "Failed to get category structure.\n");
00930                break;
00931             }
00932             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
00933          }
00934       }
00935       /* save the results */
00936       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00937       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00938       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
00939    }
00940    category = NULL;
00941    var = NULL;
00942    /* check users.conf and update the password stored for the mailbox*/
00943    /* if no vmsecret entry exists create one. */
00944    if ((cfg = ast_config_load_with_comments("users.conf"))) {
00945       if (option_debug > 3)
00946          ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
00947       while ((category = ast_category_browse(cfg, category))) {
00948          if (option_debug > 3)
00949             ast_log(LOG_DEBUG, "users.conf: %s\n", category);
00950          if (!strcasecmp(category, vmu->mailbox)) {
00951             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
00952                if (option_debug > 3)
00953                   ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
00954                var = ast_variable_new("vmsecret", newpassword);
00955             } 
00956             new = alloca(strlen(newpassword)+1);
00957             sprintf(new, "%s", newpassword);
00958             if (!(cat = ast_category_get(cfg, category))) {
00959                if (option_debug > 3)
00960                   ast_log(LOG_DEBUG, "failed to get category!\n");
00961                break;
00962             }
00963             if (!var)      
00964                ast_variable_update(cat, "vmsecret", new, NULL, 0);
00965             else
00966                ast_variable_append(cat, var);
00967          }
00968       }
00969       /* save the results and clean things up */
00970       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
00971       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00972       config_text_file_save("users.conf", cfg, "AppVoicemail");
00973    }
00974 }
00975 
00976 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
00977 {
00978    char buf[255];
00979    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
00980    if (!ast_safe_system(buf)) {
00981       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00982       /* Reset the password in memory, too */
00983       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00984    }
00985 }
00986 
00987 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
00988 {
00989    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
00990 }
00991 
00992 static int make_file(char *dest, const int len, const char *dir, const int num)
00993 {
00994    return snprintf(dest, len, "%s/msg%04d", dir, num);
00995 }
00996 
00997 /* same as mkstemp, but return a FILE * */
00998 static FILE *vm_mkftemp(char *template)
00999 {
01000    FILE *p = NULL;
01001    int pfd = mkstemp(template);
01002    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01003    if (pfd > -1) {
01004       p = fdopen(pfd, "w+");
01005       if (!p) {
01006          close(pfd);
01007          pfd = -1;
01008       }
01009    }
01010    return p;
01011 }
01012 
01013 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01014  * \param dest    String. base directory.
01015  * \param len     Length of dest.
01016  * \param context String. Ignored if is null or empty string.
01017  * \param ext     String. Ignored if is null or empty string.
01018  * \param folder  String. Ignored if is null or empty string. 
01019  * \return -1 on failure, 0 on success.
01020  */
01021 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01022 {
01023    mode_t   mode = VOICEMAIL_DIR_MODE;
01024 
01025    if (!ast_strlen_zero(context)) {
01026       make_dir(dest, len, context, "", "");
01027       if (mkdir(dest, mode) && errno != EEXIST) {
01028          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
01029          return -1;
01030       }
01031    }
01032    if (!ast_strlen_zero(ext)) {
01033       make_dir(dest, len, context, ext, "");
01034       if (mkdir(dest, mode) && errno != EEXIST) {
01035          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
01036          return -1;
01037       }
01038    }
01039    if (!ast_strlen_zero(folder)) {
01040       make_dir(dest, len, context, ext, folder);
01041       if (mkdir(dest, mode) && errno != EEXIST) {
01042          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
01043          return -1;
01044       }
01045    }
01046    return 0;
01047 }
01048 
01049 static char *mbox(int id)
01050 {
01051    static char *msgs[] = {
01052       "INBOX",
01053       "Old",
01054       "Work",
01055       "Family",
01056       "Friends",
01057       "Cust1",
01058       "Cust2",
01059       "Cust3",
01060       "Cust4",
01061       "Cust5",
01062    };
01063    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
01064 }
01065 
01066 static void free_user(struct ast_vm_user *vmu)
01067 {
01068    if (ast_test_flag(vmu, VM_ALLOCED))
01069       free(vmu);
01070 }
01071 
01072 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
01073 
01074    int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
01075    if (!vms->dh_arraysize) {
01076       /* initial allocation */
01077       if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
01078          return -1;
01079       }
01080       if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
01081          return -1;
01082       }
01083       vms->dh_arraysize = arraysize;
01084    } else if (vms->dh_arraysize < arraysize) {
01085       if (!(vms->deleted = ast_realloc(vms->deleted, arraysize * sizeof(int)))) {
01086          return -1;
01087       }
01088       if (!(vms->heard = ast_realloc(vms->heard, arraysize * sizeof(int)))) {
01089          return -1;
01090       }
01091       memset(vms->deleted, 0, arraysize * sizeof(int));
01092       memset(vms->heard, 0, arraysize * sizeof(int));
01093       vms->dh_arraysize = arraysize;
01094    }
01095 
01096    return 0;
01097 }
01098 
01099 /* All IMAP-specific functions should go in this block. This
01100  * keeps them from being spread out all over the code */
01101 #ifdef IMAP_STORAGE
01102 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
01103 {
01104    char arg[10];
01105    struct vm_state *vms;
01106    unsigned long messageNum;
01107 
01108    /* Greetings aren't stored in IMAP, so we delete them from disk */
01109    if (msgnum < 0) {
01110       ast_filedelete(file, NULL);
01111       return;
01112    }
01113 
01114    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01115       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);
01116       return;
01117    }
01118 
01119    /* find real message number based on msgnum */
01120    /* this may be an index into vms->msgArray based on the msgnum. */
01121    messageNum = vms->msgArray[msgnum];
01122    if (messageNum == 0) {
01123       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
01124       return;
01125    }
01126    if (option_debug > 2)
01127       ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
01128    /* delete message */
01129    snprintf (arg, sizeof(arg), "%lu",messageNum);
01130    ast_mutex_lock(&vms->lock);
01131    mail_setflag (vms->mailstream,arg,"\\DELETED");
01132    mail_expunge(vms->mailstream);
01133    ast_mutex_unlock(&vms->lock);
01134 }
01135 
01136 static int imap_retrieve_file(const char *dir, const int msgnum, const struct ast_vm_user *vmu)
01137 {
01138    BODY *body;
01139    char *header_content;
01140    char *attachedfilefmt;
01141    const char *cid_num;
01142    const char *cid_name;
01143    const char *duration;
01144    const char *context;
01145    const char *category;
01146    const char *origtime;
01147    struct vm_state *vms;
01148    char text_file[PATH_MAX];
01149    FILE *text_file_ptr;
01150 
01151    /* Greetings are not stored on the IMAP server, so we should not
01152     * attempt to retrieve them.
01153     */
01154    if (msgnum < 0) {
01155       return 0;
01156    }
01157    
01158    /* Before anything can happen, we need a vm_state so that we can
01159     * actually access the imap server through the vms->mailstream
01160     */
01161    if(!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
01162       /* This should not happen. If it does, then I guess we'd
01163        * need to create the vm_state, extract which mailbox to
01164        * open, and then set up the msgArray so that the correct
01165        * IMAP message could be accessed. If I have seen correctly
01166        * though, the vms should be obtainable from the vmstates list
01167        * and should have its msgArray properly set up.
01168        */
01169       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01170       return -1;
01171    }
01172    
01173    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01174 
01175    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01176    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01177       return 0;
01178    }
01179 
01180    if (option_debug > 2)
01181       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01182    if (vms->msgArray[msgnum] == 0) {
01183       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01184       return -1;
01185    }
01186 
01187    /* This will only work for new messages... */
01188    ast_mutex_lock(&vms->lock);
01189    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01190    ast_mutex_unlock(&vms->lock);
01191    /* empty string means no valid header */
01192    if (ast_strlen_zero(header_content)) {
01193       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01194       return -1;
01195    }
01196 
01197    ast_mutex_lock(&vms->lock);
01198    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01199    ast_mutex_unlock(&vms->lock);
01200 
01201    /* We have the body, now we extract the file name of the first attachment. */
01202    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01203       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01204    } else {
01205       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01206       return -1;
01207    }
01208    
01209    /* Find the format of the attached file */
01210 
01211    strsep(&attachedfilefmt, ".");
01212    if (!attachedfilefmt) {
01213       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01214       return -1;
01215    }
01216    
01217    save_body(body, vms, "2", attachedfilefmt);
01218 
01219    /* Get info from headers!! */
01220    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01221 
01222    if (!(text_file_ptr = fopen(text_file, "w"))) {
01223       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01224    }
01225 
01226    fprintf(text_file_ptr, "%s\n", "[message]");
01227 
01228    cid_name = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:");
01229    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(cid_name, ""));
01230    cid_num = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
01231    fprintf(text_file_ptr, "<%s>\n", S_OR(cid_num, ""));
01232    context = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
01233    fprintf(text_file_ptr, "context=%s\n", S_OR(context, ""));
01234    origtime = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
01235    fprintf(text_file_ptr, "origtime=%s\n", S_OR(origtime, ""));
01236    duration = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
01237    fprintf(text_file_ptr, "duration=%s\n", S_OR(origtime, ""));
01238    category = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
01239    fprintf(text_file_ptr, "category=%s\n", S_OR(category, ""));
01240    
01241    fclose(text_file_ptr);
01242    return 0;
01243 }
01244 
01245 static int folder_int(const char *folder)
01246 {
01247    /*assume a NULL folder means INBOX*/
01248    if (!folder)
01249       return 0;
01250    if (!strcasecmp(folder, "INBOX"))
01251       return 0;
01252    else if (!strcasecmp(folder, "Old"))
01253       return 1;
01254    else if (!strcasecmp(folder, "Work"))
01255       return 2;
01256    else if (!strcasecmp(folder, "Family"))
01257       return 3;
01258    else if (!strcasecmp(folder, "Friends"))
01259       return 4;
01260    else if (!strcasecmp(folder, "Cust1"))
01261       return 5;
01262    else if (!strcasecmp(folder, "Cust2"))
01263       return 6;
01264    else if (!strcasecmp(folder, "Cust3"))
01265       return 7;
01266    else if (!strcasecmp(folder, "Cust4"))
01267       return 8;
01268    else if (!strcasecmp(folder, "Cust5"))
01269       return 9;
01270    else /*assume they meant INBOX if folder is not found otherwise*/
01271       return 0;
01272 }
01273 
01274 static int messagecount(const char *context, const char *mailbox, const char *folder)
01275 {
01276    SEARCHPGM *pgm;
01277    SEARCHHEADER *hdr;
01278 
01279    struct ast_vm_user *vmu, vmus;
01280    struct vm_state *vms_p;
01281    int ret = 0;
01282    int fold = folder_int(folder);
01283    
01284    if (ast_strlen_zero(mailbox))
01285       return 0;
01286 
01287    /* We have to get the user before we can open the stream! */
01288    /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
01289    vmu = find_user(&vmus, context, mailbox);
01290    if (!vmu) {
01291       ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
01292       return -1;
01293    } else {
01294       /* No IMAP account available */
01295       if (vmu->imapuser[0] == '\0') {
01296          ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
01297          return -1;
01298       }
01299    }
01300 
01301    /* check if someone is accessing this box right now... */
01302    vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
01303    if (!vms_p) {
01304       vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
01305    }
01306    if (vms_p) {
01307       if (option_debug > 2)
01308          ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
01309       if (fold == 0) {/*INBOX*/
01310          return vms_p->newmessages;
01311       }
01312       if (fold == 1) {/*Old messages*/
01313          return vms_p->oldmessages;
01314       }
01315    }
01316 
01317    /* add one if not there... */
01318    vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
01319    if (!vms_p) {
01320       vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
01321    }
01322 
01323    if (!vms_p) {
01324       if (!(vms_p = create_vm_state_from_user(vmu))) {
01325          ast_log(LOG_WARNING, "Unable to allocate space for new vm_state!\n");
01326          return -1;
01327       }
01328    }
01329    ret = init_mailstream(vms_p, fold);
01330    if (!vms_p->mailstream) {
01331       ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
01332       return -1;
01333    }
01334    if (ret == 0) {
01335       ast_mutex_lock(&vms_p->lock);
01336       pgm = mail_newsearchpgm ();
01337       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01338       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01339       pgm->header = hdr;
01340       if (fold != 1) {
01341          pgm->unseen = 1;
01342          pgm->seen = 0;
01343       }
01344       /* In the special case where fold is 1 (old messages) we have to do things a bit
01345        * differently. Old messages are stored in the INBOX but are marked as "seen"
01346        */
01347       else {
01348          pgm->unseen = 0;
01349          pgm->seen = 1;
01350       }
01351       pgm->undeleted = 1;
01352       pgm->deleted = 0;
01353 
01354       vms_p->vmArrayIndex = 0;
01355       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01356       if (fold == 0)
01357          vms_p->newmessages = vms_p->vmArrayIndex;
01358       if (fold == 1)
01359          vms_p->oldmessages = vms_p->vmArrayIndex;
01360       /*Freeing the searchpgm also frees the searchhdr*/
01361       mail_free_searchpgm(&pgm);
01362       ast_mutex_unlock(&vms_p->lock);
01363       vms_p->updated = 0;
01364       return vms_p->vmArrayIndex;
01365    } else {
01366       ast_mutex_lock(&vms_p->lock);
01367       mail_ping(vms_p->mailstream);
01368       ast_mutex_unlock(&vms_p->lock);
01369    }
01370    return 0;
01371 }
01372 
01373 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
01374 {
01375    /* Check if mailbox is full */
01376    check_quota(vms, imapfolder);
01377    if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
01378       if (option_debug)
01379          ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
01380       ast_play_and_wait(chan, "vm-mailboxfull");
01381       return -1;
01382    }
01383 
01384    /* Check if we have exceeded maxmsg */
01385    if (option_debug > 2)
01386       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));
01387    if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
01388       ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
01389       ast_play_and_wait(chan, "vm-mailboxfull");
01390       inprocess_count(vmu->mailbox, vmu->context, -1);
01391       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
01392       return -1;
01393    }
01394    return 0;
01395 }
01396 
01397 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)
01398 {
01399    char *myserveremail = serveremail;
01400    char fn[PATH_MAX];
01401    char mailbox[256];
01402    char *stringp;
01403    FILE *p=NULL;
01404    char tmp[80] = "/tmp/astmail-XXXXXX";
01405    long len;
01406    void *buf;
01407    int tempcopy = 0;
01408    STRING str;
01409    int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
01410 
01411    /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
01412    if (msgnum < 0)
01413       return 0;
01414 
01415    if (imap_check_limits(chan, vms, vmu, msgcount)) {
01416       return -1;
01417    }
01418 
01419    /* Attach only the first format */
01420    fmt = ast_strdupa(fmt);
01421    stringp = fmt;
01422    strsep(&stringp, "|");
01423 
01424    if (!ast_strlen_zero(vmu->serveremail))
01425       myserveremail = vmu->serveremail;
01426 
01427    make_file(fn, sizeof(fn), dir, msgnum);
01428 
01429    if (ast_strlen_zero(vmu->email)) {
01430       /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
01431        * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
01432        * string if tempcopy is 1
01433        */
01434       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01435       tempcopy = 1;
01436    }
01437 
01438    if (!strcmp(fmt, "wav49"))
01439       fmt = "WAV";
01440    if (option_debug > 2)
01441       ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
01442    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01443       command hangs */
01444    if ((p = vm_mkftemp(tmp)) == NULL) {
01445       ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01446       if (tempcopy)
01447          *(vmu->email) = '\0';
01448       return -1;
01449    } else {
01450       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);
01451       /* read mail file to memory */
01452       len = ftell(p);
01453       rewind(p);
01454       if ((buf = ast_malloc(len+1)) == NIL) {
01455          ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
01456          fclose(p);
01457          return -1;
01458       }
01459       if (fread(buf, len, 1, p) != 1) {
01460          ast_log(LOG_WARNING, "Short read: %s\n", strerror(errno));
01461       }
01462       ((char *)buf)[len] = '\0';
01463       INIT(&str, mail_string, buf, len);
01464       init_mailstream(vms, 0);
01465       imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
01466       ast_mutex_lock(&vms->lock);
01467       if (!mail_append(vms->mailstream, mailbox, &str))
01468          ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01469       ast_mutex_unlock(&vms->lock);
01470       fclose(p);
01471       unlink(tmp);
01472       ast_free(buf);
01473       if (option_debug > 2)
01474          ast_log(LOG_DEBUG, "%s stored\n", fn);
01475       /* Using messagecount to populate the last place in the msgArray
01476        * is less than optimal, but it's the only way given the current setup
01477        */
01478       messagecount(vmu->context, vmu->mailbox, "INBOX");
01479    }
01480    if (tempcopy)
01481       *(vmu->email) = '\0';
01482    inprocess_count(vmu->mailbox, vmu->context, -1);
01483    return 0;
01484 
01485 }
01486 
01487 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
01488 {
01489    char tmp[PATH_MAX] = "";
01490    char *mailboxnc;  
01491    char *context;
01492    char *mb;
01493    char *cur;
01494    if (newmsgs)
01495       *newmsgs = 0;
01496    if (oldmsgs)
01497       *oldmsgs = 0;
01498 
01499    if (option_debug > 2)
01500       ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
01501    /* If no mailbox, return immediately */
01502    if (ast_strlen_zero(mailbox_context))
01503       return 0;
01504    
01505    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01506    context = strchr(tmp, '@');
01507    if (strchr(mailbox_context, ',')) {
01508       int tmpnew, tmpold;
01509       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01510       mb = tmp;
01511       while ((cur = strsep(&mb, ", "))) {
01512          if (!ast_strlen_zero(cur)) {
01513             if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01514                return -1;
01515             else {
01516                if (newmsgs)
01517                   *newmsgs += tmpnew; 
01518                if (oldmsgs)
01519                   *oldmsgs += tmpold;
01520             }
01521          }
01522       }
01523       return 0;
01524    }
01525    if (context) {
01526       *context = '\0';
01527       mailboxnc = tmp;
01528       context++;
01529    } else {
01530       context = "default";
01531       mailboxnc = (char *)mailbox_context;
01532    }
01533    if (newmsgs) {
01534       if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
01535          return -1;
01536    }
01537    if (oldmsgs) {
01538       if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
01539          return -1;
01540    }
01541    return 0;
01542 }
01543    
01544 
01545 static int has_voicemail(const char *mailbox, const char *folder)
01546 {
01547    char tmp[256], *tmp2, *mbox, *context;
01548    ast_copy_string(tmp, mailbox, sizeof(tmp));
01549    tmp2 = tmp;
01550    if (strchr(tmp2, ',')) {
01551       while ((mbox = strsep(&tmp2, ","))) {
01552          if (!ast_strlen_zero(mbox)) {
01553             if (has_voicemail(mbox, folder))
01554                return 1;
01555          }
01556       }
01557    }
01558    if ((context= strchr(tmp, '@')))
01559       *context++ = '\0';
01560    else
01561       context = "default";
01562    return messagecount(context, tmp, folder) ? 1 : 0;
01563 }
01564 
01565 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)
01566 {
01567    struct vm_state *sendvms = NULL, *destvms = NULL;
01568    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
01569    if (msgnum >= recip->maxmsg) {
01570       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
01571       return -1;
01572    }
01573    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
01574       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
01575       return -1;
01576    }
01577    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
01578       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
01579       return -1;
01580    }
01581    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
01582    ast_mutex_lock(&sendvms->lock);
01583    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
01584       ast_mutex_unlock(&sendvms->lock);
01585       return 0;
01586    }
01587    ast_mutex_unlock(&sendvms->lock);
01588    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
01589    return -1;
01590 }
01591 
01592 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
01593 {
01594    char tmp[256], *t = tmp;
01595    size_t left = sizeof(tmp);
01596 
01597    if (box == 1) {
01598       ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
01599       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
01600    } else {
01601       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
01602       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
01603    }
01604 
01605    /* Build up server information */
01606    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
01607 
01608    /* Add authentication user if present */
01609    if (!ast_strlen_zero(authuser))
01610       ast_build_string(&t, &left, "/authuser=%s", authuser);
01611 
01612    /* Add flags if present */
01613    if (!ast_strlen_zero(imapflags))
01614       ast_build_string(&t, &left, "/%s", imapflags);
01615 
01616    /* End with username */
01617 #if 1
01618    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
01619 #else
01620    ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
01621 #endif
01622 
01623    if (box == 0 || box == 1)
01624       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
01625    else
01626       snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
01627 }
01628 
01629 static int init_mailstream(struct vm_state *vms, int box)
01630 {
01631    MAILSTREAM *stream = NIL;
01632    long debug;
01633    char tmp[256];
01634    
01635    if (!vms) {
01636       ast_log (LOG_ERROR,"vm_state is NULL!\n");
01637       return -1;
01638    }
01639    if (option_debug > 2)
01640       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
01641    if (vms->mailstream == NIL || !vms->mailstream) {
01642       if (option_debug)
01643          ast_log (LOG_DEBUG,"mailstream not set.\n");
01644    } else {
01645       stream = vms->mailstream;
01646    }
01647    /* debug = T;  user wants protocol telemetry? */
01648    debug = NIL;  /* NO protocol telemetry? */
01649 
01650    if (delimiter == '\0') {      /* did not probe the server yet */
01651       char *cp;
01652 #ifdef USE_SYSTEM_IMAP
01653 #include <imap/linkage.c>
01654 #elif defined(USE_SYSTEM_CCLIENT)
01655 #include <c-client/linkage.c>
01656 #else
01657 #include "linkage.c"
01658 #endif
01659       /* Connect to INBOX first to get folders delimiter */
01660       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
01661       ast_mutex_lock(&vms->lock);
01662       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
01663       ast_mutex_unlock(&vms->lock);
01664       if (stream == NIL) {
01665          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
01666          return -1;
01667       }
01668       get_mailbox_delimiter(stream);
01669       /* update delimiter in imapfolder */
01670       for (cp = imapfolder; *cp; cp++)
01671          if (*cp == '/')
01672             *cp = delimiter;
01673    }
01674    /* Now connect to the target folder */
01675    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
01676    if (option_debug > 2)
01677       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
01678    ast_mutex_lock(&vms->lock);
01679    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
01680    ast_mutex_unlock(&vms->lock);
01681    if (vms->mailstream == NIL) {
01682       return -1;
01683    } else {
01684       return 0;
01685    }
01686 }
01687 
01688 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
01689 {
01690    SEARCHPGM *pgm;
01691    SEARCHHEADER *hdr;
01692    int ret;
01693 
01694    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
01695    vms->imapversion = vmu->imapversion;
01696 
01697    if (option_debug > 2)
01698       ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
01699    ret = init_mailstream(vms, box);
01700    if (ret != 0 || !vms->mailstream) {
01701       ast_log (LOG_ERROR,"Could not initialize mailstream\n");
01702       return -1;
01703    }
01704    
01705    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
01706 
01707    /* Check Quota */
01708    if  (box == 0)  {
01709       if (option_debug > 2)
01710          ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
01711       check_quota(vms,(char *)mbox(box));
01712    }
01713 
01714    ast_mutex_lock(&vms->lock);
01715    pgm = mail_newsearchpgm();
01716 
01717    /* Check IMAP folder for Asterisk messages only... */
01718    hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
01719    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
01720    pgm->header = hdr;
01721    pgm->deleted = 0;
01722    pgm->undeleted = 1;
01723 
01724    /* if box = 0, check for new, if box = 1, check for read */
01725    if (box == 0) {
01726       pgm->unseen = 1;
01727       pgm->seen = 0;
01728    } else if (box == 1) {
01729       pgm->seen = 1;
01730       pgm->unseen = 0;
01731    }
01732 
01733    vms->vmArrayIndex = 0;
01734    if (option_debug > 2)
01735       ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
01736    mail_search_full (vms->mailstream, NULL, pgm, NIL);
01737 
01738    vms->lastmsg = vms->vmArrayIndex - 1;
01739    /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
01740     * ensure to allocate enough space to account for all of them. Warn if old messages
01741     * have not been checked first as that is required.
01742     */
01743    if (box == 0 && !vms->dh_arraysize) {
01744       ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
01745    }
01746    if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
01747       ast_mutex_unlock(&vms->lock);
01748       return -1;
01749    }
01750 
01751    mail_free_searchpgm(&pgm);
01752    ast_mutex_unlock(&vms->lock);
01753    return 0;
01754 }
01755 
01756 static void write_file(char *filename, char *buffer, unsigned long len)
01757 {
01758    FILE *output;
01759 
01760    output = fopen (filename, "w");
01761    if (fwrite (buffer, len, 1, output) != 1) {
01762       ast_log(LOG_WARNING, "Short write: %s\n", strerror(errno));
01763    }
01764    fclose (output);
01765 }
01766 
01767 void mm_searched(MAILSTREAM *stream, unsigned long number)
01768 {
01769    struct vm_state *vms;
01770    char *mailbox;
01771    char *user;
01772    mailbox = stream->mailbox;
01773    user = get_user_by_mailbox(mailbox);
01774    vms = get_vm_state_by_imapuser(user,2);
01775    if (!vms) {
01776       vms = get_vm_state_by_imapuser(user, 0);
01777    }
01778    if (vms) {
01779       if (option_debug > 2)
01780          ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
01781       vms->msgArray[vms->vmArrayIndex++] = number;
01782    } else {
01783       ast_log (LOG_ERROR, "No state found.\n");
01784    }
01785 }
01786 
01787 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
01788 {
01789    struct ast_variable *var;
01790    struct ast_vm_user *vmu;
01791 
01792    vmu = ast_calloc(1, sizeof *vmu);
01793    if (!vmu)
01794       return NULL;
01795    ast_set_flag(vmu, VM_ALLOCED);
01796    populate_defaults(vmu);
01797 
01798    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
01799    if (var) {
01800       apply_options_full(vmu, var);
01801       ast_variables_destroy(var);
01802       return vmu;
01803    } else {
01804       free(vmu);
01805       return NULL;
01806    }
01807 }
01808 
01809 /* Interfaces to C-client */
01810 
01811 void mm_exists(MAILSTREAM * stream, unsigned long number)
01812 {
01813    /* mail_ping will callback here if new mail! */
01814    if (option_debug > 3)
01815       ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
01816    if (number == 0) return;
01817    set_update(stream);
01818 }
01819 
01820 
01821 void mm_expunged(MAILSTREAM * stream, unsigned long number)
01822 {
01823    /* mail_ping will callback here if expunged mail! */
01824    if (option_debug > 3)
01825       ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
01826    if (number == 0) return;
01827    set_update(stream);
01828 }
01829 
01830 
01831 void mm_flags(MAILSTREAM * stream, unsigned long number)
01832 {
01833    /* mail_ping will callback here if read mail! */
01834    if (option_debug > 3)
01835       ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
01836    if (number == 0) return;
01837    set_update(stream);
01838 }
01839 
01840 
01841 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
01842 {
01843    mm_log (string, errflg);
01844 }
01845 
01846 
01847 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
01848 {
01849    if (delimiter == '\0') {
01850       delimiter = delim;
01851    }
01852    if (option_debug > 4) {
01853       ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
01854       if (attributes & LATT_NOINFERIORS)
01855          ast_log(LOG_DEBUG, "no inferiors\n");
01856       if (attributes & LATT_NOSELECT)
01857          ast_log(LOG_DEBUG, "no select\n");
01858       if (attributes & LATT_MARKED)
01859          ast_log(LOG_DEBUG, "marked\n");
01860       if (attributes & LATT_UNMARKED)
01861          ast_log(LOG_DEBUG, "unmarked\n");
01862    }
01863 }
01864 
01865 
01866 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
01867 {
01868    if (option_debug > 4) {
01869       ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
01870       if (attributes & LATT_NOINFERIORS)
01871          ast_log(LOG_DEBUG, "no inferiors\n");
01872       if (attributes & LATT_NOSELECT)
01873          ast_log(LOG_DEBUG, "no select\n");
01874       if (attributes & LATT_MARKED)
01875          ast_log(LOG_DEBUG, "marked\n");
01876       if (attributes & LATT_UNMARKED)
01877          ast_log(LOG_DEBUG, "unmarked\n");
01878    }
01879 }
01880 
01881 
01882 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
01883 {
01884    ast_log (LOG_NOTICE," Mailbox %s", mailbox);
01885    if (status->flags & SA_MESSAGES)
01886       ast_log (LOG_NOTICE,", %lu messages", status->messages);
01887    if (status->flags & SA_RECENT)
01888       ast_log (LOG_NOTICE,", %lu recent", status->recent);
01889    if (status->flags & SA_UNSEEN)
01890       ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
01891    if (status->flags & SA_UIDVALIDITY)
01892       ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
01893    if (status->flags & SA_UIDNEXT)
01894       ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
01895    ast_log (LOG_NOTICE,"\n");
01896 }
01897 
01898 
01899 void mm_log(char *string, long errflg)
01900 {
01901    switch ((short) errflg) {
01902       case NIL:
01903          if (option_debug)
01904             ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
01905          break;
01906       case PARSE:
01907       case WARN:
01908          ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
01909          break;
01910       case ERROR:
01911          ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
01912          break;
01913    }
01914 }
01915 
01916 
01917 void mm_dlog(char *string)
01918 {
01919    ast_log (LOG_NOTICE, "%s\n", string);
01920 }
01921 
01922 
01923 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
01924 {
01925    struct ast_vm_user *vmu;
01926 
01927    if (option_debug > 3)
01928       ast_log(LOG_DEBUG, "Entering callback mm_login\n");
01929 
01930    ast_copy_string(user, mb->user, MAILTMPLEN);
01931 
01932    /* We should only do this when necessary */
01933    if (!ast_strlen_zero(authpassword)) {
01934       ast_copy_string(pwd, authpassword, MAILTMPLEN);
01935    } else {
01936       AST_LIST_TRAVERSE(&users, vmu, list) {
01937          if (!strcasecmp(mb->user, vmu->imapuser)) {
01938             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
01939             break;
01940          }
01941       }
01942       if (!vmu) {
01943          if ((vmu = find_user_realtime_imapuser(mb->user))) {
01944             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
01945             free_user(vmu);
01946          }
01947       }
01948    }
01949 }
01950 
01951 
01952 void mm_critical(MAILSTREAM * stream)
01953 {
01954 }
01955 
01956 
01957 void mm_nocritical(MAILSTREAM * stream)
01958 {
01959 }
01960 
01961 
01962 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
01963 {
01964    kill (getpid (), SIGSTOP);
01965    return NIL;
01966 }
01967 
01968 
01969 void mm_fatal(char *string)
01970 {
01971    ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
01972 }
01973 
01974 /* C-client callback to handle quota */
01975 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
01976 {
01977    struct vm_state *vms;
01978    char *mailbox;
01979    char *user;
01980    unsigned long usage = 0;
01981    unsigned long limit = 0;
01982    
01983    while (pquota) {
01984       usage = pquota->usage;
01985       limit = pquota->limit;
01986       pquota = pquota->next;
01987    }
01988    
01989    mailbox = stream->mailbox;
01990    user = get_user_by_mailbox(mailbox);
01991    vms = get_vm_state_by_imapuser(user,2);
01992    if (!vms) {
01993       vms = get_vm_state_by_imapuser(user, 0);
01994    }
01995    if (vms) {
01996       if (option_debug > 2)
01997          ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
01998       vms->quota_usage = usage;
01999       vms->quota_limit = limit;
02000    } else {
02001       ast_log (LOG_ERROR, "No state found.\n");
02002    }
02003 }
02004 
02005 static char *get_header_by_tag(char *header, char *tag)
02006 {
02007    char *start;
02008    int taglen;
02009    char *eol_pnt;
02010 
02011    if (!header || !tag)
02012       return NULL;
02013 
02014    taglen = strlen(tag) + 1;
02015    if (taglen < 1)
02016       return NULL;
02017 
02018    start = strstr(header, tag);
02019    if (!start)
02020       return NULL;
02021 
02022    ast_mutex_lock(&imaptemp_lock);
02023    ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
02024    ast_mutex_unlock(&imaptemp_lock);
02025    if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
02026       *eol_pnt = '\0';
02027    return imaptemp;
02028 }
02029 
02030 static char *get_user_by_mailbox(char *mailbox)
02031 {
02032    char *start, *quote;
02033    char *eol_pnt;
02034 
02035    if (!mailbox)
02036       return NULL;
02037 
02038    start = strstr(mailbox,"/user=");
02039    if (!start)
02040       return NULL;
02041 
02042    ast_mutex_lock(&imaptemp_lock);
02043    ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
02044    ast_mutex_unlock(&imaptemp_lock);
02045 
02046    quote = strchr(imaptemp,'\"');
02047    if (!quote) {  /* if username is not in quotes */
02048       eol_pnt = strchr(imaptemp,'/');
02049       if (!eol_pnt) {
02050          eol_pnt = strchr(imaptemp,'}');
02051       }
02052       *eol_pnt = '\0';
02053       return imaptemp;
02054    } else {
02055       eol_pnt = strchr(imaptemp+1,'\"');
02056       *eol_pnt = '\0';
02057       return imaptemp+1;
02058    }
02059 }
02060 
02061 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02062 {
02063    struct vm_state *vms_p;
02064 
02065    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02066    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02067       return vms_p;
02068    }
02069    if (option_debug > 4)
02070       ast_log(LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02071    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02072       return NULL;
02073    ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
02074    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02075    ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
02076    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02077    vms_p->imapversion = vmu->imapversion;
02078    if (option_debug > 4)
02079       ast_log(LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02080    vms_p->updated = 1;
02081    /* set mailbox to INBOX! */
02082    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02083    init_vm_state(vms_p);
02084    vmstate_insert(vms_p);
02085    return vms_p;
02086 }
02087 
02088 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
02089 {
02090    struct vmstate *vlist = NULL;
02091 
02092    if (interactive) {
02093       struct vm_state *vms;
02094       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02095       vms = pthread_getspecific(ts_vmstate.key);
02096       return vms;
02097    }
02098 
02099    ast_mutex_lock(&vmstate_lock);
02100    vlist = vmstates;
02101    while (vlist) {
02102       if (vlist->vms && vlist->vms->imapversion == imapversion) {
02103          if (vlist->vms->imapuser) {
02104             if (!strcmp(vlist->vms->imapuser,user)) {
02105                if (interactive == 2) {
02106                   ast_mutex_unlock(&vmstate_lock);
02107                   return vlist->vms;
02108                } else if (vlist->vms->interactive == interactive) {
02109                   ast_mutex_unlock(&vmstate_lock);
02110                   return vlist->vms;
02111                }
02112             }
02113          } else {
02114             if (option_debug > 2)
02115                ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
02116          }
02117       } else {
02118          if (option_debug > 2)
02119             ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
02120       }
02121       vlist = vlist->next;
02122    }
02123    ast_mutex_unlock(&vmstate_lock);
02124    if (option_debug > 2)
02125       ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
02126    return NULL;
02127 }
02128 
02129 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
02130 { 
02131    struct vmstate *vlist = NULL;
02132    const char *local_context = S_OR(context, "default");
02133 
02134    if (interactive) {
02135       struct vm_state *vms;
02136       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02137       vms = pthread_getspecific(ts_vmstate.key);
02138       return vms;
02139    }
02140 
02141    ast_mutex_lock(&vmstate_lock);
02142    vlist = vmstates;
02143    if (option_debug > 2) 
02144       ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
02145    while (vlist) {
02146       if (vlist->vms) {
02147          if (vlist->vms->username && vlist->vms->context && vlist->vms->imapversion == imapversion) {
02148             if (option_debug > 2)
02149                ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
02150             if (!strcmp(vlist->vms->username,mailbox) && !(strcmp(vlist->vms->context, local_context))) {
02151                if (option_debug > 2)
02152                   ast_log(LOG_DEBUG, " Found it!\n");
02153                ast_mutex_unlock(&vmstate_lock);
02154                return vlist->vms;
02155             }
02156          } else {
02157             if (option_debug > 2)
02158                ast_log(LOG_DEBUG, " error: username or context is NULL for %s\n",mailbox);
02159          }
02160       } else {
02161          if (option_debug > 2)
02162             ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
02163       }
02164       vlist = vlist->next;
02165    }
02166    ast_mutex_unlock(&vmstate_lock);
02167    if (option_debug > 2)
02168       ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
02169    return NULL;
02170 }
02171 
02172 static void vmstate_insert(struct vm_state *vms) 
02173 {
02174    struct vmstate *v;
02175    struct vm_state *altvms;
02176 
02177    /* If interactive, it probably already exists, and we should
02178       use the one we already have since it is more up to date.
02179       We can compare the username to find the duplicate */
02180    if (vms->interactive == 1) {
02181       altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
02182       if (altvms) {
02183          if (option_debug > 2)
02184             ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
02185          vms->newmessages = altvms->newmessages;
02186          vms->oldmessages = altvms->oldmessages;
02187          /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
02188          copy_msgArray(vms, altvms);
02189          vms->vmArrayIndex = altvms->vmArrayIndex;
02190          vms->lastmsg = altvms->lastmsg;
02191          vms->curmsg = altvms->curmsg;
02192          /* get a pointer to the persistent store */
02193          vms->persist_vms = altvms;
02194          /* Reuse the mailstream? */
02195 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02196          vms->mailstream = altvms->mailstream;
02197 #else
02198          vms->mailstream = NIL;
02199 #endif
02200       }
02201       return;
02202    }
02203 
02204    v = (struct vmstate *)malloc(sizeof(struct vmstate));
02205    if (!v) {
02206       ast_log(LOG_ERROR, "Out of memory\n");
02207    }
02208    if (option_debug > 2)
02209       ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02210    ast_mutex_lock(&vmstate_lock);
02211    v->vms = vms;
02212    v->next = vmstates;
02213    vmstates = v;
02214    ast_mutex_unlock(&vmstate_lock);
02215 }
02216 
02217 static void vmstate_delete(struct vm_state *vms) 
02218 {
02219    struct vmstate *vc, *vf = NULL, *vl = NULL;
02220    struct vm_state *altvms;
02221 
02222    /* If interactive, we should copy pertainent info
02223       back to the persistent state (to make update immediate) */
02224    if (vms->interactive == 1) {
02225       altvms = vms->persist_vms;
02226       if (altvms) {
02227          if (option_debug > 2)
02228             ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
02229          altvms->newmessages = vms->newmessages;
02230          altvms->oldmessages = vms->oldmessages;
02231          altvms->updated = 1;
02232       }
02233       vms->mailstream = mail_close(vms->mailstream);
02234 
02235       /* Interactive states are not stored within the persistent list */
02236       return;
02237    }
02238 
02239    ast_mutex_lock(&vmstate_lock);
02240    vc = vmstates;
02241    if (option_debug > 2)
02242       ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02243    while (vc) {
02244       if (vc->vms == vms) {
02245          vf = vc;
02246          if (vl)
02247             vl->next = vc->next;
02248          else
02249             vmstates = vc->next;
02250          break;
02251       }
02252       vl = vc;
02253       vc = vc->next;
02254    }
02255    if (!vf) {
02256       ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02257    } else {
02258       ast_mutex_destroy(&vf->vms->lock);
02259       free(vf);
02260    }
02261    ast_mutex_unlock(&vmstate_lock);
02262 }
02263 
02264 static void set_update(MAILSTREAM * stream) 
02265 {
02266    struct vm_state *vms;
02267    char *mailbox;
02268    char *user;
02269 
02270    mailbox = stream->mailbox;
02271    user = get_user_by_mailbox(mailbox);
02272    vms = get_vm_state_by_imapuser(user, 0);
02273    if (vms) {
02274       if (option_debug > 2)
02275          ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
02276       vms->updated = 1; /* set updated flag since mailbox changed */
02277    } else {
02278       if (option_debug > 2)
02279          ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
02280    }
02281 }
02282 
02283 static void init_vm_state(struct vm_state *vms) 
02284 {
02285    int x;
02286    vms->vmArrayIndex = 0;
02287    for (x = 0; x < 256; x++) {
02288       vms->msgArray[x] = 0;
02289    }
02290    ast_mutex_init(&vms->lock);
02291 }
02292 
02293 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
02294 {
02295    int x;
02296    for (x = 0; x<256; x++) {
02297       dst->msgArray[x] = src->msgArray[x];
02298    }
02299 }
02300 
02301 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format) 
02302 {
02303    char *body_content;
02304    char *body_decoded;
02305    unsigned long len;
02306    unsigned long newlen;
02307    char filename[256];
02308    
02309    if (!body || body == NIL)
02310       return -1;
02311    ast_mutex_lock(&vms->lock);
02312    body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02313    ast_mutex_unlock(&vms->lock);
02314    if (body_content != NIL) {
02315       snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
02316       /* ast_log (LOG_DEBUG,body_content); */
02317       body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
02318       write_file (filename, (char *) body_decoded, newlen);
02319    }
02320    return 0;
02321 }
02322 
02323 /* get delimiter via mm_list callback */
02324 /* MUTEX should already be held */
02325 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02326    char tmp[50];
02327    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02328    mail_list(stream, tmp, "*");
02329 }
02330 
02331 /* Check Quota for user */
02332 static void check_quota(struct vm_state *vms, char *mailbox) {
02333    ast_mutex_lock(&vms->lock);
02334    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02335    if (option_debug > 2)
02336       ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02337    if (vms && vms->mailstream != NULL) {
02338       imap_getquotaroot(vms->mailstream, mailbox);
02339    } else {
02340       ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
02341    }
02342    ast_mutex_unlock(&vms->lock);
02343 }
02344 #endif
02345 
02346 /* only return failure if ast_lock_path returns 'timeout',
02347    not if the path does not exist or any other reason
02348 */
02349 static int vm_lock_path(const char *path)
02350 {
02351    switch (ast_lock_path(path)) {
02352    case AST_LOCK_TIMEOUT:
02353       return -1;
02354    default:
02355       return 0;
02356    }
02357 }
02358 
02359 
02360 #ifdef ODBC_STORAGE
02361 struct generic_prepare_struct {
02362    char *sql;
02363    int argc;
02364    char **argv;
02365 };
02366 
02367 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02368 {
02369    struct generic_prepare_struct *gps = data;
02370    int res, i;
02371    SQLHSTMT stmt;
02372 
02373    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02374    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02375       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02376       return NULL;
02377    }
02378    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02379    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02380       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02381       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02382       return NULL;
02383    }
02384    for (i = 0; i < gps->argc; i++)
02385       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02386 
02387    return stmt;
02388 }
02389 
02390 static int retrieve_file(char *dir, int msgnum)
02391 {
02392    int x = 0;
02393    int res;
02394    int fd=-1;
02395    size_t fdlen = 0;
02396    void *fdm = MAP_FAILED;
02397    SQLSMALLINT colcount=0;
02398    SQLHSTMT stmt;
02399    char sql[PATH_MAX];
02400    char fmt[80]="";
02401    char *c;
02402    char coltitle[256];
02403    SQLSMALLINT collen;
02404    SQLSMALLINT datatype;
02405    SQLSMALLINT decimaldigits;
02406    SQLSMALLINT nullable;
02407    SQLULEN colsize;
02408    SQLLEN colsize2;
02409    FILE *f=NULL;
02410    char rowdata[80];
02411    char fn[PATH_MAX];
02412    char full_fn[PATH_MAX];
02413    char msgnums[80];
02414    char *argv[] = { dir, msgnums };
02415    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02416 
02417    struct odbc_obj *obj;
02418    obj = ast_odbc_request_obj(odbc_database, 0);
02419    if (obj) {
02420       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02421       c = strchr(fmt, '|');
02422       if (c)
02423          *c = '\0';
02424       if (!strcasecmp(fmt, "wav49"))
02425          strcpy(fmt, "WAV");
02426       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02427       if (msgnum > -1)
02428          make_file(fn, sizeof(fn), dir, msgnum);
02429       else
02430          ast_copy_string(fn, dir, sizeof(fn));
02431       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02432       
02433       if (!(f = fopen(full_fn, "w+"))) {
02434               ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02435               goto yuck;
02436       }
02437       
02438       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02439       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02440       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02441       if (!stmt) {
02442          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02443          ast_odbc_release_obj(obj);
02444          goto yuck;
02445       }
02446       res = SQLFetch(stmt);
02447       if (res == SQL_NO_DATA) {
02448          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02449          ast_odbc_release_obj(obj);
02450          goto yuck;
02451       }
02452       else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02453          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02454          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02455          ast_odbc_release_obj(obj);
02456          goto yuck;
02457       }
02458       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
02459       if (fd < 0) {
02460          ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02461          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02462          ast_odbc_release_obj(obj);
02463          goto yuck;
02464       }
02465       res = SQLNumResultCols(stmt, &colcount);
02466       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02467          ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02468          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02469          ast_odbc_release_obj(obj);
02470          goto yuck;
02471       }
02472       if (f) 
02473          fprintf(f, "[message]\n");
02474       for (x=0;x<colcount;x++) {
02475          rowdata[0] = '\0';
02476          colsize = 0;
02477          collen = sizeof(coltitle);
02478          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02479                   &datatype, &colsize, &decimaldigits, &nullable);
02480          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02481             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02482             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02483             ast_odbc_release_obj(obj);
02484             goto yuck;
02485          }
02486          if (!strcasecmp(coltitle, "recording")) {
02487             off_t offset;
02488             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02489             fdlen = colsize2;
02490             if (fd > -1) {
02491                char tmp[1]="";
02492                lseek(fd, fdlen - 1, SEEK_SET);
02493                if (write(fd, tmp, 1) != 1) {
02494                   close(fd);
02495                   fd = -1;
02496                   continue;
02497                }
02498                /* Read out in small chunks */
02499                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
02500                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
02501                      ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
02502                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02503                      ast_odbc_release_obj(obj);
02504                      goto yuck;
02505                   } else {
02506                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
02507                      munmap(fdm, CHUNKSIZE);
02508                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02509                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02510                         unlink(full_fn);
02511                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02512                         ast_odbc_release_obj(obj);
02513                         goto yuck;
02514                      }
02515                   }
02516                }
02517                if (truncate(full_fn, fdlen) < 0) {
02518                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
02519                }
02520             }
02521          } else {
02522             SQLLEN ind;
02523             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &ind);
02524             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02525                SQLINTEGER nativeerror = 0;
02526                SQLSMALLINT diagbytes = 0;
02527                unsigned char state[10], diagnostic[256];
02528                SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
02529                ast_log(LOG_WARNING, "SQL Get Data error: %s: %s!\n[%s]\n\n", state, diagnostic, sql);
02530                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02531                ast_odbc_release_obj(obj);
02532                goto yuck;
02533             }
02534             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
02535                fprintf(f, "%s=%s\n", coltitle, rowdata);
02536          }
02537       }
02538       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02539       ast_odbc_release_obj(obj);
02540    } else
02541       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02542 yuck:
02543    if (f)
02544       fclose(f);
02545    if (fd > -1)
02546       close(fd);
02547    return x - 1;
02548 }
02549 
02550 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02551 {
02552    int x = 0;
02553    int res;
02554    SQLHSTMT stmt;
02555    char sql[PATH_MAX];
02556    char rowdata[20];
02557    char *argv[] = { dir };
02558    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02559 
02560    struct odbc_obj *obj;
02561    obj = ast_odbc_request_obj(odbc_database, 0);
02562    if (obj) {
02563       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc limit 1", odbc_table);
02564 
02565       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02566       if (!stmt) {
02567          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02568          ast_odbc_release_obj(obj);
02569          goto yuck;
02570       }
02571       res = SQLFetch(stmt);
02572       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02573          if (res == SQL_NO_DATA) {
02574             ast_log(LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
02575          } else {
02576             ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02577          }
02578 
02579          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02580          ast_odbc_release_obj(obj);
02581          goto yuck;
02582       }
02583       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02584       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02585          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02586          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02587          ast_odbc_release_obj(obj);
02588          goto yuck;
02589       }
02590       if (sscanf(rowdata, "%30d", &x) != 1)
02591          ast_log(LOG_WARNING, "Failed to read message index!\n");
02592       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02593       ast_odbc_release_obj(obj);
02594       return x;
02595    } else
02596       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02597 yuck:
02598    return x - 1;
02599 }
02600 
02601 static int message_exists(char *dir, int msgnum)
02602 {
02603    int x = 0;
02604    int res;
02605    SQLHSTMT stmt;
02606    char sql[PATH_MAX];
02607    char rowdata[20];
02608    char msgnums[20];
02609    char *argv[] = { dir, msgnums };
02610    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02611 
02612    struct odbc_obj *obj;
02613    obj = ast_odbc_request_obj(odbc_database, 0);
02614    if (obj) {
02615       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
02616       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02617       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02618       if (!stmt) {
02619          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02620          ast_odbc_release_obj(obj);
02621          goto yuck;
02622       }
02623       res = SQLFetch(stmt);
02624       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02625          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02626          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02627          ast_odbc_release_obj(obj);
02628          goto yuck;
02629       }
02630       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02631       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02632          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02633          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02634          ast_odbc_release_obj(obj);
02635          goto yuck;
02636       }
02637       if (sscanf(rowdata, "%30d", &x) != 1)
02638          ast_log(LOG_WARNING, "Failed to read message count!\n");
02639       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02640       ast_odbc_release_obj(obj);
02641    } else
02642       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02643 yuck:
02644    return x;
02645 }
02646 
02647 static int count_messages(struct ast_vm_user *vmu, char *dir)
02648 {
02649    int x = 0;
02650    int res;
02651    SQLHSTMT stmt;
02652    char sql[PATH_MAX];
02653    char rowdata[20];
02654    char *argv[] = { dir };
02655    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02656 
02657    struct odbc_obj *obj;
02658    obj = ast_odbc_request_obj(odbc_database, 0);
02659    if (obj) {
02660       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
02661       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02662       if (!stmt) {
02663          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02664          ast_odbc_release_obj(obj);
02665          goto yuck;
02666       }
02667       res = SQLFetch(stmt);
02668       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02669          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02670          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02671          ast_odbc_release_obj(obj);
02672          goto yuck;
02673       }
02674       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02675       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02676          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02677          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02678          ast_odbc_release_obj(obj);
02679          goto yuck;
02680       }
02681       if (sscanf(rowdata, "%30d", &x) != 1)
02682          ast_log(LOG_WARNING, "Failed to read message count!\n");
02683       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02684       ast_odbc_release_obj(obj);
02685       return x;
02686    } else
02687       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02688 yuck:
02689    return x - 1;
02690 
02691 }
02692 
02693 static void delete_file(char *sdir, int smsg)
02694 {
02695    SQLHSTMT stmt;
02696    char sql[PATH_MAX];
02697    char msgnums[20];
02698    char *argv[] = { sdir, msgnums };
02699    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02700 
02701    struct odbc_obj *obj;
02702    obj = ast_odbc_request_obj(odbc_database, 0);
02703    if (obj) {
02704       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02705       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
02706       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02707       if (!stmt)
02708          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02709       else
02710          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02711       ast_odbc_release_obj(obj);
02712    } else
02713       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02714    return;  
02715 }
02716 
02717 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
02718 {
02719    SQLHSTMT stmt;
02720    char sql[512];
02721    char msgnums[20];
02722    char msgnumd[20];
02723    struct odbc_obj *obj;
02724    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
02725    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
02726 
02727    delete_file(ddir, dmsg);
02728    obj = ast_odbc_request_obj(odbc_database, 0);
02729    if (obj) {
02730       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02731       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
02732       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);
02733       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02734       if (!stmt)
02735          ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
02736       else
02737          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02738       ast_odbc_release_obj(obj);
02739    } else
02740       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02741    return;  
02742 }
02743 
02744 struct insert_cb_struct {
02745    char *dir;
02746    char *msgnum;
02747    void *recording;
02748    size_t recordinglen;
02749    SQLLEN indlen;
02750    const char *context;
02751    const char *macrocontext;
02752    const char *callerid;
02753    const char *origtime;
02754    const char *duration;
02755    char *mailboxuser;
02756    char *mailboxcontext;
02757    const char *category;
02758    char *sql;
02759 };
02760 
02761 static SQLHSTMT insert_cb(struct odbc_obj *obj, void *vd)
02762 {
02763    struct insert_cb_struct *d = vd;
02764    int res;
02765    SQLHSTMT stmt;
02766 
02767    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02768    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02769       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02770       return NULL;
02771    }
02772 
02773    res = SQLPrepare(stmt, (unsigned char *)d->sql, SQL_NTS);
02774    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02775       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", d->sql);
02776       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02777       return NULL;
02778    }
02779 
02780    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->dir), 0, (void *)d->dir, 0, NULL);
02781    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->msgnum), 0, (void *)d->msgnum, 0, NULL);
02782    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, d->recordinglen, 0, (void *)d->recording, 0, &d->indlen);
02783    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->context), 0, (void *)d->context, 0, NULL);
02784    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->macrocontext), 0, (void *)d->macrocontext, 0, NULL);
02785    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->callerid), 0, (void *)d->callerid, 0, NULL);
02786    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->origtime), 0, (void *)d->origtime, 0, NULL);
02787    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->duration), 0, (void *)d->duration, 0, NULL);
02788    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxuser), 0, (void *)d->mailboxuser, 0, NULL);
02789    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxcontext), 0, (void *)d->mailboxcontext, 0, NULL);
02790    if (!ast_strlen_zero(d->category)) {
02791       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->category), 0, (void *)d->category, 0, NULL);
02792    }
02793 
02794    return stmt;
02795 }
02796 
02797 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
02798 {
02799    int x = 0;
02800    int fd = -1;
02801    void *fdm = MAP_FAILED;
02802    size_t fdlen = -1;
02803    SQLHSTMT stmt;
02804    char sql[PATH_MAX];
02805    char msgnums[20];
02806    char fn[PATH_MAX];
02807    char full_fn[PATH_MAX];
02808    char fmt[80]="";
02809    char *c;
02810    struct insert_cb_struct d = {
02811       .dir = dir,
02812       .msgnum = msgnums,
02813       .context = "",
02814       .macrocontext = "",
02815       .callerid = "",
02816       .origtime = "",
02817       .duration = "",
02818       .mailboxuser = mailboxuser,
02819       .mailboxcontext = mailboxcontext,
02820       .category = "",
02821       .sql = sql
02822    };
02823    struct ast_config *cfg=NULL;
02824    struct odbc_obj *obj;
02825 
02826    delete_file(dir, msgnum);
02827    obj = ast_odbc_request_obj(odbc_database, 0);
02828    if (obj) {
02829       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02830       c = strchr(fmt, '|');
02831       if (c)
02832          *c = '\0';
02833       if (!strcasecmp(fmt, "wav49"))
02834          strcpy(fmt, "WAV");
02835       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
02836       if (msgnum > -1)
02837          make_file(fn, sizeof(fn), dir, msgnum);
02838       else
02839          ast_copy_string(fn, dir, sizeof(fn));
02840       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02841       cfg = ast_config_load(full_fn);
02842       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02843       fd = open(full_fn, O_RDWR);
02844       if (fd < 0) {
02845          ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
02846          ast_odbc_release_obj(obj);
02847          goto yuck;
02848       }
02849       if (cfg) {
02850          d.context = ast_variable_retrieve(cfg, "message", "context");
02851          if (!d.context) d.context = "";
02852          d.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
02853          if (!d.macrocontext) d.macrocontext = "";
02854          d.callerid = ast_variable_retrieve(cfg, "message", "callerid");
02855          if (!d.callerid) d.callerid = "";
02856          d.origtime = ast_variable_retrieve(cfg, "message", "origtime");
02857          if (!d.origtime) d.origtime = "";
02858          d.duration = ast_variable_retrieve(cfg, "message", "duration");
02859          if (!d.duration) d.duration = "";
02860          d.category = ast_variable_retrieve(cfg, "message", "category");
02861          if (!d.category) d.category = "";
02862       }
02863       fdlen = lseek(fd, 0, SEEK_END);
02864       lseek(fd, 0, SEEK_SET);
02865       printf("Length is %zd\n", fdlen);
02866       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
02867       if (fdm == MAP_FAILED) {
02868          ast_log(LOG_WARNING, "Memory map failed!\n");
02869          ast_odbc_release_obj(obj);
02870          goto yuck;
02871       }
02872       d.recording = fdm;
02873       d.recordinglen = d.indlen = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
02874       if (!ast_strlen_zero(d.category)) 
02875          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
02876       else
02877          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?, ? , ?,?,?,?,?,?,?)",odbc_table);
02878       stmt = ast_odbc_prepare_and_execute(obj, insert_cb, &d);
02879       if (stmt) {
02880          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02881       }
02882       ast_odbc_release_obj(obj);
02883    } else
02884       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02885 yuck:
02886    if (cfg)
02887       ast_config_destroy(cfg);
02888    if (fdm != MAP_FAILED)
02889       munmap(fdm, fdlen);
02890    if (fd > -1)
02891       close(fd);
02892    return x;
02893 }
02894 
02895 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
02896 {
02897    SQLHSTMT stmt;
02898    char sql[PATH_MAX];
02899    char msgnums[20];
02900    char msgnumd[20];
02901    struct odbc_obj *obj;
02902    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
02903    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
02904 
02905    delete_file(ddir, dmsg);
02906    obj = ast_odbc_request_obj(odbc_database, 0);
02907    if (obj) {
02908       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02909       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
02910       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
02911       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02912       if (!stmt)
02913          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02914       else
02915          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02916       ast_odbc_release_obj(obj);
02917    } else
02918       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02919    return;  
02920 }
02921 
02922 #else
02923 #ifndef IMAP_STORAGE
02924 static int count_messages(struct ast_vm_user *vmu, char *dir)
02925 {
02926    /* Find all .txt files - even if they are not in sequence from 0000 */
02927 
02928    int vmcount = 0;
02929    DIR *vmdir = NULL;
02930    struct dirent *vment = NULL;
02931 
02932    if (vm_lock_path(dir))
02933       return ERROR_LOCK_PATH;
02934 
02935    if ((vmdir = opendir(dir))) {
02936       while ((vment = readdir(vmdir))) {
02937          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
02938             vmcount++;
02939       }
02940       closedir(vmdir);
02941    }
02942    ast_unlock_path(dir);
02943    
02944    return vmcount;
02945 }
02946 
02947 static void rename_file(char *sfn, char *dfn)
02948 {
02949    char stxt[PATH_MAX];
02950    char dtxt[PATH_MAX];
02951    ast_filerename(sfn,dfn,NULL);
02952    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
02953    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
02954    rename(stxt, dtxt);
02955 }
02956 #endif
02957 
02958 /*! 
02959  * \brief Determines the highest message number in use for a given user and mailbox folder.
02960  * \param vmu 
02961  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
02962  *
02963  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
02964  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
02965  *
02966  * \note Should always be called with a lock already set on dir.
02967  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
02968  */
02969 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
02970 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02971 {
02972    int x;
02973    unsigned char map[MAXMSGLIMIT] = "";
02974    DIR *msgdir;
02975    struct dirent *msgdirent;
02976    int msgdirint;
02977    char extension[4];
02978    int stopcount = 0;
02979 
02980    /* Reading the entire directory into a file map scales better than
02981     * doing a stat repeatedly on a predicted sequence.  I suspect this
02982     * is partially due to stat(2) internally doing a readdir(2) itself to
02983     * find each file. */
02984    if (!(msgdir = opendir(dir))) {
02985       return -1;
02986    }
02987 
02988    while ((msgdirent = readdir(msgdir))) {
02989       if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
02990          map[msgdirint] = 1;
02991          stopcount++;
02992          if (option_debug > 3) {
02993             ast_log(LOG_DEBUG, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
02994          }
02995       }
02996    }
02997    closedir(msgdir);
02998 
02999    for (x = 0; x < vmu->maxmsg; x++) {
03000       if (map[x] == 1) {
03001          stopcount--;
03002       } else if (map[x] == 0 && !stopcount) {
03003          break;
03004       }
03005    }
03006 
03007    return x - 1;
03008 }
03009 #endif
03010 #endif
03011 #if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
03012 static int remove_file(char *dir, int msgnum)
03013 {
03014    char fn[PATH_MAX];
03015    char full_fn[PATH_MAX];
03016    char msgnums[80];
03017    
03018    if (msgnum > -1) {
03019       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
03020       make_file(fn, sizeof(fn), dir, msgnum);
03021    } else {
03022 #ifndef IMAP_STORAGE
03023       ast_copy_string(fn, dir, sizeof(fn));
03024 #else
03025       /*IMAP stores greetings locally so it should not
03026        * try to dispose of them
03027        */
03028       return 0;
03029 #endif
03030    }
03031    ast_filedelete(fn, NULL);  
03032    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
03033    unlink(full_fn);
03034    return 0;
03035 }
03036 #endif
03037 
03038 #ifndef IMAP_STORAGE
03039 static int copy(char *infile, char *outfile)
03040 {
03041    int ifd;
03042    int ofd;
03043    int res;
03044    int len;
03045    char buf[4096];
03046 
03047 #ifdef HARDLINK_WHEN_POSSIBLE
03048    /* Hard link if possible; saves disk space & is faster */
03049    if (link(infile, outfile)) {
03050 #endif
03051       if ((ifd = open(infile, O_RDONLY)) < 0) {
03052          ast_log(LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03053          return -1;
03054       }
03055       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03056          ast_log(LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03057          close(ifd);
03058          return -1;
03059       }
03060       do {
03061          len = read(ifd, buf, sizeof(buf));
03062          if (len < 0) {
03063             ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03064             close(ifd);
03065             close(ofd);
03066             unlink(outfile);
03067          }
03068          if (len) {
03069             res = write(ofd, buf, len);
03070             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03071                ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03072                close(ifd);
03073                close(ofd);
03074                unlink(outfile);
03075             }
03076          }
03077       } while (len);
03078       close(ifd);
03079       close(ofd);
03080       return 0;
03081 #ifdef HARDLINK_WHEN_POSSIBLE
03082    } else {
03083       /* Hard link succeeded */
03084       return 0;
03085    }
03086 #endif
03087 }
03088 #endif
03089 
03090 #ifndef IMAP_STORAGE
03091 static void copy_plain_file(char *frompath, char *topath)
03092 {
03093    char frompath2[PATH_MAX], topath2[PATH_MAX];
03094    ast_filecopy(frompath, topath, NULL);
03095    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03096    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03097    copy(frompath2, topath2);
03098 }
03099 #endif
03100 
03101 #ifndef IMAP_STORAGE
03102 static int vm_delete(char *file)
03103 {
03104    char *txt;
03105    int txtsize = 0;
03106 
03107    txtsize = (strlen(file) + 5)*sizeof(char);
03108    txt = alloca(txtsize);
03109    /* Sprintf here would safe because we alloca'd exactly the right length,
03110     * but trying to eliminate all sprintf's anyhow
03111     */
03112    snprintf(txt, txtsize, "%s.txt", file);
03113    unlink(txt);
03114    return ast_filedelete(file, NULL);
03115 }
03116 #endif
03117 
03118 static int inbuf(struct baseio *bio, FILE *fi)
03119 {
03120    int l;
03121 
03122    if (bio->ateof)
03123       return 0;
03124 
03125    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
03126       if (ferror(fi))
03127          return -1;
03128 
03129       bio->ateof = 1;
03130       return 0;
03131    }
03132 
03133    bio->iolen= l;
03134    bio->iocp= 0;
03135 
03136    return 1;
03137 }
03138 
03139 static int inchar(struct baseio *bio, FILE *fi)
03140 {
03141    if (bio->iocp>=bio->iolen) {
03142       if (!inbuf(bio, fi))
03143          return EOF;
03144    }
03145 
03146    return bio->iobuf[bio->iocp++];
03147 }
03148 
03149 static int ochar(struct baseio *bio, int c, FILE *so)
03150 {
03151    if (bio->linelength >= BASELINELEN) {
03152       if (fputs(ENDL, so) == EOF) {
03153          return -1;
03154       }
03155 
03156       bio->linelength = 0;
03157    }
03158 
03159    if (putc(((unsigned char) c), so) == EOF) {
03160       return -1;
03161    }
03162 
03163    bio->linelength++;
03164 
03165    return 1;
03166 }
03167 
03168 static int base_encode(char *filename, FILE *so)
03169 {
03170    unsigned char dtable[BASEMAXINLINE];
03171    int i,hiteof= 0;
03172    FILE *fi;
03173    struct baseio bio;
03174 
03175    memset(&bio, 0, sizeof(bio));
03176    bio.iocp = BASEMAXINLINE;
03177 
03178    if (!(fi = fopen(filename, "rb"))) {
03179       ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03180       return -1;
03181    }
03182 
03183    for (i= 0;i<9;i++) {
03184       dtable[i]= 'A'+i;
03185       dtable[i+9]= 'J'+i;
03186       dtable[26+i]= 'a'+i;
03187       dtable[26+i+9]= 'j'+i;
03188    }
03189    for (i= 0;i<8;i++) {
03190       dtable[i+18]= 'S'+i;
03191       dtable[26+i+18]= 's'+i;
03192    }
03193    for (i= 0;i<10;i++) {
03194       dtable[52+i]= '0'+i;
03195    }
03196    dtable[62]= '+';
03197    dtable[63]= '/';
03198 
03199    while (!hiteof){
03200       unsigned char igroup[3],ogroup[4];
03201       int c,n;
03202 
03203       igroup[0]= igroup[1]= igroup[2]= 0;
03204 
03205       for (n= 0;n<3;n++) {
03206          if ((c = inchar(&bio, fi)) == EOF) {
03207             hiteof= 1;
03208             break;
03209          }
03210 
03211          igroup[n]= (unsigned char)c;
03212       }
03213 
03214       if (n> 0) {
03215          ogroup[0]= dtable[igroup[0]>>2];
03216          ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
03217          ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
03218          ogroup[3]= dtable[igroup[2]&0x3F];
03219 
03220          if (n<3) {
03221             ogroup[3]= '=';
03222 
03223             if (n<2)
03224                ogroup[2]= '=';
03225          }
03226 
03227          for (i= 0;i<4;i++)
03228             ochar(&bio, ogroup[i], so);
03229       }
03230    }
03231 
03232    fclose(fi);
03233    
03234    if (fputs(ENDL, so) == EOF) {
03235       return 0;
03236    }
03237 
03238    return 1;
03239 }
03240 
03241 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)
03242 {
03243    char callerid[256];
03244    char fromdir[256], fromfile[256];
03245    struct ast_config *msg_cfg;
03246    const char *origcallerid, *origtime;
03247    char origcidname[80], origcidnum[80], origdate[80];
03248    int inttime;
03249 
03250    /* Prepare variables for substition in email body and subject */
03251    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03252    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03253    snprintf(passdata, passdatasize, "%d", msgnum);
03254    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03255    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03256    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03257    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03258       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03259    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03260    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03261    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03262    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03263 
03264    /* Retrieve info from VM attribute file */
03265    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03266    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03267    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03268       strcat(fromfile, ".txt");
03269    }
03270    if (!(msg_cfg = ast_config_load(fromfile))) {
03271       if (option_debug > 0) {
03272          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03273       }
03274       return;
03275    }
03276 
03277    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03278       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03279       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03280       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03281       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03282    }
03283 
03284    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
03285       time_t ttime = inttime;
03286       struct tm tm;
03287       ast_localtime(&ttime, &tm, NULL);
03288       strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03289       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03290    }
03291    ast_config_destroy(msg_cfg);
03292 }
03293 
03294 static char *quote(const char *from, char *to, size_t len)
03295 {
03296    char *ptr = to;
03297    *ptr++ = '"';
03298    for (; ptr < to + len - 1; from++) {
03299       if (*from == '"')
03300          *ptr++ = '\\';
03301       else if (*from == '\0')
03302          break;
03303       *ptr++ = *from;
03304    }
03305    if (ptr < to + len - 1)
03306       *ptr++ = '"';
03307    *ptr = '\0';
03308    return to;
03309 }
03310 /*
03311  * fill in *tm for current time according to the proper timezone, if any.
03312  * Return tm so it can be used as a function argument.
03313  */
03314 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
03315 {
03316    const struct vm_zone *z = NULL;
03317    time_t t = time(NULL);
03318 
03319    /* Does this user have a timezone specified? */
03320    if (!ast_strlen_zero(vmu->zonetag)) {
03321       /* Find the zone in the list */
03322       AST_LIST_LOCK(&zones);
03323       AST_LIST_TRAVERSE(&zones, z, list) {
03324          if (!strcmp(z->name, vmu->zonetag))
03325             break;
03326       }
03327       AST_LIST_UNLOCK(&zones);
03328    }
03329    ast_localtime(&t, tm, z ? z->timezone : NULL);
03330    return tm;
03331 }
03332 
03333 /*!\brief Check if the string would need encoding within the MIME standard, to
03334  * avoid confusing certain mail software that expects messages to be 7-bit
03335  * clean.
03336  */
03337 static int check_mime(const char *str)
03338 {
03339    for (; *str; str++) {
03340       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03341          return 1;
03342       }
03343    }
03344    return 0;
03345 }
03346 
03347 /*!\brief Encode a string according to the MIME rules for encoding strings
03348  * that are not 7-bit clean or contain control characters.
03349  *
03350  * Additionally, if the encoded string would exceed the MIME limit of 76
03351  * characters per line, then the encoding will be broken up into multiple
03352  * sections, separated by a space character, in order to facilitate
03353  * breaking up the associated header across multiple lines.
03354  *
03355  * \param start A string to be encoded
03356  * \param end An expandable buffer for holding the result
03357  * \param preamble The length of the first line already used for this string,
03358  * to ensure that each line maintains a maximum length of 76 chars.
03359  * \param postamble the length of any additional characters appended to the
03360  * line, used to ensure proper field wrapping.
03361  * \retval The encoded string.
03362  */
03363 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03364 {
03365    char tmp[80];
03366    int first_section = 1;
03367    size_t endlen = 0, tmplen = 0;
03368    *end = '\0';
03369 
03370    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03371    for (; *start; start++) {
03372       int need_encoding = 0;
03373       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03374          need_encoding = 1;
03375       }
03376       if ((first_section && need_encoding && preamble + tmplen > 70) ||
03377          (first_section && !need_encoding && preamble + tmplen > 72) ||
03378          (!first_section && need_encoding && tmplen > 70) ||
03379          (!first_section && !need_encoding && tmplen > 72)) {
03380          /* Start new line */
03381          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
03382          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03383          first_section = 0;
03384       }
03385       if (need_encoding && *start == ' ') {
03386          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
03387       } else if (need_encoding) {
03388          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
03389       } else {
03390          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
03391       }
03392    }
03393    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
03394    return end;
03395 }
03396 
03397 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)
03398 {
03399    char date[256];
03400    char host[MAXHOSTNAMELEN] = "";
03401    char who[256];
03402    char bound[256];
03403    char fname[256];
03404    char dur[256];
03405    char tmpcmd[256];
03406    char enc_cidnum[256] = "", enc_cidname[256] = "";
03407    struct tm tm;
03408    char *passdata = NULL, *passdata2;
03409    size_t len_passdata = 0, len_passdata2, tmplen;
03410 
03411    /* One alloca for multiple fields */
03412    len_passdata2 = strlen(vmu->fullname);
03413    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
03414       len_passdata2 = tmplen;
03415    }
03416    if ((tmplen = strlen(emailtitle)) > len_passdata2) {
03417       len_passdata2 = tmplen;
03418    }
03419    if ((tmplen = strlen(fromstring)) > len_passdata2) {
03420       len_passdata2 = tmplen;
03421    }
03422    len_passdata2 = len_passdata2 * 3 + 200;
03423    passdata2 = alloca(len_passdata2);
03424 
03425    if (cidnum) {
03426       strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
03427    }
03428    if (cidname) {
03429       strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
03430    }
03431    gethostname(host, sizeof(host) - 1);
03432    if (strchr(srcemail, '@'))
03433       ast_copy_string(who, srcemail, sizeof(who));
03434    else {
03435       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03436    }
03437    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03438    strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03439    fprintf(p, "Date: %s" ENDL, date);
03440 
03441    /* Set date format for voicemail mail */
03442    strftime(date, sizeof(date), emaildateformat, &tm);
03443 
03444    if (*fromstring) {
03445       struct ast_channel *ast;
03446       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03447          char *ptr;
03448          memset(passdata2, 0, len_passdata2);
03449          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category);
03450          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
03451          len_passdata = strlen(passdata2) * 3 + 300;
03452          passdata = alloca(len_passdata);
03453          if (check_mime(passdata2)) {
03454             int first_line = 1;
03455             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
03456             while ((ptr = strchr(passdata, ' '))) {
03457                *ptr = '\0';
03458                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
03459                first_line = 0;
03460                passdata = ptr + 1;
03461             }
03462             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
03463          } else {
03464             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
03465          }
03466          ast_channel_free(ast);
03467       } else
03468          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03469    } else
03470       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
03471 
03472    if (check_mime(vmu->fullname)) {
03473       int first_line = 1;
03474       char *ptr;
03475       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
03476       while ((ptr = strchr(passdata2, ' '))) {
03477          *ptr = '\0';
03478          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
03479          first_line = 0;
03480          passdata2 = ptr + 1;
03481       }
03482       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
03483    } else {
03484       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
03485    }
03486    if (emailsubject) {
03487       struct ast_channel *ast;
03488       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03489          int vmlen = strlen(emailsubject) * 3 + 200;
03490          /* Only allocate more space if the previous was not large enough */
03491          if (vmlen > len_passdata) {
03492             passdata = alloca(vmlen);
03493             len_passdata = vmlen;
03494          }
03495 
03496          memset(passdata, 0, len_passdata);
03497          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category);
03498          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
03499          if (check_mime(passdata)) {
03500             int first_line = 1;
03501             char *ptr;
03502             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
03503             while ((ptr = strchr(passdata2, ' '))) {
03504                *ptr = '\0';
03505                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
03506                first_line = 0;
03507                passdata2 = ptr + 1;
03508             }
03509             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
03510          } else {
03511             fprintf(p, "Subject: %s" ENDL, passdata);
03512          }
03513          ast_channel_free(ast);
03514       } else {
03515          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03516       }
03517    } else   if (*emailtitle) {
03518       fprintf(p, emailtitle, msgnum + 1, mailbox) ;
03519       fprintf(p, ENDL) ;
03520    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
03521       fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
03522    } else {
03523       fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
03524    }
03525    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
03526    if (imap) {
03527       /* additional information needed for IMAP searching */
03528       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
03529       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
03530       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
03531       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
03532 #ifdef IMAP_STORAGE
03533       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
03534 #else
03535       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
03536 #endif
03537       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
03538       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
03539       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
03540       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
03541       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
03542       if (!ast_strlen_zero(category)) {
03543          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
03544       }
03545       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
03546       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
03547    }
03548    if (!ast_strlen_zero(cidnum)) {
03549       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
03550    }
03551    if (!ast_strlen_zero(cidname)) {
03552       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
03553    }
03554    fprintf(p, "MIME-Version: 1.0" ENDL);
03555    if (attach_user_voicemail) {
03556       /* Something unique. */
03557       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
03558 
03559       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
03560       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
03561       fprintf(p, "--%s" ENDL, bound);
03562    }
03563    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
03564    if (emailbody) {
03565       struct ast_channel *ast;
03566       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03567          char *passdata;
03568          int vmlen = strlen(emailbody)*3 + 200;
03569          if ((passdata = alloca(vmlen))) {
03570             memset(passdata, 0, vmlen);
03571             prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03572             pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
03573 #ifdef IMAP_STORAGE
03574             {
03575                /* Convert body to native line terminators for IMAP backend */
03576                char *line = passdata, *next;
03577                do {
03578                   /* Terminate line before outputting it to the file */
03579                   if ((next = strchr(line, '\n'))) {
03580                      *next++ = '\0';
03581                   }
03582                   fprintf(p, "%s" ENDL, line);
03583                   line = next;
03584                } while (!ast_strlen_zero(line));
03585             }
03586 #else
03587             fprintf(p, "%s" ENDL, passdata);
03588 #endif
03589          } else
03590             ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03591          ast_channel_free(ast);
03592       } else
03593          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03594    } else {
03595       if (strcmp(vmu->mailbox, mailbox)) {
03596          /* Forwarded type */
03597          struct ast_config *msg_cfg;
03598          const char *v;
03599          int inttime;
03600          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
03601          /* Retrieve info from VM attribute file */
03602          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03603          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
03604          if (strlen(fromfile) < sizeof(fromfile) - 5) {
03605             strcat(fromfile, ".txt");
03606          }
03607          if ((msg_cfg = ast_config_load(fromfile))) {
03608             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03609                ast_copy_string(origcallerid, v, sizeof(origcallerid));
03610             }
03611 
03612             /* You might be tempted to do origdate, except that a) it's in the wrong
03613              * format, and b) it's missing for IMAP recordings. */
03614             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
03615                time_t ttime = inttime;
03616                struct tm tm;
03617                ast_localtime(&ttime, &tm, NULL);
03618                strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03619             }
03620             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
03621                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
03622                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
03623                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
03624                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
03625                date, origcallerid, origdate);
03626             ast_config_destroy(msg_cfg);
03627          } else {
03628             goto plain_message;
03629          }
03630       } else {
03631 plain_message:
03632          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
03633             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
03634             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
03635             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
03636             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
03637       }
03638    }
03639    if (attach_user_voicemail) {
03640       /* Eww. We want formats to tell us their own MIME type */
03641       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
03642       char tmpdir[256], newtmp[256];
03643       int tmpfd = -1;
03644       int soxstatus = 0;
03645 
03646       if (vmu->volgain < -.001 || vmu->volgain > .001) {
03647          create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
03648          snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
03649          tmpfd = mkstemp(newtmp);
03650          chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
03651          if (option_debug > 2)
03652             ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
03653          if (tmpfd > -1) {
03654             snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
03655             if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
03656                attach = newtmp;
03657                if (option_debug > 2) {
03658                   ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
03659                }
03660             } else {
03661                ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
03662                   soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
03663                ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
03664             }
03665          }
03666       }
03667       fprintf(p, "--%s" ENDL, bound);
03668       fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
03669       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
03670       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
03671       fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
03672       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
03673       base_encode(fname, p);
03674       fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
03675       if (tmpfd > -1) {
03676          if (soxstatus == 0) {
03677             unlink(fname);
03678          }
03679          close(tmpfd);
03680          unlink(newtmp);
03681       }
03682    }
03683 }
03684 
03685 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)
03686 {
03687    FILE *p=NULL;
03688    char tmp[80] = "/tmp/astmail-XXXXXX";
03689    char tmp2[256];
03690    char *stringp;
03691 
03692    if (vmu && ast_strlen_zero(vmu->email)) {
03693       ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
03694       return(0);
03695    }
03696 
03697    /* Mail only the first format */
03698    format = ast_strdupa(format);
03699    stringp = format;
03700    strsep(&stringp, "|");
03701 
03702    if (!strcmp(format, "wav49"))
03703       format = "WAV";
03704    if (option_debug > 2)
03705       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));
03706    /* Make a temporary file instead of piping directly to sendmail, in case the mail
03707       command hangs */
03708    if ((p = vm_mkftemp(tmp)) == NULL) {
03709       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03710       return -1;
03711    } else {
03712       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
03713       fclose(p);
03714       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03715       ast_safe_system(tmp2);
03716       if (option_debug > 2)
03717          ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
03718    }
03719    return 0;
03720 }
03721 
03722 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)
03723 {
03724    char date[256];
03725    char host[MAXHOSTNAMELEN] = "";
03726    char who[256];
03727    char dur[PATH_MAX];
03728    char tmp[80] = "/tmp/astmail-XXXXXX";
03729    char tmp2[PATH_MAX];
03730    struct tm tm;
03731    FILE *p;
03732 
03733    if ((p = vm_mkftemp(tmp)) == NULL) {
03734       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03735       return -1;
03736    } else {
03737       gethostname(host, sizeof(host)-1);
03738       if (strchr(srcemail, '@'))
03739          ast_copy_string(who, srcemail, sizeof(who));
03740       else {
03741          snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03742       }
03743       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03744       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03745       fprintf(p, "Date: %s\n", date);
03746 
03747       if (*pagerfromstring) {
03748          struct ast_channel *ast;
03749          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03750             char *passdata;
03751             int vmlen = strlen(fromstring)*3 + 200;
03752             if ((passdata = alloca(vmlen))) {
03753                memset(passdata, 0, vmlen);
03754                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03755                pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
03756                fprintf(p, "From: %s <%s>\n", passdata, who);
03757             } else 
03758                ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03759             ast_channel_free(ast);
03760          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03761       } else
03762          fprintf(p, "From: Asterisk PBX <%s>\n", who);
03763       fprintf(p, "To: %s\n", pager);
03764       if (pagersubject) {
03765          struct ast_channel *ast;
03766          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03767             char *passdata;
03768             int vmlen = strlen(pagersubject) * 3 + 200;
03769             if ((passdata = alloca(vmlen))) {
03770                memset(passdata, 0, vmlen);
03771                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03772                pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
03773                fprintf(p, "Subject: %s\n\n", passdata);
03774             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03775             ast_channel_free(ast);
03776          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03777       } else
03778          fprintf(p, "Subject: New VM\n\n");
03779       strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
03780       if (pagerbody) {
03781          struct ast_channel *ast;
03782          if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03783             char *passdata;
03784             int vmlen = strlen(pagerbody)*3 + 200;
03785             if ((passdata = alloca(vmlen))) {
03786                memset(passdata, 0, vmlen);
03787                prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03788                pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
03789                fprintf(p, "%s\n", passdata);
03790             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
03791          ast_channel_free(ast);
03792          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03793       } else {
03794          fprintf(p, "New %s long msg in box %s\n"
03795                "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
03796       }
03797       fclose(p);
03798       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03799       ast_safe_system(tmp2);
03800       if (option_debug > 2)
03801          ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
03802    }
03803    return 0;
03804 }
03805 
03806 static int get_date(char *s, int len)
03807 {
03808    struct tm tm;
03809    time_t t;
03810 
03811    time(&t);
03812 
03813    ast_localtime(&t, &tm, NULL);
03814 
03815    return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
03816 }
03817 
03818 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
03819 {
03820    int res = -2;
03821 
03822 #ifdef ODBC_STORAGE
03823    int success = 
03824 #endif
03825    RETRIEVE(filename, -1, vmu);
03826    if (ast_fileexists(filename, NULL, NULL) > 0) {
03827       res = ast_streamfile(chan, filename, chan->language);
03828       if (res > -1) 
03829          res = ast_waitstream(chan, ecodes);
03830 #ifdef ODBC_STORAGE
03831       if (success == -1) {
03832          /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
03833          if (option_debug)
03834             ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
03835          store_file(filename, vmu->mailbox, vmu->context, -1);
03836       }
03837 #endif
03838    }
03839    DISPOSE(filename, -1);
03840 
03841    return res;
03842 }
03843 
03844 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
03845 {
03846    int res;
03847    char fn[PATH_MAX];
03848    char dest[PATH_MAX];
03849 
03850    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
03851 
03852    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
03853       ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
03854       return -1;
03855    }
03856 
03857    res = play_greeting(chan, vmu, fn, ecodes);
03858    if (res == -2) {
03859       /* File did not exist */
03860       res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
03861       if (res)
03862          return res;
03863       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
03864    }
03865 
03866    if (res)
03867       return res;
03868 
03869    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
03870    return res;
03871 }
03872 
03873 static void free_zone(struct vm_zone *z)
03874 {
03875    free(z);
03876 }
03877 
03878 #ifdef ODBC_STORAGE
03879 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
03880 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
03881 {
03882    int x = -1;
03883    int res;
03884    SQLHSTMT stmt;
03885    char sql[PATH_MAX];
03886    char rowdata[20];
03887    char tmp[PATH_MAX] = "";
03888    struct odbc_obj *obj;
03889    char *context;
03890    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
03891 
03892    if (newmsgs)
03893       *newmsgs = 0;
03894    if (oldmsgs)
03895       *oldmsgs = 0;
03896 
03897    /* If no mailbox, return immediately */
03898    if (ast_strlen_zero(mailbox))
03899       return 0;
03900 
03901    ast_copy_string(tmp, mailbox, sizeof(tmp));
03902    
03903    context = strchr(tmp, '@');
03904    if (context) {
03905       *context = '\0';
03906       context++;
03907    } else
03908       context = "default";
03909    
03910    obj = ast_odbc_request_obj(odbc_database, 0);
03911    if (obj) {
03912       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
03913       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03914       if (!stmt) {
03915          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03916          ast_odbc_release_obj(obj);
03917          goto yuck;
03918       }
03919       res = SQLFetch(stmt);
03920       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03921          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03922          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03923          ast_odbc_release_obj(obj);
03924          goto yuck;
03925       }
03926       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03927       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03928          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03929          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03930          ast_odbc_release_obj(obj);
03931          goto yuck;
03932       }
03933       *newmsgs = atoi(rowdata);
03934       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03935 
03936       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
03937       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03938       if (!stmt) {
03939          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03940          ast_odbc_release_obj(obj);
03941          goto yuck;
03942       }
03943       res = SQLFetch(stmt);
03944       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03945          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03946          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03947          ast_odbc_release_obj(obj);
03948          goto yuck;
03949       }
03950       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03951       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03952          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03953          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03954          ast_odbc_release_obj(obj);
03955          goto yuck;
03956       }
03957       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03958       ast_odbc_release_obj(obj);
03959       *oldmsgs = atoi(rowdata);
03960       x = 0;
03961    } else
03962       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
03963       
03964 yuck:
03965    return x;
03966 }
03967 
03968 static int messagecount(const char *context, const char *mailbox, const char *folder)
03969 {
03970    struct odbc_obj *obj = NULL;
03971    int nummsgs = 0;
03972    int res;
03973    SQLHSTMT stmt = NULL;
03974    char sql[PATH_MAX];
03975    char rowdata[20];
03976    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
03977    if (!folder)
03978       folder = "INBOX";
03979    /* If no mailbox, return immediately */
03980    if (ast_strlen_zero(mailbox))
03981       return 0;
03982 
03983    obj = ast_odbc_request_obj(odbc_database, 0);
03984    if (obj) {
03985       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
03986       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03987       if (!stmt) {
03988          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03989          goto yuck;
03990       }
03991       res = SQLFetch(stmt);
03992       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03993          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03994          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03995          goto yuck;
03996       }
03997       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03998       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03999          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04000          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04001          goto yuck;
04002       }
04003       nummsgs = atoi(rowdata);
04004       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04005    } else
04006       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04007 
04008 yuck:
04009    if (obj)
04010       ast_odbc_release_obj(obj);
04011    return nummsgs;
04012 }
04013 
04014 static int has_voicemail(const char *mailbox, const char *folder)
04015 {
04016    char tmp[256], *tmp2 = tmp, *mbox, *context;
04017    ast_copy_string(tmp, mailbox, sizeof(tmp));
04018    while ((context = mbox = strsep(&tmp2, ","))) {
04019       strsep(&context, "@");
04020       if (ast_strlen_zero(context))
04021          context = "default";
04022       if (messagecount(context, mbox, folder))
04023          return 1;
04024    }
04025    return 0;
04026 }
04027 #endif
04028 #ifndef IMAP_STORAGE
04029 /* copy message only used by file storage */
04030 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)
04031 {
04032    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04033    const char *frombox = mbox(imbox);
04034    int recipmsgnum;
04035    int res = 0;
04036 
04037    ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04038 
04039    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04040    
04041    if (!dir)
04042       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04043    else
04044       ast_copy_string(fromdir, dir, sizeof(fromdir));
04045 
04046    make_file(frompath, sizeof(frompath), fromdir, msgnum);
04047 
04048    if (vm_lock_path(todir))
04049       return ERROR_LOCK_PATH;
04050 
04051    recipmsgnum = 0;
04052    do {
04053       make_file(topath, sizeof(topath), todir, recipmsgnum);
04054       if (!EXISTS(todir, recipmsgnum, topath, chan->language))
04055          break;
04056       recipmsgnum++;
04057    } while (recipmsgnum < recip->maxmsg);
04058    if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
04059 #ifndef ODBC_STORAGE
04060       if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
04061          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04062       } else {
04063 #endif
04064          /* If we are prepending a message for ODBC, then the message already
04065           * exists in the database, but we want to force copying from the
04066           * filesystem (since only the FS contains the prepend). */
04067          copy_plain_file(frompath, topath);
04068          STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL);
04069          vm_delete(topath);
04070 #ifndef ODBC_STORAGE
04071       }
04072 #endif
04073    } else {
04074       ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04075       res = -1;
04076    }
04077    ast_unlock_path(todir);
04078    notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04079    
04080    return res;
04081 }
04082 #endif
04083 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04084 static int messagecount(const char *context, const char *mailbox, const char *folder)
04085 {
04086    return __has_voicemail(context, mailbox, folder, 0);
04087 }
04088 
04089 
04090 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04091 {
04092    DIR *dir;
04093    struct dirent *de;
04094    char fn[256];
04095    int ret = 0;
04096    if (!folder)
04097       folder = "INBOX";
04098    /* If no mailbox, return immediately */
04099    if (ast_strlen_zero(mailbox))
04100       return 0;
04101    if (!context)
04102       context = "default";
04103    snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04104    dir = opendir(fn);
04105    if (!dir)
04106       return 0;
04107    while ((de = readdir(dir))) {
04108       if (!strncasecmp(de->d_name, "msg", 3)) {
04109          if (shortcircuit) {
04110             ret = 1;
04111             break;
04112          } else if (!strncasecmp(de->d_name + 8, "txt", 3))
04113             ret++;
04114       }
04115    }
04116    closedir(dir);
04117    return ret;
04118 }
04119 
04120 
04121 static int has_voicemail(const char *mailbox, const char *folder)
04122 {
04123    char tmp[256], *tmp2 = tmp, *mbox, *context;
04124    ast_copy_string(tmp, mailbox, sizeof(tmp));
04125    while ((mbox = strsep(&tmp2, ","))) {
04126       if ((context = strchr(mbox, '@')))
04127          *context++ = '\0';
04128       else
04129          context = "default";
04130       if (__has_voicemail(context, mbox, folder, 1))
04131          return 1;
04132    }
04133    return 0;
04134 }
04135 
04136 
04137 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04138 {
04139    char tmp[256];
04140    char *context;
04141 
04142    if (newmsgs)
04143       *newmsgs = 0;
04144    if (oldmsgs)
04145       *oldmsgs = 0;
04146    /* If no mailbox, return immediately */
04147    if (ast_strlen_zero(mailbox))
04148       return 0;
04149    if (strchr(mailbox, ',')) {
04150       int tmpnew, tmpold;
04151       char *mb, *cur;
04152 
04153       ast_copy_string(tmp, mailbox, sizeof(tmp));
04154       mb = tmp;
04155       while ((cur = strsep(&mb, ", "))) {
04156          if (!ast_strlen_zero(cur)) {
04157             if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04158                return -1;
04159             else {
04160                if (newmsgs)
04161                   *newmsgs += tmpnew; 
04162                if (oldmsgs)
04163                   *oldmsgs += tmpold;
04164             }
04165          }
04166       }
04167       return 0;
04168    }
04169    ast_copy_string(tmp, mailbox, sizeof(tmp));
04170    context = strchr(tmp, '@');
04171    if (context) {
04172       *context = '\0';
04173       context++;
04174    } else
04175       context = "default";
04176    if (newmsgs)
04177       *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04178    if (oldmsgs)
04179       *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04180    return 0;
04181 }
04182 
04183 #endif
04184 
04185 static void run_externnotify(char *context, char *extension)
04186 {
04187    char arguments[255];
04188    char ext_context[256] = "";
04189    int newvoicemails = 0, oldvoicemails = 0;
04190    struct ast_smdi_mwi_message *mwi_msg;
04191 
04192    if (!ast_strlen_zero(context))
04193       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04194    else
04195       ast_copy_string(ext_context, extension, sizeof(ext_context));
04196 
04197    if (!strcasecmp(externnotify, "smdi")) {
04198       if (ast_app_has_voicemail(ext_context, NULL)) 
04199          ast_smdi_mwi_set(smdi_iface, extension);
04200       else
04201          ast_smdi_mwi_unset(smdi_iface, extension);
04202 
04203       if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04204          ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04205          if (!strncmp(mwi_msg->cause, "INV", 3))
04206             ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04207          else if (!strncmp(mwi_msg->cause, "BLK", 3))
04208             ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04209          ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04210          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04211       } else {
04212          if (option_debug)
04213             ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
04214       }
04215    } else if (!ast_strlen_zero(externnotify)) {
04216       if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
04217          ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04218       } else {
04219          snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
04220          if (option_debug)
04221             ast_log(LOG_DEBUG, "Executing %s\n", arguments);
04222          ast_safe_system(arguments);
04223       }
04224    }
04225 }
04226 
04227 struct leave_vm_options {
04228    unsigned int flags;
04229    signed char record_gain;
04230 };
04231 
04232 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04233 {
04234 #ifdef IMAP_STORAGE
04235    int newmsgs, oldmsgs;
04236 #endif
04237    struct vm_state *vms = NULL;
04238    char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
04239    char callerid[256];
04240    FILE *txt;
04241    char date[50];
04242    int txtdes;
04243    int res = 0;
04244    int msgnum;
04245    int duration = 0;
04246    int ausemacro = 0;
04247    int ousemacro = 0;
04248    int ouseexten = 0;
04249    char dir[PATH_MAX], tmpdir[PATH_MAX];
04250    char dest[PATH_MAX];
04251    char fn[PATH_MAX];
04252    char prefile[PATH_MAX] = "";
04253    char tempfile[PATH_MAX] = "";
04254    char ext_context[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2] = "";
04255    char fmt[80];
04256    char *context;
04257    char ecodes[16] = "#";
04258    char tmp[1324] = "", *tmpptr;
04259    struct ast_vm_user *vmu;
04260    struct ast_vm_user svm;
04261    const char *category = NULL;
04262 
04263    if (strlen(ext) > sizeof(tmp) - 1) {
04264       ast_log(LOG_WARNING, "List of extensions is too long (>%ld).  Truncating.\n", (long) sizeof(tmp) - 1);
04265    }
04266    ast_copy_string(tmp, ext, sizeof(tmp));
04267    ext = tmp;
04268    context = strchr(tmp, '@');
04269    if (context) {
04270       *context++ = '\0';
04271       tmpptr = strchr(context, '&');
04272    } else {
04273       tmpptr = strchr(ext, '&');
04274    }
04275 
04276    if (tmpptr)
04277       *tmpptr++ = '\0';
04278 
04279    category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
04280 
04281    if (option_debug > 2)
04282       ast_log(LOG_DEBUG, "Before find_user\n");
04283    if (!(vmu = find_user(&svm, context, ext))) {
04284       ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
04285       if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
04286          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04287       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04288       return res;
04289    }
04290    /* Setup pre-file if appropriate */
04291    if (strcmp(vmu->context, "default"))
04292       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
04293    else
04294       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
04295    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
04296       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
04297       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
04298    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
04299       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
04300       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
04301    }
04302    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
04303    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
04304       ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
04305       return -1;
04306    }
04307    RETRIEVE(tempfile, -1, vmu);
04308    if (ast_fileexists(tempfile, NULL, NULL) > 0)
04309       ast_copy_string(prefile, tempfile, sizeof(prefile));
04310    DISPOSE(tempfile, -1);
04311    /* It's easier just to try to make it than to check for its existence */
04312 #ifndef IMAP_STORAGE
04313    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
04314 #else
04315    snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
04316    if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
04317       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
04318    }
04319 #endif
04320    create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
04321 
04322    /* Check current or macro-calling context for special extensions */
04323    if (ast_test_flag(vmu, VM_OPERATOR)) {
04324       if (!ast_strlen_zero(vmu->exit)) {
04325          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
04326             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04327             ouseexten = 1;
04328          }
04329       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
04330          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04331          ouseexten = 1;
04332       }
04333       else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
04334       strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04335       ousemacro = 1;
04336       }
04337    }
04338 
04339    if (!ast_strlen_zero(vmu->exit)) {
04340       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
04341          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04342    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
04343       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04344    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
04345       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04346       ausemacro = 1;
04347    }
04348 
04349    /* Play the beginning intro if desired */
04350    if (!ast_strlen_zero(prefile)) {
04351       res = play_greeting(chan, vmu, prefile, ecodes);
04352       if (res == -2) {
04353          /* The file did not exist */
04354          if (option_debug)
04355             ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
04356          res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
04357       }
04358       if (res < 0) {
04359          if (option_debug)
04360             ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
04361          free_user(vmu);
04362          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04363          return -1;
04364       }
04365    }
04366    if (res == '#') {
04367       /* On a '#' we skip the instructions */
04368       ast_set_flag(options, OPT_SILENT);
04369       res = 0;
04370    }
04371    if (!res && !ast_test_flag(options, OPT_SILENT)) {
04372       res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
04373       if (res == '#') {
04374          ast_set_flag(options, OPT_SILENT);
04375          res = 0;
04376       }
04377    }
04378    if (res > 0)
04379       ast_stopstream(chan);
04380    /* Check for a '*' here in case the caller wants to escape from voicemail to something
04381     other than the operator -- an automated attendant or mailbox login for example */
04382    if (res == '*') {
04383       chan->exten[0] = 'a';
04384       chan->exten[1] = '\0';
04385       if (!ast_strlen_zero(vmu->exit)) {
04386          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04387       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
04388          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04389       }
04390       chan->priority = 0;
04391       free_user(vmu);
04392       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04393       return 0;
04394    }
04395 
04396    /* Check for a '0' here */
04397    if (res == '0') {
04398    transfer:
04399       if (ouseexten || ousemacro) {
04400          chan->exten[0] = 'o';
04401          chan->exten[1] = '\0';
04402          if (!ast_strlen_zero(vmu->exit)) {
04403             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04404          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
04405             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04406          }
04407          ast_play_and_wait(chan, "transfer");
04408          chan->priority = 0;
04409          free_user(vmu);
04410          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04411       }
04412       return OPERATOR_EXIT;
04413    }
04414    if (res < 0) {
04415       free_user(vmu);
04416       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04417       return -1;
04418    }
04419    /* The meat of recording the message...  All the announcements and beeps have been played*/
04420    ast_copy_string(fmt, vmfmts, sizeof(fmt));
04421    if (!ast_strlen_zero(fmt)) {
04422       msgnum = 0;
04423 
04424 #ifdef IMAP_STORAGE
04425       /* Is ext a mailbox? */
04426       /* must open stream for this user to get info! */
04427       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
04428       if (res < 0) {
04429          ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
04430          return -1;
04431       }
04432       if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
04433       /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
04434        * rarely be used*/
04435          if (!(vms = create_vm_state_from_user(vmu))) {
04436             ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
04437             return -1;
04438          }
04439       }
04440       vms->newmessages++;
04441       /* here is a big difference! We add one to it later */
04442       msgnum = newmsgs + oldmsgs;
04443       if (option_debug > 2)
04444          ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
04445       snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
04446       /* set variable for compatability */
04447       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04448 
04449       if (imap_check_limits(chan, vms, vmu, msgnum)) {
04450          goto leave_vm_out;
04451       }
04452 #else
04453       if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
04454          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04455          if (!res)
04456             res = ast_waitstream(chan, "");
04457          ast_log(LOG_WARNING, "No more messages possible\n");
04458          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04459          inprocess_count(vmu->mailbox, vmu->context, -1);
04460          goto leave_vm_out;
04461       }
04462 
04463 #endif
04464       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
04465       txtdes = mkstemp(tmptxtfile);
04466       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
04467       if (txtdes < 0) {
04468          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04469          if (!res)
04470             res = ast_waitstream(chan, "");
04471          ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
04472          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04473          inprocess_count(vmu->mailbox, vmu->context, -1);
04474          goto leave_vm_out;
04475       }
04476 
04477       /* Now play the beep once we have the message number for our next message. */
04478       if (res >= 0) {
04479          /* Unless we're *really* silent, try to send the beep */
04480          res = ast_stream_and_wait(chan, "beep", chan->language, "");
04481       }
04482             
04483       /* Store information */
04484       txt = fdopen(txtdes, "w+");
04485       if (txt) {
04486          get_date(date, sizeof(date));
04487          fprintf(txt, 
04488             ";\n"
04489             "; Message Information file\n"
04490             ";\n"
04491             "[message]\n"
04492             "origmailbox=%s\n"
04493             "context=%s\n"
04494             "macrocontext=%s\n"
04495             "exten=%s\n"
04496             "priority=%d\n"
04497             "callerchan=%s\n"
04498             "callerid=%s\n"
04499             "origdate=%s\n"
04500             "origtime=%ld\n"
04501             "category=%s\n",
04502             ext,
04503             chan->context,
04504             chan->macrocontext, 
04505             chan->exten,
04506             chan->priority,
04507             chan->name,
04508             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
04509             date, (long)time(NULL),
04510             category ? category : ""); 
04511       } else
04512          ast_log(LOG_WARNING, "Error opening text file for output\n");
04513       res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
04514 
04515       if (txt) {
04516          if (duration < vmminmessage) {
04517             fclose(txt);
04518             if (option_verbose > 2) 
04519                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
04520             ast_filedelete(tmptxtfile, NULL);
04521             unlink(tmptxtfile);
04522             inprocess_count(vmu->mailbox, vmu->context, -1);
04523          } else {
04524             fprintf(txt, "duration=%d\n", duration);
04525             fclose(txt);
04526             if (vm_lock_path(dir)) {
04527                ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
04528                /* Delete files */
04529                ast_filedelete(tmptxtfile, NULL);
04530                unlink(tmptxtfile);
04531                inprocess_count(vmu->mailbox, vmu->context, -1);
04532             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
04533                if (option_debug) 
04534                   ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
04535                unlink(tmptxtfile);
04536                ast_unlock_path(dir);
04537                inprocess_count(vmu->mailbox, vmu->context, -1);
04538             } else {
04539 #ifndef IMAP_STORAGE
04540                msgnum = last_message_index(vmu, dir) + 1;
04541 #endif
04542                make_file(fn, sizeof(fn), dir, msgnum);
04543 
04544                /* assign a variable with the name of the voicemail file */ 
04545 #ifndef IMAP_STORAGE
04546                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
04547 #else
04548                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04549 #endif
04550 
04551                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
04552                ast_filerename(tmptxtfile, fn, NULL);
04553                rename(tmptxtfile, txtfile);
04554                inprocess_count(vmu->mailbox, vmu->context, -1);
04555 
04556                ast_unlock_path(dir);
04557                /* We must store the file first, before copying the message, because
04558                 * ODBC storage does the entire copy with SQL.
04559                 */
04560                if (ast_fileexists(fn, NULL, NULL) > 0) {
04561                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
04562                }
04563 
04564                /* Are there to be more recipients of this message? */
04565                while (tmpptr) {
04566                   struct ast_vm_user recipu, *recip;
04567                   char *exten, *context;
04568                
04569                   exten = strsep(&tmpptr, "&");
04570                   context = strchr(exten, '@');
04571                   if (context) {
04572                      *context = '\0';
04573                      context++;
04574                   }
04575                   if ((recip = find_user(&recipu, context, exten))) {
04576                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
04577                      free_user(recip);
04578                   }
04579                }
04580                /* Notification and disposal needs to happen after the copy, though. */
04581                if (ast_fileexists(fn, NULL, NULL)) {
04582                   notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04583                   DISPOSE(dir, msgnum);
04584                }
04585             }
04586          }
04587       } else {
04588          inprocess_count(vmu->mailbox, vmu->context, -1);
04589       }
04590       if (res == '0') {
04591          goto transfer;
04592       } else if (res > 0 && res != 't')
04593          res = 0;
04594 
04595       if (duration < vmminmessage)
04596          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
04597          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04598       else
04599          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
04600    } else
04601       ast_log(LOG_WARNING, "No format for saving voicemail?\n");
04602 leave_vm_out:
04603    free_user(vmu);
04604    
04605    return res;
04606 }
04607 
04608 #if !defined(IMAP_STORAGE)
04609 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
04610 {
04611    /* we know the actual number of messages, so stop process when number is hit */
04612 
04613    int x,dest;
04614    char sfn[PATH_MAX];
04615    char dfn[PATH_MAX];
04616 
04617    if (vm_lock_path(dir))
04618       return ERROR_LOCK_PATH;
04619 
04620    for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
04621       make_file(sfn, sizeof(sfn), dir, x);
04622       if (EXISTS(dir, x, sfn, NULL)) {
04623          
04624          if (x != dest) {
04625             make_file(dfn, sizeof(dfn), dir, dest);
04626             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
04627          }
04628          
04629          dest++;
04630       }
04631    }
04632    ast_unlock_path(dir);
04633 
04634    return dest;
04635 }
04636 #endif
04637 
04638 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
04639 {
04640    int d;
04641    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
04642    return d;
04643 }
04644 
04645 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
04646 {
04647 #ifdef IMAP_STORAGE
04648    /* we must use mbox(x) folder names, and copy the message there */
04649    /* simple. huh? */
04650    char sequence[10];
04651    /* get the real IMAP message number for this message */
04652    snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
04653    if (option_debug > 2)
04654       ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
04655    ast_mutex_lock(&vms->lock);
04656    if (box == 1) {
04657       mail_setflag(vms->mailstream, sequence, "\\Seen");
04658    } else if (box == 0) {
04659       mail_clearflag(vms->mailstream, sequence, "\\Seen");
04660    }
04661    if (!strcasecmp(mbox(0), vms->curbox) && (box == 0 || box == 1)) {
04662       ast_mutex_unlock(&vms->lock);
04663       return 0;
04664    } else {
04665       int res = !mail_copy(vms->mailstream,sequence,(char *) mbox(box)); 
04666       ast_mutex_unlock(&vms->lock);
04667       return res;
04668    }
04669 #else
04670    char *dir = vms->curdir;
04671    char *username = vms->username;
04672    char *context = vmu->context;
04673    char sfn[PATH_MAX];
04674    char dfn[PATH_MAX];
04675    char ddir[PATH_MAX];
04676    const char *dbox = mbox(box);
04677    int x;
04678    make_file(sfn, sizeof(sfn), dir, msg);
04679    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
04680 
04681    if (vm_lock_path(ddir))
04682       return ERROR_LOCK_PATH;
04683 
04684    for (x = 0; x < vmu->maxmsg; x++) {
04685       make_file(dfn, sizeof(dfn), ddir, x);
04686       if (!EXISTS(ddir, x, dfn, NULL))
04687          break;
04688    }
04689    if (x >= vmu->maxmsg) {
04690       ast_unlock_path(ddir);
04691       return ERROR_MAILBOX_FULL;
04692    }
04693    if (strcmp(sfn, dfn)) {
04694       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
04695    }
04696    ast_unlock_path(ddir);
04697 #endif
04698    return 0;
04699 }
04700 
04701 static int adsi_logo(unsigned char *buf)
04702 {
04703    int bytes = 0;
04704    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
04705    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
04706    return bytes;
04707 }
04708 
04709 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
04710 {
04711    unsigned char buf[256];
04712    int bytes=0;
04713    int x;
04714    char num[5];
04715 
04716    *useadsi = 0;
04717    bytes += ast_adsi_data_mode(buf + bytes);
04718    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04719 
04720    bytes = 0;
04721    bytes += adsi_logo(buf);
04722    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04723 #ifdef DISPLAY
04724    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
04725 #endif
04726    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04727    bytes += ast_adsi_data_mode(buf + bytes);
04728    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04729 
04730    if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
04731       bytes = 0;
04732       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
04733       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04734       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04735       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04736       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04737       return 0;
04738    }
04739 
04740 #ifdef DISPLAY
04741    /* Add a dot */
04742    bytes = 0;
04743    bytes += ast_adsi_logo(buf);
04744    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04745    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
04746    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04747    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04748 #endif
04749    bytes = 0;
04750    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
04751    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
04752    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
04753    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
04754    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
04755    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
04756    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04757 
04758 #ifdef DISPLAY
04759    /* Add another dot */
04760    bytes = 0;
04761    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
04762    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04763 
04764    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04765    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04766 #endif
04767 
04768    bytes = 0;
04769    /* These buttons we load but don't use yet */
04770    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
04771    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
04772    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
04773    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
04774    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
04775    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
04776    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04777 
04778 #ifdef DISPLAY
04779    /* Add another dot */
04780    bytes = 0;
04781    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
04782    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04783    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04784 #endif
04785 
04786    bytes = 0;
04787    for (x=0;x<5;x++) {
04788       snprintf(num, sizeof(num), "%d", x);
04789       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
04790    }
04791    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
04792    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04793 
04794 #ifdef DISPLAY
04795    /* Add another dot */
04796    bytes = 0;
04797    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
04798    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04799    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04800 #endif
04801 
04802    if (ast_adsi_end_download(chan)) {
04803       bytes = 0;
04804       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
04805       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04806       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04807       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04808       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04809       return 0;
04810    }
04811    bytes = 0;
04812    bytes += ast_adsi_download_disconnect(buf + bytes);
04813    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04814    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04815 
04816    if (option_debug)
04817       ast_log(LOG_DEBUG, "Done downloading scripts...\n");
04818 
04819 #ifdef DISPLAY
04820    /* Add last dot */
04821    bytes = 0;
04822    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
04823    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04824 #endif
04825    if (option_debug)
04826       ast_log(LOG_DEBUG, "Restarting session...\n");
04827 
04828    bytes = 0;
04829    /* Load the session now */
04830    if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
04831       *useadsi = 1;
04832       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
04833    } else
04834       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
04835 
04836    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04837    return 0;
04838 }
04839 
04840 static void adsi_begin(struct ast_channel *chan, int *useadsi)
04841 {
04842    int x;
04843    if (!ast_adsi_available(chan))
04844       return;
04845    x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
04846    if (x < 0)
04847       return;
04848    if (!x) {
04849       if (adsi_load_vmail(chan, useadsi)) {
04850          ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
04851          return;
04852       }
04853    } else
04854       *useadsi = 1;
04855 }
04856 
04857 static void adsi_login(struct ast_channel *chan)
04858 {
04859    unsigned char buf[256];
04860    int bytes=0;
04861    unsigned char keys[8];
04862    int x;
04863    if (!ast_adsi_available(chan))
04864       return;
04865 
04866    for (x=0;x<8;x++)
04867       keys[x] = 0;
04868    /* Set one key for next */
04869    keys[3] = ADSI_KEY_APPS + 3;
04870 
04871    bytes += adsi_logo(buf + bytes);
04872    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
04873    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
04874    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04875    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
04876    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
04877    bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
04878    bytes += ast_adsi_set_keys(buf + bytes, keys);
04879    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04880    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04881 }
04882 
04883 static void adsi_password(struct ast_channel *chan)
04884 {
04885    unsigned char buf[256];
04886    int bytes=0;
04887    unsigned char keys[8];
04888    int x;
04889    if (!ast_adsi_available(chan))
04890       return;
04891 
04892    for (x=0;x<8;x++)
04893       keys[x] = 0;
04894    /* Set one key for next */
04895    keys[3] = ADSI_KEY_APPS + 3;
04896 
04897    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04898    bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
04899    bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
04900    bytes += ast_adsi_set_keys(buf + bytes, keys);
04901    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04902    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04903 }
04904 
04905 static void adsi_folders(struct ast_channel *chan, int start, char *label)
04906 {
04907    unsigned char buf[256];
04908    int bytes=0;
04909    unsigned char keys[8];
04910    int x,y;
04911 
04912    if (!ast_adsi_available(chan))
04913       return;
04914 
04915    for (x=0;x<5;x++) {
04916       y = ADSI_KEY_APPS + 12 + start + x;
04917       if (y > ADSI_KEY_APPS + 12 + 4)
04918          y = 0;
04919       keys[x] = ADSI_KEY_SKT | y;
04920    }
04921    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
04922    keys[6] = 0;
04923    keys[7] = 0;
04924 
04925    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
04926    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
04927    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04928    bytes += ast_adsi_set_keys(buf + bytes, keys);
04929    bytes += ast_adsi_voice_mode(buf + bytes, 0);
04930 
04931    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04932 }
04933 
04934 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
04935 {
04936    int bytes=0;
04937    unsigned char buf[256]; 
04938    char buf1[256], buf2[256];
04939    char fn2[PATH_MAX];
04940 
04941    char cid[256]="";
04942    char *val;
04943    char *name, *num;
04944    char datetime[21]="";
04945    FILE *f;
04946 
04947    unsigned char keys[8];
04948 
04949    int x;
04950 
04951    if (!ast_adsi_available(chan))
04952       return;
04953 
04954    /* Retrieve important info */
04955    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
04956    f = fopen(fn2, "r");
04957    if (f) {
04958       while (!feof(f)) {   
04959          if (!fgets((char *)buf, sizeof(buf), f)) {
04960             continue;
04961          }
04962          if (!feof(f)) {
04963             char *stringp=NULL;
04964             stringp = (char *)buf;
04965             strsep(&stringp, "=");
04966             val = strsep(&stringp, "=");
04967             if (!ast_strlen_zero(val)) {
04968                if (!strcmp((char *)buf, "callerid"))
04969                   ast_copy_string(cid, val, sizeof(cid));
04970                if (!strcmp((char *)buf, "origdate"))
04971                   ast_copy_string(datetime, val, sizeof(datetime));
04972             }
04973          }
04974       }
04975       fclose(f);
04976    }
04977    /* New meaning for keys */
04978    for (x=0;x<5;x++)
04979       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
04980    keys[6] = 0x0;
04981    keys[7] = 0x0;
04982 
04983    if (!vms->curmsg) {
04984       /* No prev key, provide "Folder" instead */
04985       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
04986    }
04987    if (vms->curmsg >= vms->lastmsg) {
04988       /* If last message ... */
04989       if (vms->curmsg) {
04990          /* but not only message, provide "Folder" instead */
04991          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
04992          bytes += ast_adsi_voice_mode(buf + bytes, 0);
04993 
04994       } else {
04995          /* Otherwise if only message, leave blank */
04996          keys[3] = 1;
04997       }
04998    }
04999 
05000    if (!ast_strlen_zero(cid)) {
05001       ast_callerid_parse(cid, &name, &num);
05002       if (!name)
05003          name = num;
05004    } else
05005       name = "Unknown Caller";
05006 
05007    /* If deleted, show "undeleted" */
05008 
05009    if (vms->deleted[vms->curmsg])
05010       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05011 
05012    /* Except "Exit" */
05013    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05014    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05015       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05016    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05017 
05018    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05019    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05020    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05021    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05022    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05023    bytes += ast_adsi_set_keys(buf + bytes, keys);
05024    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05025 
05026    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05027 }
05028 
05029 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05030 {
05031    int bytes=0;
05032    unsigned char buf[256];
05033    unsigned char keys[8];
05034 
05035    int x;
05036 
05037    if (!ast_adsi_available(chan))
05038       return;
05039 
05040    /* New meaning for keys */
05041    for (x=0;x<5;x++)
05042       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05043 
05044    keys[6] = 0x0;
05045    keys[7] = 0x0;
05046 
05047    if (!vms->curmsg) {
05048       /* No prev key, provide "Folder" instead */
05049       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05050    }
05051    if (vms->curmsg >= vms->lastmsg) {
05052       /* If last message ... */
05053       if (vms->curmsg) {
05054          /* but not only message, provide "Folder" instead */
05055          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05056       } else {
05057          /* Otherwise if only message, leave blank */
05058          keys[3] = 1;
05059       }
05060    }
05061 
05062    /* If deleted, show "undeleted" */
05063    if (vms->deleted[vms->curmsg]) 
05064       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05065 
05066    /* Except "Exit" */
05067    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05068    bytes += ast_adsi_set_keys(buf + bytes, keys);
05069    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05070 
05071    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05072 }
05073 
05074 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05075 {
05076    unsigned char buf[256] = "";
05077    char buf1[256] = "", buf2[256] = "";
05078    int bytes=0;
05079    unsigned char keys[8];
05080    int x;
05081 
05082    char *newm = (vms->newmessages == 1) ? "message" : "messages";
05083    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05084    if (!ast_adsi_available(chan))
05085       return;
05086    if (vms->newmessages) {
05087       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05088       if (vms->oldmessages) {
05089          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05090          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05091       } else {
05092          snprintf(buf2, sizeof(buf2), "%s.", newm);
05093       }
05094    } else if (vms->oldmessages) {
05095       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05096       snprintf(buf2, sizeof(buf2), "%s.", oldm);
05097    } else {
05098       strcpy(buf1, "You have no messages.");
05099       buf2[0] = ' ';
05100       buf2[1] = '\0';
05101    }
05102    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05103    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05104    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05105 
05106    for (x=0;x<6;x++)
05107       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05108    keys[6] = 0;
05109    keys[7] = 0;
05110 
05111    /* Don't let them listen if there are none */
05112    if (vms->lastmsg < 0)
05113       keys[0] = 1;
05114    bytes += ast_adsi_set_keys(buf + bytes, keys);
05115 
05116    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05117 
05118    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05119 }
05120 
05121 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05122 {
05123    unsigned char buf[256] = "";
05124    char buf1[256] = "", buf2[256] = "";
05125    int bytes=0;
05126    unsigned char keys[8];
05127    int x;
05128 
05129    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05130 
05131    if (!ast_adsi_available(chan))
05132       return;
05133 
05134    /* Original command keys */
05135    for (x=0;x<6;x++)
05136       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05137 
05138    keys[6] = 0;
05139    keys[7] = 0;
05140 
05141    if ((vms->lastmsg + 1) < 1)
05142       keys[0] = 0;
05143 
05144    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05145       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05146 
05147    if (vms->lastmsg + 1)
05148       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05149    else
05150       strcpy(buf2, "no messages.");
05151    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05152    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05153    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05154    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 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 
05163 /*
05164 static void adsi_clear(struct ast_channel *chan)
05165 {
05166    char buf[256];
05167    int bytes=0;
05168    if (!ast_adsi_available(chan))
05169       return;
05170    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05171    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05172 
05173    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05174 }
05175 */
05176 
05177 static void adsi_goodbye(struct ast_channel *chan)
05178 {
05179    unsigned char buf[256];
05180    int bytes=0;
05181 
05182    if (!ast_adsi_available(chan))
05183       return;
05184    bytes += adsi_logo(buf + bytes);
05185    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
05186    bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
05187    bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05188    bytes += ast_adsi_voice_mode(buf + bytes, 0);
05189 
05190    ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05191 }
05192 
05193 /*--- get_folder: Folder menu ---*/
05194 /* Plays "press 1 for INBOX messages" etc
05195    Should possibly be internationalized
05196  */
05197 static int get_folder(struct ast_channel *chan, int start)
05198 {
05199    int x;
05200    int d;
05201    char fn[PATH_MAX];
05202    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
05203    if (d)
05204       return d;
05205    for (x = start; x< 5; x++) {  /* For all folders */
05206       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
05207          return d;
05208       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
05209       if (d)
05210          return d;
05211       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
05212       d = vm_play_folder_name(chan, fn);
05213       if (d)
05214          return d;
05215       d = ast_waitfordigit(chan, 500);
05216       if (d)
05217          return d;
05218    }
05219    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
05220    if (d)
05221       return d;
05222    d = ast_waitfordigit(chan, 4000);
05223    return d;
05224 }
05225 
05226 static int get_folder2(struct ast_channel *chan, char *fn, int start)
05227 {
05228    int res = 0;
05229    int loops = 0;
05230    res = ast_play_and_wait(chan, fn);  /* Folder name */
05231    while (((res < '0') || (res > '9')) &&
05232          (res != '#') && (res >= 0) &&
05233          loops < 4) {
05234       res = get_folder(chan, 0);
05235       loops++;
05236    }
05237    if (loops == 4) { /* give up */
05238       return '#';
05239    }
05240    return res;
05241 }
05242 
05243 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
05244               char *context, signed char record_gain, long *duration, struct vm_state *vms)
05245 {
05246    int cmd = 0;
05247    int retries = 0, prepend_duration = 0, already_recorded = 0;
05248    signed char zero_gain = 0;
05249    struct ast_config *msg_cfg;
05250    const char *duration_cstr;
05251    char msgfile[PATH_MAX], backup[PATH_MAX];
05252    char textfile[PATH_MAX];
05253    char backup_textfile[PATH_MAX];
05254    struct ast_category *msg_cat;
05255    char duration_str[12] = "";
05256 
05257    ast_log(LOG_NOTICE, "curdir=%s\n", curdir);
05258    /* Must always populate duration correctly */
05259    make_file(msgfile, sizeof(msgfile), curdir, curmsg);
05260    strcpy(textfile, msgfile);
05261    strcpy(backup, msgfile);
05262    strcpy(backup_textfile, msgfile);
05263    strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05264    strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05265    strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05266 
05267    if (!(msg_cfg = ast_config_load(textfile))) {
05268       return -1;
05269    }
05270 
05271    *duration = 0;
05272    if ((duration_cstr = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
05273       *duration = atoi(duration_cstr);
05274    }
05275 
05276    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
05277       if (cmd)
05278          retries = 0;
05279       switch (cmd) {
05280       case '1': 
05281          /* prepend a message to the current message, update the metadata and return */
05282       {
05283          prepend_duration = 0;
05284 
05285          /* Back up the original file, so we can retry the prepend */
05286 #ifndef IMAP_STORAGE
05287          if (already_recorded) {
05288             ast_filecopy(backup, msgfile, NULL);
05289             copy(textfile, backup_textfile);
05290          } else {
05291             ast_filecopy(msgfile, backup, NULL);
05292             copy(textfile, backup_textfile);
05293          }
05294 #endif
05295          already_recorded = 1;
05296 
05297          if (record_gain)
05298             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
05299 
05300          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
05301          if (cmd == 'S') {
05302             ast_filerename(backup, msgfile, NULL);
05303          }
05304 
05305          if (record_gain)
05306             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
05307 
05308          if (prepend_duration) {
05309             prepend_duration += *duration;
05310          }
05311 
05312          break;
05313       }
05314       case '2': 
05315          cmd = 't';
05316          break;
05317       case '*':
05318          cmd = '*';
05319          break;
05320       default: 
05321          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
05322             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
05323          if (!cmd)
05324             cmd = ast_play_and_wait(chan,"vm-starmain");
05325             /* "press star to return to the main menu" */
05326          if (!cmd)
05327             cmd = ast_waitfordigit(chan,6000);
05328          if (!cmd)
05329             retries++;
05330          if (retries > 3)
05331             cmd = 't';
05332       }
05333    }
05334 
05335    if (already_recorded && cmd == -1) {
05336       /* Restore original files, if operation cancelled */
05337       ast_filerename(backup, msgfile, NULL);
05338       if (duration_cstr) {
05339          ast_copy_string(duration_str, duration_cstr, sizeof(duration_str));
05340       }
05341    } else if (prepend_duration) {
05342       *duration = prepend_duration;
05343       snprintf(duration_str, sizeof(duration_str), "%d", prepend_duration);
05344    }
05345 
05346    msg_cat = ast_category_get(msg_cfg, "message");
05347    if (!ast_strlen_zero(duration_str) && !ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
05348       config_text_file_save(textfile, msg_cfg, "app_voicemail");
05349    }
05350    ast_config_destroy(msg_cfg);
05351 
05352    if (cmd == 't' || cmd == 'S')
05353       cmd = 0;
05354    return cmd;
05355 }
05356 
05357 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
05358 {
05359    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
05360    int newmsgs = 0, oldmsgs = 0;
05361    const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
05362 
05363 #ifndef IMAP_STORAGE
05364    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
05365 #else
05366    snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
05367 #endif
05368    make_file(fn, sizeof(fn), todir, msgnum);
05369    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
05370 
05371    if (!ast_strlen_zero(vmu->attachfmt)) {
05372       if (strstr(fmt, vmu->attachfmt)) {
05373          fmt = vmu->attachfmt;
05374       } else {
05375          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);
05376       }
05377    }
05378 
05379    /* Attach only the first format */
05380    fmt = ast_strdupa(fmt);
05381    stringp = fmt;
05382    strsep(&stringp, "|");
05383 
05384    if (!ast_strlen_zero(vmu->email)) {
05385       int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
05386       char *myserveremail = serveremail;
05387       attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
05388       if (!ast_strlen_zero(vmu->serveremail))
05389          myserveremail = vmu->serveremail;
05390       
05391       if (attach_user_voicemail)
05392          RETRIEVE(todir, msgnum, vmu);
05393 
05394       /*XXX possible imap issue, should category be NULL XXX*/
05395       sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
05396 
05397       if (attach_user_voicemail)
05398          DISPOSE(todir, msgnum);
05399    }
05400 
05401    if (!ast_strlen_zero(vmu->pager)) {
05402       char *myserveremail = serveremail;
05403       if (!ast_strlen_zero(vmu->serveremail))
05404          myserveremail = vmu->serveremail;
05405       sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category);
05406    }
05407 
05408    if (ast_test_flag(vmu, VM_DELETE)) {
05409       DELETE(todir, msgnum, fn, vmu);
05410    }
05411 
05412    /* Leave voicemail for someone */
05413    if (ast_app_has_voicemail(ext_context, NULL)) {
05414       ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
05415    }
05416    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);
05417    run_externnotify(vmu->context, vmu->mailbox);
05418    return 0;
05419 }
05420 
05421 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)
05422 {
05423 #ifdef IMAP_STORAGE
05424    int todircount=0;
05425    struct vm_state *dstvms;
05426 #else
05427    char textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
05428 #endif
05429    char username[70]="";
05430    int res = 0, cmd = 0;
05431    struct ast_vm_user *receiver = NULL, *vmtmp;
05432    AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
05433    char *stringp;
05434    const char *s;
05435    int saved_messages = 0;
05436    int valid_extensions = 0;
05437    char *dir;
05438    int curmsg;
05439    int prompt_played = 0;
05440 
05441    if (vms == NULL) return -1;
05442    dir = vms->curdir;
05443    curmsg = vms->curmsg;
05444    
05445    while (!res && !valid_extensions) {
05446       int use_directory = 0;
05447       if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
05448          int done = 0;
05449          int retries = 0;
05450          cmd=0;
05451          while ((cmd >= 0) && !done ){
05452             if (cmd)
05453                retries = 0;
05454             switch (cmd) {
05455             case '1': 
05456                use_directory = 0;
05457                done = 1;
05458                break;
05459             case '2': 
05460                use_directory = 1;
05461                done=1;
05462                break;
05463             case '*': 
05464                cmd = 't';
05465                done = 1;
05466                break;
05467             default: 
05468                /* Press 1 to enter an extension press 2 to use the directory */
05469                cmd = ast_play_and_wait(chan,"vm-forward");
05470                if (!cmd)
05471                   cmd = ast_waitfordigit(chan,3000);
05472                if (!cmd)
05473                   retries++;
05474                if (retries > 3)
05475                {
05476                   cmd = 't';
05477                   done = 1;
05478                }
05479                
05480             }
05481          }
05482          if (cmd < 0 || cmd == 't')
05483             break;
05484       }
05485       
05486       if (use_directory) {
05487          /* use app_directory */
05488          
05489          char old_context[sizeof(chan->context)];
05490          char old_exten[sizeof(chan->exten)];
05491          int old_priority;
05492          struct ast_app* app;
05493 
05494          
05495          app = pbx_findapp("Directory");
05496          if (app) {
05497             char vmcontext[256];
05498             /* make backup copies */
05499             memcpy(old_context, chan->context, sizeof(chan->context));
05500             memcpy(old_exten, chan->exten, sizeof(chan->exten));
05501             old_priority = chan->priority;
05502             
05503             /* call the the Directory, changes the channel */
05504             snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
05505             res = pbx_exec(chan, app, vmcontext);
05506             
05507             ast_copy_string(username, chan->exten, sizeof(username));
05508             
05509             /* restore the old context, exten, and priority */
05510             memcpy(chan->context, old_context, sizeof(chan->context));
05511             memcpy(chan->exten, old_exten, sizeof(chan->exten));
05512             chan->priority = old_priority;
05513             
05514          } else {
05515             ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
05516             ast_clear_flag((&globalflags), VM_DIRECFORWARD);   
05517          }
05518       } else {
05519          /* Ask for an extension */
05520          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
05521          prompt_played++;
05522          if (res || prompt_played > 4)
05523             break;
05524          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
05525             break;
05526       }
05527       
05528       /* start all over if no username */
05529       if (ast_strlen_zero(username))
05530          continue;
05531       stringp = username;
05532       s = strsep(&stringp, "*");
05533       /* start optimistic */
05534       valid_extensions = 1;
05535       while (s) {
05536          /* 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 */
05537          if ((is_new_message == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
05538             int oldmsgs;
05539             int newmsgs;
05540             int capacity;
05541             if (inboxcount(s, &newmsgs, &oldmsgs)) {
05542                ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
05543                /* Shouldn't happen, but allow trying another extension if it does */
05544                res = ast_play_and_wait(chan, "pbx-invalid");
05545                valid_extensions = 0;
05546                break;
05547             }
05548             capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
05549             if ((newmsgs + oldmsgs) >= capacity) {
05550                ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
05551                res = ast_play_and_wait(chan, "vm-mailboxfull");
05552                valid_extensions = 0;
05553                while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05554                   inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05555                   free_user(vmtmp);
05556                }
05557                inprocess_count(receiver->mailbox, receiver->context, -1);
05558                break;
05559             }
05560             AST_LIST_INSERT_HEAD(&extensions, receiver, list);
05561          } else {
05562             ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
05563             /* "I am sorry, that's not a valid extension.  Please try again." */
05564             res = ast_play_and_wait(chan, "pbx-invalid");
05565             valid_extensions = 0;
05566             break;
05567          }
05568          s = strsep(&stringp, "*");
05569       }
05570       /* break from the loop of reading the extensions */
05571       if (valid_extensions)
05572          break;
05573    }
05574    /* check if we're clear to proceed */
05575    if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
05576       return res;
05577    if (is_new_message == 1) {
05578       struct leave_vm_options leave_options;
05579       char mailbox[AST_MAX_EXTENSION * 2 + 2];
05580       /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
05581       if (context)
05582          snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
05583       else
05584          ast_copy_string(mailbox, username, sizeof(mailbox));
05585 
05586       /* Send VoiceMail */
05587       memset(&leave_options, 0, sizeof(leave_options));
05588       leave_options.record_gain = record_gain;
05589       cmd = leave_voicemail(chan, mailbox, &leave_options);
05590    } else {
05591       /* Forward VoiceMail */
05592       long duration = 0;
05593       struct vm_state vmstmp;
05594 #ifndef IMAP_STORAGE
05595       char msgfile[PATH_MAX];
05596 #endif
05597       int copy_msg_result = 0;
05598 
05599       memcpy(&vmstmp, vms, sizeof(vmstmp));
05600 
05601       RETRIEVE(dir, curmsg, sender);
05602 
05603       cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
05604       if (!cmd) {
05605          AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
05606 #ifdef IMAP_STORAGE
05607             char *myserveremail;
05608             int attach_user_voicemail;
05609             /* get destination mailbox */
05610             dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
05611             if (!dstvms) {
05612                dstvms = create_vm_state_from_user(vmtmp);
05613             }
05614             if (dstvms) {
05615                init_mailstream(dstvms, 0);
05616                if (!dstvms->mailstream) {
05617                   ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
05618                } else {
05619                   copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
05620                   run_externnotify(vmtmp->context, vmtmp->mailbox); 
05621                }
05622             } else {
05623                ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
05624             }
05625             myserveremail = serveremail;
05626             if (!ast_strlen_zero(vmtmp->serveremail))
05627                myserveremail = vmtmp->serveremail;
05628             attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
05629             /* NULL category for IMAP storage */
05630             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);
05631 #else
05632             copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
05633 #endif
05634             saved_messages++;
05635             inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05636             AST_LIST_REMOVE_CURRENT(&extensions, list);
05637             free_user(vmtmp);
05638             if (res)
05639                break;
05640          }
05641          AST_LIST_TRAVERSE_SAFE_END;
05642          if (saved_messages > 0 && !copy_msg_result) {
05643             /* give confirmation that the message was saved */
05644             /* commented out since we can't forward batches yet
05645             if (saved_messages == 1)
05646                res = ast_play_and_wait(chan, "vm-message");
05647             else
05648                res = ast_play_and_wait(chan, "vm-messages");
05649             if (!res)
05650                res = ast_play_and_wait(chan, "vm-saved"); */
05651             res = ast_play_and_wait(chan, "vm-msgsaved");
05652          }
05653 #ifndef IMAP_STORAGE
05654          else {
05655             /* with IMAP, mailbox full warning played by imap_check_limits */
05656             res = ast_play_and_wait(chan, "vm-mailboxfull");
05657          }
05658          /* Restore original message without prepended message if backup exists */
05659          make_file(msgfile, sizeof(msgfile), dir, curmsg);
05660          strcpy(textfile, msgfile);
05661          strcpy(backup, msgfile);
05662          strcpy(backup_textfile, msgfile);
05663          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05664          strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05665          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05666          if (ast_fileexists(backup, NULL, NULL) > 0) {
05667             ast_filerename(backup, msgfile, NULL);
05668             rename(backup_textfile, textfile);
05669          }
05670 #endif
05671       }
05672       DISPOSE(dir, curmsg);
05673 #ifndef IMAP_STORAGE
05674       if (cmd) { /* assuming hangup, cleanup backup file */
05675          make_file(msgfile, sizeof(msgfile), dir, curmsg);
05676          strcpy(textfile, msgfile);
05677          strcpy(backup_textfile, msgfile);
05678          strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05679          strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
05680          rename(backup_textfile, textfile);
05681       }
05682 #endif
05683    }
05684 
05685    /* If anything failed above, we still have this list to free */
05686    while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05687       inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
05688       free_user(vmtmp);
05689    }
05690    return res ? res : cmd;
05691 }
05692 
05693 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
05694 {
05695    int res;
05696    if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0) 
05697       ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
05698    return res;
05699 }
05700 
05701 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
05702 {
05703    return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
05704 }
05705 
05706 static int play_message_category(struct ast_channel *chan, const char *category)
05707 {
05708    int res = 0;
05709 
05710    if (!ast_strlen_zero(category))
05711       res = ast_play_and_wait(chan, category);
05712 
05713    if (res) {
05714       ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
05715       res = 0;
05716    }
05717 
05718    return res;
05719 }
05720 
05721 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
05722 {
05723    int res = 0;
05724    struct vm_zone *the_zone = NULL;
05725    time_t t;
05726 
05727    if (ast_get_time_t(origtime, &t, 0, NULL)) {
05728       ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
05729       return 0;
05730    }
05731 
05732    /* Does this user have a timezone specified? */
05733    if (!ast_strlen_zero(vmu->zonetag)) {
05734       /* Find the zone in the list */
05735       struct vm_zone *z;
05736       AST_LIST_LOCK(&zones);
05737       AST_LIST_TRAVERSE(&zones, z, list) {
05738          if (!strcmp(z->name, vmu->zonetag)) {
05739             the_zone = z;
05740             break;
05741          }
05742       }
05743       AST_LIST_UNLOCK(&zones);
05744    }
05745 
05746 /* No internal variable parsing for now, so we'll comment it out for the time being */
05747 #if 0
05748    /* Set the DIFF_* variables */
05749    ast_localtime(&t, &time_now, NULL);
05750    tv_now = ast_tvnow();
05751    tnow = tv_now.tv_sec;
05752    ast_localtime(&tnow, &time_then, NULL);
05753 
05754    /* Day difference */
05755    if (time_now.tm_year == time_then.tm_year)
05756       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
05757    else
05758       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
05759    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
05760 
05761    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
05762 #endif
05763    if (the_zone) {
05764       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
05765    } else if (!strncasecmp(chan->language, "de", 2)) {    /* GERMAN syntax */
05766       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05767    } else if (!strncasecmp(chan->language, "gr", 2)) {    /* GREEK syntax */
05768       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
05769    } else if (!strncasecmp(chan->language, "he", 2)) {    /* HEBREW syntax */
05770       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'at2' kM", NULL);
05771    } else if (!strncasecmp(chan->language, "it", 2)) {    /* ITALIAN syntax */
05772       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);
05773    } else if (!strncasecmp(chan->language, "nl", 2)) {    /* DUTCH syntax */
05774       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
05775    } else if (!strncasecmp(chan->language, "no", 2)) {    /* NORWEGIAN syntax */
05776       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05777    } else if (!strncasecmp(chan->language, "pl", 2)) {    /* POLISH syntax */
05778       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
05779    } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* PORTUGUESE syntax */
05780       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);
05781    } else if (!strncasecmp(chan->language, "se", 2)) {    /* SWEDISH syntax */
05782       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
05783    } else {
05784       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
05785    }
05786 #if 0
05787    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
05788 #endif
05789    return res;
05790 }
05791 
05792 
05793 
05794 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
05795 {
05796    int res = 0;
05797    int i;
05798    char *callerid, *name;
05799    char prefile[PATH_MAX] = "";
05800    
05801 
05802    /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
05803    /* 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 */
05804    if ((cid == NULL)||(context == NULL))
05805       return res;
05806 
05807    /* Strip off caller ID number from name */
05808    if (option_debug > 2)
05809       ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
05810    ast_callerid_parse(cid, &name, &callerid);
05811    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
05812       /* Check for internal contexts and only */
05813       /* say extension when the call didn't come from an internal context in the list */
05814       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
05815          if (option_debug > 2)
05816             ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
05817          if ((strcmp(cidinternalcontexts[i], context) == 0))
05818             break;
05819       }
05820       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
05821          if (!res) {
05822             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
05823             if (!ast_strlen_zero(prefile)) {
05824             /* See if we can find a recorded name for this person instead of their extension number */
05825                if (ast_fileexists(prefile, NULL, NULL) > 0) {
05826                   if (option_verbose > 2)
05827                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
05828                   if (!callback)
05829                      res = wait_file2(chan, vms, "vm-from");
05830                   res = ast_stream_and_wait(chan, prefile, chan->language, "");
05831                } else {
05832                   if (option_verbose > 2)
05833                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
05834                   /* BB: Say "from extension" as one saying to sound smoother */
05835                   if (!callback)
05836                      res = wait_file2(chan, vms, "vm-from-extension");
05837                   res = ast_say_digit_str(chan, callerid, "", chan->language);
05838                }
05839             }
05840          }
05841       }
05842 
05843       else if (!res){
05844          if (option_debug > 2)
05845             ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
05846          /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
05847          if (!callback)
05848             res = wait_file2(chan, vms, "vm-from-phonenumber");
05849          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
05850       }
05851    } else {
05852       /* Number unknown */
05853       if (option_debug)
05854          ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
05855       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
05856       res = wait_file2(chan, vms, "vm-unknown-caller");
05857    }
05858    return res;
05859 }
05860 
05861 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
05862 {
05863    int res = 0;
05864    int durationm;
05865    int durations;
05866    /* Verify that we have a duration for the message */
05867    if (duration == NULL)
05868       return res;
05869 
05870    /* Convert from seconds to minutes */
05871    durations=atoi(duration);
05872    durationm=(durations / 60);
05873 
05874    if (option_debug > 2)
05875       ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
05876 
05877    if ((!res) && (durationm >= minduration)) {
05878       res = wait_file2(chan, vms, "vm-duration");
05879 
05880       /* POLISH syntax */
05881       if (!strncasecmp(chan->language, "pl", 2)) {
05882          div_t num = div(durationm, 10);
05883 
05884          if (durationm == 1) {
05885             res = ast_play_and_wait(chan, "digits/1z");
05886             res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
05887          } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
05888             if (num.rem == 2) {
05889                if (!num.quot) {
05890                   res = ast_play_and_wait(chan, "digits/2-ie");
05891                } else {
05892                   res = say_and_wait(chan, durationm - 2 , chan->language);
05893                   res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
05894                }
05895             } else {
05896                res = say_and_wait(chan, durationm, chan->language);
05897             }
05898             res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
05899          } else {
05900             res = say_and_wait(chan, durationm, chan->language);
05901             res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
05902          }
05903       /* DEFAULT syntax */
05904       } else {
05905          res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
05906          res = wait_file2(chan, vms, "vm-minutes");
05907       }
05908    }
05909    return res;
05910 }
05911 
05912 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
05913 {
05914    int res = 0;
05915    char filename[256], *cid;
05916    const char *origtime, *context, *category, *duration;
05917    struct ast_config *msg_cfg;
05918 
05919    vms->starting = 0; 
05920    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
05921    adsi_message(chan, vms);
05922    
05923    if (!strncasecmp(chan->language, "he", 2)) {        /* HEBREW FORMAT */
05924       /*
05925        * The syntax in hebrew for counting the number of message is up side down
05926        * in comparison to english.
05927        */
05928       if (!vms->curmsg) {
05929          res = wait_file2(chan, vms, "vm-message");
05930          res = wait_file2(chan, vms, "vm-first");    /* "First" */
05931       } else if (vms->curmsg == vms->lastmsg) {
05932          res = wait_file2(chan, vms, "vm-message");
05933          res = wait_file2(chan, vms, "vm-last");     /* "last" */
05934       } else {
05935          res = wait_file2(chan, vms, "vm-message");  /* "message" */
05936          if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05937             ast_log(LOG_DEBUG, "curmsg: %d\n", vms->curmsg);
05938             ast_log(LOG_DEBUG, "lagmsg: %d\n", vms->lastmsg);
05939             if (!res) {
05940                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
05941             }
05942          }
05943       }
05944 
05945    } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH FORMAT */
05946       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05947          int ten, one;
05948          char nextmsg[256];
05949          ten = (vms->curmsg + 1) / 10;
05950          one = (vms->curmsg + 1) % 10;
05951 
05952          if (vms->curmsg < 20) {
05953             snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
05954             res = wait_file2(chan, vms, nextmsg);
05955          } else {
05956             snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
05957             res = wait_file2(chan, vms, nextmsg);
05958             if (one > 0) {
05959                if (!res) {
05960                   snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
05961                   res = wait_file2(chan, vms, nextmsg);
05962                }
05963             }
05964          }
05965       }
05966       if (!res)
05967          res = wait_file2(chan, vms, "vm-message");         
05968 
05969    } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH FORMAT */
05970       if (!vms->curmsg)
05971          res = wait_file2(chan, vms, "vm-first");  /* "First" */
05972       else if (vms->curmsg == vms->lastmsg)
05973          res = wait_file2(chan, vms, "vm-last");      /* "last" */      
05974       res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
05975       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05976          if (!res)
05977             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
05978       }
05979       /* We know that the difference between English and Swedish
05980        * is very small, however, we differ the two for standartization
05981        * purposes, and possible changes to either of these in the 
05982        * future
05983        */
05984    } else {
05985       if (!vms->curmsg)                      /* Default syntax */
05986          res = wait_file2(chan, vms, "vm-first");  /* "First" */
05987       else if (vms->curmsg == vms->lastmsg)
05988          res = wait_file2(chan, vms, "vm-last");      /* "last" */      
05989       res = wait_file2(chan, vms, "vm-message");
05990       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
05991          if (!res)
05992             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
05993       }
05994    }
05995 
05996    /* Retrieve info from VM attribute file */
05997    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
05998    snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
05999    RETRIEVE(vms->curdir, vms->curmsg, vmu);
06000    msg_cfg = ast_config_load(filename);
06001    if (!msg_cfg) {
06002       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06003       return 0;
06004    }
06005 
06006    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06007       ast_log(LOG_WARNING, "No origtime?!\n");
06008       DISPOSE(vms->curdir, vms->curmsg);
06009       ast_config_destroy(msg_cfg);
06010       return 0;
06011    }
06012 
06013    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06014    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06015    category = ast_variable_retrieve(msg_cfg, "message", "category");
06016 
06017    context = ast_variable_retrieve(msg_cfg, "message", "context");
06018    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06019       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06020    if (!res)
06021       res = play_message_category(chan, category);
06022    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06023       res = play_message_datetime(chan, vmu, origtime, filename);
06024    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06025       res = play_message_callerid(chan, vms, cid, context, 0);
06026    if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06027       res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06028    /* Allow pressing '1' to skip envelope / callerid */
06029    if (res == '1')
06030       res = 0;
06031    ast_config_destroy(msg_cfg);
06032 
06033    if (!res) {
06034       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06035       vms->heard[vms->curmsg] = 1;
06036       if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06037          ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06038          res = 0;
06039       }
06040    }
06041    DISPOSE(vms->curdir, vms->curmsg);
06042    return res;
06043 }
06044 
06045 #ifndef IMAP_STORAGE
06046 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
06047 {
06048    int count_msg, last_msg;
06049 
06050    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
06051    
06052    /* Rename the member vmbox HERE so that we don't try to return before
06053     * we know what's going on.
06054     */
06055    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
06056    
06057    /* Faster to make the directory than to check if it exists. */
06058    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
06059 
06060    /* traverses directory using readdir (or select query for ODBC) */
06061    count_msg = count_messages(vmu, vms->curdir);
06062    if (count_msg < 0)
06063       return count_msg;
06064    else
06065       vms->lastmsg = count_msg - 1;
06066 
06067    if (vm_allocate_dh(vms, vmu, count_msg)) {
06068       return -1;
06069    }
06070 
06071    /*
06072    The following test is needed in case sequencing gets messed up.
06073    There appears to be more than one way to mess up sequence, so
06074    we will not try to find all of the root causes--just fix it when
06075    detected.
06076    */
06077 
06078    /* for local storage, checks directory for messages up to maxmsg limit */
06079    last_msg = last_message_index(vmu, vms->curdir);
06080 
06081    if (last_msg < -1) {
06082       return last_msg;
06083    } else if (vms->lastmsg != last_msg) {
06084       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);
06085       resequence_mailbox(vmu, vms->curdir, count_msg);
06086    }
06087 
06088    return 0;
06089 }
06090 #endif
06091 
06092 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
06093 {
06094    int x = 0;
06095 #ifndef IMAP_STORAGE
06096    int last_msg_index;
06097    int res = 0, nummsg;
06098 #endif
06099 
06100    if (vms->lastmsg <= -1)
06101       goto done;
06102 
06103    vms->curmsg = -1; 
06104 #ifndef IMAP_STORAGE
06105    /* Get the deleted messages fixed */ 
06106    if (vm_lock_path(vms->curdir))
06107       return ERROR_LOCK_PATH;
06108 
06109    last_msg_index = last_message_index(vmu, vms->curdir);
06110    if (last_msg_index !=  vms->lastmsg) {
06111       ast_log(LOG_NOTICE, "%d messages arrived while mailbox was open\n", last_msg_index - vms->lastmsg);
06112    }
06113  
06114    /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
06115    for (x = 0; x < last_msg_index + 1; x++) { 
06116       if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
06117          /* Save this message.  It's not in INBOX or hasn't been heard */ 
06118          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
06119          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
06120             break;
06121          vms->curmsg++; 
06122          make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
06123          if (strcmp(vms->fn, vms->fn2)) { 
06124             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
06125          } 
06126       } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
06127          /* Move to old folder before deleting */ 
06128          res = save_to_folder(vmu, vms, x, 1);
06129          if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
06130             /* If save failed do not delete the message */
06131             ast_log(LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
06132             vms->deleted[x] = 0;
06133             vms->heard[x] = 0;
06134             --x;
06135          } 
06136       } 
06137    } 
06138 
06139    /* Delete ALL remaining messages */
06140    nummsg = x - 1;
06141    for (x = vms->curmsg + 1; x <= nummsg; x++) {
06142       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
06143       if (EXISTS(vms->curdir, x, vms->fn, NULL))
06144          DELETE(vms->curdir, x, vms->fn, vmu);
06145    }
06146    ast_unlock_path(vms->curdir);
06147 #else /* defined(IMAP_STORAGE) */
06148    if (vms->deleted) {
06149       /* Since we now expunge after each delete, deleting in reverse order
06150        * ensures that no reordering occurs between each step. */
06151       for (x = vms->dh_arraysize - 1; x >= 0; x--) {
06152          if (vms->deleted[x]) {
06153             if (option_debug > 2) {
06154                ast_log(LOG_DEBUG, "IMAP delete of %d\n", x);
06155             }
06156             DELETE(vms->curdir, x, vms->fn, vmu);
06157          }
06158       }
06159    }
06160 #endif
06161 
06162 done:
06163    if (vms->deleted)
06164       memset(vms->deleted, 0, vms->dh_arraysize * sizeof(int)); 
06165    if (vms->heard)
06166       memset(vms->heard, 0, vms->dh_arraysize * sizeof(int)); 
06167 
06168    return 0;
06169 }
06170 
06171 /* In Greek even though we CAN use a syntax like "friends messages"
06172  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
06173  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
06174  * syntax for the above three categories which is more elegant. 
06175  */
06176 
06177 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
06178 {
06179    int cmd;
06180    char *buf;
06181 
06182    buf = alloca(strlen(mbox)+2); 
06183    strcpy(buf, mbox);
06184    strcat(buf,"s");
06185 
06186    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
06187       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
06188       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06189    } else {
06190       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06191       return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
06192    }
06193 }
06194 
06195 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
06196 {
06197    int cmd;
06198 
06199    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
06200       if (!strcasecmp(mbox, "vm-INBOX"))
06201          cmd = ast_play_and_wait(chan, "vm-new-e");
06202       else
06203          cmd = ast_play_and_wait(chan, "vm-old-e");
06204       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06205    } else {
06206       cmd = ast_play_and_wait(chan, "vm-messages");
06207       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06208    }
06209 }
06210 
06211 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
06212 {
06213    int cmd;
06214 
06215    if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
06216       cmd = ast_play_and_wait(chan, "vm-messages");
06217       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06218    } else {
06219       cmd = ast_play_and_wait(chan, mbox);
06220       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06221    }
06222 }
06223 
06224 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
06225 {
06226    int cmd;
06227 
06228    if (  !strncasecmp(chan->language, "it", 2) ||
06229         !strncasecmp(chan->language, "es", 2) ||
06230         !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
06231       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
06232       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06233    } else if (!strncasecmp(chan->language, "gr", 2)) {
06234       return vm_play_folder_name_gr(chan, mbox);
06235    } else if (!strncasecmp(chan->language, "pl", 2)) {
06236       return vm_play_folder_name_pl(chan, mbox);
06237    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* Ukrainian syntax */
06238       return vm_play_folder_name_ua(chan, mbox);
06239    } else if (!strncasecmp(chan->language, "he", 2)) {  /* Hebrew syntax */
06240       cmd = ast_play_and_wait(chan, mbox);
06241       return cmd;
06242    } else {  /* Default English */
06243       cmd = ast_play_and_wait(chan, mbox);
06244       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
06245    }
06246 }
06247 
06248 /* GREEK SYNTAX 
06249    In greek the plural for old/new is
06250    different so we need the following files
06251    We also need vm-denExeteMynhmata because 
06252    this syntax is different.
06253    
06254    -> vm-Olds.wav : "Palia"
06255    -> vm-INBOXs.wav : "Nea"
06256    -> vm-denExeteMynhmata : "den exete mynhmata"
06257 */
06258                
06259    
06260 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
06261 {
06262    int res = 0;
06263 
06264    if (vms->newmessages) {
06265       res = ast_play_and_wait(chan, "vm-youhave");
06266       if (!res) 
06267          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
06268       if (!res) {
06269          if ((vms->newmessages == 1)) {
06270             res = ast_play_and_wait(chan, "vm-INBOX");
06271             if (!res)
06272                res = ast_play_and_wait(chan, "vm-message");
06273          } else {
06274             res = ast_play_and_wait(chan, "vm-INBOXs");
06275             if (!res)
06276                res = ast_play_and_wait(chan, "vm-messages");
06277          }
06278       }
06279    } else if (vms->oldmessages){
06280       res = ast_play_and_wait(chan, "vm-youhave");
06281       if (!res)
06282          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
06283       if ((vms->oldmessages == 1)){
06284          res = ast_play_and_wait(chan, "vm-Old");
06285          if (!res)
06286             res = ast_play_and_wait(chan, "vm-message");
06287       } else {
06288          res = ast_play_and_wait(chan, "vm-Olds");
06289          if (!res)
06290             res = ast_play_and_wait(chan, "vm-messages");
06291       }
06292    } else if (!vms->oldmessages && !vms->newmessages) 
06293       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
06294    return res;
06295 }
06296 
06297 /* Default English syntax */
06298 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
06299 {
06300    int res;
06301 
06302    /* Introduce messages they have */
06303    res = ast_play_and_wait(chan, "vm-youhave");
06304    if (!res) {
06305       if (vms->newmessages) {
06306          res = say_and_wait(chan, vms->newmessages, chan->language);
06307          if (!res)
06308             res = ast_play_and_wait(chan, "vm-INBOX");
06309          if (vms->oldmessages && !res)
06310             res = ast_play_and_wait(chan, "vm-and");
06311          else if (!res) {
06312             if ((vms->newmessages == 1))
06313                res = ast_play_and_wait(chan, "vm-message");
06314             else
06315                res = ast_play_and_wait(chan, "vm-messages");
06316          }
06317             
06318       }
06319       if (!res && vms->oldmessages) {
06320          res = say_and_wait(chan, vms->oldmessages, chan->language);
06321          if (!res)
06322             res = ast_play_and_wait(chan, "vm-Old");
06323          if (!res) {
06324             if (vms->oldmessages == 1)
06325                res = ast_play_and_wait(chan, "vm-message");
06326             else
06327                res = ast_play_and_wait(chan, "vm-messages");
06328          }
06329       }
06330       if (!res) {
06331          if (!vms->oldmessages && !vms->newmessages) {
06332             res = ast_play_and_wait(chan, "vm-no");
06333             if (!res)
06334                res = ast_play_and_wait(chan, "vm-messages");
06335          }
06336       }
06337    }
06338    return res;
06339 }
06340 
06341 /* Version of vm_intro() designed to work for many languages.
06342  *
06343  * It is hoped that this function can prevent the proliferation of 
06344  * language-specific vm_intro() functions and in time replace the language-
06345  * specific functions which already exist.  An examination of the language-
06346  * specific functions revealed that they all corrected the same deficiencies
06347  * in vm_intro_en() (which was the default function). Namely:
06348  *
06349  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
06350  *     wording of the voicemail greeting hides this problem.  For example,
06351  *     vm-INBOX contains only the word "new".  This means that both of these
06352  *     sequences produce valid utterances:
06353  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
06354  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
06355  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
06356  *     in many languages) the first utterance becomes "you have 1 the new message".
06357  *  2) The function contains hardcoded rules for pluralizing the word "message".
06358  *     These rules are correct for English, but not for many other languages.
06359  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
06360  *     required in many languages.
06361  *  4) The gender of the word for "message" is not specified. This is a problem
06362  *     because in many languages the gender of the number in phrases such
06363  *     as "you have one new message" must match the gender of the word
06364  *     meaning "message".
06365  *
06366  * Fixing these problems for each new language has meant duplication of effort.
06367  * This new function solves the problems in the following general ways:
06368  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
06369  *     and vm-Old respectively for those languages where it makes sense.
06370  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
06371  *     on vm-message.
06372  *  3) Call ast_say_counted_adjective() to put the proper gender and number
06373  *     prefix on vm-new and vm-old (none for English).
06374  *  4) Pass the gender of the language's word for "message" as an agument to
06375  *     this function which is can in turn pass on to the functions which 
06376  *     say numbers and put endings on nounds and adjectives.
06377  *
06378  * All languages require these messages:
06379  *  vm-youhave    "You have..."
06380  *  vm-and     "and"
06381  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
06382  *
06383  * To use it for English, you will need these additional sound files:
06384  *  vm-new     "new"
06385  *  vm-message    "message", singular
06386  *  vm-messages      "messages", plural
06387  *
06388  * If you use it for Russian and other slavic languages, you will need these additional sound files:
06389  *
06390  *  vm-newn    "novoye" (singular, neuter)
06391  *  vm-newx    "novikh" (counting plural form, genative plural)
06392  *  vm-message    "sobsheniye" (singular form)
06393  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
06394  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
06395  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
06396  *  digits/2n     "dva" (neuter singular)
06397  */
06398 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
06399 {
06400    int res;
06401    int lastnum = 0;
06402 
06403    res = ast_play_and_wait(chan, "vm-youhave");
06404 
06405    if (!res && vms->newmessages) {
06406       lastnum = vms->newmessages;
06407 
06408       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06409          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
06410       }
06411 
06412       if (!res && vms->oldmessages) {
06413          res = ast_play_and_wait(chan, "vm-and");
06414       }
06415    }
06416 
06417    if (!res && vms->oldmessages) {
06418       lastnum = vms->oldmessages;
06419 
06420       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06421          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
06422       }
06423    }
06424 
06425    if (!res) {
06426       if (lastnum == 0) {
06427          res = ast_play_and_wait(chan, "vm-no");
06428       }
06429       if (!res) {
06430          res = ast_say_counted_noun(chan, lastnum, "vm-message");
06431       }
06432    }
06433 
06434    return res;
06435 }
06436 
06437 /* Default Hebrew syntax */
06438 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
06439 {
06440    int res=0;
06441 
06442    /* Introduce messages they have */
06443    if (!res) {
06444       if ((vms->newmessages) || (vms->oldmessages)) {
06445          res = ast_play_and_wait(chan, "vm-youhave");
06446       }
06447       /*
06448        * The word "shtei" refers to the number 2 in hebrew when performing a count
06449        * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
06450        * an element, this is one of them.
06451        */
06452       if (vms->newmessages) {
06453          if (!res) {
06454             if (vms->newmessages == 1) {
06455                res = ast_play_and_wait(chan, "vm-INBOX1");
06456             } else {
06457                if (vms->newmessages == 2) {
06458                   res = ast_play_and_wait(chan, "vm-shtei");
06459                } else {
06460                   res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06461                }
06462                res = ast_play_and_wait(chan, "vm-INBOX");
06463             }
06464          }
06465          if (vms->oldmessages && !res) {
06466             res = ast_play_and_wait(chan, "vm-and");
06467             if (vms->oldmessages == 1) {
06468                res = ast_play_and_wait(chan, "vm-Old1");
06469             } else {
06470                if (vms->oldmessages == 2) {
06471                   res = ast_play_and_wait(chan, "vm-shtei");
06472                } else {
06473                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06474                }
06475                res = ast_play_and_wait(chan, "vm-Old");
06476             }
06477          }
06478       }
06479       if (!res && vms->oldmessages && !vms->newmessages) {
06480          if (!res) {
06481             if (vms->oldmessages == 1) {
06482                res = ast_play_and_wait(chan, "vm-Old1");
06483             } else {
06484                if (vms->oldmessages == 2) {
06485                   res = ast_play_and_wait(chan, "vm-shtei");
06486                } else {
06487                   res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
06488                }
06489                res = ast_play_and_wait(chan, "vm-Old");
06490             }
06491          }
06492       }
06493       if (!res) {
06494          if (!vms->oldmessages && !vms->newmessages) {
06495             if (!res) {
06496                res = ast_play_and_wait(chan, "vm-nomessages");
06497             }
06498          }
06499       }
06500    }
06501    return res;
06502 }
06503 
06504 
06505 /* ITALIAN syntax */
06506 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
06507 {
06508    /* Introduce messages they have */
06509    int res;
06510    if (!vms->oldmessages && !vms->newmessages)
06511       res = ast_play_and_wait(chan, "vm-no") ||
06512          ast_play_and_wait(chan, "vm-message");
06513    else
06514       res = ast_play_and_wait(chan, "vm-youhave");
06515    if (!res && vms->newmessages) {
06516       res = (vms->newmessages == 1) ?
06517          ast_play_and_wait(chan, "digits/un") ||
06518          ast_play_and_wait(chan, "vm-nuovo") ||
06519          ast_play_and_wait(chan, "vm-message") :
06520          /* 2 or more new messages */
06521          say_and_wait(chan, vms->newmessages, chan->language) ||
06522          ast_play_and_wait(chan, "vm-nuovi") ||
06523          ast_play_and_wait(chan, "vm-messages");
06524       if (!res && vms->oldmessages)
06525          res = ast_play_and_wait(chan, "vm-and");
06526    }
06527    if (!res && vms->oldmessages) {
06528       res = (vms->oldmessages == 1) ?
06529          ast_play_and_wait(chan, "digits/un") ||
06530          ast_play_and_wait(chan, "vm-vecchio") ||
06531          ast_play_and_wait(chan, "vm-message") :
06532          /* 2 or more old messages */
06533          say_and_wait(chan, vms->oldmessages, chan->language) ||
06534          ast_play_and_wait(chan, "vm-vecchi") ||
06535          ast_play_and_wait(chan, "vm-messages");
06536    }
06537    return res;
06538 }
06539 
06540 /* POLISH syntax */
06541 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
06542 {
06543    /* Introduce messages they have */
06544    int res;
06545    div_t num;
06546 
06547    if (!vms->oldmessages && !vms->newmessages) {
06548       res = ast_play_and_wait(chan, "vm-no");
06549       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06550       return res;
06551    } else {
06552       res = ast_play_and_wait(chan, "vm-youhave");
06553    }
06554 
06555    if (vms->newmessages) {
06556       num = div(vms->newmessages, 10);
06557       if (vms->newmessages == 1) {
06558          res = ast_play_and_wait(chan, "digits/1-a");
06559          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
06560          res = res ? res : ast_play_and_wait(chan, "vm-message");
06561       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06562          if (num.rem == 2) {
06563             if (!num.quot) {
06564                res = ast_play_and_wait(chan, "digits/2-ie");
06565             } else {
06566                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
06567                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06568             }
06569          } else {
06570             res = say_and_wait(chan, vms->newmessages, chan->language);
06571          }
06572          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
06573          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06574       } else {
06575          res = say_and_wait(chan, vms->newmessages, chan->language);
06576          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
06577          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06578       }
06579       if (!res && vms->oldmessages)
06580          res = ast_play_and_wait(chan, "vm-and");
06581    }
06582    if (!res && vms->oldmessages) {
06583       num = div(vms->oldmessages, 10);
06584       if (vms->oldmessages == 1) {
06585          res = ast_play_and_wait(chan, "digits/1-a");
06586          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
06587          res = res ? res : ast_play_and_wait(chan, "vm-message");
06588       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06589          if (num.rem == 2) {
06590             if (!num.quot) {
06591                res = ast_play_and_wait(chan, "digits/2-ie");
06592             } else {
06593                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
06594                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06595             }
06596          } else {
06597             res = say_and_wait(chan, vms->oldmessages, chan->language);
06598          }
06599          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
06600          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06601       } else {
06602          res = say_and_wait(chan, vms->oldmessages, chan->language);
06603          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
06604          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06605       }
06606    }
06607 
06608    return res;
06609 }
06610 
06611 /* SWEDISH syntax */
06612 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
06613 {
06614    /* Introduce messages they have */
06615    int res;
06616 
06617    res = ast_play_and_wait(chan, "vm-youhave");
06618    if (res)
06619       return res;
06620 
06621    if (!vms->oldmessages && !vms->newmessages) {
06622       res = ast_play_and_wait(chan, "vm-no");
06623       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06624       return res;
06625    }
06626 
06627    if (vms->newmessages) {
06628       if ((vms->newmessages == 1)) {
06629          res = ast_play_and_wait(chan, "digits/ett");
06630          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
06631          res = res ? res : ast_play_and_wait(chan, "vm-message");
06632       } else {
06633          res = say_and_wait(chan, vms->newmessages, chan->language);
06634          res = res ? res : ast_play_and_wait(chan, "vm-nya");
06635          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06636       }
06637       if (!res && vms->oldmessages)
06638          res = ast_play_and_wait(chan, "vm-and");
06639    }
06640    if (!res && vms->oldmessages) {
06641       if (vms->oldmessages == 1) {
06642          res = ast_play_and_wait(chan, "digits/ett");
06643          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
06644          res = res ? res : ast_play_and_wait(chan, "vm-message");
06645       } else {
06646          res = say_and_wait(chan, vms->oldmessages, chan->language);
06647          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
06648          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06649       }
06650    }
06651 
06652    return res;
06653 }
06654 
06655 /* NORWEGIAN syntax */
06656 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
06657 {
06658    /* Introduce messages they have */
06659    int res;
06660 
06661    res = ast_play_and_wait(chan, "vm-youhave");
06662    if (res)
06663       return res;
06664 
06665    if (!vms->oldmessages && !vms->newmessages) {
06666       res = ast_play_and_wait(chan, "vm-no");
06667       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06668       return res;
06669    }
06670 
06671    if (vms->newmessages) {
06672       if ((vms->newmessages == 1)) {
06673          res = ast_play_and_wait(chan, "digits/1");
06674          res = res ? res : ast_play_and_wait(chan, "vm-ny");
06675          res = res ? res : ast_play_and_wait(chan, "vm-message");
06676       } else {
06677          res = say_and_wait(chan, vms->newmessages, chan->language);
06678          res = res ? res : ast_play_and_wait(chan, "vm-nye");
06679          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06680       }
06681       if (!res && vms->oldmessages)
06682          res = ast_play_and_wait(chan, "vm-and");
06683    }
06684    if (!res && vms->oldmessages) {
06685       if (vms->oldmessages == 1) {
06686          res = ast_play_and_wait(chan, "digits/1");
06687          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
06688          res = res ? res : ast_play_and_wait(chan, "vm-message");
06689       } else {
06690          res = say_and_wait(chan, vms->oldmessages, chan->language);
06691          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
06692          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06693       }
06694    }
06695 
06696    return res;
06697 }
06698 
06699 /* GERMAN syntax */
06700 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
06701 {
06702    /* Introduce messages they have */
06703    int res;
06704    res = ast_play_and_wait(chan, "vm-youhave");
06705    if (!res) {
06706       if (vms->newmessages) {
06707          if ((vms->newmessages == 1))
06708             res = ast_play_and_wait(chan, "digits/1F");
06709          else
06710             res = say_and_wait(chan, vms->newmessages, chan->language);
06711          if (!res)
06712             res = ast_play_and_wait(chan, "vm-INBOX");
06713          if (vms->oldmessages && !res)
06714             res = ast_play_and_wait(chan, "vm-and");
06715          else if (!res) {
06716             if ((vms->newmessages == 1))
06717                res = ast_play_and_wait(chan, "vm-message");
06718             else
06719                res = ast_play_and_wait(chan, "vm-messages");
06720          }
06721             
06722       }
06723       if (!res && vms->oldmessages) {
06724          if (vms->oldmessages == 1)
06725             res = ast_play_and_wait(chan, "digits/1F");
06726          else
06727             res = say_and_wait(chan, vms->oldmessages, chan->language);
06728          if (!res)
06729             res = ast_play_and_wait(chan, "vm-Old");
06730          if (!res) {
06731             if (vms->oldmessages == 1)
06732                res = ast_play_and_wait(chan, "vm-message");
06733             else
06734                res = ast_play_and_wait(chan, "vm-messages");
06735          }
06736       }
06737       if (!res) {
06738          if (!vms->oldmessages && !vms->newmessages) {
06739             res = ast_play_and_wait(chan, "vm-no");
06740             if (!res)
06741                res = ast_play_and_wait(chan, "vm-messages");
06742          }
06743       }
06744    }
06745    return res;
06746 }
06747 
06748 /* SPANISH syntax */
06749 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
06750 {
06751    /* Introduce messages they have */
06752    int res;
06753    if (!vms->oldmessages && !vms->newmessages) {
06754       res = ast_play_and_wait(chan, "vm-youhaveno");
06755       if (!res)
06756          res = ast_play_and_wait(chan, "vm-messages");
06757    } else {
06758       res = ast_play_and_wait(chan, "vm-youhave");
06759    }
06760    if (!res) {
06761       if (vms->newmessages) {
06762          if (!res) {
06763             if ((vms->newmessages == 1)) {
06764                res = ast_play_and_wait(chan, "digits/1M");
06765                if (!res)
06766                   res = ast_play_and_wait(chan, "vm-message");
06767                if (!res)
06768                   res = ast_play_and_wait(chan, "vm-INBOXs");
06769             } else {
06770                res = say_and_wait(chan, vms->newmessages, chan->language);
06771                if (!res)
06772                   res = ast_play_and_wait(chan, "vm-messages");
06773                if (!res)
06774                   res = ast_play_and_wait(chan, "vm-INBOX");
06775             }
06776          }
06777          if (vms->oldmessages && !res)
06778             res = ast_play_and_wait(chan, "vm-and");
06779       }
06780       if (vms->oldmessages) {
06781          if (!res) {
06782             if (vms->oldmessages == 1) {
06783                res = ast_play_and_wait(chan, "digits/1M");
06784                if (!res)
06785                   res = ast_play_and_wait(chan, "vm-message");
06786                if (!res)
06787                   res = ast_play_and_wait(chan, "vm-Olds");
06788             } else {
06789                res = say_and_wait(chan, vms->oldmessages, chan->language);
06790                if (!res)
06791                   res = ast_play_and_wait(chan, "vm-messages");
06792                if (!res)
06793                   res = ast_play_and_wait(chan, "vm-Old");
06794             }
06795          }
06796       }
06797    }
06798 return res;
06799 }
06800 
06801 /* BRAZILIAN PORTUGUESE syntax */
06802 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
06803    /* Introduce messages they have */
06804    int res;
06805    if (!vms->oldmessages && !vms->newmessages) {
06806       res = ast_play_and_wait(chan, "vm-nomessages");
06807       return res;
06808    }
06809    else {
06810       res = ast_play_and_wait(chan, "vm-youhave");
06811    }
06812    if (vms->newmessages) {
06813       if (!res)
06814          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06815       if ((vms->newmessages == 1)) {
06816          if (!res)
06817             res = ast_play_and_wait(chan, "vm-message");
06818          if (!res)
06819             res = ast_play_and_wait(chan, "vm-INBOXs");
06820       }
06821       else {
06822          if (!res)
06823             res = ast_play_and_wait(chan, "vm-messages");
06824          if (!res)
06825             res = ast_play_and_wait(chan, "vm-INBOX");
06826       }
06827       if (vms->oldmessages && !res)
06828          res = ast_play_and_wait(chan, "vm-and");
06829    }
06830    if (vms->oldmessages) {
06831       if (!res)
06832          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06833       if (vms->oldmessages == 1) {
06834          if (!res)
06835             res = ast_play_and_wait(chan, "vm-message");
06836          if (!res)
06837             res = ast_play_and_wait(chan, "vm-Olds");
06838       }
06839       else {
06840          if (!res)
06841       res = ast_play_and_wait(chan, "vm-messages");
06842          if (!res)
06843             res = ast_play_and_wait(chan, "vm-Old");
06844       }
06845    }
06846    return res;
06847 }
06848 
06849 /* FRENCH syntax */
06850 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
06851 {
06852    /* Introduce messages they have */
06853    int res;
06854    res = ast_play_and_wait(chan, "vm-youhave");
06855    if (!res) {
06856       if (vms->newmessages) {
06857          res = say_and_wait(chan, vms->newmessages, chan->language);
06858          if (!res)
06859             res = ast_play_and_wait(chan, "vm-INBOX");
06860          if (vms->oldmessages && !res)
06861             res = ast_play_and_wait(chan, "vm-and");
06862          else if (!res) {
06863             if ((vms->newmessages == 1))
06864                res = ast_play_and_wait(chan, "vm-message");
06865             else
06866                res = ast_play_and_wait(chan, "vm-messages");
06867          }
06868             
06869       }
06870       if (!res && vms->oldmessages) {
06871          res = say_and_wait(chan, vms->oldmessages, chan->language);
06872          if (!res)
06873             res = ast_play_and_wait(chan, "vm-Old");
06874          if (!res) {
06875             if (vms->oldmessages == 1)
06876                res = ast_play_and_wait(chan, "vm-message");
06877             else
06878                res = ast_play_and_wait(chan, "vm-messages");
06879          }
06880       }
06881       if (!res) {
06882          if (!vms->oldmessages && !vms->newmessages) {
06883             res = ast_play_and_wait(chan, "vm-no");
06884             if (!res)
06885                res = ast_play_and_wait(chan, "vm-messages");
06886          }
06887       }
06888    }
06889    return res;
06890 }
06891 
06892 /* DUTCH syntax */
06893 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
06894 {
06895    /* Introduce messages they have */
06896    int res;
06897    res = ast_play_and_wait(chan, "vm-youhave");
06898    if (!res) {
06899       if (vms->newmessages) {
06900          res = say_and_wait(chan, vms->newmessages, chan->language);
06901          if (!res) {
06902             if (vms->newmessages == 1)
06903                res = ast_play_and_wait(chan, "vm-INBOXs");
06904             else
06905                res = ast_play_and_wait(chan, "vm-INBOX");
06906          }
06907          if (vms->oldmessages && !res)
06908             res = ast_play_and_wait(chan, "vm-and");
06909          else if (!res) {
06910             if ((vms->newmessages == 1))
06911                res = ast_play_and_wait(chan, "vm-message");
06912             else
06913                res = ast_play_and_wait(chan, "vm-messages");
06914          }
06915             
06916       }
06917       if (!res && vms->oldmessages) {
06918          res = say_and_wait(chan, vms->oldmessages, chan->language);
06919          if (!res) {
06920             if (vms->oldmessages == 1)
06921                res = ast_play_and_wait(chan, "vm-Olds");
06922             else
06923                res = ast_play_and_wait(chan, "vm-Old");
06924          }
06925          if (!res) {
06926             if (vms->oldmessages == 1)
06927                res = ast_play_and_wait(chan, "vm-message");
06928             else
06929                res = ast_play_and_wait(chan, "vm-messages");
06930          }
06931       }
06932       if (!res) {
06933          if (!vms->oldmessages && !vms->newmessages) {
06934             res = ast_play_and_wait(chan, "vm-no");
06935             if (!res)
06936                res = ast_play_and_wait(chan, "vm-messages");
06937          }
06938       }
06939    }
06940    return res;
06941 }
06942 
06943 /* PORTUGUESE syntax */
06944 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
06945 {
06946    /* Introduce messages they have */
06947    int res;
06948    res = ast_play_and_wait(chan, "vm-youhave");
06949    if (!res) {
06950       if (vms->newmessages) {
06951          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06952          if (!res) {
06953             if ((vms->newmessages == 1)) {
06954                res = ast_play_and_wait(chan, "vm-message");
06955                if (!res)
06956                   res = ast_play_and_wait(chan, "vm-INBOXs");
06957             } else {
06958                res = ast_play_and_wait(chan, "vm-messages");
06959                if (!res)
06960                   res = ast_play_and_wait(chan, "vm-INBOX");
06961             }
06962          }
06963          if (vms->oldmessages && !res)
06964             res = ast_play_and_wait(chan, "vm-and");
06965       }
06966       if (!res && vms->oldmessages) {
06967          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06968          if (!res) {
06969             if (vms->oldmessages == 1) {
06970                res = ast_play_and_wait(chan, "vm-message");
06971                if (!res)
06972                   res = ast_play_and_wait(chan, "vm-Olds");
06973             } else {
06974                res = ast_play_and_wait(chan, "vm-messages");
06975                if (!res)
06976                   res = ast_play_and_wait(chan, "vm-Old");
06977             }
06978          }
06979       }
06980       if (!res) {
06981          if (!vms->oldmessages && !vms->newmessages) {
06982             res = ast_play_and_wait(chan, "vm-no");
06983             if (!res)
06984                res = ast_play_and_wait(chan, "vm-messages");
06985          }
06986       }
06987    }
06988    return res;
06989 }
06990 
06991 
06992 /* CZECH syntax */
06993 /* in czech there must be declension of word new and message
06994  * czech        : english        : czech      : english
06995  * --------------------------------------------------------
06996  * vm-youhave   : you have 
06997  * vm-novou     : one new        : vm-zpravu  : message
06998  * vm-nove      : 2-4 new        : vm-zpravy  : messages
06999  * vm-novych    : 5-infinite new : vm-zprav   : messages
07000  * vm-starou   : one old
07001  * vm-stare     : 2-4 old 
07002  * vm-starych   : 5-infinite old
07003  * jednu        : one   - falling 4. 
07004  * vm-no        : no  ( no messages )
07005  */
07006 
07007 static int vm_intro_cs(struct ast_channel *chan,struct vm_state *vms)
07008 {
07009    int res;
07010    res = ast_play_and_wait(chan, "vm-youhave");
07011    if (!res) {
07012       if (vms->newmessages) {
07013          if (vms->newmessages == 1) {
07014             res = ast_play_and_wait(chan, "digits/jednu");
07015          } else {
07016             res = say_and_wait(chan, vms->newmessages, chan->language);
07017          }
07018          if (!res) {
07019             if ((vms->newmessages == 1))
07020                res = ast_play_and_wait(chan, "vm-novou");
07021             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07022                res = ast_play_and_wait(chan, "vm-nove");
07023             if (vms->newmessages > 4)
07024                res = ast_play_and_wait(chan, "vm-novych");
07025          }
07026          if (vms->oldmessages && !res)
07027             res = ast_play_and_wait(chan, "vm-and");
07028          else if (!res) {
07029             if ((vms->newmessages == 1))
07030                res = ast_play_and_wait(chan, "vm-zpravu");
07031             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07032                res = ast_play_and_wait(chan, "vm-zpravy");
07033             if (vms->newmessages > 4)
07034                res = ast_play_and_wait(chan, "vm-zprav");
07035          }
07036       }
07037       if (!res && vms->oldmessages) {
07038          res = say_and_wait(chan, vms->oldmessages, chan->language);
07039          if (!res) {
07040             if ((vms->oldmessages == 1))
07041                res = ast_play_and_wait(chan, "vm-starou");
07042             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07043                res = ast_play_and_wait(chan, "vm-stare");
07044             if (vms->oldmessages > 4)
07045                res = ast_play_and_wait(chan, "vm-starych");
07046          }
07047          if (!res) {
07048             if ((vms->oldmessages == 1))
07049                res = ast_play_and_wait(chan, "vm-zpravu");
07050             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07051                res = ast_play_and_wait(chan, "vm-zpravy");
07052             if (vms->oldmessages > 4)
07053                res = ast_play_and_wait(chan, "vm-zprav");
07054          }
07055       }
07056       if (!res) {
07057          if (!vms->oldmessages && !vms->newmessages) {
07058             res = ast_play_and_wait(chan, "vm-no");
07059             if (!res)
07060                res = ast_play_and_wait(chan, "vm-zpravy");
07061          }
07062       }
07063    }
07064    return res;
07065 }
07066 
07067 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07068 {
07069    char prefile[256];
07070    
07071    /* Notify the user that the temp greeting is set and give them the option to remove it */
07072    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07073    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
07074       RETRIEVE(prefile, -1, vmu);
07075       if (ast_fileexists(prefile, NULL, NULL) > 0)
07076          ast_play_and_wait(chan, "vm-tempgreetactive");
07077       DISPOSE(prefile, -1);
07078    }
07079 
07080    /* Play voicemail intro - syntax is different for different languages */
07081    if (0) {
07082       return 0;
07083    } else if (!strncasecmp(chan->language, "cs", 2)) {  /* CZECH syntax */
07084       return vm_intro_cs(chan, vms);
07085    } else if (!strncasecmp(chan->language, "cz", 2)) {  /* deprecated CZECH syntax */
07086       static int deprecation_warning = 0;
07087       if (deprecation_warning++ % 10 == 0) {
07088          ast_log(LOG_WARNING, "cz is not a standard language code.  Please switch to using cs instead.\n");
07089       }
07090       return vm_intro_cs(chan, vms);
07091    } else if (!strncasecmp(chan->language, "de", 2)) {  /* GERMAN syntax */
07092       return vm_intro_de(chan, vms);
07093    } else if (!strncasecmp(chan->language, "es", 2)) {  /* SPANISH syntax */
07094       return vm_intro_es(chan, vms);
07095    } else if (!strncasecmp(chan->language, "fr", 2)) {  /* FRENCH syntax */
07096       return vm_intro_fr(chan, vms);
07097    } else if (!strncasecmp(chan->language, "gr", 2)) {  /* GREEK syntax */
07098       return vm_intro_gr(chan, vms);
07099    } else if (!strncasecmp(chan->language, "he", 2)) {  /* HEBREW syntax */
07100       return vm_intro_he(chan, vms);
07101    } else if (!strncasecmp(chan->language, "it", 2)) {  /* ITALIAN syntax */
07102       return vm_intro_it(chan, vms);
07103    } else if (!strncasecmp(chan->language, "nl", 2)) {  /* DUTCH syntax */
07104       return vm_intro_nl(chan, vms);
07105    } else if (!strncasecmp(chan->language, "no", 2)) {  /* NORWEGIAN syntax */
07106       return vm_intro_no(chan, vms);
07107    } else if (!strncasecmp(chan->language, "pl", 2)) {  /* POLISH syntax */
07108       return vm_intro_pl(chan, vms);
07109    } else if (!strncasecmp(chan->language, "pt_BR", 5)) {  /* BRAZILIAN PORTUGUESE syntax */
07110       return vm_intro_pt_BR(chan, vms);
07111    } else if (!strncasecmp(chan->language, "pt", 2)) {  /* PORTUGUESE syntax */
07112       return vm_intro_pt(chan, vms);
07113    } else if (!strncasecmp(chan->language, "ru", 2)) {  /* RUSSIAN syntax */
07114       return vm_intro_multilang(chan, vms, "n");
07115    } else if (!strncasecmp(chan->language, "se", 2)) {  /* SWEDISH syntax */
07116       return vm_intro_se(chan, vms);
07117    } else if (!strncasecmp(chan->language, "ua", 2)) {  /* UKRAINIAN syntax */
07118       return vm_intro_multilang(chan, vms, "n");
07119    } else {                                             /* Default to ENGLISH */
07120       return vm_intro_en(chan, vms);
07121    }
07122 }
07123 
07124 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
07125 {
07126    int res = 0;
07127    /* Play instructions and wait for new command */
07128    while (!res) {
07129       if (vms->starting) {
07130          if (vms->lastmsg > -1) {
07131             res = ast_play_and_wait(chan, "vm-onefor");
07132             if (!strncasecmp(chan->language, "he", 2)) {
07133                res = ast_play_and_wait(chan, "vm-for");
07134             }
07135             if (!res)
07136                res = vm_play_folder_name(chan, vms->vmbox);
07137          }
07138          if (!res)
07139             res = ast_play_and_wait(chan, "vm-opts");
07140       } else {
07141          if (vms->curmsg)
07142             res = ast_play_and_wait(chan, "vm-prev");
07143          if (!res && !skipadvanced)
07144             res = ast_play_and_wait(chan, "vm-advopts");
07145          if (!res)
07146             res = ast_play_and_wait(chan, "vm-repeat");
07147          if (!res && (vms->curmsg != vms->lastmsg))
07148             res = ast_play_and_wait(chan, "vm-next");
07149          if (!res) {
07150             if (!vms->deleted[vms->curmsg])
07151                res = ast_play_and_wait(chan, "vm-delete");
07152             else
07153                res = ast_play_and_wait(chan, "vm-undelete");
07154             if (!res)
07155                res = ast_play_and_wait(chan, "vm-toforward");
07156             if (!res)
07157                res = ast_play_and_wait(chan, "vm-savemessage");
07158          }
07159       }
07160       if (!res)
07161          res = ast_play_and_wait(chan, "vm-helpexit");
07162       if (!res)
07163          res = ast_waitfordigit(chan, 6000);
07164       if (!res) {
07165          vms->repeats++;
07166          if (vms->repeats > 2) {
07167             res = 't';
07168          }
07169       }
07170    }
07171    return res;
07172 }
07173 
07174 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07175 {
07176    int cmd = 0;
07177    int duration = 0;
07178    int tries = 0;
07179    char newpassword[80] = "";
07180    char newpassword2[80] = "";
07181    char prefile[PATH_MAX] = "";
07182    unsigned char buf[256];
07183    int bytes=0;
07184 
07185    if (ast_adsi_available(chan)) {
07186       bytes += adsi_logo(buf + bytes);
07187       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
07188       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07189       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07190       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07191       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07192    }
07193 
07194    /* First, have the user change their password 
07195       so they won't get here again */
07196    for (;;) {
07197       newpassword[1] = '\0';
07198       newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
07199       if (cmd == '#')
07200          newpassword[0] = '\0';
07201       if (cmd < 0 || cmd == 't' || cmd == '#')
07202          return cmd;
07203       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
07204       if (cmd < 0 || cmd == 't' || cmd == '#')
07205          return cmd;
07206       newpassword2[1] = '\0';
07207       newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
07208       if (cmd == '#')
07209          newpassword2[0] = '\0';
07210       if (cmd < 0 || cmd == 't' || cmd == '#')
07211          return cmd;
07212       cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
07213       if (cmd < 0 || cmd == 't' || cmd == '#')
07214          return cmd;
07215       if (!strcmp(newpassword, newpassword2))
07216          break;
07217       ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07218       cmd = ast_play_and_wait(chan, "vm-mismatch");
07219       if (++tries == 3)
07220          return -1;
07221       if (cmd == 0) {
07222          cmd = ast_play_and_wait(chan, "vm-pls-try-again");
07223       }
07224    }
07225    if (ast_strlen_zero(ext_pass_cmd)) 
07226       vm_change_password(vmu,newpassword);
07227    else 
07228       vm_change_password_shell(vmu,newpassword);
07229    if (option_debug > 2)
07230       ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
07231    cmd = ast_play_and_wait(chan,"vm-passchanged");
07232 
07233    /* If forcename is set, have the user record their name */  
07234    if (ast_test_flag(vmu, VM_FORCENAME)) {
07235       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07236       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07237          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07238          if (cmd < 0 || cmd == 't' || cmd == '#')
07239             return cmd;
07240       }
07241    }
07242 
07243    /* If forcegreetings is set, have the user record their greetings */
07244    if (ast_test_flag(vmu, VM_FORCEGREET)) {
07245       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07246       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07247          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07248          if (cmd < 0 || cmd == 't' || cmd == '#')
07249             return cmd;
07250       }
07251 
07252       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07253       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07254          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07255          if (cmd < 0 || cmd == 't' || cmd == '#')
07256             return cmd;
07257       }
07258    }
07259 
07260    return cmd;
07261 }
07262 
07263 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07264 {
07265    int cmd = 0;
07266    int retries = 0;
07267    int duration = 0;
07268    char newpassword[80] = "";
07269    char newpassword2[80] = "";
07270    char prefile[PATH_MAX] = "";
07271    unsigned char buf[256];
07272    int bytes=0;
07273 
07274    if (ast_adsi_available(chan))
07275    {
07276       bytes += adsi_logo(buf + bytes);
07277       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
07278       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07279       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07280       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07281       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07282    }
07283    while ((cmd >= 0) && (cmd != 't')) {
07284       if (cmd)
07285          retries = 0;
07286       switch (cmd) {
07287       case '1':
07288          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07289          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07290          break;
07291       case '2': 
07292          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07293          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07294          break;
07295       case '3': 
07296          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07297          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07298          break;
07299       case '4': 
07300          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
07301          break;
07302       case '5':
07303          if (vmu->password[0] == '-') {
07304             cmd = ast_play_and_wait(chan, "vm-no");
07305             break;
07306          }
07307          newpassword[1] = '\0';
07308          newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
07309          if (cmd == '#')
07310             newpassword[0] = '\0';
07311          else {
07312             if (cmd < 0)
07313                break;
07314             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
07315                break;
07316             }
07317          }
07318          newpassword2[1] = '\0';
07319          newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
07320          if (cmd == '#')
07321             newpassword2[0] = '\0';
07322          else {
07323             if (cmd < 0)
07324                break;
07325 
07326             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
07327                break;
07328             }
07329          }
07330          if (strcmp(newpassword, newpassword2)) {
07331             ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07332             cmd = ast_play_and_wait(chan, "vm-mismatch");
07333             if (!cmd) {
07334                cmd = ast_play_and_wait(chan, "vm-pls-try-again");
07335             }
07336             break;
07337          }
07338          if (ast_strlen_zero(ext_pass_cmd)) 
07339             vm_change_password(vmu,newpassword);
07340          else 
07341             vm_change_password_shell(vmu,newpassword);
07342          if (option_debug > 2)
07343             ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
07344          cmd = ast_play_and_wait(chan,"vm-passchanged");
07345          break;
07346       case '*': 
07347          cmd = 't';
07348          break;
07349       default: 
07350          cmd = 0;
07351          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07352          RETRIEVE(prefile, -1, vmu);
07353          if (ast_fileexists(prefile, NULL, NULL))
07354             cmd = ast_play_and_wait(chan, "vm-tmpexists");
07355          DISPOSE(prefile, -1);
07356          if (!cmd)
07357             cmd = ast_play_and_wait(chan, "vm-options");
07358          if (!cmd)
07359             cmd = ast_waitfordigit(chan,6000);
07360          if (!cmd)
07361             retries++;
07362          if (retries > 3)
07363             cmd = 't';
07364       }
07365    }
07366    if (cmd == 't')
07367       cmd = 0;
07368    return cmd;
07369 }
07370 
07371 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07372 {
07373    int res;
07374    int cmd = 0;
07375    int retries = 0;
07376    int duration = 0;
07377    char prefile[PATH_MAX] = "";
07378    unsigned char buf[256];
07379    char dest[PATH_MAX];
07380    int bytes = 0;
07381 
07382    if (ast_adsi_available(chan)) {
07383       bytes += adsi_logo(buf + bytes);
07384       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
07385       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07386       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07387       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07388       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07389    }
07390 
07391    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07392    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
07393       ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
07394       return -1;
07395    }
07396    while ((cmd >= 0) && (cmd != 't')) {
07397       if (cmd)
07398          retries = 0;
07399       RETRIEVE(prefile, -1, vmu);
07400       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
07401          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07402          cmd = 't';  
07403       } else {
07404          switch (cmd) {
07405          case '1':
07406             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07407             break;
07408          case '2':
07409             DELETE(prefile, -1, prefile, vmu);
07410             ast_play_and_wait(chan, "vm-tempremoved"