Sat Nov 25 00:45:30 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: 43815 $")
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    int res = 0, cmd = 0;
03381    struct ast_vm_user *receiver = NULL, *extensions = NULL, *vmtmp = NULL, *vmfree;
03382    char *stringp, *s;
03383    int saved_messages = 0, found = 0;
03384    int valid_extensions = 0;
03385    
03386    while (!res && !valid_extensions) {
03387       int use_directory = 0;
03388       if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
03389          int done = 0;
03390          int retries = 0;
03391          cmd=0;
03392          while((cmd >= 0) && !done ){
03393             if (cmd)
03394                retries = 0;
03395             switch (cmd) {
03396             case '1': 
03397                use_directory = 0;
03398                done = 1;
03399                break;
03400             case '2': 
03401                use_directory = 1;
03402                done=1;
03403                break;
03404             case '*': 
03405                cmd = 't';
03406                done = 1;
03407                break;
03408             default: 
03409                /* Press 1 to enter an extension press 2 to use the directory */
03410                cmd = ast_play_and_wait(chan,"vm-forward");
03411                if (!cmd)
03412                   cmd = ast_waitfordigit(chan,3000);
03413                if (!cmd)
03414                   retries++;
03415                if (retries > 3)
03416                {
03417                   cmd = 't';
03418                   done = 1;
03419                }
03420                
03421              }
03422          }
03423          if( cmd<0 || cmd=='t' )
03424             break;
03425       }
03426       
03427       if (use_directory) {
03428          /* use app_directory */
03429          
03430          char old_context[sizeof(chan->context)];
03431          char old_exten[sizeof(chan->exten)];
03432          int old_priority;
03433          struct ast_app* app;
03434 
03435          
03436          app = pbx_findapp("Directory");
03437          if (app) {
03438             /* make backup copies */
03439             char vmcontext[256];
03440             memcpy(old_context, chan->context, sizeof(chan->context));
03441             memcpy(old_exten, chan->exten, sizeof(chan->exten));
03442             old_priority = chan->priority;
03443             
03444             /* call the the Directory, changes the channel */
03445             sprintf(vmcontext, "%s||v", context ? context : "default");
03446             res = pbx_exec(chan, app, vmcontext, 1);
03447             
03448             ast_copy_string(username, chan->exten, sizeof(username));
03449             
03450             /* restore the old context, exten, and priority */
03451             memcpy(chan->context, old_context, sizeof(chan->context));
03452             memcpy(chan->exten, old_exten, sizeof(chan->exten));
03453             chan->priority = old_priority;
03454             
03455          } else {
03456             ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
03457             ast_clear_flag((&globalflags), VM_DIRECFORWARD);   
03458          }
03459       } else   {
03460          /* Ask for an extension */
03461          res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
03462          if (res)
03463             break;
03464          if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
03465             break;
03466       }
03467       
03468       /* start all over if no username */
03469       if (ast_strlen_zero(username))
03470          continue;
03471       stringp = username;
03472       s = strsep(&stringp, "*");
03473       /* start optimistic */
03474       valid_extensions = 1;
03475       while (s) {
03476          /* Don't forward to ourselves.  find_user is going to malloc since we have a NULL as first argument */
03477          if (strcmp(s,sender->mailbox) && (receiver = find_user(NULL, context, s))) {
03478             if (!extensions)
03479                vmtmp = extensions = receiver;
03480             else {
03481                vmtmp->next = receiver;
03482                vmtmp = receiver;
03483             }
03484             found++;
03485          } else {
03486             valid_extensions = 0;
03487             break;
03488          }
03489          s = strsep(&stringp, "*");
03490       }
03491       /* break from the loop of reading the extensions */
03492       if (valid_extensions)
03493          break;
03494       /* "I am sorry, that's not a valid extension.  Please try again." */
03495       res = ast_play_and_wait(chan, "pbx-invalid");
03496    }
03497    /* check if we're clear to proceed */
03498    if (!extensions || !valid_extensions)
03499       return res;
03500    vmtmp = extensions;
03501    if (flag==1) {
03502       struct leave_vm_options leave_options;
03503       char mailbox[AST_MAX_EXTENSION * 2 + 2];
03504       snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
03505 
03506       /* Send VoiceMail */
03507       memset(&leave_options, 0, sizeof(leave_options));
03508       leave_options.record_gain = record_gain;
03509       cmd = leave_voicemail(chan, mailbox, &leave_options);
03510    } else {
03511       /* Forward VoiceMail */
03512       RETRIEVE(dir, curmsg);
03513       cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context, record_gain);
03514       if (!cmd) {
03515          while (!res && vmtmp) {
03516             copy_message(chan, sender, 0, curmsg, 0, vmtmp, fmt);
03517    
03518             saved_messages++;
03519             vmfree = vmtmp;
03520             vmtmp = vmtmp->next;
03521             free_user(vmfree);
03522          }
03523          if (saved_messages > 0) {
03524             /* give confirmation that the message was saved */
03525             /* commented out since we can't forward batches yet
03526             if (saved_messages == 1)
03527                res = ast_play_and_wait(chan, "vm-message");
03528             else
03529                res = ast_play_and_wait(chan, "vm-messages");
03530             if (!res)
03531                res = ast_play_and_wait(chan, "vm-saved"); */
03532             res = ast_play_and_wait(chan, "vm-msgsaved");
03533          }  
03534       }
03535    }
03536    return res ? res : cmd;
03537 }
03538 
03539 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
03540 {
03541    int res;
03542    if ((res = ast_streamfile(chan, file, chan->language))) 
03543       ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
03544    if (!res)
03545       res = ast_waitstream(chan, AST_DIGIT_ANY);
03546    return res;
03547 }
03548 
03549 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
03550 {
03551    return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
03552 }
03553 
03554 static int play_message_category(struct ast_channel *chan, char *category)
03555 {
03556    int res = 0;
03557 
03558    if (!ast_strlen_zero(category))
03559       res = ast_play_and_wait(chan, category);
03560 
03561    if (res) {
03562       ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
03563       res = 0;
03564    }
03565 
03566    return res;
03567 }
03568 
03569 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
03570 {
03571    int res = 0;
03572    struct vm_zone *the_zone = NULL;
03573    time_t t;
03574    long tin;
03575 
03576    if (sscanf(origtime,"%ld",&tin) < 1) {
03577       ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
03578       return 0;
03579    }
03580    t = tin;
03581 
03582    /* Does this user have a timezone specified? */
03583    if (!ast_strlen_zero(vmu->zonetag)) {
03584       /* Find the zone in the list */
03585       struct vm_zone *z;
03586       z = zones;
03587       while (z) {
03588          if (!strcmp(z->name, vmu->zonetag)) {
03589             the_zone = z;
03590             break;
03591          }
03592          z = z->next;
03593       }
03594    }
03595 
03596 /* No internal variable parsing for now, so we'll comment it out for the time being */
03597 #if 0
03598    /* Set the DIFF_* variables */
03599    localtime_r(&t, &time_now);
03600    tv_now = ast_tvnow();
03601    tnow = tv_now.tv_sec;
03602    localtime_r(&tnow,&time_then);
03603 
03604    /* Day difference */
03605    if (time_now.tm_year == time_then.tm_year)
03606       snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
03607    else
03608       snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
03609    pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
03610 
03611    /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
03612 #endif
03613    if (the_zone)
03614       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
03615        else if(!strcasecmp(chan->language,"se"))       /* SWEDISH syntax */
03616                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
03617        else if(!strcasecmp(chan->language,"no"))       /* NORWEGIAN syntax */
03618                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
03619    else if(!strcasecmp(chan->language,"de")) /* GERMAN syntax */
03620       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
03621    else if (!strcasecmp(chan->language,"nl"))   /* DUTCH syntax */
03622       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
03623    else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
03624       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);
03625    else if (!strcasecmp(chan->language,"gr"))
03626       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
03627    else
03628       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
03629 #if 0
03630    pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
03631 #endif
03632    return res;
03633 }
03634 
03635 
03636 
03637 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
03638 {
03639    int res = 0;
03640    int i;
03641    char *callerid, *name;
03642    char prefile[256]="";
03643    
03644 
03645    /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
03646    /* 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 */
03647    if ((cid == NULL)||(context == NULL))
03648       return res;
03649 
03650    /* Strip off caller ID number from name */
03651    ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
03652    ast_callerid_parse(cid, &name, &callerid);
03653    if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
03654       /* Check for internal contexts and only */
03655       /* say extension when the call didn't come from an internal context in the list */
03656       for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
03657          ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
03658          if ((strcmp(cidinternalcontexts[i], context) == 0))
03659             break;
03660       }
03661       if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
03662          if (!res) {
03663             snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
03664             if (!ast_strlen_zero(prefile)) {
03665             /* See if we can find a recorded name for this person instead of their extension number */
03666                if (ast_fileexists(prefile, NULL, NULL) > 0) {
03667                   if (option_verbose > 2)
03668                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
03669                   if (!callback)
03670                      res = wait_file2(chan, vms, "vm-from");
03671                   res = ast_streamfile(chan, prefile, chan->language) > -1;
03672                   res = ast_waitstream(chan, "");
03673                } else {
03674                   if (option_verbose > 2)
03675                      ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
03676                   /* BB: Say "from extension" as one saying to sound smoother */
03677                   if (!callback)
03678                      res = wait_file2(chan, vms, "vm-from-extension");
03679                   res = ast_say_digit_str(chan, callerid, "", chan->language);
03680                }
03681             }
03682          }
03683       }
03684 
03685       else if (!res){
03686          ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
03687          /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
03688          if (!callback)
03689             res = wait_file2(chan, vms, "vm-from-phonenumber");
03690          res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
03691       }
03692    } else {
03693       /* Number unknown */
03694       ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
03695       /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
03696       res = wait_file2(chan, vms, "vm-unknown-caller");
03697    }
03698    return res;
03699 }
03700 
03701 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
03702 {
03703    int res = 0;
03704    int durationm;
03705    int durations;
03706    /* Verify that we have a duration for the message */
03707    if((duration == NULL))
03708       return res;
03709 
03710    /* Convert from seconds to minutes */
03711    durations=atoi(duration);
03712    durationm=(durations / 60);
03713 
03714    ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
03715 
03716    if((!res)&&(durationm>=minduration)) {
03717       res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
03718       res = wait_file2(chan, vms, "vm-minutes");
03719    }
03720    return res;
03721 }
03722 
03723 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
03724 {
03725    int res = 0;
03726    char filename[256],*origtime, *cid, *context, *duration;
03727    char *category;
03728    struct ast_config *msg_cfg;
03729 
03730    vms->starting = 0; 
03731    make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
03732    adsi_message(chan, vms);
03733    if (!vms->curmsg)
03734       res = wait_file2(chan, vms, "vm-first");  /* "First" */
03735    else if (vms->curmsg == vms->lastmsg)
03736       res = wait_file2(chan, vms, "vm-last");      /* "last" */
03737    if (!res) {
03738                if (!strcasecmp(chan->language, "se")) {             /* SWEDISH syntax */
03739                        res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
03740                }
03741                else {
03742                        res = wait_file2(chan, vms, "vm-message");      /* "message" */
03743                }
03744       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
03745          if (!res)
03746             res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
03747       }
03748    }
03749 
03750    /* Retrieve info from VM attribute file */
03751    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
03752    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
03753    RETRIEVE(vms->curdir, vms->curmsg);
03754    msg_cfg = ast_config_load(filename);
03755    if (!msg_cfg) {
03756       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
03757       return 0;
03758    }
03759                                                                            
03760    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
03761       ast_log(LOG_WARNING, "No origtime?!\n");
03762       DISPOSE(vms->curdir, vms->curmsg);
03763       ast_config_destroy(msg_cfg);
03764       return 0;
03765    }
03766 
03767    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
03768    duration = ast_variable_retrieve(msg_cfg, "message", "duration");
03769    category = ast_variable_retrieve(msg_cfg, "message", "category");
03770 
03771    context = ast_variable_retrieve(msg_cfg, "message", "context");
03772    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
03773       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
03774 
03775    if (!res)
03776       res = play_message_category(chan, category);
03777    if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
03778       res = play_message_datetime(chan, vmu, origtime, filename);
03779    if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
03780       res = play_message_callerid(chan, vms, cid, context, 0);
03781         if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
03782                 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
03783    /* Allow pressing '1' to skip envelope / callerid */
03784    if (res == '1')
03785       res = 0;
03786    ast_config_destroy(msg_cfg);
03787 
03788    if (!res) {
03789       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
03790       vms->heard[vms->curmsg] = 1;
03791       res = wait_file(chan, vms, vms->fn);
03792    }
03793    DISPOSE(vms->curdir, vms->curmsg);
03794    return res;
03795 }
03796 
03797 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
03798 {
03799    int res = 0;
03800    int count_msg, last_msg;
03801 
03802    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
03803    
03804    /* Rename the member vmbox HERE so that we don't try to return before
03805     * we know what's going on.
03806     */
03807    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
03808    
03809    /* Faster to make the directory than to check if it exists. */
03810    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
03811 
03812    count_msg = count_messages(vmu, vms->curdir);
03813    if (count_msg < 0)
03814       return count_msg;
03815    else
03816       vms->lastmsg = count_msg - 1;
03817 
03818    /*
03819    The following test is needed in case sequencing gets messed up.
03820    There appears to be more than one way to mess up sequence, so
03821    we will not try to find all of the root causes--just fix it when
03822    detected.
03823    */
03824 
03825    last_msg = last_message_index(vmu, vms->curdir);
03826    if (last_msg < 0)
03827       return last_msg;
03828    else if(vms->lastmsg != last_msg)
03829    {
03830       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
03831       res = resequence_mailbox(vmu, vms->curdir);
03832       if (res)
03833          return res;
03834    }
03835 
03836    return 0;
03837 }
03838 
03839 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
03840 {
03841    int x, nummsg;
03842    int res = 0;
03843 
03844    if (vms->lastmsg <= -1)
03845       goto done;
03846 
03847    /* Get the deleted messages fixed */ 
03848    if (vm_lock_path(vms->curdir))
03849       return ERROR_LOCK_PATH;
03850    
03851    vms->curmsg = -1; 
03852    for (x = 0; x < vmu->maxmsg; x++) { 
03853       if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
03854          /* Save this message.  It's not in INBOX or hasn't been heard */ 
03855          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
03856          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
03857             break;
03858          vms->curmsg++; 
03859          make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
03860          if (strcmp(vms->fn, vms->fn2)) { 
03861             RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
03862          } 
03863       } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
03864          /* Move to old folder before deleting */ 
03865          res = save_to_folder(vmu, vms->curdir, x, vmu->context, vms->username, 1);
03866          if (res == ERROR_LOCK_PATH) {
03867             /* If save failed do not delete the message */
03868             vms->deleted[x] = 0;
03869             vms->heard[x] = 0;
03870             --x;
03871          } 
03872       } 
03873    } 
03874 
03875    /* Delete ALL remaining messages */
03876    nummsg = x - 1;
03877    for (x = vms->curmsg + 1; x <= nummsg; x++) {
03878       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
03879       if (EXISTS(vms->curdir, x, vms->fn, NULL))
03880          DELETE(vms->curdir, x, vms->fn);
03881    }
03882    ast_unlock_path(vms->curdir);
03883 
03884 done:
03885    if (vms->deleted)
03886       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
03887    if (vms->heard)
03888       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
03889 
03890    return 0;
03891 }
03892 
03893 /* In Greek even though we CAN use a syntax like "friends messages"
03894  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
03895  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
03896  * syntax for the above three categories which is more elegant. 
03897 */
03898 
03899 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
03900 {
03901    int cmd;
03902    char *buf;
03903 
03904    buf = alloca(strlen(mbox)+2); 
03905    strcpy(buf, mbox);
03906    strcat(buf,"s");
03907 
03908    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
03909       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
03910       if (cmd)
03911       return cmd;
03912       return ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
03913    } else {
03914       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
03915       if (cmd)
03916          return cmd;
03917       return ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
03918    }
03919 }
03920 
03921 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
03922 {
03923    int cmd;
03924 
03925    if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Italian, Spanish, French or Portuguese syntax */
03926       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
03927       if (cmd)
03928          return cmd;
03929       return ast_play_and_wait(chan, mbox);
03930    } else if (!strcasecmp(chan->language, "gr")){
03931       return vm_play_folder_name_gr(chan, mbox);
03932    } else {  /* Default English */
03933       cmd = ast_play_and_wait(chan, mbox);
03934       if (cmd)
03935          return cmd;
03936       return ast_play_and_wait(chan, "vm-messages"); /* "messages */
03937    }
03938 }
03939 
03940  /* GREEK SYNTAX 
03941    In greek the plural for old/new is
03942    different so we need the following files   
03943    We also need vm-denExeteMynhmata because 
03944    this syntax is different.
03945    
03946    -> vm-Olds.wav : "Palia"
03947    -> vm-INBOXs.wav : "Nea"
03948    -> vm-denExeteMynhmata : "den exete mynhmata"
03949  */
03950                
03951    
03952 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
03953 {
03954    int res = 0;
03955 
03956    if (vms->newmessages) {
03957       res = ast_play_and_wait(chan, "vm-youhave");
03958       if (!res) 
03959          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
03960       if (!res) {
03961          if ((vms->newmessages == 1)) {
03962             res = ast_play_and_wait(chan, "vm-INBOX");
03963             if (!res)
03964                res = ast_play_and_wait(chan, "vm-message");
03965          } else {
03966             res = ast_play_and_wait(chan, "vm-INBOXs");
03967             if (!res)
03968                res = ast_play_and_wait(chan, "vm-messages");
03969          }
03970       }    
03971    } else if (vms->oldmessages){
03972       res = ast_play_and_wait(chan, "vm-youhave");
03973       if (!res)
03974          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
03975       if ((vms->oldmessages == 1)){
03976          res = ast_play_and_wait(chan, "vm-Old");
03977          if (!res)
03978             res = ast_play_and_wait(chan, "vm-message");
03979       } else {
03980          res = ast_play_and_wait(chan, "vm-Olds");
03981          if (!res)
03982             res = ast_play_and_wait(chan, "vm-messages");
03983       }
03984     } else if (!vms->oldmessages && !vms->newmessages) 
03985          res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
03986     return res;
03987 }
03988    
03989 /* Default English syntax */
03990 static int vm_intro_en(struct ast_channel *chan,struct vm_state *vms)
03991 {
03992    /* Introduce messages they have */
03993    int res;
03994    res = ast_play_and_wait(chan, "vm-youhave");
03995    if (!res) {
03996       if (vms->newmessages) {
03997          res = say_and_wait(chan, vms->newmessages, chan->language);
03998          if (!res)
03999             res = ast_play_and_wait(chan, "vm-INBOX");
04000          if (vms->oldmessages && !res)
04001             res = ast_play_and_wait(chan, "vm-and");
04002          else if (!res) {
04003             if ((vms->newmessages == 1))
04004                res = ast_play_and_wait(chan, "vm-message");
04005             else
04006                res = ast_play_and_wait(chan, "vm-messages");
04007          }
04008             
04009       }
04010       if (!res && vms->oldmessages) {
04011          res = say_and_wait(chan, vms->oldmessages, chan->language);
04012          if (!res)
04013             res = ast_play_and_wait(chan, "vm-Old");
04014          if (!res) {
04015             if (vms->oldmessages == 1)
04016                res = ast_play_and_wait(chan, "vm-message");
04017             else
04018                res = ast_play_and_wait(chan, "vm-messages");
04019          }
04020       }
04021       if (!res) {
04022          if (!vms->oldmessages && !vms->newmessages) {
04023             res = ast_play_and_wait(chan, "vm-no");
04024             if (!res)
04025                res = ast_play_and_wait(chan, "vm-messages");
04026          }
04027       }
04028    }
04029    return res;
04030 }
04031 
04032 /* ITALIAN syntax */
04033 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
04034 {
04035    /* Introduce messages they have */
04036    int res;
04037    if (!vms->oldmessages && !vms->newmessages)
04038       res = ast_play_and_wait(chan, "vm-no") ||
04039          ast_play_and_wait(chan, "vm-message");
04040    else
04041       res = ast_play_and_wait(chan, "vm-youhave");
04042    if (!res && vms->newmessages) {
04043       res = (vms->newmessages == 1) ?
04044          ast_play_and_wait(chan, "digits/un") ||
04045          ast_play_and_wait(chan, "vm-nuovo") ||
04046          ast_play_and_wait(chan, "vm-message") :
04047          /* 2 or more new messages */
04048          say_and_wait(chan, vms->newmessages, chan->language) ||
04049          ast_play_and_wait(chan, "vm-nuovi") ||
04050          ast_play_and_wait(chan, "vm-messages");
04051       if (!res && vms->oldmessages)
04052          res = ast_play_and_wait(chan, "vm-and");
04053    }
04054    if (!res && vms->oldmessages) {
04055       res = (vms->oldmessages == 1) ?
04056          ast_play_and_wait(chan, "digits/un") ||
04057          ast_play_and_wait(chan, "vm-vecchio") ||
04058          ast_play_and_wait(chan, "vm-message") :
04059          /* 2 or more old messages */
04060          say_and_wait(chan, vms->oldmessages, chan->language) ||
04061          ast_play_and_wait(chan, "vm-vecchi") ||
04062          ast_play_and_wait(chan, "vm-messages");
04063    }
04064    return res;
04065 }
04066 
04067 /* SWEDISH syntax */
04068 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
04069 {
04070         /* Introduce messages they have */
04071         int res;
04072 
04073    res = ast_play_and_wait(chan, "vm-youhave");
04074    if (res)
04075       return res;
04076 
04077         if (!vms->oldmessages && !vms->newmessages) {
04078       res = ast_play_and_wait(chan, "vm-no");
04079       res = res ? res : ast_play_and_wait(chan, "vm-messages");
04080       return res;
04081         }
04082 
04083    if (vms->newmessages) {
04084       if ((vms->newmessages == 1)) {
04085          res = ast_play_and_wait(chan, "digits/ett");
04086          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
04087          res = res ? res : ast_play_and_wait(chan, "vm-message");
04088       } else {
04089          res = say_and_wait(chan, vms->newmessages, chan->language);
04090          res = res ? res : ast_play_and_wait(chan, "vm-nya");
04091          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04092       }
04093       if (!res && vms->oldmessages)
04094          res = ast_play_and_wait(chan, "vm-and");
04095    }
04096    if (!res && vms->oldmessages) {
04097       if (vms->oldmessages == 1) {
04098          res = ast_play_and_wait(chan, "digits/ett");
04099          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
04100          res = res ? res : ast_play_and_wait(chan, "vm-message");
04101       } else {
04102          res = say_and_wait(chan, vms->oldmessages, chan->language);
04103          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
04104          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04105       }
04106    }
04107 
04108    return res;
04109 }
04110 
04111 /* NORWEGIAN syntax */
04112 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
04113 {
04114         /* Introduce messages they have */
04115         int res;
04116 
04117    res = ast_play_and_wait(chan, "vm-youhave");
04118    if (res)
04119       return res;
04120 
04121         if (!vms->oldmessages && !vms->newmessages) {
04122       res = ast_play_and_wait(chan, "vm-no");
04123       res = res ? res : ast_play_and_wait(chan, "vm-messages");
04124       return res;
04125         }
04126 
04127    if (vms->newmessages) {
04128       if ((vms->newmessages == 1)) {
04129          res = ast_play_and_wait(chan, "digits/1");
04130          res = res ? res : ast_play_and_wait(chan, "vm-ny");
04131          res = res ? res : ast_play_and_wait(chan, "vm-message");
04132       } else {
04133          res = say_and_wait(chan, vms->newmessages, chan->language);
04134          res = res ? res : ast_play_and_wait(chan, "vm-nye");
04135          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04136       }
04137       if (!res && vms->oldmessages)
04138          res = ast_play_and_wait(chan, "vm-and");
04139    }
04140    if (!res && vms->oldmessages) {
04141       if (vms->oldmessages == 1) {
04142          res = ast_play_and_wait(chan, "digits/1");
04143          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
04144          res = res ? res : ast_play_and_wait(chan, "vm-message");
04145       } else {
04146          res = say_and_wait(chan, vms->oldmessages, chan->language);
04147          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
04148          res = res ? res : ast_play_and_wait(chan, "vm-messages");
04149       }
04150    }
04151 
04152    return res;
04153 }
04154 
04155 /* GERMAN syntax */
04156 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
04157 {
04158    /* Introduce messages they have */
04159    int res;
04160    res = ast_play_and_wait(chan, "vm-youhave");
04161    if (!res) {
04162       if (vms->newmessages) {
04163          if ((vms->newmessages == 1))
04164             res = ast_play_and_wait(chan, "digits/1F");
04165          else
04166             res = say_and_wait(chan, vms->newmessages, chan->language);
04167          if (!res)
04168             res = ast_play_and_wait(chan, "vm-INBOX");
04169          if (vms->oldmessages && !res)
04170             res = ast_play_and_wait(chan, "vm-and");
04171          else if (!res) {
04172             if ((vms->newmessages == 1))
04173                res = ast_play_and_wait(chan, "vm-message");
04174             else
04175                res = ast_play_and_wait(chan, "vm-messages");
04176          }
04177             
04178       }
04179       if (!res && vms->oldmessages) {
04180          if (vms->oldmessages == 1)
04181             res = ast_play_and_wait(chan, "digits/1F");
04182          else
04183             res = say_and_wait(chan, vms->oldmessages, chan->language);
04184          if (!res)
04185             res = ast_play_and_wait(chan, "vm-Old");
04186          if (!res) {
04187             if (vms->oldmessages == 1)
04188                res = ast_play_and_wait(chan, "vm-message");
04189             else
04190                res = ast_play_and_wait(chan, "vm-messages");
04191          }
04192       }
04193       if (!res) {
04194          if (!vms->oldmessages && !vms->newmessages) {
04195             res = ast_play_and_wait(chan, "vm-no");
04196             if (!res)
04197                res = ast_play_and_wait(chan, "vm-messages");
04198          }
04199       }
04200    }
04201    return res;
04202 }
04203 
04204 /* SPANISH syntax */
04205 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
04206 {
04207    /* Introduce messages they have */
04208    int res;
04209    if (!vms->oldmessages && !vms->newmessages) {
04210       res = ast_play_and_wait(chan, "vm-youhaveno");
04211       if (!res)
04212          res = ast_play_and_wait(chan, "vm-messages");
04213    } else {
04214       res = ast_play_and_wait(chan, "vm-youhave");
04215    }
04216    if (!res) {
04217       if (vms->newmessages) {
04218          if (!res) {
04219             if ((vms->newmessages == 1)) {
04220                res = ast_play_and_wait(chan, "digits/1M");
04221                if (!res)
04222                   res = ast_play_and_wait(chan, "vm-message");
04223                if (!res)
04224                   res = ast_play_and_wait(chan, "vm-INBOXs");
04225             } else {
04226                res = say_and_wait(chan, vms->newmessages, chan->language);
04227                if (!res)
04228                   res = ast_play_and_wait(chan, "vm-messages");
04229                if (!res)
04230                   res = ast_play_and_wait(chan, "vm-INBOX");
04231             }
04232          }
04233          if (vms->oldmessages && !res)
04234             res = ast_play_and_wait(chan, "vm-and");
04235       }
04236       if (vms->oldmessages) {
04237          if (!res) {
04238             if (vms->oldmessages == 1) {
04239                res = ast_play_and_wait(chan, "digits/1M");
04240                if (!res)
04241                   res = ast_play_and_wait(chan, "vm-message");
04242                if (!res)
04243                   res = ast_play_and_wait(chan, "vm-Olds");
04244             } else {
04245                res = say_and_wait(chan, vms->oldmessages, chan->language);
04246                if (!res)
04247                   res = ast_play_and_wait(chan, "vm-messages");
04248                if (!res)
04249                   res = ast_play_and_wait(chan, "vm-Old");
04250             }
04251          }
04252       }
04253    }
04254 return res;
04255 }
04256 
04257 /* FRENCH syntax */
04258 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
04259 {
04260    /* Introduce messages they have */
04261    int res;
04262    res = ast_play_and_wait(chan, "vm-youhave");
04263    if (!res) {
04264       if (vms->newmessages) {
04265          res = say_and_wait(chan, vms->newmessages, chan->language);
04266          if (!res)
04267             res = ast_play_and_wait(chan, "vm-INBOX");
04268          if (vms->oldmessages && !res)
04269             res = ast_play_and_wait(chan, "vm-and");
04270          else if (!res) {
04271             if ((vms->newmessages == 1))
04272                res = ast_play_and_wait(chan, "vm-message");
04273             else
04274                res = ast_play_and_wait(chan, "vm-messages");
04275          }
04276             
04277       }
04278       if (!res && vms->oldmessages) {
04279          res = say_and_wait(chan, vms->oldmessages, chan->language);
04280          if (!res) {
04281             if (vms->oldmessages == 1)
04282                res = ast_play_and_wait(chan, "vm-message");
04283             else
04284                res = ast_play_and_wait(chan, "vm-messages");
04285          }
04286          if (!res)
04287             res = ast_play_and_wait(chan, "vm-Old");
04288       }
04289       if (!res) {
04290          if (!vms->oldmessages && !vms->newmessages) {
04291             res = ast_play_and_wait(chan, "vm-no");
04292             if (!res)
04293                res = ast_play_and_wait(chan, "vm-messages");
04294          }
04295       }
04296    }
04297    return res;
04298 }
04299 
04300 /* DUTCH syntax */
04301 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
04302 {
04303    /* Introduce messages they have */
04304    int res;
04305    res = ast_play_and_wait(chan, "vm-youhave");
04306    if (!res) {
04307       if (vms->newmessages) {
04308          res = say_and_wait(chan, vms->newmessages, chan->language);
04309          if (!res) {
04310             if (vms->newmessages == 1)
04311                res = ast_play_and_wait(chan, "vm-INBOXs");
04312             else
04313                res = ast_play_and_wait(chan, "vm-INBOX");
04314          }
04315          if (vms->oldmessages && !res)
04316             res = ast_play_and_wait(chan, "vm-and");
04317          else if (!res) {
04318             if ((vms->newmessages == 1))
04319                res = ast_play_and_wait(chan, "vm-message");
04320             else
04321                res = ast_play_and_wait(chan, "vm-messages");
04322          }
04323             
04324       }
04325       if (!res && vms->oldmessages) {
04326          res = say_and_wait(chan, vms->oldmessages, chan->language);
04327          if (!res) {
04328             if (vms->oldmessages == 1)
04329                res = ast_play_and_wait(chan, "vm-Olds");
04330             else
04331                res = ast_play_and_wait(chan, "vm-Old");
04332          }
04333          if (!res) {
04334             if (vms->oldmessages == 1)
04335                res = ast_play_and_wait(chan, "vm-message");
04336             else
04337                res = ast_play_and_wait(chan, "vm-messages");
04338          }
04339       }
04340       if (!res) {
04341          if (!vms->oldmessages && !vms->newmessages) {
04342             res = ast_play_and_wait(chan, "vm-no");
04343             if (!res)
04344                res = ast_play_and_wait(chan, "vm-messages");
04345          }
04346       }
04347    }
04348    return res;
04349 }
04350 
04351 /* PORTUGUESE syntax */
04352 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
04353 {
04354    /* Introduce messages they have */
04355    int res;
04356    res = ast_play_and_wait(chan, "vm-youhave");
04357    if (!res) {
04358       if (vms->newmessages) {
04359          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
04360          if (!res) {
04361             if ((vms->newmessages == 1)) {
04362                res = ast_play_and_wait(chan, "vm-message");
04363                if (!res)
04364                   res = ast_play_and_wait(chan, "vm-INBOXs");
04365             } else {
04366                res = ast_play_and_wait(chan, "vm-messages");
04367                if (!res)
04368                   res = ast_play_and_wait(chan, "vm-INBOX");
04369             }
04370          }
04371          if (vms->oldmessages && !res)
04372             res = ast_play_and_wait(chan, "vm-and");
04373       }
04374       if (!res && vms->oldmessages) {
04375          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
04376          if (!res) {
04377             if (vms->oldmessages == 1) {
04378                res = ast_play_and_wait(chan, "vm-message");
04379                if (!res)
04380                   res = ast_play_and_wait(chan, "vm-Olds");
04381             } else {
04382                res = ast_play_and_wait(chan, "vm-messages");
04383                if (!res)
04384                   res = ast_play_and_wait(chan, "vm-Old");
04385             }
04386          }
04387       }
04388       if (!res) {
04389          if (!vms->oldmessages && !vms->newmessages) {
04390             res = ast_play_and_wait(chan, "vm-no");
04391             if (!res)
04392                res = ast_play_and_wait(chan, "vm-messages");
04393          }
04394       }
04395    }
04396    return res;
04397 }
04398 
04399 
04400 /* CZECH syntax */
04401 /* in czech there must be declension of word new and message
04402  * czech    : english      : czech  : english
04403  * --------------------------------------------------------
04404  * vm-youhave  : you have 
04405  * vm-novou    : one new      : vm-zpravu    : message
04406  * vm-nove  : 2-4 new      : vm-zpravy    : messages
04407  * vm-novych   : 5-infinite new   : vm-zprav    : messages
04408  * vm-starou   : one old
04409  * vm-stare : 2-4 old 
04410  * vm-starych  : 5-infinite old
04411  * jednu : one - falling 4. 
04412  * vm-no : no  ( no messages )
04413  */
04414 
04415 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
04416 {
04417    int res;
04418    res = ast_play_and_wait(chan, "vm-youhave");
04419    if (!res) {
04420       if (vms->newmessages) {
04421          if (vms->newmessages == 1) {
04422             res = ast_play_and_wait(chan, "digits/jednu");
04423          } else {
04424             res = say_and_wait(chan, vms->newmessages, chan->language);
04425          }
04426          if (!res) {
04427             if ((vms->newmessages == 1))
04428                res = ast_play_and_wait(chan, "vm-novou");
04429             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
04430                res = ast_play_and_wait(chan, "vm-nove");
04431             if (vms->newmessages > 4)
04432                res = ast_play_and_wait(chan, "vm-novych");
04433          }
04434          if (vms->oldmessages && !res)
04435             res = ast_play_and_wait(chan, "vm-and");
04436          else if (!res) {
04437             if ((vms->newmessages == 1))
04438                res = ast_play_and_wait(chan, "vm-zpravu");
04439             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
04440                res = ast_play_and_wait(chan, "vm-zpravy");
04441             if (vms->newmessages > 4)
04442                res = ast_play_and_wait(chan, "vm-zprav");
04443          }
04444       }
04445       if (!res && vms->oldmessages) {
04446          res = say_and_wait(chan, vms->oldmessages, chan->language);
04447          if (!res) {
04448             if ((vms->oldmessages == 1))
04449                res = ast_play_and_wait(chan, "vm-starou");
04450             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
04451                res = ast_play_and_wait(chan, "vm-stare");
04452             if (vms->oldmessages > 4)
04453                res = ast_play_and_wait(chan, "vm-starych");
04454          }
04455          if (!res) {
04456             if ((vms->oldmessages == 1))
04457                res = ast_play_and_wait(chan, "vm-zpravu");
04458             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
04459                res = ast_play_and_wait(chan, "vm-zpravy");
04460             if (vms->oldmessages > 4)
04461                res = ast_play_and_wait(chan, "vm-zprav");
04462          }
04463       }
04464       if (!res) {
04465          if (!vms->oldmessages && !vms->newmessages) {
04466             res = ast_play_and_wait(chan, "vm-no");
04467             if (!res)
04468                res = ast_play_and_wait(chan, "vm-zpravy");
04469          }
04470       }
04471    }
04472    return res;
04473 }
04474 
04475 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
04476 {
04477    /* Play voicemail intro - syntax is different for different languages */
04478    if (!strcasecmp(chan->language, "de")) {  /* GERMAN syntax */
04479       return vm_intro_de(chan, vms);
04480    } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
04481       return vm_intro_es(chan, vms);
04482    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
04483       return vm_intro_it(chan, vms);
04484    } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
04485       return vm_intro_fr(chan, vms);
04486    } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
04487       return vm_intro_nl(chan, vms);
04488    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
04489       return vm_intro_pt(chan, vms);
04490    } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
04491       return vm_intro_cz(chan, vms);
04492    } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
04493       return vm_intro_gr(chan, vms);
04494    } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
04495       return vm_intro_se(chan, vms);
04496    } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
04497       return vm_intro_no(chan, vms);
04498    } else {             /* Default to ENGLISH */
04499       return vm_intro_en(chan, vms);
04500    }
04501 }
04502 
04503 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
04504 {
04505    int res = 0;
04506    /* Play instructions and wait for new command */
04507    while (!res) {
04508       if (vms->starting) {
04509          if (vms->lastmsg > -1) {
04510             res = ast_play_and_wait(chan, "vm-onefor");
04511             if (!res)
04512                res = vm_play_folder_name(chan, vms->vmbox);
04513          }
04514          if (!res)
04515             res = ast_play_and_wait(chan, "vm-opts");
04516       } else {
04517          if (vms->curmsg)
04518             res = ast_play_and_wait(chan, "vm-prev");
04519          if (!res && !skipadvanced)
04520             res = ast_play_and_wait(chan, "vm-advopts");
04521          if (!res)
04522             res = ast_play_and_wait(chan, "vm-repeat");
04523          if (!res && (vms->curmsg != vms->lastmsg))
04524             res = ast_play_and_wait(chan, "vm-next");
04525          if (!res) {
04526             if (!vms->deleted[vms->curmsg])
04527                res = ast_play_and_wait(chan, "vm-delete");
04528             else
04529                res = ast_play_and_wait(chan, "vm-undelete");
04530             if (!res)
04531                res = ast_play_and_wait(chan, "vm-toforward");
04532             if (!res)
04533                res = ast_play_and_wait(chan, "vm-savemessage");
04534          }
04535       }
04536       if (!res)
04537          res = ast_play_and_wait(chan, "vm-helpexit");
04538       if (!res)
04539          res = ast_waitfordigit(chan, 6000);
04540       if (!res) {
04541          vms->repeats++;
04542          if (vms->repeats > 2) {
04543             res = 't';
04544          }
04545       }
04546    }
04547    return res;
04548 }
04549 
04550 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
04551 {
04552    int cmd = 0;
04553    int duration = 0;
04554    int tries = 0;
04555    char newpassword[80] = "";
04556    char newpassword2[80] = "";
04557    char prefile[256]="";
04558    unsigned char buf[256];
04559    int bytes=0;
04560 
04561    if (adsi_available(chan)) {
04562       bytes += adsi_logo(buf + bytes);
04563       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
04564       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
04565       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04566       bytes += adsi_voice_mode(buf + bytes, 0);
04567       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04568    }
04569 
04570    /* First, have the user change their password 
04571       so they won't get here again */
04572    for (;;) {
04573       newpassword[1] = '\0';
04574       newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
04575       if (cmd == '#')
04576          newpassword[0] = '\0';
04577       if (cmd < 0 || cmd == 't' || cmd == '#')
04578          return cmd;
04579       cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
04580       if (cmd < 0 || cmd == 't' || cmd == '#')
04581          return cmd;
04582       newpassword2[1] = '\0';
04583       newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
04584       if (cmd == '#')
04585          newpassword2[0] = '\0';
04586       if (cmd < 0 || cmd == 't' || cmd == '#')
04587          return cmd;
04588       cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
04589       if (cmd < 0 || cmd == 't' || cmd == '#')
04590          return cmd;
04591       if (!strcmp(newpassword, newpassword2))
04592          break;
04593       ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
04594       cmd = ast_play_and_wait(chan, "vm-mismatch");
04595       if (++tries == 3)
04596          return -1;
04597    }
04598    if (ast_strlen_zero(ext_pass_cmd)) 
04599       vm_change_password(vmu,newpassword);
04600    else 
04601       vm_change_password_shell(vmu,newpassword);
04602    
04603    cmd = ast_play_and_wait(chan,"vm-passchanged");
04604 
04605    /* If forcename is set, have the user record their name */  
04606    if (ast_test_flag(vmu, VM_FORCENAME)) {
04607       snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
04608       cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04609       if (cmd < 0 || cmd == 't' || cmd == '#')
04610          return cmd;
04611    }
04612 
04613    /* If forcegreetings is set, have the user record their greetings */
04614    if (ast_test_flag(vmu, VM_FORCEGREET)) {
04615       snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
04616       cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04617       if (cmd < 0 || cmd == 't' || cmd == '#')
04618          return cmd;
04619       snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
04620       cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04621       if (cmd < 0 || cmd == 't' || cmd == '#')
04622          return cmd;
04623    }
04624 
04625    return cmd;
04626 }
04627 
04628 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
04629 {
04630    int cmd = 0;
04631    int retries = 0;
04632    int duration = 0;
04633    char newpassword[80] = "";
04634    char newpassword2[80] = "";
04635    char prefile[256]="";
04636    unsigned char buf[256];
04637    int bytes=0;
04638 
04639    if (adsi_available(chan))
04640    {
04641       bytes += adsi_logo(buf + bytes);
04642       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
04643       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
04644       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04645       bytes += adsi_voice_mode(buf + bytes, 0);
04646       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04647    }
04648    while ((cmd >= 0) && (cmd != 't')) {
04649       if (cmd)
04650          retries = 0;
04651       switch (cmd) {
04652       case '1':
04653          snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
04654          cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04655          break;
04656       case '2': 
04657          snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
04658          cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04659          break;
04660       case '3': 
04661          snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
04662          cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04663          break;
04664       case '4': 
04665          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
04666          break;
04667       case '5':
04668          if (vmu->password[0] == '-') {
04669             cmd = ast_play_and_wait(chan, "vm-no");
04670             break;
04671          }
04672          newpassword[1] = '\0';
04673          newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
04674          if (cmd == '#')
04675             newpassword[0] = '\0';
04676          else {
04677             if (cmd < 0)
04678                break;
04679             if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
04680                break;
04681             }
04682          }
04683          newpassword2[1] = '\0';
04684          newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
04685          if (cmd == '#')
04686             newpassword2[0] = '\0';
04687          else {
04688             if (cmd < 0)
04689                break;
04690 
04691             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
04692                break;
04693             }
04694          }
04695          if (strcmp(newpassword, newpassword2)) {
04696             ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
04697             cmd = ast_play_and_wait(chan, "vm-mismatch");
04698             break;
04699          }
04700          if (ast_strlen_zero(ext_pass_cmd)) 
04701             vm_change_password(vmu,newpassword);
04702          else 
04703             vm_change_password_shell(vmu,newpassword);
04704          ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
04705          cmd = ast_play_and_wait(chan,"vm-passchanged");
04706          break;
04707       case '*': 
04708          cmd = 't';
04709          break;
04710       default: 
04711          cmd = ast_play_and_wait(chan,"vm-options");
04712          if (!cmd)
04713             cmd = ast_waitfordigit(chan,6000);
04714          if (!cmd)
04715             retries++;
04716          if (retries > 3)
04717             cmd = 't';
04718        }
04719    }
04720    if (cmd == 't')
04721       cmd = 0;
04722    return cmd;
04723 }
04724 
04725 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
04726 {
04727    int cmd = 0;
04728    int retries = 0;
04729    int duration = 0;
04730    char prefile[256]="";
04731    unsigned char buf[256];
04732    int bytes=0;
04733 
04734    if (adsi_available(chan))
04735    {
04736       bytes += adsi_logo(buf + bytes);
04737       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
04738       bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
04739       bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04740       bytes += adsi_voice_mode(buf + bytes, 0);
04741       adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04742    }
04743    snprintf(prefile,sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
04744    while((cmd >= 0) && (cmd != 't')) {
04745       if (cmd)
04746          retries = 0;
04747       RETRIEVE(prefile, -1);
04748       if (ast_fileexists(prefile, NULL, NULL) > 0) {
04749          switch (cmd) {
04750          case '1':
04751             cmd = play_record_review(chan,"vm-rec-temp",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04752             break;
04753          case '2':
04754             DELETE(prefile, -1, prefile);
04755             ast_play_and_wait(chan,"vm-tempremoved");
04756             cmd = 't';  
04757             break;
04758          case '*': 
04759             cmd = 't';
04760             break;
04761          default:
04762             if (ast_fileexists(prefile, NULL, NULL) > 0) {
04763                cmd = ast_play_and_wait(chan,"vm-tempgreeting2");
04764             } else {
04765                cmd = ast_play_and_wait(chan,"vm-tempgreeting");
04766             } if (!cmd) {
04767                cmd = ast_waitfordigit(chan,6000);
04768             } if (!cmd) {
04769                retries++;
04770             } if (retries > 3) {
04771                cmd = 't';
04772             }
04773          }
04774       } else {
04775          play_record_review(chan,"vm-rec-temp",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain);
04776          cmd = 't';  
04777       }
04778       DISPOSE(prefile, -1);
04779    }
04780    if (cmd == 't')
04781       cmd = 0;
04782    return cmd;
04783 }
04784 
04785 /* GREEK SYNTAX */
04786    
04787 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04788 {
04789    int cmd=0;
04790 
04791    if (vms->lastmsg > -1) {
04792       cmd = play_message(chan, vmu, vms);
04793    } else {
04794       cmd = ast_play_and_wait(chan, "vm-youhaveno");
04795       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
04796          if (!cmd) {
04797             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
04798             cmd = ast_play_and_wait(chan, vms->fn);
04799          }
04800          if (!cmd)
04801             cmd = ast_play_and_wait(chan, "vm-messages");
04802       } else {
04803          if (!cmd)
04804             cmd = ast_play_and_wait(chan, "vm-messages");
04805          if (!cmd) {
04806             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04807             cmd = ast_play_and_wait(chan, vms->fn);
04808          }
04809       }
04810    } 
04811    return cmd;
04812 }
04813 
04814 /* Default English syntax */
04815 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04816 {
04817    int cmd=0;
04818 
04819    if (vms->lastmsg > -1) {
04820       cmd = play_message(chan, vmu, vms);
04821    } else {
04822       cmd = ast_play_and_wait(chan, "vm-youhave");
04823       if (!cmd) 
04824          cmd = ast_play_and_wait(chan, "vm-no");
04825       if (!cmd) {
04826          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04827          cmd = ast_play_and_wait(chan, vms->fn);
04828       }
04829       if (!cmd)
04830          cmd = ast_play_and_wait(chan, "vm-messages");
04831    }
04832    return cmd;
04833 }
04834 
04835 /* ITALIAN syntax */
04836 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04837 {
04838         int cmd=0;
04839 
04840         if (vms->lastmsg > -1) {
04841                 cmd = play_message(chan, vmu, vms);
04842         } else {
04843                 cmd = ast_play_and_wait(chan, "vm-no");
04844                 if (!cmd)
04845                         cmd = ast_play_and_wait(chan, "vm-message");
04846                 if (!cmd) {
04847                         snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04848                         cmd = ast_play_and_wait(chan, vms->fn);
04849                 }
04850         }
04851         return cmd;
04852 }
04853 
04854 /* SPANISH syntax */
04855 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04856 {
04857    int cmd=0;
04858 
04859    if (vms->lastmsg > -1) {
04860       cmd = play_message(chan, vmu, vms);
04861    } else {
04862       cmd = ast_play_and_wait(chan, "vm-youhaveno");
04863       if (!cmd)
04864          cmd = ast_play_and_wait(chan, "vm-messages");
04865       if (!cmd) {
04866          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04867          cmd = ast_play_and_wait(chan, vms->fn);
04868       }
04869    }
04870    return cmd;
04871 }
04872 
04873 /* PORTUGUESE syntax */
04874 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04875 {
04876    int cmd=0;
04877 
04878    if (vms->lastmsg > -1) {
04879       cmd = play_message(chan, vmu, vms);
04880    } else {
04881       cmd = ast_play_and_wait(chan, "vm-no");
04882       if (!cmd) {
04883          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
04884          cmd = ast_play_and_wait(chan, vms->fn);
04885       }
04886       if (!cmd)
04887          cmd = ast_play_and_wait(chan, "vm-messages");
04888    }
04889    return cmd;
04890 }
04891 
04892 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
04893 {
04894    if (!strcasecmp(chan->language, "es")) {  /* SPANISH */
04895       return vm_browse_messages_es(chan, vms, vmu);
04896    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
04897       return vm_browse_messages_it(chan, vms, vmu);
04898    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE */
04899       return vm_browse_messages_pt(chan, vms, vmu);
04900    } else if (!strcasecmp(chan->language, "gr")){
04901       return vm_browse_messages_gr(chan, vms, vmu);   /* GREEK */
04902    } else { /* Default to English syntax */
04903       return vm_browse_messages_en(chan, vms, vmu);
04904    }
04905 }
04906 
04907 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
04908             struct ast_vm_user *res_vmu, const char *context, const char *prefix,
04909             int skipuser, int maxlogins, int silent)
04910 {
04911    int useadsi=0, valid=0, logretries=0;
04912    char password[AST_MAX_EXTENSION]="", *passptr;
04913    struct ast_vm_user vmus, *vmu = NULL;
04914 
04915    /* If ADSI is supported, setup login screen */
04916    adsi_begin(chan, &useadsi);
04917    if (!skipuser && useadsi)
04918       adsi_login(chan);
04919    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
04920       ast_log(LOG_WARNING, "Couldn't stream login file\n");
04921       return -1;
04922    }
04923    
04924    /* Authenticate them and get their mailbox/password */
04925    
04926    while (!valid && (logretries < maxlogins)) {
04927       /* Prompt for, and read in the username */
04928       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
04929          ast_log(LOG_WARNING, "Couldn't read username\n");
04930          return -1;
04931       }
04932       if (ast_strlen_zero(mailbox)) {
04933          if (chan->cid.cid_num) {
04934             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
04935          } else {
04936             if (option_verbose > 2)
04937                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");  
04938             return -1;
04939          }
04940       }
04941       if (useadsi)
04942          adsi_password(chan);
04943 
04944       if (!ast_strlen_zero(prefix)) {
04945          char fullusername[80] = "";
04946          ast_copy_string(fullusername, prefix, sizeof(fullusername));
04947          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
04948          ast_copy_string(mailbox, fullusername, mailbox_size);
04949       }
04950 
04951       vmu = find_user(&vmus, context, mailbox);
04952       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
04953          /* saved password is blank, so don't bother asking */
04954          password[0] = '\0';
04955       } else {
04956          if (ast_streamfile(chan, "vm-password", chan->language)) {
04957             ast_log(LOG_WARNING, "Unable to stream password file\n");
04958             return -1;
04959          }
04960          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
04961             ast_log(LOG_WARNING, "Unable to read password\n");
04962             return -1;
04963          }
04964       }
04965 
04966       if (vmu) {
04967          passptr = vmu->password;
04968          if (passptr[0] == '-') passptr++;
04969       }
04970       if (vmu && !strcmp(passptr, password))
04971          valid++;
04972       else {
04973          if (option_verbose > 2)
04974             ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
04975          if (!ast_strlen_zero(prefix))
04976             mailbox[0] = '\0';
04977       }
04978       logretries++;
04979       if (!valid) {
04980          if (skipuser || logretries >= maxlogins) {
04981             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
04982                ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
04983                return -1;
04984             }
04985          } else {
04986             if (useadsi)
04987                adsi_login(chan);
04988             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
04989                ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
04990                return -1;
04991             }
04992          }
04993          if (ast_waitstream(chan, "")) /* Channel is hung up */
04994             return -1;
04995       }
04996    }
04997    if (!valid && (logretries >= maxlogins)) {
04998       ast_stopstream(chan);
04999       ast_play_and_wait(chan, "vm-goodbye");
05000       return -1;
05001    }
05002    if (vmu && !skipuser) {
05003       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
05004    }
05005    return 0;
05006 }
05007 
05008 static int vm_execmain(struct ast_channel *chan, void *data)
05009 {
05010    /* XXX This is, admittedly, some pretty horrendus code.  For some
05011       reason it just seemed a lot easier to do with GOTO's.  I feel
05012       like I'm back in my GWBASIC days. XXX */
05013    int res=-1;
05014    int cmd=0;
05015    int valid = 0;
05016    struct localuser *u;
05017    char prefixstr[80] ="";
05018    char ext_context[256]="";
05019    int box;
05020    int useadsi = 0;
05021    int skipuser = 0;
05022    struct vm_state vms;
05023    struct ast_vm_user *vmu = NULL, vmus;
05024    char *context=NULL;
05025    int silentexit = 0;
05026    struct ast_flags flags = { 0 };
05027    signed char record_gain = 0;
05028 
05029    LOCAL_USER_ADD(u);
05030 
05031    memset(&vms, 0, sizeof(vms));
05032    vms.lastmsg = -1;
05033 
05034    memset(&vmus, 0, sizeof(vmus));
05035 
05036    if (chan->_state != AST_STATE_UP)
05037       ast_answer(chan);
05038 
05039    if (!ast_strlen_zero(data)) {
05040       char *tmp;
05041       int argc;
05042       char *argv[2];
05043       char *opts[OPT_ARG_ARRAY_SIZE];
05044 
05045       tmp = ast_strdupa(data);
05046       argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
05047       if (argc == 2) {
05048          if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
05049             LOCAL_USER_REMOVE(u);
05050             return -1;
05051          }
05052          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
05053             int gain;
05054 
05055             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
05056                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
05057                LOCAL_USER_REMOVE(u);
05058                return -1;
05059             } else {
05060                record_gain = (signed char) gain;
05061             }
05062          }
05063       } else {
05064          /* old style options parsing */
05065          while (*argv[0]) {
05066             if (*argv[0] == 's') {
05067                ast_set_flag(&flags, OPT_SILENT);
05068                argv[0]++;
05069             } else if (*argv[0] == 'p') {
05070                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
05071                argv[0]++;
05072             } else 
05073                break;
05074          }
05075 
05076       }
05077 
05078       valid = ast_test_flag(&flags, OPT_SILENT);
05079 
05080       if ((context = strchr(argv[0], '@')))
05081          *context++ = '\0';
05082 
05083       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
05084          ast_copy_string(prefixstr, argv[0], sizeof(prefixstr));
05085       else
05086          ast_copy_string(vms.username, argv[0], sizeof(vms.username));
05087 
05088       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
05089          skipuser++;
05090       else {
05091          if (!ast_strlen_zero(vms.username))
05092             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 : "");
05093          valid = 0;
05094       }
05095    }
05096 
05097    if (!valid)
05098       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
05099 
05100    if (!res) {
05101       valid = 1;
05102       if (!skipuser)
05103          vmu = &vmus;
05104    } else {
05105       res = 0;
05106    }
05107 
05108    /* If ADSI is supported, setup login screen */
05109    adsi_begin(chan, &useadsi);
05110 
05111    if (!valid)
05112       goto out;
05113 
05114    vms.deleted = calloc(vmu->maxmsg, sizeof(int));
05115    vms.heard = calloc(vmu->maxmsg, sizeof(int));
05116    
05117    /* Set language from config to override channel language */
05118    if (!ast_strlen_zero(vmu->language))
05119       ast_copy_string(chan->language, vmu->language, sizeof(chan->language));
05120    create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
05121    /* Retrieve old and new message counts */
05122    res = open_mailbox(&vms, vmu, 1);
05123    if (res == ERROR_LOCK_PATH)
05124       goto out;
05125    vms.oldmessages = vms.lastmsg + 1;
05126    /* Start in INBOX */
05127    res = open_mailbox(&vms, vmu, 0);
05128    if (res == ERROR_LOCK_PATH)
05129       goto out;
05130    vms.newmessages = vms.lastmsg + 1;
05131       
05132    /* Select proper mailbox FIRST!! */
05133    if (!vms.newmessages && vms.oldmessages) {
05134       /* If we only have old messages start here */
05135       res = open_mailbox(&vms, vmu, 1);
05136       if (res == ERROR_LOCK_PATH)
05137          goto out;
05138    }
05139 
05140    if (useadsi)
05141       adsi_status(chan, &vms);
05142    res = 0;
05143 
05144    /* Check to see if this is a new user */
05145    if (!strcasecmp(vmu->mailbox, vmu->password) && 
05146        (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
05147       if (ast_play_and_wait(chan, "vm-newuser") == -1)
05148          ast_log(LOG_WARNING, "Couldn't stream new user file\n");
05149       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
05150       if ((cmd == 't') || (cmd == '#')) {
05151          /* Timeout */
05152          res = 0;
05153          goto out;
05154       } else if (cmd < 0) {
05155          /* Hangup */
05156          res = -1;
05157          goto out;
05158       }
05159    }
05160 
05161    cmd = vm_intro(chan, &vms);
05162 
05163    vms.repeats = 0;
05164    vms.starting = 1;
05165    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
05166       /* Run main menu */
05167       switch(cmd) {
05168       case '1':
05169          vms.curmsg = 0;
05170          /* Fall through */
05171       case '5':
05172          cmd = vm_browse_messages(chan, &vms, vmu);
05173          break;
05174       case '2': /* Change folders */
05175          if (useadsi)
05176             adsi_folders(chan, 0, "Change to folder...");
05177          cmd = get_folder2(chan, "vm-changeto", 0);
05178          if (cmd == '#') {
05179             cmd = 0;
05180          } else if (cmd > 0) {
05181             cmd = cmd - '0';
05182             res = close_mailbox(&vms, vmu);
05183             if (res == ERROR_LOCK_PATH)
05184                goto out;
05185             res = open_mailbox(&vms, vmu, cmd);
05186             if (res == ERROR_LOCK_PATH)
05187                goto out;
05188             cmd = 0;
05189          }
05190          if (useadsi)
05191             adsi_status2(chan, &vms);
05192             
05193          if (!cmd)
05194             cmd = vm_play_folder_name(chan, vms.vmbox);
05195 
05196          vms.starting = 1;
05197          break;
05198       case '3': /* Advanced options */
05199          cmd = 0;
05200          vms.repeats = 0;
05201          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
05202             switch(cmd) {
05203             case '1': /* Reply */
05204                if (vms.lastmsg > -1 && !vms.starting) {
05205                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
05206                   if (cmd == ERROR_LOCK_PATH) {
05207                      res = cmd;
05208                      goto out;
05209                   }
05210                } else
05211                   cmd = ast_play_and_wait(chan, "vm-sorry");
05212                cmd = 't';
05213                break;
05214             case '2': /* Callback */
05215                if (option_verbose > 2 && !vms.starting)
05216                   ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
05217                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
05218                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
05219                   if (cmd == 9) {
05220                      silentexit = 1;
05221                      goto out;
05222                   } else if (cmd == ERROR_LOCK_PATH) {
05223                      res = cmd;
05224                      goto out;
05225                   }
05226                }
05227                else 
05228                   cmd = ast_play_and_wait(chan, "vm-sorry");
05229                cmd = 't';
05230                break;
05231             case '3': /* Envelope */
05232                if (vms.lastmsg > -1 && !vms.starting) {
05233                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
05234                   if (cmd == ERROR_LOCK_PATH) {
05235                      res = cmd;
05236                      goto out;
05237                   }
05238                } else
05239                   cmd = ast_play_and_wait(chan, "vm-sorry");
05240                cmd = 't';
05241                break;
05242             case '4': /* Dialout */
05243                if (!ast_strlen_zero(vmu->dialout)) {
05244                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
05245                   if (cmd == 9) {
05246                      silentexit = 1;
05247                      goto out;
05248                   }
05249                }
05250                else 
05251                   cmd = ast_play_and_wait(chan, "vm-sorry");
05252                cmd = 't';
05253                break;
05254 
05255             case '5': /* Leave VoiceMail */
05256                if (ast_test_flag(vmu, VM_SVMAIL)) {
05257                   cmd = forward_message(chan, vmu->context, vms.curdir, vms.curmsg, vmu, vmfmts, 1, record_gain);
05258                   if (cmd == ERROR_LOCK_PATH) {
05259                      res = cmd;
05260                      goto out;
05261                   }
05262                } else
05263                   cmd = ast_play_and_wait(chan,"vm-sorry");
05264                cmd='t';
05265                break;
05266                
05267             case '*': /* Return to main menu */
05268                cmd = 't';
05269                break;
05270 
05271             default:
05272                cmd = 0;
05273                if (!vms.starting) {
05274                   cmd = ast_play_and_wait(chan, "vm-toreply");
05275                }
05276                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
05277                   cmd = ast_play_and_wait(chan, "vm-tocallback");
05278                }
05279                if (!cmd && !vms.starting) {
05280                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
05281                }
05282                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
05283                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
05284                }
05285                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
05286                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
05287                if (!cmd)
05288                   cmd = ast_play_and_wait(chan, "vm-starmain");
05289                if (!cmd)
05290                   cmd = ast_waitfordigit(chan,6000);
05291                if (!cmd)
05292                   vms.repeats++;
05293                if (vms.repeats > 3)
05294                   cmd = 't';
05295             }
05296          }
05297          if (cmd == 't') {
05298             cmd = 0;
05299             vms.repeats = 0;
05300          }
05301          break;
05302       case '4':
05303          if (vms.curmsg) {
05304             vms.curmsg--;
05305             cmd = play_message(chan, vmu, &vms);
05306          } else {
05307             cmd = ast_play_and_wait(chan, "vm-nomore");
05308          }
05309          break;
05310       case '6':
05311          if (vms.curmsg < vms.lastmsg) {
05312             vms.curmsg++;
05313             cmd = play_message(chan, vmu, &vms);
05314          } else {
05315             cmd = ast_play_and_wait(chan, "vm-nomore");
05316          }
05317          break;
05318       case '7':
05319          vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
05320          if (useadsi)
05321             adsi_delete(chan, &vms);
05322          if (vms.deleted[vms.curmsg]) 
05323             cmd = ast_play_and_wait(chan, "vm-deleted");
05324          else
05325             cmd = ast_play_and_wait(chan, "vm-undeleted");
05326          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
05327             if (vms.curmsg < vms.lastmsg) {
05328                vms.curmsg++;
05329                cmd = play_message(chan, vmu, &vms);
05330             } else {
05331                cmd = ast_play_and_wait(chan, "vm-nomore");
05332             }
05333          }
05334          break;
05335    
05336       case '8':
05337          if (vms.lastmsg > -1) {
05338             cmd = forward_message(chan, vmu->context, vms.curdir, vms.curmsg, vmu, vmfmts, 0, record_gain);
05339             if (cmd == ERROR_LOCK_PATH) {
05340                res = cmd;
05341                goto out;
05342             }
05343          } else
05344             cmd = ast_play_and_wait(chan, "vm-nomore");
05345          break;
05346       case '9':
05347          if (useadsi)
05348             adsi_folders(chan, 1, "Save to folder...");
05349          cmd = get_folder2(chan, "vm-savefolder", 1);
05350          box = 0; /* Shut up compiler */
05351          if (cmd == '#') {
05352             cmd = 0;
05353             break;
05354          } else if (cmd > 0) {
05355             box = cmd = cmd - '0';
05356             cmd = save_to_folder(vmu, vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
05357             if (cmd == ERROR_LOCK_PATH) {
05358                res = cmd;
05359                goto out;
05360             } else if (!cmd) {
05361                vms.deleted[vms.curmsg] = 1;
05362             } else {
05363                vms.deleted[vms.curmsg] = 0;
05364                vms.heard[vms.curmsg] = 0;
05365             }
05366          }
05367          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
05368          if (useadsi)
05369             adsi_message(chan, &vms);
05370          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
05371          if (!cmd) {
05372             cmd = ast_play_and_wait(chan, "vm-message");
05373             if (!cmd)
05374                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
05375             if (!cmd)
05376                cmd = ast_play_and_wait(chan, "vm-savedto");
05377             if (!cmd)
05378                cmd = vm_play_folder_name(chan, vms.fn);
05379          } else {
05380             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
05381          }
05382          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
05383             if (vms.curmsg < vms.lastmsg) {
05384                vms.curmsg++;
05385                cmd = play_message(chan, vmu, &vms);
05386             } else {
05387                cmd = ast_play_and_wait(chan, "vm-nomore");
05388             }
05389          }
05390          break;
05391       case '*':
05392          if (!vms.starting) {
05393             cmd = ast_play_and_wait(chan, "vm-onefor");
05394             if (!cmd)
05395                cmd = vm_play_folder_name(chan, vms.vmbox);
05396             if (!cmd)
05397                cmd = ast_play_and_wait(chan, "vm-opts");
05398             if (!cmd)
05399                cmd = vm_instructions(chan, &vms, 1);
05400          } else
05401             cmd = 0;
05402          break;
05403       case '0':
05404          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
05405          if (useadsi)
05406             adsi_status(chan, &vms);
05407          break;
05408       default: /* Nothing */
05409          cmd = vm_instructions(chan, &vms, 0);
05410          break;
05411       }
05412    }
05413    if ((cmd == 't') || (cmd == '#')) {
05414       /* Timeout */
05415       res = 0;
05416    } else {
05417       /* Hangup */
05418       res = -1;
05419    }
05420 
05421 out:
05422    if (res > -1) {
05423       ast_stopstream(chan);
05424       adsi_goodbye(chan);
05425       if (valid) {
05426          if (silentexit)
05427             res = ast_play_and_wait(chan, "vm-dialout");
05428          else 
05429             res = ast_play_and_wait(chan, "vm-goodbye");
05430          if (res > 0)
05431             res = 0;
05432       }
05433       if (useadsi)
05434          adsi_unload_session(chan);
05435    }
05436    if (vmu)
05437       close_mailbox(&vms, vmu);
05438    if (valid) {
05439       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
05440       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
05441       run_externnotify(vmu->context, vmu->mailbox);
05442    }
05443    if (vmu)
05444       free_user(vmu);
05445    if (vms.deleted)
05446       free(vms.deleted);
05447    if (vms.heard)
05448       free(vms.heard);
05449    LOCAL_USER_REMOVE(u);
05450 
05451    return res;
05452 }
05453 
05454 static int vm_exec(struct ast_channel *chan, void *data)
05455 {
05456    int res = 0;
05457    struct localuser *u;
05458    char *tmp;
05459    struct leave_vm_options leave_options;
05460    int argc;
05461    char *argv[2];
05462    struct ast_flags flags = { 0 };
05463    char *opts[OPT_ARG_ARRAY_SIZE];
05464    
05465    LOCAL_USER_ADD(u);
05466    
05467    memset(&leave_options, 0, sizeof(leave_options));
05468 
05469    if (chan->_state != AST_STATE_UP)
05470       ast_answer(chan);
05471 
05472    if (!ast_strlen_zero(data)) {
05473       tmp = ast_strdupa((char *)data);
05474       if (!tmp) {
05475          ast_log(LOG_ERROR, "Out of memory\n");
05476          LOCAL_USER_REMOVE(u);
05477          return -1;
05478       }
05479       argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
05480       if (argc == 2) {
05481          if (ast_app_parse_options(vm_app_options, &flags, opts, argv[1])) {
05482             LOCAL_USER_REMOVE(u);
05483             return -1;
05484          }
05485          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
05486          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
05487             int gain;
05488 
05489             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
05490                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
05491                LOCAL_USER_REMOVE(u);
05492                return -1;
05493             } else {
05494                leave_options.record_gain = (signed char) gain;
05495             }
05496          }
05497       } else {
05498          /* old style options parsing */
05499          while (*argv[0]) {
05500             if (*argv[0] == 's') {
05501                ast_set_flag(&leave_options, OPT_SILENT);
05502                argv[0]++;
05503             } else if (*argv[0] == 'b') {
05504                ast_set_flag(&leave_options, OPT_BUSY_GREETING);
05505                argv[0]++;
05506             } else if (*argv[0] == 'u') {
05507                ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
05508                argv[0]++;
05509             } else if (*argv[0] == 'j') {
05510                ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
05511                argv[0]++;
05512             } else 
05513                break;
05514          }
05515       }
05516    } else {
05517       char tmp[256];
05518       res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
05519       if (res < 0) {
05520          LOCAL_USER_REMOVE(u);
05521          return res;
05522       }
05523       if (ast_strlen_zero(tmp)) {
05524          LOCAL_USER_REMOVE(u);
05525          return 0;
05526       }
05527       argv[0] = ast_strdupa(tmp);
05528    }
05529 
05530    res = leave_voicemail(chan, argv[0], &leave_options);
05531 
05532    if (res == ERROR_LOCK_PATH) {
05533       ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
05534       /*Send the call to n+101 priority, where n is the current priority*/
05535       if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || option_priority_jumping)
05536          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
05537             ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
05538       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
05539       res = 0;
05540    }
05541    
05542    LOCAL_USER_REMOVE(u);
05543 
05544    return res;
05545 }
05546 
05547 static int append_mailbox(char *context, char *mbox, char *data)
05548 {
05549    /* Assumes lock is already held */
05550    char *tmp;
05551    char *stringp;
05552    char *s;
05553    struct ast_vm_user *vmu;
05554 
05555    tmp = ast_strdupa(data);
05556 
05557    vmu = malloc(sizeof(struct ast_vm_user));
05558    if (vmu) {
05559       memset(vmu, 0, sizeof(struct ast_vm_user));
05560       ast_copy_string(vmu->context, context, sizeof(vmu->context));
05561       ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
05562 
05563       populate_defaults(vmu);
05564 
05565       stringp = tmp;
05566       if ((s = strsep(&stringp, ","))) 
05567          ast_copy_string(vmu->password, s, sizeof(vmu->password));
05568       if (stringp && (s = strsep(&stringp, ","))) 
05569          ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
05570       if (stringp && (s = strsep(&stringp, ","))) 
05571          ast_copy_string(vmu->email, s, sizeof(vmu->email));
05572       if (stringp && (s = strsep(&stringp, ","))) 
05573          ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
05574       if (stringp && (s = strsep(&stringp, ","))) 
05575          apply_options(vmu, s);
05576       
05577       vmu->next = NULL;
05578       if (usersl)
05579          usersl->next = vmu;
05580       else
05581          users = vmu;
05582       usersl = vmu;
05583    }
05584    return 0;
05585 }
05586 
05587 static int vm_box_exists(struct ast_channel *chan, void *data) 
05588 {
05589    struct localuser *u;
05590    struct ast_vm_user svm;
05591    char *context, *box;
05592    int priority_jump = 0;
05593    AST_DECLARE_APP_ARGS(args,
05594       AST_APP_ARG(mbox);
05595       AST_APP_ARG(options);
05596    );
05597 
05598    if (ast_strlen_zero(data)) {
05599       ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
05600       return -1;
05601    }
05602 
05603    LOCAL_USER_ADD(u);
05604 
05605    box = ast_strdupa(data);
05606    if (!box) {
05607       ast_log(LOG_ERROR, "Out of memory\n");
05608       LOCAL_USER_REMOVE(u);
05609       return -1;
05610    }
05611 
05612    AST_STANDARD_APP_ARGS(args, box);
05613 
05614    if (args.options) {
05615       if (strchr(args.options, 'j'))
05616          priority_jump = 1;
05617    }
05618 
05619    if ((context = strchr(args.mbox, '@'))) {
05620       *context = '\0';
05621       context++;
05622    }
05623 
05624    if (find_user(&svm, context, args.mbox)) {
05625       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
05626       if (priority_jump || option_priority_jumping)
05627          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) 
05628             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);
05629    } else
05630       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
05631    LOCAL_USER_REMOVE(u);
05632    return 0;
05633 }
05634 
05635 static int vmauthenticate(struct ast_channel *chan, void *data)
05636 {
05637    struct localuser *u;
05638    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
05639    struct ast_vm_user vmus;
05640    char *options = NULL;
05641    int silent = 0, skipuser = 0;
05642    int res = -1;
05643 
05644    LOCAL_USER_ADD(u);
05645    
05646    if (s) {
05647       s = ast_strdupa(s);
05648       if (!s) {
05649          ast_log(LOG_ERROR, "Out of memory\n");
05650          return -1;
05651       }
05652       user = strsep(&s, "|");
05653       options = strsep(&s, "|");
05654       if (user) {
05655          s = user;
05656          user = strsep(&s, "@");
05657          context = strsep(&s, "");
05658          if (!ast_strlen_zero(user))
05659             skipuser++;
05660          ast_copy_string(mailbox, user, sizeof(mailbox));
05661       }
05662    }
05663 
05664    if (options) {
05665       silent = (strchr(options, 's')) != NULL;
05666    }
05667 
05668    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
05669       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
05670       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
05671       ast_play_and_wait(chan, "auth-thankyou");
05672       res = 0;
05673    }
05674 
05675    LOCAL_USER_REMOVE(u);
05676    return res;
05677 }
05678 
05679 static char show_voicemail_users_help[] =
05680 "Usage: show voicemail users [for <context>]\n"
05681 "       Lists all mailboxes currently set up\n";
05682 
05683 static char show_voicemail_zones_help[] =
05684 "Usage: show voicemail zones\n"
05685 "       Lists zone message formats\n";
05686 
05687 static int handle_show_voicemail_users(int fd, int argc, char *argv[])
05688 {
05689    struct ast_vm_user *vmu = users;
05690    char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
05691 
05692    if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
05693    else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
05694 
05695    if (vmu) {
05696       if (argc == 3)
05697          ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
05698       else {
05699          int count = 0;
05700          while (vmu) {
05701             if (!strcmp(argv[4],vmu->context))
05702                count++;
05703             vmu = vmu->next;
05704          }
05705          if (count) {
05706             vmu = users;
05707             ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
05708          } else {
05709             ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
05710             return RESULT_FAILURE;
05711          }
05712       }
05713       while (vmu) {
05714          char dirname[256];
05715          DIR *vmdir;
05716          struct dirent *vment;
05717          int vmcount = 0;
05718          char count[12];
05719 
05720          if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
05721             make_dir(dirname, 255, vmu->context, vmu->mailbox, "INBOX");
05722             if ((vmdir = opendir(dirname))) {
05723                /* No matter what the format of VM, there will always be a .txt file for each message. */
05724                while ((vment = readdir(vmdir)))
05725                   if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
05726                      vmcount++;
05727                closedir(vmdir);
05728             }
05729             snprintf(count,sizeof(count),"%d",vmcount);
05730             ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
05731          }
05732          vmu = vmu->next;
05733       }
05734    } else {
05735       ast_cli(fd, "There are no voicemail users currently defined\n");
05736       return RESULT_FAILURE;
05737    }
05738    return RESULT_SUCCESS;
05739 }
05740 
05741 static int handle_show_voicemail_zones(int fd, int argc, char *argv[])
05742 {
05743    struct vm_zone *zone = zones;
05744    char *output_format = "%-15s %-20s %-45s\n";
05745 
05746    if (argc != 3) return RESULT_SHOWUSAGE;
05747 
05748    if (zone) {
05749       ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
05750       while (zone) {
05751          ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
05752          zone = zone->next;
05753       }
05754    } else {
05755       ast_cli(fd, "There are no voicemail zones currently defined\n");
05756       return RESULT_FAILURE;
05757    }
05758    return RESULT_SUCCESS;
05759 }
05760 
05761 static char *complete_show_voicemail_users(char *line, char *word, int pos, int state)
05762 {
05763    int which = 0;
05764    struct ast_vm_user *vmu = users;
05765    char *context = "";
05766 
05767    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
05768    if (pos > 4)
05769       return NULL;
05770    if (pos == 3) {
05771       if (state == 0)
05772          return strdup("for");
05773       else
05774          return NULL;
05775    }
05776    while (vmu) {
05777       if (!strncasecmp(word, vmu->context, strlen(word))) {
05778          if (context && strcmp(context, vmu->context)) {
05779             if (++which > state) {
05780                return strdup(vmu->context);
05781             }
05782             context = vmu->context;
05783          }
05784       }
05785       vmu = vmu->next;
05786    }
05787    return NULL;
05788 }
05789 
05790 static struct ast_cli_entry show_voicemail_users_cli =
05791    { { "show", "voicemail", "users", NULL },
05792    handle_show_voicemail_users, "List defined voicemail boxes",
05793    show_voicemail_users_help, complete_show_voicemail_users };
05794 
05795 static struct ast_cli_entry show_voicemail_zones_cli =
05796    { { "show", "voicemail", "zones", NULL },
05797    handle_show_voicemail_zones, "List zone message formats",
05798    show_voicemail_zones_help, NULL };
05799 
05800 static int load_config(void)
05801 {
05802    struct ast_vm_user *cur, *l;
05803    struct vm_zone *zcur, *zl;
05804    struct ast_config *cfg;
05805    char *cat;
05806    struct ast_variable *var;
05807    char *notifystr = NULL;
05808    char *astattach;
05809    char *astsearch;
05810    char *astsaycid;
05811    char *send_voicemail;
05812    char *astcallop;
05813    char *astreview;
05814    char *astskipcmd;
05815    char *asthearenv;
05816    char *astsaydurationinfo;
05817    char *astsaydurationminfo;
05818    char *silencestr;
05819    char *maxmsgstr;
05820    char *astdirfwd;
05821    char *thresholdstr;
05822    char *fmt;
05823    char *astemail;
05824    char *astmailcmd = SENDMAIL;
05825    char *s,*q,*stringp;
05826    char *dialoutcxt = NULL;
05827    char *callbackcxt = NULL;  
05828    char *exitcxt = NULL;   
05829    char *extpc;
05830    char *emaildateformatstr;
05831    int x;
05832    int tmpadsi[4];
05833 
05834    cfg = ast_config_load(VOICEMAIL_CONFIG);
05835    ast_mutex_lock(&vmlock);
05836    cur = users;
05837    while (cur) {
05838       l = cur;
05839       cur = cur->next;
05840       ast_set_flag(l, VM_ALLOCED);  
05841       free_user(l);
05842    }
05843    zcur = zones;
05844    while (zcur) {
05845       zl = zcur;
05846       zcur = zcur->next;
05847       free_zone(zl);
05848    }
05849    zones = NULL;
05850    zonesl = NULL;
05851    users = NULL;
05852    usersl = NULL;
05853    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
05854 
05855    if (cfg) {
05856       /* General settings */
05857 
05858       /* Attach voice message to mail message ? */
05859       if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
05860          astattach = "yes";
05861       ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH); 
05862 
05863       if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
05864          astsearch = "no";
05865       ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
05866 
05867 #ifdef USE_ODBC_STORAGE
05868       strcpy(odbc_database, "asterisk");
05869       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
05870          ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
05871       }
05872       strcpy(odbc_table, "voicemessages");
05873                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
05874                         ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
05875                 }
05876 #endif      
05877       /* Mail command */
05878       strcpy(mailcmd, SENDMAIL);
05879       if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
05880          ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
05881 
05882       maxsilence = 0;
05883       if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
05884          maxsilence = atoi(silencestr);
05885          if (maxsilence > 0)
05886             maxsilence *= 1000;
05887       }
05888       
05889       if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
05890          maxmsg = MAXMSG;
05891       } else {
05892          maxmsg = atoi(maxmsgstr);
05893          if (maxmsg <= 0) {
05894             ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
05895             maxmsg = MAXMSG;
05896          } else if (maxmsg > MAXMSGLIMIT) {
05897             ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
05898             maxmsg = MAXMSGLIMIT;
05899          }
05900       }
05901 
05902       /* Load date format config for voicemail mail */
05903       if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
05904          ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
05905       }
05906 
05907       /* External password changing command */
05908       if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
05909          ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
05910       }
05911 
05912       /* External voicemail notify application */
05913       
05914       if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
05915          ast_copy_string(externnotify, notifystr, sizeof(externnotify));
05916          ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
05917       } else {
05918          externnotify[0] = '\0';
05919       }
05920 
05921       /* Silence treshold */
05922       silencethreshold = 256;
05923       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
05924          silencethreshold = atoi(thresholdstr);
05925       
05926       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
05927          astemail = ASTERISK_USERNAME;
05928       ast_copy_string(serveremail, astemail, sizeof(serveremail));
05929       
05930       vmmaxmessage = 0;
05931       if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
05932          if (sscanf(s, "%d", &x) == 1) {
05933             vmmaxmessage = x;
05934          } else {
05935             ast_log(LOG_WARNING, "Invalid max message time length\n");
05936          }
05937       }
05938 
05939       vmminmessage = 0;
05940       if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
05941          if (sscanf(s, "%d", &x) == 1) {
05942             vmminmessage = x;
05943             if (maxsilence <= vmminmessage)
05944                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
05945          } else {
05946             ast_log(LOG_WARNING, "Invalid min message time length\n");
05947          }
05948       }
05949       fmt = ast_variable_retrieve(cfg, "general", "format");
05950       if (!fmt)
05951          fmt = "wav";   
05952       ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
05953 
05954       skipms = 3000;
05955       if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
05956          if (sscanf(s, "%d", &x) == 1) {
05957             maxgreet = x;
05958          } else {
05959             ast_log(LOG_WARNING, "Invalid max message greeting length\n");
05960          }
05961       }
05962 
05963       if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
05964          if (sscanf(s, "%d", &x) == 1) {
05965             skipms = x;
05966          } else {
05967             ast_log(LOG_WARNING, "Invalid skipms value\n");
05968          }
05969       }
05970 
05971       maxlogins = 3;
05972       if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
05973          if (sscanf(s, "%d", &x) == 1) {
05974             maxlogins = x;
05975          } else {
05976             ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
05977          }
05978       }
05979 
05980       /* Force new user to record name ? */
05981       if (!(astattach = ast_variable_retrieve(cfg, "general", "forcename"))) 
05982          astattach = "no";
05983       ast_set2_flag((&globalflags), ast_true(astattach), VM_FORCENAME);
05984 
05985       /* Force new user to record greetings ? */
05986       if (!(astattach = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
05987          astattach = "no";
05988       ast_set2_flag((&globalflags), ast_true(astattach), VM_FORCEGREET);
05989 
05990       if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
05991          ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
05992          stringp = ast_strdupa(s);
05993          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
05994             if (!ast_strlen_zero(stringp)) {
05995                q = strsep(&stringp,",");
05996                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
05997                   q++;
05998                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
05999                ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
06000             } else {
06001                cidinternalcontexts[x][0] = '\0';
06002             }
06003          }
06004       }
06005       if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
06006          ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
06007          astreview = "no";
06008       }
06009       ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW); 
06010 
06011       if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
06012          ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
06013          astcallop = "no";
06014       }
06015       ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);  
06016 
06017       if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
06018          ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
06019          astsaycid = "no";
06020       } 
06021       ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID); 
06022 
06023       if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
06024          ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
06025          send_voicemail = "no";
06026       }
06027       ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
06028    
06029       if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
06030          ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
06031          asthearenv = "yes";
06032       }
06033       ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE); 
06034 
06035       if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
06036          ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
06037          astsaydurationinfo = "yes";
06038       }
06039       ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);  
06040 
06041       saydurationminfo = 2;
06042       if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
06043          if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
06044             saydurationminfo = x;
06045          } else {
06046             ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
06047          }
06048       }
06049 
06050       if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
06051          ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
06052          astskipcmd = "no";
06053       }
06054       ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
06055 
06056       if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
06057          ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
06058          ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
06059       } else {
06060          dialcontext[0] = '\0';  
06061       }
06062       
06063       if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
06064          ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
06065          ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
06066       } else {
06067          callcontext[0] = '\0';
06068       }
06069 
06070       if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
06071          ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
06072          ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
06073       } else {
06074          exitcontext[0] = '\0';
06075       }
06076 
06077       if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
06078          astdirfwd = "no";
06079       ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD); 
06080       cat = ast_category_browse(cfg, NULL);
06081       while (cat) {
06082          if (strcasecmp(cat, "general")) {
06083             var = ast_variable_browse(cfg, cat);
06084             if (strcasecmp(cat, "zonemessages")) {
06085                /* Process mailboxes in this context */
06086                while (var) {
06087                   append_mailbox(cat, var->name, var->value);
06088                   var = var->next;
06089                }
06090             } else {
06091                /* Timezones in this context */
06092                while (var) {
06093                   struct vm_zone *z;
06094                   z = malloc(sizeof(struct vm_zone));
06095                   if (z != NULL) {
06096                      char *msg_format, *timezone;
06097                      msg_format = ast_strdupa(var->value);
06098                      if (msg_format != NULL) {
06099                         timezone = strsep(&msg_format, "|");
06100                         if (msg_format) {
06101                            ast_copy_string(z->name, var->name, sizeof(z->name));
06102                            ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
06103                            ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
06104                            z->next = NULL;
06105                            if (zones) {
06106                               zonesl->next = z;
06107                               zonesl = z;
06108                            } else {
06109                               zones = z;
06110                               zonesl = z;
06111                            }
06112                         } else {
06113                            ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
06114                            free(z);
06115                         }
06116                      } else {
06117                         ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
06118                         free(z);
06119                         ast_mutex_unlock(&vmlock);
06120                         ast_config_destroy(cfg);
06121                         return -1;
06122                      }
06123                   } else {
06124                      ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
06125                      ast_mutex_unlock(&vmlock);
06126                      ast_config_destroy(cfg);
06127                      return -1;
06128                   }
06129                   var = var->next;
06130                }
06131             }
06132          }
06133          cat = ast_category_browse(cfg, cat);
06134       }
06135       memset(fromstring,0,sizeof(fromstring));
06136       memset(pagerfromstring,0,sizeof(pagerfromstring));
06137       memset(emailtitle,0,sizeof(emailtitle));
06138       strcpy(charset, "ISO-8859-1");
06139       if (emailbody) {
06140          free(emailbody);
06141          emailbody = NULL;
06142       }
06143       if (emailsubject) {
06144          free(emailsubject);
06145          emailsubject = NULL;
06146       }
06147                if (pagerbody) {
06148                        free(pagerbody);
06149                        pagerbody = NULL;
06150                }
06151                if (pagersubject) {
06152                        free(pagersubject);
06153                        pagersubject = NULL;
06154                }
06155       if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
06156          ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
06157       if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
06158          ast_copy_string(fromstring,s,sizeof(fromstring));
06159       if ((s=ast_variable_retrieve(cfg, "general", "pagerfromstring")))
06160          ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
06161       if ((s=ast_variable_retrieve(cfg, "general", "charset")))
06162          ast_copy_string(charset,s,sizeof(charset));
06163       if ((s=ast_variable_retrieve(cfg, "general", "adsifdn"))) {
06164          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
06165          for (x=0; x<4; x++) {
06166             memcpy(&adsifdn[x], &tmpadsi[x], 1);
06167          }
06168       }
06169       if ((s=ast_variable_retrieve(cfg, "general", "adsisec"))) {
06170          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
06171          for (x=0; x<4; x++) {
06172             memcpy(&adsisec[x], &tmpadsi[x], 1);
06173          }
06174       }
06175       if ((s=ast_variable_retrieve(cfg, "general", "adsiver")))
06176          if (atoi(s)) {
06177             adsiver = atoi(s);
06178          }
06179       if ((s=ast_variable_retrieve(cfg, "general", "emailtitle"))) {
06180          ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
06181          ast_copy_string(emailtitle,s,sizeof(emailtitle));
06182       }
06183       if ((s=ast_variable_retrieve(cfg, "general", "emailsubject")))
06184          emailsubject = strdup(s);
06185       if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
06186          char *tmpread, *tmpwrite;
06187          emailbody = strdup(s);
06188 
06189          /* substitute strings \t and \n into the apropriate characters */
06190          tmpread = tmpwrite = emailbody;
06191                        while ((tmpwrite = strchr(tmpread,'\\'))) {
06192                                int len = strlen("\n");
06193                                switch (tmpwrite[1]) {
06194                                        case 'n':
06195                                                strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06196                                                strncpy(tmpwrite,"\n",len);
06197                                                break;
06198                                        case 't':
06199                                                strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06200                                                strncpy(tmpwrite,"\t",len);
06201                                                break;
06202                                        default:
06203                                                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
06204                                }
06205                                tmpread = tmpwrite+len;
06206                        }
06207                }
06208                if ((s=ast_variable_retrieve(cfg, "general", "pagersubject")))
06209                        pagersubject = strdup(s);
06210                if ((s=ast_variable_retrieve(cfg, "general", "pagerbody"))) {
06211                        char *tmpread, *tmpwrite;
06212                        pagerbody = strdup(s);
06213 
06214                        /* substitute strings \t and \n into the apropriate characters */
06215                        tmpread = tmpwrite = pagerbody;
06216          while ((tmpwrite = strchr(tmpread,'\\'))) {
06217             int len = strlen("\n");
06218             switch (tmpwrite[1]) {
06219                case 'n':
06220                   strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06221                   strncpy(tmpwrite,"\n",len);
06222                   break;
06223                case 't':
06224                   strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
06225                   strncpy(tmpwrite,"\t",len);
06226                   break;
06227                default:
06228                   ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
06229             }
06230             tmpread = tmpwrite+len;
06231          }
06232       }
06233       ast_mutex_unlock(&vmlock);
06234       ast_config_destroy(cfg);
06235       return 0;
06236    } else {
06237       ast_mutex_unlock(&vmlock);
06238       ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
06239       return 0;
06240    }
06241 }
06242 
06243 int reload(void)
06244 {
06245    return(load_config());
06246 }
06247 
06248 int unload_module(void)
06249 {
06250    int res;
06251    
06252    res = ast_unregister_application(app);
06253    res |= ast_unregister_application(app2);
06254    res |= ast_unregister_application(app3);
06255    res |= ast_unregister_application(app4);
06256    res |= ast_cli_unregister(&show_voicemail_users_cli);
06257    res |= ast_cli_unregister(&show_voicemail_zones_cli);
06258    ast_uninstall_vm_functions();
06259    
06260    STANDARD_HANGUP_LOCALUSERS;
06261 
06262    return res;
06263 }
06264 
06265 int load_module(void)
06266 {
06267    int res;
06268    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
06269    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
06270    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
06271    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
06272    if (res)
06273       return(res);
06274 
06275    if ((res=load_config())) {
06276       return(res);
06277    }
06278 
06279    ast_cli_register(&show_voicemail_users_cli);
06280    ast_cli_register(&show_voicemail_zones_cli);
06281 
06282    /* compute the location of the voicemail spool directory */
06283    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
06284 
06285    ast_install_vm_functions(has_voicemail, messagecount);
06286 
06287 #if defined(USE_ODBC_STORAGE) && !defined(EXTENDED_ODBC_STORAGE)
06288    ast_log(LOG_WARNING, "The current ODBC storage table format will be changed soon."
06289             "Please update your tables as per the README and edit the apps/Makefile "
06290             "and uncomment the line containing EXTENDED_ODBC_STORAGE to enable the "
06291             "new table format.\n");
06292 #endif
06293 
06294    return res;
06295 }
06296 
06297 char *description(void)
06298 {
06299    return tdesc;
06300 }
06301 
06302 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
06303 {
06304    int cmd = 0;
06305    char destination[80] = "";
06306    int retries = 0;
06307 
06308    if (!num) {
06309       if (option_verbose > 2)
06310          ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
06311       while (retries < 3 && cmd != 't') {
06312          destination[1] = '\0';
06313          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
06314          if (!cmd)
06315             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
06316          if (!cmd)
06317             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
06318          if (!cmd) {
06319             cmd = ast_waitfordigit(chan, 6000);
06320             if (cmd)
06321                destination[0] = cmd;
06322          }
06323          if (!cmd) {
06324             retries++;
06325          } else {
06326 
06327             if (cmd < 0)
06328                return 0;
06329             if (cmd == '*') {
06330                if (option_verbose > 2)
06331                   ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
06332                return 0;
06333             }
06334             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
06335                retries++;
06336             else
06337                cmd = 't';
06338          }
06339       }
06340       if (retries >= 3) {
06341          return 0;
06342       }
06343       
06344    } else {
06345       if (option_verbose > 2)
06346          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
06347       ast_copy_string(destination, num, sizeof(destination));
06348    }
06349 
06350    if (!ast_strlen_zero(destination)) {
06351       if (destination[strlen(destination) -1 ] == '*')
06352          return 0; 
06353       if (option_verbose > 2)
06354          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
06355       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
06356       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
06357       chan->priority = 0;
06358       return 9;
06359    }
06360    return 0;
06361 }
06362 
06363 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
06364              int option, signed char record_gain)
06365 {
06366    int res = 0;
06367    char filename[256],*origtime, *cid, *context, *name, *num;
06368    struct ast_config *msg_cfg;
06369    int retries = 0;
06370 
06371    vms->starting = 0; 
06372    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
06373 
06374    /* Retrieve info from VM attribute file */
06375 
06376    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
06377    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
06378    RETRIEVE(vms->curdir, vms->curmsg);
06379    msg_cfg = ast_config_load(filename);
06380    DISPOSE(vms->curdir, vms->curmsg);
06381    if (!msg_cfg) {
06382       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06383       return 0;
06384    }
06385 
06386    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06387       ast_config_destroy(msg_cfg);
06388       return 0;
06389    }
06390 
06391    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06392 
06393    context = ast_variable_retrieve(msg_cfg, "message", "context");
06394    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
06395       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
06396 
06397    if (option == 3) {
06398 
06399       if (!res)
06400          res = play_message_datetime(chan, vmu, origtime, filename);
06401       if (!res)
06402          res = play_message_callerid(chan, vms, cid, context, 0);
06403 
06404       res = 't';
06405 
06406    } else if (option == 2) { /* Call back */
06407 
06408       if (!ast_strlen_zero(cid)) {
06409          ast_callerid_parse(cid, &name, &num);
06410          while ((res > -1) && (res != 't')) {
06411             switch(res) {
06412                case '1':
06413                   if (num) {
06414                      /* Dial the CID number */
06415                      res = dialout(chan, vmu, num, vmu->callback);
06416                      if (res) {
06417                         ast_config_destroy(msg_cfg);
06418                         return 9;
06419                      }
06420                   } else {
06421                      res = '2';
06422                   }
06423                   break;
06424 
06425                case '2':
06426                   /* Want to enter a different number, can only do this if there's a dialout context for this user */
06427                   if (!ast_strlen_zero(vmu->dialout)) {
06428                      res = dialout(chan, vmu, NULL, vmu->dialout);
06429                      if (res) {
06430                         ast_config_destroy(msg_cfg);
06431                         return 9;
06432                      }
06433                   } else {
06434                      if (option_verbose > 2)
06435                         ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
06436                      res = ast_play_and_wait(chan, "vm-sorry");
06437                   }
06438                   ast_config_destroy(msg_cfg);
06439                   return res;
06440                case '*':
06441                   res = 't';
06442                   break;
06443                case '3':
06444                case '4':
06445                case '5':
06446                case '6':
06447                case '7':
06448                case '8':
06449                case '9':
06450                case '0':
06451 
06452                   res = ast_play_and_wait(chan, "vm-sorry");
06453                   retries++;
06454                   break;
06455                default:
06456                   if (num) {
06457                      if (option_verbose > 2)
06458                         ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
06459                      res = ast_play_and_wait(chan, "vm-num-i-have");
06460                      if (!res)
06461                         res = play_message_callerid(chan, vms, num, vmu->context, 1);
06462                      if (!res)
06463                         res = ast_play_and_wait(chan, "vm-tocallnum");
06464                      /* Only prompt for a caller-specified number if there is a dialout context specified */
06465                      if (!ast_strlen_zero(vmu->dialout)) {
06466                         if (!res)
06467                            res = ast_play_and_wait(chan, "vm-calldiffnum");
06468                      }
06469                   } else {
06470                      res = ast_play_and_wait(chan, "vm-nonumber");
06471                      if (!ast_strlen_zero(vmu->dialout)) {
06472                         if (!res)
06473                            res = ast_play_and_wait(chan, "vm-toenternumber");
06474                      }
06475                   }
06476                   if (!res)
06477                      res = ast_play_and_wait(chan, "vm-star-cancel");
06478                   if (!res)
06479                      res = ast_waitfordigit(chan, 6000);
06480                   if (!res) {
06481                      retries++;
06482                      if (retries > 3)
06483                         res = 't';
06484                   }
06485                   break; 
06486                   
06487             }
06488             if (res == 't')
06489                res = 0;
06490             else if (res == '*')
06491                res = -1;
06492          }
06493       }
06494       
06495    }
06496    else if (option == 1) { /* Reply */
06497       /* Send reply directly to sender */
06498       if (!ast_strlen_zero(cid)) {
06499          ast_callerid_parse(cid, &name, &num);
06500          if (!num) {
06501             if (option_verbose > 2)
06502                ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
06503             if (!res)
06504                res = ast_play_and_wait(chan, "vm-nonumber");
06505             ast_config_destroy(msg_cfg);
06506             return res;
06507          } else {
06508             if (find_user(NULL, vmu->context, num)) {
06509                struct leave_vm_options leave_options;
06510                char mailbox[AST_MAX_EXTENSION * 2 + 2];
06511                snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
06512 
06513                if (option_verbose > 2)
06514                   ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
06515                
06516                memset(&leave_options, 0, sizeof(leave_options));
06517                leave_options.record_gain = record_gain;
06518                res = leave_voicemail(chan, mailbox, &leave_options);
06519                ast_config_destroy(msg_cfg);
06520                if (!res)
06521                   res = 't';
06522                return res;
06523             } else {
06524                /* Sender has no mailbox, can't reply */
06525                if (option_verbose > 2)
06526                   ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
06527                ast_play_and_wait(chan, "vm-nobox");
06528                ast_config_destroy(msg_cfg);
06529                res = 't';
06530                return res;
06531             }
06532          } 
06533          res = 0;
06534       }
06535    }
06536 
06537    if (!res) {
06538       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
06539       vms->heard[msg] = 1;
06540       res = wait_file(chan, vms, vms->fn);
06541    }
06542    ast_config_destroy(msg_cfg);
06543    return res;
06544 }
06545  
06546 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
06547                int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
06548                signed char record_gain)
06549 {
06550    /* Record message & let caller review or re-record it, or set options if applicable */
06551    int res = 0;
06552    int cmd = 0;
06553    int max_attempts = 3;
06554    int attempts = 0;
06555    int recorded = 0;
06556    int message_exists = 0;
06557    signed char zero_gain = 0;
06558    char *acceptdtmf = "#";
06559    char *canceldtmf = "";
06560 
06561    /* Note that urgent and private are for flagging messages as such in the future */
06562  
06563    /* barf if no pointer passed to store duration in */
06564    if (duration == NULL) {
06565       ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
06566       return -1;
06567    }
06568 
06569    cmd = '3';   /* Want to start by recording */
06570  
06571    while ((cmd >= 0) && (cmd != 't')) {
06572       switch (cmd) {
06573       case '1':
06574          if (!message_exists) {
06575             /* In this case, 1 is to record a message */
06576             cmd = '3';
06577             break;
06578          } else {
06579             /* Otherwise 1 is to save the existing message */
06580             if (option_verbose > 2)
06581                ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
06582             ast_streamfile(chan, "vm-msgsaved", chan->language);
06583             ast_waitstream(chan, "");
06584             STORE(recordfile, vmu->mailbox, vmu->context, -1);
06585             DISPOSE(recordfile, -1);
06586             cmd = 't';
06587             return res;
06588          }
06589       case '2':
06590          /* Review */
06591          if (option_verbose > 2)
06592             ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
06593          ast_streamfile(chan, recordfile, chan->language);
06594          cmd = ast_waitstream(chan, AST_DIGIT_ANY);
06595          break;
06596       case '3':
06597          message_exists = 0;
06598          /* Record */
06599          if (recorded == 1) {
06600             if (option_verbose > 2)
06601                ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
06602          } else { 
06603             if (option_verbose > 2)
06604                ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
06605          }
06606          if (recorded && outsidecaller) {
06607             cmd = ast_play_and_wait(chan, INTRO);
06608             cmd = ast_play_and_wait(chan, "beep");
06609          }
06610          recorded = 1;
06611          /* 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 */
06612          if (record_gain)
06613             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
06614          if (ast_test_flag(vmu, VM_OPERATOR))
06615             canceldtmf = "0";
06616          cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
06617          if (record_gain)
06618             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
06619          if (cmd == -1) {
06620          /* User has hung up, no options to give */
06621             return cmd;
06622          }
06623          if (cmd == '0') {
06624             break;
06625          } else if (cmd == '*') {
06626             break;
06627          } 
06628 #if 0       
06629          else if (vmu->review && (*duration < 5)) {
06630             /* Message is too short */
06631             if (option_verbose > 2)
06632                ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
06633             cmd = ast_play_and_wait(chan, "vm-tooshort");
06634             cmd = vm_delete(recordfile);
06635             break;
06636          }
06637          else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
06638             /* Message is all silence */
06639             if (option_verbose > 2)
06640                ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
06641             cmd = vm_delete(recordfile);
06642             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
06643             if (!cmd)
06644                cmd = ast_play_and_wait(chan, "vm-speakup");
06645             break;
06646          }
06647 #endif
06648          else {
06649             /* If all is well, a message exists */
06650             message_exists = 1;
06651             cmd = 0;
06652          }
06653          break;
06654       case '4':
06655       case '5':
06656       case '6':
06657       case '7':
06658       case '8':
06659       case '9':
06660       case '*':
06661       case '#':
06662          cmd = ast_play_and_wait(chan, "vm-sorry");
06663          break;
06664 #if 0 
06665 /*  XXX Commented out for the moment because of the dangers of deleting
06666     a message while recording (can put the message numbers out of sync) */
06667       case '*':
06668          /* Cancel recording, delete message, offer to take another message*/
06669          cmd = ast_play_and_wait(chan, "vm-deleted");
06670          cmd = vm_delete(recordfile);
06671          if (outsidecaller) {
06672             res = vm_exec(chan, NULL);
06673             return res;
06674          }
06675          else
06676             return 1;
06677 #endif
06678       case '0':
06679          if(!ast_test_flag(vmu, VM_OPERATOR)) {
06680             cmd = ast_play_and_wait(chan, "vm-sorry");
06681             break;
06682          }
06683          if (message_exists || recorded) {
06684             cmd = ast_play_and_wait(chan, "vm-saveoper");
06685             if (!cmd)
06686                cmd = ast_waitfordigit(chan, 3000);
06687             if (cmd == '1') {
06688                ast_play_and_wait(chan, "vm-msgsaved");
06689                cmd = '0';
06690             } else {
06691                ast_play_and_wait(chan, "vm-deleted");
06692                DELETE(recordfile, -1, recordfile);
06693                cmd = '0';
06694             }
06695          }
06696          return cmd;
06697       default:
06698          /* If the caller is an ouside caller, and the review option is enabled,
06699             allow them to review the message, but let the owner of the box review
06700             their OGM's */
06701          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
06702             return cmd;
06703          if (message_exists) {
06704             cmd = ast_play_and_wait(chan, "vm-review");
06705          }
06706          else {
06707             cmd = ast_play_and_wait(chan, "vm-torerecord");
06708             if (!cmd)
06709                cmd = ast_waitfordigit(chan, 600);
06710          }
06711          
06712          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
06713             cmd = ast_play_and_wait(chan, "vm-reachoper");
06714             if (!cmd)
06715                cmd = ast_waitfordigit(chan, 600);
06716          }
06717 #if 0
06718          if (!cmd)
06719             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
06720 #endif
06721          if (!cmd)
06722             cmd = ast_waitfordigit(chan, 6000);
06723          if (!cmd) {
06724             attempts++;
06725          }
06726          if (attempts > max_attempts) {
06727             cmd = 't';
06728          }
06729       }
06730    }
06731    if (outsidecaller)  
06732       ast_play_and_wait(chan, "vm-goodbye");
06733    if (cmd == 't')
06734       cmd = 0;
06735    return cmd;
06736  }
06737  
06738 
06739 int usecount(void)
06740 {
06741    int res;
06742    STANDARD_USECOUNT(res);
06743    return res;
06744 }
06745 
06746 char *key()
06747 {
06748    return ASTERISK_GPL_KEY;
06749 }
06750 

Generated on Sat Nov 25 00:45:30 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6