Mon Sep 18 09:12:46 2006

Asterisk developer's documentation


app_voicemail.c

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

Generated on Mon Sep 18 09:12:49 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7