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