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