1 /*
2 * sma -- Sendmail log analyser
3 *
4 * Copyright (c) 2000 - 2003 Jarkko Turkulainen. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY JARKKO TURKULAINEN ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL JARKKO TURKULAINEN BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Date: 2002/12/06 12:48:51 $
30 */
31
32
33 /* Copyright for routines in scan_time() */
34
35 /*
36 * Copyright (c) 1985, 1987, 1988, 1993
37 * The Regents of the University of California. All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the University of
50 * California, Berkeley and its contributors.
51 * 4. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 */
67
68 #include "sma.h"
69
70 #ifndef __DragonFly__
71 int isdigit(int);
72 #endif
73
74 /*
75 * Take month as an ascii string and return integer
76 */
77 int
conv_mon(const char * mon)78 conv_mon(const char *mon) {
79 int k;
80 const char *montab[] = {
81 "Jan", "Feb", "Mar", "Apr", "May",
82 "Jun", "Jul", "Aug", "Sep", "Oct",
83 "Nov", "Dec"
84 };
85
86 for (k = 0; k < 12; k++) {
87 if (!strcmp(mon, montab[k]))
88 break;
89 }
90 return k;
91 }
92
93 /*
94 * Take month, day, hour, minute and second as integers
95 * and return time_t
96 */
97 time_t
conv_time(int mon,int day,int hh,int min,int sec)98 conv_time(int mon, int day, int hh, int min, int sec) {
99
100 /*
101 * Assume that the year is current year...
102 * This is bug.
103 */
104
105 /* sanity check: */
106 if (mon < 0 || mon >= 12)
107 return (time_t)NULL;
108
109 /* fill the structure: */
110 tp.tm_sec=sec;
111 tp.tm_min=min;
112 tp.tm_hour=hh;
113 tp.tm_mday=day;
114 tp.tm_mon=mon;
115 tp.tm_year=curr->tm_year;
116 tp.tm_isdst=-1;
117
118 return mktime(&tp);
119 }
120
121 void
scan_time(time_t * utime,char * p)122 scan_time(time_t *utime, char *p) {
123 #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
124
125 struct tm *lt;
126 char *t, *dot;
127 int bigyear;
128 int yearset = 0;
129
130 for (t = p, dot = NULL; *t; ++t) {
131 if (isdigit(*t))
132 continue;
133 if (*t == '.' && dot == NULL) {
134 dot = t;
135 continue;
136 }
137 usage();
138 }
139
140 tval = time((time_t *)NULL);
141 lt = localtime(&tval);
142
143 lt->tm_isdst = -1; /* correct for DST */
144
145 if (dot != NULL) { /* .SS */
146 *dot++ = '\0';
147 if (strlen(dot) != 2)
148 usage();
149 lt->tm_sec = ATOI2(dot);
150 if (lt->tm_sec > 61)
151 usage();
152 } else
153 lt->tm_sec = 0;
154
155 switch (strlen(p)) {
156 case 12: /* cc */
157 bigyear = ATOI2(p);
158 lt->tm_year = bigyear * 100 - 1900;
159 yearset = 1;
160 /* FALLTHROUGH */
161 case 10: /* yy */
162 if (yearset) {
163 lt->tm_year += ATOI2(p);
164 } else {
165 lt->tm_year = ATOI2(p);
166 if (lt->tm_year < 69) /* hack for 2000 ;-} */
167 lt->tm_year += (2000 - 1900);
168 else
169 lt->tm_year += (1900 - 1900);
170 }
171 /* FALLTHROUGH */
172 case 8: /* mm */
173 lt->tm_mon = ATOI2(p);
174 if ((lt->tm_mon > 12) || !lt->tm_mon)
175 usage();
176 --lt->tm_mon; /* time struct is 0 - 11 */
177 /* FALLTHROUGH */
178 case 6: /* dd */
179 lt->tm_mday = ATOI2(p);
180 if ((lt->tm_mday > 31) || !lt->tm_mday)
181 usage();
182 /* FALLTHROUGH */
183 case 4: /* HH */
184 lt->tm_hour = ATOI2(p);
185 if (lt->tm_hour > 23)
186 usage();
187 /* FALLTHROUGH */
188 case 2: /* MM */
189 lt->tm_min = ATOI2(p);
190 if (lt->tm_min > 59)
191 usage();
192 break;
193 default:
194 usage();
195 }
196 *utime = mktime(lt);
197 }
198
199 /*
200 * Prepare a space for sorted address stuctures and do the sorting
201 */
202 void
sort(struct host * hptr)203 sort(struct host *hptr) {
204 int i, j, k;
205 struct envpair *eptr;
206 struct in *iptr;
207 struct out *optr;
208 struct relpair *rptr;
209 struct rin *riptr;
210 struct rout *roptr;
211 struct status *statptr;
212 struct rule *ruleptr;
213 struct rrelay *rrptr;
214
215 /* space for sorted structures: */
216 if (!(hptr->setab = malloc(hptr->edif * sizeof(struct envpair *))))
217 error_memory();
218 if (!(hptr->sitab = malloc(hptr->idif * sizeof(struct sin *))))
219 error_memory();
220 if (!(hptr->sotab = malloc(hptr->odif * sizeof(struct sout *))))
221 error_memory();
222 if (!(hptr->srtab = malloc(hptr->rrdif * sizeof(struct relpair *))))
223 error_memory();
224 if (!(hptr->rsitab = malloc(hptr->ridif * sizeof(struct rin *))))
225 error_memory();
226 if (!(hptr->rsotab = malloc(hptr->rodif * sizeof(struct rout *))))
227 error_memory();
228 if (!(hptr->ssttab = malloc(hptr->sdif * sizeof(struct status *))))
229 error_memory();
230 if (!(hptr->sruletab = malloc(hptr->rdif * sizeof(struct rule *))))
231 error_memory();
232
233 /* envelope pairs: */
234 for (i = 0, k = 0; k < asize; k++)
235 for (eptr = hptr->etab[k]; eptr; eptr = eptr->next)
236 /* copy pointers: */
237 hptr->setab[i++] = eptr;
238 /* relay pairs: */
239 for (i = 0, k = 0; k < rsize; k++)
240 for (rptr = hptr->rtab[k]; rptr; rptr = rptr->next)
241 /* copy pointers: */
242 hptr->srtab[i++] = rptr;
243
244 /* input: */
245 for (i = 0, k = 0; k < asize; k++)
246 for (iptr = hptr->itab[k]; iptr; iptr = iptr->next)
247 /* copy pointers: */
248 hptr->sitab[i++] = iptr;
249 for (i = 0, k = 0; k < rsize; k++)
250 for (riptr = hptr->ritab[k]; riptr; riptr = riptr->next)
251 /* copy pointers: */
252 hptr->rsitab[i++] = riptr;
253
254 /* output: */
255 for (i = 0, k = 0; k < asize; k++)
256 for (optr = hptr->otab[k]; optr; optr = optr->next)
257 /* copy pointers: */
258 hptr->sotab[i++] = optr;
259 for (i = 0, k = 0; k < rsize; k++)
260 for (roptr = hptr->rotab[k];roptr; roptr = roptr->next)
261 /* copy pointers: */
262 hptr->rsotab[i++] = roptr;
263
264 /* status and ruleset: */
265 for (i = 0, k = 0; k < rsize; k++)
266 for (statptr = hptr->sttab[k]; statptr; statptr = statptr->next)
267 /* copy pointers: */
268 hptr->ssttab[i++] = statptr;
269
270 for (i = 0, k = 0; k < rsize; k++)
271 for (ruleptr = hptr->ruletab[k]; ruleptr; ruleptr = ruleptr->next) {
272
273 /* copy pointers: */
274 hptr->sruletab[i++] = ruleptr;
275
276 /* alloc space for sorted relays: */
277 if (!(ruleptr->srrelaytab =
278 malloc(ruleptr->reldif * sizeof(struct rrelay *))))
279 error_memory();
280
281 for (j = 0, rrptr = ruleptr->rrelaytab; rrptr;
282 rrptr = rrptr->next)
283 ruleptr->srrelaytab[j++] = rrptr;
284
285 /* sort */
286 qsort(ruleptr->srrelaytab, ruleptr->reldif,
287 sizeof(struct rrelay *), comp_rrel);
288 }
289
290 /* sort */
291 qsort(hptr->setab, hptr->edif, sizeof(struct envpair *), comp_env);
292 qsort(hptr->sitab, hptr->idif, sizeof(struct in *), comp_in);
293 qsort(hptr->sotab, hptr->odif, sizeof(struct out *), comp_out);
294 qsort(hptr->srtab, hptr->rrdif, sizeof(struct relpair *), comp_rel);
295 qsort(hptr->rsitab, hptr->ridif, sizeof(struct rin *), comp_rin);
296 qsort(hptr->rsotab, hptr->rodif, sizeof(struct rout *), comp_rout);
297 qsort(hptr->ssttab, hptr->sdif, sizeof(struct status *), comp_s);
298 qsort(hptr->sruletab, hptr->rdif, sizeof(struct rule *), comp_r);
299 }
300
301 /*
302 * Comparing functions, called from sort()
303 */
304 int
comp_env(const void * p1,const void * p2)305 comp_env(const void *p1, const void *p2) {
306 const struct envpair * sp1 = *(const struct envpair * const *)p1;
307 const struct envpair * sp2 = *(const struct envpair * const *)p2;
308
309 if (sflag) {
310 if (sp1->size < sp2->size)
311 return (1);
312 if (sp1->size > sp2->size)
313 return (-1);
314 } else {
315 if (sp1->num < sp2->num)
316 return (1);
317 if (sp1->num > sp2->num)
318 return (-1);
319 }
320 return (0);
321 }
322 int
comp_rel(const void * p1,const void * p2)323 comp_rel(const void *p1, const void *p2) {
324 const struct relpair * sp1 = *(const struct relpair * const *)p1;
325 const struct relpair * sp2 = *(const struct relpair * const *)p2;
326
327 if (sflag) {
328 if (sp1->size < sp2->size)
329 return (1);
330 if (sp1->size > sp2->size)
331 return (-1);
332 } else {
333 if (sp1->num < sp2->num)
334 return (1);
335 if (sp1->num > sp2->num)
336 return (-1);
337 }
338 return (0);
339 }
340 int
comp_in(const void * p1,const void * p2)341 comp_in(const void *p1, const void *p2) {
342 const struct in * sp1 = *(const struct in * const *)p1;
343 const struct in * sp2 = *(const struct in * const *)p2;
344
345 if (sflag) {
346 if (sp1->size < sp2->size)
347 return (1);
348 if (sp1->size > sp2->size)
349 return (-1);
350 } else {
351 if (sp1->num < sp2->num)
352 return (1);
353 if (sp1->num > sp2->num)
354 return (-1);
355 }
356 return (0);
357 }
358 int
comp_rin(const void * p1,const void * p2)359 comp_rin(const void *p1, const void *p2) {
360 const struct rin * sp1 = *(const struct rin * const *)p1;
361 const struct rin * sp2 = *(const struct rin * const *)p2;
362
363 if (sflag) {
364 if (sp1->size < sp2->size)
365 return (1);
366 if (sp1->size > sp2->size)
367 return (-1);
368 } else {
369 if (sp1->num < sp2->num)
370 return (1);
371 if (sp1->num > sp2->num)
372 return (-1);
373 }
374 return (0);
375 }
376
377 int
comp_out(const void * p1,const void * p2)378 comp_out(const void *p1, const void *p2) {
379 const struct out * sp1 = *(const struct out * const *)p1;
380 const struct out * sp2 = *(const struct out * const *)p2;
381
382 if (sflag) {
383 if (sp1->size < sp2->size)
384 return (1);
385 if (sp1->size > sp2->size)
386 return (-1);
387 } else {
388 if (sp1->num < sp2->num)
389 return (1);
390 if (sp1->num > sp2->num)
391 return (-1);
392 }
393 return (0);
394 }
395 int
comp_rout(const void * p1,const void * p2)396 comp_rout(const void *p1, const void *p2) {
397 const struct rout * sp1 = *(const struct rout * const *)p1;
398 const struct rout * sp2 = *(const struct rout * const *)p2;
399
400 if (sflag) {
401 if (sp1->size < sp2->size)
402 return (1);
403 if (sp1->size > sp2->size)
404 return (-1);
405 } else {
406 if (sp1->num < sp2->num)
407 return (1);
408 if (sp1->num > sp2->num)
409 return (-1);
410 }
411 return (0);
412 }
413 int
comp_s(const void * p1,const void * p2)414 comp_s(const void *p1, const void *p2) {
415 const struct status * sp1 = *(const struct status * const *)p1;
416 const struct status * sp2 = *(const struct status * const *)p2;
417
418 if (sp1->num < sp2->num)
419 return (1);
420 if (sp1->num > sp2->num)
421 return (-1);
422 return (0);
423 }
424 int
comp_r(const void * p1,const void * p2)425 comp_r(const void *p1, const void *p2) {
426 const struct rule * sp1 = *(const struct rule * const *)p1;
427 const struct rule * sp2 = *(const struct rule * const *)p2;
428
429 if (sp1->num < sp2->num)
430 return (1);
431 if (sp1->num > sp2->num)
432 return (-1);
433 return (0);
434 }
435 int
comp_rrel(const void * p1,const void * p2)436 comp_rrel(const void *p1, const void *p2) {
437 const struct rrelay * sp1 = *(const struct rrelay * const *)p1;
438 const struct rrelay * sp2 = *(const struct rrelay * const *)p2;
439
440 if (sp1->num < sp2->num)
441 return (1);
442 if (sp1->num > sp2->num)
443 return (-1);
444 return (0);
445 }
446
447
448 /*
449 * Calculate the average daily/hour messages
450 */
451 void
average(struct host * hptr,int * dd,float * ad,int * hh,float * ah)452 average(struct host *hptr, int *dd, float *ad, int *hh, float *ah) {
453 int fulld;
454 int fullh;
455 int weeks;
456 int hours;
457 int hfday, hfhour;
458 int i;
459
460 hfday = hptr->fday;
461 hfhour = hptr->fhour;
462
463 /* integer value of hours: */
464 fullh = (int)hptr->dtime/3600;
465 fullh++;
466
467 /* integer value of days: */
468 fulld = (int)hptr->dtime/(24*3600);
469 fulld++;
470
471 /*
472 * calculate full weeks:
473 *
474 * days left over from last full week are returned
475 * as int fulld:
476 */
477 weeks = 0;
478 for (; fulld >= 7; fulld -= 7)
479 weeks++;
480
481 /* calculate daily averages: */
482
483 for (i = 0; i < 7; i++)
484 if (i == hfday && fulld > 0) {
485 ad[i] = (float)dd[i] / ((float)weeks + 1.0);
486 fulld--;
487 hfday++;
488 } else
489 if (weeks)
490 ad[i] = (float)dd[i] / (float)weeks;
491 else
492 ad[i] = (float)dd[i];
493
494
495 /* same calculations for hours: */
496
497 hours = 0;
498 for (; fullh >= 24; fullh -= 24)
499 hours++;
500 for (i = 0; i < 24; i++)
501 if (i == hfhour && fullh > 0) {
502 ah[i] = (float)hh[i] / ((float)hours + 1.0);
503 fullh--;
504 hfhour++;
505 } else
506 if (hours)
507 ah[i] = (float)hh[i] / (float)hours;
508 else
509 ah[i] = (float)hh[i];
510
511 }
512
513 /* Strip newline: */
514
515 char *
stripn(char * s)516 stripn(char *s) {
517 char *p;
518
519 for (p = s; *s != '\n'; s++)
520 ;
521 *s = '\0';
522 return p;
523 }
524
525 void
error_memory()526 error_memory() {
527 fprintf(stderr, "%s: memory allocation failure, errno: %d\n",
528 pname, errno);
529 exit(1);
530 }
531
532 void
copying()533 copying() {
534 fprintf(stderr, "\nSMA version %s\n\n" , VERSION);
535 fprintf(stderr, "This program is Copyright (c) 2000 - 2003\n"
536 " Jarkko Turkulainen. All rights reserved.\n"
537 "Some parts are "
538 "Copyright (c) 1992, 1993, 1994\n"
539 " Henry Spencer. All rights reserved.\n"
540 "Some parts are "
541 "Copyright (c) 1985, 1987, 1988, 1993, 1994\n"
542 " The Regents of the University of California. "
543 "All rights reserved.\n\n");
544 exit(0);
545 }
546
547 void
usage()548 usage() {
549 /* Use stdout - easier to "more" or "less" .. */
550 fprintf(stdout, "\nSMA version %s\n\n" , VERSION);
551 fprintf(stdout, "usage: %s [OPTIONS] [files...]\n"
552 "OPTIONS are\n"
553 " -A\t\tforce addresses to lower case to help with consistent counts\n"
554 " -a\t\tset the output format as ascii\n"
555 " -b <rgb>\tbackground color (RGB) of the HTML form\n"
556 " -c\t\tshow copyright information and exit\n"
557 " -C <string>\tset report header as <string>\n"
558 " -D <d1,d2>\tanalyse only log entries between dates d1 and d2\n"
559 " -d\t\tprint only the domain portion of email addresses\n"
560 " -f <file>\tuse configuration from <file> instead of default\n"
561 " -F\t\tdo not use default configuration file even if it exists\n"
562 " -h\t\tprint this text and exit\n"
563 " -H <name>\toverride the hostname with <name>\n"
564 " -i\t\tprint ASCII report as HTML comment (requires -w or -O html)\n"
565 " -L <string>\tprocess only lines with syslog tag <string>\n"
566 " -n\t\tdo not print the time distribution\n"
567 " -o <file>\twrite output to <file>\n"
568 " -O <format>\tset output format (ascii/html/clog)\n"
569 " -p\t\tprint current configuration to stdout\n"
570 " -s\t\tsort by tranfers\n"
571 " -q\t\tbe quiet (no error messages)\n"
572 " -v\t\tshow debugging information\n"
573 " -l <num>\tprint <num> top addresses\n"
574 " -r <num>\tprint <num> top relay addresses\n"
575 " -t <value>\tset the internal hash table size (normal/big/huge/a,r)\n"
576 " -w\t\tset the output format as html\n"
577 " files\tlog files\n\n", pname);
578 exit(0);
579 }
580
581 void
dump_config(FILE * handle)582 dump_config(FILE *handle) {
583 char *p = NULL;
584 char dates[128];
585
586 fprintf(handle, " BgColor %s\n", bchar);
587 fprintf(handle, " BounceAddress %s\n", bechar);
588 fprintf(handle, " CaseSensitive %s\n", csflag ? "no" : "yes");
589 if (cfchar)
590 fprintf(handle, " ClogFormat \"%s\"\n", cfchar);
591 fprintf(handle, " ClogSentOnly %s\n", clsflag ? "yes" : "no");
592 fprintf(handle, " Comment \"%s\"\n", Cchar);
593 fprintf(handle, " Debug %s\n", vflag ? "yes" : "no");
594 if (eetime) {
595 strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&eetime));
596 fprintf(handle, " EndTime %s\n", dates);
597 }
598 fprintf(handle, " EnvelopePairs %d\n", epnum);
599 fprintf(handle, " EnvelopeRecipientFilter %s\n", ref ? ref : "*");
600 fprintf(handle, " EnvelopeRecipients %d\n", lrnum);
601 fprintf(handle, " EnvelopeSenderFilter %s\n", sef ? sef : "*");
602 fprintf(handle, " EnvelopeSenders %d\n", lnum);
603 if (ftchar)
604 fprintf(handle, " FooterText \"%s\"\n", ftchar);
605 switch(Oflag) {
606 case FORMAT_ASCII:
607 if (!(p = strdup("ascii")))
608 error_memory();
609 break;
610 case FORMAT_HTML:
611 if (!(p = strdup("html")))
612 error_memory();
613 break;
614 case FORMAT_CLOG:
615 if (!(p = strdup("clog")))
616 error_memory();
617 break;
618 }
619 fprintf(handle, " Format %s\n", p);
620 free(p);
621 fprintf(handle, " HashTables %d,%d\n", asize, rsize);
622 if (htchar)
623 fprintf(handle, " HeaderText \"%s\"\n", htchar);
624 if (Hchar)
625 fprintf(handle, " HostName \"%s\"\n", Hchar);
626 fprintf(handle, " IncludeAscii %s\n", iflag ? "yes" : "no");
627 if (ochar)
628 fprintf(handle, " OutFile \"%s\"\n", ochar);
629 if (plchar)
630 fprintf(handle, " PictureLink \"%s\"\n", plchar);
631 if (ppchar)
632 fprintf(handle, " PictureParameters \"%s\"\n", ppchar);
633 if (pachar)
634 fprintf(handle, " PictureURL %s\n", pachar);
635 fprintf(handle, " PrintGeneralInfo %s\n", pgflag ? "yes" : "no");
636 fprintf(handle, " PrintRuleset %s\n", rsnum ? "yes" : "no");
637 fprintf(handle, " PrintStatus %s\n", stnum ? "yes" : "no");
638 fprintf(handle, " RelayPairs %d\n", rpnum);
639 fprintf(handle, " RelayRecipientFilter %s\n", rrf ? rrf : "*");
640 fprintf(handle, " RelayRecipients %d\n", rrnum);
641 fprintf(handle, " RelaySenderFilter %s\n", srf ? srf : "*");
642 fprintf(handle, " RelaySenders %d\n", rnum);
643 fprintf(handle, " RulesetRelays %d\n", rsrnum);
644 fprintf(handle, " ShowUsers %s\n", dflag ? "no" : "yes");
645 fprintf(handle, " Silent %s\n", qflag ? "yes" : "no");
646 fprintf(handle, " Sorting %s\n", sflag ? "transfer" : "number");
647 if (sstime) {
648 strftime(dates, 127, "%Y/%m/%d-%H:%M:%S", localtime(&sstime));
649 fprintf(handle, " StartTime %s\n", dates);
650 }
651 if (Lchar)
652 fprintf(handle, " SyslogTag %s\n", Lchar);
653 fprintf(handle, " TbColor %s\n", tbchar);
654 }
655