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