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