ee51df61d6c70475e3560c8b0e7eb006b23ea63f
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \par See also
26  * \arg \ref Config_vm
27  * \ingroup applications
28  */
29
30 /*
31  * 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
32  *                               George Konstantoulakis <gkon@inaccessnetworks.com>
33  *
34  * 05-10-2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
35  *
36  * 05-11-2005 : An option for maximum number of messsages per mailbox added by GDS Partners (www.gdspartners.com)
37  * 07-11-2005 : An issue with voicemail synchronization has been fixed by GDS Partners (www.gdspartners.com)
38  *                               Stojan Sljivic <stojan.sljivic@gdspartners.com>
39  *
40  */
41
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <sys/time.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <sys/mman.h>
52 #include <time.h>
53 #include <dirent.h>
54
55 #include "asterisk.h"
56
57 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
58
59 #include "asterisk/lock.h"
60 #include "asterisk/file.h"
61 #include "asterisk/logger.h"
62 #include "asterisk/channel.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/options.h"
65 #include "asterisk/config.h"
66 #include "asterisk/say.h"
67 #include "asterisk/module.h"
68 #include "asterisk/adsi.h"
69 #include "asterisk/app.h"
70 #include "asterisk/manager.h"
71 #include "asterisk/dsp.h"
72 #include "asterisk/localtime.h"
73 #include "asterisk/cli.h"
74 #include "asterisk/utils.h"
75 #include "asterisk/stringfields.h"
76 #ifdef WITH_SMDI
77 #include "asterisk/smdi.h"
78 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
79 #endif
80 #ifdef USE_ODBC_STORAGE
81 #include "asterisk/res_odbc.h"
82 #endif
83
84 #define COMMAND_TIMEOUT 5000
85 #define VOICEMAIL_DIR_MODE      0770
86 #define VOICEMAIL_FILE_MODE     0660
87
88 #define VOICEMAIL_CONFIG "voicemail.conf"
89 #define ASTERISK_USERNAME "asterisk"
90
91 /* Default mail command to mail voicemail. Change it with the
92     mailcmd= command in voicemail.conf */
93 #define SENDMAIL "/usr/sbin/sendmail -t"
94
95 #define INTRO "vm-intro"
96
97 #define MAXMSG 100
98 #define MAXMSGLIMIT 9999
99
100 #define BASEMAXINLINE 256
101 #define BASELINELEN 72
102 #define BASEMAXINLINE 256
103 #define eol "\r\n"
104
105 #define MAX_DATETIME_FORMAT     512
106 #define MAX_NUM_CID_CONTEXTS    10
107 #define MSGNUM_LENGTH           80
108
109 #define VM_REVIEW               (1 << 0)
110 #define VM_OPERATOR             (1 << 1)
111 #define VM_SAYCID               (1 << 2)
112 #define VM_SVMAIL               (1 << 3)
113 #define VM_ENVELOPE             (1 << 4)
114 #define VM_SAYDURATION          (1 << 5)
115 #define VM_SKIPAFTERCMD         (1 << 6)
116 #define VM_FORCENAME            (1 << 7)        /*!< Have new users record their name */
117 #define VM_FORCEGREET           (1 << 8)        /*!< Have new users record their greetings */
118 #define VM_PBXSKIP              (1 << 9)
119 #define VM_DIRECFORWARD         (1 << 10)       /*!< directory_forward */
120 #define VM_ATTACH               (1 << 11)
121 #define VM_DELETE               (1 << 12)
122 #define VM_ALLOCED              (1 << 13)
123 #define VM_SEARCH               (1 << 14)
124
125 #define ERROR_LOCK_PATH         -100
126
127 enum {
128         OPT_SILENT =           (1 << 0),
129         OPT_BUSY_GREETING =    (1 << 1),
130         OPT_UNAVAIL_GREETING = (1 << 2),
131         OPT_RECORDGAIN =       (1 << 3),
132         OPT_PREPEND_MAILBOX =  (1 << 4),
133         OPT_PRIORITY_JUMP =    (1 << 5),
134         OPT_AUTOPLAY =         (1 << 6),
135 } vm_option_flags;
136
137 enum {
138         OPT_ARG_RECORDGAIN = 0,
139         OPT_ARG_PLAYFOLDER = 1,
140         /* This *must* be the last value in this enum! */
141         OPT_ARG_ARRAY_SIZE = 2,
142 } vm_option_args;
143
144 AST_APP_OPTIONS(vm_app_options, {
145         AST_APP_OPTION('s', OPT_SILENT),
146         AST_APP_OPTION('b', OPT_BUSY_GREETING),
147         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
148         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
149         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
150         AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
151         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
152 });
153
154 static int load_config(void);
155
156 /*! \page vmlang Voicemail Language Syntaxes Supported
157
158         \par Syntaxes supported, not really language codes.
159         \arg \b en - English
160         \arg \b de - German
161         \arg \b es - Spanish
162         \arg \b fr - French
163         \arg \b it = Italian
164         \arg \b nl - Dutch
165         \arg \b pt - Portuguese
166         \arg \b gr - Greek
167         \arg \b no - Norwegian
168         \arg \b se - Swedish
169
170 German requires the following additional soundfile:
171 \arg \b 1F      einE (feminine)
172
173 Spanish requires the following additional soundfile:
174 \arg \b 1M      un (masculine)
175
176 Dutch, Portuguese & Spanish require the following additional soundfiles:
177 \arg \b vm-INBOXs       singular of 'new'
178 \arg \b vm-Olds         singular of 'old/heard/read'
179
180 NB these are plural:
181 \arg \b vm-INBOX        nieuwe (nl)
182 \arg \b vm-Old          oude (nl)
183
184 Swedish uses:
185 \arg \b vm-nytt         singular of 'new'
186 \arg \b vm-nya          plural of 'new'
187 \arg \b vm-gammalt      singular of 'old'
188 \arg \b vm-gamla        plural of 'old'
189 \arg \b digits/ett      'one', not always same as 'digits/1'
190
191 Norwegian uses:
192 \arg \b vm-ny           singular of 'new'
193 \arg \b vm-nye          plural of 'new'
194 \arg \b vm-gammel       singular of 'old'
195 \arg \b vm-gamle        plural of 'old'
196
197 Dutch also uses:
198 \arg \b nl-om           'at'?
199
200 Spanish also uses:
201 \arg \b vm-youhaveno
202
203 Italian requires the following additional soundfile:
204
205 For vm_intro_it:
206 \arg \b vm-nuovo        new
207 \arg \b vm-nuovi        new plural
208 \arg \b vm-vecchio      old
209 \arg \b vm-vecchi       old plural
210
211 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
212 spelled among others when you have to change folder. For the above reasons, vm-INBOX
213 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
214
215 */
216
217 struct baseio {
218         int iocp;
219         int iolen;
220         int linelength;
221         int ateof;
222         unsigned char iobuf[BASEMAXINLINE];
223 };
224
225 /*! Structure for linked list of users */
226 struct ast_vm_user {
227         char context[AST_MAX_CONTEXT];  /*!< Voicemail context */
228         char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */
229         char password[80];              /*!< Secret pin code, numbers only */
230         char fullname[80];              /*!< Full name, for directory app */
231         char email[80];                 /*!< E-mail address */
232         char pager[80];                 /*!< E-mail address to pager (no attachment) */
233         char serveremail[80];           /*!< From: Mail address */
234         char mailcmd[160];              /*!< Configurable mail command */
235         char language[MAX_LANGUAGE];    /*!< Config: Language setting */
236         char zonetag[80];               /*!< Time zone */
237         char callback[80];
238         char dialout[80];
239         char uniqueid[20];              /*!< Unique integer identifier */
240         char exit[80];
241         unsigned int flags;             /*!< VM_ flags */       
242         int saydurationm;
243         int maxmsg;                     /*!< Maximum number of msgs per folder for this mailbox */
244         AST_LIST_ENTRY(ast_vm_user) list;
245 };
246
247 struct vm_zone {
248         char name[80];
249         char timezone[80];
250         char msg_format[512];
251         struct vm_zone *next;
252 };
253
254 struct vm_state {
255         char curbox[80];                /*!< Current mailbox */
256         char username[80];              /*!< User name */
257         char curdir[AST_MAX_FILENAME_LEN];              /*!< Current directory */
258         char vmbox[256];                /*!< Voicemail box */
259         char fn[AST_MAX_FILENAME_LEN];
260         char fn2[AST_MAX_FILENAME_LEN];
261         int *deleted;
262         int *heard;
263         int curmsg;
264         int lastmsg;
265         int newmessages;
266         int oldmessages;
267         int starting;
268         int repeats;
269 };
270 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg,
271                             int option, signed char record_gain);
272 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
273 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
274                               char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
275                               signed char record_gain);
276 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
277 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
278
279 static void apply_options(struct ast_vm_user *vmu, const char *options);
280
281 #ifdef USE_ODBC_STORAGE
282 static char odbc_database[80];
283 static char odbc_table[80];
284 #define RETRIEVE(a,b) retrieve_file(a,b)
285 #define DISPOSE(a,b) remove_file(a,b)
286 #define STORE(a,b,c,d) store_file(a,b,c,d)
287 #define EXISTS(a,b,c,d) (message_exists(a,b))
288 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
289 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
290 #define DELETE(a,b,c) (delete_file(a,b))
291 #else
292 #define RETRIEVE(a,b)
293 #define DISPOSE(a,b)
294 #define STORE(a,b,c,d)
295 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
296 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
297 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
298 #define DELETE(a,b,c) (vm_delete(c))
299 #endif
300
301 static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
302
303 static char ext_pass_cmd[AST_MAX_SHELL_COMMAND];
304
305 static char *tdesc = "Comedian Mail (Voicemail System)";
306
307 static char *addesc = "Comedian Mail";
308
309 static char *synopsis_vm =
310 "Leave a Voicemail message";
311
312 static char *descrip_vm =
313 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
314 "application allows the calling party to leave a message for the specified\n"
315 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
316 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
317 "specified mailbox does not exist.\n"
318 "  The Voicemail application will exit if any of the following DTMF digits are\n"
319 "received:\n"
320 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
321 "    * - Jump to the 'a' extension in the current dialplan context.\n"
322 "  This application will set the following channel variable upon completion:\n"
323 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
324 "               application. The possible values are:\n"
325 "               SUCCESS | USEREXIT | FAILED\n\n"
326 "  Options:\n"
327 "    b    - Play the 'busy' greeting to the calling party.\n"
328 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
329 "           message. The units are whole-number decibels (dB).\n"
330 "    s    - Skip the playback of instructions for leaving a message to the\n"
331 "           calling party.\n"
332 "    u    - Play the 'unavailble greeting.\n"
333 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
334 "           error occurs.\n";
335
336 static char *synopsis_vmain =
337 "Check Voicemail messages";
338
339 static char *descrip_vmain =
340 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
341 "calling party to check voicemail messages. A specific mailbox, and optional\n"
342 "corresponding context, may be specified. If a mailbox is not provided, the\n"
343 "calling party will be prompted to enter one. If a context is not specified,\n"
344 "the 'default' context will be used.\n\n"
345 "  Options:\n"
346 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
347 "           is entered by the caller.\n"
348 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
349 "           message. The units are whole-number decibels (dB).\n"
350 "    s    - Skip checking the passcode for the mailbox.\n"
351 "    a(#) - Skip folder prompt and go directly to folder specified.\n"
352 "           Defaults to INBOX\n";
353
354 static char *synopsis_vm_box_exists =
355 "Check to see if Voicemail mailbox exists";
356
357 static char *descrip_vm_box_exists =
358 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
359 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
360 "will be used.\n"
361 "  This application will set the following channel variable upon completion:\n"
362 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
363 "                        MailboxExists application. Possible values include:\n"
364 "                        SUCCESS | FAILED\n\n"
365 "  Options:\n"
366 "    j - Jump to priority n+101 if the mailbox is found.\n";
367
368 static char *synopsis_vmauthenticate =
369 "Authenticate with Voicemail passwords";
370
371 static char *descrip_vmauthenticate =
372 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
373 "same way as the Authenticate application, but the passwords are taken from\n"
374 "voicemail.conf.\n"
375 "  If the mailbox is specified, only that mailbox's password will be considered\n"
376 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
377 "be set with the authenticated mailbox.\n\n"
378 "  Options:\n"
379 "    s - Skip playing the initial prompts.\n";
380
381 /* Leave a message */
382 static char *app = "VoiceMail";
383
384 /* Check mail, control, etc */
385 static char *app2 = "VoiceMailMain";
386
387 static char *app3 = "MailboxExists";
388 static char *app4 = "VMAuthenticate";
389
390 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
391 struct vm_zone *zones = NULL;
392 struct vm_zone *zonesl = NULL;
393 static int maxsilence;
394 static int maxmsg;
395 static int silencethreshold = 128;
396 static char serveremail[80];
397 static char mailcmd[AST_MAX_SHELL_COMMAND];     /* Configurable mail cmd */
398 static char externnotify[AST_MAX_SHELL_COMMAND]; 
399 #ifdef WITH_SMDI
400 static struct ast_smdi_interface *smdi_iface = NULL;
401 #endif
402 static char vmfmts[80];
403 static int vmminmessage;
404 static int vmmaxmessage;
405 static int maxgreet;
406 static int skipms;
407 static int maxlogins;
408
409 static struct ast_flags globalflags = {0};
410
411 static int saydurationminfo;
412
413 static char dialcontext[AST_MAX_CONTEXT];
414 static char callcontext[AST_MAX_CONTEXT];
415 static char exitcontext[AST_MAX_CONTEXT];
416
417 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
418
419
420 static char *emailbody = NULL;
421 static char *emailsubject = NULL;
422 static char *pagerbody = NULL;
423 static char *pagersubject = NULL;
424 static char fromstring[100];
425 static char pagerfromstring[100];
426 static char emailtitle[100];
427 static char charset[32] = "ISO-8859-1";
428
429 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
430 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
431 static int adsiver = 1;
432 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
433
434 LOCAL_USER_DECL;
435
436 static void populate_defaults(struct ast_vm_user *vmu)
437 {
438         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
439         if (saydurationminfo)
440                 vmu->saydurationm = saydurationminfo;
441         if (callcontext)
442                 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
443         if (dialcontext)
444                 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
445         if (exitcontext)
446                 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
447         if (maxmsg)
448                 vmu->maxmsg = maxmsg;
449 }
450
451 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
452 {
453         int x;
454         if (!strcasecmp(var, "attach")) {
455                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH); 
456         } else if (!strcasecmp(var, "serveremail")) {
457                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
458         } else if (!strcasecmp(var, "language")) {
459                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
460         } else if (!strcasecmp(var, "tz")) {
461                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
462         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
463                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
464         } else if (!strcasecmp(var, "saycid")){
465                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
466         } else if (!strcasecmp(var,"sendvoicemail")){
467                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
468         } else if (!strcasecmp(var, "review")){
469                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW); 
470         } else if (!strcasecmp(var, "operator")){
471                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
472         } else if (!strcasecmp(var, "envelope")){
473                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
474         } else if (!strcasecmp(var, "sayduration")){
475                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
476         } else if (!strcasecmp(var, "saydurationm")){
477                 if (sscanf(value, "%d", &x) == 1) {
478                         vmu->saydurationm = x;
479                 } else {
480                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
481                 }
482         } else if (!strcasecmp(var, "forcename")){
483                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
484         } else if (!strcasecmp(var, "forcegreetings")){
485                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
486         } else if (!strcasecmp(var, "callback")) {
487                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
488         } else if (!strcasecmp(var, "dialout")) {
489                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
490         } else if (!strcasecmp(var, "exitcontext")) {
491                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
492         } else if (!strcasecmp(var, "maxmsg")) {
493                 vmu->maxmsg = atoi(value);
494                 if (vmu->maxmsg <= 0) {
495                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
496                         vmu->maxmsg = MAXMSG;
497                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
498                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
499                         vmu->maxmsg = MAXMSGLIMIT;
500                 }
501         } else if (!strcasecmp(var, "options")) {
502                 apply_options(vmu, value);
503         }
504 }
505
506 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
507 {
508         int res;
509         if (!ast_strlen_zero(vmu->uniqueid)) {
510                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
511                 if (res > 0) {
512                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
513                         res = 0;
514                 } else if (!res) {
515                         res = -1;
516                 }
517                 return res;
518         }
519         return -1;
520 }
521
522 static void apply_options(struct ast_vm_user *vmu, const char *options)
523 {       /* Destructively Parse options and apply */
524         char *stringp;
525         char *s;
526         char *var, *value;
527         stringp = ast_strdupa(options);
528         while ((s = strsep(&stringp, "|"))) {
529                 value = s;
530                 if ((var = strsep(&value, "=")) && value) {
531                         apply_option(vmu, var, value);
532                 }
533         }       
534 }
535
536 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
537 {
538         struct ast_variable *var, *tmp;
539         struct ast_vm_user *retval;
540
541         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
542                 if (!ivm)
543                         ast_set_flag(retval, VM_ALLOCED);       
544                 else
545                         memset(retval, 0, sizeof(*retval));
546                 if (mailbox) 
547                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
548                 populate_defaults(retval);
549                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
550                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
551                 else
552                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
553                 if (var) {
554                         tmp = var;
555                         while(tmp) {
556                                 printf("%s => %s\n", tmp->name, tmp->value);
557                                 if (!strcasecmp(tmp->name, "password")) {
558                                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
559                                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
560                                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
561                                 } else if (!strcasecmp(tmp->name, "pager")) {
562                                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
563                                 } else if (!strcasecmp(tmp->name, "email")) {
564                                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
565                                 } else if (!strcasecmp(tmp->name, "fullname")) {
566                                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
567                                 } else if (!strcasecmp(tmp->name, "context")) {
568                                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
569                                 } else
570                                         apply_option(retval, tmp->name, tmp->value);
571                                 tmp = tmp->next;
572                         } 
573                 } else { 
574                         if (!ivm) 
575                                 free(retval);
576                         retval = NULL;
577                 }       
578         } 
579         return retval;
580 }
581
582 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
583 {
584         /* This function could be made to generate one from a database, too */
585         struct ast_vm_user *vmu=NULL, *cur;
586         AST_LIST_LOCK(&users);
587
588         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
589                 context = "default";
590
591         AST_LIST_TRAVERSE(&users, cur, list) {
592                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
593                         break;
594                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
595                         break;
596         }
597         if (cur) {
598                 /* Make a copy, so that on a reload, we have no race */
599                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
600                         memcpy(vmu, cur, sizeof(*vmu));
601                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
602                         AST_LIST_NEXT(vmu, list) = NULL;
603                 }
604         } else
605                 vmu = find_user_realtime(ivm, context, mailbox);
606         AST_LIST_UNLOCK(&users);
607         return vmu;
608 }
609
610 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
611 {
612         /* This function could be made to generate one from a database, too */
613         struct ast_vm_user *cur;
614         int res = -1;
615         AST_LIST_LOCK(&users);
616         AST_LIST_TRAVERSE(&users, cur, list) {
617                 if ((!context || !strcasecmp(context, cur->context)) &&
618                         (!strcasecmp(mailbox, cur->mailbox)))
619                                 break;
620         }
621         if (cur) {
622                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
623                 res = 0;
624         }
625         AST_LIST_UNLOCK(&users);
626         return res;
627 }
628
629 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
630 {
631         /*  There's probably a better way of doing this. */
632         /*  That's why I've put the password change in a separate function. */
633         /*  This could also be done with a database function */
634         
635         FILE *configin;
636         FILE *configout;
637         int linenum=0;
638         char inbuf[256];
639         char orig[256];
640         char currcontext[256] ="";
641         char tmpin[AST_CONFIG_MAX_PATH];
642         char tmpout[AST_CONFIG_MAX_PATH];
643         struct stat statbuf;
644
645         if (!change_password_realtime(vmu, newpassword))
646                 return;
647
648         snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
649         snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
650         configin = fopen(tmpin,"r");
651         if (configin)
652                 configout = fopen(tmpout,"w+");
653         else
654                 configout = NULL;
655         if (!configin || !configout) {
656                 if (configin)
657                         fclose(configin);
658                 else
659                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
660                 if (configout)
661                         fclose(configout);
662                 else
663                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
664                         return;
665         }
666
667         while (!feof(configin)) {
668                 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
669
670                 /* Read in the line */
671                 fgets(inbuf, sizeof(inbuf), configin);
672                 linenum++;
673
674                 if (ast_strlen_zero(inbuf)) {
675                         fprintf(configout, "\n");
676                         continue;
677                 }
678
679                 /* Make a backup of it */
680                 ast_copy_string(orig, inbuf, sizeof(orig));
681
682                 /*
683                   Read the file line by line, split each line into a comment and command section
684                   only parse the command portion of the line
685                 */
686                 if (inbuf[strlen(inbuf) - 1] == '\n')
687                         inbuf[strlen(inbuf) - 1] = '\0';
688
689                 if ((comment = strchr(inbuf, ';')))
690                         *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
691
692                 if (ast_strlen_zero(inbuf)) {
693                         fprintf(configout, "%s", orig);
694                         continue;
695                 }
696
697                 /* Check for a context, first '[' to first ']' */
698                 if ((tmpctx = strchr(inbuf, '['))) {
699                         tmpctxend = strchr(tmpctx, ']');
700                         if (tmpctxend) {
701                                 /* Valid context */
702                                 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
703                                 fprintf(configout, "%s", orig);
704                                 continue;
705                         }
706                 }
707
708                 /* This isn't a context line, check for MBX => PSWD... */
709                 user = inbuf;
710                 if ((pass = strchr(user, '='))) {
711                         /* We have a line in the form of aaaaa=aaaaaa */
712                         *pass++ = '\0';
713
714                         user = ast_strip(user);
715
716                         if (*pass == '>')
717                                 *pass++ = '\0';
718
719                         pass = ast_skip_blanks(pass);
720
721                         /* 
722                            Since no whitespace allowed in fields, or more correctly white space
723                            inside the fields is there for a purpose, we can just terminate pass
724                            at the comma or EOL whichever comes first.
725                         */
726                         if ((rest = strchr(pass, ',')))
727                                 *rest++ = '\0';
728                 } else {
729                         user = NULL;
730                 }                       
731
732                 /* Compare user, pass AND context */
733                 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
734                     !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
735                     !strcasecmp(currcontext, vmu->context)) {
736                         /* This is the line */
737                         if (rest) {
738                                 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
739                         } else {
740                                 fprintf(configout, "%s => %s", user, newpassword);
741                         }
742                         /* If there was a comment on the line print it out */
743                         if (comment) {
744                                 fprintf(configout, ";%s\n", comment);
745                         } else {
746                                 fprintf(configout, "\n");
747                         }
748                 } else {
749                         /* Put it back like it was */
750                         fprintf(configout, "%s", orig);
751                 }
752         }
753         fclose(configin);
754         fclose(configout);
755
756         stat(tmpin, &statbuf);
757         chmod(tmpout, statbuf.st_mode);
758         chown(tmpout, statbuf.st_uid, statbuf.st_gid);
759         unlink(tmpin);
760         rename(tmpout, tmpin);
761         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
762         ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
763 }
764
765 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
766 {
767         char buf[AST_MAX_SHELL_COMMAND];
768         snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
769         if (!ast_safe_system(buf))
770                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
771 }
772
773 static int make_dir(char *dest, int len, char *context, char *ext, char *folder)
774 {
775         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
776 }
777
778 static int make_file(char *dest, int len, char *dir, int num)
779 {
780         return snprintf(dest, len, "%s/msg%04d", dir, num);
781 }
782
783 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
784  * \param dest    String. base directory.
785  * \param context String. Ignored if is null or empty string.
786  * \param ext     String. Ignored if is null or empty string.
787  * \param folder  String. Ignored if is null or empty string. 
788  * \return 0 on failure, 1 on success.
789  */
790 static int create_dirpath(char *dest, int len, char *context, char *ext, char *folder)
791 {
792         mode_t  mode = VOICEMAIL_DIR_MODE;
793
794         if (!ast_strlen_zero(context)) {
795                 make_dir(dest, len, context, "", "");
796                 if(mkdir(dest, mode) && errno != EEXIST) {
797                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
798                         return 0;
799                 }
800         }
801         if (!ast_strlen_zero(ext)) {
802                 make_dir(dest, len, context, ext, "");
803                 if(mkdir(dest, mode) && errno != EEXIST) {
804                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
805                         return 0;
806                 }
807         }
808         if (!ast_strlen_zero(folder)) {
809                 make_dir(dest, len, context, ext, folder);
810                 if(mkdir(dest, mode) && errno != EEXIST) {
811                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
812                         return 0;
813                 }
814         }
815         return 1;
816 }
817
818 /* only return failure if ast_lock_path returns 'timeout',
819    not if the path does not exist or any other reason
820 */
821 static int vm_lock_path(const char *path)
822 {
823         switch (ast_lock_path(path)) {
824         case AST_LOCK_TIMEOUT:
825                 return -1;
826         default:
827                 return 0;
828         }
829 }
830
831
832 #ifdef USE_ODBC_STORAGE
833 static int retrieve_file(char *dir, int msgnum)
834 {
835         int x = 0;
836         int res;
837         int fd=-1;
838         size_t fdlen = 0;
839         void *fdm=NULL;
840         SQLSMALLINT colcount=0;
841         SQLHSTMT stmt;
842         char sql[256];
843         char fmt[80]="";
844         char *c;
845         char coltitle[256];
846         SQLSMALLINT collen;
847         SQLSMALLINT datatype;
848         SQLSMALLINT decimaldigits;
849         SQLSMALLINT nullable;
850         SQLULEN colsize;
851         FILE *f=NULL;
852         char rowdata[80];
853         char fn[AST_MAX_FILENAME_LEN];
854         char full_fn[AST_MAX_FILENAME_LEN];
855         char msgnums[MSGNUM_LENGTH];
856         
857         odbc_obj *obj;
858         obj = fetch_odbc_obj(odbc_database, 0);
859         if (obj) {
860                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
861                 c = strchr(fmt, '|');
862                 if (c)
863                         *c = '\0';
864                 if (!strcasecmp(fmt, "wav49"))
865                         strcpy(fmt, "WAV");
866                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
867                 if (msgnum > -1)
868                         make_file(fn, sizeof(fn), dir, msgnum);
869                 else
870                         ast_copy_string(fn, dir, sizeof(fn));
871                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
872                 f = fopen(full_fn, "w+");
873                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
874                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
875                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
876                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
877                         goto yuck;
878                 }
879                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
880                 res = SQLPrepare(stmt, sql, SQL_NTS);
881                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
882                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
883                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
884                         goto yuck;
885                 }
886                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
887                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
888                 res = odbc_smart_execute(obj, stmt);
889                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
890                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
891                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
892                         goto yuck;
893                 }
894                 res = SQLFetch(stmt);
895                 if (res == SQL_NO_DATA) {
896                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
897                         goto yuck;
898                 }
899                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
900                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
901                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
902                         goto yuck;
903                 }
904                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
905                 if (fd < 0) {
906                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
907                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
908                         goto yuck;
909                 }
910                 res = SQLNumResultCols(stmt, &colcount);
911                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
912                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
913                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
914                         goto yuck;
915                 }
916                 if (f) 
917                         fprintf(f, "[message]\n");
918                 for (x=0;x<colcount;x++) {
919                         rowdata[0] = '\0';
920                         collen = sizeof(coltitle);
921                         res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
922                                                 &datatype, &colsize, &decimaldigits, &nullable);
923                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
924                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
925                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
926                                 goto yuck;
927                         }
928                         if (!strcasecmp(coltitle, "recording")) {
929                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
930                                 fdlen = colsize;
931                                 fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
932                                 if (fd > -1) {
933                                         char tmp[1]="";
934                                         lseek(fd, fdlen - 1, SEEK_SET);
935                                         if (write(fd, tmp, 1) != 1) {
936                                                 close(fd);
937                                                 fd = -1;
938                                         }
939                                         if (fd > -1)
940                                                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
941                                 }
942                                 if (fdm) {
943                                         memset(fdm, 0, fdlen);
944                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
945                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
946                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
947                                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
948                                                 goto yuck;
949                                         }
950                                 }
951                         } else {
952                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
953                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
954                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
955                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
956                                         goto yuck;
957                                 }
958                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
959                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
960                         }
961                 }
962                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
963         } else
964                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
965 yuck:   
966         if (f)
967                 fclose(f);
968         if (fdm)
969                 munmap(fdm, fdlen);
970         if (fd > -1)
971                 close(fd);
972         return x - 1;
973 }
974
975 static int remove_file(char *dir, int msgnum)
976 {
977         char fn[AST_MAX_FILENAME_LEN];
978         char full_fn[AST_MAX_FILENAME_LEN];
979         char msgnums[MSGNUM_LENGTH];
980         
981         if (msgnum > -1) {
982                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
983                 make_file(fn, sizeof(fn), dir, msgnum);
984         } else
985                 ast_copy_string(fn, dir, sizeof(fn));
986         ast_filedelete(fn, NULL);       
987         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
988         unlink(full_fn);
989         return 0;
990 }
991
992 static int last_message_index(struct ast_vm_user *vmu, char *dir)
993 {
994         int x = 0;
995         int res;
996         SQLHSTMT stmt;
997         char sql[256];
998         char rowdata[20];
999         
1000         odbc_obj *obj;
1001         obj = fetch_odbc_obj(odbc_database, 0);
1002         if (obj) {
1003                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1004                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1005                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1006                         goto yuck;
1007                 }
1008                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1009                 res = SQLPrepare(stmt, sql, SQL_NTS);
1010                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1011                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1012                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1013                         goto yuck;
1014                 }
1015                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1016                 res = odbc_smart_execute(obj, stmt);
1017                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1018                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1019                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1020                         goto yuck;
1021                 }
1022                 res = SQLFetch(stmt);
1023                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1024                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1025                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1026                         goto yuck;
1027                 }
1028                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1029                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1030                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1031                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1032                         goto yuck;
1033                 }
1034                 if (sscanf(rowdata, "%d", &x) != 1)
1035                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1036                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1037         } else
1038                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1039 yuck:   
1040         return x - 1;
1041 }
1042
1043 static int message_exists(char *dir, int msgnum)
1044 {
1045         int x = 0;
1046         int res;
1047         SQLHSTMT stmt;
1048         char sql[256];
1049         char rowdata[20];
1050         char msgnums[MSGNUM_LENGTH];
1051         
1052         odbc_obj *obj;
1053         obj = fetch_odbc_obj(odbc_database, 0);
1054         if (obj) {
1055                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1056                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1057                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1058                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1059                         goto yuck;
1060                 }
1061                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1062                 res = SQLPrepare(stmt, sql, SQL_NTS);
1063                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1064                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1065                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1066                         goto yuck;
1067                 }
1068                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1069                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1070                 res = odbc_smart_execute(obj, stmt);
1071                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1072                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1073                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1074                         goto yuck;
1075                 }
1076                 res = SQLFetch(stmt);
1077                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1078                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1079                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1080                         goto yuck;
1081                 }
1082                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1083                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1084                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1085                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1086                         goto yuck;
1087                 }
1088                 if (sscanf(rowdata, "%d", &x) != 1)
1089                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1090                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1091         } else
1092                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1093 yuck:   
1094         return x;
1095 }
1096
1097 static int count_messages(struct ast_vm_user *vmu, char *dir)
1098 {
1099         return last_message_index(vmu, dir) + 1;
1100 }
1101
1102 static void delete_file(char *sdir, int smsg)
1103 {
1104         int res;
1105         SQLHSTMT stmt;
1106         char sql[256];
1107         char msgnums[MSGNUM_LENGTH];
1108         
1109         odbc_obj *obj;
1110         obj = fetch_odbc_obj(odbc_database, 0);
1111         if (obj) {
1112                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1113                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1114                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1115                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1116                         goto yuck;
1117                 }
1118                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1119                 res = SQLPrepare(stmt, sql, SQL_NTS);
1120                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1121                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1122                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1123                         goto yuck;
1124                 }
1125                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1126                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1127                 res = odbc_smart_execute(obj, stmt);
1128                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1129                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1130                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1131                         goto yuck;
1132                 }
1133                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1134         } else
1135                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1136 yuck:
1137         return; 
1138 }
1139
1140 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1141 {
1142         int res;
1143         SQLHSTMT stmt;
1144         char sql[512];
1145         char msgnums[MSGNUM_LENGTH];
1146         char msgnumd[MSGNUM_LENGTH];
1147         odbc_obj *obj;
1148
1149         delete_file(ddir, dmsg);
1150         obj = fetch_odbc_obj(odbc_database, 0);
1151         if (obj) {
1152                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1153                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1154                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1155                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1156                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1157                         goto yuck;
1158                 }
1159 #ifdef EXTENDED_ODBC_STORAGE
1160                 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); 
1161 #else
1162                 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); 
1163 #endif
1164                 res = SQLPrepare(stmt, sql, SQL_NTS);
1165                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1166                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1167                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1168                         goto yuck;
1169                 }
1170                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1171                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1172 #ifdef EXTENDED_ODBC_STORAGE
1173                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1174                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1175                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1176                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1177 #else
1178                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1179                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1180 #endif           
1181                 res = odbc_smart_execute(obj, stmt);
1182                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1183                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1184                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1185                         goto yuck;
1186                 }
1187                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1188         } else
1189                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1190 yuck:
1191         return; 
1192 }
1193
1194 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1195 {
1196         int x = 0;
1197         int res;
1198         int fd = -1;
1199         void *fdm=NULL;
1200         size_t fdlen = -1;
1201         SQLHSTMT stmt;
1202         SQLINTEGER len;
1203         char sql[256];
1204         char msgnums[MSGNUM_LENGTH];
1205         char fn[AST_MAX_FILENAME_LEN];
1206         char full_fn[AST_MAX_FILENAME_LEN];
1207         char fmt[80]="";
1208         char *c;
1209         char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1210         char *category = "";
1211         struct ast_config *cfg=NULL;
1212         odbc_obj *obj;
1213
1214         delete_file(dir, msgnum);
1215         obj = fetch_odbc_obj(odbc_database, 0);
1216         if (obj) {
1217                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1218                 c = strchr(fmt, '|');
1219                 if (c)
1220                         *c = '\0';
1221                 if (!strcasecmp(fmt, "wav49"))
1222                         strcpy(fmt, "WAV");
1223                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1224                 if (msgnum > -1)
1225                         make_file(fn, sizeof(fn), dir, msgnum);
1226                 else
1227                         ast_copy_string(fn, dir, sizeof(fn));
1228                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1229                 cfg = ast_config_load(full_fn);
1230                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1231                 fd = open(full_fn, O_RDWR);
1232                 if (fd < 0) {
1233                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1234                         goto yuck;
1235                 }
1236                 if (cfg) {
1237                         context = ast_variable_retrieve(cfg, "message", "context");
1238                         if (!context) context = "";
1239                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1240                         if (!macrocontext) macrocontext = "";
1241                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1242                         if (!callerid) callerid = "";
1243                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1244                         if (!origtime) origtime = "";
1245                         duration = ast_variable_retrieve(cfg, "message", "duration");
1246                         if (!duration) duration = "";
1247                         category = ast_variable_retrieve(cfg, "message", "category");
1248                         if (!category) category = "";
1249                 }
1250                 fdlen = lseek(fd, 0, SEEK_END);
1251                 lseek(fd, 0, SEEK_SET);
1252                 printf("Length is %d\n", fdlen);
1253                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1254                 if (!fdm) {
1255                         ast_log(LOG_WARNING, "Memory map failed!\n");
1256                         goto yuck;
1257                 } 
1258                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1259                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1260                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1261                         goto yuck;
1262                 }
1263                 if (!ast_strlen_zero(category)) 
1264 #ifdef EXTENDED_ODBC_STORAGE
1265                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1266 #else
1267                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)",odbc_table);
1268 #endif
1269                 else
1270 #ifdef EXTENDED_ODBC_STORAGE
1271                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1272 #else
1273                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)",odbc_table);
1274 #endif
1275                 res = SQLPrepare(stmt, sql, SQL_NTS);
1276                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1277                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1278                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1279                         goto yuck;
1280                 }
1281                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1282                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1283                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1284                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1285                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1286                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1287                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1288                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1289                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1290 #ifdef EXTENDED_ODBC_STORAGE
1291                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1292                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1293                 if (!ast_strlen_zero(category))
1294                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1295 #else
1296                 if (!ast_strlen_zero(category))
1297                         SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1298 #endif
1299                 res = odbc_smart_execute(obj, stmt);
1300                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1302                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1303                         goto yuck;
1304                 }
1305                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1306         } else
1307                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1308 yuck:   
1309         if (cfg)
1310                 ast_config_destroy(cfg);
1311         if (fdm)
1312                 munmap(fdm, fdlen);
1313         if (fd > -1)
1314                 close(fd);
1315         return x;
1316 }
1317
1318 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1319 {
1320         int res;
1321         SQLHSTMT stmt;
1322         char sql[256];
1323         char msgnums[MSGNUM_LENGTH];
1324         char msgnumd[MSGNUM_LENGTH];
1325         odbc_obj *obj;
1326
1327         delete_file(ddir, dmsg);
1328         obj = fetch_odbc_obj(odbc_database, 0);
1329         if (obj) {
1330                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1331                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1332                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1333                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1334                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1335                         goto yuck;
1336                 }
1337 #ifdef EXTENDED_ODBC_STORAGE
1338                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1339 #else
1340                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=? WHERE dir=? AND msgnum=?",odbc_table);
1341 #endif
1342                 res = SQLPrepare(stmt, sql, SQL_NTS);
1343                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1344                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1345                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1346                         goto yuck;
1347                 }
1348                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1349                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1350 #ifdef EXTENDED_ODBC_STORAGE
1351                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1352                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1353                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1354                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1355 #else
1356                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1357                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1358 #endif           
1359                 res = odbc_smart_execute(obj, stmt);
1360                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1361                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1362                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1363                         goto yuck;
1364                 }
1365                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1366         } else
1367                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1368 yuck:
1369         return; 
1370 }
1371
1372 #else
1373
1374 static int count_messages(struct ast_vm_user *vmu, char *dir)
1375 {
1376         /* Find all .txt files - even if they are not in sequence from 0000 */
1377
1378         int vmcount = 0;
1379         DIR *vmdir = NULL;
1380         struct dirent *vment = NULL;
1381
1382         if (vm_lock_path(dir))
1383                 return ERROR_LOCK_PATH;
1384
1385         if ((vmdir = opendir(dir))) {
1386                 while ((vment = readdir(vmdir))) {
1387                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1388                                 vmcount++;
1389                 }
1390                 closedir(vmdir);
1391         }
1392         ast_unlock_path(dir);
1393         
1394         return vmcount;
1395 }
1396
1397 static void rename_file(char *sfn, char *dfn)
1398 {
1399         char stxt[AST_MAX_FILENAME_LEN];
1400         char dtxt[AST_MAX_FILENAME_LEN];
1401         ast_filerename(sfn, dfn, NULL);
1402         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1403         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1404         rename(stxt, dtxt);
1405 }
1406
1407 static int copy(char *infile, char *outfile)
1408 {
1409         int ifd;
1410         int ofd;
1411         int res;
1412         int len;
1413         char buf[4096];
1414
1415 #ifdef HARDLINK_WHEN_POSSIBLE
1416         /* Hard link if possible; saves disk space & is faster */
1417         if (link(infile, outfile)) {
1418 #endif
1419                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1420                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1421                         return -1;
1422                 }
1423                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1424                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1425                         close(ifd);
1426                         return -1;
1427                 }
1428                 do {
1429                         len = read(ifd, buf, sizeof(buf));
1430                         if (len < 0) {
1431                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1432                                 close(ifd);
1433                                 close(ofd);
1434                                 unlink(outfile);
1435                         }
1436                         if (len) {
1437                                 res = write(ofd, buf, len);
1438                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1439                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1440                                         close(ifd);
1441                                         close(ofd);
1442                                         unlink(outfile);
1443                                 }
1444                         }
1445                 } while (len);
1446                 close(ifd);
1447                 close(ofd);
1448                 return 0;
1449 #ifdef HARDLINK_WHEN_POSSIBLE
1450         } else {
1451                 /* Hard link succeeded */
1452                 return 0;
1453         }
1454 #endif
1455 }
1456
1457 static void copy_file(char *frompath, char *topath)
1458 {
1459         char frompath2[AST_MAX_FILENAME_LEN], topath2[AST_MAX_FILENAME_LEN];
1460
1461         ast_filecopy(frompath, topath, NULL);
1462         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1463         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1464         copy(frompath2, topath2);
1465 }
1466
1467 /*
1468  * A negative return value indicates an error.
1469  */
1470 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1471 {
1472         int x;
1473         char fn[AST_MAX_FILENAME_LEN];
1474
1475         if (vm_lock_path(dir))
1476                 return ERROR_LOCK_PATH;
1477
1478         for (x = 0; x < vmu->maxmsg; x++) {
1479                 make_file(fn, sizeof(fn), dir, x);
1480                 if (ast_fileexists(fn, NULL, NULL) < 1)
1481                         break;
1482         }
1483         ast_unlock_path(dir);
1484
1485         return x - 1;
1486 }
1487
1488 static int vm_delete(char *file)
1489 {
1490         char *txt;
1491         int txtsize = 0;
1492
1493         txtsize = (strlen(file) + 5)*sizeof(char);
1494         txt = alloca(txtsize);
1495         /* Sprintf here would safe because we alloca'd exactly the right length,
1496          * but trying to eliminate all sprintf's anyhow
1497          */
1498         snprintf(txt, txtsize, "%s.txt", file);
1499         unlink(txt);
1500         return ast_filedelete(file, NULL);
1501 }
1502
1503
1504 #endif
1505 static int
1506 inbuf(struct baseio *bio, FILE *fi)
1507 {
1508         int l;
1509
1510         if (bio->ateof)
1511                 return 0;
1512
1513         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1514                 if (ferror(fi))
1515                         return -1;
1516
1517                 bio->ateof = 1;
1518                 return 0;
1519         }
1520
1521         bio->iolen= l;
1522         bio->iocp= 0;
1523
1524         return 1;
1525 }
1526
1527 static int 
1528 inchar(struct baseio *bio, FILE *fi)
1529 {
1530         if (bio->iocp>=bio->iolen) {
1531                 if (!inbuf(bio, fi))
1532                         return EOF;
1533         }
1534
1535         return bio->iobuf[bio->iocp++];
1536 }
1537
1538 static int
1539 ochar(struct baseio *bio, int c, FILE *so)
1540 {
1541         if (bio->linelength>=BASELINELEN) {
1542                 if (fputs(eol,so)==EOF)
1543                         return -1;
1544
1545                 bio->linelength= 0;
1546         }
1547
1548         if (putc(((unsigned char)c),so)==EOF)
1549                 return -1;
1550
1551         bio->linelength++;
1552
1553         return 1;
1554 }
1555
1556 static int base_encode(char *filename, FILE *so)
1557 {
1558         unsigned char dtable[BASEMAXINLINE];
1559         int i,hiteof= 0;
1560         FILE *fi;
1561         struct baseio bio;
1562
1563         memset(&bio, 0, sizeof(bio));
1564         bio.iocp = BASEMAXINLINE;
1565
1566         if (!(fi = fopen(filename, "rb"))) {
1567                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1568                 return -1;
1569         }
1570
1571         for (i= 0;i<9;i++) {
1572                 dtable[i]= 'A'+i;
1573                 dtable[i+9]= 'J'+i;
1574                 dtable[26+i]= 'a'+i;
1575                 dtable[26+i+9]= 'j'+i;
1576         }
1577         for (i= 0;i<8;i++) {
1578                 dtable[i+18]= 'S'+i;
1579                 dtable[26+i+18]= 's'+i;
1580         }
1581         for (i= 0;i<10;i++) {
1582                 dtable[52+i]= '0'+i;
1583         }
1584         dtable[62]= '+';
1585         dtable[63]= '/';
1586
1587         while (!hiteof){
1588                 unsigned char igroup[3],ogroup[4];
1589                 int c,n;
1590
1591                 igroup[0]= igroup[1]= igroup[2]= 0;
1592
1593                 for (n= 0;n<3;n++) {
1594                         if ((c = inchar(&bio, fi)) == EOF) {
1595                                 hiteof= 1;
1596                                 break;
1597                         }
1598
1599                         igroup[n]= (unsigned char)c;
1600                 }
1601
1602                 if (n> 0) {
1603                         ogroup[0]= dtable[igroup[0]>>2];
1604                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1605                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1606                         ogroup[3]= dtable[igroup[2]&0x3F];
1607
1608                         if (n<3) {
1609                                 ogroup[3]= '=';
1610
1611                                 if (n<2)
1612                                         ogroup[2]= '=';
1613                         }
1614
1615                         for (i= 0;i<4;i++)
1616                                 ochar(&bio, ogroup[i], so);
1617                 }
1618         }
1619
1620         if (fputs(eol,so)==EOF)
1621                 return 0;
1622
1623         fclose(fi);
1624
1625         return 1;
1626 }
1627
1628 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, const char *category)
1629 {
1630         char callerid[256];
1631         /* Prepare variables for substition in email body and subject */
1632         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1633         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1634         snprintf(passdata, passdatasize, "%d", msgnum);
1635         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1636         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1637         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1638         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1639         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1640         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1641         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1642         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1643 }
1644
1645 /*
1646  * fill in *tm for current time according to the proper timezone, if any.
1647  * Return tm so it can be used as a function argument.
1648  */
1649 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1650 {
1651         const struct vm_zone *z = NULL;
1652         time_t t = time(NULL);
1653
1654         /* Does this user have a timezone specified? */
1655         if (!ast_strlen_zero(vmu->zonetag)) {
1656                 /* Find the zone in the list */
1657                 for (z = zones; z ; z = z->next)
1658                         if (!strcmp(z->name, vmu->zonetag))
1659                                 break;
1660         }
1661         ast_localtime(&t, tm, z ? z->timezone : NULL);
1662         return tm;
1663 }
1664
1665 /* same as mkstemp, but return a FILE * */
1666 static FILE *vm_mkftemp(char *template)
1667 {
1668         FILE *p = NULL;
1669         int pfd = mkstemp(template);
1670         if (pfd > -1) {
1671                 p = fdopen(pfd, "w");
1672                 if (!p) {
1673                         close(pfd);
1674                         pfd = -1;
1675                 }
1676         }
1677         return p;
1678 }
1679
1680 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, const char *category)
1681 {
1682         FILE *p=NULL;
1683         char date[256];
1684         char host[MAXHOSTNAMELEN] = "";
1685         char who[256];
1686         char bound[256];
1687         char fname[AST_MAX_FILENAME_LEN];
1688         char dur[256];
1689         char tmp[AST_MAX_FILENAME_LEN] = "/tmp/astmail-XXXXXX";
1690         char mailcommand[AST_MAX_SHELL_COMMAND];
1691         struct tm tm;
1692
1693         if (vmu && ast_strlen_zero(vmu->email)) {
1694                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
1695                 return(0);
1696         }
1697         if (!strcmp(format, "wav49"))
1698                 format = "WAV";
1699         if (option_debug)
1700                 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));
1701         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1702            command hangs */
1703         p = vm_mkftemp(tmp);
1704         if (p == NULL) {
1705                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1706                 return -1;
1707         } else {
1708                 gethostname(host, sizeof(host)-1);
1709                 if (strchr(srcemail, '@'))
1710                         ast_copy_string(who, srcemail, sizeof(who));
1711                 else {
1712                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1713                 }
1714                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1715                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1716                 fprintf(p, "Date: %s\n", date);
1717
1718                 /* Set date format for voicemail mail */
1719                 strftime(date, sizeof(date), emaildateformat, &tm);
1720
1721                 if (*fromstring) {
1722                         struct ast_channel *ast;
1723                         if ((ast = ast_channel_alloc(0))) {
1724                                 char *passdata;
1725                                 int vmlen = strlen(fromstring)*3 + 200;
1726                                 if ((passdata = alloca(vmlen))) {
1727                                         memset(passdata, 0, vmlen);
1728                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1729                                         pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1730                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1731                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1732                                 ast_channel_free(ast);
1733                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1734                 } else
1735                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1736                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1737
1738                 if (emailsubject) {
1739                         struct ast_channel *ast;
1740                         if ((ast = ast_channel_alloc(0))) {
1741                                 char *passdata;
1742                                 int vmlen = strlen(emailsubject)*3 + 200;
1743                                 if ((passdata = alloca(vmlen))) {
1744                                         memset(passdata, 0, vmlen);
1745                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1746                                         pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1747                                         fprintf(p, "Subject: %s\n",passdata);
1748                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1749                                 ast_channel_free(ast);
1750                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1751                 } else
1752                 if (*emailtitle) {
1753                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1754                         fprintf(p,"\n") ;
1755                 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1756                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1757                 else
1758                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1759                 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1760                 fprintf(p, "MIME-Version: 1.0\n");
1761                 if (attach_user_voicemail) {
1762                         /* Something unique. */
1763                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1764
1765                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1766
1767                         fprintf(p, "--%s\n", bound);
1768                 }
1769                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1770                 if (emailbody) {
1771                         struct ast_channel *ast;
1772                         if ((ast = ast_channel_alloc(0))) {
1773                                 char *passdata;
1774                                 int vmlen = strlen(emailbody)*3 + 200;
1775                                 if ((passdata = alloca(vmlen))) {
1776                                         memset(passdata, 0, vmlen);
1777                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1778                                         pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1779                                         fprintf(p, "%s\n",passdata);
1780                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1781                                 ast_channel_free(ast);
1782                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1783                 } else {
1784                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1785
1786                         "in mailbox %s from %s, on %s so you might\n"
1787                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
1788                         dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1789                 }
1790                 if (attach_user_voicemail) {
1791                         /* Eww. We want formats to tell us their own MIME type */
1792                         char *ctype = "audio/x-";
1793                         if (!strcasecmp(format, "ogg"))
1794                                 ctype = "application/";
1795                 
1796                         fprintf(p, "--%s\n", bound);
1797                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1798                         fprintf(p, "Content-Transfer-Encoding: base64\n");
1799                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1800                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1801
1802                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1803                         base_encode(fname, p);
1804                         fprintf(p, "\n\n--%s--\n.\n", bound);
1805                 }
1806                 fclose(p);
1807                 snprintf(mailcommand, sizeof(mailcommand), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1808                 ast_safe_system(mailcommand);
1809                 if (option_debug)
1810                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1811         }
1812         return 0;
1813 }
1814
1815 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
1816 {
1817         char date[256];
1818         char host[MAXHOSTNAMELEN]="";
1819         char who[256];
1820         char dur[256];
1821         char tmp[AST_MAX_FILENAME_LEN] = "/tmp/astmail-XXXXXX";
1822         char mailcommand[AST_MAX_SHELL_COMMAND];
1823         struct tm tm;
1824         FILE *p = vm_mkftemp(tmp);
1825
1826         if (p == NULL) {
1827                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1828                 return -1;
1829         } else {
1830                 gethostname(host, sizeof(host)-1);
1831                 if (strchr(srcemail, '@'))
1832                         ast_copy_string(who, srcemail, sizeof(who));
1833                 else {
1834                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1835                 }
1836                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1837                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1838                 fprintf(p, "Date: %s\n", date);
1839
1840                 if (*pagerfromstring) {
1841                         struct ast_channel *ast;
1842                         if ((ast = ast_channel_alloc(0))) {
1843                                 char *passdata;
1844                                 int vmlen = strlen(fromstring)*3 + 200;
1845                                 if ((passdata = alloca(vmlen))) {
1846                                         memset(passdata, 0, vmlen);
1847                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1848                                         pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1849                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1850                                 } else 
1851                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1852                                 ast_channel_free(ast);
1853                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1854                 } else
1855                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1856                 fprintf(p, "To: %s\n", pager);
1857                 if (pagersubject) {
1858                        struct ast_channel *ast;
1859                        if ((ast = ast_channel_alloc(0))) {
1860                                char *passdata;
1861                                int vmlen = strlen(pagersubject)*3 + 200;
1862                                if ((passdata = alloca(vmlen))) {
1863                                        memset(passdata, 0, vmlen);
1864                                        prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1865                                        pbx_substitute_variables_helper(ast,pagersubject,passdata,vmlen);
1866                                        fprintf(p, "Subject: %s\n\n",passdata);
1867                                } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1868                                ast_channel_free(ast);
1869                        } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1870                } else
1871                        fprintf(p, "Subject: New VM\n\n");
1872                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1873                if (pagerbody) {
1874                        struct ast_channel *ast;
1875                        if ((ast = ast_channel_alloc(0))) {
1876                                char *passdata;
1877                                int vmlen = strlen(pagerbody)*3 + 200;
1878                                if ((passdata = alloca(vmlen))) {
1879                                        memset(passdata, 0, vmlen);
1880                                        prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1881                                        pbx_substitute_variables_helper(ast,pagerbody,passdata,vmlen);
1882                                        fprintf(p, "%s\n",passdata);
1883                                } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1884                                ast_channel_free(ast);
1885                        } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1886                } else {
1887                        fprintf(p, "New %s long msg in box %s\n"
1888                                        "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1889                }
1890                 fclose(p);
1891                 snprintf(mailcommand, sizeof(mailcommand), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1892                 ast_safe_system(mailcommand);
1893                 if (option_debug)
1894                         ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1895         }
1896         return 0;
1897 }
1898
1899 static int get_date(char *s, int len)
1900 {
1901         struct tm tm;
1902         time_t t;
1903         t = time(0);
1904         localtime_r(&t,&tm);
1905         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1906 }
1907
1908 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1909 {
1910         int res;
1911         char fn[AST_MAX_FILENAME_LEN];
1912
1913         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1914         RETRIEVE(fn, -1);
1915         if (ast_fileexists(fn, NULL, NULL) > 0) {
1916                 res = ast_streamfile(chan, fn, chan->language);
1917                 if (res) {
1918                         DISPOSE(fn, -1);
1919                         return -1;
1920                 }
1921                 res = ast_waitstream(chan, ecodes);
1922                 if (res) {
1923                         DISPOSE(fn, -1);
1924                         return res;
1925                 }
1926         } else {
1927                 /* Dispose just in case */
1928                 DISPOSE(fn, -1);
1929                 res = ast_streamfile(chan, "vm-theperson", chan->language);
1930                 if (res)
1931                         return -1;
1932                 res = ast_waitstream(chan, ecodes);
1933                 if (res)
1934                         return res;
1935                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1936                 if (res)
1937                         return res;
1938         }
1939         if (busy)
1940                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1941         else
1942                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1943         if (res)
1944                 return -1;
1945         res = ast_waitstream(chan, ecodes);
1946         return res;
1947 }
1948
1949 static void free_user(struct ast_vm_user *vmu)
1950 {
1951         if (ast_test_flag(vmu, VM_ALLOCED))
1952                 free(vmu);
1953 }
1954
1955 static void free_zone(struct vm_zone *z)
1956 {
1957         free(z);
1958 }
1959
1960 static char *mbox(int id)
1961 {
1962         switch(id) {
1963         case 0:
1964                 return "INBOX";
1965         case 1:
1966                 return "Old";
1967         case 2:
1968                 return "Work";
1969         case 3:
1970                 return "Family";
1971         case 4:
1972                 return "Friends";
1973         case 5:
1974                 return "Cust1";
1975         case 6:
1976                 return "Cust2";
1977         case 7:
1978                 return "Cust3";
1979         case 8:
1980                 return "Cust4";
1981         case 9:
1982                 return "Cust5";
1983         default:
1984                 return "Unknown";
1985         }
1986 }
1987
1988 #ifdef USE_ODBC_STORAGE
1989 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1990 {
1991         int x = 0;
1992         int res;
1993         SQLHSTMT stmt;
1994         char sql[256];
1995         char rowdata[20];
1996         char tmp[256]="";
1997         char *context;
1998
1999         if (newmsgs)
2000                 *newmsgs = 0;
2001         if (oldmsgs)
2002                 *oldmsgs = 0;
2003         /* If no mailbox, return immediately */
2004         if (ast_strlen_zero(mailbox))
2005                 return 0;
2006
2007         ast_copy_string(tmp, mailbox, sizeof(tmp));
2008         
2009         context = strchr(tmp, '@');
2010         if (context) {   
2011                 *context = '\0';
2012                 context++;
2013         } else  
2014                 context = "default";
2015         
2016         odbc_obj *obj;
2017         obj = fetch_odbc_obj(odbc_database, 0);
2018         if (obj) {
2019                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2020                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2021                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2022                         goto yuck;
2023                 }
2024                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir LIKE '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2025                 res = SQLPrepare(stmt, sql, SQL_NTS);
2026                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2027                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2028                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2029                         goto yuck;
2030                 }
2031                 res = odbc_smart_execute(obj, stmt);
2032                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2033                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2034                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2035                         goto yuck;
2036                 }
2037                 res = SQLFetch(stmt);
2038                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2039                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2040                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2041                         goto yuck;
2042                 }
2043                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2044                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2045                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2046                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2047                         goto yuck;
2048                 }
2049                 *newmsgs = atoi(rowdata);
2050                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2051
2052                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2053                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2054                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2055                         goto yuck;
2056                 }
2057                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "Old");
2058                 res = SQLPrepare(stmt, sql, SQL_NTS);
2059                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2060                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2061                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2062                         goto yuck;
2063                 }
2064                 res = odbc_smart_execute(obj, stmt);
2065                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2066                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2067                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2068                         goto yuck;
2069                 }
2070                 res = SQLFetch(stmt);
2071                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2072                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2073                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2074                         goto yuck;
2075                 }
2076                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2077                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2078                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2079                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2080                         goto yuck;
2081                 }
2082                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2083                 *oldmsgs = atoi(rowdata);
2084                 x = 1;
2085         } else
2086                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2087                 
2088 yuck:   
2089         return x;
2090 }
2091
2092 static int has_voicemail(const char *mailbox, const char *folder)
2093 {
2094         int nummsgs = 0;
2095         int res;
2096         SQLHSTMT stmt;
2097         char sql[256];
2098         char rowdata[20];
2099         char tmp[256]="";
2100         char *context;
2101         if (!folder)
2102                 folder = "INBOX";
2103         /* If no mailbox, return immediately */
2104         if (ast_strlen_zero(mailbox))
2105                 return 0;
2106
2107         ast_copy_string(tmp, mailbox, sizeof(tmp));
2108                         
2109         context = strchr(tmp, '@');
2110         if (context) {
2111                 *context = '\0';
2112                 context++;
2113         } else
2114                 context = "default";
2115
2116         odbc_obj *obj;
2117         obj = fetch_odbc_obj(odbc_database, 0);
2118         if (obj) {
2119                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2120                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2121                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2122                         goto yuck;
2123                 }
2124                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir like '%%%s/%s/%s'", odbc_table, context, tmp, "INBOX");
2125                 res = SQLPrepare(stmt, sql, SQL_NTS);
2126                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
2127                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2128                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2129                         goto yuck;
2130                 }
2131                 res = odbc_smart_execute(obj, stmt);
2132                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2133                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2134                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2135                         goto yuck;
2136                 }
2137                 res = SQLFetch(stmt);
2138                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2139                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2140                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2141                         goto yuck;
2142                 }
2143                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2144                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2145                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2146                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2147                         goto yuck;
2148                 }
2149                 nummsgs = atoi(rowdata);
2150                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2151        } else
2152                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2153
2154 yuck:
2155         if (nummsgs>=1)
2156                 return 1;
2157         else
2158                 return 0;
2159 }
2160
2161 #else
2162
2163 static int has_voicemail(const char *mailbox, const char *folder)
2164 {
2165         DIR *dir;
2166         struct dirent *de;
2167         char fn[AST_MAX_FILENAME_LEN];
2168         char tmp[256]="";
2169         char *mb, *cur;
2170         char *context;
2171         int ret;
2172
2173         if (!folder)
2174                 folder = "INBOX";
2175         /* If no mailbox, return immediately */
2176         if (ast_strlen_zero(mailbox))
2177                 return 0;
2178         if (strchr(mailbox, ',')) {
2179                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2180                 mb = tmp;
2181                 ret = 0;
2182                 while((cur = strsep(&mb, ","))) {
2183                         if (!ast_strlen_zero(cur)) {
2184                                 if (has_voicemail(cur, folder))
2185                                         return 1; 
2186                         }
2187                 }
2188                 return 0;
2189         }
2190         ast_copy_string(tmp, mailbox, sizeof(tmp));
2191         context = strchr(tmp, '@');
2192         if (context) {
2193                 *context = '\0';
2194                 context++;
2195         } else
2196                 context = "default";
2197         snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
2198         dir = opendir(fn);
2199         if (!dir)
2200                 return 0;
2201         while ((de = readdir(dir))) {
2202                 if (!strncasecmp(de->d_name, "msg", 3))
2203                         break;
2204         }
2205         closedir(dir);
2206         if (de)
2207                 return 1;
2208         return 0;
2209 }
2210
2211
2212 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
2213 {
2214         DIR *dir;
2215         struct dirent *de;
2216         char fn[AST_MAX_FILENAME_LEN];
2217         char tmp[256]="";
2218         char *mb, *cur;
2219         char *context;
2220         int ret;
2221
2222         if (newmsgs)
2223                 *newmsgs = 0;
2224         if (oldmsgs)
2225                 *oldmsgs = 0;
2226         /* If no mailbox, return immediately */
2227         if (ast_strlen_zero(mailbox))
2228                 return 0;
2229         if (strchr(mailbox, ',')) {
2230                 int tmpnew, tmpold;
2231                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2232                 mb = tmp;
2233                 ret = 0;
2234                 while((cur = strsep(&mb, ", "))) {
2235                         if (!ast_strlen_zero(cur)) {
2236                                 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2237                                         return -1;
2238                                 else {
2239                                         if (newmsgs)
2240                                                 *newmsgs += tmpnew; 
2241                                         if (oldmsgs)
2242                                                 *oldmsgs += tmpold;
2243                                 }
2244                         }
2245                 }
2246                 return 0;
2247         }
2248         ast_copy_string(tmp, mailbox, sizeof(tmp));
2249         context = strchr(tmp, '@');
2250         if (context) {
2251                 *context = '\0';
2252                 context++;
2253         } else
2254                 context = "default";
2255         if (newmsgs) {
2256                 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
2257                 dir = opendir(fn);
2258                 if (dir) {
2259                         while ((de = readdir(dir))) {
2260                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2261                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2262                                                 (*newmsgs)++;
2263                                         
2264                         }
2265                         closedir(dir);
2266                 }
2267         }
2268         if (oldmsgs) {
2269                 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
2270                 dir = opendir(fn);
2271                 if (dir) {
2272                         while ((de = readdir(dir))) {
2273                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
2274                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
2275                                                 (*oldmsgs)++;
2276                                         
2277                         }
2278                         closedir(dir);
2279                 }
2280         }
2281         return 0;
2282 }
2283
2284 #endif
2285
2286 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
2287
2288 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)
2289 {
2290         char fromdir[AST_MAX_FILENAME_LEN], todir[AST_MAX_FILENAME_LEN], frompath[AST_MAX_FILENAME_LEN], topath[AST_MAX_FILENAME_LEN];
2291         char *frombox = mbox(imbox);
2292         int recipmsgnum;
2293
2294         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2295
2296         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2297   
2298         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2299         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2300
2301         if (vm_lock_path(todir))
2302                 return ERROR_LOCK_PATH;
2303
2304         recipmsgnum = 0;
2305         do {
2306                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2307                 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2308                         break;
2309                 recipmsgnum++;
2310         } while (recipmsgnum < recip->maxmsg);
2311         if (recipmsgnum < recip->maxmsg) {
2312                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2313         } else {
2314                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2315         }
2316         ast_unlock_path(todir);
2317         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2318         
2319         return 0;
2320 }
2321
2322 static void run_externnotify(char *context, char *extension)
2323 {
2324         char arguments[AST_MAX_SHELL_COMMAND];
2325         char ext_context[AST_MAX_EXTENSION] = "";
2326         int newvoicemails = 0, oldvoicemails = 0;
2327 #ifdef WITH_SMDI
2328         struct ast_smdi_mwi_message *mwi_msg;
2329 #endif
2330
2331         if (!ast_strlen_zero(context))
2332                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2333         else
2334                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2335
2336 #ifdef WITH_SMDI
2337         if (!strcasecmp(externnotify, "smdi")) {
2338                 if (ast_app_has_voicemail(ext_context, NULL)) 
2339                         ast_smdi_mwi_set(smdi_iface, extension);
2340                 else
2341                         ast_smdi_mwi_unset(smdi_iface, extension);
2342
2343                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2344                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2345                         if (!strncmp(mwi_msg->cause, "INV", 3))
2346                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2347                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2348                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2349                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2350                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2351                 } else {
2352                         ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2353                 }
2354         } else if (!ast_strlen_zero(externnotify)) {
2355 #else
2356         if (!ast_strlen_zero(externnotify)) {
2357 #endif
2358                 if (messagecount(ext_context, &newvoicemails, &oldvoicemails)) {
2359                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2360                 } else {
2361                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2362                         if (option_debug)
2363                                 ast_log(LOG_DEBUG, "Executing external notification: %s\n", arguments);
2364                         ast_safe_system(arguments);
2365                 }
2366         }
2367 }
2368
2369 struct leave_vm_options {
2370         unsigned int flags;
2371         signed char record_gain;
2372 };
2373
2374 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2375 {
2376         char txtfile[AST_MAX_FILENAME_LEN];
2377         char callerid[256];
2378         FILE *txt;
2379         int res = 0;
2380         int msgnum;
2381         int duration = 0;
2382         int ausemacro = 0;
2383         int ousemacro = 0;
2384         char date[256];
2385         char dir[256];
2386         char fn[AST_MAX_FILENAME_LEN];
2387         char prefile[AST_MAX_FILENAME_LEN]="";
2388         char tempfile[AST_MAX_FILENAME_LEN]="";
2389         char ext_context[AST_MAX_EXTENSION] = "";
2390         char fmt[80];
2391         char *context;
2392         char ecodes[16] = "#";
2393         char tmp[AST_MAX_EXTENSION] = "", *tmpptr;
2394         struct ast_vm_user *vmu;
2395         struct ast_vm_user svm;
2396         const char *category = NULL;
2397
2398         ast_copy_string(tmp, ext, sizeof(tmp));
2399         ext = tmp;
2400         context = strchr(tmp, '@');
2401         if (context) {
2402                 *context = '\0';
2403                 context++;
2404                 tmpptr = strchr(context, '&');
2405         } else {
2406                 tmpptr = strchr(ext, '&');
2407         }
2408
2409         if (tmpptr) {
2410                 *tmpptr = '\0';
2411                 tmpptr++;
2412         }
2413
2414         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2415
2416         if (!(vmu = find_user(&svm, context, ext))) {
2417                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2418                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2419                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2420                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2421                 return res;
2422         }
2423
2424         /* Setup pre-file if appropriate */
2425         if (strcmp(vmu->context, "default"))
2426                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2427         else
2428                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2429         if (ast_test_flag(options, OPT_BUSY_GREETING))
2430                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2431         else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2432                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2433         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2434         RETRIEVE(tempfile, -1);
2435         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2436                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2437         DISPOSE(tempfile, -1);
2438         /* It's easier just to try to make it than to check for its existence */
2439         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2440
2441         /* Check current or macro-calling context for special extensions */
2442         if (!ast_strlen_zero(vmu->exit)) {
2443                 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2444                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2445         } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2446                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2447         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2448                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2449                 ousemacro = 1;
2450         }
2451
2452         if (!ast_strlen_zero(vmu->exit)) {
2453                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2454                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2455         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2456                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2457         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2458                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2459                 ausemacro = 1;
2460         }
2461
2462         /* Play the beginning intro if desired */
2463         if (!ast_strlen_zero(prefile)) {
2464                 RETRIEVE(prefile, -1);
2465                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2466                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2467                                 res = ast_waitstream(chan, ecodes);
2468                 } else {
2469                         if (option_debug)
2470                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2471                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2472                 }
2473                 DISPOSE(prefile, -1);
2474                 if (res < 0) {
2475                         if (option_debug)
2476                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2477                         free_user(vmu);
2478                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2479                         return -1;
2480                 }
2481         }
2482         if (res == '#') {
2483                 /* On a '#' we skip the instructions */
2484                 ast_set_flag(options, OPT_SILENT);
2485                 res = 0;
2486         }
2487         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2488                 res = ast_streamfile(chan, INTRO, chan->language);
2489                 if (!res)
2490                         res = ast_waitstream(chan, ecodes);
2491                 if (res == '#') {
2492                         ast_set_flag(options, OPT_SILENT);
2493                         res = 0;
2494                 }
2495         }
2496         if (res > 0)
2497                 ast_stopstream(chan);
2498         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2499            other than the operator -- an automated attendant or mailbox login for example */
2500         if (res == '*') {
2501                 chan->exten[0] = 'a';
2502                 chan->exten[1] = '\0';
2503                 if (!ast_strlen_zero(vmu->exit)) {
2504                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2505                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2506                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2507                 }
2508                 chan->priority = 0;
2509                 free_user(vmu);
2510                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2511                 return 0;
2512         }
2513         /* Check for a '0' here */
2514         if (res == '0') {
2515         transfer:
2516                 if (ast_test_flag(vmu, VM_OPERATOR)) {
2517                         chan->exten[0] = 'o';
2518                         chan->exten[1] = '\0';
2519                         if (!ast_strlen_zero(vmu->exit)) {
2520                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2521                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2522                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2523                         }
2524                         ast_play_and_wait(chan, "transfer");
2525                         chan->priority = 0;
2526                         free_user(vmu);
2527                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2528                         return 0;
2529                 } else {
2530                         ast_play_and_wait(chan, "vm-sorry");
2531                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2532                         return 0;
2533                 }
2534         }
2535         if (res < 0) {
2536                 free_user(vmu);
2537                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2538                 return -1;
2539         }
2540         /* The meat of recording the message...  All the announcements and beeps have been played*/
2541         ast_copy_string(fmt, vmfmts, sizeof(fmt));
2542         if (!ast_strlen_zero(fmt)) {
2543                 msgnum = 0;
2544
2545                 if (vm_lock_path(dir)) {
2546                         free_user(vmu);
2547                         return ERROR_LOCK_PATH;
2548                 }
2549
2550                 /* 
2551                  * This operation can be very expensive if done say over NFS or if the mailbox has 100+ messages
2552                  * in the folder.  So we should get this first so we don't cut off the first few seconds of the 
2553                  * message.  
2554                  */
2555                 do {
2556                         make_file(fn, sizeof(fn), dir, msgnum);
2557                         if (!EXISTS(dir,msgnum,fn,chan->language))
2558                                 break;
2559                         msgnum++;
2560                 } while (msgnum < vmu->maxmsg);
2561
2562                 /* Now play the beep once we have the message number for our next message. */
2563                 if (res >= 0) {
2564                         /* Unless we're *really* silent, try to send the beep */
2565                         res = ast_streamfile(chan, "beep", chan->language);
2566                         if (!res)
2567                                 res = ast_waitstream(chan, "");
2568                 }
2569                 if (msgnum < vmu->maxmsg) {
2570                         /* assign a variable with the name of the voicemail file */       
2571                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
2572                                 
2573                         /* Store information */
2574                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2575                         txt = fopen(txtfile, "w+");
2576                         if (txt) {
2577                                 get_date(date, sizeof(date));
2578                                 fprintf(txt, 
2579                                         ";\n"
2580                                         "; Message Information file\n"
2581                                         ";\n"
2582                                         "[message]\n"
2583                                         "origmailbox=%s\n"
2584                                         "context=%s\n"
2585                                         "macrocontext=%s\n"
2586                                         "exten=%s\n"
2587                                         "priority=%d\n"
2588                                         "callerchan=%s\n"
2589                                         "callerid=%s\n"
2590                                         "origdate=%s\n"
2591                                         "origtime=%ld\n"
2592                                         "category=%s\n",
2593                                         ext,
2594                                         chan->context,
2595                                         chan->macrocontext, 
2596                                         chan->exten,
2597                                         chan->priority,
2598                                         chan->name,
2599                                         ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2600                                         date, (long)time(NULL),
2601                                         category ? category : ""); 
2602                         } else
2603                                 ast_log(LOG_WARNING, "Error opening text file for output\n");
2604                         res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2605                         if (res == '0') {
2606                                 if (txt)
2607                                         fclose(txt);
2608                                 goto transfer;
2609                         }
2610                         if (res > 0)
2611                                 res = 0;
2612                         if (txt) {
2613                                 fprintf(txt, "duration=%d\n", duration);
2614                                 fclose(txt);
2615                         }
2616                                 
2617                         if (duration < vmminmessage) {
2618                                 if (option_verbose > 2) 
2619                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2620                                 DELETE(dir,msgnum,fn);
2621                                 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2622                                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2623                                 goto leave_vm_out;
2624                         }
2625                         /* Are there to be more recipients of this message? */
2626                         while (tmpptr) {
2627                                 struct ast_vm_user recipu, *recip;
2628                                 char *exten, *context;
2629                                         
2630                                 exten = strsep(&tmpptr, "&");
2631                                 context = strchr(exten, '@');
2632                                 if (context) {
2633                                         *context = '\0';
2634                                         context++;
2635                                 }
2636                                 if ((recip = find_user(&recipu, context, exten))) {
2637                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2638                                         free_user(recip);
2639                                 }
2640                         }
2641                         if (ast_fileexists(fn, NULL, NULL)) {
2642                                 STORE(dir, vmu->mailbox, vmu->context, msgnum);
2643                                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2644                                 DISPOSE(dir, msgnum);
2645                         }
2646                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
2647                 } else {
2648                         ast_unlock_path(dir);
2649                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2650                         if (!res)
2651                                 res = ast_waitstream(chan, "");
2652                         ast_log(LOG_WARNING, "No more messages possible\n");
2653                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2654                 }
2655         } else
2656                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2657  leave_vm_out:
2658         free_user(vmu);
2659         
2660         return res;
2661 }
2662
2663 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
2664 {
2665         /* we know max messages, so stop process when number is hit */
2666
2667         int x,dest;
2668         char sfn[AST_MAX_FILENAME_LEN];
2669         char dfn[AST_MAX_FILENAME_LEN];
2670
2671         if (vm_lock_path(dir))
2672                 return ERROR_LOCK_PATH;
2673
2674         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
2675                 make_file(sfn, sizeof(sfn), dir, x);
2676                 if (EXISTS(dir, x, sfn, NULL)) {
2677                         
2678                         if(x != dest) {
2679                                 make_file(dfn, sizeof(dfn), dir, dest);
2680                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
2681                         }
2682                         
2683                         dest++;
2684                 }
2685         }
2686         ast_unlock_path(dir);
2687
2688         return 0;
2689 }
2690
2691
2692 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
2693 {
2694         int d;
2695         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2696         return d;
2697 }
2698
2699 static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *context, char *username, int box)
2700 {
2701         char sfn[AST_MAX_FILENAME_LEN];
2702         char dfn[AST_MAX_FILENAME_LEN];
2703         char ddir[AST_MAX_FILENAME_LEN];
2704         char *dbox = mbox(box);
2705         int x;
2706
2707         make_file(sfn, sizeof(sfn), dir, msg);
2708         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
2709
2710         if (vm_lock_path(ddir))
2711                 return ERROR_LOCK_PATH;
2712
2713         for (x = 0; x < vmu->maxmsg; x++) {
2714                 make_file(dfn, sizeof(dfn), ddir, x);
2715                 if (!EXISTS(ddir, x, dfn, NULL))
2716                         break;
2717         }
2718         if (x >= vmu->maxmsg) {
2719                 ast_unlock_path(ddir);
2720                 return -1;
2721         }
2722         if (strcmp(sfn, dfn)) {
2723                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
2724         }
2725         ast_unlock_path(ddir);
2726         
2727         return 0;
2728 }
2729
2730 static int adsi_logo(unsigned char *buf)
2731 {
2732         int bytes = 0;
2733         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2734         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2735         return bytes;
2736 }
2737
2738 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2739 {
2740         unsigned char buf[256];
2741         int bytes=0;
2742         int x;
2743         char num[5];
2744
2745         *useadsi = 0;
2746         bytes += adsi_data_mode(buf + bytes);
2747         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2748
2749         bytes = 0;
2750         bytes += adsi_logo(buf);
2751         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2752 #ifdef DISPLAY
2753         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
2754 #endif
2755         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2756         bytes += adsi_data_mode(buf + bytes);
2757         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2758
2759         if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2760                 bytes = 0;
2761                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2762                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2763                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2764                 bytes += adsi_voice_mode(buf + bytes, 0);
2765                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2766                 return 0;
2767         }
2768
2769 #ifdef DISPLAY
2770         /* Add a dot */
2771         bytes = 0;
2772         bytes += adsi_logo(buf);
2773         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2774         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
2775         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2776         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2777 #endif
2778         bytes = 0;
2779         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2780         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2781         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2782         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2783         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2784         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2785         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2786
2787 #ifdef DISPLAY
2788         /* Add another dot */
2789         bytes = 0;
2790         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
2791         bytes += adsi_voice_mode(buf + bytes, 0);
2792
2793         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2794         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2795 #endif
2796
2797         bytes = 0;
2798         /* These buttons we load but don't use yet */
2799         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2800         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2801         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2802         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2803         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2804         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2805         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2806
2807 #ifdef DISPLAY
2808         /* Add another dot */
2809         bytes = 0;
2810         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
2811         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2812         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2813 #endif
2814
2815         bytes = 0;
2816         for (x=0;x<5;x++) {
2817                 snprintf(num, sizeof(num), "%d", x);
2818                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2819         }
2820         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2821         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2822
2823 #ifdef DISPLAY
2824         /* Add another dot */
2825         bytes = 0;
2826         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
2827         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2828         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2829 #endif
2830
2831         if (adsi_end_download(chan)) {
2832                 bytes = 0;
2833                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2834                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2835                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2836                 bytes += adsi_voice_mode(buf + bytes, 0);
2837                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2838                 return 0;
2839         }
2840         bytes = 0;
2841         bytes += adsi_download_disconnect(buf + bytes);
2842         bytes += adsi_voice_mode(buf + bytes, 0);
2843         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2844
2845         if (option_debug)
2846                 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2847
2848 #ifdef DISPLAY
2849         /* Add last dot */
2850         bytes = 0;
2851         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
2852         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2853 #endif
2854         if (option_debug)
2855                 ast_log(LOG_DEBUG, "Restarting session...\n");
2856
2857         bytes = 0;
2858         /* Load the session now */
2859         if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2860                 *useadsi = 1;
2861                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2862         } else
2863                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2864
2865         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2866         return 0;
2867 }
2868
2869 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2870 {
2871         int x;
2872
2873         if (!adsi_available(chan))
2874                 return;
2875         x = adsi_load_session(chan, adsifdn, adsiver, 1);
2876         if (x < 0)
2877                 return;
2878         if (!x) {
2879                 if (adsi_load_vmail(chan, useadsi)) {
2880                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2881                         return;
2882                 }
2883         } else
2884                 *useadsi = 1;
2885 }
2886
2887 static void adsi_login(struct ast_channel *chan)
2888 {
2889         unsigned char buf[256];
2890         int bytes=0;
2891         unsigned char keys[8];
2892         int x;
2893         if (!adsi_available(chan))
2894                 return;
2895
2896         for (x=0;x<8;x++)
2897                 keys[x] = 0;
2898         /* Set one key for next */
2899         keys[3] = ADSI_KEY_APPS + 3;
2900
2901         bytes += adsi_logo(buf + bytes);
2902         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2903         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2904         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2905         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2906         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2907         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2908         bytes += adsi_set_keys(buf + bytes, keys);
2909         bytes += adsi_voice_mode(buf + bytes, 0);
2910         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2911 }
2912
2913 static void adsi_password(struct ast_channel *chan)
2914 {
2915         unsigned char buf[256];
2916         int bytes=0;
2917         unsigned char keys[8];
2918         int x;
2919         if (!adsi_available(chan))
2920                 return;
2921
2922         for (x=0;x<8;x++)
2923                 keys[x] = 0;
2924         /* Set one key for next */
2925         keys[3] = ADSI_KEY_APPS + 3;
2926
2927         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2928         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2929         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2930         bytes += adsi_set_keys(buf + bytes, keys);
2931         bytes += adsi_voice_mode(buf + bytes, 0);
2932         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2933 }
2934
2935 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2936 {
2937         unsigned char buf[256];
2938         int bytes=0;
2939         unsigned char keys[8];
2940         int x,y;
2941
2942         if (!adsi_available(chan))
2943                 return;
2944
2945         for (x=0;x<5;x++) {
2946                 y = ADSI_KEY_APPS + 12 + start + x;
2947                 if (y > ADSI_KEY_APPS + 12 + 4)
2948                         y = 0;
2949                 keys[x] = ADSI_KEY_SKT | y;
2950         }
2951         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2952         keys[6] = 0;
2953         keys[7] = 0;
2954
2955         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2956         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2957         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2958         bytes += adsi_set_keys(buf + bytes, keys);
2959         bytes += adsi_voice_mode(buf + bytes, 0);
2960
2961         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2962 }
2963
2964 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2965 {
2966         int bytes=0;
2967         unsigned char buf[256]; 
2968         char buf1[256], buf2[256];
2969         char fn2[256];
2970
2971         char cid[256]="";
2972         char *val;
2973         char *name, *num;
2974         char datetime[21]="";
2975         FILE *f;
2976
2977         unsigned char keys[8];
2978
2979         int x;
2980
2981         if (!adsi_available(chan))
2982                 return;
2983
2984         /* Retrieve important info */
2985         snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2986         f = fopen(fn2, "r");
2987         if (f) {
2988                 while (!feof(f)) {      
2989                         fgets((char *)buf, sizeof(buf), f);
2990                         if (!feof(f)) {
2991                                 char *stringp=NULL;
2992                                 stringp = (char *)buf;
2993                                 strsep(&stringp, "=");
2994                                 val = strsep(&stringp, "=");
2995                                 if (!ast_strlen_zero(val)) {
2996                                         if (!strcmp((char *)buf, "callerid"))
2997                                                 ast_copy_string(cid, val, sizeof(cid));
2998                                         if (!strcmp((char *)buf, "origdate"))
2999                                                 ast_copy_string(datetime, val, sizeof(datetime));
3000                                 }
3001                         }
3002                 }
3003                 fclose(f);
3004         }
3005         /* New meaning for keys */
3006         for (x=0;x<5;x++)
3007                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3008         keys[6] = 0x0;
3009         keys[7] = 0x0;
3010
3011         if (!vms->curmsg) {
3012                 /* No prev key, provide "Folder" instead */
3013                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3014         }
3015         if (vms->curmsg >= vms->lastmsg) {
3016                 /* If last message ... */
3017                 if (vms->curmsg) {
3018                         /* but not only message, provide "Folder" instead */
3019                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3020                         bytes += adsi_voice_mode(buf + bytes, 0);
3021
3022                 } else {
3023                         /* Otherwise if only message, leave blank */
3024                         keys[3] = 1;
3025                 }
3026         }
3027
3028         if (!ast_strlen_zero(cid)) {
3029                 ast_callerid_parse(cid, &name, &num);
3030                 if (!name)
3031                         name = num;
3032         } else
3033                 name = "Unknown Caller";
3034
3035         /* If deleted, show "undeleted" */
3036
3037         if (vms->deleted[vms->curmsg])
3038                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3039
3040         /* Except "Exit" */
3041         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3042         snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3043                 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3044         snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3045
3046         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3047         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3048         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3049         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3050         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3051         bytes += adsi_set_keys(buf + bytes, keys);
3052         bytes += adsi_voice_mode(buf + bytes, 0);
3053
3054         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3055 }
3056
3057 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3058 {
3059         int bytes=0;
3060         unsigned char buf[256];
3061         unsigned char keys[8];
3062
3063         int x;
3064
3065         if (!adsi_available(chan))
3066                 return;
3067
3068         /* New meaning for keys */
3069         for (x=0;x<5;x++)
3070                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3071
3072         keys[6] = 0x0;
3073         keys[7] = 0x0;
3074
3075         if (!vms->curmsg) {
3076                 /* No prev key, provide "Folder" instead */
3077                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3078         }
3079         if (vms->curmsg >= vms->lastmsg) {
3080                 /* If last message ... */
3081                 if (vms->curmsg) {
3082                         /* but not only message, provide "Folder" instead */
3083                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3084                 } else {
3085                         /* Otherwise if only message, leave blank */
3086                         keys[3] = 1;
3087                 }
3088         }
3089
3090         /* If deleted, show "undeleted" */
3091         if (vms->deleted[vms->curmsg]) 
3092                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3093
3094         /* Except "Exit" */
3095         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3096         bytes += adsi_set_keys(buf + bytes, keys);
3097         bytes += adsi_voice_mode(buf + bytes, 0);
3098
3099         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3100 }
3101
3102 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
3103 {
3104         unsigned char buf[256] = "";
3105         char buf1[256] = "", buf2[256] = "";
3106         int bytes=0;
3107         unsigned char keys[8];
3108         int x;
3109
3110         char *newm = (vms->newmessages == 1) ? "message" : "messages";
3111         char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
3112         if (!adsi_available(chan))
3113                 return;
3114         if (vms->newmessages) {
3115                 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
3116                 if (vms->oldmessages) {
3117                         strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
3118                         snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
3119                 } else {
3120                         snprintf(buf2, sizeof(buf2), "%s.", newm);
3121                 }
3122         } else if (vms->oldmessages) {
3123                 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
3124                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
3125         } else {
3126                 strcpy(buf1, "You have no messages.");
3127                 buf2[0] = ' ';
3128                 buf2[1] = '\0';
3129         }
3130         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3131         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3132         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3133
3134         for (x=0;x<6;x++)
3135                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3136         keys[6] = 0;
3137         keys[7] = 0;
3138
3139         /* Don't let them listen if there are none */
3140         if (vms->lastmsg < 0)
3141                 keys[0] = 1;
3142         bytes += adsi_set_keys(buf + bytes, keys);
3143
3144         bytes += adsi_voice_mode(buf + bytes, 0);
3145
3146         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3147 }
3148
3149 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
3150 {
3151         unsigned char buf[256] = "";
3152         char buf1[256] = "", buf2[256] = "";
3153         int bytes=0;
3154         unsigned char keys[8];
3155         int x;
3156
3157         char *mess = (vms->lastmsg == 0) ? "message" : "messages";
3158
3159         if (!adsi_available(chan))
3160                 return;
3161
3162         /* Original command keys */
3163         for (x=0;x<6;x++)
3164                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
3165
3166         keys[6] = 0;
3167         keys[7] = 0;
3168
3169         if ((vms->lastmsg + 1) < 1)
3170                 keys[0] = 0;
3171
3172         snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
3173                 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
3174
3175         if (vms->lastmsg + 1)
3176                 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
3177         else
3178                 strcpy(buf2, "no messages.");
3179         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3180         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3181         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
3182         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3183         bytes += adsi_set_keys(buf + bytes, keys);
3184
3185         bytes += adsi_voice_mode(buf + bytes, 0);
3186
3187         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3188         
3189 }
3190
3191 /*
3192 static void adsi_clear(struct ast_channel *chan)
3193 {
3194         char buf[256];
3195         int bytes=0;
3196         if (!adsi_available(chan))
3197                 return;
3198         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3199         bytes += adsi_voice_mode(buf + bytes, 0);
3200
3201         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3202 }
3203 */
3204
3205 static void adsi_goodbye(struct ast_channel *chan)
3206 {
3207         unsigned char buf[256];
3208         int bytes=0;
3209
3210         if (!adsi_available(chan))
3211                 return;
3212         bytes += adsi_logo(buf + bytes);
3213         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
3214         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
3215         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3216         bytes += adsi_voice_mode(buf + bytes, 0);
3217
3218         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3219 }
3220
3221 /*! get_folder: voicemail Folder menu 
3222  * Plays "press 1 for INBOX messages" etc
3223  * Should possibly be internationalized
3224  */
3225 static int get_folder(struct ast_channel *chan, int start)
3226 {
3227         int x;
3228         int d;
3229         char fn[AST_MAX_FILENAME_LEN];
3230
3231         d = ast_play_and_wait(chan, "vm-press");        /* "Press" */
3232         if (d)
3233                 return d;
3234         for (x = start; x< 5; x++) {    /* For all folders */
3235                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
3236                         return d;
3237                 d = ast_play_and_wait(chan, "vm-for");  /* "for" */
3238                 if (d)
3239                         return d;
3240                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));     /* Folder name */
3241                 d = vm_play_folder_name(chan, fn);
3242                 if (d)
3243                         return d;
3244                 d = ast_waitfordigit(chan, 500);
3245                 if (d)
3246                         return d;
3247         }
3248         d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
3249         if (d)
3250                 return d;
3251         d = ast_waitfordigit(chan, 4000);
3252         return d;
3253 }
3254
3255 static int get_folder2(struct ast_channel *chan, char *fn, int start)
3256 {
3257         int res = 0;
3258         res = ast_play_and_wait(chan, fn);      /* Folder name */
3259         while (((res < '0') || (res > '9')) &&
3260                         (res != '#') && (res >= 0)) {
3261                 res = get_folder(chan, 0);
3262         }
3263         return res;
3264 }
3265
3266 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts,
3267                              char *context, signed char record_gain)
3268 {
3269         int cmd = 0;
3270         int retries = 0;
3271         int duration = 0;
3272         signed char zero_gain = 0;
3273
3274         while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
3275                 if (cmd)
3276                         retries = 0;
3277                 switch (cmd) {
3278                 case '1': 
3279                         /* prepend a message to the current message and return */
3280                 {
3281                         char file[200];
3282                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
3283                         if (record_gain)
3284                                 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
3285                         cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
3286                         if (record_gain)
3287                                 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
3288                         break;
3289                 }
3290                 case '2': 
3291                         cmd = 't';
3292                         break;
3293                 case '*':
3294                         cmd = '*';
3295                         break;
3296                 default: 
3297                         cmd = ast_play_and_wait(chan,"vm-forwardoptions");
3298                                 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3299                         if (!cmd)
3300                                 cmd = ast_play_and_wait(chan,"vm-starmain");
3301                                 /* "press star to return to the main menu" */
3302                         if (!cmd)
3303                                 cmd = ast_waitfordigit(chan,6000);
3304                         if (!cmd)
3305                                 retries++;
3306                         if (retries > 3)
3307                                 cmd = 't';
3308                  }
3309         }
3310         if (cmd == 't')
3311                 cmd = 0;
3312         return cmd;
3313 }
3314
3315 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
3316 {
3317         char todir[AST_MAX_FILENAME_LEN], fn[AST_MAX_FILENAME_LEN];
3318         char ext_context[AST_MAX_EXTENSION], *stringp;
3319         int newmsgs = 0, oldmsgs = 0;
3320         const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3321
3322         make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
3323         make_file(fn, sizeof(fn), todir, msgnum);
3324         snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
3325
3326         /* Attach only the first format */
3327         if ((fmt = ast_strdupa(fmt))) {
3328                 stringp = fmt;
3329                 strsep(&stringp, "|");
3330
3331                 if (!ast_strlen_zero(vmu->email)) {
3332                         int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3333                         char *myserveremail = serveremail;
3334                         attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
3335                         if (!ast_strlen_zero(vmu->serveremail))
3336                                 myserveremail = vmu->serveremail;
3337                         sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, category);
3338                 }
3339
3340                 if (!ast_strlen_zero(vmu->pager)) {
3341                         char *myserveremail = serveremail;
3342                         if (!ast_strlen_zero(vmu->serveremail))
3343                                 myserveremail = vmu->serveremail;
3344                         sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
3345                 }
3346         }
3347
3348         if (ast_test_flag(vmu, VM_DELETE)) {
3349                 DELETE(todir, msgnum, fn);
3350         }
3351
3352         /* Leave voicemail for someone */
3353         if (ast_app_has_voicemail(ext_context, NULL)) {
3354                 ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
3355         }
3356         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);
3357         run_externnotify(vmu->context, vmu->mailbox);
3358         return 0;
3359 }
3360
3361 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender,
3362                            char *fmt, int flag, signed char record_gain)
3363 {
3364         char username[70]="";
3365         char sys[AST_MAX_SHELL_COMMAND];
3366         char todir[AST_MAX_FILENAME_LEN];
3367         int todircount=0;
3368         int duration;
3369         struct ast_config *mif;
3370         char miffile[AST_MAX_FILENAME_LEN];
3371         char fn[AST_MAX_FILENAME_LEN];
3372         char callerid[512];
3373         char ext_context[AST_MAX_EXTENSION]="";
3374         int res = 0, cmd = 0;
3375         struct ast_vm_user *receiver = NULL, *vmtmp;
3376         AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
3377         char tmp[256];
3378         char *stringp, *s;
3379         int saved_messages = 0, found = 0;
3380         int valid_extensions = 0;
3381         
3382         while (!res && !valid_extensions) {
3383                 int use_directory = 0;
3384                 if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
3385                         int done = 0;
3386                         int retries = 0;
3387                         cmd=0;
3388                         while((cmd >= 0) && !done ){
3389                                 if (cmd)
3390                                         retries = 0;
3391                                 switch (cmd) {
3392                                 case '1': 
3393                                         use_directory = 0;
3394                                         done = 1;
3395                                         break;
3396                                 case '2': 
3397                                         use_directory = 1;
3398                                         done=1;
3399                                         break;
3400                                 case '*': 
3401                                         cmd = 't';
3402                                         done = 1;
3403                                         break;
3404                                 default: 
3405                                         /* Press 1 to enter an extension press 2 to use the directory */
3406                                         cmd = ast_play_and_wait(chan,"vm-forward");
3407                                         if (!cmd)
3408                                                 cmd = ast_waitfordigit(chan,3000);
3409                                         if (!cmd)
3410                                                 retries++;
3411                                         if (retries > 3)
3412                                         {
3413                                                 cmd = 't';
3414                                                 done = 1;
3415                                         }
3416                                         
3417                                  }
3418                         }
3419                         if( cmd<0 || cmd=='t' )
3420                                 break;
3421                 }
3422                 
3423                 if (use_directory) {
3424                         /* use app_directory */
3425                         
3426                         char old_context[sizeof(chan->context)];
3427                         char old_exten[sizeof(chan->exten)];
3428                         int old_priority;
3429                         struct ast_app* app;
3430
3431                         
3432                         app = pbx_findapp("Directory");
3433                         if (app) {
3434                                 /* make mackup copies */
3435                                 memcpy(old_context, chan->context, sizeof(chan->context));
3436                                 memcpy(old_exten, chan->exten, sizeof(chan->exten));
3437                                 old_priority = chan->priority;
3438                                 
3439                                 /* call the the Directory, changes the channel */
3440                                 res = pbx_exec(chan, app, context ? context : "default", 1);
3441                                 
3442                                 ast_copy_string(username, chan->exten, sizeof(username));
3443                                 
3444                                 /* restore the old context, exten, and priority */
3445                                 memcpy(chan->context, old_context, sizeof(chan->context));
3446                                 memcpy(chan->exten, old_exten, sizeof(chan->exten));
3447                                 chan->priority = old_priority;
3448                                 
3449                         } else {
3450                                 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
3451                                 ast_clear_flag((&globalflags), VM_DIRECFORWARD);        
3452                         }
3453                 } else  {
3454                         /* Ask for an extension */
3455                         res = ast_streamfile(chan, "vm-extension", chan->language);     /* "extension" */
3456                         if (res)
3457                                 break;
3458                         if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
3459                                 break;
3460                 }
3461                 
3462                 /* start all over if no username */
3463