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