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