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