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