/[pkg-mixmaster]/trunk/Mix/Src/mix.c
ViewVC logotype

Contents of /trunk/Mix/Src/mix.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:keywords Id

  ViewVC Help
Powered by ViewVC 1.1.5