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