3487bdd4f3bbc8ecdb402e24d07c9630e3231e70
[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);
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);
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 = SQLExecute(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                                         fdlen = colsize;
747                                         fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
748                                         if (fd > -1) {
749                                                 /* Ugh, gotta fill it  so we can mmap */
750                                                 char tmp[1024]="";
751                                                 size_t left = 0, bytes = 0;
752                                                 left = fdlen;
753                                                 while(left) {
754                                                         bytes = left;
755                                                         if (bytes > sizeof(tmp))
756                                                                 bytes = sizeof(tmp);
757                                                         if (write(fd, tmp, bytes) != bytes) {
758                                                                 close(fd); 
759                                                                 fd = -1;
760                                                                 break;
761                                                         }
762                                                         left -= bytes;
763                                                 }
764                                                 if (fd > -1)
765                                                         fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
766                                         }
767                                         if (fdm) {
768                                                 memset(fdm, 0, fdlen);
769                                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
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                                                 fdlen = colsize;
776                                                 ftruncate(fd, fdlen);
777                                         }
778                                 } else {
779                                         res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
780                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
781                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
782                                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
783                                                 goto yuck;
784                                         }
785                                         if (strcmp(coltitle, "msgnum") && strcmp(coltitle, "dir") && f)
786                                                 fprintf(f, "%s=%s\n", coltitle, rowdata);
787                                 }
788                         }
789                 }
790                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
791         } else
792                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
793 yuck:   
794         if (f)
795                 fclose(f);
796         if (fdm)
797                 munmap(fdm, fdlen);
798         if (fd > -1)
799                 close(fd);
800         return x - 1;
801 }
802
803 static int remove_file(char *dir, int msgnum)
804 {
805         char fn[256];
806         char full_fn[256];
807         char msgnums[80];
808         
809         if (msgnum > -1) {
810                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
811                 make_file(fn, sizeof(fn), dir, msgnum);
812         } else
813                 strncpy(fn, dir, sizeof(fn) - 1);
814         ast_filedelete(fn, NULL);       
815         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
816         unlink(full_fn);
817         return 0;
818 }
819
820 static int last_message_index(char *dir)
821 {
822         int x = 0;
823         int res;
824         SQLLEN rowcount=0;
825         SQLHSTMT stmt;
826         char sql[256];
827         char rowdata[20];
828         
829         odbc_obj *obj;
830         obj = fetch_odbc_obj(odbc_database);
831         if (obj) {
832                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
833                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
834                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
835                         goto yuck;
836                 }
837                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=?");
838                 res = SQLPrepare(stmt, sql, SQL_NTS);
839                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
840                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
841                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
842                         goto yuck;
843                 }
844                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
845                 res = SQLExecute(stmt);
846                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
847                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
848                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
849                         goto yuck;
850                 }
851                 res = SQLRowCount(stmt, &rowcount);
852                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
853                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
854                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
855                         goto yuck;
856                 }
857                 res = SQLFetch(stmt);
858                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
859                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
860                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
861                         goto yuck;
862                 }
863                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
864                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
865                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
866                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
867                         goto yuck;
868                 }
869                 if (sscanf(rowdata, "%i", &x) != 1)
870                         ast_log(LOG_WARNING, "Failed to read message count!\n");
871                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
872         } else
873                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
874 yuck:   
875         return x - 1;
876 }
877
878 static int message_exists(char *dir, int msgnum)
879 {
880         int x = 0;
881         int res;
882         SQLLEN rowcount=0;
883         SQLHSTMT stmt;
884         char sql[256];
885         char rowdata[20];
886         char msgnums[20];
887         
888         odbc_obj *obj;
889         obj = fetch_odbc_obj(odbc_database);
890         if (obj) {
891                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
892                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
893                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
894                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
895                         goto yuck;
896                 }
897                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=? AND msgnum=?");
898                 res = SQLPrepare(stmt, sql, SQL_NTS);
899                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
900                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
901                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
902                         goto yuck;
903                 }
904                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
905                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
906                 res = SQLExecute(stmt);
907                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
908                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
909                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
910                         goto yuck;
911                 }
912                 res = SQLRowCount(stmt, &rowcount);
913                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
914                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
915                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
916                         goto yuck;
917                 }
918                 res = SQLFetch(stmt);
919                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
920                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
921                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
922                         goto yuck;
923                 }
924                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
925                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
926                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
927                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
928                         goto yuck;
929                 }
930                 if (sscanf(rowdata, "%i", &x) != 1)
931                         ast_log(LOG_WARNING, "Failed to read message count!\n");
932                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
933         } else
934                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
935 yuck:   
936         return x;
937 }
938
939
940 static int count_messages(char *dir)
941 {
942         return last_message_index(dir) + 1;
943 }
944 static void delete_file(char *sdir, int smsg)
945 {
946         int res;
947         SQLLEN rowcount=0;
948         SQLHSTMT stmt;
949         char sql[256];
950         char msgnums[20];
951         
952         odbc_obj *obj;
953         obj = fetch_odbc_obj(odbc_database);
954         if (obj) {
955                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
956                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
957                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
958                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
959                         goto yuck;
960                 }
961                 snprintf(sql, sizeof(sql), "DELETE FROM voicemessages WHERE dir=? AND msgnum=?");
962                 res = SQLPrepare(stmt, sql, SQL_NTS);
963                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
964                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
965                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
966                         goto yuck;
967                 }
968                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
969                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
970                 res = SQLExecute(stmt);
971                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
972                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
973                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
974                         goto yuck;
975                 }
976                 res = SQLRowCount(stmt, &rowcount);
977                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
978                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
979                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
980                         goto yuck;
981                 }
982                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
983         } else
984                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
985 yuck:
986         return; 
987 }
988
989 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg)
990 {
991         int res;
992         SQLLEN rowcount=0;
993         SQLHSTMT stmt;
994         char sql[256];
995         char msgnums[20];
996         char msgnumd[20];
997         odbc_obj *obj;
998
999         delete_file(ddir, dmsg);
1000         obj = fetch_odbc_obj(odbc_database);
1001         if (obj) {
1002                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1003                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1004                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1005                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1006                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1007                         goto yuck;
1008                 }
1009                 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=?");
1010                 res = SQLPrepare(stmt, sql, SQL_NTS);
1011                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1012                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1013                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1014                         goto yuck;
1015                 }
1016                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1017                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1018                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1019                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1020                 res = SQLExecute(stmt);
1021                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1022                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1023                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1024                         goto yuck;
1025                 }
1026                 res = SQLRowCount(stmt, &rowcount);
1027                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1028                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1029                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1030                         goto yuck;
1031                 }
1032                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1033         } else
1034                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1035 yuck:
1036         return; 
1037 }
1038
1039 static int store_file(char *dir, int msgnum)
1040 {
1041         int x = 0;
1042         int res;
1043         int fd = -1;
1044         void *fdm=NULL;
1045         size_t fdlen = -1;
1046         SQLLEN rowcount=0;
1047         SQLHSTMT stmt;
1048         SQLINTEGER len;
1049         char sql[256];
1050         char msgnums[20];
1051         char fn[256];
1052         char full_fn[256];
1053         char fmt[80]="";
1054         char *c;
1055         char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1056         char *category = "";
1057         struct ast_config *cfg=NULL;
1058         odbc_obj *obj;
1059
1060         delete_file(dir, msgnum);
1061         obj = fetch_odbc_obj(odbc_database);
1062         if (obj) {
1063                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1064                 c = strchr(fmt, '|');
1065                 if (c)
1066                         *c = '\0';
1067                 if (!strcasecmp(fmt, "wav49"))
1068                         strncpy(fmt, "WAV", sizeof(fmt));
1069                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1070                 if (msgnum > -1)
1071                         make_file(fn, sizeof(fn), dir, msgnum);
1072                 else
1073                         strncpy(fn, dir, sizeof(fn) - 1);
1074                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1075                 cfg = ast_load(full_fn);
1076                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1077                 fd = open(full_fn, O_RDWR);
1078                 if (fd < 0) {
1079                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1080                         goto yuck;
1081                 }
1082                 if (cfg) {
1083                         context = ast_variable_retrieve(cfg, "message", "context");
1084                         if (!context) context = "";
1085                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1086                         if (!macrocontext) macrocontext = "";
1087                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1088                         if (!callerid) callerid = "";
1089                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1090                         if (!origtime) origtime = "";
1091                         duration = ast_variable_retrieve(cfg, "message", "duration");
1092                         if (!duration) duration = "";
1093                         category = ast_variable_retrieve(cfg, "message", "category");
1094                         if (!category) category = "";
1095                 }
1096                 fdlen = lseek(fd, 0, SEEK_END);
1097                 lseek(fd, 0, SEEK_SET);
1098                 printf("Length is %d\n", fdlen);
1099                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1100                 if (!fdm) {
1101                         ast_log(LOG_WARNING, "Memory map failed!\n");
1102                         goto yuck;
1103                 } 
1104                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1105                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1106                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1107                         goto yuck;
1108                 }
1109                 if (!ast_strlen_zero(category)) 
1110                         snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,category) VALUES (?,?,?,?,?,?,?,?,?)");
1111                 else
1112                         snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)");
1113                 res = SQLPrepare(stmt, sql, SQL_NTS);
1114                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1115                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1116                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1117                         goto yuck;
1118                 }
1119                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1120                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1121                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1122                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1123                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1124                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1125                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1126                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1127                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1128                 if (!ast_strlen_zero(category))
1129                         SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1130                 res = SQLExecute(stmt);
1131                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1132                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1133                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1134                         goto yuck;
1135                 }
1136                 res = SQLRowCount(stmt, &rowcount);
1137                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1138                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1139                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1140                         goto yuck;
1141                 }
1142                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1143         } else
1144                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1145 yuck:   
1146         if (cfg)
1147                 ast_destroy(cfg);
1148         if (fdm)
1149                 munmap(fdm, fdlen);
1150         if (fd > -1)
1151                 close(fd);
1152         return x;
1153 }
1154
1155 static void rename_file(char *sdir, int smsg, char *ddir, int dmsg)
1156 {
1157         int res;
1158         SQLLEN rowcount=0;
1159         SQLHSTMT stmt;
1160         char sql[256];
1161         char msgnums[20];
1162         char msgnumd[20];
1163         odbc_obj *obj;
1164
1165         delete_file(ddir, dmsg);
1166         obj = fetch_odbc_obj(odbc_database);
1167         if (obj) {
1168                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1169                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1170                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1171                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1172                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1173                         goto yuck;
1174                 }
1175                 snprintf(sql, sizeof(sql), "UPDATE voicemessages SET dir=?, msgnum=? WHERE dir=? AND msgnum=?");
1176                 res = SQLPrepare(stmt, sql, SQL_NTS);
1177                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1178                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1179                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1180                         goto yuck;
1181                 }
1182                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1183                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1184                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1185                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1186                 res = SQLExecute(stmt);
1187                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1188                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1189                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1190                         goto yuck;
1191                 }
1192                 res = SQLRowCount(stmt, &rowcount);
1193                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1194                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1195                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1196                         goto yuck;
1197                 }
1198                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1199         } else
1200                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1201 yuck:
1202         return; 
1203 }
1204
1205 #else
1206
1207 static int count_messages(char *dir)
1208 {
1209         /* Find all .txt files - even if they are not in sequence from 0000 */
1210
1211         int vmcount = 0;
1212         DIR *vmdir = NULL;
1213         struct dirent *vment = NULL;
1214
1215         if ((vmdir = opendir(dir))) {
1216                 while ((vment = readdir(vmdir)))
1217                 {
1218                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
1219                         {
1220                                 vmcount++;
1221                         }
1222                 }
1223                 closedir(vmdir);
1224         }
1225
1226         return vmcount;
1227 }
1228
1229 static void rename_file(char *sfn, char *dfn)
1230 {
1231         char stxt[256];
1232         char dtxt[256];
1233         ast_filerename(sfn,dfn,NULL);
1234         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1235         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1236         rename(stxt, dtxt);
1237 }
1238
1239 static int copy(char *infile, char *outfile)
1240 {
1241         int ifd;
1242         int ofd;
1243         int res;
1244         int len;
1245         char buf[4096];
1246
1247 #ifdef HARDLINK_WHEN_POSSIBLE
1248         /* Hard link if possible; saves disk space & is faster */
1249         if (link(infile, outfile)) {
1250 #endif
1251                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1252                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1253                         return -1;
1254                 }
1255                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1256                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1257                         close(ifd);
1258                         return -1;
1259                 }
1260                 do {
1261                         len = read(ifd, buf, sizeof(buf));
1262                         if (len < 0) {
1263                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1264                                 close(ifd);
1265                                 close(ofd);
1266                                 unlink(outfile);
1267                         }
1268                         if (len) {
1269                                 res = write(ofd, buf, len);
1270                                 if (res != len) {
1271                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1272                                         close(ifd);
1273                                         close(ofd);
1274                                         unlink(outfile);
1275                                 }
1276                         }
1277                 } while (len);
1278                 close(ifd);
1279                 close(ofd);
1280                 return 0;
1281 #ifdef HARDLINK_WHEN_POSSIBLE
1282         } else {
1283                 /* Hard link succeeded */
1284                 return 0;
1285         }
1286 #endif
1287 }
1288
1289 static void copy_file(char *frompath, char *topath)
1290 {
1291         char frompath2[256],topath2[256];
1292         ast_filecopy(frompath, topath, NULL);
1293         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1294         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1295         copy(frompath2, topath2);
1296 }
1297
1298 static int last_message_index(char *dir)
1299 {
1300         int x;
1301         char fn[256];
1302         for (x=0;x<MAXMSG;x++) {
1303                 make_file(fn, sizeof(fn), dir, x);
1304                 if (ast_fileexists(fn, NULL, NULL) < 1)
1305                         break;
1306         }
1307         return x-1;
1308 }
1309
1310 static int vm_delete(char *file)
1311 {
1312         char *txt;
1313         int txtsize = 0;
1314
1315         txtsize = (strlen(file) + 5)*sizeof(char);
1316         txt = (char *)alloca(txtsize);
1317         /* Sprintf here would safe because we alloca'd exactly the right length,
1318          * but trying to eliminate all sprintf's anyhow
1319          */
1320         snprintf(txt, txtsize, "%s.txt", file);
1321         unlink(txt);
1322         return ast_filedelete(file, NULL);
1323 }
1324
1325
1326 #endif
1327 static int
1328 inbuf(struct baseio *bio, FILE *fi)
1329 {
1330         int l;
1331
1332         if (bio->ateof)
1333                 return 0;
1334
1335         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1336                 if (ferror(fi))
1337                         return -1;
1338
1339                 bio->ateof = 1;
1340                 return 0;
1341         }
1342
1343         bio->iolen= l;
1344         bio->iocp= 0;
1345
1346         return 1;
1347 }
1348
1349 static int 
1350 inchar(struct baseio *bio, FILE *fi)
1351 {
1352         if (bio->iocp>=bio->iolen) {
1353                 if (!inbuf(bio, fi))
1354                         return EOF;
1355         }
1356
1357         return bio->iobuf[bio->iocp++];
1358 }
1359
1360 static int
1361 ochar(struct baseio *bio, int c, FILE *so)
1362 {
1363         if (bio->linelength>=BASELINELEN) {
1364                 if (fputs(eol,so)==EOF)
1365                         return -1;
1366
1367                 bio->linelength= 0;
1368         }
1369
1370         if (putc(((unsigned char)c),so)==EOF)
1371                 return -1;
1372
1373         bio->linelength++;
1374
1375         return 1;
1376 }
1377
1378 static int base_encode(char *filename, FILE *so)
1379 {
1380         unsigned char dtable[BASEMAXINLINE];
1381         int i,hiteof= 0;
1382         FILE *fi;
1383         struct baseio bio;
1384
1385         memset(&bio, 0, sizeof(bio));
1386         bio.iocp = BASEMAXINLINE;
1387
1388         if (!(fi = fopen(filename, "rb"))) {
1389                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1390                 return -1;
1391         }
1392
1393         for (i= 0;i<9;i++) {
1394                 dtable[i]= 'A'+i;
1395                 dtable[i+9]= 'J'+i;
1396                 dtable[26+i]= 'a'+i;
1397                 dtable[26+i+9]= 'j'+i;
1398         }
1399         for (i= 0;i<8;i++) {
1400                 dtable[i+18]= 'S'+i;
1401                 dtable[26+i+18]= 's'+i;
1402         }
1403         for (i= 0;i<10;i++) {
1404                 dtable[52+i]= '0'+i;
1405         }
1406         dtable[62]= '+';
1407         dtable[63]= '/';
1408
1409         while (!hiteof){
1410                 unsigned char igroup[3],ogroup[4];
1411                 int c,n;
1412
1413                 igroup[0]= igroup[1]= igroup[2]= 0;
1414
1415                 for (n= 0;n<3;n++) {
1416                         if ((c = inchar(&bio, fi)) == EOF) {
1417                                 hiteof= 1;
1418                                 break;
1419                         }
1420
1421                         igroup[n]= (unsigned char)c;
1422                 }
1423
1424                 if (n> 0) {
1425                         ogroup[0]= dtable[igroup[0]>>2];
1426                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1427                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1428                         ogroup[3]= dtable[igroup[2]&0x3F];
1429
1430                         if (n<3) {
1431                                 ogroup[3]= '=';
1432
1433                                 if (n<2)
1434                                         ogroup[2]= '=';
1435                         }
1436
1437                         for (i= 0;i<4;i++)
1438                                 ochar(&bio, ogroup[i], so);
1439                 }
1440         }
1441
1442         if (fputs(eol,so)==EOF)
1443                 return 0;
1444
1445         fclose(fi);
1446
1447         return 1;
1448 }
1449
1450 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)
1451 {
1452         char callerid[256];
1453         /* Prepare variables for substition in email body and subject */
1454         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1455         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1456         snprintf(passdata, passdatasize, "%d", msgnum);
1457         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1458         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1459         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1460         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1461         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1462         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1463 }
1464
1465 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)
1466 {
1467         FILE *p=NULL;
1468         int pfd;
1469         char date[256];
1470         char host[256];
1471         char who[256];
1472         char bound[256];
1473         char fname[256];
1474         char dur[256];
1475         char tmp[80] = "/tmp/astmail-XXXXXX";
1476         char tmp2[256];
1477         time_t t;
1478         struct tm tm;
1479         struct vm_zone *the_zone = NULL;
1480         if (vmu && ast_strlen_zero(vmu->email)) {
1481                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
1482                 return(0);
1483         }
1484         if (!strcmp(format, "wav49"))
1485                 format = "WAV";
1486         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));
1487         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1488            command hangs */
1489         pfd = mkstemp(tmp);
1490         if (pfd > -1) {
1491                 p = fdopen(pfd, "w");
1492                 if (!p) {
1493                         close(pfd);
1494                         pfd = -1;
1495                 }
1496         }
1497         if (p) {
1498                 gethostname(host, sizeof(host));
1499                 if (strchr(srcemail, '@'))
1500                         strncpy(who, srcemail, sizeof(who)-1);
1501                 else {
1502                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1503                 }
1504                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1505                 time(&t);
1506
1507                 /* Does this user have a timezone specified? */
1508                 if (!ast_strlen_zero(vmu->zonetag)) {
1509                         /* Find the zone in the list */
1510                         struct vm_zone *z;
1511                         z = zones;
1512                         while (z) {
1513                                 if (!strcmp(z->name, vmu->zonetag)) {
1514                                         the_zone = z;
1515                                         break;
1516                                 }
1517                                 z = z->next;
1518                         }
1519                 }
1520
1521                 if (the_zone)
1522                         ast_localtime(&t,&tm,the_zone->timezone);
1523                 else
1524                         ast_localtime(&t,&tm,NULL);
1525                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1526                 fprintf(p, "Date: %s\n", date);
1527
1528                 if (*fromstring) {
1529                         struct ast_channel *ast = ast_channel_alloc(0);
1530                         if (ast) {
1531                                 char *passdata;
1532                                 int vmlen = strlen(fromstring)*3 + 200;
1533                                 if ((passdata = alloca(vmlen))) {
1534                                         memset(passdata, 0, vmlen);
1535                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1536                                         pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1537                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1538                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1539                                 ast_channel_free(ast);
1540                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1541                 } else
1542                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1543                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1544
1545                 if (emailsubject) {
1546                         struct ast_channel *ast = ast_channel_alloc(0);
1547                         if (ast) {
1548                                 char *passdata;
1549                                 int vmlen = strlen(emailsubject)*3 + 200;
1550                                 if ((passdata = alloca(vmlen))) {
1551                                         memset(passdata, 0, vmlen);
1552                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1553                                         pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1554                                         fprintf(p, "Subject: %s\n",passdata);
1555                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1556                                 ast_channel_free(ast);
1557                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1558                 } else
1559                 if (*emailtitle) {
1560                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1561                         fprintf(p,"\n") ;
1562                 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1563                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1564                 else
1565                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1566                 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1567                 fprintf(p, "MIME-Version: 1.0\n");
1568                 if (attach_user_voicemail) {
1569                         /* Something unique. */
1570                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1571
1572                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1573
1574                         fprintf(p, "--%s\n", bound);
1575                 }
1576                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1577                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1578                 if (emailbody) {
1579                         struct ast_channel *ast = ast_channel_alloc(0);
1580                         if (ast) {
1581                                 char *passdata;
1582                                 int vmlen = strlen(emailbody)*3 + 200;
1583                                 if ((passdata = alloca(vmlen))) {
1584                                         memset(passdata, 0, vmlen);
1585                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1586                                         pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1587                                         fprintf(p, "%s\n",passdata);
1588                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1589                                 ast_channel_free(ast);
1590                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1591                 } else {
1592                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1593
1594                         "in mailbox %s from %s, on %s so you might\n"
1595                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
1596                         dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1597                 }
1598                 if (attach_user_voicemail) {
1599                         fprintf(p, "--%s\n", bound);
1600                         fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
1601                         fprintf(p, "Content-Transfer-Encoding: base64\n");
1602                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1603                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1604
1605                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1606                         base_encode(fname, p);
1607                         fprintf(p, "\n\n--%s--\n.\n", bound);
1608                 }
1609                 fclose(p);
1610                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1611                 ast_safe_system(tmp2);
1612                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1613         } else {
1614                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1615                 return -1;
1616         }
1617         return 0;
1618 }
1619
1620 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
1621 {
1622         FILE *p=NULL;
1623         int pfd;
1624         char date[256];
1625         char host[256];
1626         char who[256];
1627         char dur[256];
1628         char tmp[80] = "/tmp/astmail-XXXXXX";
1629         char tmp2[256];
1630         time_t t;
1631         struct tm tm;
1632         struct vm_zone *the_zone = NULL;
1633         pfd = mkstemp(tmp);
1634
1635         if (pfd > -1) {
1636                 p = fdopen(pfd, "w");
1637                 if (!p) {
1638                         close(pfd);
1639                         pfd = -1;
1640                 }
1641         }
1642
1643         if (p) {
1644                 gethostname(host, sizeof(host));
1645                 if (strchr(srcemail, '@'))
1646                         strncpy(who, srcemail, sizeof(who)-1);
1647                 else {
1648                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1649                 }
1650                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1651                 time(&t);
1652
1653                 /* Does this user have a timezone specified? */
1654                 if (!ast_strlen_zero(vmu->zonetag)) {
1655                         /* Find the zone in the list */
1656                         struct vm_zone *z;
1657                         z = zones;
1658                         while (z) {
1659                                 if (!strcmp(z->name, vmu->zonetag)) {
1660                                         the_zone = z;
1661                                         break;
1662                                 }
1663                                 z = z->next;
1664                         }
1665                 }
1666
1667                 if (the_zone)
1668                         ast_localtime(&t,&tm,the_zone->timezone);
1669                 else
1670                         ast_localtime(&t,&tm,NULL);
1671
1672                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1673                 fprintf(p, "Date: %s\n", date);
1674
1675                 if (*pagerfromstring) {
1676                         struct ast_channel *ast = ast_channel_alloc(0);
1677                         if (ast) {
1678                                 char *passdata;
1679                                 int vmlen = strlen(fromstring)*3 + 200;
1680                                 if ((passdata = alloca(vmlen))) {
1681                                         memset(passdata, 0, vmlen);
1682                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1683                                         pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1684                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1685                                 } else 
1686                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1687                                 ast_channel_free(ast);
1688                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1689                 } else
1690                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1691                 fprintf(p, "To: %s\n", pager);
1692                 fprintf(p, "Subject: New VM\n\n");
1693                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1694                 fprintf(p, "New %s long msg in box %s\n"
1695                            "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1696                 fclose(p);
1697                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1698                 ast_safe_system(tmp2);
1699                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1700         } else {
1701                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1702                 return -1;
1703         }
1704         return 0;
1705 }
1706
1707 static int get_date(char *s, int len)
1708 {
1709         struct tm tm;
1710         time_t t;
1711         t = time(0);
1712         localtime_r(&t,&tm);
1713         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1714 }
1715
1716 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1717 {
1718         int res;
1719         char fn[256];
1720         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
1721         RETRIEVE(fn, -1);
1722         if (ast_fileexists(fn, NULL, NULL) > 0) {
1723                 res = ast_streamfile(chan, fn, chan->language);
1724                 if (res) {
1725                         DISPOSE(fn, -1);
1726                         return -1;
1727                 }
1728                 res = ast_waitstream(chan, ecodes);
1729                 if (res) {
1730                         DISPOSE(fn, -1);
1731                         return res;
1732                 }
1733         } else {
1734                 /* Dispose just in case */
1735                 DISPOSE(fn, -1);
1736                 res = ast_streamfile(chan, "vm-theperson", chan->language);
1737                 if (res)
1738                         return -1;
1739                 res = ast_waitstream(chan, ecodes);
1740                 if (res)
1741                         return res;
1742                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1743                 if (res)
1744                         return res;
1745         }
1746         if (busy)
1747                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1748         else
1749                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1750         if (res)
1751                 return -1;
1752         res = ast_waitstream(chan, ecodes);
1753         return res;
1754 }
1755
1756 static void free_user(struct ast_vm_user *vmu)
1757 {
1758         if (ast_test_flag(vmu, VM_ALLOCED))
1759                 free(vmu);
1760 }
1761
1762 static void free_zone(struct vm_zone *z)
1763 {
1764         free(z);
1765 }
1766
1767 static char *mbox(int id)
1768 {
1769         switch(id) {
1770         case 0:
1771                 return "INBOX";
1772         case 1:
1773                 return "Old";
1774         case 2:
1775                 return "Work";
1776         case 3:
1777                 return "Family";
1778         case 4:
1779                 return "Friends";
1780         case 5:
1781                 return "Cust1";
1782         case 6:
1783                 return "Cust2";
1784         case 7:
1785                 return "Cust3";
1786         case 8:
1787                 return "Cust4";
1788         case 9:
1789                 return "Cust5";
1790         default:
1791                 return "Unknown";
1792         }
1793 }
1794
1795 static int has_voicemail(const char *mailbox, const char *folder)
1796 {
1797         DIR *dir;
1798         struct dirent *de;
1799         char fn[256];
1800         char tmp[256]="";
1801         char *mb, *cur;
1802         char *context;
1803         int ret;
1804         if (!folder)
1805                 folder = "INBOX";
1806         /* If no mailbox, return immediately */
1807         if (ast_strlen_zero(mailbox))
1808                 return 0;
1809         if (strchr(mailbox, ',')) {
1810                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
1811                 mb = tmp;
1812                 ret = 0;
1813                 while((cur = strsep(&mb, ","))) {
1814                         if (!ast_strlen_zero(cur)) {
1815                                 if (has_voicemail(cur, folder))
1816                                         return 1; 
1817                         }
1818                 }
1819                 return 0;
1820         }
1821         strncpy(tmp, mailbox, sizeof(tmp) - 1);
1822         context = strchr(tmp, '@');
1823         if (context) {
1824                 *context = '\0';
1825                 context++;
1826         } else
1827                 context = "default";
1828         snprintf(fn, sizeof(fn), "%s/%s/%s/%s", VM_SPOOL_DIR, context, tmp, folder);
1829         dir = opendir(fn);
1830         if (!dir)
1831                 return 0;
1832         while ((de = readdir(dir))) {
1833                 if (!strncasecmp(de->d_name, "msg", 3))
1834                         break;
1835         }
1836         closedir(dir);
1837         if (de)
1838                 return 1;
1839         return 0;
1840 }
1841
1842 static int messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
1843 {
1844         DIR *dir;
1845         struct dirent *de;
1846         char fn[256];
1847         char tmp[256]="";
1848         char *mb, *cur;
1849         char *context;
1850         int ret;
1851         if (newmsgs)
1852                 *newmsgs = 0;
1853         if (oldmsgs)
1854                 *oldmsgs = 0;
1855         /* If no mailbox, return immediately */
1856         if (ast_strlen_zero(mailbox))
1857                 return 0;
1858         if (strchr(mailbox, ',')) {
1859                 int tmpnew, tmpold;
1860                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
1861                 mb = tmp;
1862                 ret = 0;
1863                 while((cur = strsep(&mb, ", "))) {
1864                         if (!ast_strlen_zero(cur)) {
1865                                 if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
1866                                         return -1;
1867                                 else {
1868                                         if (newmsgs)
1869                                                 *newmsgs += tmpnew; 
1870                                         if (oldmsgs)
1871                                                 *oldmsgs += tmpold;
1872                                 }
1873                         }
1874                 }
1875                 return 0;
1876         }
1877         strncpy(tmp, mailbox, sizeof(tmp) - 1);
1878         context = strchr(tmp, '@');
1879         if (context) {
1880                 *context = '\0';
1881                 context++;
1882         } else
1883                 context = "default";
1884         if (newmsgs) {
1885                 snprintf(fn, sizeof(fn), "%s/%s/%s/INBOX", VM_SPOOL_DIR, context, tmp);
1886                 dir = opendir(fn);
1887                 if (dir) {
1888                         while ((de = readdir(dir))) {
1889                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
1890                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
1891                                                 (*newmsgs)++;
1892                                         
1893                         }
1894                         closedir(dir);
1895                 }
1896         }
1897         if (oldmsgs) {
1898                 snprintf(fn, sizeof(fn), "%s/%s/%s/Old", VM_SPOOL_DIR, context, tmp);
1899                 dir = opendir(fn);
1900                 if (dir) {
1901                         while ((de = readdir(dir))) {
1902                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
1903                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
1904                                                 (*oldmsgs)++;
1905                                         
1906                         }
1907                         closedir(dir);
1908                 }
1909         }
1910         return 0;
1911 }
1912
1913 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
1914
1915 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)
1916 {
1917         char fromdir[256], todir[256], frompath[256], topath[256];
1918         char *frombox = mbox(imbox);
1919         int recipmsgnum;
1920
1921         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1922
1923         make_dir(todir, sizeof(todir), recip->context, "", "");
1924         /* It's easier just to try to make it than to check for its existence */
1925         if (mkdir(todir, 0700) && (errno != EEXIST))
1926                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1927         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1928         /* It's easier just to try to make it than to check for its existence */
1929         if (mkdir(todir, 0700) && (errno != EEXIST))
1930                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1931         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1932         if (mkdir(todir, 0700) && (errno != EEXIST))
1933                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1934
1935         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1936         make_file(frompath, sizeof(frompath), fromdir, msgnum);
1937         recipmsgnum = 0;
1938         do {
1939                 make_file(topath, sizeof(topath), todir, recipmsgnum);
1940                 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
1941                         break;
1942                 recipmsgnum++;
1943         } while (recipmsgnum < MAXMSG);
1944         if (recipmsgnum < MAXMSG) {
1945                 COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
1946         } else {
1947                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1948         }
1949
1950         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
1951 }
1952
1953 static void run_externnotify(char *context, char *extension)
1954 {
1955         char arguments[255];
1956         int newvoicemails = 0, oldvoicemails = 0;
1957
1958         if (!ast_strlen_zero(externnotify)) {
1959                 if (messagecount(extension, &newvoicemails, &oldvoicemails)) {
1960                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
1961                 } else {
1962                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
1963                         ast_log(LOG_DEBUG, "Executing %s\n", arguments);
1964                         ast_safe_system(arguments);
1965                 }
1966         }
1967 }
1968
1969
1970 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1971 {
1972         char txtfile[256];
1973         char callerid[256];
1974         FILE *txt;
1975         int res = 0;
1976         int msgnum;
1977         int fd;
1978         int duration = 0;
1979         int ausemacro = 0;
1980         int ousemacro = 0;
1981         char date[256];
1982         char dir[256];
1983         char fn[256];
1984         char prefile[256]="";
1985         char tempfile[256]="";
1986         char ext_context[256] = "";
1987         char fmt[80];
1988         char *context;
1989         char ecodes[16] = "#";
1990         char tmp[256] = "", *tmpptr;
1991         struct ast_vm_user *vmu;
1992         struct ast_vm_user svm;
1993         char *category = NULL;
1994
1995         strncpy(tmp, ext, sizeof(tmp) - 1);
1996         ext = tmp;
1997         context = strchr(tmp, '@');
1998         if (context) {
1999                 *context = '\0';
2000                 context++;
2001                 tmpptr = strchr(context, '&');
2002         } else {
2003                 tmpptr = strchr(ext, '&');
2004         }
2005
2006         if (tmpptr) {
2007                 *tmpptr = '\0';
2008                 tmpptr++;
2009         }
2010
2011         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2012
2013         if ((vmu = find_user(&svm, context, ext))) {
2014                 /* Setup pre-file if appropriate */
2015                 if (strcmp(vmu->context, "default"))
2016                         snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2017                 else
2018                         strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);            
2019                 if (busy)
2020                         snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2021                 else if (unavail)
2022                         snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2023                 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2024                 RETRIEVE(tempfile, -1);
2025                 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2026                         strncpy(prefile, tempfile, sizeof(prefile) - 1);
2027                 DISPOSE(tempfile, -1);
2028                 make_dir(dir, sizeof(dir), vmu->context, "", "");
2029                 /* It's easier just to try to make it than to check for its existence */
2030                 if (mkdir(dir, 0700) && (errno != EEXIST))
2031                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2032                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
2033                 /* It's easier just to try to make it than to check for its existence */
2034                 if (mkdir(dir, 0700) && (errno != EEXIST))
2035                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2036                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
2037                 if (mkdir(dir, 0700) && (errno != EEXIST))
2038                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2039
2040                 /* Check current or macro-calling context for special extensions */
2041                 if (!ast_strlen_zero(vmu->exit)) {
2042                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2043                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2044                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2045                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2046                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2047                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2048                         ousemacro = 1;
2049                 }
2050
2051                 if (!ast_strlen_zero(vmu->exit)) {
2052                         if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2053                                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2054                 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2055                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2056                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2057                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2058                         ausemacro = 1;
2059                 }
2060
2061
2062                 /* Play the beginning intro if desired */
2063                 if (!ast_strlen_zero(prefile)) {
2064                         RETRIEVE(prefile, -1);
2065                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2066                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
2067                                         res = ast_waitstream(chan, ecodes);
2068                         } else {
2069                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2070                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
2071                         }
2072                         DISPOSE(prefile, -1);
2073                         if (res < 0) {
2074                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2075                                 free_user(vmu);
2076                                 return -1;
2077                         }
2078                 }
2079                 if (res == '#') {
2080                         /* On a '#' we skip the instructions */
2081                         silent = 1;
2082                         res = 0;
2083                 }
2084                 if (!res && !silent) {
2085                         res = ast_streamfile(chan, INTRO, chan->language);
2086                         if (!res)
2087                                 res = ast_waitstream(chan, ecodes);
2088                         if (res == '#') {
2089                                 silent = 1;
2090                                 res = 0;
2091                         }
2092                 }
2093                 if (res > 0)
2094                         ast_stopstream(chan);
2095                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2096                 other than the operator -- an automated attendant or mailbox login for example */
2097                 if (res == '*') {
2098                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
2099                         if (!ast_strlen_zero(vmu->exit)) {
2100                                 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2101                         } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2102                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2103                         }
2104                         chan->priority = 0;
2105                         free_user(vmu);
2106                         return 0;
2107                 }
2108                 /* Check for a '0' here */
2109                 if (res == '0') {
2110                         transfer:
2111                         if (ast_test_flag(vmu, VM_OPERATOR)) {
2112                                 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
2113                                 if (!ast_strlen_zero(vmu->exit)) {
2114                                         strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2115                                 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2116                                         strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2117                                 }
2118                                 ast_play_and_wait(chan, "transfer");
2119                                 chan->priority = 0;
2120                                 free_user(vmu);
2121                                 return 0;
2122                         } else {
2123                                 ast_play_and_wait(chan, "vm-sorry");
2124                                 return 0;
2125                         }
2126                 }
2127                 if (res < 0) {
2128                         free_user(vmu);
2129                         return -1;
2130                 }
2131                 /* The meat of recording the message...  All the announcements and beeps have been played*/
2132                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
2133                 if (!ast_strlen_zero(fmt)) {
2134                         msgnum = 0;
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 (res >= 0) {
2142                                 /* Unless we're *really* silent, try to send the beep */
2143                                 res = ast_streamfile(chan, "beep", chan->language);
2144                                 if (!res)
2145                                         res = ast_waitstream(chan, "");
2146                         }
2147                         if (msgnum < MAXMSG) {
2148                                 /* Store information */
2149                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2150                                 txt = fopen(txtfile, "w+");
2151                                 if (txt) {
2152                                         get_date(date, sizeof(date));
2153                                         fprintf(txt, 
2154 ";\n"
2155 "; Message Information file\n"
2156 ";\n"
2157 "[message]\n"
2158 "origmailbox=%s\n"
2159 "context=%s\n"
2160 "macrocontext=%s\n"
2161 "exten=%s\n"
2162 "priority=%d\n"
2163 "callerchan=%s\n"
2164 "callerid=%s\n"
2165 "origdate=%s\n"
2166 "origtime=%ld\n"
2167 "category=%s\n",
2168         ext,
2169         chan->context,
2170         chan->macrocontext, 
2171         chan->exten,
2172         chan->priority,
2173         chan->name,
2174         ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2175         date, (long)time(NULL),
2176         category ? category : "");
2177                                         fclose(txt);
2178                                 } else
2179                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
2180                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
2181                                 if (res == '0')
2182                                         goto transfer;
2183                                 if (res > 0)
2184                                         res = 0;
2185                                 fd = open(txtfile, O_APPEND | O_WRONLY);
2186                                 if (fd > -1) {
2187                                         txt = fdopen(fd, "a");
2188                                         if (txt) {
2189                                                 fprintf(txt, "duration=%d\n", duration);
2190                                                 fclose(txt);
2191                                         } else
2192                                                 close(fd);
2193                                 }
2194                                 if (duration < vmminmessage) {
2195                                         if (option_verbose > 2) 
2196                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2197                                         DELETE(dir,msgnum,fn);
2198                                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2199                                         goto leave_vm_out;
2200                                 }
2201                                 /* Are there to be more recipients of this message? */
2202                                 while (tmpptr) {
2203                                         struct ast_vm_user recipu, *recip;
2204                                         char *exten, *context;
2205
2206                                         exten = strsep(&tmpptr, "&");
2207                                         context = strchr(exten, '@');
2208                                         if (context) {
2209                                                 *context = '\0';
2210                                                 context++;
2211                                         }
2212                                         if ((recip = find_user(&recipu, context, exten))) {
2213                                                 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2214                                                 free_user(recip);
2215                                         }
2216                                 }
2217                                 if (ast_fileexists(fn, NULL, NULL)) {
2218                                         notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2219                                         STORE(dir, msgnum);
2220                                         DISPOSE(dir, msgnum);
2221                                 }
2222                         } else {
2223                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2224                                 if (!res)
2225                                         res = ast_waitstream(chan, "");
2226                                 ast_log(LOG_WARNING, "No more messages possible\n");
2227                         }
2228                 } else
2229                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2230 leave_vm_out:
2231                 free_user(vmu);
2232         } else {
2233                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2234                 /*Send the call to n+101 priority, where n is the current priority*/
2235                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
2236                         chan->priority+=100;
2237         }
2238
2239         return res;
2240 }
2241
2242
2243 static void resequence_mailbox(char * dir)
2244 {
2245         /* we know max messages, so stop process when number is hit */
2246
2247         int x,dest;
2248         char sfn[256];
2249         char dfn[256];
2250
2251         for (x=0,dest=0;x<MAXMSG;x++) {
2252                 make_file(sfn, sizeof(sfn), dir, x);
2253                 if (EXISTS(dir, x, sfn, NULL)) {
2254
2255                         if(x != dest) {
2256                                 make_file(dfn, sizeof(dfn), dir, dest);
2257                                 RENAME(dir, x, dir, dest, sfn, dfn);
2258                         }
2259
2260                         dest++;
2261                 }
2262         }
2263 }
2264
2265
2266 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2267 {
2268         int d;
2269         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2270         return d;
2271 }
2272
2273 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
2274 {
2275         char sfn[256];
2276         char dfn[256];
2277         char ddir[256];
2278         char *dbox = mbox(box);
2279         int x;
2280         make_file(sfn, sizeof(sfn), dir, msg);
2281         make_dir(ddir, sizeof(ddir), context, username, dbox);
2282         mkdir(ddir, 0700);
2283         for (x=0;x<MAXMSG;x++) {
2284                 make_file(dfn, sizeof(dfn), ddir, x);
2285                 if (!EXISTS(ddir, x, dfn, NULL))
2286                         break;
2287         }
2288         if (x >= MAXMSG)
2289                 return -1;
2290         if (strcmp(sfn, dfn)) {
2291                 COPY(dir, msg, ddir, x, sfn, dfn);
2292         }
2293         return 0;
2294 }
2295
2296 static int adsi_logo(unsigned char *buf)
2297 {
2298         int bytes = 0;
2299         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2300         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2301         return bytes;
2302 }
2303
2304 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2305 {
2306         char buf[256];
2307         int bytes=0;
2308         int x;
2309         char num[5];
2310
2311         *useadsi = 0;
2312         bytes += adsi_data_mode(buf + bytes);
2313         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2314
2315         bytes = 0;
2316         bytes += adsi_logo(buf);
2317         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2318 #ifdef DISPLAY
2319         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
2320 #endif
2321         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2322         bytes += adsi_data_mode(buf + bytes);
2323         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2324
2325         if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2326                 bytes = 0;
2327                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2328                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2329                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2330                 bytes += adsi_voice_mode(buf + bytes, 0);
2331                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2332                 return 0;
2333         }
2334
2335 #ifdef DISPLAY
2336         /* Add a dot */
2337         bytes = 0;
2338         bytes += adsi_logo(buf);
2339         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2340         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
2341         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2342         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2343 #endif
2344         bytes = 0;
2345         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2346         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2347         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2348         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2349         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2350         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2351         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2352
2353 #ifdef DISPLAY
2354         /* Add another dot */
2355         bytes = 0;
2356         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
2357         bytes += adsi_voice_mode(buf + bytes, 0);
2358
2359         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2360         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2361 #endif
2362
2363         bytes = 0;
2364         /* These buttons we load but don't use yet */
2365         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2366         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2367         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2368         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2369         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2370         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2371         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2372
2373 #ifdef DISPLAY
2374         /* Add another dot */
2375         bytes = 0;
2376         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
2377         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2378         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2379 #endif
2380
2381         bytes = 0;
2382         for (x=0;x<5;x++) {
2383                 snprintf(num, sizeof(num), "%d", x);
2384                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2385         }
2386         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2387         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2388
2389 #ifdef DISPLAY
2390         /* Add another dot */
2391         bytes = 0;
2392         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
2393         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2394         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2395 #endif
2396
2397         if (adsi_end_download(chan)) {
2398                 bytes = 0;
2399                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2400                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2401                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2402                 bytes += adsi_voice_mode(buf + bytes, 0);
2403                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2404                 return 0;
2405         }
2406         bytes = 0;
2407         bytes += adsi_download_disconnect(buf + bytes);
2408         bytes += adsi_voice_mode(buf + bytes, 0);
2409         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2410
2411         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2412
2413 #ifdef DISPLAY
2414         /* Add last dot */
2415         bytes = 0;
2416         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
2417         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2418 #endif
2419         ast_log(LOG_DEBUG, "Restarting session...\n");
2420
2421         bytes = 0;
2422         /* Load the session now */
2423         if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2424                 *useadsi = 1;
2425                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2426         } else
2427                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2428
2429         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2430         return 0;
2431 }
2432
2433 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2434 {
2435         int x;
2436         if (!adsi_available(chan))
2437                 return;
2438         x = adsi_load_session(chan, adsifdn, adsiver, 1);
2439         if (x < 0)
2440                 return;
2441         if (!x) {
2442                 if (adsi_load_vmail(chan, useadsi)) {
2443                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2444                         return;
2445                 }
2446         } else
2447                 *useadsi = 1;
2448 }
2449
2450 static void adsi_login(struct ast_channel *chan)
2451 {
2452         char buf[256];
2453         int bytes=0;
2454         unsigned char keys[8];
2455         int x;
2456         if (!adsi_available(chan))
2457                 return;
2458
2459         for (x=0;x<8;x++)
2460                 keys[x] = 0;
2461         /* Set one key for next */
2462         keys[3] = ADSI_KEY_APPS + 3;
2463
2464         bytes += adsi_logo(buf + bytes);
2465         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2466         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2467         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2468         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2469         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2470         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2471         bytes += adsi_set_keys(buf + bytes, keys);
2472         bytes += adsi_voice_mode(buf + bytes, 0);
2473         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2474 }
2475
2476 static void adsi_password(struct ast_channel *chan)
2477 {
2478         char buf[256];
2479         int bytes=0;
2480         unsigned char keys[8];
2481         int x;
2482         if (!adsi_available(chan))
2483                 return;
2484
2485         for (x=0;x<8;x++)
2486                 keys[x] = 0;
2487         /* Set one key for next */
2488         keys[3] = ADSI_KEY_APPS + 3;
2489
2490         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2491         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2492         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2493         bytes += adsi_set_keys(buf + bytes, keys);
2494         bytes += adsi_voice_mode(buf + bytes, 0);
2495         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2496 }
2497
2498 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2499 {
2500         char buf[256];
2501         int bytes=0;
2502         unsigned char keys[8];
2503         int x,y;
2504
2505         if (!adsi_available(chan))
2506                 return;
2507
2508         for (x=0;x<5;x++) {
2509                 y = ADSI_KEY_APPS + 12 + start + x;
2510                 if (y > ADSI_KEY_APPS + 12 + 4)
2511                         y = 0;
2512                 keys[x] = ADSI_KEY_SKT | y;
2513         }
2514         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2515         keys[6] = 0;
2516         keys[7] = 0;
2517
2518         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2519         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2520         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2521         bytes += adsi_set_keys(buf + bytes, keys);
2522         bytes += adsi_voice_mode(buf + bytes, 0);
2523
2524         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2525 }
2526
2527 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2528 {
2529         int bytes=0;
2530         char buf[256], buf1[256], buf2[256];
2531         char fn2[256];
2532
2533         char cid[256]="";
2534         char *val;
2535         char *name, *num;
2536         char datetime[21]="";
2537         FILE *f;
2538
2539         unsigned char keys[8];
2540
2541         int x;
2542
2543         if (!adsi_available(chan))
2544                 return;
2545
2546         /* Retrieve important info */
2547         snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2548         f = fopen(fn2, "r");
2549         if (f) {
2550                 while (!feof(f)) {      
2551                         fgets(buf, sizeof(buf), f);
2552                         if (!feof(f)) {
2553                                 char *stringp=NULL;
2554                                 stringp=buf;
2555                                 strsep(&stringp, "=");
2556                                 val = strsep(&stringp, "=");
2557                                 if (val && !ast_strlen_zero(val)) {
2558                                         if (!strcmp(buf, "callerid"))
2559                                                 strncpy(cid, val, sizeof(cid) - 1);
2560                                         if (!strcmp(buf, "origdate"))
2561                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2562                                 }
2563                         }
2564                 }
2565                 fclose(f);
2566         }
2567         /* New meaning for keys */
2568         for (x=0;x<5;x++)
2569                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2570         keys[6] = 0x0;
2571         keys[7] = 0x0;
2572
2573         if (!vms->curmsg) {
2574                 /* No prev key, provide "Folder" instead */
2575                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2576         }
2577         if (vms->curmsg >= vms->lastmsg) {
2578                 /* If last message ... */
2579                 if (vms->curmsg) {
2580                         /* but not only message, provide "Folder" instead */
2581                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2582                         bytes += adsi_voice_mode(buf + bytes, 0);
2583
2584                 } else {
2585                         /* Otherwise if only message, leave blank */
2586                         keys[3] = 1;
2587                 }
2588         }
2589
2590         if (!ast_strlen_zero(cid)) {
2591                 ast_callerid_parse(cid, &name, &num);
2592                 if (!name)
2593                         name = num;
2594         } else
2595                 name = "Unknown Caller";
2596
2597         /* If deleted, show "undeleted" */
2598
2599         if (vms->deleted[vms->curmsg])
2600                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2601
2602         /* Except "Exit" */
2603         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2604         snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
2605                 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
2606         snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
2607
2608         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2609         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2610         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2611         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2612         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2613         bytes += adsi_set_keys(buf + bytes, keys);
2614         bytes += adsi_voice_mode(buf + bytes, 0);
2615
2616         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2617 }
2618
2619 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
2620 {
2621         int bytes=0;
2622         char buf[256];
2623         unsigned char keys[8];
2624
2625         int x;
2626
2627         if (!adsi_available(chan))
2628                 return;
2629
2630         /* New meaning for keys */
2631         for (x=0;x<5;x++)
2632                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2633
2634         keys[6] = 0x0;
2635         keys[7] = 0x0;
2636
2637         if (!vms->curmsg) {
2638                 /* No prev key, provide "Folder" instead */
2639                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2640         }
2641         if (vms->curmsg >= vms->lastmsg) {
2642                 /* If last message ... */
2643                 if (vms->curmsg) {
2644                         /* but not only message, provide "Folder" instead */
2645                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2646                 } else {
2647                         /* Otherwise if only message, leave blank */
2648                         keys[3] = 1;
2649                 }
2650         }
2651
2652         /* If deleted, show "undeleted" */
2653         if (vms->deleted[vms->curmsg]) 
2654                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2655
2656         /* Except "Exit" */
2657         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2658         bytes += adsi_set_keys(buf + bytes, keys);
2659         bytes += adsi_voice_mode(buf + bytes, 0);
2660
2661         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2662 }
2663
2664 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
2665 {
2666         char buf[256] = "", buf1[256] = "", buf2[256] = "";
2667         int bytes=0;
2668         unsigned char keys[8];
2669         int x;
2670
2671         char *newm = (vms->newmessages == 1) ? "message" : "messages";
2672         char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
2673         if (!adsi_available(chan))
2674                 return;
2675         if (vms->newmessages) {
2676                 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
2677                 if (vms->oldmessages) {
2678                         strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
2679                         snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
2680                 } else {
2681                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2682                 }
2683         } else if (vms->oldmessages) {
2684                 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
2685                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2686         } else {
2687                 strncpy(buf1, "You have no messages.", sizeof(buf1) - 1);
2688                 buf2[0] = ' ';
2689                 buf2[1] = '\0';
2690         }
2691         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2692         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2693         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2694
2695         for (x=0;x<6;x++)
2696                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2697         keys[6] = 0;
2698         keys[7] = 0;
2699
2700         /* Don't let them listen if there are none */
2701         if (vms->lastmsg < 0)
2702                 keys[0] = 1;
2703         bytes += adsi_set_keys(buf + bytes, keys);
2704
2705         bytes += adsi_voice_mode(buf + bytes, 0);
2706
2707         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2708 }
2709
2710 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
2711 {
2712         char buf[256] = "", buf1[256] = "", buf2[256] = "";
2713         int bytes=0;
2714         unsigned char keys[8];
2715         int x;
2716
2717         char *mess = (vms->lastmsg == 0) ? "message" : "messages";
2718
2719         if (!adsi_available(chan))
2720                 return;
2721
2722         /* Original command keys */
2723         for (x=0;x<6;x++)
2724                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2725
2726         keys[6] = 0;
2727         keys[7] = 0;
2728
2729         if ((vms->lastmsg + 1) < 1)
2730                 keys[0] = 0;
2731
2732         snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
2733                 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
2734
2735         if (vms->lastmsg + 1)
2736                 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
2737         else
2738                 strncpy(buf2, "no messages.", sizeof(buf2) - 1);
2739         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2740         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2741         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2742         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2743         bytes += adsi_set_keys(buf + bytes, keys);
2744
2745         bytes += adsi_voice_mode(buf + bytes, 0);
2746
2747         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2748         
2749 }
2750
2751 /*
2752 static void adsi_clear(struct ast_channel *chan)
2753 {
2754         char buf[256];
2755         int bytes=0;
2756         if (!adsi_available(chan))
2757                 return;
2758         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2759         bytes += adsi_voice_mode(buf + bytes, 0);
2760
2761         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2762 }
2763 */
2764
2765 static void adsi_goodbye(struct ast_channel *chan)
2766 {
2767         char buf[256];
2768         int bytes=0;
2769
2770         if (!adsi_available(chan))
2771                 return;
2772         bytes += adsi_logo(buf + bytes);
2773         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2774         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2775         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2776         bytes += adsi_voice_mode(buf + bytes, 0);
2777
2778         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2779 }
2780
2781 /*--- get_folder: Folder menu ---*/
2782 /* Plays "press 1 for INBOX messages" etc
2783    Should possibly be internationalized
2784  */
2785 static int get_folder(struct ast_channel *chan, int start)
2786 {
2787         int x;
2788         int d;
2789         char fn[256];
2790         d = ast_play_and_wait(chan, "vm-press");        /* "Press" */
2791         if (d)
2792                 return d;
2793         for (x = start; x< 5; x++) {    /* For all folders */
2794                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2795                         return d;
2796                 d = ast_play_and_wait(chan, "vm-for");  /* "for" */
2797                 if (d)
2798                         return d;
2799                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));     /* Folder name */
2800                 d = vm_play_folder_name(chan, fn);
2801                 if (d)
2802                         return d;
2803                 d = ast_waitfordigit(chan, 500);
2804                 if (d)
2805                         return d;
2806         }
2807         d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2808         if (d)
2809                 return d;
2810         d = ast_waitfordigit(chan, 4000);
2811         return d;
2812 }
2813
2814 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2815 {
2816         int res = 0;
2817         res = ast_play_and_wait(chan, fn);      /* Folder name */
2818         while (((res < '0') || (res > '9')) &&
2819                         (res != '#') && (res >= 0)) {
2820                 res = get_folder(chan, 0);
2821         }
2822         return res;
2823 }
2824
2825 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2826 {
2827         int cmd = 0;
2828         int retries = 0;
2829         int duration = 0;
2830
2831         while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2832                 if (cmd)
2833                         retries = 0;
2834                 switch (cmd) {
2835                 case '1': 
2836                         /* prepend a message to the current message and return */
2837                 {
2838                         char file[200];
2839                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2840                         cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
2841                         break;
2842                 }
2843                 case '2': 
2844                         cmd = 't';
2845                         break;
2846                 case '*':
2847                         cmd = '*';
2848                         break;
2849                 default: 
2850                         cmd = ast_play_and_wait(chan,"vm-forwardoptions");
2851                                 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2852                         if (!cmd)
2853                                 cmd = ast_play_and_wait(chan,"vm-starmain");
2854                                 /* "press star to return to the main menu" */
2855                         if (!cmd)
2856                                 cmd = ast_waitfordigit(chan,6000);
2857                         if (!cmd)
2858                                 retries++;
2859                         if (retries > 3)
2860                                 cmd = 't';
2861                  }
2862         }
2863         if (cmd == 't')
2864                 cmd = 0;
2865         return cmd;
2866 }
2867
2868 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
2869 {
2870         char todir[256], fn[256], ext_context[256], *stringp;
2871         int newmsgs = 0, oldmsgs = 0;
2872
2873         make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2874         make_file(fn, sizeof(fn), todir, msgnum);
2875         snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2876
2877         /* Attach only the first format */
2878         fmt = ast_strdupa(fmt);
2879         if (fmt) {
2880                 stringp = fmt;
2881                 strsep(&stringp, "|");
2882
2883                 if (!ast_strlen_zero(vmu->email)) {
2884                         int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
2885                         char *myserveremail = serveremail;
2886                         attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
2887                         if (!ast_strlen_zero(vmu->serveremail))
2888                                 myserveremail = vmu->serveremail;
2889                         sendmail(myserveremail, vmu, msgnum, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
2890                 }
2891
2892                 if (!ast_strlen_zero(vmu->pager)) {
2893                         char *myserveremail = serveremail;
2894                         if (!ast_strlen_zero(vmu->serveremail))
2895                                 myserveremail = vmu->serveremail;
2896                         sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, cidnum, cidname, duration, vmu);
2897                 }
2898         } else {
2899                 ast_log(LOG_ERROR, "Out of memory\n");
2900         }
2901
2902         if (ast_test_flag(vmu, VM_DELETE)) {
2903                 DELETE(todir, msgnum, fn);
2904         }
2905
2906         /* Leave voicemail for someone */
2907         if (ast_app_has_voicemail(ext_context, NULL)) {
2908                 ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
2909         }
2910         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);
2911         run_externnotify(chan->context, ext_context);
2912         return 0;
2913 }
2914
2915 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt,int flag)
2916 {
2917         char username[70]="";
2918         char sys[256];
2919         char todir[256];
2920         int todircount=0;
2921         int duration;
2922         struct ast_config *mif;
2923         char miffile[256];
2924         char fn[256];
2925         char callerid[512];
2926         char ext_context[256]="";
2927         int res = 0, cmd = 0;
2928         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2929         char tmp[256];
2930         char *stringp, *s;
2931         int saved_messages = 0, found = 0;
2932         int valid_extensions = 0;
2933         
2934         while (!res && !valid_extensions) {
2935                 
2936                 int use_directory = 0;
2937                 if(ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
2938                         int done = 0;
2939                         int retries = 0;
2940                         cmd=0;
2941                         while((cmd >= 0) && !done ){
2942                                 if (cmd)
2943                                         retries = 0;
2944                                 switch (cmd) {
2945                                 case '1': 
2946                                         use_directory = 0;
2947                                         done = 1;
2948                                         break;
2949                                 case '2': 
2950                                         use_directory = 1;
2951                                         done=1;
2952                                         break;
2953                                 case '*': 
2954                                         cmd = 't';
2955                                         done = 1;
2956                                         break;
2957                                 default: 
2958                                         /* Press 1 to enter an extension press 2 to use the directory */
2959                                         cmd = ast_play_and_wait(chan,"vm-forward");
2960                                         if (!cmd)
2961                                                 cmd = ast_waitfordigit(chan,3000);
2962                                         if (!cmd)
2963                                                 retries++;
2964                                         if (retries > 3)
2965                                         {
2966                                                 cmd = 't';
2967                                                 done = 1;
2968                                         }
2969                                         
2970                                  }
2971                         }
2972                         if( cmd<0 || cmd=='t' )
2973                                 break;
2974                 }
2975                 
2976                 if( use_directory ) {
2977                         /* use app_directory */
2978                         
2979                         char old_context[sizeof(chan->context)];
2980                         char old_exten[sizeof(chan->exten)];
2981                         int old_priority;
2982                         struct ast_app* app;
2983
2984                         
2985                         app = pbx_findapp("Directory");
2986                         if (app) {
2987                                 /* make mackup copies */
2988                                 memcpy(old_context, chan->context, sizeof(chan->context));
2989                                 memcpy(old_exten, chan->exten, sizeof(chan->exten));
2990                                 old_priority = chan->priority;
2991                                 
2992                                 /* call the the Directory, changes the channel */
2993                                 res = pbx_exec(chan, app, ((context)?context:chan->context), 1);
2994                                 
2995                                 strncpy(username, chan->exten, sizeof(username)-1);
2996                                 
2997                                 /* restore the old context, exten, and priority */
2998                                 memcpy(chan->context, old_context, sizeof(chan->context));
2999                                 memcpy(chan->exten, old_exten, sizeof(chan->exten));
3000                                 chan->priority = old_priority;
3001                                 
3002                         } else {
3003                                 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
3004                                 ast_clear_flag((&globalflags), VM_DIRECFORWARD);        
3005                         }
3006                 } else  {
3007                         /* Ask for an extension */
3008                         res = ast_streamfile(chan, "vm-extension", chan->language);     /* "extension" */
3009                         if (res)
3010                                 break;
3011                         if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
3012                                 break;
3013                 }
3014                 
3015                 /* start all over if no username */
3016                 if (ast_strlen_zero(username))
3017                         continue;
3018                 stringp = username;
3019                 s = strsep(&stringp, "*");
3020                 /* start optimistic */
3021                 valid_extensions = 1;
3022                 while (s) {
3023                         /* find_user is going to malloc since we have a NULL as first argument */
3024                         if ((receiver = find_user(NULL, context, s))) {
3025                                 if (!extensions)
3026                                         vmtmp = extensions = receiver;
3027                                 else {
3028                                         vmtmp->next = receiver;
3029                                         vmtmp = receiver;
3030                                 }
3031                                 found++;
3032                         } else {
3033                                 valid_extensions = 0;
3034                                 break;
3035                         }
3036                         s = strsep(&stringp, "*");
3037                 }
3038                 /* break from the loop of reading the extensions */
3039                 if (valid_extensions)
3040                         break;
3041                 /* "I am sorry, that's not a valid extension.  Please try again." */
3042                 res = ast_play_and_wait(chan, "pbx-invalid");
3043         }
3044         /* check if we're clear to proceed */
3045         if (!extensions || !valid_extensions)
3046                 return res;
3047         vmtmp = extensions;
3048         if (flag==1) {
3049                 /* Send VoiceMail */
3050                 cmd=leave_voicemail(chan,username,0,0,0);
3051         } else {
3052                 /* Forward VoiceMail */
3053                 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
3054                 if (!cmd) {
3055                         while (!res && vmtmp) {
3056                                 /* if (ast_play_and_wait(chan, "vm-savedto"))
3057                                         break;
3058                                 */
3059                                 snprintf(todir, sizeof(todir), "%s%s/%s/INBOX",  VM_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
3060                                 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
3061                                 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
3062                                 ast_log(LOG_DEBUG, "%s", sys);
3063                                 ast_safe_system(sys);
3064                 
3065                                 todircount = count_messages(todir);
3066                                 strncpy(tmp, fmt, sizeof(tmp) - 1);
3067                                 stringp = tmp;
3068                                 while ((s = strsep(&stringp, "|"))) {
3069                                         /* XXX This is a hack -- we should use build_filename or similar XXX */
3070                                         if (!strcasecmp(s, "wav49"))
3071                                                 s = "WAV";
3072                                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
3073                                         ast_log(LOG_DEBUG, "%s", sys);
3074                                         ast_safe_system(sys);
3075                                 }
3076                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
3077                                 ast_log(LOG_DEBUG, "%s", sys);
3078                                 ast_safe_system(sys);
3079                                 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
3080         
3081                                 /* load the information on the source message so we can send an e-mail like a new message */
3082                                 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
3083                                 if ((mif=ast_load(miffile))) {
3084         
3085                                         /* set callerid and duration variables */
3086                                         snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
3087                                         s = ast_variable_retrieve(mif, NULL, "duration");
3088                                         if (s)
3089                                                 duration = atoi(s);
3090                                         else
3091                                                 duration = 0;
3092                                         if (!ast_strlen_zero(vmtmp->email)) {
3093                                                 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
3094                                                 char *myserveremail = serveremail;
3095                                                 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
3096                                                 if (!ast_strlen_zero(vmtmp->serveremail))
3097                                                         myserveremail = vmtmp->serveremail;
3098                                                 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, tmp, duration, attach_user_voicemail);
3099                                         }
3100
3101                                         if (!ast_strlen_zero(vmtmp->pager)) {
3102                                                 char *myserveremail = serveremail;
3103                                                 if (!ast_strlen_zero(vmtmp->serveremail))
3104                                                         myserveremail = vmtmp->serveremail;
3105                                                 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, duration, vmtmp);
3106                                         }
3107                                   
3108                                         ast_destroy(mif); /* or here */
3109                                 }
3110                                 /* Leave voicemail for someone */
3111                                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
3112                                 run_externnotify(chan->context, ext_context);
3113         
3114                                 saved_messages++;
3115                                 vmfree = vmtmp;
3116                                 vmtmp = vmtmp->next;
3117                                 free_user(vmfree);
3118                         }
3119                         if (saved_messages > 0) {
3120                                 /* give confirmation that the message was saved */
3121                                 /* commented out since we can't forward batches yet
3122                                 if (saved_messages == 1)
3123                                         res = ast_play_and_wait(chan, "vm-message");
3124                                 else
3125                                         res = ast_play_and_wait(chan, "vm-messages");
3126                                 if (!res)
3127                                         res = ast_play_and_wait(chan, "vm-saved"); */
3128                                 if (!res)
3129                                         res = ast_play_and_wait(chan, "vm-msgsaved");
3130                         }       
3131                 }
3132         }
3133         return res ? res : cmd;
3134 }
3135
3136 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
3137 {
3138         int res;
3139         if ((res = ast_streamfile(chan, file, chan->language))) 
3140                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
3141         if (!res)
3142                 res = ast_waitstream(chan, AST_DIGIT_ANY);
3143         return res;
3144 }
3145
3146 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
3147 {
3148         return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
3149 }
3150
3151 static int play_message_category(struct ast_channel *chan, char *category)
3152 {
3153         int res = 0;
3154
3155         if (category && !ast_strlen_zero(category))
3156                 res = ast_play_and_wait(chan, category);
3157
3158         return res;
3159 }
3160
3161 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
3162 {
3163         int res = 0;
3164         struct vm_zone *the_zone = NULL;
3165         time_t t;
3166         long tin;
3167
3168         if (sscanf(origtime,"%ld",&tin) < 1) {
3169                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
3170                 return 0;
3171         }
3172         t = tin;
3173
3174         /* Does this user have a timezone specified? */
3175         if (!ast_strlen_zero(vmu->zonetag)) {
3176                 /* Find the zone in the list */
3177                 struct vm_zone *z;
3178                 z = zones;
3179                 while (z) {
3180                         if (!strcmp(z->name, vmu->zonetag)) {
3181                                 the_zone = z;
3182                                 break;
3183                         }
3184                         z = z->next;
3185                 }
3186         }
3187
3188 /* No internal variable parsing for now, so we'll comment it out for the time being */
3189 #if 0
3190         /* Set the DIFF_* variables */
3191         localtime_r(&t, &time_now);
3192         gettimeofday(&tv_now,NULL);
3193         tnow = tv_now.tv_sec;
3194         localtime_r(&tnow,&time_then);
3195
3196         /* Day difference */
3197         if (time_now.tm_year == time_then.tm_year)
3198                 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
3199         else
3200                 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
3201         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
3202
3203         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
3204 #endif
3205         if (the_zone)
3206                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
3207         else if(!strcasecmp(chan->language,"de"))       /* GERMAN syntax */
3208                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
3209         else if (!strcasecmp(chan->language,"nl"))      /* DUTCH syntax */
3210                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
3211         else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
3212                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
3213         else if (!strcasecmp(chan->language,"gr"))
3214                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
3215         else
3216                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
3217 #if 0
3218         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
3219 #endif
3220         return res;
3221 }
3222
3223
3224
3225 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
3226 {
3227         int res = 0;
3228         int i;
3229         char *callerid, *name;
3230         char prefile[256]="";
3231         
3232
3233         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
3234         /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
3235         if ((cid == NULL)||(context == NULL))
3236                 return res;
3237
3238         /* Strip off caller ID number from name */
3239         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
3240         ast_callerid_parse(cid, &name, &callerid);
3241         if ((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
3242                 /* Check for internal contexts and only */
3243                 /* say extension when the call didn't come from an internal context in the list */
3244                 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
3245                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
3246                         if ((strcmp(cidinternalcontexts[i], context) == 0))
3247                                 break;
3248                 }
3249                 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
3250                         if (!res) {
3251                                 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
3252                                 if (!ast_strlen_zero(prefile)) {
3253                                 /* See if we can find a recorded name for this person instead of their extension number */
3254                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
3255                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
3256                                                 if (!callback)
3257                                                         res = wait_file2(chan, vms, "vm-from");
3258                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
3259                                                 res = ast_waitstream(chan, "");
3260                                         } else {
3261                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
3262                                                 /* BB: Say "from extension" as one saying to sound smoother */
3263                                                 if (!callback)
3264                                                         res = wait_file2(chan, vms, "vm-from-extension");
3265                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
3266                                         }
3267                                 }
3268                         }
3269                 }
3270
3271                 else if (!res){
3272                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
3273                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
3274                         if (!callback)
3275                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
3276                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
3277                 }
3278         } else {
3279                 /* Number unknown */
3280                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
3281                 if (!res)
3282                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
3283                         res = wait_file2(chan, vms, "vm-unknown-caller");
3284         }
3285         return res;
3286 }
3287
3288 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
3289 {
3290         int res = 0;
3291         int durationm;
3292         int durations;
3293         /* Verify that we have a duration for the message */
3294         if((duration == NULL))
3295                 return res;
3296
3297         /* Convert from seconds to minutes */
3298         durations=atoi(duration);
3299         durationm=(durations / 60);
3300
3301         ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
3302
3303         if((!res)&&(durationm>=minduration)) {
3304                 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
3305                 res = wait_file2(chan, vms, "vm-minutes");
3306         }
3307         return res;
3308 }
3309
3310 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
3311 {
3312         int res = 0;
3313         char filename[256],*origtime, *cid, *context, *duration;
3314         char *category;
3315         struct ast_config *msg_cfg;
3316
3317         vms->starting = 0; 
3318         make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
3319         adsi_message(chan, vms);
3320         if (!vms->curmsg)
3321                 res = wait_file2(chan, vms, "vm-first");        /* "First" */
3322         else if (vms->curmsg == vms->lastmsg)
3323                 res = wait_file2(chan, vms, "vm-last");         /* "last" */
3324         if (!res) {
3325                 res = wait_file2(chan, vms, "vm-message");      /* "message" */
3326                 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
3327                         if (!res)
3328                                 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
3329                 }
3330         }
3331
3332         /* Retrieve info from VM attribute file */
3333         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
3334         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
3335         RETRIEVE(vms->curdir, vms->curmsg);
3336         msg_cfg = ast_load(filename);
3337         if (!msg_cfg) {
3338                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
3339                 return 0;
3340         }
3341                                                                                                                                                                                                         
3342         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
3343                 ast_log(LOG_WARNING, "No origtime?!\n");
3344                 DISPOSE(vms->curdir, vms->curmsg);
3345                 return 0;
3346         }
3347
3348         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
3349         duration = ast_variable_retrieve(msg_cfg, "message", "duration");
3350         category = ast_variable_retrieve(msg_cfg, "message", "category");
3351
3352         context = ast_variable_retrieve(msg_cfg, "message", "context");
3353         if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
3354                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
3355
3356         if (!res)
3357                 res = play_message_category(chan, category);
3358         if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
3359                 res = play_message_datetime(chan, vmu, origtime, filename);
3360         if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
3361                 res = play_message_callerid(chan, vms, cid, context, 0);
3362         if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
3363                 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
3364         /* Allow pressing '1' to skip envelope / callerid */
3365         if (res == '1')
3366                 res = 0;
3367         ast_destroy(msg_cfg);
3368
3369         if (!res) {
3370                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
3371                 vms->heard[vms->curmsg] = 1;
3372                 printf("yay!\n");
3373                 res = wait_file(chan, vms, vms->fn);
3374         }
3375         DISPOSE(vms->curdir, vms->curmsg);