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