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/09/12 10:46:00 $
30  */
31 
32 extern const char *sma_optarg;
33 extern int sma_optind;
34 
35 #include "sma.h"
36 
37 /* pointer to program name: */
38 char *pname;
39 
40 /* current time: */
41 time_t tval;
42 struct tm *curr;
43 struct tm tp;
44 
45 /*
46  * Command line arguments
47  * xflag sets argument x on/off
48  * xchar is pointer to argument string if x requires an argument
49  */
50 int aflag;
51 int cflag;
52 int dflag;
53 int hflag;
54 int nflag;
55 int sflag;
56 int qflag;
57 int lflag;
58 int vflag;
59 int wflag;
60 unsigned int lnum;
61 unsigned int lrnum;
62 int rflag;
63 unsigned int rnum;
64 unsigned int rrnum;
65 int bflag;
66 const char *bchar;
67 int fflag;
68 const char *fchar;
69 int oflag;
70 const char *ochar;
71 int Lflag;
72 const char *Lchar;
73 int Oflag;
74 const char *Ochar;
75 int Dflag;
76 const char *Dchar;
77 int pflag;
78 int iflag;
79 int tflag;
80 int dcaddrflag;
81 const char *tchar;
82 
83 /* Configuration file parameters: */
84 int Hflag;
85 const char *Hchar;
86 int Cflag;
87 const char *Cchar;
88 int Fflag;
89 const char *tbchar;
90 int pgflag;
91 const char *bechar;
92 const char *cfchar;
93 const char *puchar;
94 const char *pachar;
95 const char *plchar;
96 const char *ppchar;
97 const char *htchar;
98 const char *ftchar;
99 int csflag;
100 int lrflag;
101 int rrflag;
102 int clsflag;
103 unsigned int stnum;
104 unsigned int rsnum;
105 unsigned int rsrnum;
106 unsigned int epnum;
107 unsigned int rpnum;
108 
109 /* Start and end times: */
110 char *sstring;
111 char *estring;
112 char *tstring;
113 time_t sstime;
114 time_t eetime;
115 int syear;
116 int smonth;
117 int sday;
118 int shour;
119 int sminute;
120 int ssecond;
121 int eyear;
122 int emonth;
123 int eday;
124 int ehour;
125 int eminute;
126 int esecond;
127 
128 /* hash table sizes: */
129 int asize;
130 int rsize;
131 char *hsstring;
132 char *hastring;
133 char *hrstring;
134 
135 /* Filters */
136 char *sef;
137 char *ref;
138 char *srf;
139 char *rrf;
140 #ifdef USE_REGEXP
141 regex_t csef;
142 regex_t cref;
143 regex_t csrf;
144 regex_t crrf;
145 #endif
146 
147 /* Output file handle: */
148 FILE *ofp;
149 
150 /* total number of hosts: */
151 int hosts;
152 
153 /* inital host structure: */
154 struct host first;
155 
156 int
main(int argc,char ** argv)157 main(int argc, char **argv) {
158 	FILE *fp = NULL;
159 	struct host *hptr;
160 	int c;
161 
162 #ifdef _WIN32
163 	int getopt(int, char **, char *);
164 #endif
165 
166 	/* initialize global variables: */
167 	hosts = 0;
168 	tval = time((time_t *)NULL);
169 	curr = localtime(&tval);
170 	first.next = NULL;
171 	pname = argv[0];
172 
173 	/* get arguments: */
174 	while ((c = getopt(argc, argv, "C:D:H:L:b:f:l:r:t:o:O:AFacdhinpsqvw")) != -1)
175 		switch(c) {
176 			case 'A':
177 				dcaddrflag = 1;
178 				break;
179 			case 'D':
180 				Dflag = 1;
181 				Dchar = sma_optarg;
182 				break;
183 			case 'C':
184 				Cflag = 1;
185 				Cchar = sma_optarg;
186 				break;
187 			case 'F':
188 				Fflag = 1;
189 				break;
190 			case 'H':
191 				Hflag = 1;
192 				Hchar = sma_optarg;
193 				break;
194 			case 'a':
195 				aflag = 1;
196 				break;
197 			case 'b':
198 				bflag = 1;
199 				bchar = sma_optarg;
200 				break;
201 			case 'c':
202 				cflag = 1;
203 				break;
204 			case 'd':
205 				dflag = 1;
206 				break;
207 			case 'f':
208 				fflag = 1;
209 				fchar = sma_optarg;
210 				break;
211 			case 'h':
212 				hflag = 1;
213 				break;
214 			case 'i':
215 				iflag = 1;
216 				break;
217 			case 'l':
218 				lflag = 1;
219 				lnum = atoi(sma_optarg);
220 				break;
221 			case 'L':
222 				Lflag = 1;
223 				Lchar = sma_optarg;
224 				break;
225 			case 'r':
226 				rflag = 1;
227 				rnum = atoi(sma_optarg);
228 				break;
229 			case 'n':
230 				nflag = 1;
231 				break;
232 			case 'o':
233 				oflag = 1;
234 				ochar = sma_optarg;
235 				break;
236 			case 'O':
237 				Oflag = 1;
238 				Ochar = sma_optarg;
239 				break;
240 			case 'p':
241 				pflag = 1;
242 				break;
243 			case 's':
244 				sflag = 1;
245 				break;
246 			case 't':
247 				tflag = 1;
248 				tchar = sma_optarg;
249 				break;
250 			case 'q':
251 				qflag = 1;
252 				break;
253 			case 'v':
254 				vflag = 1;
255 				break;
256 			case 'w':
257 				wflag = 1;
258 				break;
259 			default:
260 				usage();
261                 }
262         argc -= sma_optind;
263         argv += sma_optind;
264 
265         /* Debug */
266 	if (vflag)
267 		fprintf(stderr, "%s: running in Debug mode\n", pname);
268 
269 	/* Check argument logic: */
270 
271 	if (hflag) usage();
272 	if (cflag) copying();
273 	if (!pgflag) pgflag = 1;
274 
275 	/* Read in the configuration file: */
276 	if (fchar) {
277 		if (!(fp = fopen(fchar, "r")))
278 			fprintf(stderr, "%s: cannot open %s\n", pname, fchar);
279 		else {
280 			init(fp);
281 			(void)fclose(fp);
282 		}
283 	} else if (!Fflag) {
284 		if (!(fp = fopen(DEFAULT_CONF, "r")))
285 			fprintf(stderr, "%s: cannot open %s, using defaults\n",
286 				pname, "configuration file");
287 		else {
288 			fchar = DEFAULT_CONF;
289 			init(fp);
290 			(void)fclose(fp);
291 		}
292 	}
293 
294 	/* Hash table size: */
295 	if (!tflag || !strncmp(tchar, "normal", 6)) {
296 		asize = ASIZE_NORMAL;
297 		rsize = RSIZE_NORMAL;
298 	} else if (!strncmp(tchar, "big", 3)) {
299 		asize = ASIZE_BIG;
300 		rsize = RSIZE_BIG;
301 	} else if (!strncmp(tchar, "huge", 4)) {
302 		asize = ASIZE_HUGE;
303 		rsize = RSIZE_HUGE;
304 	} else {
305 		if (!(hsstring = strdup(tchar)))
306 			error_memory();
307 		hastring = hsstring;
308 		if (*hsstring == ',') {
309 			hastring = NULL;
310 			if (*(hsstring + 1) == '\0')
311 				hrstring = NULL;
312 			else
313 				hrstring = ++hsstring;
314 		}
315 		while (*hsstring != '\0') {
316 			if (*hsstring == ',') {
317 				*hsstring = '\0';
318 				if (*(hsstring + 1) == '\0')
319 					hrstring = NULL;
320 				else
321 					hrstring = ++hsstring;
322 				break;
323 			}
324 			hsstring++;
325 		}
326 		/* Address Hash table: */
327 		if (hastring != NULL)
328 			asize = atoi(hastring);
329 		else asize = ASIZE_NORMAL;
330 		/* Relay Hash table: */
331 		if (hrstring != NULL)
332 			rsize = atoi(hrstring);
333 		else rsize = RSIZE_NORMAL;
334 
335 	}
336 	if (asize <= 0 || rsize <= 0) {
337 		fprintf(stderr, "%s: Illegal hash table size\n", pname);
338 		usage();
339 	}
340 
341 	if (!Oflag) Oflag = FORMAT_ASCII;
342 	else if (!strncmp(Ochar, "ascii", 5)) Oflag = FORMAT_ASCII;
343 	else if (!strncmp(Ochar, "html", 4)) Oflag = FORMAT_HTML;
344 	else if (!strncmp(Ochar, "clog", 4)) Oflag = FORMAT_CLOG;
345 	else {
346 		fprintf(stderr, "%s: illegal output format \"%s\".\n",
347 			pname, Ochar);
348 		exit (1);
349 	}
350 	if (aflag) Oflag = FORMAT_ASCII;
351 	if (wflag) Oflag = FORMAT_HTML;
352 
353 	if (!Cchar) Cchar = COMMENT;
354 	if (aflag) wflag = 0;
355 	if (!bchar) bchar = BG_COLOR;
356 	if (!tbchar) tbchar = TB_COLOR;
357 	if (!bechar) bechar = BOUNCE_ADDR;
358 
359 	if (lflag) {
360 		if (lnum <= 0) lnum = 0;
361 		lrnum = lnum;
362 	} else if (!lrflag) {
363 		lnum = LDEF;
364 		if (lnum <= 0) lnum = 0;
365 		lrnum = LRDEF;
366 		if (lrnum <= 0) lrnum = 0;
367 	}
368 	if (rflag) {
369 		if (rnum <= 0) rnum = 0;
370 		rrnum = rnum;
371 	} else if (!rrflag) {
372 		rnum = RDEF;
373 		if (rnum <= 0) rnum = 0;
374 		rrnum = RRDEF;
375 		if (rrnum <= 0) rrnum = 0;
376 	}
377 
378 	/* Set output: */
379 	if (ochar) {
380 		if (!(ofp = fopen(ochar, "w"))) {
381 			fprintf(stderr, "%s: cannot open %s\n", pname, ochar);
382 			ofp = stdout;
383 		}
384 	} else
385 		ofp = stdout;
386 
387 	/* Check filter logic: */
388 
389 	/* Envelope and relay filters: */
390 	if (sef || srf || ref || rrf) {
391 		if (!qflag) fprintf(stderr, "%s: setting filters\n", pname);
392 #ifdef USE_REGEXP
393 		if (csflag)
394 			csflag = REG_EXTENDED|REG_NOSUB;
395 		else
396 			csflag = REG_EXTENDED|REG_ICASE|REG_NOSUB;
397 #endif
398 	}
399 
400 	if (!sef || !strncmp(sef, "*", 1)) {
401 		sef = (char *)NULL;
402 	} else {
403 #ifdef USE_REGEXP
404 		if (*sef == '!') {
405 			if (*(sef+1) == '\0')
406 				sef = NULL;
407 			regcomp(&csef, sef+1, csflag);
408 		} else
409 			regcomp(&csef, sef, csflag);
410 #else
411 		if (*sef == '!' && *(sef+1) == '\0')
412 			sef = (char *)NULL;
413 #endif
414 		if (!qflag) fprintf(stderr,
415 		"  envelope sender filter: %s\n", sef);
416 	}
417 
418 	if (!srf || !strncmp(srf, "*", 1)) srf = (char *)NULL;
419 	else {
420 #ifdef USE_REGEXP
421 		if (*srf == '!') {
422 			if (*(srf+1) == '\0')
423 				srf = NULL;
424 			regcomp(&csrf, srf+1, csflag);
425 		} else
426 			regcomp(&csrf, srf, csflag);
427 #else
428 		if (*srf == '!' && *(srf+1) == '\0')
429 			srf = (char *)NULL;
430 #endif
431 		if (!qflag) fprintf(stderr,
432 		"  relay sender filter: %s\n", srf);
433 	}
434 
435 	if (!ref || !strncmp(ref, "*", 1)) ref = (char *)NULL;
436 	else {
437 #ifdef USE_REGEXP
438 		if (*ref == '!')
439 			regcomp(&cref, ref+1, csflag);
440 		else
441 			regcomp(&cref, ref, csflag);
442 #else
443 		if (*ref == '!' && *(ref+1) == '\0')
444 			ref = (char *)NULL;
445 
446 #endif
447 		if (!qflag) fprintf(stderr,
448 		"  envelope recipient filter: %s\n", ref);
449 	}
450 
451 	if (!rrf || !strncmp(rrf, "*", 1)) rrf = (char *)NULL;
452 	else {
453 #ifdef USE_REGEXP
454 		if (*rrf == '!')
455 			regcomp(&crrf, rrf+1, csflag);
456 		else
457 			regcomp(&crrf, rrf, csflag);
458 #else
459 		if (*rrf == '!' && *(rrf+1) == '\0')
460 			rrf = (char *)NULL;
461 #endif
462 		if (!qflag) fprintf(stderr,
463 		"  relay recipient filter: %s\n", rrf);
464 	}
465 
466 	/* Start -and end times: */
467 	if (Dflag) {
468 		/* Read from command line.. */
469 		if (!(sstring = strdup(Dchar)))
470 			error_memory();
471 		tstring = sstring;
472 		if (*sstring == ',') {
473 			tstring = NULL;
474 			if (*(sstring + 1) == '\0')
475 				estring = NULL;
476 			else
477 				estring = ++sstring;
478 		}
479 		while (*sstring != '\0') {
480 			if (*sstring == ',') {
481 				*sstring = '\0';
482 				if (*(sstring + 1) == '\0')
483 					estring = NULL;
484 				else
485 					estring = ++sstring;
486 				break;
487 			}
488 			sstring++;
489 		}
490 		/* StartTime: */
491 		if (tstring != NULL)
492 			scan_time(&sstime, tstring);
493 		/* EndTime: */
494 		if (estring != NULL)
495 			scan_time(&eetime, estring);
496 	} else {
497 		if (!sstring || !(strncmp(sstring, "*", 1)))
498 			sstring = NULL;
499 		else if (sscanf(sstring, "%d/%d/%d-%d:%d:%d",
500 	  	  &syear, &smonth, &sday, &shour, &sminute, &ssecond) != 6) {
501 			sstring = NULL;
502 			if (!qflag) {
503 				fprintf(stderr, "%s: illegal StartTime, "
504 				  "using (*)\n", pname);
505 			}
506 		} else {
507 			sstime = (time_t)conv_time(smonth-1, sday, shour,
508 				sminute, ssecond);
509 			if (!sstime && !qflag)
510 				fprintf(stderr, "%s: illegal StartTime, "
511 				  "using (*)\n", pname);
512 		}
513 		if (!estring || !(strncmp(estring, "*", 1)))
514 			estring = NULL;
515 		else if (sscanf(estring, "%d/%d/%d-%d:%d:%d",
516 		  &eyear, &emonth, &eday, &ehour, &eminute, &esecond) != 6) {
517 			if (!qflag) {
518 				fprintf(stderr, "%s: illegal EndTime, "
519 				  "using (*)\n", pname);
520 			}
521 		} else {
522 			eetime = (time_t)conv_time(emonth-1, eday, ehour,
523 				eminute, esecond);
524 			if (!eetime && !qflag)
525 				fprintf(stderr, "%s: illegal EndTime, "
526 				  "using (*)\n", pname);
527 		}
528 	}
529 	/* Print out configuration to stdout: */
530 	if (pflag) {
531 		fprintf(stderr, "%s: configuration:\n", pname);
532 		dump_config(stdout);
533 		exit (0);
534 	}
535 
536 	/* Print configuration in debug mode: */
537 	if (vflag) {
538 		fprintf(stderr, "%s: configuration:\n", pname);
539 		dump_config(stderr);
540 	}
541 
542 	/* loop through remaining arguments (if any) and do parsing: */
543 	if (*argv)
544 		for (; *argv; ++argv) {
545 			if (!qflag) fprintf(stderr,
546 				"%s: opening file %s\n", pname, *argv);
547 			if (!(fp = fopen(*argv, "r")))
548 				fprintf(stderr,
549 					"%s: cannot open %s\n", pname, *argv);
550 			else {
551 				parse(fp, *argv);
552 				(void)fclose(fp);
553 			}
554 		}
555 	else {
556 		if (!qflag)
557 			fprintf(stderr, "%s: reading from stdin\n", pname);
558 		parse(stdin, "stdin");
559 	}
560 
561 	/* If output format is clog, nothing to do here: */
562 	if (Oflag == FORMAT_CLOG)  exit (0);
563 
564 	/* check host structure: */
565 	hptr = first.next;
566 	if (!hptr || !(hptr->inum) || !(hptr->onum)) {
567 		if (!qflag)
568 			fprintf(stderr, "%s: nothing to report\n", pname);
569 		exit(1);
570 	}
571 	for (hptr = first.next; hptr; hptr = hptr->next) {
572 
573 		/* rearrange and sort: */
574 		sort(hptr);
575 
576 		/* seconds between first and last log entry: */
577 		hptr->dtime = difftime(hptr->ltime, hptr->ftime);
578 
579 		/* first hour and day: */
580 		hptr->fday = (localtime(&hptr->ftime))->tm_wday;
581 		hptr->fhour = (localtime(&hptr->ftime))->tm_hour;
582 
583 		/* time distributions: */
584 		average(hptr, hptr->idd, hptr->fidd,
585 			hptr->ihh, hptr->fihh);
586 		average(hptr, hptr->odd, hptr->fodd,
587 			hptr->ohh, hptr->fohh);
588 	}
589 
590 	/* print: */
591 	if (Oflag == FORMAT_HTML) html(ofp);
592 	else if (Oflag == FORMAT_ASCII) ascii(ofp);
593 	(void)fclose(ofp);
594 
595 	/* OK: */
596 	exit(0);
597 }
598