Sun Aug 6 15:02:26 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Sun Aug 6 15:02:28 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2