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