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