Sat Mar 24 23:26:00 2007

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

Generated on Sat Mar 24 23:26:00 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.6