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