1 /*
2 ** Modular Logfile Analyzer
3 ** Copyright 2000 Jan Kneschke <jan@kneschke.de>
4 **
5 ** Homepage: http://www.modlogan.org
6 **
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version, and provided that the above
12     copyright and permission notice is included with all distributed
13     copies of this or derived software.
14 
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19 
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
23 
24 **
25 ** $Id: generate.c,v 1.27 2003/06/03 16:35:52 ostborn Exp $
26 */
27 
28 #include <libintl.h>
29 #include <locale.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <time.h>
33 #include <math.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 
38 #include "mconfig.h"
39 #include "mstate.h"
40 #include "mlocale.h"
41 #include "mhash.h"
42 #include "mlist.h"
43 #include "mdatatypes.h"
44 #include "mplugins.h"
45 
46 #include "plugin_config.h"
47 
48 #if 0
49 #define DEBUG_STRREP
50 #endif
51 
52 int mplugins_output_text_ippl_generate_monthly_output(mconfig *ext_conf, mstate *state, const char *subpath);
53 int mplugins_output_text_mail_generate_monthly_output(mconfig *ext_conf, mstate *state, const char *subpath);
54 char *put_gap_before( float );
55 int show_data_stat_ippl(mconfig *ext_conf, FILE *f, mhash *h, int count, int formlenght);
56 mdata_ipplwatchelement **sort_ipplwatchelements( mdata_ipplwatchelement **src, int num);
57 
58 mdata_ipplwatchelement **sort_ipplwatchelements( mdata_ipplwatchelement **src, int num ) {
59 	int i, ii;
60 	mdata_ipplwatchelement **tosort, **sorted;
61 
62 	if (num<2)
63 		return src;
64 
65 	tosort = malloc( sizeof(mdata_ipplwatchelement *) * num);
66 	memcpy( tosort, src, num * sizeof(mdata_ipplwatchelement *));
67 	sorted = malloc( sizeof(mdata_ipplwatchelement *) * num);
68 
69 	for (ii=0; ii<num; ii++) {
70 		int elemaxnum = -1;
71 		long countnum = -1;
72 
73 		for (i=0; i<num; i++) {
74 			if (tosort[i] != NULL &&
75 			    tosort[i]->count >= countnum) {
76 				countnum = tosort[i]->count;
77 				elemaxnum = i;
78 			}
79 		}
80 
81 		if (elemaxnum > -1) {
82 			sorted[ii] = tosort[elemaxnum];
83 			tosort[elemaxnum] = NULL;
84 		} else {
85 			fprintf(stderr, "%s.%d: Fatal error: something screwed up in sort!\n",
86 				__FILE__, __LINE__ );
87 		}
88 	}
89 
90 	return sorted;
91 }
92 
93 int mlist_sumup(mlist *l) {
94 	int c = 0;
95 	if (!l) return 0;
96 
97 	while (l) {
98 		if (l->data) {
99 			c += mdata_get_count(l->data);
100 		}
101 		l = l->next;
102 	}
103 
104 	return c;
105 }
106 
107 long mhash_sumup(mhash *h) {
108 	int i, c = 0;
109 	if (!h) return 0;
110 
111 	for ( i = 0; i < h->size; i++) {
112 		c += mlist_sumup(h->data[i]->list);
113 	}
114 
115 	return c;
116 }
117 
118 mlist * get_next_element(mhash *h) {
119 	mlist *ret = NULL;
120 	int max = 0, i;
121 
122 	for ( i = 0; i < h->size; i++) {
123 		mlist *l = h->data[i]->list;
124 		while (l) {
125 			if (l->data) {
126 				mdata *data = l->data;
127 
128 				if ( mdata_get_count(data) > max) {
129 					max = mdata_get_count(data);
130 					ret = l;
131 				}
132 			}
133 			l = l->next;
134 		}
135 	}
136 
137 	if (ret) {
138 		mdata_set_count(ret->data, -mdata_get_count(ret->data));
139 	}
140 
141 	return ret;
142 }
143 
144 int cleanup_elements(mhash *h) {
145 	int i;
146 	for ( i = 0; i < h->size; i++) {
147 		mlist *l = h->data[i]->list;
148 		while (l) {
149 			if (l->data) {
150 				mdata *data = l->data;
151 
152 				mdata_set_count(data, -mdata_get_count(data));
153 
154 			}
155 			l = l->next;
156 		}
157 	}
158 
159 	return 0;
160 }
161 
162 int show_visit_path (mconfig *ext_conf, FILE *f, mhash *h, int count) {
163 	int i = 0;
164 	mlist *l;
165 	long sum;
166 
167 	if (!h) return 0;
168 
169 	sum = mhash_sumup(h);
170 
171 	while ((l = get_next_element(h)) && i < count) {
172 		if (l->data) {
173 			mdata *data = l->data;
174 			int c = -mdata_get_count(data);
175 
176 			i++;
177 
178 			fprintf(f,"%2d %8d %6.2f %s\n", i, c, c * 100.0 / sum, data->key);
179 		}
180 	}
181 
182 	cleanup_elements(h);
183 
184 	return 0;
185 }
186 
187 int show_port_stat_ippl(mconfig *ext_conf, FILE *f, mhash *h, int count) {
188 	return show_data_stat_ippl( ext_conf, f, h, count, 5 );
189 }
190 
191 int show_host_stat_ippl(mconfig *ext_conf, FILE *f, mhash *h, int count) {
192 	return show_data_stat_ippl( ext_conf, f, h, count, 15 );
193 }
194 
195 int show_data_stat_ippl(mconfig *ext_conf, FILE *f, mhash *h, int count, int formlength) {
196 	int i = 0;
197 	mlist *l;
198 	long sum;
199 
200 	if (!h) return 0;
201 
202 	sum = mhash_sumup(h);
203 
204 	while ((l = get_next_element(h)) && i < count) {
205 		if (l->data) {
206 			mdata *data = l->data;
207 			int c = -mdata_get_count(data);
208 
209 			i++;
210 
211 			fprintf(f, "| %2d | %8d | %s%3.2f | %*s |\n", i, c,
212 				put_gap_before( c * 100.0 / sum ),
213 				c * 100.0 / sum, formlength, data->key);
214 		}
215 	}
216 
217 	cleanup_elements(h);
218 
219 	return 0;
220 }
221 
222 int mplugins_output_text_generate_monthly_output(mconfig *ext_conf, mstate *state, const char *subpath) {
223 	if (state == NULL) return -1;
224 	if (state->ext == NULL) return -1;
225 
226 	switch (state->ext_type ) {
227 	case M_STATE_TYPE_MAIL:
228 		return mplugins_output_text_mail_generate_monthly_output(ext_conf, state, subpath);
229 	case M_STATE_TYPE_IPPL:
230 		return mplugins_output_text_ippl_generate_monthly_output(ext_conf, state, subpath);
231 	default:
232 		return -1;
233 	}
234 }
235 
236 char* strrep( char *torep, int num ) {
237 	int resize;
238 	char *result;
239 
240 	if (!num) return NULL;
241 
242 	if (num==1) {
243 		return strdup( torep );
244 	}
245 
246 #ifdef DEBUG_STRREP
247 	fprintf( stderr, "Calculating result size..." );
248 #endif
249 	resize = num * strlen( torep ) + 1;
250 #ifdef DEBUG_STRREP
251 	fprintf( stderr, "done, %d.\nAllocating result memory and filling w/ original content...", resize );
252 #endif
253 	/* we use strn* because we don't want buffer overrun for
254 	 * extremely _long_ strings.. (Miham) */
255 	result = strncpy( malloc( resize ), torep, (resize-1) / num );
256 	result[1] = 0;
257 #ifdef DEBUG_STRREP
258 	fprintf( stderr, "done, '%s'.\nEntering while-loop...", result );
259 #endif
260 
261 	while( --num ) {
262 #ifdef DEBUG_STRREP
263 		fprintf( stderr, "done.\nConcatenating to '%s'...", result );
264 #endif
265 		result = strncat( result, torep, (resize-1) / num );
266 	}
267 
268 #ifdef DEBUG_STRREP
269 	fprintf( stderr, "done.\nResult is '%s'.\nReturning result and leaving strrep...\n", result );
270 #endif
271 	return result;
272 }
273 
274 char *put_gap_before( float percent ) {
275 	if (percent < 10.0 )
276 		return "  ";
277 	if (percent < 100.0)
278 		return " ";
279 	return "";
280 }
281 
282 int mplugins_output_text_ippl_generate_monthly_output(mconfig *ext_conf, mstate *state, const char *subpath) {
283 	FILE *f = NULL;
284 	char filename[255];
285 	config_output *conf = ext_conf->plugin_conf;
286 	mstate_ippl *staipl = NULL;
287 	marray_ippl sum;
288 	int i;
289 	int pad;
290 	char *padstr;
291 	long psum;
292 
293 	if (state == NULL) return -1;
294 	if (state->ext == NULL) return -1;
295 	if (state->ext_type != M_STATE_TYPE_IPPL) return -1;
296 
297 	staipl = state->ext;
298 
299 	if (subpath) {
300 		sprintf(filename, "%s/%s/",
301 			conf->outputdir ? conf->outputdir : ".",
302 			subpath);
303 		mkdir(filename, 0755);
304 	}
305 
306 	sprintf(filename, "%s%s%s/index-%04d%02d.txt",
307 		conf->outputdir ? conf->outputdir : ".",
308 		subpath ? "/" : "",
309 		subpath ? subpath : "",
310 		state->year, state->month
311 		);
312 
313 	if (!(f = fopen(filename, "w"))) {
314 		return -1;
315 	}
316 
317 	/* Main title */
318 	pad = 80 - 19 - strlen( conf->hostname );
319 	if (pad > 1) {
320 		pad /= 2;
321 		padstr = strrep( strdup(" "), pad);
322 	} else {
323 		padstr = 0;
324 	}
325 
326 	fprintf(f, "\n%s+----------------%s-+\n", padstr, strrep( strdup("-"), strlen( conf->hostname ) ) );
327 	fprintf(f, "%s| ippl-stats for %s |\n", padstr, conf->hostname);
328 	fprintf(f, "%s+----------------%s-+\n\n", padstr, strrep( strdup("-"), strlen( conf->hostname ) ) );
329 
330 	/* hourly stat */
331 	sum.packets = 0;
332 	sum.hosts = 0;
333 	sum.ports = 0;
334 	sum.portscannum = 0;
335 
336 	fprintf(f, "\n+------------------------------------------------+\n");
337 	fprintf(f, "| Hourly statistics for packets, hosts and ports |\n");
338 	fprintf(f, "+-------+----------+----------+----------+-------+--+\n");
339 	fprintf(f,"| %5s | %8s | %8s | %8s | %8s |\n",
340 		"hour",
341 		"packets",
342 		"hosts",
343 		"ports",
344 		"portscan");
345 	fprintf(f, "+-------+----------+----------+----------+----------+\n");
346 	for ( i = 0; i < 24; i++) {
347 		fprintf(f,"| %5d | %8ld | %8ld | %8ld | %8ld |\n",
348 			i,
349 			staipl->hours[i].packets,
350 			staipl->hours[i].hosts,
351 			staipl->hours[i].ports,
352 			staipl->hours[i].portscannum
353 			);
354 		sum.packets += staipl->hours[i].packets;
355 		sum.hosts += staipl->hours[i].hosts;
356 		sum.ports += staipl->hours[i].ports;
357 		sum.portscannum += staipl->hours[i].portscannum;
358 	}
359 
360 	fprintf(f, "+-------+----------+----------+----------+----------+\n");
361 	fprintf(f,"| %5s | %8ld | %8ld | %8ld | %8ld |\n",
362 		"TOTAL",
363 		sum.packets,
364 		sum.hosts,
365 		sum.ports,
366 		sum.portscannum
367 		);
368 
369 	fprintf(f, "+-------+----------+----------+----------+----------+\n\n\n");
370 
371 	sum.packets = 0;
372 	sum.hosts = 0;
373 	sum.ports = 0;
374 	sum.portscannum = 0;
375 
376 	/* Daily stat */
377 	fprintf(f, "+-----------------------------------------------+\n");
378 	fprintf(f, "| Daily statistics for packets, hosts and ports |\n");
379 	fprintf(f, "+-------+----------+----------+----------+------+---+\n");
380 	fprintf(f,"| %5s | %8s | %8s | %8s | %8s |\n",
381 		"day",
382 		"packets",
383 		"hosts",
384 		"ports",
385 		"portscan");
386 	for ( i = 0; i < 31; i++) {
387 		fprintf(f,"| %5d | %8ld | %8ld | %8ld | %8ld |\n",
388 			i,
389 			staipl->days[i].packets,
390 			staipl->days[i].hosts,
391 			staipl->days[i].ports,
392 			staipl->days[i].portscannum
393 			);
394 		sum.packets += staipl->days[i].packets;
395 		sum.hosts += staipl->days[i].hosts;
396 		sum.ports += staipl->days[i].ports;
397 		sum.portscannum += staipl->days[i].portscannum;
398 	}
399 
400 	fprintf(f, "+-------+----------+----------+----------+----------+\n");
401 	fprintf(f,"| %5s | %8ld | %8ld | %8ld | %8ld |\n",
402 		"TOTAL",
403 		sum.packets,
404 		sum.hosts,
405 		sum.ports,
406 		sum.portscannum
407 		);
408 	fprintf(f, "+-------+----------+----------+----------+----------+\n\n\n");
409 
410 	/* packettypes */
411 	fprintf(f, "+-----------------+\n");
412 	fprintf(f, "| Packets by type |\n");
413 	psum = staipl->protocols.icmp +
414 		    staipl->protocols.udp +
415 		    staipl->protocols.tcp +
416 		    staipl->protocols.other;
417 	fprintf(f, "+-------+---------+--------+\n");
418 	fprintf(f, "| Type  |  Number |      %% |\n");
419 	fprintf(f, "+-------+---------+--------+\n");
420 	fprintf(f, "| %5s | %7ld | %s%3.2f |\n", "ICMP", staipl->protocols.icmp,
421 			put_gap_before( 100.0 * staipl->protocols.icmp / psum ),
422 			100.0 * staipl->protocols.icmp / psum);
423 	fprintf(f, "| %5s | %7ld | %s%3.2f |\n", "TCP", staipl->protocols.tcp,
424 			put_gap_before( 100.0 * staipl->protocols.tcp / psum ),
425 			100.0 * staipl->protocols.tcp / psum);
426 	fprintf(f, "| %5s | %7ld | %s%3.2f |\n", "UDP", staipl->protocols.udp,
427 			put_gap_before( 100.0 * staipl->protocols.udp / psum ),
428 			100.0 * staipl->protocols.udp / psum);
429 	fprintf(f, "| %5s | %7ld | %s%3.2f |\n", "OTHER", staipl->protocols.other,
430 			put_gap_before( 100.0 * staipl->protocols.other / psum ),
431 			100.0 * staipl->protocols.other / psum);
432 	fprintf(f, "+-------+---------+--------+\n\n\n");
433 
434 	/* ipopts stat */
435 	fprintf(f, "+--------------------+\n");
436 	fprintf(f, "| Packets by IP opts |\n");
437 	psum = staipl->ipopts.has_ipopts + staipl->ipopts.has_no_ipopts;
438 	fprintf(f, "+-------------+------+--+--------+\n");
439 	fprintf(f, "| Has IP-opts |  Number |      %% |\n");
440 	fprintf(f, "+-------------+---------+--------+\n");
441 	fprintf(f, "| %11s | %7ld | %s%3.2f |\n", "Yes", staipl->ipopts.has_ipopts,
442 			put_gap_before( 100.0 * staipl->ipopts.has_ipopts / psum ),
443 			100.0 * staipl->ipopts.has_ipopts / psum);
444 	fprintf(f, "| %11s | %7ld | %s%3.2f |\n", "No", staipl->ipopts.has_no_ipopts,
445 			put_gap_before( 100.0 * staipl->ipopts.has_no_ipopts / psum ),
446 			100.0 * staipl->ipopts.has_no_ipopts / psum);
447 	fprintf(f, "+-------------+---------+--------+\n\n\n");
448 
449 	/* source hosts */
450 	fprintf(f, "+--------------+\n");
451 	fprintf(f, "| source hosts |\n");
452 	fprintf(f, "+----+---------++--------+-----------------+\n");
453 	fprintf(f, "|  # | %8s |      %% | %15s |\n", "number", "IP" );
454 	fprintf(f, "+----+----------+--------+-----------------+\n");
455 	show_host_stat_ippl(ext_conf, f, staipl->source_ips, 20);
456 	fprintf(f, "+----+----------+--------+-----------------+\n\n\n");
457 
458 	/* destination ports */
459 	fprintf(f, "+-------------------+\n");
460 	fprintf(f, "| destination ports |\n");
461 	fprintf(f, "+----+----------+---+----+-------+\n");
462 	fprintf(f, "|  # | %8s |      %% | %5s |\n", "number", "port" );
463 	fprintf(f, "+----+----------+--------+-------+\n");
464 	show_port_stat_ippl(ext_conf, f, staipl->destination_ports, 20);
465 	fprintf(f, "+----+----------+--------+-------+\n\n\n");
466 
467 	/* fully remembered source hosts, if any */
468 	if (mhash_count( staipl->watched_shosts )) {
469 		int wi;
470 
471 		mdata **watches = mhash_sorted_to_marray(staipl->watched_shosts,M_SORTBY_KEY,M_SORTDIR_ASC);
472 
473 		fprintf(f, "+----------------------+\n");
474 		fprintf(f, "| Watched source hosts |\n");
475 		fprintf(f, "+-------+--------------+----------------------+\n");
476 		fprintf(f, "| # NUM |                         Source host |\n");
477 		fprintf(f, "+-------+--------------------------+----------+\n");
478 		fprintf(f, "|  Port | Last timestamp           |    Count |\n");
479 		fprintf(f, "+=======+==========================+==========+\n");
480 
481 		for (wi = 0; watches[wi] != NULL; wi++) {
482 			int wj;
483 			char *ctimedata;
484 			mdata *actw = watches[wi];
485 			mdata_ipplwatchelement **actcs = sort_ipplwatchelements(actw->data.ipplwatch->watches, actw->data.ipplwatch->count);
486 
487 			fprintf(f, "| %4d. | %35s |\n", wi+1, actw->key );
488 			fprintf(f, "+-------+--------------------------+----------+\n");
489 
490 
491 			for (wj = 0;
492 			     wj < actw->data.ipplwatch->count && actcs[wj] != NULL;
493 			     wj++ ) {
494 				ctimedata = malloc(45);
495 				if( !strftime(ctimedata, 44, "%c", localtime( &(actcs[wj]->timestamp) ) ) ) {
496 					fprintf(stderr, "%s.%d: Time formating failed!\n", __FILE__, __LINE__);
497 				}
498 
499 				fprintf(f, "| %5s | %24s | %8ld |\n", actcs[wj]->data,
500 					ctimedata, actcs[wj]->count );
501 				free(ctimedata);
502 			}
503 			free(actcs);
504 			fprintf(f, "+=======+==========================+==========+\n");
505 		}
506 		fprintf(f,"\n\n");
507 	}
508 
509 	/* fully remembered destination ports, if any */
510 	if (mhash_count( staipl->watched_dports )) {
511 		int wi;
512 
513 		mdata **watches = mhash_sorted_to_marray(staipl->watched_dports,M_SORTBY_KEY,M_SORTDIR_ASC);
514 
515 		fprintf(f, "+---------------------------+\n");
516 		fprintf(f, "| Watched destination ports |\n");
517 		fprintf(f, "+-----------------+---------+---------------------------+\n");
518 		fprintf(f, "|           # NUM |                    Destination port |\n");
519 		fprintf(f, "+-----------------+--------------------------+----------+\n");
520 		fprintf(f, "|            Host | Last timestamp           |    Count |\n");
521 		fprintf(f, "+=================+==========================+==========+\n");
522 
523 		for (wi = 0; watches[wi] != NULL; wi++) {
524 			int wj;
525 			char *ctimedata;
526 			mdata *actw = watches[wi];
527 
528 			mdata_ipplwatchelement **actcs = sort_ipplwatchelements(actw->data.ipplwatch->watches, actw->data.ipplwatch->count);
529 
530 			fprintf(f, "| %14d. | %35s |\n", wi+1, actw->key );
531 			fprintf(f, "+-----------------+--------------------------+----------+\n");
532 
533 			for (wj = 0;
534 			     wj < actw->data.ipplwatch->count && actcs[wj] != NULL;
535 			     wj++ ) {
536 				ctimedata = malloc(45);
537 				if( !strftime(ctimedata, 44, "%c", localtime( &(actcs[wj]->timestamp) ) ) ) {
538 					fprintf(stderr, "%s.%d: Time formating failed!\n", __FILE__, __LINE__);
539 				}
540 
541 				fprintf(f, "| %15s | %24s | %8ld |\n", actcs[wj]->data,
542 					ctimedata, actcs[wj]->count);
543 				free(ctimedata);
544 			}
545 			free(actcs);
546 			fprintf(f, "+=================+==========================+==========+\n");
547 		}
548 		fprintf(f,"\n\n");
549 	}
550 
551 	fclose(f);
552 
553 	return 0;
554 }
555 
556 
557 int mplugins_output_text_mail_generate_monthly_output(mconfig *ext_conf, mstate *state, const char *subpath) {
558 	FILE *f = NULL;
559 	char filename[255];
560 	config_output *conf = ext_conf->plugin_conf;
561 	mstate_mail *stamail = NULL;
562 	marray_mail sum;
563 	int i,j;
564 
565 	if (state == NULL) return -1;
566 	if (state->ext == NULL) return -1;
567 	if (state->ext_type != M_STATE_TYPE_MAIL) return -1;
568 
569 	stamail = state->ext;
570 
571 	if (subpath) {
572 		sprintf(filename, "%s/%s/",
573 			conf->outputdir ? conf->outputdir : ".",
574 			subpath);
575 		mkdir(filename, 0755);
576 	}
577 
578 	sprintf(filename, "%s%s%s/index-%04d%02d.txt",
579 		conf->outputdir ? conf->outputdir : ".",
580 		subpath ? "/" : "",
581 		subpath ? subpath : "",
582 		state->year, state->month
583 		);
584 
585 	if (!(f = fopen(filename, "w"))) {
586 		return -1;
587 	}
588 
589 	sum.incoming_mails = 0;
590 	sum.outgoing_mails = 0;
591 	sum.incoming_bytes = 0;
592 	sum.outgoing_bytes = 0;
593 
594 	fprintf(f, "Oo. mailstats for %s.oO\n\n", conf->hostname);
595 
596 	fprintf(f, ".-= mailcount and traffic by day =-.\n");
597 	fprintf(f," %5s %10s %10s %10s %10s\n",
598 		"hour",
599 		"mail-in",
600 		"mail-out",
601 		"bytes-in",
602 		"bytes-out");
603 	for ( i = 0; i < 24; i++) {
604 		fprintf(f," %5d %10ld %10ld %10ld %10ld\n",
605 			i,
606 			stamail->hours[i].incoming_mails,
607 			stamail->hours[i].outgoing_mails,
608 			stamail->hours[i].incoming_bytes,
609 			stamail->hours[i].outgoing_bytes
610 			);
611 		sum.incoming_mails += stamail->hours[i].incoming_mails;
612 		sum.outgoing_mails += stamail->hours[i].outgoing_mails;
613 		sum.incoming_bytes += stamail->hours[i].incoming_bytes;
614 		sum.outgoing_bytes += stamail->hours[i].outgoing_bytes;
615 	}
616 
617 	fprintf(f," %5s %10ld %10ld %10ld %10ld\n",
618 		"sum",
619 		sum.incoming_mails,
620 		sum.outgoing_mails,
621 		sum.incoming_bytes,
622 		sum.outgoing_bytes
623 		);
624 
625 	fprintf(f, "\n.-= mailcount and traffic by hour =-.\n");
626 	sum.incoming_mails = 0;
627 	sum.outgoing_mails = 0;
628 	sum.incoming_bytes = 0;
629 	sum.outgoing_bytes = 0;
630 	fprintf(f," %5s %10s %10s %10s %10s\n",
631 		"day",
632 		"mail-in",
633 		"mail-out",
634 		"bytes-in",
635 		"bytes-out");
636 	for ( i = 0; i < 31; i++) {
637 		fprintf(f," %5d %10ld %10ld %10ld %10ld\n",
638 			i,
639 			stamail->days[i].incoming_mails,
640 			stamail->days[i].outgoing_mails,
641 			stamail->days[i].incoming_bytes,
642 			stamail->days[i].outgoing_bytes
643 			);
644 		sum.incoming_mails += stamail->days[i].incoming_mails;
645 		sum.outgoing_mails += stamail->days[i].outgoing_mails;
646 		sum.incoming_bytes += stamail->days[i].incoming_bytes;
647 		sum.outgoing_bytes += stamail->days[i].outgoing_bytes;
648 	}
649 
650 	fprintf(f," %5s %10ld %10ld %10ld %10ld\n",
651 		"sum",
652 		sum.incoming_mails,
653 		sum.outgoing_mails,
654 		sum.incoming_bytes,
655 		sum.outgoing_bytes
656 		);
657 
658 	fprintf(f, "\n.-= mails by sender =-.\n");
659 	show_visit_path (ext_conf, f, stamail->sender, 20);
660 
661 	fprintf(f, "\n.-= mails by receipient =-.\n");
662 	show_visit_path (ext_conf, f, stamail->receipient, 20);
663 
664 	fprintf(f, "\n.-= queuepolution =-.\n");
665 	fprintf(f, "%s %s %s %s %s %s %s %s\n",
666 		"day", "day",
667 		"local-cur", "local-max",
668 		"remote-cur", "remote-cur",
669 		"deliver-cur", "queue-cur");
670 	for (i = 0; i < 31; i++) {
671 		for (j = 0; j < 24; j++) {
672 			if (stamail->qstat[i][j].count) {
673 				fprintf(f, "%5d %3d %9.0f %9.0f %10.0f %10.0f %11.0f %9.0f\n",
674 					i+1,
675 					j,
676 					stamail->qstat[i][j].local_cur / stamail->qstat[i][j].count,
677 					stamail->qstat[i][j].local_max / stamail->qstat[i][j].count,
678 					stamail->qstat[i][j].remote_cur / stamail->qstat[i][j].count,
679 					stamail->qstat[i][j].remote_max / stamail->qstat[i][j].count,
680 					stamail->qstat[i][j].deliver_cur / stamail->qstat[i][j].count,
681 					stamail->qstat[i][j].queue_cur / stamail->qstat[i][j].count
682 					);
683 			}
684 		}
685 	}
686 
687 	fclose(f);
688 
689 	return 0;
690 
691 }
692 
693 int mplugins_output_generate_history_output(mconfig *ext_conf, mlist *history, const char *subpath) {
694 
695 	return 0;
696 }
697