tmix.c - mixmaster - mixmaster 3.0 patched for libressl
HTML git clone git://parazyd.org/mixmaster.git
DIR Log
DIR Files
DIR Refs
DIR README
---
tmix.c (33886B)
---
1 /* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others.
2
3 Mixmaster may be redistributed and modified under certain conditions.
4 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
5 ANY KIND, either express or implied. See the file COPYRIGHT for
6 details.
7
8 Mixmaster initialization, configuration
9 $Id: mix.c 962 2007-11-19 13:42:41Z zax $ */
10
11
12 #include "mix3.h"
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdarg.h>
17 #include <ctype.h>
18 #include <time.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #ifdef POSIX
22 #include <signal.h>
23 #include <unistd.h>
24 #include <pwd.h>
25 #include <sys/utsname.h>
26 #else /* end of POSIX */
27 #include <io.h>
28 #include <direct.h>
29 #endif /* else if not POSIX */
30 #ifdef WIN32
31 #include <windows.h>
32 #include <shlobj.h>
33 #include <shlobj.h>
34 #endif /* WIN32 */
35 #include <assert.h>
36 #include "menu.h"
37
38 int buf_vappendf(BUFFER *b, char *fmt, va_list args);
39
40 /** filenames ************************************************************/
41 char MIXCONF[PATHMAX] = DEFAULT_MIXCONF;
42 char DISCLAIMFILE[PATHMAX];
43 char FROMDSCLFILE[PATHMAX];
44 char MSGFOOTERFILE[PATHMAX];
45 char POP3CONF[PATHMAX];
46 char HELPFILE[PATHMAX];
47 char REQUESTDIR[PATHMAX];
48 char ABUSEFILE[PATHMAX];
49 char REPLYFILE[PATHMAX];
50 char USAGEFILE[PATHMAX];
51 char USAGELOG[PATHMAX];
52 char BLOCKFILE[PATHMAX];
53 char ADMKEYFILE[PATHMAX];
54 char KEYFILE[PATHMAX];
55 char PGPKEY[PATHMAX];
56 char DSAPARAMS[PATHMAX];
57 char DHPARAMS[PATHMAX];
58 char MIXRAND[PATHMAX];
59 char SECRING[PATHMAX];
60 char PUBRING[PATHMAX];
61 char IDLOG[PATHMAX];
62 char STATS[PATHMAX];
63 char PGPMAXCOUNT[PATHMAX];
64 char DESTBLOCK[PATHMAX];
65 char DESTALLOW[PATHMAX];
66 char DESTALLOW2[PATHMAX];
67 char SOURCEBLOCK[PATHMAX];
68 char HDRFILTER[PATHMAX];
69 char REGULAR[PATHMAX];
70 char POOL[PATHMAX];
71 char TYPE1LIST[PATHMAX];
72 char TYPE2REL[PATHMAX];
73 char PIDFILE[PATHMAX];
74
75 char PGPREMPUBRING[PATHMAX];
76 char PGPREMPUBASC[PATHMAX];
77 char PGPREMSECRING[PATHMAX];
78 char NYMSECRING[PATHMAX];
79 char NYMDB[PATHMAX];
80 char STAREX[PATHMAX];
81
82 /** config ***************************************************************/
83
84 char MIXDIR[PATHMAX];
85 char POOLDIR[PATHMAX];
86
87 /* programs */
88 char SENDMAIL[LINELEN];
89 char SENDANONMAIL[LINELEN];
90 char NEWS[LINELEN];
91 char TYPE1[LINELEN];
92
93 /* addresses */
94 char MAILtoNEWS[LINELEN];
95 char REMAILERNAME[LINELEN];
96 char ANONNAME[LINELEN];
97 char REMAILERADDR[LINELEN];
98 char ANONADDR[LINELEN];
99 char COMPLAINTS[LINELEN];
100 int AUTOREPLY;
101 char SMTPRELAY[LINELEN];
102 char SMTPUSERNAME[LINELEN];
103 char SMTPPASSWORD[LINELEN];
104
105 #ifdef USE_SOCK
106 char HELONAME[LINELEN];
107 char ENVFROM[LINELEN];
108 int POP3DEL;
109 int POP3SIZELIMIT;
110 long POP3TIME;
111
112 #endif /* USE_SOCK */
113
114 char SHORTNAME[LINELEN];
115 char ALLPINGERSURL[BUFSIZE];
116 char ALLPINGERSFILE[PATHMAX];
117 char WGET[PATHMAX];
118 char STATSSRC[PATHMAX];
119 int STATSAUTOUPDATE;
120 long STATSINTERVAL;
121
122
123 /* remailer configuration */
124 int REMAIL;
125 int MIX;
126 int PGP;
127 int UNENCRYPTED;
128 int REMIX;
129 int REPGP;
130 char EXTFLAGS[LINELEN]; /* user-defined capstring flags */
131
132 char PRECEDENCE[LINELEN]; /* default Precedence: header for outgoing mail */
133 int POOLSIZE;
134 int RATE;
135 int INDUMMYP;
136 int OUTDUMMYP;
137 int INDUMMYMAXP;
138 int OUTDUMMYMAXP;
139 int MIDDLEMAN;
140 int AUTOBLOCK;
141 int STATSDETAILS;
142 char FORWARDTO[LINELEN];
143 int SIZELIMIT; /* maximal size of remailed messages */
144 int INFLATEMAX; /* maximal size of Inflate: padding */
145 int MAXRANDHOPS;
146 int BINFILTER; /* filter binary attachments? */
147 int LISTSUPPORTED; /* list supported remailers in remailer-conf reply? */
148 long PACKETEXP; /* Expiration time for old packets */
149 long IDEXP; /* 0 = no ID log !! */
150 long SENDPOOLTIME; /* frequency for sending pool messages */
151 long MAILINTIME; /* frequency for processing MAILIN mail */
152
153 long KEYLIFETIME;
154 long KEYOVERLAPPERIOD;
155 long KEYGRACEPERIOD;
156
157 char ERRLOG[LINELEN];
158 char ADDRESS[LINELEN];
159 char NAME[LINELEN];
160
161 char ORGANIZATION[LINELEN];
162 char MID[LINELEN];
163
164 /* client config */
165 int NUMCOPIES;
166 char CHAIN[LINELEN];
167 int VERBOSE;
168 int DISTANCE;
169 int MINREL;
170 int RELFINAL;
171 long MAXLAT;
172 long MINLAT;
173 char PGPPUBRING[PATHMAX];
174 char PGPSECRING[PATHMAX];
175 char PASSPHRASE[LINELEN];
176 char MAILIN[PATHMAX];
177 char MAILBOX[PATHMAX];
178 char MAILABUSE[PATHMAX];
179 char MAILBLOCK[PATHMAX];
180 char MAILUSAGE[PATHMAX];
181 char MAILANON[PATHMAX];
182 char MAILERROR[PATHMAX];
183 char MAILBOUNCE[PATHMAX];
184
185 int CLIENTAUTOFLUSH;
186 int MAXRECIPIENTS;
187
188 long TIMESKEW_FORWARD;
189 long TIMESKEW_BACK;
190 int TEMP_FAIL;
191
192 char ENTEREDPASSPHRASE[LINELEN] = "";
193
194 static int rereadconfig = 0;
195 static int terminatedaemon = 0;
196
197 #if defined(S_IFDIR) && !defined(S_ISDIR)
198 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
199 #endif /* defined(S_IFDIR) && !defined(S_ISDIR) */
200
201 static int mixdir(char *d, int create)
202 {
203 int err;
204 struct stat buf;
205
206 if (d != MIXDIR)
207 strncpy(MIXDIR, d, PATHMAX);
208 if (MIXDIR[strlen(MIXDIR) - 1] == DIRSEP)
209 MIXDIR[strlen(MIXDIR) - 1] = '\0';
210 err = stat(MIXDIR, &buf);
211 if (err == -1) {
212 if (create) {
213 #ifndef POSIX
214 err = mkdir(MIXDIR);
215 #else /* end of not POSIX */
216 err = mkdir(MIXDIR, S_IRWXU);
217 #endif /* else if POSIX */
218 if (err == 0)
219 errlog(NOTICE, "Creating directory %s.\n", MIXDIR);
220 } else
221 err = 1;
222 } else if (!S_ISDIR(buf.st_mode))
223 err = -1;
224 if (err == 0)
225 strcatn(MIXDIR, DIRSEPSTR, PATHMAX);
226 return (err);
227 }
228
229 void whoami(char *addr, char *defaultname)
230 {
231 char *p = NULL;
232
233 #if defined(HAVE_GETDOMAINNAME) || (defined(HAVE_GETHOSTNAME) && ! defined(HAVE_UNAME))
234 char line[LINELEN];
235
236 #endif /* defined(HAVE_GETDOMAINNAME) || [...] */
237 #ifdef HAVE_UNAME
238 struct utsname uts;
239
240 #endif /* HAVE_UNAME */
241 #ifdef POSIX
242 p = getlogin();
243 #endif /* POSIX */
244 if (p == NULL)
245 strcpy(addr, defaultname);
246 else
247 strncpy(addr, p, LINELEN);
248
249 strcatn(addr, "@", LINELEN);
250 #ifdef HAVE_UNAME
251 if (uname(&uts) != -1)
252 strcatn(addr, uts.nodename, LINELEN);
253 #elif defined(HAVE_GETHOSTNAME) /* end of HAVE_UNAME */
254 if (gethostname(line, LINELEN) == 0)
255 strcatn(addr, line, LINELEN);
256 #endif /* defined(HAVE_GETHOSTNAME) */
257 if (addr[strlen(addr) - 1] == '@')
258 strcatn(addr, SHORTNAME, LINELEN);
259
260 if (strchr(strchr(addr, '@'), '.') == NULL) {
261 #ifdef HAVE_GETDOMAINNAME
262 if (getdomainname(line, LINELEN) == 0 && !streq(line, "(none)")) {
263 strcatn(addr, ".", LINELEN);
264 strcatn(addr, line, LINELEN);
265 }
266 #endif /* HAVE_GETDOMAINNAME */
267 }
268 }
269
270 #define read_conf(t) readconfline(line, #t, sizeof(#t)-1, t)
271
272 static int readconfline(char *line, char *name, int namelen, char *var)
273 {
274 if (strncmp(line, name, namelen) == 0 &&
275 (isspace(line[namelen]) || line[namelen] == '=')) {
276 line += namelen;
277 if (*line == '=')
278 line++;
279 while (isspace(*line))
280 line++;
281 if (line[0] == '\n' || line[0] == '\0') /* leave default */
282 return (1);
283 strncpy(var, line, LINELEN);
284 if (var[strlen(var) - 1] == '\n')
285 var[strlen(var) - 1] = '\0';
286 return (1);
287 } else
288 return (0);
289 }
290
291 #define read_conf_i(t) readiconfline(line, #t, sizeof(#t)-1, &t)
292
293 static int readiconfline(char *line, char *name, int namelen, int *var)
294 {
295 if (strncmp(line, name, namelen) == 0 &&
296 (isspace(line[namelen]) || line[namelen] == '=')) {
297 line += namelen;
298 if (*line == '=')
299 line++;
300 while (isspace(*line))
301 line++;
302 if (line[0] == '\n' || line[0] == '\0') /* leave default */
303 return (1);
304 switch (tolower(line[0])) {
305 case 'n':
306 *var = 0;
307 break;
308 case 'y':
309 *var = 1;
310 break;
311 case 'x':
312 *var = 2;
313 break;
314 default:
315 sscanf(line, "%d", var);
316 }
317 return (1);
318 } else
319 return (0);
320 }
321
322 #define read_conf_t(t) readtconfline(line, #t, sizeof(#t)-1, &t)
323
324 static int readtconfline(char *line, char *name, int namelen, long *var)
325 {
326 char *linenext;
327 int mod = 0;
328 long l = 0;
329 long n;
330
331 if (strncmp(line, name, namelen) == 0 &&
332 (isspace(line[namelen]) || line[namelen] == '=')) {
333 line += namelen;
334 if (*line == '=')
335 line++;
336 for (;; line++) {
337 n = strtol(line, &linenext, 10);
338 if (linenext == line)
339 break;
340 line = linenext;
341 mod = 1;
342 assert(line != NULL);
343 while (isspace(*line))
344 line++;
345 switch (tolower(*line)) {
346 case 'y': /* years */
347 l += 365 * 24 * 60 * 60 * n;
348 break;
349 case 'b': /* months */
350 l += 30 * 24 * 60 * 60 * n;
351 break;
352 case 'w': /* weeks */
353 l += 7 * 24 * 60 * 60 * n;
354 break;
355 case 'd': /* days */
356 l += 24 * 60 * 60 * n;
357 break;
358 case 's': /* seconds */
359 l += n;
360 break;
361 case 'm': /* minutes */
362 l += 60 * n;
363 break;
364 case 'h': /* hours - default */
365 default:
366 l += 60 * 60 * n;
367 break;
368 }
369 }
370 if (mod)
371 *var = l;
372 return (1);
373 } else
374 return (0);
375 }
376
377 static void mix_setdefaults()
378 {
379 #define strnncpy(a,b) strncpy(a, b, sizeof(a)); a[sizeof(a)-1] = '\0'
380
381 strnncpy(DISCLAIMFILE , DEFAULT_DISCLAIMFILE);
382 strnncpy(FROMDSCLFILE , DEFAULT_FROMDSCLFILE);
383 strnncpy(MSGFOOTERFILE, DEFAULT_MSGFOOTERFILE);
384 strnncpy(POP3CONF , DEFAULT_POP3CONF);
385 strnncpy(HELPFILE , DEFAULT_HELPFILE);
386 strnncpy(REQUESTDIR , DEFAULT_REQUESTDIR);
387 strnncpy(ABUSEFILE , DEFAULT_ABUSEFILE);
388 strnncpy(REPLYFILE , DEFAULT_REPLYFILE);
389 strnncpy(USAGEFILE , DEFAULT_USAGEFILE);
390 strnncpy(USAGELOG , DEFAULT_USAGELOG);
391 strnncpy(BLOCKFILE , DEFAULT_BLOCKFILE);
392 strnncpy(ADMKEYFILE , DEFAULT_ADMKEYFILE);
393 strnncpy(KEYFILE , DEFAULT_KEYFILE);
394 strnncpy(PGPKEY , DEFAULT_PGPKEY);
395 strnncpy(DSAPARAMS , DEFAULT_DSAPARAMS);
396 strnncpy(DHPARAMS , DEFAULT_DHPARAMS);
397 strnncpy(MIXRAND , DEFAULT_MIXRAND);
398 strnncpy(SECRING , DEFAULT_SECRING);
399 strnncpy(PUBRING , DEFAULT_PUBRING);
400 strnncpy(IDLOG , DEFAULT_IDLOG);
401 strnncpy(STATS , DEFAULT_STATS);
402 strnncpy(PGPMAXCOUNT , DEFAULT_PGPMAXCOUNT);
403 strnncpy(DESTBLOCK , DEFAULT_DESTBLOCK);
404 strnncpy(DESTALLOW , DEFAULT_DESTALLOW);
405 strnncpy(DESTALLOW2 , DEFAULT_DESTALLOW2);
406 strnncpy(SOURCEBLOCK , DEFAULT_SOURCEBLOCK);
407 strnncpy(HDRFILTER , DEFAULT_HDRFILTER);
408 strnncpy(REGULAR , DEFAULT_REGULAR);
409 strnncpy(POOL , DEFAULT_POOL);
410 strnncpy(TYPE1LIST , DEFAULT_TYPE1LIST);
411 strnncpy(TYPE2REL , DEFAULT_TYPE2REL);
412 strnncpy(PIDFILE , DEFAULT_PIDFILE);
413
414 strnncpy(PGPREMPUBRING, DEFAULT_PGPREMPUBRING);
415 strnncpy(PGPREMPUBASC , DEFAULT_PGPREMPUBASC);
416 strnncpy(PGPREMSECRING, DEFAULT_PGPREMSECRING);
417 strnncpy(NYMSECRING , DEFAULT_NYMSECRING);
418 strnncpy(NYMDB , DEFAULT_NYMDB);
419 strnncpy(STAREX , DEFAULT_STAREX);
420 strnncpy(ALLPINGERSURL, DEFAULT_ALLPINGERSURL);
421 strnncpy(ALLPINGERSFILE, DEFAULT_ALLPINGERSFILE);
422 strnncpy(WGET , DEFAULT_WGET);
423 strnncpy(STATSSRC , DEFAULT_STATSSRC);
424
425 strnncpy(MIXDIR , "");
426 strnncpy(POOLDIR , "");
427
428 /* programs */
429 #ifdef WIN32
430 strnncpy(SENDMAIL , "outfile");
431 #else /* end of WIN32 */
432 strnncpy(SENDMAIL , "/usr/lib/sendmail -t");
433 #endif /* else if not WIN32 */
434 strnncpy(SENDANONMAIL , "");
435 strnncpy(NEWS , "");
436 strnncpy(TYPE1 , "");
437
438 /* addresses */
439 strnncpy(MAILtoNEWS , "mail2news@dizum.com,mail2news@m2n.mixmin.net");
440 strnncpy(REMAILERNAME , "Anonymous Remailer");
441 strnncpy(ANONNAME , "Anonymous");
442 strnncpy(REMAILERADDR , "");
443 strnncpy(ANONADDR , "");
444 strnncpy(COMPLAINTS , "");
445 strnncpy(SMTPRELAY , "");
446 AUTOREPLY = 0;
447
448 #ifdef USE_SOCK
449 strnncpy(HELONAME , "");
450 strnncpy(ENVFROM , "");
451 POP3DEL = 0;
452 POP3SIZELIMIT = 0;
453 POP3TIME = 60 * 60;
454
455 #endif /* USE_SOCK */
456
457 strnncpy(SHORTNAME , "");
458
459 /* configuration */
460 REMAIL = 0;
461 MIX = 1;
462 PGP = 1;
463 UNENCRYPTED = 0;
464 REMIX = 1;
465 REPGP = 1;
466 STATSAUTOUPDATE = 0;
467 STATSINTERVAL = 8 * 60 * 60;
468 strnncpy(EXTFLAGS, "");
469
470 strnncpy(PRECEDENCE, "");
471 POOLSIZE = 0;
472 RATE = 100;
473 INDUMMYP = 3; /* add dummy messages with probability p for each message added to the pool */
474 OUTDUMMYP = 10; /* add dummy messages with probability p each time we send from the pool */
475 INDUMMYMAXP = 84; /* for both of the above: while (rnd < p) { senddummy(); } */
476 OUTDUMMYMAXP = 96; /* set max INDUMMYP and OUTDUMMYP such that 24 and 5.25 dummy messages will */
477 MIDDLEMAN = 0; /* be generated on average. More than this is insane. */
478 AUTOBLOCK = 1;
479 STATSDETAILS = 1;
480 strnncpy(FORWARDTO, "*");
481 SIZELIMIT = 0; /* maximal size of remailed messages */
482 INFLATEMAX = 50; /* maximal size of Inflate: padding */
483 MAXRANDHOPS = 5;
484 BINFILTER = 0; /* filter binary attachments? */
485 LISTSUPPORTED = 1; /* list supported remailers in remailer-conf reply? */
486 PACKETEXP = 7 * SECONDSPERDAY; /* Expiration time for old packets */
487 IDEXP = 7 * SECONDSPERDAY; /* 0 = no ID log !! */
488 SENDPOOLTIME = 0; /* frequency for sending pool messages */
489 MAILINTIME = 5 * 60; /* frequency for processing MAILIN mail */
490
491 KEYLIFETIME = 13 * 30 * 24 * 60 * 60; /* validity period for keys. */
492 KEYOVERLAPPERIOD = 1 * 30 * 24 * 60 * 60; /* when keys have this amount of time */
493 /* left before expiration, create */
494 /* new ones when ./mix -K is run.*/
495 KEYGRACEPERIOD = 7 * 24 * 60 * 60; /* accept mail to the old key for this */
496 /* amount of time after it has expired. */
497
498
499 strnncpy(ERRLOG , "");
500 strnncpy(ADDRESS , "");
501 strnncpy(NAME , "");
502
503 strnncpy(ORGANIZATION, "Anonymous Posting Service");
504 strnncpy(MID , "y");
505
506 /* client config */
507 NUMCOPIES = 1;
508 strnncpy(CHAIN, "*,*,*,*");
509 VERBOSE = 2;
510 DISTANCE = 2;
511 MINREL = 98;
512 RELFINAL = 99;
513 MAXLAT = 36 * 60 * 60;
514 MINLAT = 5 * 60;
515 strnncpy(PGPPUBRING, "");
516 strnncpy(PGPSECRING, "");
517 #ifdef COMPILEDPASS
518 strnncpy(PASSPHRASE, COMPILEDPASS);
519 #else /* end of COMPILEDPASS */
520 strnncpy(PASSPHRASE, "");
521 #endif /* else if not COMPILEDPASS */
522 strnncpy(MAILIN , "");
523 strnncpy(MAILBOX , "mbox");
524 strnncpy(MAILABUSE , "");
525 strnncpy(MAILBLOCK , "");
526 #ifdef WIN32
527 strnncpy(MAILUSAGE , "nul:");
528 strnncpy(MAILANON , "nul:");
529 strnncpy(MAILERROR , "nul:");
530 #else /* end of WIN32 */
531 strnncpy(MAILUSAGE , "/dev/null");
532 strnncpy(MAILANON , "/dev/null");
533 strnncpy(MAILERROR , "/dev/null");
534 #endif /* else if not WIN32 */
535 strnncpy(MAILBOUNCE, "");
536
537 CLIENTAUTOFLUSH = 1;
538 MAXRECIPIENTS = 5;
539
540 TIMESKEW_FORWARD = 2*7*24*60*60;
541 TIMESKEW_BACK = 12*60*60;
542 TEMP_FAIL = 75;
543 }
544
545 int mix_configline(char *line)
546 {
547 return (read_conf(ADDRESS) || read_conf(NAME) ||
548 read_conf(SHORTNAME) || read_conf(REMAILERADDR) ||
549 read_conf(ANONADDR) || read_conf(REMAILERNAME) ||
550 read_conf(ANONNAME) || read_conf(COMPLAINTS) ||
551 read_conf_i(AUTOREPLY) || read_conf(SMTPRELAY) ||
552 read_conf(SMTPUSERNAME) || read_conf(SMTPPASSWORD) ||
553 #ifdef USE_SOCK
554 read_conf(HELONAME) || read_conf(ENVFROM) ||
555 #endif /* USE_SOCK */
556 read_conf(SENDMAIL) || read_conf(SENDANONMAIL) ||
557 read_conf(PRECEDENCE) ||
558 read_conf_i(REMAIL) || read_conf_i(MIX) ||
559 read_conf_i(PGP) || read_conf_i(UNENCRYPTED) ||
560 read_conf_i(REMIX) || read_conf(NEWS) ||
561 read_conf_i(REPGP) || read_conf(EXTFLAGS) ||
562 read_conf(MAILtoNEWS) || read_conf(ERRLOG) ||
563 read_conf(ORGANIZATION) || read_conf(MID) ||
564 read_conf(TYPE1) || read_conf_i(POOLSIZE) ||
565 read_conf_i(RATE) || read_conf_i(MIDDLEMAN) ||
566 read_conf_i(INDUMMYP) ||
567 read_conf_i(OUTDUMMYP) ||
568 read_conf_i(AUTOBLOCK) || read_conf(FORWARDTO) ||
569 read_conf_i(STATSDETAILS) ||
570 read_conf_i(SIZELIMIT) || read_conf_i(INFLATEMAX) ||
571 read_conf_i(MAXRANDHOPS) || read_conf_i(BINFILTER) ||
572 read_conf_i(LISTSUPPORTED) ||
573 read_conf_t(PACKETEXP) || read_conf_t(IDEXP) ||
574 read_conf_t(SENDPOOLTIME) || read_conf_i(NUMCOPIES) ||
575 read_conf_t(MAILINTIME) ||
576 read_conf(CHAIN) || read_conf_i(VERBOSE) ||
577 read_conf_i(DISTANCE) || read_conf_i(MINREL) ||
578 read_conf_i(RELFINAL) || read_conf_t(MAXLAT) ||
579 read_conf_t(MINLAT) ||
580 read_conf(PGPPUBRING) || read_conf(PGPSECRING) ||
581 read_conf(PASSPHRASE) || read_conf_t(KEYLIFETIME) ||
582 read_conf_t(KEYGRACEPERIOD) || read_conf_t(KEYOVERLAPPERIOD) ||
583 #ifdef USE_SOCK
584 read_conf_i(POP3DEL) || read_conf_i(POP3SIZELIMIT) ||
585 read_conf_t(POP3TIME) ||
586 #endif /* USE_SOCK */
587 read_conf(MAILBOX) || read_conf(MAILABUSE) ||
588 read_conf(MAILBLOCK) || read_conf(MAILUSAGE) ||
589 read_conf(MAILANON) || read_conf(MAILERROR) ||
590 read_conf(MAILBOUNCE) || read_conf(MAILIN) ||
591
592 read_conf(DISCLAIMFILE) || read_conf(FROMDSCLFILE) ||
593 read_conf(MSGFOOTERFILE) ||
594 read_conf(POP3CONF) || read_conf(HELPFILE) ||
595 read_conf(REQUESTDIR) ||
596 read_conf(ABUSEFILE) || read_conf(REPLYFILE) ||
597 read_conf(USAGEFILE) || read_conf(USAGELOG) ||
598 read_conf(BLOCKFILE) || read_conf(ADMKEYFILE) ||
599 read_conf(KEYFILE) || read_conf(PGPKEY) ||
600 read_conf(DSAPARAMS) || read_conf(DHPARAMS) ||
601 read_conf(MIXRAND) || read_conf(SECRING) ||
602 read_conf(PUBRING) || read_conf(IDLOG) ||
603 read_conf(STATS) || read_conf(DESTBLOCK) ||
604 read_conf(PGPMAXCOUNT) ||
605 read_conf(DESTALLOW) || read_conf(DESTALLOW2) ||
606 read_conf(SOURCEBLOCK) ||
607 read_conf(STAREX) || read_conf(ALLPINGERSURL) ||
608 read_conf(ALLPINGERSFILE) ||
609 read_conf(HDRFILTER) || read_conf(REGULAR) ||
610 read_conf(POOL) || read_conf(TYPE1LIST) ||
611 read_conf(TYPE2REL) ||
612 read_conf(PGPREMPUBRING) || read_conf(PGPREMPUBASC) ||
613 read_conf(PGPREMSECRING) || read_conf(NYMSECRING) ||
614 read_conf(NYMDB) || read_conf(PIDFILE) ||
615 read_conf(WGET) || read_conf(STATSSRC) ||
616 read_conf_i(STATSAUTOUPDATE) || read_conf_t(STATSINTERVAL) ||
617
618 read_conf_i(CLIENTAUTOFLUSH) ||
619 read_conf_i(MAXRECIPIENTS) ||
620
621 read_conf_t(TIMESKEW_FORWARD) ||
622 read_conf_t(TIMESKEW_BACK) ||
623 read_conf_i(TEMP_FAIL) );
624 }
625
626 int mix_config(void)
627 {
628 char *d;
629 FILE *f;
630 char line[PATHMAX];
631 int err = -1;
632 #ifdef POSIX
633 struct passwd *pw;
634 #endif /* POSIX */
635 struct stat buf;
636 #ifdef HAVE_UNAME
637 struct utsname uts;
638 #endif /* HAVE_UNAME */
639 #ifdef WIN32
640 HKEY regsw, reg, regpgp;
641 DWORD type, len;
642 int rkey = 0;
643 #endif /* WIN32 */
644
645 mix_setdefaults();
646
647 #ifdef POSIX
648 pw = getpwuid(getuid());
649 #endif /* POSIX */
650
651 /* find our base directory
652 *
653 * first match wins.
654 *
655 * - what the MIXPATH environment variable points to, if it is set.
656 * - On WIN32, HKEY_CURRENT_USER\Software\Mixmaster\MixDir, if it exists
657 * - whatever is compiled in with -DSPOOL
658 * - On Win32 %APPDATA%\Mixmaster
659 * - on POSIX, ~/Mix (or ~/<HOMEMIXDIR>)
660 * - the current working directory
661 */
662
663 if (err == -1 && (d = getenv("MIXPATH")) != NULL)
664 err = mixdir(d, 1);
665
666 #ifdef WIN32
667 RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, ®sw);
668 len=sizeof(line);
669 if (err == -1 &&
670 RegOpenKeyEx(regsw, "Mixmaster", 0, KEY_QUERY_VALUE, ®) == 0) {
671 if (RegQueryValueEx(reg, "MixDir", 0, &type, line, &len) == 0)
672 err = mixdir(line, 1);
673 RegCloseKey(reg);
674 }
675 #endif /* WIN32 */
676
677 #ifdef SPOOL
678 if (err == -1 && strlen(SPOOL) > 0)
679 err = mixdir(SPOOL, 0);
680 #endif /* SPOOL */
681
682 #ifdef WIN32
683 if (err == -1) {
684 LPMALLOC lpmalloc;
685 ITEMIDLIST *itemidlist;
686 if (SUCCEEDED(SHGetMalloc(&lpmalloc)))
687 {
688 SHGetSpecialFolderLocation(0,CSIDL_APPDATA,&itemidlist);
689 SHGetPathFromIDList(itemidlist,line);
690 lpmalloc->lpVtbl->Free(lpmalloc,&itemidlist);
691 lpmalloc->lpVtbl->Release(lpmalloc);
692
693 strcatn(line, "\\Mixmaster", PATHMAX);
694 err = mixdir(line, 1);
695
696 }
697 }
698 #endif /* WIN32 */
699
700 #ifdef POSIX
701 if (err == -1 && pw != NULL) {
702 strncpy(line, pw->pw_dir, PATHMAX);
703 line[PATHMAX-1] = '\0';
704 if (line[strlen(line) - 1] != DIRSEP)
705 strcatn(line, DIRSEPSTR, PATHMAX);
706 strcatn(line, HOMEMIXDIR, PATHMAX);
707 err = mixdir(line, 1);
708 }
709 #endif /* POSIX */
710
711 if (err == -1) {
712 getcwd(MIXDIR, PATHMAX);
713 mixdir(MIXDIR, 0);
714 }
715
716 #ifdef GLOBALMIXCONF
717 f = mix_openfile(GLOBALMIXCONF, "r");
718 if (f != NULL) {
719 while (fgets(line, LINELEN, f) != NULL)
720 if (line[0] > ' ' && line[0] != '#')
721 mix_configline(line);
722 fclose(f);
723 }
724 #endif /* GLOBALMIXCONF */
725 f = mix_openfile(MIXCONF, "r");
726 if (f != NULL) {
727 while (fgets(line, LINELEN, f) != NULL)
728 if (line[0] > ' ' && line[0] != '#')
729 mix_configline(line);
730 fclose(f);
731 }
732
733 mixfile(POOLDIR, POOL); /* set POOLDIR after reading POOL from cfg file */
734 if (POOLDIR[strlen(POOLDIR) - 1] == DIRSEP)
735 POOLDIR[strlen(POOLDIR) - 1] = '\0';
736 if (stat(POOLDIR, &buf) != 0)
737 if
738 #ifndef POSIX
739 (mkdir(POOLDIR) != 0)
740 #else /* end of not POSIX */
741 (mkdir(POOLDIR, S_IRWXU) == -1)
742 #endif /* else if POSIX */
743 strncpy(POOLDIR, MIXDIR, PATHMAX);
744
745 if (IDEXP > 0 && IDEXP < 5 * SECONDSPERDAY)
746 IDEXP = 5 * SECONDSPERDAY;
747 if (MAXRANDHOPS > 20)
748 MAXRANDHOPS = 20;
749 if (INDUMMYP > INDUMMYMAXP)
750 INDUMMYP = INDUMMYMAXP;
751 if (OUTDUMMYP > OUTDUMMYMAXP)
752 OUTDUMMYP = OUTDUMMYMAXP;
753
754 if (strchr(SHORTNAME, '.'))
755 *strchr(SHORTNAME, '.') = '\0';
756 if (strchr(SHORTNAME, ' '))
757 *strchr(SHORTNAME, ' ') = '\0';
758 #ifdef HAVE_UNAME
759 if (SHORTNAME[0] == '\0' && uname(&uts) != -1)
760 strncpy(SHORTNAME, uts.nodename, LINELEN);
761 #elif defined(HAVE_GETHOSTNAME) /* end of HAVE_UNAME */
762 if (SHORTNAME[0] == '\0')
763 gethostname(SHORTNAME, LINELEN);
764 #endif /* defined(HAVE_GETHOSTNAME) */
765 if (SHORTNAME[0] == '\0')
766 strcpy(SHORTNAME, "unknown");
767
768 if (ADDRESS[0] == '\0')
769 whoami(ADDRESS, "user");
770
771 #ifdef HAVE_GECOS
772 if (NAME[0] == '\0' && pw != NULL)
773 strcatn(NAME, pw->pw_gecos, sizeof(NAME));
774 #endif /* HAVE_GECOS */
775
776 if (REMAILERADDR[0] == '\0')
777 strncpy(REMAILERADDR, ADDRESS, LINELEN);
778
779 if (COMPLAINTS[0] == '\0')
780 strncpy(COMPLAINTS, REMAILERADDR, LINELEN);
781
782 if (strchr(REMAILERNAME, '@') == NULL) {
783 strcatn(REMAILERNAME, " <", LINELEN);
784 strcatn(REMAILERNAME, REMAILERADDR, LINELEN);
785 strcatn(REMAILERNAME, ">", LINELEN);
786 }
787 if (strchr(ANONNAME, '@') == NULL && ANONADDR[0] != '\0') {
788 strcatn(ANONNAME, " <", LINELEN);
789 strcatn(ANONNAME, ANONADDR, LINELEN);
790 strcatn(ANONNAME, ">", LINELEN);
791 }
792 if (strchr(ANONNAME, '@') == NULL) {
793 strcatn(ANONNAME, " <", LINELEN);
794 strcatn(ANONNAME, REMAILERADDR, LINELEN);
795 strcatn(ANONNAME, ">", LINELEN);
796 }
797 #ifndef USE_PGP
798 if (TYPE1[0] == '\0')
799 PGP = 0;
800 #endif /* not USE_PGP */
801
802 #ifdef WIN32
803 if (RegOpenKeyEx(regsw, "PGP", 0, KEY_ALL_ACCESS, ®pgp) == 0)
804 rkey++;
805 if (rkey && RegOpenKeyEx(regpgp, "PGPlib", 0, KEY_QUERY_VALUE, ®) == 0)
806 rkey++;
807 if (PGPPUBRING[0] == '\0' && rkey == 2) {
808 len = PATHMAX;
809 RegQueryValueEx(reg, "PubRing", 0, &type, PGPPUBRING, &len);
810 }
811 if (PGPSECRING[0] == '\0' && rkey == 2) {
812 len = PATHMAX;
813 RegQueryValueEx(reg, "SecRing", 0, &type, PGPSECRING, &len);
814 }
815 if (rkey == 2)
816 RegCloseKey(reg);
817 if (rkey)
818 RegCloseKey(regpgp);
819 RegCloseKey(regsw);
820 #endif /* WIN32 */
821
822 if (PGPPUBRING[0] == '\0') {
823 char *d;
824
825 if ((d = getenv("HOME")) != NULL) {
826 strcpy(PGPPUBRING, d);
827 strcatn(PGPPUBRING, "/.pgp/", PATHMAX);
828 }
829 strcatn(PGPPUBRING, "pubring.pkr", PATHMAX);
830 if (stat(PGPPUBRING, &buf) == -1)
831 strcpy(strrchr(PGPPUBRING, '.'), ".pgp");
832 }
833 if (PGPSECRING[0] == '\0') {
834 char *d;
835
836 if ((d = getenv("HOME")) != NULL) {
837 strcpy(PGPSECRING, d);
838 strcatn(PGPSECRING, "/.pgp/", PATHMAX);
839 }
840 strcatn(PGPSECRING, "secring.skr", PATHMAX);
841 if (stat(PGPSECRING, &buf) == -1)
842 strcpy(strrchr(PGPSECRING, '.'), ".pgp");
843 }
844 if (streq(NEWS, "mail-to-news"))
845 strncpy(NEWS, MAILtoNEWS, sizeof(NEWS));
846
847 if (f == NULL) {
848 #ifndef GLOBALMIXCONF
849 /* Only write the config file in non systemwide installation */
850 f = mix_openfile(MIXCONF, "w");
851 if (f == NULL)
852 errlog(WARNING, "Can't open %s%s!\n", MIXDIR, MIXCONF);
853 else {
854 fprintf(f, "# mix.cfg - mixmaster configuration file\n");
855 fprintf(f, "NAME %s\n", NAME);
856 fprintf(f, "ADDRESS %s\n", ADDRESS);
857 fprintf(f, "\n# edit to set up a remailer:\n");
858 fprintf(f, "REMAIL n\n");
859 fprintf(f, "SHORTNAME %s\n", SHORTNAME);
860 fprintf(f, "REMAILERADDR %s\n", REMAILERADDR);
861 fprintf(f, "COMPLAINTS %s\n", COMPLAINTS);
862 fclose(f);
863 }
864 #endif /* not GLOBALMIXCONF */
865 REMAIL = 0;
866 }
867
868 if (ENTEREDPASSPHRASE[0] != '\0') {
869 strncpy(PASSPHRASE, ENTEREDPASSPHRASE, LINELEN);
870 PASSPHRASE[LINELEN-1] = 0;
871 };
872
873 return (0);
874 }
875
876 /** Library initialization: ******************************************/
877
878 static int initialized = 0;
879
880 void mix_check_timeskew() {
881 FILE *f;
882 long now, tpool = 0, tpop3 = 0, tdaily = 0, tmailin = 0, latest = 0;
883
884 f = mix_openfile(REGULAR, "r+");
885 if (f != NULL) {
886 lock(f);
887 fscanf(f, "%ld %ld %ld %ld", &tpool, &tpop3, &tdaily, &tmailin);
888 latest = tpool;
889 latest = latest > tpop3 ? latest : tpop3;
890 latest = latest > tdaily ? latest : tdaily;
891 latest = latest > tmailin ? latest : tmailin;
892 now = time(NULL);
893
894
895 if (( (TIMESKEW_BACK != 0) && (now < latest - TIMESKEW_BACK )) ||
896 ( (TIMESKEW_FORWARD != 0) && (now > latest + TIMESKEW_FORWARD)) ) {
897 /* Possible timeskew */
898 errlog(ERRORMSG, "Possible timeskew detected. Check clock and rm %s\n", REGULAR);
899 exit(TEMP_FAIL);
900 }
901 fclose(f);
902 } else {
903 /* shrug */
904 }
905 }
906
907 int mix_init(char *mixdir)
908 {
909 if (!initialized) {
910 if (mixdir)
911 strncpy(MIXDIR, mixdir, LINELEN);
912 mix_config();
913 #if defined(USE_SOCK) && defined(WIN32)
914 sock_init();
915 #endif /* defined(USE_SOCK) && defined(WIN32) */
916 /* atexit (mix_exit); */
917 initialized = 1;
918 }
919
920 if (rnd_init() == -1)
921 rnd_seed();
922 return(0);
923 }
924
925 void mix_exit(void)
926 {
927 if (!initialized)
928 return;
929 rnd_final();
930 #if defined(USE_SOCK) && defined(WIN32)
931 sock_exit();
932 #endif /* defined(USE_SOCK) && defined(WIN32) */
933 initialized=0;
934 }
935
936 void mix_upd_stats(void)
937 {
938 FILE *f;
939 BUFFER *statssrc;
940 statssrc = buf_new();
941 buf_clear(statssrc);
942 f = mix_openfile(STATSSRC, "r");
943 if (f != NULL) {
944 buf_read(statssrc, f);
945 fclose(f);
946 }
947 if (statssrc->length > 0)
948 download_stats(statssrc->data);
949 buf_free(statssrc);
950 }
951
952 int mix_regular(int force)
953 {
954 FILE *f;
955 long now, tpool = 0, tpop3 = 0, tdaily = 0, tmailin = 0, tstats = 0;
956 int ret = 0;
957
958 mix_init(NULL);
959 now = time(NULL);
960
961 f = mix_openfile(REGULAR, "r+");
962 if (f != NULL) {
963 lock(f);
964 fscanf(f, "%ld %ld %ld %ld %ld", &tpool, &tpop3, &tdaily, &tmailin, &tstats);
965 if (now - tpool >= SENDPOOLTIME)
966 force |= FORCE_POOL | FORCE_MAILIN;
967 #ifdef USE_SOCK
968 if (now - tpop3 >= POP3TIME)
969 force |= FORCE_POP3 | FORCE_MAILIN;
970 #endif /* USE_SOCK */
971 if (now - tdaily >= SECONDSPERDAY)
972 force |= FORCE_DAILY;
973 if (now - tmailin >= MAILINTIME)
974 force |= FORCE_MAILIN;
975 if (now - tstats >= STATSINTERVAL)
976 force |= FORCE_STATS;
977 if (force & FORCE_POOL)
978 tpool = now;
979 if (force & FORCE_POP3)
980 tpop3 = now;
981 if (force & FORCE_DAILY)
982 tdaily = now;
983 if (force & FORCE_MAILIN)
984 tmailin = now;
985 if (force & FORCE_STATS)
986 tstats = now;
987 rewind(f);
988 fprintf(f, "%ld %ld %ld %ld %ld\n", tpool, tpop3, tdaily, tmailin, tstats);
989 unlock(f);
990 fclose(f);
991 } else {
992 force = FORCE_POOL | FORCE_POP3 | FORCE_DAILY | FORCE_MAILIN | FORCE_STATS;
993 f = mix_openfile(REGULAR, "w+");
994 if (f != NULL) {
995 lock(f);
996 fprintf(f, "%ld %ld %ld %ld %ld\n", now, now, now, now, now);
997 unlock(f);
998 fclose(f);
999 } else
1000 errlog(ERRORMSG, "Can't create %s!\n", REGULAR);
1001 }
1002
1003 if (force & FORCE_DAILY)
1004 mix_daily(), ret = 1;
1005 #ifdef USE_SOCK
1006 if (force & FORCE_POP3)
1007 pop3get();
1008 #endif /* USE_SOCK */
1009 if (force & FORCE_MAILIN)
1010 ret = process_mailin();
1011 if (force & FORCE_POOL)
1012 ret = pool_send();
1013 if ((force & FORCE_STATS) && (STATSAUTOUPDATE != 0))
1014 mix_upd_stats();
1015
1016 return (ret);
1017 }
1018
1019 int mix_daily(void)
1020 {
1021 idexp();
1022 pgpmaxexp();
1023 pool_packetexp();
1024 stats(NULL);
1025 keymgt(0);
1026 return (0);
1027 }
1028
1029 /** Handle signals SIGHUP, SIGINT, and SIGTERM
1030 This signal handler gets called if the daemon
1031 process receives one of SIGHUP, SIGINT, or SIGTERM.
1032 It then sets either rereadconfig of terminatedaemon
1033 to true depending on the signal received.
1034
1035 @author PP
1036 @return nothing
1037 */
1038 #ifdef POSIX
1039 void sighandler(int signal) {
1040 if (signal == SIGHUP)
1041 rereadconfig = 1;
1042 else if (signal == SIGINT || signal == SIGTERM)
1043 terminatedaemon = 1;
1044 };
1045 #endif /* POSIX */
1046
1047 /** Set the signal handler for SIGHUP, SIGINT and SIGTERM
1048 This function registers signal handlers so that
1049 we can react on signals send by the user in daemon
1050 mode. SIGHUP will instruct mixmaster to reload its
1051 configuration while SIGINT and SIGTERM will instruct
1052 it to shut down. Mixmaster will finish the current
1053 pool run before it terminates.
1054
1055 @param restart Whether or not system calls should be
1056 restarted. Usually we want this, the
1057 only excetion is the sleep() in the
1058 daemon mail loop.
1059 @author PP
1060 @return -1 if calling sigaction failed, 0 on
1061 no error
1062 */
1063 int setsignalhandler(int restart)
1064 {
1065 #ifdef POSIX
1066 struct sigaction hdl;
1067 int err = 0;
1068
1069 memset(&hdl, 0, sizeof(hdl));
1070 hdl.sa_handler = sighandler;
1071 hdl.sa_flags = restart ? SA_RESTART : 0;
1072
1073 if (sigaction(SIGHUP, &hdl, NULL))
1074 err = -1;
1075 if (sigaction(SIGINT, &hdl, NULL))
1076 err = -1;
1077 if (sigaction(SIGTERM, &hdl, NULL))
1078 err = -1;
1079 return (err);
1080 #else /* POSIX */
1081 return(0);
1082 #endif /* POSIX */
1083 }
1084
1085 #ifdef WIN32
1086 /* Try to detect if we are the service or not...
1087 seems there is no easy reliable way */
1088 int is_nt_service(void)
1089 {
1090 static int issvc = -1;
1091 #ifdef WIN32SERVICE
1092 STARTUPINFO StartupInfo;
1093 OSVERSIONINFO VersionInfo;
1094 DWORD dwsize;
1095
1096 if (issvc != -1) /* do it only once */
1097 return issvc;
1098
1099 VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo);
1100 if (GetVersionEx(&VersionInfo))
1101 if (VersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT)
1102 return issvc = 0; /* not NT - not the service */
1103
1104 GetStartupInfo(&StartupInfo);
1105 if (StartupInfo.lpDesktop[0] == 0)
1106 return issvc = 1; /* have no desktop - we are the service probably */
1107 #endif /* WIN32SERVICE */
1108
1109 return issvc = 0; /* assume not the service */
1110 } /* is_nt_service */
1111
1112 HANDLE hMustTerminate = NULL;
1113 void set_nt_exit_event(HANDLE h_svc_exit_event)
1114 {
1115 hMustTerminate = h_svc_exit_event;
1116 } /* set_nt_exit_event */
1117
1118 #endif /* WIN32 */
1119
1120 int mix_daemon(void)
1121 {
1122 long t, slept;
1123 t = SENDPOOLTIME;
1124 if (MAILINTIME < t && (MAILIN != NULL && MAILIN[0] != '\0'))
1125 t = MAILINTIME;
1126 #ifdef USE_SOCK
1127 if (POP3TIME < t)
1128 t = POP3TIME;
1129 #endif /* USE_SOCK */
1130 if (t < 5)
1131 t = 5; /* Some kind of safety for broken systems */
1132 slept = t;
1133
1134 setsignalhandler(1); /* set signal handlers and restart any interrupted system calls */
1135 for(;;) {
1136 if (terminatedaemon)
1137 break;
1138 if (rereadconfig) {
1139 rereadconfig = 0;
1140 mix_config();
1141 t = SENDPOOLTIME;
1142 if (MAILINTIME < t && (MAILIN != NULL && MAILIN[0] != '\0'))
1143 t = MAILINTIME;
1144 #ifdef USE_SOCK
1145 if (POP3TIME < t)
1146 t = POP3TIME;
1147 if (t < 5)
1148 t = 5; /* Some kind of safety for broken systems */
1149 #endif /* USE_SOCK */
1150 }
1151 if (slept >= t) {
1152 mix_regular(0);
1153 slept = 0;
1154 }
1155
1156 #ifdef WIN32SERVICE
1157 if (hMustTerminate) {
1158 if (WaitForSingleObject(hMustTerminate, t * 1000) == WAIT_OBJECT_0) {
1159 CloseHandle(hMustTerminate);
1160 terminatedaemon = 1;
1161 }
1162 }
1163 #endif /* WIN32SERVICE */
1164
1165 if (!terminatedaemon && !rereadconfig) {
1166 setsignalhandler(0); /* set signal handlers; don't restart system calls */
1167 #ifdef WIN32
1168 sleep(t); /* how to get the real number of seconds slept? */
1169 slept = t;
1170 #else /* end of WIN32 */
1171 slept += (t - slept) - sleep(t - slept);
1172 #endif /* else if not WIN32 */
1173 setsignalhandler(1); /* set signal handlers and restart any interrupted system calls */
1174 }
1175 }
1176 return (0);
1177 }
1178
1179 /** error ***************************************************************/
1180
1181 void errlog(int type, char *fmt,...)
1182 {
1183 va_list args;
1184 BUFFER *msg;
1185 FILE *e = NULL;
1186 time_t t;
1187 struct tm *tc;
1188 char line[LINELEN];
1189 int p;
1190 char err[6][8] =
1191 {"", "Error", "Warning", "Notice", "Info", "Info"};
1192
1193 if ((VERBOSE == 0 && type != ERRORMSG) || (type == LOG && VERBOSE < 2)
1194 || (type == DEBUGINFO && VERBOSE < 3))
1195 return;
1196
1197 t = time(NULL);
1198 tc = localtime(&t);
1199 strftime(line, LINELEN, "[%Y-%m-%d %H:%M:%S] ", tc);
1200
1201 msg = buf_new();
1202 buf_appends(msg, line);
1203 p = msg->length;
1204 buf_appendf(msg, "%s: ", err[type]);
1205 va_start(args, fmt);
1206 buf_vappendf(msg, fmt, args);
1207 va_end(args);
1208
1209 if (streq(ERRLOG, "stdout"))
1210 e = stdout;
1211 else if (streq(ERRLOG, "stderr"))
1212 e = stderr;
1213
1214 if (e == NULL && (ERRLOG[0] == '\0' ||
1215 (e = mix_openfile(ERRLOG, "a")) == NULL))
1216 mix_status("%s", msg->data + p);
1217 else {
1218 buf_write(msg, e);
1219 if (e != stderr && e != stdout) {
1220 fclose(e);
1221 /* duplicate the error message on screen */
1222 mix_status("%s", msg->data + p);
1223 }
1224 }
1225 buf_free(msg);
1226 }
1227
1228 static char statusline[BUFSIZE] = "";
1229
1230 void mix_status(char *fmt,...)
1231 {
1232 va_list args;
1233
1234 if (fmt != NULL) {
1235 va_start(args, fmt);
1236 #ifdef _MSC
1237 _vsnprintf(statusline, sizeof(statusline) - 1, fmt, args);
1238 #else /* end of _MSC */
1239 vsnprintf(statusline, sizeof(statusline) - 1, fmt, args);
1240 #endif /* else if not _MSC */
1241 va_end(args);
1242 }
1243 #ifdef USE_NCURSES
1244 if (menu_initialized) {
1245 cl(LINES - 2, 10);
1246 printw("%s", statusline);
1247 refresh();
1248 } else
1249 #endif /* USE_NCURSES */
1250 {
1251 fprintf(stderr, "%s", statusline);
1252 }
1253 }
1254
1255 void mix_genericerror(void)
1256 {
1257 if (streq(statusline, "") || strfind(statusline, "...") ||
1258 strifind(statusline, "generating"))
1259 mix_status("Failed!");
1260 else
1261 mix_status(NULL);
1262 }