Wed Oct 28 15:47:49 2009

Asterisk developer's documentation


app_voicemail.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, 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  * \par See also
00024  * \arg \ref Config_vm
00025  * \ingroup applications
00026  */
00027 
00028 /*
00029  * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
00030  *           George Konstantoulakis <gkon@inaccessnetworks.com>
00031  *
00032  * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
00033  *
00034  * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
00035  * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
00036  *           Stojan Sljivic <stojan.sljivic@gdspartners.com>
00037  *
00038  */
00039 
00040 #include <stdlib.h>
00041 #include <errno.h>
00042 #include <unistd.h>
00043 #include <string.h>
00044 #include <stdlib.h>
00045 #include <stdio.h>
00046 #include <sys/time.h>
00047 #include <sys/stat.h>
00048 #include <sys/types.h>
00049 #include <sys/mman.h>
00050 #include <time.h>
00051 #include <dirent.h>
00052 
00053 #include "asterisk.h"
00054 
00055 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211526 $")
00056 
00057 #include "asterisk/lock.h"
00058 #include "asterisk/file.h"
00059 #include "asterisk/logger.h"
00060 #include "asterisk/channel.h"
00061 #include "asterisk/pbx.h"
00062 #include "asterisk/options.h"
00063 #include "asterisk/config.h"
00064 #include "asterisk/say.h"
00065 #include "asterisk/module.h"
00066 #include "asterisk/adsi.h"
00067 #include "asterisk/app.h"
00068 #include "asterisk/manager.h"
00069 #include "asterisk/dsp.h"
00070 #include "asterisk/localtime.h"
00071 #include "asterisk/cli.h"
00072 #include "asterisk/utils.h"
00073 #ifdef USE_ODBC_STORAGE
00074 #include "asterisk/res_odbc.h"
00075 #endif
00076 
00077 #define COMMAND_TIMEOUT 5000
00078 #define  VOICEMAIL_DIR_MODE   0700
00079 #define  VOICEMAIL_FILE_MODE  0600
00080 #define  CHUNKSIZE   65536
00081 
00082 #define VOICEMAIL_CONFIG "voicemail.conf"
00083 #define ASTERISK_USERNAME "asterisk"
00084 
00085 /* Default mail command to mail voicemail. Change it with the
00086     mailcmd= command in voicemail.conf */
00087 #define SENDMAIL "/usr/sbin/sendmail -t"
00088 
00089 #define INTRO "vm-intro"
00090 
00091 #define MAXMSG 100
00092 #define MAXMSGLIMIT 9999
00093 
00094 #define BASEMAXINLINE 256
00095 #define BASELINELEN 72
00096 #define BASEMAXINLINE 256
00097 #define eol "\r\n"
00098 
00099 #define MAX_DATETIME_FORMAT   512
00100 #define MAX_NUM_CID_CONTEXTS 10
00101 
00102 #define VM_REVIEW    (1 << 0)
00103 #define VM_OPERATOR     (1 << 1)
00104 #define VM_SAYCID    (1 << 2)
00105 #define VM_SVMAIL    (1 << 3)
00106 #define VM_ENVELOPE     (1 << 4)
00107 #define VM_SAYDURATION     (1 << 5)
00108 #define VM_SKIPAFTERCMD    (1 << 6)
00109 #define VM_FORCENAME    (1 << 7) /*!< Have new users record their name */
00110 #define VM_FORCEGREET      (1 << 8) /*!< Have new users record their greetings */
00111 #define VM_PBXSKIP      (1 << 9)
00112 #define VM_DIRECFORWARD    (1 << 10)   /*!< directory_forward */
00113 #define VM_ATTACH    (1 << 11)
00114 #define VM_DELETE    (1 << 12)
00115 #define VM_ALLOCED      (1 << 13)
00116 #define VM_SEARCH    (1 << 14)
00117 
00118 #define ERROR_LOCK_PATH    -100
00119 
00120 enum {
00121    OPT_SILENT =           (1 << 0),
00122    OPT_BUSY_GREETING =    (1 << 1),
00123    OPT_UNAVAIL_GREETING = (1 << 2),
00124    OPT_RECORDGAIN =       (1 << 3),
00125    OPT_PREPEND_MAILBOX =  (1 << 4),
00126    OPT_PRIORITY_JUMP =    (1 << 5),
00127 } vm_option_flags;
00128 
00129 enum {
00130    OPT_ARG_RECORDGAIN = 0,
00131    OPT_ARG_ARRAY_SIZE = 1,
00132 } vm_option_args;
00133 
00134 AST_APP_OPTIONS(vm_app_options, {
00135    AST_APP_OPTION('s', OPT_SILENT),
00136    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00137    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00138    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00139    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00140    AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
00141 });
00142 
00143 static int load_config(void);
00144 
00145 /*! \page vmlang Voicemail Language Syntaxes Supported
00146 
00147    \par Syntaxes supported, not really language codes.
00148    \arg \b en - English
00149    \arg \b de - German
00150    \arg \b es - Spanish
00151    \arg \b fr - French
00152    \arg \b it = Italian
00153    \arg \b nl - Dutch
00154    \arg \b pt - Portuguese
00155    \arg \b gr - Greek
00156    \arg \b no - Norwegian
00157    \arg \b se - Swedish
00158 
00159 German requires the following additional soundfile:
00160 \arg \b 1F  einE (feminine)
00161 
00162 Spanish requires the following additional soundfile:
00163 \arg \b 1M      un (masculine)
00164 
00165 Dutch, Portuguese & Spanish require the following additional soundfiles:
00166 \arg \b vm-INBOXs singular of 'new'
00167 \arg \b vm-Olds      singular of 'old/heard/read'
00168 
00169 NB these are plural:
00170 \arg \b vm-INBOX  nieuwe (nl)
00171 \arg \b vm-Old    oude (nl)
00172 
00173 Swedish uses:
00174 \arg \b vm-nytt      singular of 'new'
00175 \arg \b vm-nya    plural of 'new'
00176 \arg \b vm-gammalt   singular of 'old'
00177 \arg \b vm-gamla  plural of 'old'
00178 \arg \b digits/ett   'one', not always same as 'digits/1'
00179 
00180 Norwegian uses:
00181 \arg \b vm-ny     singular of 'new'
00182 \arg \b vm-nye    plural of 'new'
00183 \arg \b vm-gammel singular of 'old'
00184 \arg \b vm-gamle  plural of 'old'
00185 
00186 Dutch also uses:
00187 \arg \b nl-om     'at'?
00188 
00189 Spanish also uses:
00190 \arg \b vm-youhaveno
00191 
00192 Italian requires the following additional soundfile:
00193 
00194 For vm_intro_it:
00195 \arg \b vm-nuovo  new
00196 \arg \b vm-nuovi  new plural
00197 \arg \b vm-vecchio   old
00198 \arg \b vm-vecchi old plural
00199 
00200 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00201 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00202 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00203 
00204 */
00205 
00206 struct baseio {
00207    int iocp;
00208    int iolen;
00209    int linelength;
00210    int ateof;
00211    unsigned char iobuf[BASEMAXINLINE];
00212 };
00213 
00214 /*! Structure for linked list of users */
00215 struct ast_vm_user {
00216    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00217    char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
00218    char password[80];      /*!< Secret pin code, numbers only */
00219    char fullname[80];      /*!< Full name, for directory app */
00220    char email[80];         /*!< E-mail address */
00221    char pager[80];         /*!< E-mail address to pager (no attachment) */
00222    char serveremail[80];      /*!< From: Mail address */
00223    char mailcmd[160];      /*!< Configurable mail command */
00224    char language[MAX_LANGUAGE];    /*!< Config: Language setting */
00225    char zonetag[80];    /*!< Time zone */
00226    char callback[80];
00227    char dialout[80];
00228    char uniqueid[20];      /*!< Unique integer identifier */
00229    char exit[80];
00230    unsigned int flags;     /*!< VM_ flags */ 
00231    int saydurationm;
00232    int maxmsg;       /*!< Maximum number of msgs per folder for this mailbox */
00233    struct ast_vm_user *next;
00234 };
00235 
00236 struct vm_zone {
00237    char name[80];
00238    char timezone[80];
00239    char msg_format[512];
00240    struct vm_zone *next;
00241 };
00242 
00243 struct vm_state {
00244    char curbox[80];
00245    char username[80];
00246    char curdir[PATH_MAX];
00247    char vmbox[PATH_MAX];
00248    char fn[PATH_MAX];
00249    char fn2[PATH_MAX];
00250    int *deleted;
00251    int *heard;
00252    int curmsg;
00253    int lastmsg;
00254    int newmessages;
00255    int oldmessages;
00256    int starting;
00257    int repeats;
00258 };
00259 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
00260              int option, signed char record_gain);
00261 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00262 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00263                char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00264                signed char record_gain);
00265 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00266 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00267 
00268 static void apply_options(struct ast_vm_user *vmu, const char *options);
00269 
00270 #ifdef USE_ODBC_STORAGE
00271 static char odbc_database[80];
00272 static char odbc_table[80];
00273 #define RETRIEVE(a,b) retrieve_file(a,b)
00274 #define DISPOSE(a,b) remove_file(a,b)
00275 #define STORE(a,b,c,d) store_file(a,b,c,d)
00276 #define EXISTS(a,b,c,d) (message_exists(a,b))
00277 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00278 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00279 #define DELETE(a,b,c) (delete_file(a,b))
00280 #else
00281 #define RETRIEVE(a,b)
00282 #define DISPOSE(a,b)
00283 #define STORE(a,b,c,d)
00284 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00285 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00286 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00287 #define DELETE(a,b,c) (vm_delete(c))
00288 #endif
00289 
00290 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
00291 
00292 static char ext_pass_cmd[128];
00293 
00294 static char *tdesc = "Comedian Mail (Voicemail System)";
00295 
00296 static char *addesc = "Comedian Mail";
00297 
00298 static char *synopsis_vm =
00299 "Leave a Voicemail message";
00300 
00301 static char *descrip_vm =
00302 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
00303 "application allows the calling party to leave a message for the specified\n"
00304 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00305 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00306 "specified mailbox does not exist.\n"
00307 "  The Voicemail application will exit if any of the following DTMF digits are\n"
00308 "received:\n"
00309 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00310 "    * - Jump to the 'a' extension in the current dialplan context.\n"
00311 "  This application will set the following channel variable upon completion:\n"
00312 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00313 "               application. The possible values are:\n"
00314 "               SUCCESS | USEREXIT | FAILED\n\n"
00315 "  Options:\n"
00316 "    b    - Play the 'busy' greeting to the calling party.\n"
00317 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00318 "           message. The units are whole-number decibels (dB).\n"
00319 "    s    - Skip the playback of instructions for leaving a message to the\n"
00320 "           calling party.\n"
00321 "    u    - Play the 'unavailable' greeting.\n"
00322 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
00323 "           error occurs.\n";
00324 
00325 static char *synopsis_vmain =
00326 "Check Voicemail messages";
00327 
00328 static char *descrip_vmain =
00329 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
00330 "calling party to check voicemail messages. A specific mailbox, and optional\n"
00331 "corresponding context, may be specified. If a mailbox is not provided, the\n"
00332 "calling party will be prompted to enter one. If a context is not specified,\n"
00333 "the 'default' context will be used.\n\n"
00334 "  Options:\n"
00335 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00336 "           is entered by the caller.\n"
00337 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00338 "           message. The units are whole-number decibels (dB).\n"
00339 "    s    - Skip checking the passcode for the mailbox.\n";
00340 
00341 static char *synopsis_vm_box_exists =
00342 "Check to see if Voicemail mailbox exists";
00343 
00344 static char *descrip_vm_box_exists =
00345 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
00346 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00347 "will be used.\n"
00348 "  This application will set the following channel variable upon completion:\n"
00349 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00350 "                        MailboxExists application. Possible values include:\n"
00351 "                        SUCCESS | FAILED\n\n"
00352 "  Options:\n"
00353 "    j - Jump to priority n+101 if the mailbox is found.\n";
00354 
00355 static char *synopsis_vmauthenticate =
00356 "Authenticate with Voicemail passwords";
00357 
00358 static char *descrip_vmauthenticate =
00359 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
00360 "same way as the Authenticate application, but the passwords are taken from\n"
00361 "voicemail.conf.\n"
00362 "  If the mailbox is specified, only that mailbox's password will be considered\n"
00363 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00364 "be set with the authenticated mailbox.\n\n"
00365 "  Options:\n"
00366 "    s - Skip playing the initial prompts.\n";
00367 
00368 /* Leave a message */
00369 static char *app = "VoiceMail";
00370 
00371 /* Check mail, control, etc */
00372 static char *app2 = "VoiceMailMain";
00373 
00374 static char *app3 = "MailboxExists";
00375 static char *app4 = "VMAuthenticate";
00376 
00377 AST_MUTEX_DEFINE_STATIC(vmlock);
00378 struct ast_vm_user *users;
00379 struct ast_vm_user *usersl;
00380 struct vm_zone *zones = NULL;
00381 struct vm_zone *zonesl = NULL;
00382 static int maxsilence;
00383 static int maxmsg;
00384 static int silencethreshold = 128;
00385 static char serveremail[80];
00386 static char mailcmd[160];  /* Configurable mail cmd */
00387 static char externnotify[160]; 
00388 
00389 static char vmfmts[80];
00390 static int vmminmessage;
00391 static int vmmaxmessage;
00392 static int maxgreet;
00393 static int skipms;
00394 static int maxlogins;
00395 
00396 static struct ast_flags globalflags = {0};
00397 
00398 static int saydurationminfo;
00399 
00400 static char dialcontext[AST_MAX_CONTEXT];
00401 static char callcontext[AST_MAX_CONTEXT];
00402 static char exitcontext[AST_MAX_CONTEXT];
00403 
00404 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00405 
00406 
00407 static char *emailbody = NULL;
00408 static char *emailsubject = NULL;
00409 static char *pagerbody = NULL;
00410 static char *pagersubject = NULL;
00411 static char fromstring[100];
00412 static char pagerfromstring[100];
00413 static char emailtitle[100];
00414 static char charset[32] = "ISO-8859-1";
00415 
00416 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00417 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00418 static int adsiver = 1;
00419 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00420 
00421 STANDARD_LOCAL_USER;
00422 
00423 LOCAL_USER_DECL;
00424 
00425 static void populate_defaults(struct ast_vm_user *vmu)
00426 {
00427    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00428    if (saydurationminfo)
00429       vmu->saydurationm = saydurationminfo;
00430    if (callcontext)
00431       ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00432    if (dialcontext)
00433       ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00434    if (exitcontext)
00435       ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00436    if (maxmsg)
00437       vmu->maxmsg = maxmsg;
00438 }
00439 
00440 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00441 {
00442    int x;
00443    if (!strcasecmp(var, "attach")) {
00444       ast_set2_flag(vmu, ast_true(value), VM_ATTACH); 
00445    } else if (!strcasecmp(var, "serveremail")) {
00446       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00447    } else if (!strcasecmp(var, "language")) {
00448       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00449    } else if (!strcasecmp(var, "tz")) {
00450       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00451    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00452       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00453    } else if (!strcasecmp(var, "saycid")){
00454       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00455    } else if (!strcasecmp(var,"sendvoicemail")){
00456       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00457    } else if (!strcasecmp(var, "review")){
00458       ast_set2_flag(vmu, ast_true(value), VM_REVIEW); 
00459    } else if (!strcasecmp(var, "operator")){
00460       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00461    } else if (!strcasecmp(var, "envelope")){
00462       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00463    } else if (!strcasecmp(var, "sayduration")){
00464       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00465    } else if (!strcasecmp(var, "saydurationm")){
00466       if (sscanf(value, "%30d", &x) == 1) {
00467          vmu->saydurationm = x;
00468       } else {
00469          ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
00470       }
00471    } else if (!strcasecmp(var, "forcename")){
00472       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00473    } else if (!strcasecmp(var, "forcegreetings")){
00474       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00475    } else if (!strcasecmp(var, "callback")) {
00476       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00477    } else if (!strcasecmp(var, "dialout")) {
00478       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00479    } else if (!strcasecmp(var, "exitcontext")) {
00480       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00481    } else if (!strcasecmp(var, "maxmsg")) {
00482       vmu->maxmsg = atoi(value);
00483       if (vmu->maxmsg <= 0) {
00484          ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
00485          vmu->maxmsg = MAXMSG;
00486       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00487          ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00488          vmu->maxmsg = MAXMSGLIMIT;
00489       }
00490    } else if (!strcasecmp(var, "options")) {
00491       apply_options(vmu, value);
00492    }
00493 }
00494 
00495 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00496 {
00497    int res;
00498    if (!ast_strlen_zero(vmu->uniqueid)) {
00499       res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
00500       if (res > 0) {
00501          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00502          res = 0;
00503       } else if (!res) {
00504          res = -1;
00505       }
00506       return res;
00507    }
00508    return -1;
00509 }
00510 
00511 static void apply_options(struct ast_vm_user *vmu, const char *options)
00512 {  /* Destructively Parse options and apply */
00513    char *stringp;
00514    char *s;
00515    char *var, *value;
00516    stringp = ast_strdupa(options);
00517    while ((s = strsep(&stringp, "|"))) {
00518       value = s;
00519       if ((var = strsep(&value, "=")) && value) {
00520          apply_option(vmu, var, value);
00521       }
00522    }  
00523 }
00524 
00525 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00526 {
00527    struct ast_variable *var, *tmp;
00528    struct ast_vm_user *retval;
00529 
00530    if (ivm)
00531       retval=ivm;
00532    else
00533       retval=malloc(sizeof(struct ast_vm_user));
00534 
00535    if (retval) {
00536       memset(retval, 0, sizeof(struct ast_vm_user));
00537       if (!ivm)
00538          ast_set_flag(retval, VM_ALLOCED);   
00539       if (mailbox) 
00540          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
00541       populate_defaults(retval);
00542       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
00543          var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
00544       else
00545          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
00546       if (var) {
00547          tmp = var;
00548          while(tmp) {
00549             printf("%s => %s\n", tmp->name, tmp->value);
00550             if (!strcasecmp(tmp->name, "password")) {
00551                ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00552             } else if (!strcasecmp(tmp->name, "uniqueid")) {
00553                ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00554             } else if (!strcasecmp(tmp->name, "pager")) {
00555                ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
00556             } else if (!strcasecmp(tmp->name, "email")) {
00557                ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
00558             } else if (!strcasecmp(tmp->name, "fullname")) {
00559                ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
00560             } else if (!strcasecmp(tmp->name, "context")) {
00561                ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
00562             } else
00563                apply_option(retval, tmp->name, tmp->value);
00564             tmp = tmp->next;
00565          } 
00566          ast_variables_destroy(var);
00567       } else { 
00568          if (!ivm) 
00569             free(retval);
00570          retval = NULL;
00571       }  
00572    } 
00573    return retval;
00574 }
00575 
00576 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00577 {
00578    /* This function could be made to generate one from a database, too */
00579    struct ast_vm_user *vmu=NULL, *cur;
00580    ast_mutex_lock(&vmlock);
00581    cur = users;
00582 
00583    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
00584       context = "default";
00585 
00586    while (cur) {
00587       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
00588          break;
00589       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
00590          break;
00591       cur=cur->next;
00592    }
00593    if (cur) {
00594       if (ivm)
00595          vmu = ivm;
00596       else
00597          /* Make a copy, so that on a reload, we have no race */
00598          vmu = malloc(sizeof(struct ast_vm_user));
00599       if (vmu) {
00600          memcpy(vmu, cur, sizeof(struct ast_vm_user));
00601          ast_set2_flag(vmu, !ivm, VM_ALLOCED);  
00602          vmu->next = NULL;
00603       }
00604    } else
00605       vmu = find_user_realtime(ivm, context, mailbox);
00606    ast_mutex_unlock(&vmlock);
00607    return vmu;
00608 }
00609 
00610 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
00611 {
00612    /* This function could be made to generate one from a database, too */
00613    struct ast_vm_user *cur;
00614    int res = -1;
00615    ast_mutex_lock(&vmlock);
00616    cur = users;
00617    while (cur) {
00618       if ((!context || !strcasecmp(context, cur->context)) &&
00619          (!strcasecmp(mailbox, cur->mailbox)))
00620             break;
00621       cur=cur->next;
00622    }
00623    if (cur) {
00624       ast_copy_string(cur->password, newpass, sizeof(cur->password));
00625       res = 0;
00626    }
00627    ast_mutex_unlock(&vmlock);
00628    return res;
00629 }
00630 
00631 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
00632 {
00633    /*  There's probably a better way of doing this. */
00634    /*  That's why I've put the password change in a separate function. */
00635    /*  This could also be done with a database function */
00636    
00637    FILE *configin;
00638    FILE *configout;
00639    int linenum=0;
00640    char inbuf[256];
00641    char orig[256];
00642    char currcontext[256] = "";
00643    char tmpin[AST_CONFIG_MAX_PATH];
00644    char tmpout[AST_CONFIG_MAX_PATH];
00645    struct stat statbuf;
00646 
00647    if (!change_password_realtime(vmu, newpassword))
00648       return;
00649 
00650    snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
00651    snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
00652    configin = fopen(tmpin,"r");
00653    if (configin)
00654       configout = fopen(tmpout,"w+");
00655    else
00656       configout = NULL;
00657    if (!configin || !configout) {
00658       if (configin)
00659          fclose(configin);
00660       else
00661          ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
00662       if (configout)
00663          fclose(configout);
00664       else
00665          ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
00666          return;
00667    }
00668 
00669    while (!feof(configin)) {
00670       char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
00671 
00672       /* Read in the line */
00673       if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
00674          continue;
00675       linenum++;
00676 
00677       /* Make a backup of it */
00678       ast_copy_string(orig, inbuf, sizeof(orig));
00679 
00680       /*
00681         Read the file line by line, split each line into a comment and command section
00682         only parse the command portion of the line
00683       */
00684       if (inbuf[strlen(inbuf) - 1] == '\n')
00685          inbuf[strlen(inbuf) - 1] = '\0';
00686 
00687       if ((comment = strchr(inbuf, ';')))
00688          *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
00689 
00690       if (ast_strlen_zero(inbuf)) {
00691          fprintf(configout, "%s", orig);
00692          continue;
00693       }
00694 
00695       /* Check for a context, first '[' to first ']' */
00696       if ((tmpctx = strchr(inbuf, '['))) {
00697          tmpctxend = strchr(tmpctx, ']');
00698          if (tmpctxend) {
00699             /* Valid context */
00700             ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
00701             fprintf(configout, "%s", orig);
00702             continue;
00703          }
00704       }
00705 
00706       /* This isn't a context line, check for MBX => PSWD... */
00707       user = inbuf;
00708       if ((pass = strchr(user, '='))) {
00709          /* We have a line in the form of aaaaa=aaaaaa */
00710          *pass++ = '\0';
00711 
00712          user = ast_strip(user);
00713 
00714          if (*pass == '>')
00715             *pass++ = '\0';
00716 
00717          pass = ast_skip_blanks(pass);
00718 
00719          /* 
00720             Since no whitespace allowed in fields, or more correctly white space
00721             inside the fields is there for a purpose, we can just terminate pass
00722             at the comma or EOL whichever comes first.
00723          */
00724          if ((rest = strchr(pass, ',')))
00725             *rest++ = '\0';
00726       } else {
00727          user = NULL;
00728       }        
00729 
00730       /* Compare user, pass AND context */
00731       if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
00732           !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
00733           !strcasecmp(currcontext, vmu->context)) {
00734          /* This is the line */
00735          if (rest) {
00736             fprintf(configout, "%s => %s,%s", user, newpassword, rest);
00737          } else {
00738             fprintf(configout, "%s => %s", user, newpassword);
00739          }
00740          /* If there was a comment on the line print it out */
00741          if (comment) {
00742             fprintf(configout, ";%s\n", comment);
00743          } else {
00744             fprintf(configout, "\n");
00745          }
00746       } else {
00747          /* Put it back like it was */
00748          fprintf(configout, "%s", orig);
00749       }
00750    }
00751    fclose(configin);
00752    fclose(configout);
00753 
00754    stat(tmpin, &statbuf);
00755    chmod(tmpout, statbuf.st_mode);
00756    chown(tmpout, statbuf.st_uid, statbuf.st_gid);
00757    unlink(tmpin);
00758    rename(tmpout, tmpin);
00759    reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00760    ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00761 }
00762 
00763 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
00764 {
00765    char buf[255];
00766    snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
00767    if (!ast_safe_system(buf)) {
00768       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
00769       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
00770    }
00771 }
00772 
00773 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
00774 {
00775    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
00776 }
00777 
00778 static int make_file(char *dest, int len, char *dir, int num)
00779 {
00780    return snprintf(dest, len, "%s/msg%04d", dir, num);
00781 }
00782 
00783 /** basically mkdir -p $dest/$context/$ext/$mailbox
00784  * @dest    String. base directory.
00785  * @context String. Ignored if is null or empty string.
00786  * @ext     String. Ignored if is null or empty string.
00787  * @mailbox String. Ignored if is null or empty string. 
00788  * @returns -1 on failure, 0 on success.
00789  * */
00790 static int create_dirpath(char *dest, int len, char *context, char *ext, char *mailbox)
00791 {
00792    mode_t   mode = VOICEMAIL_DIR_MODE;
00793 
00794    if(context && context[0] != '\0') {
00795       make_dir(dest, len, context, "", "");
00796       if(mkdir(dest, mode) && errno != EEXIST) {
00797          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
00798          return -1;
00799       }
00800    }
00801    if(ext && ext[0] != '\0') {
00802       make_dir(dest, len, context, ext, "");
00803       if(mkdir(dest, mode) && errno != EEXIST) {
00804          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
00805          return -1;
00806       }
00807    }
00808    if(mailbox && mailbox[0] != '\0') {
00809       make_dir(dest, len, context, ext, mailbox);
00810       if(mkdir(dest, mode) && errno != EEXIST) {
00811          ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
00812          return -1;
00813       }
00814    }
00815    return 0;
00816 }
00817 
00818 /* only return failure if ast_lock_path returns 'timeout',
00819    not if the path does not exist or any other reason
00820 */
00821 static int vm_lock_path(const char *path)
00822 {
00823    switch (ast_lock_path(path)) {
00824    case AST_LOCK_TIMEOUT:
00825       return -1;
00826    default:
00827       return 0;
00828    }
00829 }
00830 
00831 
00832 #ifdef USE_ODBC_STORAGE
00833 static int retrieve_file(char *dir, int msgnum)
00834 {
00835    int x = 0;
00836    int res;
00837    int fd=-1;
00838    size_t fdlen = 0;
00839    void *fdm = MAP_FAILED;
00840    SQLSMALLINT colcount=0;
00841    SQLHSTMT stmt;
00842    char sql[PATH_MAX];
00843    char fmt[80]="";
00844    char *c;
00845    char coltitle[256];
00846    SQLSMALLINT collen;
00847    SQLSMALLINT datatype;
00848    SQLSMALLINT decimaldigits;
00849    SQLSMALLINT nullable;
00850    SQLULEN colsize;
00851    FILE *f=NULL;
00852    char rowdata[80];
00853    char fn[PATH_MAX];
00854    char full_fn[PATH_MAX];
00855    char msgnums[80];
00856    
00857    odbc_obj *obj;
00858    obj = fetch_odbc_obj(odbc_database, 0);
00859    if (obj) {
00860       ast_copy_string(fmt, vmfmts, sizeof(fmt));
00861       c = strchr(fmt, '|');
00862       if (c)
00863          *c = '\0';
00864       if (!strcasecmp(fmt, "wav49"))
00865          strcpy(fmt, "WAV");
00866       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
00867       if (msgnum > -1)
00868          make_file(fn, sizeof(fn), dir, msgnum);
00869       else
00870          ast_copy_string(fn, dir, sizeof(fn));
00871       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
00872       
00873       if (!(f = fopen(full_fn, "w+"))) {
00874               ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
00875               goto yuck;
00876       }
00877       
00878       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
00879       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00880       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00881          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00882          goto yuck;
00883       }
00884       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
00885       res = SQLPrepare(stmt, sql, SQL_NTS);
00886       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00887          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00888          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00889          goto yuck;
00890       }
00891       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
00892       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
00893       res = odbc_smart_execute(obj, stmt);
00894       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00895          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
00896          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00897          goto yuck;
00898       }
00899       res = SQLFetch(stmt);
00900       if (res == SQL_NO_DATA) {
00901          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00902          goto yuck;
00903       }
00904       else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00905          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00906          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00907          goto yuck;
00908       }
00909       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
00910       if (fd < 0) {
00911          ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
00912          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00913          goto yuck;
00914       }
00915       res = SQLNumResultCols(stmt, &colcount);
00916       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
00917          ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00918          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00919          goto yuck;
00920       }
00921       if (f) 
00922          fprintf(f, "[message]\n");
00923       for (x=0;x<colcount;x++) {
00924          rowdata[0] = '\0';
00925          collen = sizeof(coltitle);
00926          res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
00927                   &datatype, &colsize, &decimaldigits, &nullable);
00928          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00929             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00930             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00931             goto yuck;
00932          }
00933          if (!strcasecmp(coltitle, "recording")) {
00934             off_t offset;
00935             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize);
00936             fdlen = colsize;
00937             if (fd > -1) {
00938                char tmp[1]="";
00939                lseek(fd, fdlen - 1, SEEK_SET);
00940                if (write(fd, tmp, 1) != 1) {
00941                   close(fd);
00942                   fd = -1;
00943                   continue;
00944                }
00945                /* Read out in small chunks */
00946                for (offset = 0; offset < colsize; offset += CHUNKSIZE) {
00947                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
00948                      ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
00949                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00950                      goto yuck;
00951                   } else {
00952                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
00953                      munmap(fdm, CHUNKSIZE);
00954                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00955                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00956                         unlink(full_fn);
00957                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00958                         goto yuck;
00959                      }
00960                   }
00961                }
00962                truncate(full_fn, fdlen);
00963             }
00964          } else {
00965             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
00966             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00967                ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00968                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00969                goto yuck;
00970             }
00971             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
00972                fprintf(f, "%s=%s\n", coltitle, rowdata);
00973          }
00974       }
00975       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00976    } else
00977       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
00978 yuck: 
00979    if (f)
00980       fclose(f);
00981    if (fd > -1)
00982       close(fd);
00983    return x - 1;
00984 }
00985 
00986 static int remove_file(char *dir, int msgnum)
00987 {
00988    char fn[PATH_MAX];
00989    char full_fn[PATH_MAX];
00990    char msgnums[80];
00991    
00992    if (msgnum > -1) {
00993       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
00994       make_file(fn, sizeof(fn), dir, msgnum);
00995    } else
00996       ast_copy_string(fn, dir, sizeof(fn));
00997    ast_filedelete(fn, NULL);  
00998    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
00999    unlink(full_fn);
01000    return 0;
01001 }
01002 
01003 static int last_message_index(struct ast_vm_user *vmu, char *dir)
01004 {
01005    int x = 0;
01006    int res;
01007    SQLHSTMT stmt;
01008    char sql[PATH_MAX];
01009    char rowdata[20];
01010    
01011    odbc_obj *obj;
01012    obj = fetch_odbc_obj(odbc_database, 0);
01013    if (obj) {
01014       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
01015       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01016          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
01017          goto yuck;
01018       }
01019       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
01020       res = SQLPrepare(stmt, sql, SQL_NTS);
01021       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01022          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
01023          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01024          goto yuck;
01025       }
01026       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
01027       res = odbc_smart_execute(obj, stmt);
01028       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01029          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
01030          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01031          goto yuck;
01032       }
01033       res = SQLFetch(stmt);
01034       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01035          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
01036          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01037          goto yuck;
01038       }
01039       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
01040       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01041          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
01042          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01043          goto yuck;
01044       }
01045       if (sscanf(rowdata, "%30d", &x) != 1)
01046          ast_log(LOG_WARNING, "Failed to read message count!\n");
01047       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01048    } else
01049       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
01050 yuck: 
01051    return x - 1;
01052 }
01053 
01054 static int message_exists(char *dir, int msgnum)
01055 {
01056    int x = 0;
01057    int res;
01058    SQLHSTMT stmt;
01059    char sql[PATH_MAX];
01060    char rowdata[20];
01061    char msgnums[20];
01062    
01063    odbc_obj *obj;
01064    obj = fetch_odbc_obj(odbc_database, 0);
01065    if (obj) {
01066       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
01067       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
01068       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01069          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
01070          goto yuck;
01071       }
01072       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
01073       res = SQLPrepare(stmt, sql, SQL_NTS);
01074       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01075          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
01076          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01077          goto yuck;
01078       }
01079       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
01080       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01081       res = odbc_smart_execute(obj, stmt);
01082       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01083          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
01084          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01085          goto yuck;
01086       }
01087       res = SQLFetch(stmt);
01088       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01089          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
01090          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01091          goto yuck;
01092       }
01093       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
01094       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01095          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
01096          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01097          goto yuck;
01098       }
01099       if (sscanf(rowdata, "%30d", &x) != 1)
01100          ast_log(LOG_WARNING, "Failed to read message count!\n");
01101       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01102    } else
01103       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
01104 yuck: 
01105    return x;
01106 }
01107 
01108 static int count_messages(struct ast_vm_user *vmu, char *dir)
01109 {
01110    return last_message_index(vmu, dir) + 1;
01111 }
01112 
01113 static void delete_file(char *sdir, int smsg)
01114 {
01115    int res;
01116    SQLHSTMT stmt;
01117    char sql[PATH_MAX];
01118    char msgnums[20];
01119    
01120    odbc_obj *obj;
01121    obj = fetch_odbc_obj(odbc_database, 0);
01122    if (obj) {
01123       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
01124       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
01125       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01126          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
01127          goto yuck;
01128       }
01129       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
01130       res = SQLPrepare(stmt, sql, SQL_NTS);
01131       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01132          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
01133          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01134          goto yuck;
01135       }
01136       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
01137       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01138       res = odbc_smart_execute(obj, stmt);
01139       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01140          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
01141          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01142          goto yuck;
01143       }
01144       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01145    } else
01146       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
01147 yuck:
01148    return;  
01149 }
01150 
01151 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
01152 {
01153    int res;
01154    SQLHSTMT stmt;
01155    char sql[512];
01156    char msgnums[20];
01157    char msgnumd[20];
01158    odbc_obj *obj;
01159 
01160    delete_file(ddir, dmsg);
01161    obj = fetch_odbc_obj(odbc_database, 0);
01162    if (obj) {
01163       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
01164       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
01165       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
01166       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01167          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
01168          goto yuck;
01169       }
01170 #ifdef EXTENDED_ODBC_STORAGE
01171       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); 
01172 #else
01173       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table); 
01174 #endif
01175       res = SQLPrepare(stmt, sql, SQL_NTS);
01176       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01177          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
01178          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01179          goto yuck;
01180       }
01181       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
01182       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
01183 #ifdef EXTENDED_ODBC_STORAGE
01184       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
01185       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
01186       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
01187       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01188 #else
01189       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
01190       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01191 #endif       
01192       res = odbc_smart_execute(obj, stmt);
01193       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01194          ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
01195          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01196          goto yuck;
01197       }
01198       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01199    } else
01200       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
01201 yuck:
01202    return;  
01203 }
01204 
01205 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
01206 {
01207    int x = 0;
01208    int res;
01209    int fd = -1;
01210    void *fdm = MAP_FAILED;
01211    size_t fdlen = -1;
01212    SQLHSTMT stmt;
01213    SQLINTEGER len;
01214    char sql[PATH_MAX];
01215    char msgnums[20];
01216    char fn[PATH_MAX];
01217    char full_fn[PATH_MAX];
01218    char fmt[80]="";
01219    char *c;
01220    char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
01221    char *category = "";
01222    struct ast_config *cfg=NULL;
01223    odbc_obj *obj;
01224 
01225    delete_file(dir, msgnum);
01226    obj = fetch_odbc_obj(odbc_database, 0);
01227    if (obj) {
01228       ast_copy_string(fmt, vmfmts, sizeof(fmt));
01229       c = strchr(fmt, '|');
01230       if (c)
01231          *c = '\0';
01232       if (!strcasecmp(fmt, "wav49"))
01233          strcpy(fmt, "WAV");
01234       snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
01235       if (msgnum > -1)
01236          make_file(fn, sizeof(fn), dir, msgnum);
01237       else
01238          ast_copy_string(fn, dir, sizeof(fn));
01239       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
01240       cfg = ast_config_load(full_fn);
01241       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
01242       fd = open(full_fn, O_RDWR);
01243       if (fd < 0) {
01244          ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
01245          goto yuck;
01246       }
01247       if (cfg) {
01248          context = ast_variable_retrieve(cfg, "message", "context");
01249          if (!context) context = "";
01250          macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
01251          if (!macrocontext) macrocontext = "";
01252          callerid = ast_variable_retrieve(cfg, "message", "callerid");
01253          if (!callerid) callerid = "";
01254          origtime = ast_variable_retrieve(cfg, "message", "origtime");
01255          if (!origtime) origtime = "";
01256          duration = ast_variable_retrieve(cfg, "message", "duration");
01257          if (!duration) duration = "";
01258          category = ast_variable_retrieve(cfg, "message", "category");
01259          if (!category) category = "";
01260       }
01261       fdlen = lseek(fd, 0, SEEK_END);
01262       lseek(fd, 0, SEEK_SET);
01263       printf("Length is %d\n", fdlen);
01264       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
01265       if (fdm == MAP_FAILED) {
01266          ast_log(LOG_WARNING, "Memory map failed!\n");
01267          goto yuck;
01268       } 
01269       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
01270       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01271          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
01272          goto yuck;
01273       }
01274       if (!ast_strlen_zero(category)) 
01275 #ifdef EXTENDED_ODBC_STORAGE
01276          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
01277 #else
01278          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
01279 #endif
01280       else
01281 #ifdef EXTENDED_ODBC_STORAGE
01282          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
01283 #else
01284          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
01285 #endif
01286       res = SQLPrepare(stmt, sql, SQL_NTS);
01287       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01288          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
01289          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01290          goto yuck;
01291       }
01292       len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
01293       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
01294       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01295       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
01296       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
01297       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
01298       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
01299       SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
01300       SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
01301 #ifdef EXTENDED_ODBC_STORAGE
01302       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
01303       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
01304       if (!ast_strlen_zero(category))
01305          SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
01306 #else
01307       if (!ast_strlen_zero(category))
01308          SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
01309 #endif
01310       res = odbc_smart_execute(obj, stmt);
01311       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01312          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
01313          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01314          goto yuck;
01315       }
01316       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01317    } else
01318       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
01319 yuck: 
01320    if (cfg)
01321       ast_config_destroy(cfg);
01322    if (fdm != MAP_FAILED)
01323       munmap(fdm, fdlen);
01324    if (fd > -1)
01325       close(fd);
01326    return x;
01327 }
01328 
01329 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
01330 {
01331    int res;
01332    SQLHSTMT stmt;
01333    char sql[PATH_MAX];
01334    char msgnums[20];
01335    char msgnumd[20];
01336    odbc_obj *obj;
01337 
01338    delete_file(ddir, dmsg);
01339    obj = fetch_odbc_obj(odbc_database, 0);
01340    if (obj) {
01341       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
01342       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
01343       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
01344       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01345          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
01346          goto yuck;
01347       }
01348 #ifdef EXTENDED_ODBC_STORAGE
01349       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
01350 #else
01351       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
01352 #endif
01353       res = SQLPrepare(stmt, sql, SQL_NTS);
01354       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01355          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
01356          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01357          goto yuck;
01358       }
01359       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
01360       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
01361 #ifdef EXTENDED_ODBC_STORAGE
01362       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
01363       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
01364       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
01365       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01366 #else
01367       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
01368       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
01369 #endif       
01370       res = odbc_smart_execute(obj, stmt);
01371       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01372          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
01373          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01374          goto yuck;
01375       }
01376       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01377    } else
01378       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
01379 yuck:
01380    return;  
01381 }
01382 
01383 #else
01384 
01385 static int count_messages(struct ast_vm_user *vmu, char *dir)
01386 {
01387    /* Find all .txt files - even if they are not in sequence from 0000 */
01388 
01389    int vmcount = 0;
01390    DIR *vmdir = NULL;
01391    struct dirent *vment = NULL;
01392 
01393    if (vm_lock_path(dir))
01394       return ERROR_LOCK_PATH;
01395 
01396    if ((vmdir = opendir(dir))) {
01397       while ((vment = readdir(vmdir))) {
01398          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
01399             vmcount++;
01400       }
01401       closedir(vmdir);
01402    }
01403    ast_unlock_path(dir);
01404    
01405    return vmcount;
01406 }
01407 
01408 static void rename_file(char *sfn, char *dfn)
01409 {
01410    char stxt[PATH_MAX];
01411    char dtxt[PATH_MAX];
01412    ast_filerename(sfn,dfn,NULL);
01413    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
01414    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
01415    rename(stxt, dtxt);
01416 }
01417 
01418 static int copy(char *infile, char *outfile)
01419 {
01420    int ifd;
01421    int ofd;
01422    int res;
01423    int len;
01424    char buf[4096];
01425 
01426 #ifdef HARDLINK_WHEN_POSSIBLE
01427    /* Hard link if possible; saves disk space & is faster */
01428    if (link(infile, outfile)) {
01429 #endif
01430       if ((ifd = open(infile, O_RDONLY)) < 0) {
01431          ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
01432          return -1;
01433       }
01434       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
01435          ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
01436          close(ifd);
01437          return -1;
01438       }
01439       do {
01440          len = read(ifd, buf, sizeof(buf));
01441          if (len < 0) {
01442             ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
01443             close(ifd);
01444             close(ofd);
01445             unlink(outfile);
01446          }
01447          if (len) {
01448             res = write(ofd, buf, len);
01449             if (errno == ENOMEM || errno == ENOSPC || res != len) {
01450                ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
01451                close(ifd);
01452                close(ofd);
01453                unlink(outfile);
01454             }
01455          }
01456       } while (len);
01457       close(ifd);
01458       close(ofd);
01459       return 0;
01460 #ifdef HARDLINK_WHEN_POSSIBLE
01461    } else {
01462       /* Hard link succeeded */
01463       return 0;
01464    }
01465 #endif
01466 }
01467 
01468 static void copy_file(char *frompath, char *topath)
01469 {
01470    char frompath2[PATH_MAX], topath2[PATH_MAX];
01471    ast_filecopy(frompath, topath, NULL);
01472    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
01473    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
01474    copy(frompath2, topath2);
01475 }
01476 
01477 /*
01478  * A negative return value indicates an error.
01479  */
01480 static int last_message_index(struct ast_vm_user *vmu, char *dir)
01481 {
01482    int x;
01483    char fn[PATH_MAX];
01484 
01485    if (vm_lock_path(dir))
01486       return ERROR_LOCK_PATH;
01487 
01488    for (x = 0; x < vmu->maxmsg; x++) {
01489       make_file(fn, sizeof(fn), dir, x);
01490       if (ast_fileexists(fn, NULL, NULL) < 1)
01491          break;
01492    }
01493    ast_unlock_path(dir);
01494 
01495    return x - 1;
01496 }
01497 
01498 static int vm_delete(char *file)
01499 {
01500    char *txt;
01501    int txtsize = 0;
01502 
01503    txtsize = (strlen(file) + 5)*sizeof(char);
01504    txt = (char *)alloca(txtsize);
01505    /* Sprintf here would safe because we alloca'd exactly the right length,
01506     * but trying to eliminate all sprintf's anyhow
01507     */
01508    snprintf(txt, txtsize, "%s.txt", file);
01509    unlink(txt);
01510    return ast_filedelete(file, NULL);
01511 }
01512 
01513 
01514 #endif
01515 static int
01516 inbuf(struct baseio *bio, FILE *fi)
01517 {
01518    int l;
01519 
01520    if (bio->ateof)
01521       return 0;
01522 
01523    if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
01524       if (ferror(fi))
01525          return -1;
01526 
01527       bio->ateof = 1;
01528       return 0;
01529    }
01530 
01531    bio->iolen= l;
01532    bio->iocp= 0;
01533 
01534    return 1;
01535 }
01536 
01537 static int 
01538 inchar(struct baseio *bio, FILE *fi)
01539 {
01540    if (bio->iocp>=bio->iolen) {
01541       if (!inbuf(bio, fi))
01542          return EOF;
01543    }
01544 
01545    return bio->iobuf[bio->iocp++];
01546 }
01547 
01548 static int
01549 ochar(struct baseio *bio, int c, FILE *so)
01550 {
01551    if (bio->linelength>=BASELINELEN) {
01552       if (fputs(eol,so)==EOF)
01553          return -1;
01554 
01555       bio->linelength= 0;
01556    }
01557 
01558    if (putc(((unsigned char)c),so)==EOF)
01559       return -1;
01560 
01561    bio->linelength++;
01562 
01563    return 1;
01564 }
01565 
01566 static int base_encode(char *filename, FILE *so)
01567 {
01568    unsigned char dtable[BASEMAXINLINE];
01569    int i,hiteof= 0;
01570    FILE *fi;
01571    struct baseio bio;
01572 
01573    memset(&bio, 0, sizeof(bio));
01574    bio.iocp = BASEMAXINLINE;
01575 
01576    if (!(fi = fopen(filename, "rb"))) {
01577       ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
01578       return -1;
01579    }
01580 
01581    for (i= 0;i<9;i++) {
01582       dtable[i]= 'A'+i;
01583       dtable[i+9]= 'J'+i;
01584       dtable[26+i]= 'a'+i;
01585       dtable[26+i+9]= 'j'+i;
01586    }
01587    for (i= 0;i<8;i++) {
01588       dtable[i+18]= 'S'+i;
01589       dtable[26+i+18]= 's'+i;
01590    }
01591    for (i= 0;i<10;i++) {
01592       dtable[52+i]= '0'+i;
01593    }
01594    dtable[62]= '+';
01595    dtable[63]= '/';
01596 
01597    while (!hiteof){
01598       unsigned char igroup[3],ogroup[4];
01599       int c,n;
01600 
01601       igroup[0]= igroup[1]= igroup[2]= 0;
01602 
01603       for (n= 0;n<3;n++) {
01604          if ((c = inchar(&bio, fi)) == EOF) {
01605             hiteof= 1;
01606             break;
01607          }
01608 
01609          igroup[n]= (unsigned char)c;
01610       }
01611 
01612       if (n> 0) {
01613          ogroup[0]= dtable[igroup[0]>>2];
01614          ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
01615          ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
01616          ogroup[3]= dtable[igroup[2]&0x3F];
01617 
01618          if (n<3) {
01619             ogroup[3]= '=';
01620 
01621             if (n<2)
01622                ogroup[2]= '=';
01623          }
01624 
01625          for (i= 0;i<4;i++)
01626             ochar(&bio, ogroup[i], so);
01627       }
01628    }
01629 
01630    if (fputs(eol,so)==EOF)
01631       return 0;
01632 
01633    fclose(fi);
01634 
01635    return 1;
01636 }
01637 
01638 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize)
01639 {
01640    char callerid[256];
01641    /* Prepare variables for substition in email body and subject */
01642    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
01643    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
01644    snprintf(passdata, passdatasize, "%d", msgnum);
01645    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
01646    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
01647    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
01648    pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
01649    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
01650    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
01651    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
01652 }
01653 
01654 static char *quote(const char *from, char *to, size_t len)
01655 {
01656    char *ptr = to;
01657    *ptr++ = '"';
01658    for (; ptr < to + len - 1; from++) {
01659       if (*from == '"')
01660          *ptr++ = '\\';
01661       else if (*from == '\0')
01662          break;
01663       *ptr++ = *from;
01664    }
01665    if (ptr < to + len - 1)
01666       *ptr++ = '"';
01667    *ptr = '\0';
01668    return to;
01669 }
01670 
01671 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail)
01672 {
01673    FILE *p=NULL;
01674    int pfd;
01675    char date[256];
01676    char host[MAXHOSTNAMELEN] = "";
01677    char who[256];
01678    char bound[256];
01679    char fname[PATH_MAX];
01680    char dur[PATH_MAX];
01681    char tmp[80] = "/tmp/astmail-XXXXXX";
01682    char tmp2[PATH_MAX];
01683    time_t t;
01684    struct tm tm;
01685    struct vm_zone *the_zone = NULL;
01686    int len_passdata;
01687    char *passdata2;
01688 
01689    if (vmu && ast_strlen_zero(vmu->email)) {
01690       ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
01691       return(0);
01692    }
01693    if (!strcmp(format, "wav49"))
01694       format = "WAV";
01695    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));
01696    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01697       command hangs */
01698    pfd = mkstemp(tmp);
01699    if (pfd > -1) {
01700       p = fdopen(pfd, "w");
01701       if (!p) {
01702          close(pfd);
01703          pfd = -1;
01704       }
01705    }
01706    if (p) {
01707       gethostname(host, sizeof(host)-1);
01708       if (strchr(srcemail, '@'))
01709          ast_copy_string(who, srcemail, sizeof(who));
01710       else {
01711          snprintf(who, sizeof(who), "%s@%s", srcemail, host);
01712       }
01713       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
01714       time(&t);
01715 
01716       /* Does this user have a timezone specified? */
01717       if (!ast_strlen_zero(vmu->zonetag)) {
01718          /* Find the zone in the list */
01719          struct vm_zone *z;
01720          z = zones;
01721          while (z) {
01722             if (!strcmp(z->name, vmu->zonetag)) {
01723                the_zone = z;
01724                break;
01725             }
01726             z = z->next;
01727          }
01728       }
01729 
01730       if (the_zone)
01731          ast_localtime(&t,&tm,the_zone->timezone);
01732       else
01733          ast_localtime(&t,&tm,NULL);
01734       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
01735       fprintf(p, "Date: %s\n", date);
01736 
01737       /* Set date format for voicemail mail */
01738       strftime(date, sizeof(date), emaildateformat, &tm);
01739 
01740       if (*fromstring) {
01741          struct ast_channel *ast = ast_channel_alloc(0);
01742          if (ast) {
01743             char *passdata;
01744             int vmlen = strlen(fromstring)*3 + 200;
01745             if ((passdata = alloca(vmlen))) {
01746                memset(passdata, 0, vmlen);
01747                prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
01748                pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
01749                len_passdata = strlen(passdata) * 2 + 3;
01750                passdata2 = alloca(len_passdata);
01751                fprintf(p, "From: %s <%s>\n", quote(passdata, passdata2, len_passdata), who);
01752             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01753             ast_channel_free(ast);
01754          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
01755       } else
01756          fprintf(p, "From: Asterisk PBX <%s>\n", who);
01757       len_passdata = strlen(vmu->fullname) * 2 + 3;
01758       passdata2 = alloca(len_passdata);
01759       fprintf(p, "To: %s <%s>\n", quote(vmu->fullname, passdata2, len_passdata), vmu->email);
01760 
01761       if (emailsubject) {
01762          struct ast_channel *ast = ast_channel_alloc(0);
01763          if (ast) {
01764             char *passdata;
01765             int vmlen = strlen(emailsubject)*3 + 200;
01766             if ((passdata = alloca(vmlen))) {
01767                memset(passdata, 0, vmlen);
01768                prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
01769                pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
01770                fprintf(p, "Subject: %s\n",passdata);
01771             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01772             ast_channel_free(ast);
01773          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
01774       } else
01775       if (*emailtitle) {
01776          fprintf(p, emailtitle, msgnum + 1, mailbox) ;
01777          fprintf(p,"\n") ;
01778       } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
01779          fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
01780       else
01781          fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
01782       fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
01783       fprintf(p, "MIME-Version: 1.0\n");
01784       if (attach_user_voicemail) {
01785          /* Something unique. */
01786          snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
01787 
01788          fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
01789 
01790          fprintf(p, "--%s\n", bound);
01791       }
01792       fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
01793       if (emailbody) {
01794          struct ast_channel *ast = ast_channel_alloc(0);
01795          if (ast) {
01796             char *passdata;
01797             int vmlen = strlen(emailbody)*3 + 200;
01798             if ((passdata = alloca(vmlen))) {
01799                memset(passdata, 0, vmlen);
01800                prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
01801                pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
01802                fprintf(p, "%s\n",passdata);
01803             } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01804             ast_channel_free(ast);
01805          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
01806       } else {
01807          fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
01808 
01809          "in mailbox %s from %s, on %s so you might\n"
01810          "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
01811          dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
01812       }
01813       if (attach_user_voicemail) {
01814          /* Eww. We want formats to tell us their own MIME type */
01815          char *ctype = "audio/x-";
01816          if (!strcasecmp(format, "ogg"))
01817             ctype = "application/";
01818       
01819          fprintf(p, "--%s\n", bound);
01820          fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
01821          fprintf(p, "Content-Transfer-Encoding: base64\n");
01822          fprintf(p, "Content-Description: Voicemail sound attachment.\n");
01823          fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
01824 
01825          snprintf(fname, sizeof(fname), "%s.%s", attach, format);
01826          base_encode(fname, p);
01827          fprintf(p, "\n\n--%s--\n.\n", bound);
01828       }
01829       fclose(p);
01830       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
01831       ast_safe_system(tmp2);
01832       ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
01833    } else {
01834       ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
01835       return -1;
01836    }
01837    return 0;
01838 }
01839 
01840 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
01841 {
01842    FILE *p=NULL;
01843    int pfd;
01844    char date[256];
01845    char host[MAXHOSTNAMELEN] = "";
01846    char who[256];
01847    char dur[PATH_MAX];
01848    char tmp[80] = "/tmp/astmail-XXXXXX";
01849    char tmp2[PATH_MAX];
01850    time_t t;
01851    struct tm tm;
01852    struct vm_zone *the_zone = NULL;
01853    pfd = mkstemp(tmp);
01854 
01855    if (pfd > -1) {
01856       p = fdopen(pfd, "w");
01857       if (!p) {
01858          close(pfd);
01859          pfd = -1;
01860       }
01861    }
01862 
01863    if (p) {
01864       gethostname(host, sizeof(host)-1);
01865       if (strchr(srcemail, '@'))
01866          ast_copy_string(who, srcemail, sizeof(who));
01867       else {
01868          snprintf(who, sizeof(who), "%s@%s", srcemail, host);
01869       }
01870       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
01871       time(&t);
01872 
01873       /* Does this user have a timezone specified? */
01874       if (!ast_strlen_zero(vmu->zonetag)) {
01875          /* Find the zone in the list */
01876          struct vm_zone *z;
01877          z = zones;
01878          while (z) {
01879             if (!strcmp(z->name, vmu->zonetag)) {
01880                the_zone = z;
01881                break;
01882             }
01883             z = z->next;
01884          }
01885       }
01886 
01887       if (the_zone)
01888          ast_localtime(&t,&tm,the_zone->timezone);
01889       else
01890          ast_localtime(&t,&tm,NULL);
01891 
01892       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
01893       fprintf(p, "Date: %s\n", date);
01894 
01895       if (*pagerfromstring) {
01896          struct ast_channel *ast = ast_channel_alloc(0);
01897          if (ast) {
01898             char *passdata;
01899             int vmlen = strlen(fromstring)*3 + 200;
01900             if ((passdata = alloca(vmlen))) {
01901                memset(passdata, 0, vmlen);
01902                prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
01903                pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
01904                fprintf(p, "From: %s <%s>\n",passdata,who);
01905             } else 
01906                ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01907             ast_channel_free(ast);
01908          } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
01909       } else
01910          fprintf(p, "From: Asterisk PBX <%s>\n", who);
01911       fprintf(p, "To: %s\n", pager);
01912                if (pagersubject) {
01913                        struct ast_channel *ast = ast_channel_alloc(0);
01914                        if (ast) {
01915                                char *passdata;
01916                                int vmlen = strlen(pagersubject)*3 + 200;
01917                                if ((passdata = alloca(vmlen))) {
01918                                        memset(passdata, 0, vmlen);
01919                                        prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
01920                                        pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
01921                                        fprintf(p, "Subject: %s\n\n",passdata);
01922                                } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01923                                ast_channel_free(ast);
01924                        } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
01925                } else
01926                        fprintf(p, "Subject: New VM\n\n");
01927       strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
01928                if (pagerbody) {
01929                        struct ast_channel *ast = ast_channel_alloc(0);
01930                        if (ast) {
01931                                char *passdata;
01932                                int vmlen = strlen(pagerbody)*3 + 200;
01933                                if ((passdata = alloca(vmlen))) {
01934                                        memset(passdata, 0, vmlen);
01935                                        prep_email_sub_vars(ast,vmu,msgnum + 1,context,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
01936                                        pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
01937                                        fprintf(p, "%s\n",passdata);
01938                                } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
01939                                ast_channel_free(ast);
01940                        } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
01941                } else {
01942                        fprintf(p, "New %s long msg in box %s\n"
01943                                        "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
01944                }
01945       fclose(p);
01946       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
01947       ast_safe_system(tmp2);
01948       ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
01949    } else {
01950       ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
01951       return -1;
01952    }
01953    return 0;
01954 }
01955 
01956 static int get_date(char *s, int len)
01957 {
01958    struct tm tm;
01959    time_t t;
01960    t = time(0);
01961    localtime_r(&t,&tm);
01962    return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
01963 }
01964 
01965 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
01966 {
01967    int res;
01968    char fn[PATH_MAX];
01969    char dest[PATH_MAX];
01970 
01971    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
01972 
01973    if ((res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
01974       ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
01975       return -1;
01976    }
01977 
01978    RETRIEVE(fn, -1);
01979    if (ast_fileexists(fn, NULL, NULL) > 0) {
01980       res = ast_streamfile(chan, fn, chan->language);
01981       if (res) {
01982          DISPOSE(fn, -1);
01983          return -1;
01984       }
01985       res = ast_waitstream(chan, ecodes);
01986       if (res) {
01987          DISPOSE(fn, -1);
01988          return res;
01989       }
01990    } else {
01991       /* Dispose just in case */
01992       DISPOSE(fn, -1);
01993       res = ast_streamfile(chan, "vm-theperson", chan->language);
01994       if (res)
01995          return -1;
01996       res = ast_waitstream(chan, ecodes);
01997       if (res)
01998          return res;
01999       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
02000       if (res)
02001          return res;
02002    }
02003    if (busy)
02004       res = ast_streamfile(chan, "vm-isonphone", chan->language);
02005    else
02006       res = ast_streamfile(chan, "vm-isunavail", chan->language);
02007    if (res)
02008       return -1;
02009    res = ast_waitstream(chan, ecodes);
02010    return res;
02011 }
02012 
02013 static void free_user(struct ast_vm_user *vmu)
02014 {
02015    if (ast_test_flag(vmu, VM_ALLOCED))
02016       free(vmu);
02017 }
02018 
02019 static void free_zone(struct vm_zone *z)
02020 {
02021    free(z);
02022 }
02023 
02024 static char *mbox(int id)
02025 {
02026    switch(id) {
02027    case 0:
02028       return "INBOX";
02029    case 1:
02030       return "Old";
02031    case 2:
02032       return "Work";
02033    case 3:
02034       return "Family";
02035    case 4:
02036       return "Friends";
02037    case 5:
02038       return "Cust1";
02039    case 6:
02040       return "Cust2";
02041    case 7:
02042       return "Cust3";
02043    case 8:
02044       return "Cust4";
02045    case 9:
02046       return "Cust5";
02047    default:
02048       return "Unknown";
02049    }
02050 }
02051 
02052 #ifdef USE_ODBC_STORAGE
02053 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
02054 {
02055    int x = -1;
02056    int res;
02057    SQLHSTMT stmt;
02058    char sql[PATH_MAX];
02059    char rowdata[20];
02060    char tmp[PATH_MAX] = "";
02061         char *context;
02062 
02063         if (newmsgs)
02064                 *newmsgs = 0;
02065         if (oldmsgs)
02066                 *oldmsgs = 0;
02067 
02068         /* If no mailbox, return immediately */
02069         if (ast_strlen_zero(mailbox))
02070                 return 0;
02071 
02072         ast_copy_string(tmp, mailbox, sizeof(tmp));
02073         
02074    context = strchr(tmp, '@');
02075         if (context) {   
02076                 *context = '\0';
02077                 context++;
02078         } else  
02079                 context = "default";
02080    
02081    odbc_obj *obj;
02082    obj = fetch_odbc_obj(odbc_database, 0);
02083    if (obj) {
02084       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02085       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02086          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02087          goto yuck;
02088       }
02089       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
02090       res = SQLPrepare(stmt, sql, SQL_NTS);
02091       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02092          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
02093          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02094          goto yuck;
02095       }
02096       res = odbc_smart_execute(obj, stmt);
02097       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02098          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02099          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02100          goto yuck;
02101       }
02102       res = SQLFetch(stmt);
02103       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02104          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02105          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02106          goto yuck;
02107       }
02108       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02109       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02110          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02111          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02112          goto yuck;
02113       }
02114       *newmsgs = atoi(rowdata);
02115       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02116 
02117       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02118       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02119          ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02120          goto yuck;
02121       }
02122       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
02123       res = SQLPrepare(stmt, sql, SQL_NTS);
02124       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02125          ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
02126          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02127          goto yuck;
02128       }
02129       res = odbc_smart_execute(obj, stmt);
02130       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02131          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02132          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02133          goto yuck;
02134       }
02135       res = SQLFetch(stmt);
02136       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02137          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02138          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02139          goto yuck;
02140       }
02141       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02142       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02143          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02144          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02145          goto yuck;
02146       }
02147       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02148       *oldmsgs = atoi(rowdata);
02149       x = 0;
02150    } else
02151       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02152       
02153 yuck: 
02154    return x;
02155 }
02156 
02157 static int has_voicemail(const char *mailbox, const char *folder)
02158 {
02159    int nummsgs = 0;
02160         int res;
02161         SQLHSTMT stmt;
02162         char sql[PATH_MAX];
02163         char rowdata[20];
02164         char tmp[PATH_MAX] = "";
02165         char *context;
02166    if (!folder)
02167                 folder = "INBOX";
02168    /* If no mailbox, return immediately */
02169         if (ast_strlen_zero(mailbox))
02170                 return 0;
02171 
02172    ast_copy_string(tmp, mailbox, sizeof(tmp));
02173                         
02174         context = strchr(tmp, '@');
02175         if (context) {
02176                 *context = '\0';
02177                 context++;
02178         } else
02179                 context = "default";
02180 
02181         odbc_obj *obj;
02182         obj = fetch_odbc_obj(odbc_database, 0);
02183         if (obj) {
02184                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02185                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02186                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02187                         goto yuck;
02188                 }
02189       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
02190                 res = SQLPrepare(stmt, sql, SQL_NTS);
02191                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02192                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
02193                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02194                         goto yuck;
02195                 }
02196                 res = odbc_smart_execute(obj, stmt);
02197                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02198                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02199                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02200                         goto yuck;
02201                 }
02202                 res = SQLFetch(stmt);
02203                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02204                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02205                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02206                         goto yuck;
02207                 }
02208                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02209                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02210                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02211                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02212                         goto yuck;
02213                 }
02214                 nummsgs = atoi(rowdata);
02215                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02216        } else
02217                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02218 
02219 yuck:
02220    if (nummsgs>=1)
02221       return 1;
02222    else
02223       return 0;
02224 }
02225 
02226 #else
02227 
02228 static int has_voicemail(const char *mailbox, const char *folder)
02229 {
02230    DIR *dir;
02231    struct dirent *de;
02232    char fn[PATH_MAX];
02233    char tmp[PATH_MAX] = "";
02234    char *mb, *cur;
02235    char *context;
02236    int ret;
02237    if (!folder)
02238       folder = "INBOX";
02239    /* If no mailbox, return immediately */
02240    if (ast_strlen_zero(mailbox))
02241       return 0;
02242    if (strchr(mailbox, ',')) {
02243       ast_copy_string(tmp, mailbox, sizeof(tmp));
02244       mb = tmp;
02245       ret = 0;
02246       while((cur = strsep(&mb, ","))) {
02247          if (!ast_strlen_zero(cur)) {
02248             if (has_voicemail(cur, folder))
02249                return 1; 
02250          }
02251       }
02252       return 0;
02253    }
02254    ast_copy_string(tmp, mailbox, sizeof(tmp));
02255    context = strchr(tmp, '@');
02256    if (context) {
02257       *context = '\0';
02258       context++;
02259    } else
02260       context = "default";
02261    snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
02262    dir = opendir(fn);
02263    if (!dir)
02264       return 0;
02265    while ((de = readdir(dir))) {
02266       if (!strncasecmp(de->d_name, "msg", 3))
02267          break;
02268    }
02269    closedir(dir);
02270    if (de)
02271       return 1;
02272    return 0;
02273 }
02274 
02275 
02276 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
02277 {
02278    DIR *dir;
02279    struct dirent *de;
02280    char fn[PATH_MAX];
02281    char tmp[PATH_MAX] = "";
02282    char *mb, *cur;
02283    char *context;
02284    int ret;
02285    if (newmsgs)
02286       *newmsgs = 0;
02287    if (oldmsgs)
02288       *oldmsgs = 0;
02289    /* If no mailbox, return immediately */
02290    if (ast_strlen_zero(mailbox))
02291       return 0;
02292    if (strchr(mailbox, ',')) {
02293       int tmpnew, tmpold;
02294       ast_copy_string(tmp, mailbox, sizeof(tmp));
02295       mb = tmp;
02296       ret = 0;
02297       while((cur = strsep(&mb, ", "))) {
02298          if (!ast_strlen_zero(cur)) {
02299             if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
02300                return -1;
02301             else {
02302                if (newmsgs)
02303                   *newmsgs += tmpnew; 
02304                if (oldmsgs)
02305                   *oldmsgs += tmpold;
02306             }
02307          }
02308       }
02309       return 0;
02310    }
02311    ast_copy_string(tmp, mailbox, sizeof(tmp));
02312    context = strchr(tmp, '@');
02313    if (context) {
02314       *context = '\0';
02315       context++;
02316    } else
02317       context = "default";
02318    if (newmsgs) {
02319       snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
02320       dir = opendir(fn);
02321       if (dir) {
02322          while ((de = readdir(dir))) {
02323             if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
02324                !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
02325                   (*newmsgs)++;
02326                
02327          }
02328          closedir(dir);
02329       }
02330    }
02331    if (oldmsgs) {
02332       snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
02333       dir = opendir(fn);
02334       if (dir) {
02335          while ((de = readdir(dir))) {
02336             if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
02337                !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
02338                   (*oldmsgs)++;
02339                
02340          }
02341          closedir(dir);
02342       }
02343    }
02344    return 0;
02345 }
02346 
02347 #endif
02348 
02349 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
02350 
02351 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)
02352 {
02353    char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
02354    char *frombox = mbox(imbox);
02355    int recipmsgnum;
02356 
02357    ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
02358 
02359    create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
02360    
02361    if (!dir)
02362       make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
02363    else
02364       ast_copy_string(fromdir, dir, sizeof(fromdir));
02365 
02366    make_file(frompath, sizeof(frompath), fromdir, msgnum);
02367 
02368    if (vm_lock_path(todir))
02369       return ERROR_LOCK_PATH;
02370 
02371    recipmsgnum = 0;
02372    do {
02373       make_file(topath, sizeof(topath), todir, recipmsgnum);
02374       if (!EXISTS(todir, recipmsgnum, topath, chan->language))
02375          break;
02376       recipmsgnum++;
02377    } while (recipmsgnum < recip->maxmsg);
02378    if (recipmsgnum < recip->maxmsg) {
02379       COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
02380    } else {
02381       ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
02382    }
02383    ast_unlock_path(todir);
02384    notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
02385    
02386    return 0;
02387 }
02388 
02389 static void run_externnotify(char *context, char *extension)
02390 {
02391    char arguments[255];
02392    char ext_context[256] = "";
02393    int newvoicemails = 0, oldvoicemails = 0;
02394 
02395    if (!ast_strlen_zero(context))
02396       snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
02397    else
02398       ast_copy_string(ext_context, extension, sizeof(ext_context));
02399 
02400    if (!ast_strlen_zero(externnotify)) {
02401       if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
02402          ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
02403       } else {
02404          snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
02405          ast_log(LOG_DEBUG, "Executing %s\n", arguments);
02406          ast_safe_system(arguments);
02407       }
02408    }
02409 }
02410 
02411 struct leave_vm_options {
02412    unsigned int flags;
02413    signed char record_gain;
02414 };
02415 
02416 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
02417 {
02418    char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
02419    char callerid[256];
02420    FILE *txt;
02421    int res = 0, txtdes;
02422    int msgnum;
02423    int duration = 0;
02424    int ausemacro = 0;
02425    int ousemacro = 0;
02426    int ouseexten = 0;
02427    char date[256];
02428    char dir[PATH_MAX], tmpdir[PATH_MAX];
02429    char dest[PATH_MAX];
02430    char fn[PATH_MAX];
02431    char prefile[PATH_MAX] = "";
02432    char tempfile[PATH_MAX] = "";
02433    char ext_context[256] = "";
02434    char fmt[80];
02435    char *context;
02436    char ecodes[16] = "#";
02437    char tmp[1024] = "", *tmpptr;
02438    struct ast_vm_user *vmu;
02439    struct ast_vm_user svm;
02440    char *category = NULL;
02441 
02442    ast_copy_string(tmp, ext, sizeof(tmp));
02443    ext = tmp;
02444    context = strchr(tmp, '@');
02445    if (context) {
02446       *context = '\0';
02447       context++;
02448       tmpptr = strchr(context, '&');
02449    } else {
02450       tmpptr = strchr(ext, '&');
02451    }
02452 
02453    if (tmpptr) {
02454       *tmpptr = '\0';
02455       tmpptr++;
02456    }
02457 
02458    category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
02459 
02460    if (!(vmu = find_user(&svm, context, ext))) {
02461       ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
02462       if (ast_test_flag(options, OPT_PRIORITY_JUMP) || option_priority_jumping)
02463          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02464       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02465       return res;
02466    }
02467    /* Setup pre-file if appropriate */
02468    if (strcmp(vmu->context, "default"))
02469       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
02470    else
02471       ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
02472    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
02473       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
02474       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
02475    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
02476       res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
02477       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
02478    }
02479    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
02480    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
02481       ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
02482       return -1;
02483    }
02484    RETRIEVE(tempfile, -1);
02485    if (ast_fileexists(tempfile, NULL, NULL) > 0)
02486       ast_copy_string(prefile, tempfile, sizeof(prefile));
02487    DISPOSE(tempfile, -1);
02488    /* It's easier just to try to make it than to check for its existence */
02489    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
02490    create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
02491 
02492    /* Check current or macro-calling context for special extensions */
02493    if (ast_test_flag(vmu, VM_OPERATOR)) {
02494       if (!ast_strlen_zero(vmu->exit)) {
02495          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
02496             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
02497             ouseexten = 1;
02498          }
02499       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
02500          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
02501          ouseexten = 1;
02502       }
02503       else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
02504          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
02505          ousemacro = 1;
02506       }
02507    }
02508 
02509    if (!ast_strlen_zero(vmu->exit)) {
02510       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
02511          strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
02512    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
02513       strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
02514    else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
02515       strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
02516       ausemacro = 1;
02517    }
02518 
02519    /* Play the beginning intro if desired */
02520    if (!ast_strlen_zero(prefile)) {
02521       RETRIEVE(prefile, -1);
02522       if (ast_fileexists(prefile, NULL, NULL) > 0) {
02523          if (ast_streamfile(chan, prefile, chan->language) > -1) 
02524             res = ast_waitstream(chan, ecodes);
02525       } else {
02526          ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
02527          res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
02528       }
02529       DISPOSE(prefile, -1);
02530       if (res < 0) {
02531          ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
02532          free_user(vmu);
02533          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02534          return -1;
02535       }
02536    }
02537    if (res == '#') {
02538       /* On a '#' we skip the instructions */
02539       ast_set_flag(options, OPT_SILENT);
02540       res = 0;
02541    }
02542    if (!res && !ast_test_flag(options, OPT_SILENT)) {
02543       res = ast_streamfile(chan, INTRO, chan->language);
02544       if (!res)
02545          res = ast_waitstream(chan, ecodes);
02546       if (res == '#') {
02547          ast_set_flag(options, OPT_SILENT);
02548          res = 0;
02549       }
02550    }
02551    if (res > 0)
02552       ast_stopstream(chan);
02553    /* Check for a '*' here in case the caller wants to escape from voicemail to something
02554       other than the operator -- an automated attendant or mailbox login for example */
02555    if (res == '*') {
02556       chan->exten[0] = 'a';
02557       chan->exten[1] = '\0';
02558       if (!ast_strlen_zero(vmu->exit)) {
02559          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
02560       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
02561          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
02562       }
02563       chan->priority = 0;
02564       free_user(vmu);
02565       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
02566       return 0;
02567    }
02568 
02569    /* Check for a '0' here */
02570    if (res == '0') {
02571    transfer:
02572       if(ouseexten || ousemacro) {
02573          chan->exten[0] = 'o';
02574          chan->exten[1] = '\0';
02575          if (!ast_strlen_zero(vmu->exit)) {
02576             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
02577          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
02578             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
02579          }
02580          ast_play_and_wait(chan, "transfer");
02581          chan->priority = 0;
02582          free_user(vmu);
02583          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
02584       }
02585       return 0;
02586    }
02587    if (res < 0) {
02588       free_user(vmu);
02589       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02590       return -1;
02591    }
02592    /* The meat of recording the message...  All the announcements and beeps have been played*/
02593    ast_copy_string(fmt, vmfmts, sizeof(fmt));
02594    if (!ast_strlen_zero(fmt)) {
02595       msgnum = 0;
02596 
02597       if (count_messages(vmu, dir) >= vmu->maxmsg) {
02598          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
02599          if (!res)
02600             res = ast_waitstream(chan, "");
02601          ast_log(LOG_WARNING, "No more messages possible\n");
02602          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02603          goto leave_vm_out;
02604       }
02605 
02606       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
02607       txtdes = mkstemp(tmptxtfile);
02608       if (txtdes < 0) {
02609          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
02610          if (!res)
02611             res = ast_waitstream(chan, "");
02612          ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
02613          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02614          goto leave_vm_out;
02615       }
02616 
02617       /* Now play the beep once we have the message number for our next message. */
02618       if (res >= 0) {
02619          /* Unless we're *really* silent, try to send the beep */
02620          res = ast_streamfile(chan, "beep", chan->language);
02621          if (!res)
02622             res = ast_waitstream(chan, "");
02623       }
02624 
02625       /* Store information */
02626       txt = fdopen(txtdes, "w+");
02627       if (txt) {
02628          get_date(date, sizeof(date));
02629          fprintf(txt, 
02630             ";\n"
02631             "; Message Information file\n"
02632             ";\n"
02633             "[message]\n"
02634             "origmailbox=%s\n"
02635             "context=%s\n"
02636             "macrocontext=%s\n"
02637             "exten=%s\n"
02638             "priority=%d\n"
02639             "callerchan=%s\n"
02640             "callerid=%s\n"
02641             "origdate=%s\n"
02642             "origtime=%ld\n"
02643             "category=%s\n",
02644             ext,
02645             chan->context,
02646             chan->macrocontext, 
02647             chan->exten,
02648             chan->priority,
02649             chan->name,
02650             ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
02651             date, (long)time(NULL),
02652             category ? category : ""); 
02653       } else
02654          ast_log(LOG_WARNING, "Error opening text file for output\n");
02655       res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
02656 
02657       if (txt) {
02658          if (duration < vmminmessage) {
02659             if (option_verbose > 2) 
02660                ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
02661             fclose(txt);
02662             ast_filedelete(tmptxtfile, NULL);
02663             unlink(tmptxtfile);
02664          } else {
02665             fprintf(txt, "duration=%d\n", duration);
02666             fclose(txt);
02667             if (vm_lock_path(dir)) {
02668                ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
02669                /* Delete files */
02670                ast_filedelete(tmptxtfile, NULL);
02671                unlink(tmptxtfile);
02672             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
02673                if (option_debug) 
02674                   ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
02675                unlink(tmptxtfile);
02676                ast_unlock_path(dir);
02677             } else {
02678                for (;;) {
02679                   make_file(fn, sizeof(fn), dir, msgnum);
02680                   if (!EXISTS(dir, msgnum, fn, NULL))
02681                      break;
02682                   msgnum++;
02683                }
02684 
02685                /* assign a variable with the name of the voicemail file */   
02686                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
02687 
02688                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
02689                ast_filerename(tmptxtfile, fn, NULL);
02690                rename(tmptxtfile, txtfile);
02691 
02692                ast_unlock_path(dir);
02693 
02694                /* We must store the file first, before copying the message, because
02695                 * ODBC storage does the entire copy with SQL.
02696                 */
02697                if (ast_fileexists(fn, NULL, NULL) > 0) {
02698                   STORE(dir, vmu->mailbox, vmu->context, msgnum);
02699                }
02700 
02701                /* Are there to be more recipients of this message? */
02702                while (tmpptr) {
02703                   struct ast_vm_user recipu, *recip;
02704                   char *exten, *context;
02705 
02706                   exten = strsep(&tmpptr, "&");
02707                   context = strchr(exten, '@');
02708                   if (context) {
02709                      *context = '\0';
02710                      context++;
02711                   }
02712                   if ((recip = find_user(&recipu, context, exten))) {
02713                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
02714                      free_user(recip);
02715                   }
02716                }
02717 
02718                /* Notification and disposal needs to happen after the copy, though. */
02719                if (ast_fileexists(fn, NULL, NULL) > 0) {
02720                   notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
02721                   DISPOSE(dir, msgnum);
02722                }
02723             }
02724          }
02725       }
02726 
02727       if (res == '0') {
02728          goto transfer;
02729       } else if (res > 0)
02730          res = 0;
02731 
02732       if (duration < vmminmessage)
02733          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
02734          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
02735       else
02736          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
02737    } else
02738       ast_log(LOG_WARNING, "No format for saving voicemail?\n");
02739  leave_vm_out:
02740    free_user(vmu);
02741    
02742    return res;
02743 }
02744 
02745 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
02746 {
02747    /* we know max messages, so stop process when number is hit */
02748 
02749    int x,dest;
02750    char sfn[PATH_MAX];
02751    char dfn[PATH_MAX];
02752 
02753    if (vm_lock_path(dir))
02754       return ERROR_LOCK_PATH;
02755 
02756    for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
02757       make_file(sfn, sizeof(sfn), dir, x);
02758       if (EXISTS(dir, x, sfn, NULL)) {
02759          
02760          if(x != dest) {
02761             make_file(dfn, sizeof(dfn), dir, dest);
02762             RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
02763          }
02764          
02765          dest++;
02766       }
02767    }
02768    ast_unlock_path(dir);
02769 
02770    return 0;
02771 }
02772 
02773 
02774 static int say_and_wait(struct ast_channel *chan, int num, char *language)
02775 {
02776    int d;
02777    d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
02778    return d;
02779 }
02780 
02781 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
02782 {
02783    char sfn[PATH_MAX];
02784    char dfn[PATH_MAX];
02785    char ddir[PATH_MAX];
02786    char *dbox = mbox(box);
02787    int x;
02788    make_file(sfn, sizeof(sfn), dir, msg);
02789    create_dirpath(ddir, sizeof(ddir), context, username, dbox);
02790 
02791    if (vm_lock_path(ddir))
02792       return ERROR_LOCK_PATH;
02793 
02794    for (x = 0; x < vmu->maxmsg; x++) {
02795       make_file(dfn, sizeof(dfn), ddir, x);
02796       if (!EXISTS(ddir, x, dfn, NULL))
02797          break;
02798    }
02799    if (x >= vmu->maxmsg) {
02800       ast_unlock_path(ddir);
02801       return -1;
02802    }
02803    if (strcmp(sfn, dfn)) {
02804       COPY(dir, msg, ddir, x, username, context, sfn, dfn);
02805    }
02806    ast_unlock_path(ddir);
02807    
02808    return 0;
02809 }
02810 
02811 static int adsi_logo(unsigned char *buf)
02812 {
02813    int bytes = 0;
02814    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
02815    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
02816    return bytes;
02817 }
02818 
02819 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
02820 {
02821    unsigned char buf[256];
02822    int bytes=0;
02823    int x;
02824    char num[5];
02825 
02826    *useadsi = 0;
02827    bytes += adsi_data_mode(buf + bytes);
02828    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02829 
02830    bytes = 0;
02831    bytes += adsi_logo(buf);
02832    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
02833 #ifdef DISPLAY
02834    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
02835 #endif
02836    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02837    bytes += adsi_data_mode(buf + bytes);
02838    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02839 
02840    if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
02841       bytes = 0;
02842       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
02843       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
02844       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02845       bytes += adsi_voice_mode(buf + bytes, 0);
02846       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02847       return 0;
02848    }
02849 
02850 #ifdef DISPLAY
02851    /* Add a dot */
02852    bytes = 0;
02853    bytes += adsi_logo(buf);
02854    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
02855    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
02856    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02857    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02858 #endif
02859    bytes = 0;
02860    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
02861    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
02862    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
02863    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
02864    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
02865    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
02866    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
02867 
02868 #ifdef DISPLAY
02869    /* Add another dot */
02870    bytes = 0;
02871    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
02872    bytes += adsi_voice_mode(buf + bytes, 0);
02873 
02874    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02875    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02876 #endif
02877 
02878    bytes = 0;
02879    /* These buttons we load but don't use yet */
02880    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
02881    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
02882    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
02883    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
02884    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
02885    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
02886    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
02887 
02888 #ifdef DISPLAY
02889    /* Add another dot */
02890    bytes = 0;
02891    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
02892    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02893    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02894 #endif
02895 
02896    bytes = 0;
02897    for (x=0;x<5;x++) {
02898       snprintf(num, sizeof(num), "%d", x);
02899       bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
02900    }
02901    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
02902    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
02903 
02904 #ifdef DISPLAY
02905    /* Add another dot */
02906    bytes = 0;
02907    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
02908    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02909    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02910 #endif
02911 
02912    if (adsi_end_download(chan)) {
02913       bytes = 0;
02914       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
02915       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
02916       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02917       bytes += adsi_voice_mode(buf + bytes, 0);
02918       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02919       return 0;
02920    }
02921    bytes = 0;
02922    bytes += adsi_download_disconnect(buf + bytes);
02923    bytes += adsi_voice_mode(buf + bytes, 0);
02924    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
02925 
02926    ast_log(LOG_DEBUG, "Done downloading scripts...\n");
02927 
02928 #ifdef DISPLAY
02929    /* Add last dot */
02930    bytes = 0;
02931    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
02932    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02933 #endif
02934    ast_log(LOG_DEBUG, "Restarting session...\n");
02935 
02936    bytes = 0;
02937    /* Load the session now */
02938    if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
02939       *useadsi = 1;
02940       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
02941    } else
02942       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
02943 
02944    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02945    return 0;
02946 }
02947 
02948 static void adsi_begin(struct ast_channel *chan, int *useadsi)
02949 {
02950    int x;
02951    if (!adsi_available(chan))
02952       return;
02953    x = adsi_load_session(chan, adsifdn, adsiver, 1);
02954    if (x < 0)
02955       return;
02956    if (!x) {
02957       if (adsi_load_vmail(chan, useadsi)) {
02958          ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
02959          return;
02960       }
02961    } else
02962       *useadsi = 1;
02963 }
02964 
02965 static void adsi_login(struct ast_channel *chan)
02966 {
02967    unsigned char buf[256];
02968    int bytes=0;
02969    unsigned char keys[8];
02970    int x;
02971    if (!adsi_available(chan))
02972       return;
02973 
02974    for (x=0;x<8;x++)
02975       keys[x] = 0;
02976    /* Set one key for next */
02977    keys[3] = ADSI_KEY_APPS + 3;
02978 
02979    bytes += adsi_logo(buf + bytes);
02980    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
02981    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
02982    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
02983    bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
02984    bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
02985    bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
02986    bytes += adsi_set_keys(buf + bytes, keys);
02987    bytes += adsi_voice_mode(buf + bytes, 0);
02988    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
02989 }
02990 
02991 static void adsi_password(struct ast_channel *chan)
02992 {
02993    unsigned char buf[256];
02994    int bytes=0;
02995    unsigned char keys[8];
02996    int x;
02997    if (!adsi_available(chan))
02998       return;
02999 
03000    for (x=0;x<8;x++)
03001       keys[x] = 0;
03002    /* Set one key for next */
03003    keys[3] = ADSI_KEY_APPS + 3;
03004 
03005    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03006    bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
03007    bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
03008    bytes += adsi_set_keys(buf + bytes, keys);
03009    bytes += adsi_voice_mode(buf + bytes, 0);
03010    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03011 }
03012 
03013 static void adsi_folders(struct ast_channel *chan, int start, char *label)
03014 {
03015    unsigned char buf[256];
03016    int bytes=0;
03017    unsigned char keys[8];
03018    int x,y;
03019 
03020    if (!adsi_available(chan))
03021       return;
03022 
03023    for (x=0;x<5;x++) {
03024       y = ADSI_KEY_APPS + 12 + start + x;
03025       if (y > ADSI_KEY_APPS + 12 + 4)
03026          y = 0;
03027       keys[x] = ADSI_KEY_SKT | y;
03028    }
03029    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
03030    keys[6] = 0;
03031    keys[7] = 0;
03032 
03033    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
03034    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
03035    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03036    bytes += adsi_set_keys(buf + bytes, keys);
03037    bytes += adsi_voice_mode(buf + bytes, 0);
03038 
03039    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03040 }
03041 
03042 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
03043 {
03044    int bytes=0;
03045    unsigned char buf[256]; 
03046    char buf1[256], buf2[256];
03047    char fn2[PATH_MAX];
03048 
03049    char cid[256]="";
03050    char *val;
03051    char *name, *num;
03052    char datetime[21]="";
03053    FILE *f;
03054 
03055    unsigned char keys[8];
03056 
03057    int x;
03058 
03059    if (!adsi_available(chan))
03060       return;
03061 
03062    /* Retrieve important info */
03063    snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
03064    f = fopen(fn2, "r");
03065    if (f) {
03066       while (!feof(f)) {   
03067          fgets((char *)buf, sizeof(buf), f);
03068          if (!feof(f)) {
03069             char *stringp=NULL;
03070             stringp = (char *)buf;
03071             strsep(&stringp, "=");
03072             val = strsep(&stringp, "=");
03073             if (!ast_strlen_zero(val)) {
03074                if (!strcmp((char *)buf, "callerid"))
03075                   ast_copy_string(cid, val, sizeof(cid));
03076                if (!strcmp((char *)buf, "origdate"))
03077                   ast_copy_string(datetime, val, sizeof(datetime));
03078             }
03079          }
03080       }
03081       fclose(f);
03082    }
03083    /* New meaning for keys */
03084    for (x=0;x<5;x++)
03085       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
03086    keys[6] = 0x0;
03087    keys[7] = 0x0;
03088 
03089    if (!vms->curmsg) {
03090       /* No prev key, provide "Folder" instead */
03091       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
03092    }
03093    if (vms->curmsg >= vms->lastmsg) {
03094       /* If last message ... */
03095       if (vms->curmsg) {
03096          /* but not only message, provide "Folder" instead */
03097          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
03098          bytes += adsi_voice_mode(buf + bytes, 0);
03099 
03100       } else {
03101          /* Otherwise if only message, leave blank */
03102          keys[3] = 1;
03103       }
03104    }
03105 
03106    if (!ast_strlen_zero(cid)) {
03107       ast_callerid_parse(cid, &name, &num);
03108       if (!name)
03109          name = num;
03110    } else
03111       name = "Unknown Caller";
03112 
03113    /* If deleted, show "undeleted" */
03114 
03115    if (vms->deleted[vms->curmsg])
03116       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
03117 
03118    /* Except "Exit" */
03119    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
03120    snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
03121       strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
03122    snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
03123 
03124    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
03125    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
03126    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
03127    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
03128    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03129    bytes += adsi_set_keys(buf + bytes, keys);
03130    bytes += adsi_voice_mode(buf + bytes, 0);
03131 
03132    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03133 }
03134 
03135 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
03136 {
03137    int bytes=0;
03138    unsigned char buf[256];
03139    unsigned char keys[8];
03140 
03141    int x;
03142 
03143    if (!adsi_available(chan))
03144       return;
03145 
03146    /* New meaning for keys */
03147    for (x=0;x<5;x++)
03148       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
03149 
03150    keys[6] = 0x0;
03151    keys[7] = 0x0;
03152 
03153    if (!vms->curmsg) {
03154       /* No prev key, provide "Folder" instead */
03155       keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
03156    }
03157    if (vms->curmsg >= vms->lastmsg) {
03158       /* If last message ... */
03159       if (vms->curmsg) {
03160          /* but not only message, provide "Folder" instead */
03161          keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
03162       } else {
03163          /* Otherwise if only message, leave blank */
03164          keys[3] = 1;
03165       }
03166    }
03167 
03168    /* If deleted, show "undeleted" */
03169    if (vms->deleted[vms->curmsg]) 
03170       keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
03171 
03172    /* Except "Exit" */
03173    keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
03174    bytes += adsi_set_keys(buf + bytes, keys);
03175    bytes += adsi_voice_mode(buf + bytes, 0);
03176 
03177    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03178 }
03179 
03180 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
03181 {
03182    unsigned char buf[256] = "";
03183    char buf1[256] = "", buf2[256] = "";
03184    int bytes=0;
03185    unsigned char keys[8];
03186    int x;
03187 
03188    char *newm = (vms->newmessages == 1) ? "message" : "messages";
03189    char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
03190    if (!adsi_available(chan))
03191       return;
03192    if (vms->newmessages) {
03193       snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
03194       if (vms->oldmessages) {
03195          strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
03196          snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
03197       } else {
03198          snprintf(buf2, sizeof(buf2), "%s.", newm);
03199       }
03200    } else if (vms->oldmessages) {
03201       snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
03202       snprintf(buf2, sizeof(buf2), "%s.", oldm);
03203    } else {
03204       strcpy(buf1, "You have no messages.");
03205       buf2[0] = ' ';
03206       buf2[1] = '\0';
03207    }
03208    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
03209    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
03210    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03211 
03212    for (x=0;x<6;x++)
03213       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
03214    keys[6] = 0;
03215    keys[7] = 0;
03216 
03217    /* Don't let them listen if there are none */
03218    if (vms->lastmsg < 0)
03219       keys[0] = 1;
03220    bytes += adsi_set_keys(buf + bytes, keys);
03221 
03222    bytes += adsi_voice_mode(buf + bytes, 0);
03223 
03224    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03225 }
03226 
03227 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
03228 {
03229    unsigned char buf[256] = "";
03230    char buf1[256] = "", buf2[256] = "";
03231    int bytes=0;
03232    unsigned char keys[8];
03233    int x;
03234 
03235    char *mess = (vms->lastmsg == 0) ? "message" : "messages";
03236 
03237    if (!adsi_available(chan))
03238       return;
03239 
03240    /* Original command keys */
03241    for (x=0;x<6;x++)
03242       keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
03243 
03244    keys[6] = 0;
03245    keys[7] = 0;
03246 
03247    if ((vms->lastmsg + 1) < 1)
03248       keys[0] = 0;
03249 
03250    snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
03251       strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
03252 
03253    if (vms->lastmsg + 1)
03254       snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
03255    else
03256       strcpy(buf2, "no messages.");
03257    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
03258    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
03259    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
03260    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03261    bytes += adsi_set_keys(buf + bytes, keys);
03262 
03263    bytes += adsi_voice_mode(buf + bytes, 0);
03264 
03265    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03266    
03267 }
03268 
03269 /*
03270 static void adsi_clear(struct ast_channel *chan)
03271 {
03272    char buf[256];
03273    int bytes=0;
03274    if (!adsi_available(chan))
03275       return;
03276    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03277    bytes += adsi_voice_mode(buf + bytes, 0);
03278 
03279    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03280 }
03281 */
03282 
03283 static void adsi_goodbye(struct ast_channel *chan)
03284 {
03285    unsigned char buf[256];
03286    int bytes=0;
03287 
03288    if (!adsi_available(chan))
03289       return;
03290    bytes += adsi_logo(buf + bytes);
03291    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
03292    bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
03293    bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
03294    bytes += adsi_voice_mode(buf + bytes, 0);
03295 
03296    adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
03297 }
03298 
03299 /*--- get_folder: Folder menu ---*/
03300 /* Plays "press 1 for INBOX messages" etc
03301    Should possibly be internationalized
03302  */
03303 static int get_folder(struct ast_channel *chan, int start)
03304 {
03305    int x;
03306    int d;
03307    char fn[PATH_MAX];
03308    d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
03309    if (d)
03310       return d;
03311    for (x = start; x< 5; x++) {  /* For all folders */
03312       if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
03313          return d;
03314       d = ast_play_and_wait(chan, "vm-for"); /* "for" */
03315       if (d)
03316          return d;
03317       snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
03318       d = vm_play_folder_name(chan, fn);
03319       if (d)
03320          return d;
03321       d = ast_waitfordigit(chan, 500);
03322       if (d)
03323          return d;
03324    }
03325    d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
03326    if (d)
03327       return d;
03328    d = ast_waitfordigit(chan, 4000);
03329    return d;
03330 }
03331 
03332 static int get_folder2(struct ast_channel *chan, char *fn, int start)
03333 {
03334    int res = 0;
03335    res = ast_play_and_wait(chan, fn);  /* Folder name */
03336    while (((res < '0') || (res > '9')) &&
03337          (res != '#') && (res >= 0)) {
03338       res = get_folder(chan, 0);
03339    }
03340    return res;
03341 }
03342 
03343 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
03344               char *context, signed char record_gain, long *duration)
03345 {
03346    int cmd = 0;
03347    int retries = 0;
03348    signed char zero_gain = 0;
03349 
03350    while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
03351       if (cmd)
03352          retries = 0;
03353       switch (cmd) {
03354       case '1': 
03355          /* prepend a message to the current message, update the metadata and return */
03356       {
03357          char msgfile[PATH_MAX];
03358          char textfile[PATH_MAX];
03359          int prepend_duration = 0;
03360          struct ast_config *msg_cfg;
03361          char *duration_str;
03362 
03363          make_file(msgfile, sizeof(msgfile), curdir, curmsg);
03364          strcpy(textfile, msgfile);
03365          strncat(textfile, ".txt", sizeof(textfile) - 1);
03366          *duration = 0;
03367 
03368          /* if we can't read the message metadata, stop now */
03369          if (!(msg_cfg = ast_config_load(textfile))) {
03370             cmd = 0;
03371             break;
03372          }
03373 
03374          if (record_gain)
03375             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
03376 
03377          cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
03378          if (record_gain)
03379             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
03380 
03381          
03382          if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
03383             *duration = atoi(duration_str);
03384 
03385          if (prepend_duration) {
03386             struct ast_variable *var, *prev = NULL, *varlist;
03387             struct ast_category *msg_cat;
03388 
03389             *duration += prepend_duration;
03390             msg_cat = ast_category_get(msg_cfg, "message");
03391             varlist = ast_category_detach_variables(msg_cat);
03392             for (var = varlist; var; prev = var, var = var->next) {
03393                if (!strcmp(var->name, "duration")) {
03394                   if (!prev)
03395                      varlist = var->next;
03396                   else
03397                      prev->next = var->next;
03398                   free(var);
03399                   break;
03400                }
03401             }
03402             /* need enough space for a maximum-length message duration */
03403             duration_str = alloca(12);
03404             snprintf(duration_str, 11, "%ld", *duration);
03405             if ((var = ast_variable_new("duration", duration_str))) {
03406                ast_variable_append(msg_cat, varlist);
03407                ast_variable_append(msg_cat, var);
03408                config_text_file_save(textfile, msg_cfg, "app_voicemail");
03409                STORE(curdir, vmu->mailbox, context, curmsg);
03410             }
03411          }
03412 
03413          ast_config_destroy(msg_cfg);
03414 
03415          break;
03416       }
03417       case '2': 
03418          cmd = 't';
03419          break;
03420       case '*':
03421          cmd = '*';
03422          break;
03423       default: 
03424          cmd = ast_play_and_wait(chan,"vm-forwardoptions");
03425             /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
03426          if (!cmd)
03427             cmd = ast_play_and_wait(chan,"vm-starmain");
03428             /* "press star to return to the main menu" */
03429          if (!cmd)
03430             cmd = ast_waitfordigit(chan,6000);
03431          if (!cmd)
03432             retries++;
03433          if (retries > 3)
03434             cmd = 't';
03435        }
03436    }
03437    if (cmd == 't' || cmd == 'S')
03438       cmd = 0;
03439    return cmd;
03440 }
03441 
03442 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
03443 {
03444    char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
03445    int newmsgs = 0, oldmsgs = 0;
03446 
03447    make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
03448    make_file(fn, sizeof(fn), todir, msgnum);
03449    snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
03450 
03451    /* Attach only the first format */
03452    fmt = ast_strdupa(fmt);
03453    if (fmt) {
03454       stringp = fmt;
03455       strsep(&stringp, "|");
03456 
03457       if (!ast_strlen_zero(vmu->email)) {
03458          int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
03459          char *myserveremail = serveremail;
03460          attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
03461          if (!ast_strlen_zero(vmu->serveremail))
03462             myserveremail = vmu->serveremail;
03463          sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
03464       }
03465 
03466       if (!ast_strlen_zero(vmu->pager)) {
03467          char *myserveremail = serveremail;
03468          if (!ast_strlen_zero(vmu->serveremail))
03469             myserveremail = vmu->serveremail;
03470          sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu);
03471       }
03472    } else {
03473       ast_log(LOG_ERROR, "Out of memory\n");
03474    }
03475 
03476    if (ast_test_flag(vmu, VM_DELETE)) {
03477       DELETE(todir, msgnum, fn);
03478    }
03479 
03480    /* Leave voicemail for someone */
03481    if (ast_app_has_voicemail(ext_context, NULL)) {
03482       ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
03483    }
03484    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);
03485    run_externnotify(vmu->context, vmu->mailbox);
03486    return 0;
03487 }
03488 
03489 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender,
03490             char *fmt, int flag, signed char record_gain)
03491 {
03492    char username[70]="";
03493    int res = 0, cmd = 0;
03494    struct ast_vm_user *receiver = NULL, *extensions = NULL, *vmtmp = NULL, *vmfree;
03495    char *stringp, *s;
03496    int saved_messages = 0, found = 0;
03497    int valid_extensions = 0;
03498    
03499    while (!res && !valid_extensions) {
03500       int use_directory = 0;
03501       if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
03502          int done = 0;
03503          int retries = 0;
03504          cmd=0;
03505          while((cmd >= 0) && !done ){
03506             if (cmd)
03507                retries = 0;
03508             switch (cmd) {
03509             case '1': 
03510                use_directory = 0;
03511                done = 1;
03512                break;
03513             case '2': 
03514                use_directory = 1;
03515                done=1;
03516                break;
03517             case '*': 
03518                cmd = 't';
03519                done = 1;
03520                break;
03521             default: 
03522                /* Press 1 to enter an extension press 2 to use the directory */
03523                cmd = ast_play_and_wait(chan,"vm-forward");
03524                if (!cmd)
03525                   cmd = ast_waitfordigit(chan,3000);
03526                if (!cmd)
03527                   retries++;
03528                if (retries > 3)
03529                {
03530                   cmd = 't';
03531                   done = 1;
03532                }
03533                
03534              }
03535          }
03536          if( cmd<0 || cmd=='t' )
03537             break;
03538       }
03539       
03540       if (use_directory) {
03541          /* use app_directory */
03542          
03543          char old_context[sizeof(chan->context)];
03544          char old_exten[sizeof(chan->exten)];
03545          int old_priority;
03546          struct ast_app* app;
03547 
03548          
03549          app = pbx_findapp("Directory");
03550          if (app) {
03551             /* make backup copies */
03552             char vmcontext[256];
03553             memcpy(old_context, chan->context, sizeof(chan->context));
03554             memcpy(old_exten, chan->exten, sizeof(chan->exten));
03555             old_priority = chan->priority;
03556             
03557             /* call the the Directory, changes the channel */
03558             sprintf(vmcontext, "%s||v", context ? context : "default");
03559             res = pbx_exec(chan, app, vmcontext, 1);
03560             
03561             ast_copy_string(username, chan->exten, sizeof(username));
03562             
03563             /* restore the old context, exten, and priority */
03564             memcpy(chan->context, old_context, sizeof(chan->context));
03565             memcpy(chan->exten, old_exten, sizeof(chan->exten));
03566             chan->priority = old_priority;
03567             
03568          } else {
03569             ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
03570             ast_clear_flag((&globalflags), VM_DIRECFORWARD);   
03571          }
03572       } else   {
03573          /* Ask for an extension */
03574          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
03575          if (res)
03576             break;
03577          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
03578             break;
03579       }
03580       
03581       /* start all over if no username */
03582       if (ast_strlen_zero(username))
03583          continue;
03584       stringp = username;
03585       s = strsep(&stringp, "*");
03586       /* start optimistic */
03587       valid_extensions = 1;
03588       while (s) {
03589          /* Don't forward to ourselves.  find_user is going to malloc since we have a NULL as first argument */
03590          if (strcmp(s,sender->mailbox) && (receiver = find_user(NULL, context, s))) {
03591             if (!extensions)
03592                vmtmp = extensions = receiver;
03593             else {
03594                vmtmp->next = receiver;
03595                vmtmp = receiver;
03596             }
03597             found++;
03598          } else {
03599             valid_extensions = 0;
03600             break;
03601          }
03602          s = strsep(&stringp, "*");
03603       }
03604       /* break from the loop of reading the extensions */
03605       if (valid_extensions)
03606          break;
03607       /* "I am sorry, that's not a valid extension.  Please try again." */
03608       res = ast_play_and_wait(chan, "pbx-invalid");
03609    }
03610    /* check if we're clear to proceed */
03611    if (!extensions || !valid_extensions)
03612       return res;
03613    vmtmp = extensions;
03614    if (flag==1) {
03615       struct leave_vm_options leave_options;
03616       char mailbox[AST_MAX_EXTENSION * 2 + 2];
03617       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
03618 
03619       /* Send VoiceMail */
03620       memset(&leave_options, 0, sizeof(leave_options));
03621       leave_options.record_gain = record_gain;
03622       cmd = leave_voicemail(chan, mailbox, &leave_options);
03623    } else {
03624       /* Forward VoiceMail */
03625       long duration = 0;
03626       RETRIEVE(dir, curmsg);
03627       cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context, record_gain, &duration);
03628       if (!cmd) {
03629          while (!res && vmtmp) {
03630             copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir);
03631    
03632             saved_messages++;
03633             vmfree = vmtmp;
03634             vmtmp = vmtmp->next;
03635             free_user(vmfree);
03636          }
03637          extensions = NULL;
03638          if (saved_messages > 0) {
03639             /* give confirmation that the message was saved */
03640             /* commented out since we can't forward batches yet
03641             if (saved_messages == 1)
03642                res = ast_play_and_wait(chan, "vm-message");
03643             else
03644                res = ast_play_and_wait(chan, "vm-messages");
03645             if (!res)
03646                res = ast_play_and_wait(chan, "vm-saved"); */
03647             res = ast_play_and_wait(chan, "vm-msgsaved");
03648          }  
03649       }
03650    }
03651 
03652    /* If anything failed above, we still have this list to free */
03653    while (extensions) {
03654       vmfree = extensions;
03655       extensions = extensions->next;
03656       free_user(vmfree);
03657    }
03658    return res ? res : cmd;
03659 }
03660 
03661 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
03662 {
03663    int res;
03664    if ((res = ast_streamfile(chan, file, chan->language))) 
03665       ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
03666    if (!res)
03667       res = ast_waitstream(chan, AST_DIGIT_ANY);
03668    return res;
03669 }
03670 
03671 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
03672 {
03673    return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
03674 }
03675 
03676 static int play_message_category(struct ast_channel *chan, char *category)
03677 {
03678    int res = 0;
03679 
03680    if (!ast_strlen_zero(category))
03681       res = ast_play_and_wait(chan, category);
03682 
03683    if (res) {
03684       ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
03685       res = 0;
03686    }
03687 
03688    return res;
03689 }
03690 
03691 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
03692 {
03693    int res = 0;
03694    struct vm_zone *the_zone = NULL;
03695    time_t t;
03696    long tin;
03697 
03698    if (sscanf(origtime,"%30ld",&tin) < 1) {
03699       ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
03700       return 0;
03701    }
03702    t = tin;
03703 
03704    /* Does this user have a timezone specified? */
03705    if (!ast_strlen_zero(vmu->zonetag)) {
03706       /* Find the zone in the list */
03707       struct vm_zone *z;
03708       z = zones;
03709       while (z) {
03710          if (!strcmp(z->name, vmu->zonetag)) {
03711             the_zone = z;
03712             break;
03713          }
03714          z = z->next;
03715       }
03716    }
03717 
03718 /* No internal variable parsing for now, so we'll comment it out for the time being */
03719 #if 0
03720    /* Set the DIFF_* variables */
03721    localtime_r(&t, &time_now);
03722    tv_now = ast_tvnow();
03723    tnow = tv_now.tv_sec;
03724    localtime_r(&tnow,&time_then);
03725 
03726    /* Day difference */
03727    if (time_now.tm_year == time_then.tm_year)
03728       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
03729    else
03730       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
03731    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
03732 
03733    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
03734 #endif
03735    if (the_zone)
03736       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
03737        else if(!strcasecmp(chan->language,"se"))       /* SWEDISH syntax */
03738                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
03739        else if(!strcasecmp(chan->language,"no"))       /* NORWEGIAN syntax */
03740                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
03741    else if(!strcasecmp(chan->language,"de")) /* GERMAN syntax */
03742       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
03743    else if (!strcasecmp(chan->language,"nl"))   /* DUTCH syntax */
03744       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
03745    else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
03746       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);
03747    else if (!strcasecmp(chan->language,"gr"))
03748       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
03749    else
03750       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
03751 #if 0
03752    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
03753 #endif
03754    return res;
03755 }
03756 
03757 
03758 
03759 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
03760 {
03761    int res = 0;
03762    int i;
03763    char *callerid, *name;
03764    char prefile[PATH_MAX] = "";
03765    
03766 
03767    /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
03768    /* 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 */
03769    if ((cid == NULL)||(context == NULL))
03770       return res;
03771 
03772    /* Strip off caller ID number from name */
03773    ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
03774    ast_callerid_parse(cid, &name, &callerid);
03775    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
03776       /* Check for internal contexts and only */
03777       /* say extension when the call didn't come from an internal context in the list */
03778       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
03779          ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
03780          if ((strcmp(cidinternalcontexts[i], context) == 0))
03781             break;
03782       }
03783       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
03784          if (!res) {
03785             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
03786             if (!ast_strlen_zero(prefile)) {
03787             /* See if we can find a recorded name for this person instead of their extension number */
03788                if (ast_fileexists(prefile, NULL, NULL) > 0) {
03789                   if (option_verbose > 2)
03790                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
03791                   if (!callback)
03792                      res = wait_file2(chan, vms, "vm-from");
03793                   res = ast_streamfile(chan, prefile, chan->language) > -1;
03794                   res = ast_waitstream(chan, "");
03795                } else {
03796                   if (option_verbose > 2)
03797                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
03798                   /* BB: Say "from extension" as one saying to sound smoother */
03799                   if (!callback)
03800                      res = wait_file2(chan, vms, "vm-from-extension");
03801                   res = ast_say_digit_str(chan, callerid, "", chan->language);
03802                }
03803             }
03804          }
03805       }
03806 
03807       else if (!res){
03808          ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
03809          /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
03810          if (!callback)
03811             res = wait_file2(chan, vms, "vm-from-phonenumber");
03812          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
03813       }
03814    } else {
03815       /* Number unknown */
03816       ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
03817       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
03818       res = wait_file2(chan, vms, "vm-unknown-caller");
03819    }
03820    return res;
03821 }
03822 
03823 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
03824 {
03825    int res = 0;
03826    int durationm;
03827    int durations;
03828    /* Verify that we have a duration for the message */
03829    if((duration == NULL))
03830       return res;
03831 
03832    /* Convert from seconds to minutes */
03833    durations=atoi(duration);
03834    durationm=(durations / 60);
03835 
03836    ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
03837 
03838    if((!res)&&(durationm>=minduration)) {
03839       res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
03840       res = wait_file2(chan, vms, "vm-minutes");
03841    }
03842    return res;
03843 }
03844 
03845 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
03846 {
03847    int res = 0;
03848    char filename[PATH_MAX], *origtime, *cid, *context, *duration;
03849    char *category;
03850    struct ast_config *msg_cfg;
03851 
03852    vms->starting = 0; 
03853    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
03854    adsi_message(chan, vms);
03855    if (!vms->curmsg)
03856       res = wait_file2(chan, vms, "vm-first");  /* "First" */
03857    else if (vms->curmsg == vms->lastmsg)
03858       res = wait_file2(chan, vms, "vm-last");      /* "last" */
03859    if (!res) {
03860                if (!strcasecmp(chan->language, "se")) {             /* SWEDISH syntax */
03861                        res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
03862                }
03863                else {
03864                        res = wait_file2(chan, vms, "vm-message");      /* "message" */
03865                }
03866       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
03867          if (!res)
03868             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03869       }
03870    }
03871 
03872    /* Retrieve info from VM attribute file */
03873    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
03874    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
03875    RETRIEVE(vms->curdir, vms->curmsg);
03876    msg_cfg = ast_config_load(filename);
03877    if (!msg_cfg) {
03878       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
03879       return 0;
03880    }
03881                                                                            
03882    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
03883       ast_log(LOG_WARNING, "No origtime?!\n");
03884       DISPOSE(vms->curdir, vms->curmsg);
03885       ast_config_destroy(msg_cfg);
03886       return 0;
03887    }
03888 
03889    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
03890    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
03891    category = ast_variable_retrieve(msg_cfg, "message", "category");
03892 
03893    context = ast_variable_retrieve(msg_cfg, "message", "context");
03894    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
03895       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
03896 
03897    if (!res)
03898       res = play_message_category(chan, category);
03899    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
03900       res = play_message_datetime(chan, vmu, origtime, filename);
03901    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
03902       res = play_message_callerid(chan, vms, cid, context, 0);
03903         if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
03904                 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
03905    /* Allow pressing '1' to skip envelope / callerid */
03906    if (res == '1')
03907       res = 0;
03908    ast_config_destroy(msg_cfg);
03909 
03910    if (!res) {
03911       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
03912       vms->heard[vms->curmsg] = 1;
03913       res = wait_file(chan, vms, vms->fn);
03914    }
03915    DISPOSE(vms->curdir, vms->curmsg);
03916    return res;
03917 }
03918 
03919 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
03920 {
03921    int res = 0;
03922    int count_msg, last_msg;
03923 
03924    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
03925    
03926    /* Rename the member vmbox HERE so that we don't try to return before
03927     * we know what's going on.
03928     */
03929    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
03930    
03931    /* Faster to make the directory than to check if it exists. */
03932    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
03933 
03934    count_msg = count_messages(vmu, vms->curdir);
03935    if (count_msg < 0)
03936       return count_msg;
03937    else
03938       vms->lastmsg = count_msg - 1;
03939 
03940    /*
03941    The following test is needed in case sequencing gets messed up.
03942    There appears to be more than one way to mess up sequence, so
03943    we will not try to find all of the root causes--just fix it when
03944    detected.
03945    */
03946 
03947    last_msg = last_message_index(vmu, vms->curdir);
03948    if (last_msg < 0)
03949       return last_msg;
03950    else if(vms->lastmsg != last_msg)
03951    {
03952       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
03953       res = resequence_mailbox(vmu, vms->curdir);
03954       if (res)
03955          return res;
03956    }
03957 
03958    return 0;
03959 }
03960 
03961 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
03962 {
03963    int x, nummsg;
03964    int res = 0;
03965 
03966    if (vms->lastmsg <= -1)
03967       goto done;
03968 
03969    /* Get the deleted messages fixed */ 
03970    if (vm_lock_path(vms->curdir))
03971       return ERROR_LOCK_PATH;
03972    
03973    vms->curmsg = -1; 
03974    for (x = 0; x < vmu->maxmsg; x++) { 
03975       if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
03976          /* Save this message.  It's not in INBOX or hasn't been heard */ 
03977          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
03978          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
03979             break;
03980          vms->curmsg++; 
03981          make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
03982          if (strcmp(vms->fn, vms->fn2)) { 
03983             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
03984          } 
03985       } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
03986          /* Move to old folder before deleting */ 
03987          res = save_to_folder(vmu, vms->curdir, x, vmu->context, vms->username, 1);
03988          if (res == ERROR_LOCK_PATH) {
03989             /* If save failed do not delete the message */
03990             vms->deleted[x] = 0;
03991             vms->heard[x] = 0;
03992             --x;
03993          } 
03994       } 
03995    } 
03996 
03997    /* Delete ALL remaining messages */
03998    nummsg = x - 1;
03999    for (x = vms->curmsg + 1; x <= nummsg; x++) {
04000       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
04001       if (EXISTS(vms->curdir, x, vms->fn, NULL))
04002          DELETE(vms->curdir, x, vms->fn);
04003    }
04004    ast_unlock_path(vms->curdir);
04005 
04006 done:
04007    if (vms->deleted)
04008       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
04009    if (vms->heard)
04010       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
04011 
04012    return 0;
04013 }
04014 
04015 /* In Greek even though we CAN use a syntax like "friends messages"
04016  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
04017  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
04018  * syntax for the above three categories which is more elegant. 
04019 */
04020 
04021 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
04022 {
04023    int cmd;
04024    char *buf;
04025 
04026    buf = alloca(strlen(mbox)+2); 
04027    strcpy(buf, mbox);
04028    strcat(buf,"s");
04029 
04030    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
04031       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
04032       if (cmd)
04033       return cmd;
04034       return ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
04035    } else {
04036       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
04037       if (cmd)
04038          return cmd;
04039       return ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
04040    }
04041 }
04042 
04043 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
04044 {
04045    int cmd;
04046 
04047    if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Italian, Spanish, French or Portuguese syntax */
04048       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
04049       if (cmd)
04050          return cmd;
04051       return ast_play_and_wait(chan, mbox);
04052    } else if (!strcasecmp(chan->language, "gr")){
04053       return vm_play_folder_name_gr(chan, mbox);
04054    } else {  /* Default English */
04055       cmd = ast_play_and_wait(chan, mbox);
04056       if (cmd)
04057          return cmd;
04058       return ast_play_and_wait(chan, "vm-messages"); /* "messages */
04059    }
04060 }
04061 
04062  /* GREEK SYNTAX 
04063    In greek the plural for old/new is
04064    different so we need the following files   
04065    We also need vm-denExeteMynhmata because 
04066    this syntax is different.
04067    
04068    -> vm-Olds.wav : "Palia"
04069    -> vm-INBOXs.wav : "Nea"
04070    -> vm-denExeteMynhmata : "den exete mynhmata"
04071  */
04072                
04073    
04074 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
04075 {
04076    int res = 0;
04077 
04078    if (vms->newmessages) {
04079       res = ast_play_and_wait(chan, "vm-youhave");
04080       if (!res) 
04081          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
04082       if (!res) {
04083          if ((vms->newmessages == 1)) {
04084             res = ast_play_and_wait(chan, "vm-INBOX");
04085             if (!res)
04086                res = ast_play_and_wait(chan, "vm-message");
04087          } else {
04088             res = ast_play_and_wait(chan, "vm-INBOXs");
04089             if (!res)
04090                res = ast_play_and_wait(chan, "vm-messages");
04091          }
04092       }    
04093    } else if (vms->oldmessages){
04094       res = ast_play_and_wait(chan, "vm-youhave");
04095       if (!res)
04096          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
04097       if ((vms->oldmessages == 1)){
04098          res = ast_play_and_wait(chan, "vm-Old");
04099          if (!res)
04100             res = ast_play_and_wait(chan, "vm-message");
04101       } else {
04102          res = ast_play_and_wait(chan, "vm-Olds");
04103          if (!res)
04104             res = ast_play_and_wait(chan, "vm-messages");
04105       }
04106     } else if (!vms->oldmessages && !vms->newmessages) 
04107          res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
04108     return res;
04109 }
04110    
04111 /* Default English syntax */
04112 static int vm_intro_en(struct ast_channel *chan,struct vm_state *vms)
04113 {
04114    /* Introduce messages they have */
04115    int res;
04116    res = ast_play_and_wait(chan, "vm-youhave");
04117    if (!res) {
04118       if (vms->newmessages) {
04119          res = say_and_wait(chan, vms->newmessages, chan->language);
04120          if (!res)
04121             res = ast_play_and_wait(chan, "vm-INBOX");
04122          if (vms->oldmessages && !res)
04123             res = ast_play_and_wait(chan, "vm-and");
04124          else if (!res) {
04125             if ((vms->newmessages == 1))
04126                res = ast_play_and_wait(chan, "vm-message");
04127             else
04128                res = ast_play_and_wait(chan, "vm-messages");
04129          }
04130             
04131       }
04132       if (!res && vms->oldmessages) {
04133          res = say_and_wait(chan, vms->oldmessages, chan->language);
04134          if (!res)
04135             res = ast_play_and_wait(chan, "vm-Old");
04136          if (!res) {
04137             if (vms->oldmessages == 1)
04138                res = ast_play_and_wait(chan, "vm-message");
04139             else
04140                res = ast_play_and_wait(chan, "vm-messages");
04141          }
04142       }
04143       if (!res) {
04144          if (!vms->oldmessages && !vms->newmessages) {
04145             res = ast_play_and_wait(chan, "vm-no");
04146             if (!res)
04147                res = ast_play_and_wait(chan, "vm-messages");
04148          }
04149       }
04150    }
04151    return res;
04152 }
04153 
04154 /* ITALIAN syntax */
04155 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
04156 {
04157    /* Introduce messages they have */
04158    int res;
04159    if (!vms->oldmessages && !vms->newmessages)
04160       res = ast_play_and_wait(chan, "vm-no") ||
04161          ast_play_and_wait(chan, "vm-message");
04162    else
04163       res = ast_play_and_wait(chan, "vm-youhave");
04164    if (!res && vms->newmessages) {
04165       res = (vms->newmessages == 1) ?
04166          ast_play_and_wait(chan, "digits/un") ||
04167          ast_play_and_wait(chan, "vm-nuovo") ||
04168          ast_play_and_wait(chan, "vm-message") :
04169          /* 2 or more new messages */
04170          say_and_wait(chan, vms->newmessages, chan->language) ||
04171          ast_play_and_wait(chan, "vm-nuovi") ||
04172          ast_play_and_wait(chan, "vm-messages");
04173       if (!res && vms->oldmessages)
04174          res = ast_play_and_wait(chan, "vm-and");
04175    }
04176    if (!res && vms->oldmessages) {
04177       res = (vms->oldmessages == 1) ?
04178          ast_play_and_wait(chan, "digits/un") ||
04179          ast_play_and_wait(chan, "vm-vecchio") ||
04180          ast_play_and_wait(chan, "vm-message") :
04181          /* 2 or more old messages */
04182          say_and_wait(chan, vms->oldmessages, chan->language) ||
04183          ast_play_and_wait(chan, "vm-vecchi") ||
04184          ast_play_and_wait(chan, "vm-messages");
04185    }
04186    return res;
04187 }
04188 
04189 /* SWEDISH syntax */
04190 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
04191 {
04192         /* Introduce messages they have */
04193         int res;
04194 
04195    res = ast_play_and_wait(chan, "vm-youhave");
04196    if (res)
04197       return res;
04198 
04199         if (!vms->oldmessages && !vms->newmessages) {
04200       res = ast_play_and_wait(chan, "vm-no");
04201       res = res ? res : ast_play_and_wait(chan, "vm-messages");
04202       return res;
04203         }
04204 
04205    if (vms->newmessages) {
04206       if ((vms->newmessages == 1)) {
04207          res = ast_play_and_wait(chan, "digits/ett");
04208          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
04209          res = res ? res : ast_play_and_wait(chan, "vm-message");
04210       } else {
04211          res = say_and_wait(chan, vms->newmessages, chan->language);
04212          res = res ? res : ast_play_and_wait(chan, "vm-nya");
04213          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04214       }
04215       if (!res && vms->oldmessages)
04216          res = ast_play_and_wait(chan, "vm-and");
04217    }
04218    if (!res && vms->oldmessages) {
04219       if (vms->oldmessages == 1) {
04220          res = ast_play_and_wait(chan, "digits/ett");
04221          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
04222          res = res ? res : ast_play_and_wait(chan, "vm-message");
04223       } else {
04224          res = say_and_wait(chan, vms->oldmessages, chan->language);
04225          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
04226          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04227       }
04228    }
04229 
04230    return res;
04231 }
04232 
04233 /* NORWEGIAN syntax */
04234 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
04235 {
04236         /* Introduce messages they have */
04237         int res;
04238 
04239    res = ast_play_and_wait(chan, "vm-youhave");
04240    if (res)
04241       return res;
04242 
04243         if (!vms->oldmessages && !vms->newmessages) {
04244       res = ast_play_and_wait(chan, "vm-no");
04245       res = res ? res : ast_play_and_wait(chan, "vm-messages");
04246       return res;
04247         }
04248 
04249    if (vms->newmessages) {
04250       if ((vms->newmessages == 1)) {
04251          res = ast_play_and_wait(chan, "digits/1");
04252          res = res ? res : ast_play_and_wait(chan, "vm-ny");
04253          res = res ? res : ast_play_and_wait(chan, "vm-message");
04254       } else {
04255          res = say_and_wait(chan, vms->newmessages, chan->language);
04256          res = res ? res : ast_play_and_wait(chan, "vm-nye");
04257          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04258       }
04259       if (!res && vms->oldmessages)
04260          res = ast_play_and_wait(chan, "vm-and");
04261    }
04262    if (!res && vms->oldmessages) {
04263       if (vms->oldmessages == 1) {
04264          res = ast_play_and_wait(chan, "digits/1");
04265          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
04266          res = res ? res : ast_play_and_wait(chan, "vm-message");
04267       } else {
04268          res = say_and_wait(chan, vms->oldmessages, chan->language);
04269          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
04270          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04271       }
04272    }
04273 
04274    return res;
04275 }
04276 
04277 /* GERMAN syntax */
04278 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
04279 {
04280    /* Introduce messages they have */
04281    int res;
04282    res = ast_play_and_wait(chan, "vm-youhave");
04283    if (!res) {
04284       if (vms->newmessages) {
04285          if ((vms->newmessages == 1))
04286             res = ast_play_and_wait(chan, "digits/1F");
04287          else
04288             res = say_and_wait(chan, vms->newmessages, chan->language);
04289          if (!res)
04290             res = ast_play_and_wait(chan, "vm-INBOX");
04291          if (vms->oldmessages && !res)
04292             res = ast_play_and_wait(chan, "vm-and");
04293          else if (!res) {
04294             if ((vms->newmessages == 1))
04295                res = ast_play_and_wait(chan, "vm-message");
04296             else
04297                res = ast_play_and_wait(chan, "vm-messages");
04298          }
04299             
04300       }
04301       if (!res && vms->oldmessages) {
04302          if (vms->oldmessages == 1)
04303             res = ast_play_and_wait(chan, "digits/1F");
04304          else
04305             res = say_and_wait(chan, vms->oldmessages, chan->language);
04306          if (!res)
04307             res = ast_play_and_wait(chan, "vm-Old");
04308          if (!res) {
04309             if (vms->oldmessages == 1)
04310                res = ast_play_and_wait(chan, "vm-message");
04311             else
04312                res = ast_play_and_wait(chan, "vm-messages");
04313          }
04314       }
04315       if (!res) {
04316          if (!vms->oldmessages && !vms->newmessages) {
04317             res = ast_play_and_wait(chan, "vm-no");
04318             if (!res)
04319                res = ast_play_and_wait(chan, "vm-messages");
04320          }
04321       }
04322    }
04323    return res;
04324 }
04325 
04326 /* SPANISH syntax */
04327 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
04328 {
04329    /* Introduce messages they have */
04330    int res;
04331    if (!vms->oldmessages && !vms->newmessages) {
04332       res = ast_play_and_wait(chan, "vm-youhaveno");
04333       if (!res)
04334          res = ast_play_and_wait(chan, "vm-messages");
04335    } else {
04336       res = ast_play_and_wait(chan, "vm-youhave");
04337    }
04338    if (!res) {
04339       if (vms->newmessages) {
04340          if (!res) {
04341             if ((vms->newmessages == 1)) {
04342                res = ast_play_and_wait(chan, "digits/1M");
04343                if (!res)
04344                   res = ast_play_and_wait(chan, "vm-message");
04345                if (!res)
04346                   res = ast_play_and_wait(chan, "vm-INBOXs");
04347             } else {
04348                res = say_and_wait(chan, vms->newmessages, chan->language);
04349                if (!res)
04350                   res = ast_play_and_wait(chan, "vm-messages");
04351                if (!res)
04352                   res = ast_play_and_wait(chan, "vm-INBOX");
04353             }
04354          }
04355          if (vms->oldmessages && !res)
04356             res = ast_play_and_wait(chan, "vm-and");
04357       }
04358       if (vms->oldmessages) {
04359          if (!res) {
04360             if (vms->oldmessages == 1) {
04361                res = ast_play_and_wait(chan, "digits/1M");
04362                if (!res)
04363                   res = ast_play_and_wait(chan, "vm-message");
04364                if (!res)
04365                   res = ast_play_and_wait(chan, "vm-Olds");
04366             } else {
04367                res = say_and_wait(chan, vms->oldmessages, chan->language);
04368                if (!res)
04369                   res = ast_play_and_wait(chan, "vm-messages");
04370                if (!res)
04371                   res = ast_play_and_wait(chan, "vm-Old");
04372             }
04373          }
04374       }
04375    }
04376 return res;
04377 }
04378 
04379 /* FRENCH syntax */
04380 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
04381 {
04382    /* Introduce messages they have */
04383    int res;
04384    res = ast_play_and_wait(chan, "vm-youhave");
04385    if (!res) {
04386       if (vms->newmessages) {
04387          res = say_and_wait(chan, vms->newmessages, chan->language);
04388          if (!res)
04389             res = ast_play_and_wait(chan, "vm-INBOX");
04390          if (vms->oldmessages && !res)
04391             res = ast_play_and_wait(chan, "vm-and");
04392          else if (!res) {
04393             if ((vms->newmessages == 1))
04394                res = ast_play_and_wait(chan, "vm-message");
04395             else
04396                res = ast_play_and_wait(chan, "vm-messages");
04397          }
04398             
04399       }
04400       if (!res && vms->oldmessages) {
04401          res = say_and_wait(chan, vms->oldmessages, chan->language);
04402          if (!res) {
04403             if (vms->oldmessages == 1)
04404                res = ast_play_and_wait(chan, "vm-message");
04405             else
04406                res = ast_play_and_wait(chan, "vm-messages");
04407          }
04408          if (!res)
04409             res = ast_play_and_wait(chan, "vm-Old");
04410       }
04411       if (!res) {
04412          if (!vms->oldmessages && !vms->newmessages) {
04413             res = ast_play_and_wait(chan, "vm-no");
04414             if (!res)
04415                res = ast_play_and_wait(chan, "vm-messages");
04416          }
04417       }
04418    }
04419    return res;
04420 }
04421 
04422 /* DUTCH syntax */
04423 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
04424 {
04425    /* Introduce messages they have */
04426    int res;
04427    res = ast_play_and_wait(chan, "vm-youhave");
04428    if (!res) {
04429       if (vms->newmessages) {
04430          res = say_and_wait(chan, vms->newmessages, chan->language);
04431          if (!res) {
04432             if (vms->newmessages == 1)
04433                res = ast_play_and_wait(chan, "vm-INBOXs");
04434             else
04435                res = ast_play_and_wait(chan, "vm-INBOX");
04436          }
04437          if (vms->oldmessages && !res)
04438             res = ast_play_and_wait(chan, "vm-and");
04439          else if (!res) {
04440             if ((vms->newmessages == 1))
04441                res = ast_play_and_wait(chan, "vm-message");
04442             else
04443                res = ast_play_and_wait(chan, "vm-messages");
04444          }
04445             
04446       }
04447       if (!res && vms->oldmessages) {
04448          res = say_and_wait(chan, vms->oldmessages, chan->language);
04449          if (!res) {
04450             if (vms->oldmessages == 1)
04451                res = ast_play_and_wait(chan, "vm-Olds");
04452             else
04453                res = ast_play_and_wait(chan, "vm-Old");
04454          }
04455          if (!res) {
04456             if (vms->oldmessages == 1)
04457                res = ast_play_and_wait(chan, "vm-message");
04458             else
04459                res = ast_play_and_wait(chan, "vm-messages");
04460          }
04461       }
04462       if (!res) {
04463          if (!vms->oldmessages && !vms->newmessages) {
04464             res = ast_play_and_wait(chan, "vm-no");
04465             if (!res)
04466                res = ast_play_and_wait(chan, "vm-messages");
04467          }
04468       }
04469    }
04470    return res;
04471 }
04472 
04473 /* PORTUGUESE syntax */
04474 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
04475 {
04476    /* Introduce messages they have */
04477    int res;
04478    res = ast_play_and_wait(chan, "vm-youhave");
04479    if (!res) {
04480       if (vms->newmessages) {
04481          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
04482          if (!res) {
04483             if ((vms->newmessages == 1)) {
04484                res = ast_play_and_wait(chan, "vm-message");
04485                if (!res)
04486                   res = ast_play_and_wait(chan, "vm-INBOXs");
04487             } else {
04488                res = ast_play_and_wait(chan, "vm-messages");
04489                if (!res)
04490                   res = ast_play_and_wait(chan, "vm-INBOX");
04491             }
04492          }
04493          if (vms->oldmessages && !res)
04494             res = ast_play_and_wait(chan, "vm-and");
04495       }
04496       if (!res && vms->oldmessages) {
04497          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
04498          if (!res) {
04499             if (vms->oldmessages == 1) {
04500                res = ast_play_and_wait(chan, "vm-message");
04501                if (!res)
04502                   res = ast_play_and_wait(chan, "vm-Olds");
04503             } else {
04504                res = ast_play_and_wait(chan, "vm-messages");
04505                if (!res)
04506                   res = ast_play_and_wait(chan, "vm-Old");
04507             }
04508          }
04509       }
04510       if (!res) {
04511          if (!vms->oldmessages && !vms->newmessages) {
04512             res = ast_play_and_wait(chan, "vm-no");
04513             if (!res)
04514                res = ast_play_and_wait(chan, "vm-messages");
04515          }
04516       }
04517    }
04518    return res;
04519 }
04520 
04521 
04522 /* CZECH syntax */
04523 /* in czech there must be declension of word new and message
04524  * czech    : english      : czech  : english
04525  * --------------------------------------------------------
04526  * vm-youhave  : you have 
04527  * vm-novou    : one new      : vm-zpravu    : message
04528  * vm-nove  : 2-4 new      : vm-zpravy    : messages
04529  * vm-novych   : 5-infinite new   : vm-zprav    : messages
04530  * vm-starou   : one old
04531  * vm-stare : 2-4 old 
04532  * vm-starych  : 5-infinite old
04533  * jednu : one - falling 4. 
04534  * vm-no : no  ( no messages )
04535  */
04536 
04537 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
04538 {
04539    int res;
04540    res = ast_play_and_wait(chan, "vm-youhave");
04541    if (!res) {
04542       if (vms->newmessages) {
04543          if (vms->newmessages == 1) {
04544             res = ast_play_and_wait(chan, "digits/jednu");
04545          } else {
04546             res = say_and_wait(chan, vms->newmessages, chan->language);
04547          }
04548          if (!res) {
04549             if ((vms->newmessages == 1))
04550                res = ast_play_and_wait(chan, "vm-novou");
04551             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
04552                res = ast_play_and_wait(chan, "vm-nove");
04553             if (vms->newmessages > 4)
04554                res = ast_play_and_wait(chan, "vm-novych");
04555          }
04556          if (vms->oldmessages && !res)
04557             res = ast_play_and_wait(chan, "vm-and");
04558          else if (!res) {
04559             if ((vms->newmessages == 1))
04560                res = ast_play_and_wait(chan, "vm-zpravu");
04561             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
04562                res = ast_play_and_wait(chan, "vm-zpravy");
04563             if (vms->newmessages > 4)
04564                res = ast_play_and_wait(chan, "vm-zprav");
04565          }
04566       }
04567       if (!res && vms->oldmessages) {
04568          res = say_and_wait(chan, vms->oldmessages, chan->language);
04569          if (!res) {
04570             if ((vms->oldmessages == 1))
04571                res = ast_play_and_wait(chan, "vm-starou");
04572             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
04573                res = ast_play_and_wait(chan, "vm-stare");
04574             if (vms->oldmessages > 4)
04575                res = ast_play_and_wait(chan, "vm-starych");
04576          }
04577          if (!res) {
04578             if ((vms->oldmessages == 1))
04579                res = ast_play_and_wait(chan, "vm-zpravu");
04580             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
04581                res = ast_play_and_wait(chan, "vm-zpravy");
04582             if (vms->oldmessages > 4)
04583                res = ast_play_and_wait(chan, "vm-zprav");
04584          }
04585       }
04586       if (!res) {
04587          if (!vms->oldmessages && !vms->newmessages) {
04588             res = ast_play_and_wait(chan, "vm-no");
04589             if (!res)
04590                res = ast_play_and_wait(chan, "vm-zpravy");
04591          }
04592       }
04593    }
04594    return res;
04595 }
04596 
04597 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
04598 {
04599    /* Play voicemail intro - syntax is different for different languages */
04600    if (!strcasecmp(chan->language, "de")) {  /* GERMAN syntax */
04601       return vm_intro_de(chan, vms);
04602    } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
04603       return vm_intro_es(chan, vms);
04604    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
04605       return vm_intro_it(chan, vms);
04606    } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
04607       return vm_intro_fr(chan, vms);
04608    } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
04609       return vm_intro_nl(chan, vms);
04610    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
04611       return vm_intro_pt(chan, vms);
04612    } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
04613       return vm_intro_cz(chan, vms);
04614    } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
04615       return vm_intro_gr(chan, vms);
04616    } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
04617       return vm_intro_se(chan, vms);
04618    } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
04619       return vm_intro_no(chan, vms);
04620    } else {             /* Default to ENGLISH */
04621       return vm_intro_en(chan, vms);
04622    }
04623 }
04624 
04625 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
04626 {
04627    int res = 0;
04628    /* Play instructions and wait for new command */
04629    while (!res) {
04630       if (vms->starting) {
04631          if (vms->lastmsg > -1) {
04632             res = ast_play_and_wait(chan, "vm-onefor");
04633             if (!res)
04634                res = vm_play_folder_name(chan, vms->vmbox);
04635          }
04636          if (!res)
04637             res = ast_play_and_wait(chan, "vm-opts");
04638       } else {
04639          if (vms->curmsg)
04640             res = ast_play_and_wait(chan, "vm-prev");
04641          if (!res && !skipadvanced)
04642             res = ast_play_and_wait(chan, "vm-advopts");
04643          if (!res)
04644             res = ast_play_and_wait(chan, "vm-repeat");
04645          if (!res && (vms->curmsg != vms->lastmsg))
04646             res = ast_play_and_wait(chan, "vm-next");
04647          if (!res) {
04648             if (!vms->deleted[vms->curmsg])
04649                res = ast_play_and_wait(chan, "vm-delete");
04650             else
04651                res = ast_play_and_wait(chan, "vm-undelete");
04652             if (!res)
04653                res = ast_play_and_wait(chan, "vm-toforward");
04654             if (!res)
04655                res = ast_play_and_wait(chan, "vm-savemessage");
04656          }
04657       }
04658       if (!res)
04659          res = ast_play_and_wait(chan, "vm-helpexit");
04660       if (!res)
04661          res = ast_waitfordigit(chan, 6000);
04662       if (!res) {
04663          vms->repeats++;
04664          if (vms->repeats > 2) {
04665             res = 't';
04666          }
04667       }
04668    }
04669    return res;
04670 }
04671 
04672 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
04673 {
04674    int cmd = 0;
04675    int duration = 0;
04676    int tries = 0;
04677    char newpassword[80] = "";
04678    char newpassword2[80] = "";
04679    char prefile[PATH_MAX] = "";
04680    unsigned char buf[256];
04681    int bytes=0;
04682 
04683    if (adsi_available(chan)) {
04684       bytes += adsi_logo(buf + bytes);
04685       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
04686       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
04687       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04688       bytes += adsi_voice_mode(buf + bytes, 0);
04689       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04690    }
04691 
04692    /* First, have the user change their password 
04693       so they won't get here again */
04694    for (;;) {
04695       newpassword[1] = '\0';
04696       newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
04697       if (cmd == '#')
04698          newpassword[0] = '\0';
04699       if (cmd < 0 || cmd == 't' || cmd == '#')
04700          return cmd;
04701       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
04702       if (cmd < 0 || cmd == 't' || cmd == '#')
04703          return cmd;
04704       newpassword2[1] = '\0';
04705       newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
04706       if (cmd == '#')
04707          newpassword2[0] = '\0';
04708       if (cmd < 0 || cmd == 't' || cmd == '#')
04709          return cmd;
04710       cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
04711       if (cmd < 0 || cmd == 't' || cmd == '#')
04712          return cmd;
04713       if (!strcmp(newpassword, newpassword2))
04714          break;
04715       ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
04716       cmd = ast_play_and_wait(chan, "vm-mismatch");
04717       if (++tries == 3)
04718          return -1;
04719    }
04720    if (ast_strlen_zero(ext_pass_cmd)) 
04721       vm_change_password(vmu,newpassword);
04722    else 
04723       vm_change_password_shell(vmu,newpassword);
04724    
04725    cmd = ast_play_and_wait(chan,"vm-passchanged");
04726 
04727    /* If forcename is set, have the user record their name */  
04728    if (ast_test_flag(vmu, VM_FORCENAME)) {
04729       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
04730       if (ast_fileexists(prefile, NULL, NULL) < 1) {
04731          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04732          if (cmd < 0 || cmd == 't' || cmd == '#')
04733             return cmd;
04734       }
04735    }
04736 
04737    /* If forcegreetings is set, have the user record their greetings */
04738    if (ast_test_flag(vmu, VM_FORCEGREET)) {
04739       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
04740       if (ast_fileexists(prefile, NULL, NULL) < 1) {
04741          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04742          if (cmd < 0 || cmd == 't' || cmd == '#')
04743             return cmd;
04744       }
04745 
04746       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
04747       if (ast_fileexists(prefile, NULL, NULL) < 1) {
04748          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04749          if (cmd < 0 || cmd == 't' || cmd == '#')
04750             return cmd;
04751       }
04752    }
04753 
04754    return cmd;
04755 }
04756 
04757 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
04758 {
04759    int cmd = 0;
04760    int retries = 0;
04761    int duration = 0;
04762    char newpassword[80] = "";
04763    char newpassword2[80] = "";
04764    char prefile[PATH_MAX] = "";
04765    unsigned char buf[256];
04766    int bytes=0;
04767 
04768    if (adsi_available(chan))
04769    {
04770       bytes += adsi_logo(buf + bytes);
04771       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
04772       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
04773       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04774       bytes += adsi_voice_mode(buf + bytes, 0);
04775       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04776    }
04777    while ((cmd >= 0) && (cmd != 't')) {
04778       if (cmd)
04779          retries = 0;
04780       switch (cmd) {
04781       case '1':
04782          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
04783          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04784          break;
04785       case '2': 
04786          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
04787          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04788          break;
04789       case '3': 
04790          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
04791          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04792          break;
04793       case '4': 
04794          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
04795          break;
04796       case '5':
04797          if (vmu->password[0] == '-') {
04798             cmd = ast_play_and_wait(chan, "vm-no");
04799             break;
04800          }
04801          newpassword[1] = '\0';
04802          newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
04803          if (cmd == '#')
04804             newpassword[0] = '\0';
04805          else {
04806             if (cmd < 0)
04807                break;
04808             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
04809                break;
04810             }
04811          }
04812          newpassword2[1] = '\0';
04813          newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
04814          if (cmd == '#')
04815             newpassword2[0] = '\0';
04816          else {
04817             if (cmd < 0)
04818                break;
04819 
04820             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
04821                break;
04822             }
04823          }
04824          if (strcmp(newpassword, newpassword2)) {
04825             ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
04826             cmd = ast_play_and_wait(chan, "vm-mismatch");
04827             break;
04828          }
04829          if (ast_strlen_zero(ext_pass_cmd)) 
04830             vm_change_password(vmu,newpassword);
04831          else 
04832             vm_change_password_shell(vmu,newpassword);
04833          ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
04834          cmd = ast_play_and_wait(chan,"vm-passchanged");
04835          break;
04836       case '*': 
04837          cmd = 't';
04838          break;
04839       default: 
04840          cmd = ast_play_and_wait(chan,"vm-options");
04841          if (!cmd)
04842             cmd = ast_waitfordigit(chan,6000);
04843          if (!cmd)
04844             retries++;
04845          if (retries > 3)
04846             cmd = 't';
04847        }
04848    }
04849    if (cmd == 't')
04850       cmd = 0;
04851    return cmd;
04852 }
04853 
04854 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
04855 {
04856    int res;
04857    int cmd = 0;
04858    int retries = 0;
04859    int duration = 0;
04860    char prefile[PATH_MAX] = "";
04861    unsigned char buf[256];
04862    char dest[PATH_MAX];
04863    int bytes = 0;
04864 
04865    if (adsi_available(chan))
04866    {
04867       bytes += adsi_logo(buf + bytes);
04868       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
04869       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
04870       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04871       bytes += adsi_voice_mode(buf + bytes, 0);
04872       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04873    }
04874    snprintf(prefile,sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
04875    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
04876       ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
04877       return -1;
04878    }
04879    while((cmd >= 0) && (cmd != 't')) {
04880       if (cmd)
04881          retries = 0;
04882       RETRIEVE(prefile, -1);
04883       if (ast_fileexists(prefile, NULL, NULL) > 0) {
04884          switch (cmd) {
04885          case '1':
04886             cmd = play_record_review(chan,"vm-rec-temp",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04887             break;
04888          case '2':
04889             DELETE(prefile, -1, prefile);
04890             ast_play_and_wait(chan,"vm-tempremoved");
04891             cmd = 't';  
04892             break;
04893          case '*': 
04894             cmd = 't';
04895             break;
04896          default:
04897             if (ast_fileexists(prefile, NULL, NULL) > 0) {
04898                cmd = ast_play_and_wait(chan,"vm-tempgreeting2");
04899             } else {
04900                cmd = ast_play_and_wait(chan,"vm-tempgreeting");
04901             } if (!cmd) {
04902                cmd = ast_waitfordigit(chan,6000);
04903             } if (!cmd) {
04904                retries++;
04905             } if (retries > 3) {
04906                cmd = 't';
04907             }
04908          }
04909       } else {
04910          play_record_review(chan,"vm-rec-temp",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04911          cmd = 't';  
04912       }
04913       DISPOSE(prefile, -1);
04914    }
04915    if (cmd == 't')
04916       cmd = 0;
04917    return cmd;
04918 }
04919 
04920 /* GREEK SYNTAX */
04921    
04922 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04923 {
04924    int cmd=0;
04925 
04926    if (vms->lastmsg > -1) {
04927       cmd = play_message(chan, vmu, vms);
04928    } else {
04929       cmd = ast_play_and_wait(chan, "vm-youhaveno");
04930       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
04931          if (!cmd) {
04932             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
04933             cmd = ast_play_and_wait(chan, vms->fn);
04934          }
04935          if (!cmd)
04936             cmd = ast_play_and_wait(chan, "vm-messages");
04937       } else {
04938          if (!cmd)
04939             cmd = ast_play_and_wait(chan, "vm-messages");
04940          if (!cmd) {
04941             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04942             cmd = ast_play_and_wait(chan, vms->fn);
04943          }
04944       }
04945    } 
04946    return cmd;
04947 }
04948 
04949 /* Default English syntax */
04950 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04951 {
04952    int cmd=0;
04953 
04954    if (vms->lastmsg > -1) {
04955       cmd = play_message(chan, vmu, vms);
04956    } else {
04957       cmd = ast_play_and_wait(chan, "vm-youhave");
04958       if (!cmd) 
04959          cmd = ast_play_and_wait(chan, "vm-no");
04960       if (!cmd) {
04961          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04962          cmd = ast_play_and_wait(chan, vms->fn);
04963       }
04964       if (!cmd)
04965          cmd = ast_play_and_wait(chan, "vm-messages");
04966    }
04967    return cmd;
04968 }
04969 
04970 /* ITALIAN syntax */
04971 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04972 {
04973         int cmd=0;
04974 
04975         if (vms->lastmsg > -1) {
04976                 cmd = play_message(chan, vmu, vms);
04977         } else {
04978                 cmd = ast_play_and_wait(chan, "vm-no");
04979                 if (!cmd)
04980                         cmd = ast_play_and_wait(chan, "vm-message");
04981                 if (!cmd) {
04982                         snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04983                         cmd = ast_play_and_wait(chan, vms->fn);
04984                 }
04985         }
04986         return cmd;
04987 }
04988 
04989 /* SPANISH syntax */
04990 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04991 {
04992    int cmd=0;
04993 
04994    if (vms->lastmsg > -1) {
04995       cmd = play_message(chan, vmu, vms);
04996    } else {
04997       cmd = ast_play_and_wait(chan, "vm-youhaveno");
04998       if (!cmd)
04999          cmd = ast_play_and_wait(chan, "vm-messages");
05000       if (!cmd) {
05001          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
05002          cmd = ast_play_and_wait(chan, vms->fn);
05003       }
05004    }
05005    return cmd;
05006 }
05007 
05008 /* PORTUGUESE syntax */
05009 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
05010 {
05011    int cmd=0;
05012 
05013    if (vms->lastmsg > -1) {
05014       cmd = play_message(chan, vmu, vms);
05015    } else {
05016       cmd = ast_play_and_wait(chan, "vm-no");
05017       if (!cmd) {
05018          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
05019          cmd = ast_play_and_wait(chan, vms->fn);
05020       }
05021       if (!cmd)
05022          cmd = ast_play_and_wait(chan, "vm-messages");
05023    }
05024    return cmd;
05025 }
05026 
05027 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
05028 {
05029    if (!strcasecmp(chan->language, "es")) {  /* SPANISH */
05030       return vm_browse_messages_es(chan, vms, vmu);
05031    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
05032       return vm_browse_messages_it(chan, vms, vmu);
05033    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */
05034       return vm_browse_messages_pt(chan, vms, vmu);
05035    } else if (!strcasecmp(chan->language, "gr")){
05036       return vm_browse_messages_gr(chan, vms, vmu);   /* GREEK */
05037    } else { /* Default to English syntax */
05038       return vm_browse_messages_en(chan, vms, vmu);
05039    }
05040 }
05041 
05042 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
05043             struct ast_vm_user *res_vmu, const char *context, const char *prefix,
05044             int skipuser, int maxlogins, int silent)
05045 {
05046    int useadsi=0, valid=0, logretries=0;
05047    char password[AST_MAX_EXTENSION]="", *passptr;
05048    struct ast_vm_user vmus, *vmu = NULL;
05049 
05050    /* If ADSI is supported, setup login screen */
05051    adsi_begin(chan, &useadsi);
05052    if (!skipuser && useadsi)
05053       adsi_login(chan);
05054    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
05055       ast_log(LOG_WARNING, "Couldn't stream login file\n");
05056       return -1;
05057    }
05058    
05059    /* Authenticate them and get their mailbox/password */
05060    
05061    while (!valid && (logretries < maxlogins)) {
05062       /* Prompt for, and read in the username */
05063       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
05064          ast_log(LOG_WARNING, "Couldn't read username\n");
05065          return -1;
05066       }
05067       if (ast_strlen_zero(mailbox)) {
05068          if (chan->cid.cid_num) {
05069             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
05070          } else {
05071             if (option_verbose > 2)
05072                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");  
05073             return -1;
05074          }
05075       }
05076       if (useadsi)
05077          adsi_password(chan);
05078 
05079       if (!ast_strlen_zero(prefix)) {
05080          char fullusername[80] = "";
05081          ast_copy_string(fullusername, prefix, sizeof(fullusername));
05082          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
05083          ast_copy_string(mailbox, fullusername, mailbox_size);
05084       }
05085 
05086       vmu = find_user(&vmus, context, mailbox);
05087       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
05088          /* saved password is blank, so don't bother asking */
05089          password[0] = '\0';
05090       } else {
05091          if (ast_streamfile(chan, "vm-password", chan->language)) {
05092             ast_log(LOG_WARNING, "Unable to stream password file\n");
05093             return -1;
05094          }
05095          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
05096             ast_log(LOG_WARNING, "Unable to read password\n");
05097             return -1;
05098          }
05099       }
05100 
05101       if (vmu) {
05102          passptr = vmu->password;
05103          if (passptr[0] == '-') passptr++;
05104       }
05105       if (vmu && !strcmp(passptr, password))
05106          valid++;
05107       else {
05108          if (option_verbose > 2)
05109             ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
05110          if (!ast_strlen_zero(prefix))
05111             mailbox[0] = '\0';
05112       }
05113       logretries++;
05114       if (!valid) {
05115          if (skipuser || logretries >= maxlogins) {
05116             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
05117                ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
05118                return -1;
05119             }
05120          } else {
05121             if (useadsi)
05122                adsi_login(chan);
05123             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
05124                ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
05125                return -1;
05126             }
05127          }
05128          if (ast_waitstream(chan, "")) /* Channel is hung up */
05129             return -1;
05130       }
05131    }
05132    if (!valid && (logretries >= maxlogins)) {
05133       ast_stopstream(chan);
05134       ast_play_and_wait(chan, "vm-goodbye");
05135       return -1;
05136    }
05137    if (vmu && !skipuser) {
05138       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
05139    }
05140    return 0;
05141 }
05142 
05143 static int vm_execmain(struct ast_channel *chan, void *data)
05144 {
05145    /* XXX This is, admittedly, some pretty horrendus code.  For some
05146       reason it just seemed a lot easier to do with GOTO's.  I feel
05147       like I'm back in my GWBASIC days. XXX */
05148    int res=-1;
05149    int cmd=0;
05150    int valid = 0;
05151    struct localuser *u;
05152    char prefixstr[80] ="";
05153    char ext_context[256]="";
05154    int box;
05155    int useadsi = 0;
05156    int skipuser = 0;
05157    struct vm_state vms;
05158    struct ast_vm_user *vmu = NULL, vmus;
05159    char *context=NULL;
05160    int silentexit = 0;
05161    struct ast_flags flags = { 0 };
05162    signed char record_gain = 0;
05163 
05164    LOCAL_USER_ADD(u);
05165 
05166    memset(&vms, 0, sizeof(vms));
05167    vms.lastmsg = -1;
05168 
05169    memset(&vmus, 0, sizeof(vmus));
05170 
05171    if (chan->_state != AST_STATE_UP)
05172       ast_answer(chan);
05173 
05174    if (!ast_strlen_zero(data)) {
05175       char *tmp;
05176       int argc;
05177       char *argv[2];
05178       char *opts[OPT_ARG_ARRAY_SIZE];
05179 
05180       tmp = ast_strdupa(data);
05181       argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
05182       if (argc == 2) {
05183          if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
05184             LOCAL_USER_REMOVE(u);
05185             return -1;
05186          }
05187          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
05188             int gain;
05189 
05190             if (ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
05191                ast_log(LOG_WARNING, "No value provided for record gain option\n");
05192                LOCAL_USER_REMOVE(u);
05193                return -1;
05194             } else if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
05195                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
05196                LOCAL_USER_REMOVE(u);
05197                return -1;
05198             } else {
05199                record_gain = (signed char) gain;
05200             }
05201          }
05202       } else {
05203          /* old style options parsing */
05204          while (*argv[0]) {
05205             if (*argv[0] == 's') {
05206                ast_set_flag(&flags, OPT_SILENT);
05207                argv[0]++;
05208             } else if (*argv[0] == 'p') {
05209                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
05210                argv[0]++;
05211             } else 
05212                break;
05213          }
05214 
05215       }
05216 
05217       valid = ast_test_flag(&flags, OPT_SILENT);
05218 
05219       if ((context = strchr(argv[0], '@')))
05220          *context++ = '\0';
05221 
05222       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
05223          ast_copy_string(prefixstr, argv[0], sizeof(prefixstr));
05224       else
05225          ast_copy_string(vms.username, argv[0], sizeof(vms.username));
05226 
05227       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
05228          skipuser++;
05229       else {
05230          if (!ast_strlen_zero(vms.username))
05231             ast_log(LOG_NOTICE, "Specified user '%s%s%s' not found (check voicemail.conf and/or realtime config).  Falling back to authentication mode.\n", vms.username, context ? "@" : "", context ? context : "");
05232          valid = 0;
05233       }
05234    }
05235 
05236    if (!valid)
05237       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
05238 
05239    if (!res) {
05240       valid = 1;
05241       if (!skipuser)
05242          vmu = &vmus;
05243    } else {
05244       res = 0;
05245    }
05246 
05247    /* If ADSI is supported, setup login screen */
05248    adsi_begin(chan, &useadsi);
05249 
05250    if (!valid)
05251       goto out;
05252 
05253    vms.deleted = calloc(vmu->maxmsg, sizeof(int));
05254    vms.heard = calloc(vmu->maxmsg, sizeof(int));
05255    
05256    /* Set language from config to override channel language */
05257    if (!ast_strlen_zero(vmu->language))
05258       ast_copy_string(chan->language, vmu->language, sizeof(chan->language));
05259    create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
05260    /* Retrieve old and new message counts */
05261    res = open_mailbox(&vms, vmu, 1);
05262    if (res == ERROR_LOCK_PATH)
05263       goto out;
05264    vms.oldmessages = vms.lastmsg + 1;
05265    /* Start in INBOX */
05266    res = open_mailbox(&vms, vmu, 0);
05267    if (res == ERROR_LOCK_PATH)
05268       goto out;
05269    vms.newmessages = vms.lastmsg + 1;
05270       
05271    /* Select proper mailbox FIRST!! */
05272    if (!vms.newmessages && vms.oldmessages) {
05273       /* If we only have old messages start here */
05274       res = open_mailbox(&vms, vmu, 1);
05275       if (res == ERROR_LOCK_PATH)
05276          goto out;
05277    }
05278 
05279    if (useadsi)
05280       adsi_status(chan, &vms);
05281    res = 0;
05282 
05283    /* Check to see if this is a new user */
05284    if (!strcasecmp(vmu->mailbox, vmu->password) && 
05285        (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
05286       if (ast_play_and_wait(chan, "vm-newuser") == -1)
05287          ast_log(LOG_WARNING, "Couldn't stream new user file\n");
05288       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
05289       if ((cmd == 't') || (cmd == '#')) {
05290          /* Timeout */
05291          res = 0;
05292          goto out;
05293       } else if (cmd < 0) {
05294          /* Hangup */
05295          res = -1;
05296          goto out;
05297       }
05298    }
05299 
05300    cmd = vm_intro(chan, &vms);
05301 
05302    vms.repeats = 0;
05303    vms.starting = 1;
05304    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
05305       /* Run main menu */
05306       switch(cmd) {
05307       case '1':
05308          vms.curmsg = 0;
05309          /* Fall through */
05310       case '5':
05311          cmd = vm_browse_messages(chan, &vms, vmu);
05312          break;
05313       case '2': /* Change folders */
05314          if (useadsi)
05315             adsi_folders(chan, 0, "Change to folder...");
05316          cmd = get_folder2(chan, "vm-changeto", 0);
05317          if (cmd == '#') {
05318             cmd = 0;
05319          } else if (cmd > 0) {
05320             cmd = cmd - '0';
05321             res = close_mailbox(&vms, vmu);
05322             if (res == ERROR_LOCK_PATH)
05323                goto out;
05324             res = open_mailbox(&vms, vmu, cmd);
05325             if (res == ERROR_LOCK_PATH)
05326                goto out;
05327             cmd = 0;
05328          }
05329          if (useadsi)
05330             adsi_status2(chan, &vms);
05331             
05332          if (!cmd)
05333             cmd = vm_play_folder_name(chan, vms.vmbox);
05334 
05335          vms.starting = 1;
05336          break;
05337       case '3': /* Advanced options */
05338          cmd = 0;
05339          vms.repeats = 0;
05340          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
05341             switch(cmd) {
05342             case '1': /* Reply */
05343                if (vms.lastmsg > -1 && !vms.starting) {
05344                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
05345                   if (cmd == ERROR_LOCK_PATH) {
05346                      res = cmd;
05347                      goto out;
05348                   }
05349                } else
05350                   cmd = ast_play_and_wait(chan, "vm-sorry");
05351                cmd = 't';
05352                break;
05353             case '2': /* Callback */
05354                if (option_verbose > 2 && !vms.starting)
05355                   ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
05356                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
05357                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
05358                   if (cmd == 9) {
05359                      silentexit = 1;
05360                      goto out;
05361                   } else if (cmd == ERROR_LOCK_PATH) {
05362                      res = cmd;
05363                      goto out;
05364                   }
05365                }
05366                else 
05367                   cmd = ast_play_and_wait(chan, "vm-sorry");
05368                cmd = 't';
05369                break;
05370             case '3': /* Envelope */
05371                if (vms.lastmsg > -1 && !vms.starting) {
05372                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
05373                   if (cmd == ERROR_LOCK_PATH) {
05374                      res = cmd;
05375                      goto out;
05376                   }
05377                } else
05378                   cmd = ast_play_and_wait(chan, "vm-sorry");
05379                cmd = 't';
05380                break;
05381             case '4': /* Dialout */
05382                if (!ast_strlen_zero(vmu->dialout)) {
05383                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
05384                   if (cmd == 9) {
05385                      silentexit = 1;
05386                      goto out;
05387                   }
05388                }
05389                else 
05390                   cmd = ast_play_and_wait(chan, "vm-sorry");
05391                cmd = 't';
05392                break;
05393 
05394             case '5': /* Leave VoiceMail */
05395                if (ast_test_flag(vmu, VM_SVMAIL)) {
05396                   cmd = forward_message(chan, vmu->context, vms.curdir, vms.curmsg, vmu, vmfmts, 1, record_gain);
05397                   if (cmd == ERROR_LOCK_PATH) {
05398                      res = cmd;
05399                      goto out;
05400                   }
05401                } else
05402                   cmd = ast_play_and_wait(chan,"vm-sorry");
05403                cmd='t';
05404                break;
05405                
05406             case '*': /* Return to main menu */
05407                cmd = 't';
05408                break;
05409 
05410             default:
05411                cmd = 0;
05412                if (!vms.starting) {
05413                   cmd = ast_play_and_wait(chan, "vm-toreply");
05414                }
05415                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
05416                   cmd = ast_play_and_wait(chan, "vm-tocallback");
05417                }
05418                if (!cmd && !vms.starting) {
05419                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
05420                }
05421                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
05422                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
05423                }
05424                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
05425                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
05426                if (!cmd)
05427                   cmd = ast_play_and_wait(chan, "vm-starmain");
05428                if (!cmd)
05429                   cmd = ast_waitfordigit(chan,6000);
05430                if (!cmd)
05431                   vms.repeats++;
05432                if (vms.repeats > 3)
05433                   cmd = 't';
05434             }
05435          }
05436          if (cmd == 't') {
05437             cmd = 0;
05438             vms.repeats = 0;
05439          }
05440          break;
05441       case '4':
05442          if (vms.curmsg > 0) {
05443             vms.curmsg--;
05444             cmd = play_message(chan, vmu, &vms);
05445          } else {
05446             cmd = ast_play_and_wait(chan, "vm-nomore");
05447          }
05448          break;
05449       case '6':
05450          if (vms.curmsg < vms.lastmsg) {
05451             vms.curmsg++;
05452             cmd = play_message(chan, vmu, &vms);
05453          } else {
05454             cmd = ast_play_and_wait(chan, "vm-nomore");
05455          }
05456          break;
05457       case '7':
05458          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
05459             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
05460             if (useadsi)
05461                adsi_delete(chan, &vms);
05462             if (vms.deleted[vms.curmsg]) 
05463                cmd = ast_play_and_wait(chan, "vm-deleted");
05464             else
05465                cmd = ast_play_and_wait(chan, "vm-undeleted");
05466             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
05467                if (vms.curmsg < vms.lastmsg) {
05468                   vms.curmsg++;
05469                   cmd = play_message(chan, vmu, &vms);
05470                } else {
05471                   cmd = ast_play_and_wait(chan, "vm-nomore");
05472                }
05473             }
05474          } else /* Delete not valid if we haven't selected a message */
05475             cmd = 0;
05476          break;
05477    
05478       case '8':
05479          if (vms.lastmsg > -1) {
05480             cmd = forward_message(chan, vmu->context, vms.curdir, vms.curmsg, vmu, vmfmts, 0, record_gain);
05481             if (cmd == ERROR_LOCK_PATH) {
05482                res = cmd;
05483                goto out;
05484             }
05485          } else
05486             cmd = ast_play_and_wait(chan, "vm-nomore");
05487          break;
05488       case '9':
05489          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
05490             /* No message selected */
05491             cmd = 0;
05492             break;
05493          }
05494          if (useadsi)
05495             adsi_folders(chan, 1, "Save to folder...");
05496          cmd = get_folder2(chan, "vm-savefolder", 1);
05497          box = 0; /* Shut up compiler */
05498          if (cmd == '#') {
05499             cmd = 0;
05500             break;
05501          } else if (cmd > 0) {
05502             box = cmd = cmd - '0';
05503             cmd = save_to_folder(vmu, vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
05504             if (cmd == ERROR_LOCK_PATH) {
05505                res = cmd;
05506                goto out;
05507             } else if (!cmd) {
05508                vms.deleted[vms.curmsg] = 1;
05509             } else {
05510                vms.deleted[vms.curmsg] = 0;
05511                vms.heard[vms.curmsg] = 0;
05512             }
05513          }
05514          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
05515          if (useadsi)
05516             adsi_message(chan, &vms);
05517          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
05518          if (!cmd) {
05519             cmd = ast_play_and_wait(chan, "vm-message");
05520             if (!cmd)
05521                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
05522             if (!cmd)
05523                cmd = ast_play_and_wait(chan, "vm-savedto");
05524             if (!cmd)
05525                cmd = vm_play_folder_name(chan, vms.fn);
05526          } else {
05527             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
05528          }
05529          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
05530             if (vms.curmsg < vms.lastmsg) {
05531                vms.curmsg++;
05532                cmd = play_message(chan, vmu, &vms);
05533             } else {
05534                cmd = ast_play_and_wait(chan, "vm-nomore");
05535             }
05536          }
05537          break;
05538       case '*':
05539          if (!vms.starting) {
05540             cmd = ast_play_and_wait(chan, "vm-onefor");
05541             if (!cmd)
05542                cmd = vm_play_folder_name(chan, vms.vmbox);
05543             if (!cmd)
05544                cmd = ast_play_and_wait(chan, "vm-opts");
05545             if (!cmd)
05546                cmd = vm_instructions(chan, &vms, 1);
05547          } else
05548             cmd = 0;
05549          break;
05550       case '0':
05551          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
05552          if (useadsi)
05553             adsi_status(chan, &vms);
05554          break;
05555       default: /* Nothing */
05556          cmd = vm_instructions(chan, &vms, 0);
05557          break;
05558       }
05559    }
05560    if ((cmd == 't') || (cmd == '#')) {
05561       /* Timeout */
05562       res = 0;
05563    } else {
05564       /* Hangup */
05565       res = -1;
05566    }
05567 
05568 out:
05569    if (res > -1) {
05570       ast_stopstream(chan);
05571       adsi_goodbye(chan);
05572       if (valid) {
05573          if (silentexit)
05574             res = ast_play_and_wait(chan, "vm-dialout");
05575          else 
05576             res = ast_play_and_wait(chan, "vm-goodbye");
05577          if (res > 0)
05578             res = 0;
05579       }
05580       if (useadsi)
05581          adsi_unload_session(chan);
05582    }
05583    if (vmu)
05584       close_mailbox(&vms, vmu);
05585    if (valid) {
05586       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
05587       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
05588       run_externnotify(vmu->context, vmu->mailbox);
05589    }
05590    if (vmu)
05591       free_user(vmu);
05592    if (vms.deleted)
05593       free(vms.deleted);
05594    if (vms.heard)
05595       free(vms.heard);
05596    LOCAL_USER_REMOVE(u);
05597 
05598    return res;
05599 }
05600 
05601 static int vm_exec(struct ast_channel *chan, void *data)
05602 {
05603    int res = 0;
05604    struct localuser *u;
05605    char *tmp;
05606    struct leave_vm_options leave_options;
05607    int argc;
05608    char *argv[2];
05609    struct ast_flags flags = { 0 };
05610    char *opts[OPT_ARG_ARRAY_SIZE];
05611    
05612    LOCAL_USER_ADD(u);
05613    
05614    memset(&leave_options, 0, sizeof(leave_options));
05615 
05616    if (chan->_state != AST_STATE_UP)
05617       ast_answer(chan);
05618 
05619    if (!ast_strlen_zero(data)) {
05620       tmp = ast_strdupa((char *)data);
05621       if (!tmp) {
05622          ast_log(LOG_ERROR, "Out of memory\n");
05623          LOCAL_USER_REMOVE(u);
05624          return -1;
05625       }
05626       argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
05627       if (argc == 2) {
05628          if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
05629             LOCAL_USER_REMOVE(u);
05630             return -1;
05631          }
05632          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
05633          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
05634             int gain;
05635 
05636             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
05637                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
05638                LOCAL_USER_REMOVE(u);
05639                return -1;
05640             } else {
05641                leave_options.record_gain = (signed char) gain;
05642             }
05643          }
05644       } else {
05645          /* old style options parsing */
05646          while (*argv[0]) {
05647             if (*argv[0] == 's') {
05648                ast_set_flag(&leave_options, OPT_SILENT);
05649                argv[0]++;
05650             } else if (*argv[0] == 'b') {
05651                ast_set_flag(&leave_options, OPT_BUSY_GREETING);
05652                argv[0]++;
05653             } else if (*argv[0] == 'u') {
05654                ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
05655                argv[0]++;
05656             } else if (*argv[0] == 'j') {
05657                ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
05658                argv[0]++;
05659             } else 
05660                break;
05661          }
05662       }
05663    } else {
05664       char tmp[256];
05665       res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
05666       if (res < 0) {
05667          LOCAL_USER_REMOVE(u);
05668          return res;
05669       }
05670       if (ast_strlen_zero(tmp)) {
05671          LOCAL_USER_REMOVE(u);
05672          return 0;
05673       }
05674       argv[0] = ast_strdupa(tmp);
05675    }
05676 
05677    res = leave_voicemail(chan, argv[0], &leave_options);
05678 
05679    if (res == ERROR_LOCK_PATH) {
05680       ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
05681       /*Send the call to n+101 priority, where n is the current priority*/
05682       if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || option_priority_jumping)
05683          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
05684             ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
05685       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05686       res = 0;
05687    }
05688    
05689    LOCAL_USER_REMOVE(u);
05690 
05691    return res;
05692 }
05693 
05694 static int append_mailbox(char *context, char *mbox, char *data)
05695 {
05696    /* Assumes lock is already held */
05697    char *tmp;
05698    char *stringp;
05699    char *s;
05700    struct ast_vm_user *vmu;
05701 
05702    tmp = ast_strdupa(data);
05703 
05704    vmu = malloc(sizeof(struct ast_vm_user));
05705    if (vmu) {
05706       memset(vmu, 0, sizeof(struct ast_vm_user));
05707       ast_copy_string(vmu->context, context, sizeof(vmu->context));
05708       ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
05709 
05710       populate_defaults(vmu);
05711 
05712       stringp = tmp;
05713       if ((s = strsep(&stringp, ","))) 
05714          ast_copy_string(vmu->password, s, sizeof(vmu->password));
05715       if (stringp && (s = strsep(&stringp, ","))) 
05716          ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
05717       if (stringp && (s = strsep(&stringp, ","))) 
05718          ast_copy_string(vmu->email, s, sizeof(vmu->email));
05719       if (stringp && (s = strsep(&stringp, ","))) 
05720          ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
05721       if (stringp && (s = strsep(&stringp, ","))) 
05722          apply_options(vmu, s);
05723       
05724       vmu->next = NULL;
05725       if (usersl)
05726          usersl->next = vmu;
05727       else
05728          users = vmu;
05729       usersl = vmu;
05730    }
05731    return 0;
05732 }
05733 
05734 static int vm_box_exists(struct ast_channel *chan, void *data) 
05735 {
05736    struct localuser *u;
05737    struct ast_vm_user svm;
05738    char *context, *box;
05739    int priority_jump = 0;
05740    AST_DECLARE_APP_ARGS(args,
05741       AST_APP_ARG(mbox);
05742       AST_APP_ARG(options);
05743    );
05744 
05745    if (ast_strlen_zero(data)) {
05746       ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
05747       return -1;
05748    }
05749 
05750    LOCAL_USER_ADD(u);
05751 
05752    box = ast_strdupa(data);
05753    if (!box) {
05754       ast_log(LOG_ERROR, "Out of memory\n");
05755       LOCAL_USER_REMOVE(u);
05756       return -1;
05757    }
05758 
05759    AST_STANDARD_APP_ARGS(args, box);
05760 
05761    if (args.options) {
05762       if (strchr(args.options, 'j'))
05763          priority_jump = 1;
05764    }
05765 
05766    if ((context = strchr(args.mbox, '@'))) {
05767       *context = '\0';
05768       context++;
05769    }
05770 
05771    if (find_user(&svm, context, args.mbox)) {
05772       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
05773       if (priority_jump || option_priority_jumping)
05774          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) 
05775             ast_log(LOG_WARNING, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box, context, chan->exten, chan->priority + 101);
05776    } else
05777       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
05778    LOCAL_USER_REMOVE(u);
05779    return 0;
05780 }
05781 
05782 static int vmauthenticate(struct ast_channel *chan, void *data)
05783 {
05784    struct localuser *u;
05785    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
05786    struct ast_vm_user vmus;
05787    char *options = NULL;
05788    int silent = 0, skipuser = 0;
05789    int res = -1;
05790 
05791    LOCAL_USER_ADD(u);
05792    
05793    if (s) {
05794       s = ast_strdupa(s);
05795       if (!s) {
05796          ast_log(LOG_ERROR, "Out of memory\n");
05797          return -1;
05798       }
05799       user = strsep(&s, "|");
05800       options = strsep(&s, "|");
05801       if (user) {
05802          s = user;
05803          user = strsep(&s, "@");
05804          context = strsep(&s, "");
05805          if (!ast_strlen_zero(user))
05806             skipuser++;
05807          ast_copy_string(mailbox, user, sizeof(mailbox));
05808       }
05809    }
05810 
05811    if (options) {
05812       silent = (strchr(options, 's')) != NULL;
05813    }
05814 
05815    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
05816       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
05817       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
05818       ast_play_and_wait(chan, "auth-thankyou");
05819       res = 0;
05820    }
05821 
05822    LOCAL_USER_REMOVE(u);
05823    return res;
05824 }
05825 
05826 static char show_voicemail_users_help[] =
05827 "Usage: show voicemail users [for <context>]\n"
05828 "       Lists all mailboxes currently set up\n";
05829 
05830 static char show_voicemail_zones_help[] =
05831 "Usage: show voicemail zones\n"
05832 "       Lists zone message formats\n";
05833 
05834 static int handle_show_voicemail_users(int fd, int argc, char *argv[])
05835 {
05836    struct ast_vm_user *vmu = users;
05837    char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
05838 
05839    if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
05840    else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
05841 
05842    if (vmu) {
05843       if (argc == 3)
05844          ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
05845       else {
05846          int count = 0;
05847          while (vmu) {
05848             if (!strcmp(argv[4],vmu->context))
05849                count++;
05850             vmu = vmu->next;
05851          }
05852          if (count) {
05853             vmu = users;
05854             ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
05855          } else {
05856             ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
05857             return RESULT_FAILURE;
05858          }
05859       }
05860       while (vmu) {
05861          int newmsgs = 0, oldmsgs = 0;
05862          char count[12], tmp[256] = "";
05863 
05864          if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
05865             snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
05866             messagecount(tmp, &newmsgs, &oldmsgs);
05867             snprintf(count,sizeof(count),"%d",newmsgs);
05868             ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
05869          }
05870          vmu = vmu->next;
05871       }
05872    } else {
05873       ast_cli(fd, "There are no voicemail users currently defined\n");
05874       return RESULT_FAILURE;
05875    }
05876    return RESULT_SUCCESS;
05877 }
05878 
05879 static int handle_show_voicemail_zones(int fd, int argc, char *argv[])
05880 {
05881    struct vm_zone *zone = zones;
05882    char *output_format = "%-15s %-20s %-45s\n";
05883 
05884    if (argc != 3) return RESULT_SHOWUSAGE;
05885 
05886    if (zone) {
05887       ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
05888       while (zone) {
05889          ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
05890          zone = zone->next;
05891       }
05892    } else {
05893       ast_cli(fd, "There are no voicemail zones currently defined\n");
05894       return RESULT_FAILURE;
05895    }
05896    return RESULT_SUCCESS;
05897 }
05898 
05899 static char *complete_show_voicemail_users(char *line, char *word, int pos, int state)
05900 {
05901    int which = 0;
05902    struct ast_vm_user *vmu = users;
05903    char *context = "";
05904 
05905    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
05906    if (pos > 4)
05907       return NULL;
05908    if (pos == 3) {
05909       if (state == 0)
05910          return strdup("for");
05911       else
05912          return NULL;
05913    }
05914    while (vmu) {
05915       if (!strncasecmp(word, vmu->context, strlen(word))) {
05916          if (context && strcmp(context, vmu->context)) {
05917             if (++which > state) {
05918                return strdup(vmu->context);
05919             }
05920             context = vmu->context;
05921          }
05922       }
05923       vmu = vmu->next;
05924    }
05925    return NULL;
05926 }
05927 
05928 static struct ast_cli_entry show_voicemail_users_cli =
05929    { { "show", "voicemail", "users", NULL },
05930    handle_show_voicemail_users, "List defined voicemail boxes",
05931    show_voicemail_users_help, complete_show_voicemail_users };
05932 
05933 static struct ast_cli_entry show_voicemail_zones_cli =
05934    { { "show", "voicemail", "zones", NULL },
05935    handle_show_voicemail_zones, "List zone message formats",
05936    show_voicemail_zones_help, NULL };
05937 
05938 static int load_config(void)
05939 {
05940    struct ast_vm_user *cur, *l;
05941    struct vm_zone *zcur, *zl;
05942    struct ast_config *cfg;
05943    char *cat;
05944    struct ast_variable *var;
05945    char *notifystr = NULL;
05946    char *astattach;
05947    char *astsearch;
05948    char *astsaycid;
05949    char *send_voicemail;
05950    char *astcallop;
05951    char *astreview;
05952    char *astskipcmd;
05953    char *asthearenv;
05954    char *astsaydurationinfo;
05955    char *astsaydurationminfo;
05956    char *silencestr;
05957    char *maxmsgstr;
05958    char *astdirfwd;
05959    char *thresholdstr;
05960    char *fmt;
05961    char *astemail;
05962    char *astmailcmd = SENDMAIL;
05963    char *s,*q,*stringp;
05964    char *dialoutcxt = NULL;
05965    char *callbackcxt = NULL;  
05966    char *exitcxt = NULL;   
05967    char *extpc;
05968    char *emaildateformatstr;
05969    int x;
05970    int tmpadsi[4];
05971 
05972    cfg = ast_config_load(VOICEMAIL_CONFIG);
05973    ast_mutex_lock(&vmlock);
05974    cur = users;
05975    while (cur) {
05976       l = cur;
05977       cur = cur->next;
05978       ast_set_flag(l, VM_ALLOCED);  
05979       free_user(l);
05980    }
05981    zcur = zones;
05982    while (zcur) {
05983       zl = zcur;
05984       zcur = zcur->next;
05985       free_zone(zl);
05986    }
05987    zones = NULL;
05988    zonesl = NULL;
05989    users = NULL;
05990    usersl = NULL;
05991    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
05992 
05993    if (cfg) {
05994       /* General settings */
05995 
05996       /* Attach voice message to mail message ? */
05997       if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
05998          astattach = "yes";
05999       ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH); 
06000 
06001       if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
06002          astsearch = "no";
06003       ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
06004 
06005 #ifdef USE_ODBC_STORAGE
06006       strcpy(odbc_database, "asterisk");
06007       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
06008          ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
06009       }
06010       strcpy(odbc_table, "voicemessages");
06011                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
06012                         ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
06013                 }
06014 #endif      
06015       /* Mail command */
06016       strcpy(mailcmd, SENDMAIL);
06017       if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
06018          ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
06019 
06020       maxsilence = 0;
06021       if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
06022          maxsilence = atoi(silencestr);
06023          if (maxsilence > 0)
06024             maxsilence *= 1000;
06025       }
06026       
06027       if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
06028          maxmsg = MAXMSG;
06029       } else {
06030          maxmsg = atoi(maxmsgstr);
06031          if (maxmsg <= 0) {
06032             ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
06033             maxmsg = MAXMSG;
06034          } else if (maxmsg > MAXMSGLIMIT) {
06035             ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
06036             maxmsg = MAXMSGLIMIT;
06037          }
06038       }
06039 
06040       /* Load date format config for voicemail mail */
06041       if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
06042          ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
06043       }
06044 
06045       /* External password changing command */
06046       if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
06047          ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
06048       }
06049 
06050       /* External voicemail notify application */
06051       
06052       if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
06053          ast_copy_string(externnotify, notifystr, sizeof(externnotify));
06054          ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
06055       } else {
06056          externnotify[0] = '\0';
06057       }
06058 
06059       /* Silence treshold */
06060       silencethreshold = 256;
06061       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
06062          silencethreshold = atoi(thresholdstr);
06063       
06064       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
06065          astemail = ASTERISK_USERNAME;
06066       ast_copy_string(serveremail, astemail, sizeof(serveremail));
06067       
06068       vmmaxmessage = 0;
06069       if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
06070          if (sscanf(s, "%30d", &x) == 1) {
06071             vmmaxmessage = x;
06072          } else {
06073             ast_log(LOG_WARNING, "Invalid max message time length\n");
06074          }
06075       }
06076 
06077       vmminmessage = 0;
06078       if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
06079          if (sscanf(s, "%30d", &x) == 1) {
06080             vmminmessage = x;
06081             if (maxsilence <= vmminmessage)
06082                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
06083          } else {
06084             ast_log(LOG_WARNING, "Invalid min message time length\n");
06085          }
06086       }
06087       fmt = ast_variable_retrieve(cfg, "general", "format");
06088       if (!fmt)
06089          fmt = "wav";   
06090       ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
06091 
06092       skipms = 3000;
06093       if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
06094          if (sscanf(s, "%30d", &x) == 1) {
06095             maxgreet = x;
06096          } else {
06097             ast_log(LOG_WARNING, "Invalid max message greeting length\n");
06098          }
06099       }
06100 
06101       if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
06102          if (sscanf(s, "%30d", &x) == 1) {
06103             skipms = x;
06104          } else {
06105             ast_log(LOG_WARNING, "Invalid skipms value\n");
06106          }
06107       }
06108 
06109       maxlogins = 3;
06110       if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
06111          if (sscanf(s, "%30d", &x) == 1) {
06112             maxlogins = x;
06113          } else {
06114             ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
06115          }
06116       }
06117 
06118       /* Force new user to record name ? */
06119       if (!(astattach = ast_variable_retrieve(cfg, "general", "forcename"))) 
06120          astattach = "no";
06121       ast_set2_flag((&globalflags), ast_true(astattach), VM_FORCENAME);
06122 
06123       /* Force new user to record greetings ? */
06124       if (!(astattach = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
06125          astattach = "no";
06126       ast_set2_flag((&globalflags), ast_true(astattach), VM_FORCEGREET);
06127 
06128       if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
06129          ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
06130          stringp = ast_strdupa(s);
06131          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
06132             if (!ast_strlen_zero(stringp)) {
06133                q = strsep(&stringp,",");
06134                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
06135                   q++;
06136                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
06137                ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
06138             } else {
06139                cidinternalcontexts[x][0] = '\0';
06140             }
06141          }
06142       }
06143       if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
06144          ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
06145          astreview = "no";
06146       }
06147       ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW); 
06148 
06149       if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
06150          ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
06151          astcallop = "no";
06152       }
06153       ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);  
06154 
06155       if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
06156          ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
06157          astsaycid = "no";
06158       } 
06159       ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID); 
06160 
06161       if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
06162          ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
06163          send_voicemail = "no";
06164       }
06165       ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
06166    
06167       if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
06168          ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
06169          asthearenv = "yes";
06170       }
06171       ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE); 
06172 
06173       if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
06174          ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
06175          astsaydurationinfo = "yes";
06176       }
06177       ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);  
06178 
06179       saydurationminfo = 2;
06180       if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
06181          if (sscanf(astsaydurationminfo, "%30d", &x) == 1) {
06182             saydurationminfo = x;
06183          } else {
06184             ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
06185          }
06186       }
06187 
06188       if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
06189          ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
06190          astskipcmd = "no";
06191       }
06192       ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
06193 
06194       if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
06195          ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
06196          ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
06197       } else {
06198          dialcontext[0] = '\0';  
06199       }
06200       
06201       if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
06202          ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
06203          ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
06204       } else {
06205          callcontext[0] = '\0';
06206       }
06207 
06208       if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
06209          ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
06210          ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
06211       } else {
06212          exitcontext[0] = '\0';
06213       }
06214 
06215       if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
06216          astdirfwd = "no";
06217       ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD); 
06218       cat = ast_category_browse(cfg, NULL);
06219       while (cat) {
06220          if (strcasecmp(cat, "general")) {
06221             var = ast_variable_browse(cfg, cat);
06222             if (strcasecmp(cat, "zonemessages")) {
06223                /* Process mailboxes in this context */
06224                while (var) {
06225                   append_mailbox(cat, var->name, var->value);
06226                   var = var->next;
06227                }
06228             } else {
06229                /* Timezones in this context */
06230                while (var) {
06231                   struct vm_zone *z;
06232                   z = malloc(sizeof(struct vm_zone));
06233                   if (z != NULL) {
06234                      char *msg_format, *timezone;
06235                      msg_format = ast_strdupa(var->value);
06236                      if (msg_format != NULL) {
06237                         timezone = strsep(&msg_format, "|");
06238                         if (msg_format) {
06239                            ast_copy_string(z->name, var->name, sizeof(z->name));
06240                            ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
06241                            ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
06242                            z->next = NULL;
06243                            if (zones) {
06244                               zonesl->next = z;
06245                               zonesl = z;
06246                            } else {
06247                               zones = z;
06248                               zonesl = z;
06249                            }
06250                         } else {
06251                            ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
06252                            free(z);
06253                         }
06254                      } else {
06255                         ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
06256                         free(z);
06257                         ast_mutex_unlock(&vmlock);
06258                         ast_config_destroy(cfg);
06259                         return -1;
06260                      }
06261                   } else {
06262                      ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
06263                      ast_mutex_unlock(&vmlock);
06264                      ast_config_destroy(cfg);
06265                      return -1;
06266                   }
06267                   var = var->next;
06268                }
06269             }
06270          }
06271          cat = ast_category_browse(cfg, cat);
06272       }
06273       memset(fromstring,0,sizeof(fromstring));
06274       memset(pagerfromstring,0,sizeof(pagerfromstring));
06275       memset(emailtitle,0,sizeof(emailtitle));
06276       strcpy(charset, "ISO-8859-1");
06277       if (emailbody) {
06278          free(emailbody);
06279          emailbody = NULL;
06280       }
06281       if (emailsubject) {
06282          free(emailsubject);
06283          emailsubject = NULL;
06284       }
06285                if (pagerbody) {
06286                        free(pagerbody);
06287                        pagerbody = NULL;
06288                }
06289                if (pagersubject) {
06290                        free(pagersubject);
06291                        pagersubject = NULL;
06292                }
06293       if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
06294          ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
06295       if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
06296          ast_copy_string(fromstring,s,sizeof(fromstring));
06297       if ((s=ast_variable_retrieve(cfg, "general", "pagerfromstring")))
06298          ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
06299       if ((s=ast_variable_retrieve(cfg, "general", "charset")))
06300          ast_copy_string(charset,s,sizeof(charset));
06301       if ((s=ast_variable_retrieve(cfg, "general", "adsifdn"))) {
06302          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
06303          for (x=0; x<4; x++) {
06304             memcpy(&adsifdn[x], &tmpadsi[x], 1);
06305          }
06306       }
06307       if ((s=ast_variable_retrieve(cfg, "general", "adsisec"))) {
06308          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
06309          for (x=0; x<4; x++) {
06310             memcpy(&adsisec[x], &tmpadsi[x], 1);
06311          }
06312       }
06313       if ((s=ast_variable_retrieve(cfg, "general", "adsiver")))
06314          if (atoi(s)) {
06315             adsiver = atoi(s);
06316          }
06317       if ((s=ast_variable_retrieve(cfg, "general", "emailtitle"))) {
06318          ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
06319          ast_copy_string(emailtitle,s,sizeof(emailtitle));
06320       }
06321       if ((s=ast_variable_retrieve(cfg, "general", "emailsubject")))
06322          emailsubject = strdup(s);
06323       if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
06324          char *tmpread, *tmpwrite;
06325          emailbody = strdup(s);
06326 
06327          /* substitute strings \t and \n into the apropriate characters */
06328          tmpread = tmpwrite = emailbody;
06329                        while ((tmpwrite = strchr(tmpread,'\\'))) {
06330                                int len = strlen("\n");
06331                                switch (tmpwrite[1]) {
06332                                        case 'n':
06333                                                strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06334                                                strncpy(tmpwrite,"\n",len);
06335                                                break;
06336                                        case 't':
06337                                                strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06338                                                strncpy(tmpwrite,"\t",len);
06339                                                break;
06340                                        default:
06341                                                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
06342                                }
06343                                tmpread = tmpwrite+len;
06344                        }
06345                }
06346                if ((s=ast_variable_retrieve(cfg, "general", "pagersubject")))
06347                        pagersubject = strdup(s);
06348                if ((s=ast_variable_retrieve(cfg, "general", "pagerbody"))) {
06349                        char *tmpread, *tmpwrite;
06350                        pagerbody = strdup(s);
06351 
06352                        /* substitute strings \t and \n into the apropriate characters */
06353                        tmpread = tmpwrite = pagerbody;
06354          while ((tmpwrite = strchr(tmpread,'\\'))) {
06355             int len = strlen("\n");
06356             switch (tmpwrite[1]) {
06357                case 'n':
06358                   strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06359                   strncpy(tmpwrite,"\n",len);
06360                   break;
06361                case 't':
06362                   strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06363                   strncpy(tmpwrite,"\t",len);
06364                   break;
06365                default:
06366                   ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
06367             }
06368             tmpread = tmpwrite+len;
06369          }
06370       }
06371       ast_mutex_unlock(&vmlock);
06372       ast_config_destroy(cfg);
06373       return 0;
06374    } else {
06375       ast_mutex_unlock(&vmlock);
06376       ast_log(LOG_WARNING, "Failed to load configuration file.\n");
06377       return 0;
06378    }
06379 }
06380 
06381 int reload(void)
06382 {
06383    return(load_config());
06384 }
06385 
06386 int unload_module(void)
06387 {
06388    int res;
06389    
06390    res = ast_unregister_application(app);
06391    res |= ast_unregister_application(app2);
06392    res |= ast_unregister_application(app3);
06393    res |= ast_unregister_application(app4);
06394    res |= ast_cli_unregister(&show_voicemail_users_cli);
06395    res |= ast_cli_unregister(&show_voicemail_zones_cli);
06396    ast_uninstall_vm_functions();
06397    
06398    STANDARD_HANGUP_LOCALUSERS;
06399 
06400    return res;
06401 }
06402 
06403 int load_module(void)
06404 {
06405    int res;
06406    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
06407    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
06408    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
06409    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
06410    if (res)
06411       return(res);
06412 
06413    if ((res=load_config())) {
06414       return(res);
06415    }
06416 
06417    ast_cli_register(&show_voicemail_users_cli);
06418    ast_cli_register(&show_voicemail_zones_cli);
06419 
06420    /* compute the location of the voicemail spool directory */
06421    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
06422 
06423    ast_install_vm_functions(has_voicemail, messagecount);
06424 
06425 #if defined(USE_ODBC_STORAGE) && !defined(EXTENDED_ODBC_STORAGE)
06426    ast_log(LOG_WARNING, "The current ODBC storage table format will be changed soon."
06427             "Please update your tables as per the README and edit the apps/Makefile "
06428             "and uncomment the line containing EXTENDED_ODBC_STORAGE to enable the "
06429             "new table format.\n");
06430 #endif
06431 
06432    return res;
06433 }
06434 
06435 char *description(void)
06436 {
06437    return tdesc;
06438 }
06439 
06440 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
06441 {
06442    int cmd = 0;
06443    char destination[80] = "";
06444    int retries = 0;
06445 
06446    if (!num) {
06447       if (option_verbose > 2)
06448          ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
06449       while (retries < 3 && cmd != 't') {
06450          destination[1] = '\0';
06451          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
06452          if (!cmd)
06453             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
06454          if (!cmd)
06455             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
06456          if (!cmd) {
06457             cmd = ast_waitfordigit(chan, 6000);
06458             if (cmd)
06459                destination[0] = cmd;
06460          }
06461          if (!cmd) {
06462             retries++;
06463          } else {
06464 
06465             if (cmd < 0)
06466                return 0;
06467             if (cmd == '*') {
06468                if (option_verbose > 2)
06469                   ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
06470                return 0;
06471             }
06472             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
06473                retries++;
06474             else
06475                cmd = 't';
06476          }
06477       }
06478       if (retries >= 3) {
06479          return 0;
06480       }
06481       
06482    } else {
06483       if (option_verbose > 2)
06484          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
06485       ast_copy_string(destination, num, sizeof(destination));
06486    }
06487 
06488    if (!ast_strlen_zero(destination)) {
06489       if (destination[strlen(destination) -1 ] == '*')
06490          return 0; 
06491       if (option_verbose > 2)
06492          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
06493       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
06494       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
06495       chan->priority = 0;
06496       return 9;
06497    }
06498    return 0;
06499 }
06500 
06501 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
06502              int option, signed char record_gain)
06503 {
06504    int res = 0;
06505    char filename[PATH_MAX], *origtime, *cid, *context, *name, *num;
06506    struct ast_config *msg_cfg;
06507    int retries = 0;
06508 
06509    vms->starting = 0; 
06510    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
06511 
06512    /* Retrieve info from VM attribute file */
06513 
06514    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
06515    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
06516    RETRIEVE(vms->curdir, vms->curmsg);
06517    msg_cfg = ast_config_load(filename);
06518    DISPOSE(vms->curdir, vms->curmsg);
06519    if (!msg_cfg) {
06520       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06521       return 0;
06522    }
06523 
06524    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06525       ast_config_destroy(msg_cfg);
06526       return 0;
06527    }
06528 
06529    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06530 
06531    context = ast_variable_retrieve(msg_cfg, "message", "context");
06532    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06533       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06534 
06535    if (option == 3) {
06536 
06537       if (!res)
06538          res = play_message_datetime(chan, vmu, origtime, filename);
06539       if (!res)
06540          res = play_message_callerid(chan, vms, cid, context, 0);
06541 
06542       res = 't';
06543 
06544    } else if (option == 2) { /* Call back */
06545 
06546       if (!ast_strlen_zero(cid)) {
06547          ast_callerid_parse(cid, &name, &num);
06548          while ((res > -1) && (res != 't')) {
06549             switch(res) {
06550                case '1':
06551                   if (num) {
06552                      /* Dial the CID number */
06553                      res = dialout(chan, vmu, num, vmu->callback);
06554                      if (res) {
06555                         ast_config_destroy(msg_cfg);
06556                         return 9;
06557                      }
06558                   } else {
06559                      res = '2';
06560                   }
06561                   break;
06562 
06563                case '2':
06564                   /* Want to enter a different number, can only do this if there's a dialout context for this user */
06565                   if (!ast_strlen_zero(vmu->dialout)) {
06566                      res = dialout(chan, vmu, NULL, vmu->dialout);
06567                      if (res) {
06568                         ast_config_destroy(msg_cfg);
06569                         return 9;
06570                      }
06571                   } else {
06572                      if (option_verbose > 2)
06573                         ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
06574                      res = ast_play_and_wait(chan, "vm-sorry");
06575                   }
06576                   ast_config_destroy(msg_cfg);
06577                   return res;
06578                case '*':
06579                   res = 't';
06580                   break;
06581                case '3':
06582                case '4':
06583                case '5':
06584                case '6':
06585                case '7':
06586                case '8':
06587                case '9':
06588                case '0':
06589 
06590                   res = ast_play_and_wait(chan, "vm-sorry");
06591                   retries++;
06592                   break;
06593                default:
06594                   if (num) {
06595                      if (option_verbose > 2)
06596                         ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
06597                      res = ast_play_and_wait(chan, "vm-num-i-have");
06598                      if (!res)
06599                         res = play_message_callerid(chan, vms, num, vmu->context, 1);
06600                      if (!res)
06601                         res = ast_play_and_wait(chan, "vm-tocallnum");
06602                      /* Only prompt for a caller-specified number if there is a dialout context specified */
06603                      if (!ast_strlen_zero(vmu->dialout)) {
06604                         if (!res)
06605                            res = ast_play_and_wait(chan, "vm-calldiffnum");
06606                      }
06607                   } else {
06608                      res = ast_play_and_wait(chan, "vm-nonumber");
06609                      if (!ast_strlen_zero(vmu->dialout)) {
06610                         if (!res)
06611                            res = ast_play_and_wait(chan, "vm-toenternumber");
06612                      }
06613                   }
06614                   if (!res)
06615                      res = ast_play_and_wait(chan, "vm-star-cancel");
06616                   if (!res)
06617                      res = ast_waitfordigit(chan, 6000);
06618                   if (!res) {
06619                      retries++;
06620                      if (retries > 3)
06621                         res = 't';
06622                   }
06623                   break; 
06624                   
06625             }
06626             if (res == 't')
06627                res = 0;
06628             else if (res == '*')
06629                res = -1;
06630          }
06631       }
06632       
06633    }
06634    else if (option == 1) { /* Reply */
06635       /* Send reply directly to sender */
06636       if (!ast_strlen_zero(cid)) {
06637          ast_callerid_parse(cid, &name, &num);
06638          if (!num) {
06639             if (option_verbose > 2)
06640                ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
06641             if (!res)
06642                res = ast_play_and_wait(chan, "vm-nonumber");
06643             ast_config_destroy(msg_cfg);
06644             return res;
06645          } else {
06646             if (find_user(NULL, vmu->context, num)) {
06647                struct leave_vm_options leave_options;
06648                char mailbox[AST_MAX_EXTENSION * 2 + 2];
06649                snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
06650 
06651                if (option_verbose > 2)
06652                   ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
06653                
06654                memset(&leave_options, 0, sizeof(leave_options));
06655                leave_options.record_gain = record_gain;
06656                res = leave_voicemail(chan, mailbox, &leave_options);
06657                ast_config_destroy(msg_cfg);
06658                if (!res)
06659                   res = 't';
06660                return res;
06661             } else {
06662                /* Sender has no mailbox, can't reply */
06663                if (option_verbose > 2)
06664                   ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
06665                ast_play_and_wait(chan, "vm-nobox");
06666                ast_config_destroy(msg_cfg);
06667                res = 't';
06668                return res;
06669             }
06670          } 
06671          res = 0;
06672       }
06673    }
06674 
06675    if (!res) {
06676       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
06677       vms->heard[msg] = 1;
06678       res = wait_file(chan, vms, vms->fn);
06679    }
06680    ast_config_destroy(msg_cfg);
06681    return res;
06682 }
06683  
06684 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
06685                int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
06686                signed char record_gain)
06687 {
06688    /* Record message & let caller review or re-record it, or set options if applicable */
06689    int res = 0;
06690    int cmd = 0;
06691    int max_attempts = 3;
06692    int attempts = 0;
06693    int recorded = 0;
06694    int message_exists = 0;
06695    signed char zero_gain = 0;
06696    char tempfile[PATH_MAX];
06697    char *acceptdtmf = "#";
06698    char *canceldtmf = "";
06699 
06700    /* Note that urgent and private are for flagging messages as such in the future */
06701  
06702    /* barf if no pointer passed to store duration in */
06703    if (duration == NULL) {
06704       ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
06705       return -1;
06706    }
06707 
06708    if (!outsidecaller)
06709       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
06710    else
06711       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
06712 
06713    cmd = '3';   /* Want to start by recording */
06714  
06715    while ((cmd >= 0) && (cmd != 't')) {
06716       switch (cmd) {
06717       case '1':
06718          if (!message_exists) {
06719             /* In this case, 1 is to record a message */
06720             cmd = '3';
06721             break;
06722          } else {
06723             /* Otherwise 1 is to save the existing message */
06724             if (option_verbose > 2)
06725                ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
06726             if (!outsidecaller)
06727                ast_filerename(tempfile, recordfile, NULL);
06728             ast_streamfile(chan, "vm-msgsaved", chan->language);
06729             ast_waitstream(chan, "");
06730             STORE(recordfile, vmu->mailbox, vmu->context, -1);
06731             DISPOSE(recordfile, -1);
06732             cmd = 't';
06733             return res;
06734          }
06735       case '2':
06736          /* Review */
06737          if (option_verbose > 2)
06738             ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
06739          ast_streamfile(chan, tempfile, chan->language);
06740          cmd = ast_waitstream(chan, AST_DIGIT_ANY);
06741          break;
06742       case '3':
06743          message_exists = 0;
06744          /* Record */
06745          if (recorded == 1) {
06746             if (option_verbose > 2)
06747                ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
06748          } else { 
06749             if (option_verbose > 2)
06750                ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
06751          }
06752          if (recorded && outsidecaller) {
06753             cmd = ast_play_and_wait(chan, INTRO);
06754             cmd = ast_play_and_wait(chan, "beep");
06755          }
06756          recorded = 1;
06757          /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
06758          if (record_gain)
06759             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06760          if (ast_test_flag(vmu, VM_OPERATOR))
06761             canceldtmf = "0";
06762          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
06763          if (record_gain)
06764             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06765          if (cmd == -1) {
06766             /* User has hung up, no options to give */
06767             if (!outsidecaller) {
06768                /* user was recording a greeting and they hung up, so let's delete the recording. */
06769                ast_filedelete(tempfile, NULL);
06770             }
06771             return cmd;
06772          }
06773          if (cmd == '0') {
06774             break;
06775          } else if (cmd == '*') {
06776             break;
06777          } 
06778 #if 0       
06779          else if (vmu->review && (*duration < 5)) {
06780             /* Message is too short */
06781             if (option_verbose > 2)
06782                ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
06783             cmd = ast_play_and_wait(chan, "vm-tooshort");
06784             cmd = ast_filedelete(tempfile, NULL);
06785             break;
06786          }
06787          else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
06788             /* Message is all silence */
06789             if (option_verbose > 2)
06790                ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
06791             cmd = ast_filedelete(tempfile, NULL);
06792             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
06793             if (!cmd)
06794                cmd = ast_play_and_wait(chan, "vm-speakup");
06795             break;
06796          }
06797 #endif
06798          else {
06799             /* If all is well, a message exists */
06800             message_exists = 1;
06801             cmd = 0;
06802          }
06803          break;
06804       case '4':
06805       case '5':
06806       case '6':
06807       case '7':
06808       case '8':
06809       case '9':
06810       case '*':
06811       case '#':
06812          cmd = ast_play_and_wait(chan, "vm-sorry");
06813          break;
06814 #if 0 
06815 /*  XXX Commented out for the moment because of the dangers of deleting
06816     a message while recording (can put the message numbers out of sync) */
06817       case '*':
06818          /* Cancel recording, delete message, offer to take another message*/
06819          cmd = ast_play_and_wait(chan, "vm-deleted");
06820          cmd = ast_filedelete(tempfile, NULL);
06821          if (outsidecaller) {
06822             res = vm_exec(chan, NULL);
06823             return res;
06824          }
06825          else
06826             return 1;
06827 #endif
06828       case '0':
06829          if(!ast_test_flag(vmu, VM_OPERATOR)) {
06830             cmd = ast_play_and_wait(chan, "vm-sorry");
06831             break;
06832          }
06833          if (message_exists || recorded) {
06834             cmd = ast_play_and_wait(chan, "vm-saveoper");
06835             if (!cmd)
06836                cmd = ast_waitfordigit(chan, 3000);
06837             if (cmd == '1') {
06838                ast_play_and_wait(chan, "vm-msgsaved");
06839                cmd = '0';
06840             } else {
06841                ast_play_and_wait(chan, "vm-deleted");
06842                DELETE(recordfile, -1, recordfile);
06843                cmd = '0';
06844             }
06845          }
06846          return cmd;
06847       default:
06848          /* If the caller is an ouside caller, and the review option is enabled,
06849             allow them to review the message, but let the owner of the box review
06850             their OGM's */
06851          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
06852             return cmd;
06853          if (message_exists) {
06854             cmd = ast_play_and_wait(chan, "vm-review");
06855          }
06856          else {
06857             cmd = ast_play_and_wait(chan, "vm-torerecord");
06858             if (!cmd)
06859                cmd = ast_waitfordigit(chan, 600);
06860          }
06861          
06862          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
06863             cmd = ast_play_and_wait(chan, "vm-reachoper");
06864             if (!cmd)
06865                cmd = ast_waitfordigit(chan, 600);
06866          }
06867 #if 0
06868          if (!cmd)
06869             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
06870 #endif
06871          if (!cmd)
06872             cmd = ast_waitfordigit(chan, 6000);
06873          if (!cmd) {
06874             attempts++;
06875          }
06876          if (attempts > max_attempts) {
06877             cmd = 't';
06878          }
06879       }
06880    }
06881    if (outsidecaller)  
06882       ast_play_and_wait(chan, "vm-goodbye");
06883    if (cmd == 't')
06884       cmd = 0;
06885    return cmd;
06886  }
06887  
06888 
06889 int usecount(void)
06890 {
06891    int res;
06892    STANDARD_USECOUNT(res);
06893    return res;
06894 }
06895 
06896 char *key()
06897 {
06898    return ASTERISK_GPL_KEY;
06899 }
06900 

Generated on Wed Oct 28 15:47:49 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6