Fri Sep 29 11:12:24 2006

Asterisk developer's documentation


app_voicemail.c

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

Generated on Fri Sep 29 11:12:24 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7