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