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