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