/*
messagewallstats.c - MessageWall statistics program
Copyright (C) 2002 Ian Gulliver

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <firestring.h>

static const char tagstring[] = "$Id: messagewallstats.c,v 1.17.2.3 2002/10/01 19:05:22 ian Exp $";

struct counter {
	char *name;
	int count;
	struct counter *next;
};

struct counter *dnsbl = NULL;
struct counter *filename = NULL;
struct counter *body_text = NULL;
struct counter *rdns = NULL;
struct counter *rdns_temp = NULL;
struct counter *rmx = NULL;
struct counter *rmx_temp = NULL;
struct counter *to_cc = NULL;
struct counter *from = NULL;
struct counter *dnsbl_domain = NULL;
struct counter *dnsdcc = NULL;
struct counter *header = NULL;
struct counter *charfrom = NULL;
struct counter *charto = NULL;
struct counter *overflow = NULL;
struct counter *per_ip = NULL;
struct counter *virus = NULL;
struct counter *encoding = NULL;
struct counter *illegal_multipart = NULL;

void increment(struct counter **head, char *name) {
	struct counter *temp;

	temp = *head;
	while (temp != NULL) {
		if (firestring_strcasecmp(temp->name,name) == 0) {
			temp->count++;
			return;
		}
		temp = temp->next;
	}

	temp = *head;
	*head = firestring_malloc(sizeof(struct counter));
	(*head)->count = 1;
	(*head)->name = firestring_strdup(name);
	(*head)->next = temp;
}

void print (struct counter *head, char *begin) {
	struct counter *temp;
	struct counter *max;

	while (1) {
		temp = head;
		max = NULL;
		while (temp != NULL) {
			if (max == NULL || temp->count > max->count)
				max = temp;
			temp = temp->next;
		}
		if (max == NULL)
			break;
		if (max->count == 0)
			break;
		fprintf(stdout,"%s%s: %d\n",begin,max->name,max->count);
		max->count = 0;
	}
}

int main(int argc, char *argv[]) {
	FILE *f;
	char line[2048];
	int connections = 0;
	int quit = 0;
	int disconnect = 0;
	int disconnect_data = 0;
        long bytes_total = 0;
        long bytes_delivered = 0;
        long bytes_received =0;
	int idle = 0;
	int errors = 0;
	int bare_lf = 0;
	int bare_lf_data = 0;
	int client_8bit = 0;
	int tls_attempt = 0;
	int tls_success = 0;
	int invalid_mail = 0;
	int invalid_rcpt = 0;
	int messages = 0;
	int messages_reject = 0;
	int messages_received = 0;
	int backend_send = 0;
	int backend_accept = 0;
	int filter_reject = 0;
	int filter_dnsdcc = 0;
	int filter_dnsbl_domain = 0;
	int filter_dnsbl = 0;
	int filter_to_cc = 0;
	int filter_from = 0;
	int filter_rmx = 0;
	int filter_rdns = 0;
	int filter_rmx_temp = 0;
	int filter_rdns_temp = 0;
	int filter_header = 0;
	int filter_body = 0;
	int filter_filename = 0;
	int filter_virus = 0;
	int filter_useless = 0;
	int filter_noboundary = 0;
	int filter_toomanyparts = 0;
	int filter_illegalmultipart = 0;
	int filter_unknownencoding = 0;
	int filter_qp = 0;
	int filter_base64 = 0;
	int backend_attempt = 0;
	int backend_success = 0;
	int backend_tls_success = 0;
	int backend_overflows = 0;
	int backend_reject_overflows = 0;
	int overflows = 0;
	int per_ips = 0;
	char *end;
	char *start;

	if (argc != 2) {
		fprintf(stderr,"usage: %s <log file>\n",argv[0]);
		exit(100);
	}

	f = fopen(argv[1],"r");
	if (f == NULL) {
		perror("fopen");
		exit(100);
	}

	while (fgets(line,2048,f) != NULL) {
		if (strstr(line,") SERVER/STATUS: connection from") != NULL) {
			connections++;
			continue;
		}

		if (strstr(line,"SMTP/FATAL: client QUIT") != NULL) {
			quit++;
			continue;
		}

		if (strstr(line,"SERVER/FATAL: disconnect\n") != NULL) {
			disconnect++;
			continue;
		}

		if (strstr(line,"SERVER/FATAL: disconnect inside DATA\n") != NULL) {
			disconnect_data++;
			continue;
		}

		if (strstr(line,"bare LF inside DATA\n") != NULL) {
			bare_lf_data++;
			continue;
		}

		if (strstr(line,"bare LF in command\n") != NULL) {
			bare_lf++;
			continue;
		}
		
		if (strstr(line,"SERVER/FATAL: idle timeout\n") != NULL) {
			idle++;
			continue;
		}

		if (strstr(line,"SMTP/FATAL: too many errors\n") != NULL) {
			errors++;
			continue;
		}

		if (strstr(line,") TLS/STATUS: TLS negotiation started") != NULL) {
			tls_attempt++;
			continue;
		}

		if (strstr(line,") TLS/SUCCESS: now speaking TLS") != NULL) {
			tls_success++;
			continue;
		}

		if (strstr(line,": Message start") != NULL) {
			messages++;
			continue;
		}

		if (strstr(line,"PROFILE/REJECT: reject set in profile\n") != NULL) {
			messages_reject++;
			continue;
		}

		if ((start = strstr(line,"SMTP/STATUS: received message")) != NULL) {
			messages_received++;
                        sscanf(start,"SMTP/STATUS: received message of %ld bytes\n",&bytes_received);
                        bytes_total += bytes_received;
			continue;
		}

		if ((start = strstr(line,"invalid MAIL character (")) != NULL) {
			invalid_mail++;
			start += 24;
			start[1] = '\0';
			increment(&charfrom,start);
			continue;
		}

		if ((start = strstr(line,"invalid RCPT character (")) != NULL) {
			invalid_rcpt++;
			start += 24;
			start[1] = '\0';
			increment(&charto,start);
			continue;
		}

		if (strstr(line,") BACKEND/STATUS: message to [") != NULL) {
			backend_send++;
                        bytes_delivered += bytes_received;
                        bytes_received = 0;
			continue;
		}

		if (strstr(line,"] BACKEND/ACCEPT: accepted message responsibility:") != NULL) {
			backend_accept++;
			continue;
		}

		if (strstr(line,"has reached or exceeded maximum") != NULL) {
			filter_reject++;
			continue;
		}

		if ((start = strstr(line,"DNSDCC/WARNING: ")) != NULL) {
			filter_dnsdcc++;
			start += 16;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&dnsdcc,start);
			}
			continue;
		}

		if ((start = strstr(line,"DNSBL/WARNING: ")) != NULL) {
			filter_dnsbl++;
			start += 15;
			end = strchr(start,'/');
			if (end != NULL) {
				*end = '\0';
				increment(&dnsbl,start);
			}
			continue;
		}

		if ((start = strstr(line,"DNSBL-DOMAIN/WARNING: ")) != NULL) {
			filter_dnsbl_domain++;
			start += 22;
			end = strchr(start,'/');
			if (end != NULL) {
				*end = '\0';
				increment(&dnsbl_domain,start);
			}
			continue;
		}

		if (strstr(line,"envelope forward path not in To/CC\n") != NULL) {
			filter_to_cc++;
			start = strstr(line,"RFC822/WARNING: ");
			if (start == NULL)
				continue;
			start += 16;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&to_cc,start);
			}
			continue;
		}
		
		if (strstr(line,"envelope reverse path not in From\n") != NULL) {
			filter_from++;
			start = strstr(line,"RFC822/WARNING: ");
			if (start == NULL)
				continue;
			start += 16;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&from,start);
			}
			continue;
		}

		if (strstr(line,"reverse path domain has no MX or A records (temporary)\n") != NULL) {
			filter_rmx_temp++;
			start = strstr(line,"RMX/WARNING: ");
			if (start == NULL)
				continue;
			start += 13;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&rmx_temp,start);
			}
			continue;
		}

		if (strstr(line,"reverse path domain has no MX or A records\n") != NULL) {
			filter_rmx++;
			start = strstr(line,"RMX/WARNING: ");
			if (start == NULL)
				continue;
			start += 13;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&rmx,start);
			}
			continue;
		}

		if (strstr(line,"IP has no reverse DNS (temporary)\n") != NULL) {
			filter_rdns_temp++;
			start = strstr(line,"RDNS/WARNING: ");
			if (start == NULL)
				continue;
			start += 14;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&rdns_temp,start);
			}
			continue;
		}

		if (strstr(line,"IP has no reverse DNS\n") != NULL) {
			filter_rdns++;
			start = strstr(line,"RDNS/WARNING: ");
			if (start == NULL)
				continue;
			start += 14;
			end = strchr(start,':');
			if (end != NULL) {
				*end = '\0';
				increment(&rdns,start);
			}
			continue;
		}
		
		if ((start = strstr(line,"message failed header check for ")) != NULL) {
			filter_header++;
			start += 32;
			end = strchr(start,'\n');
			if (end != NULL) {
				*end = '\0';
				increment(&header,start);
			}
			continue;
		}

		if ((start = strstr(line,"body text contains '")) != NULL) {
			filter_body++;
			start += 20;
			end = strchr(start,'\'');
			if (end != NULL) {
				*end = '\0';
				increment(&body_text,start);
			}
			continue;
		}

		if ((start = strstr(line,"illegal attachment filename: '")) != NULL) {
			filter_filename++;
			start += 30;
			start = strstr(start,"\' contains \'");
			if (start == NULL)
				continue;
			start += 12;
			end = strchr(start,'\'');
			if (end != NULL) {
				*end = '\0';
				increment(&filename,start);
			}
			continue;
		}

		if (strstr(line,"BACKEND/STATUS: connect to") != NULL) {
			backend_attempt++;
			continue;
		}

		if (strstr(line,"BACKEND/STATUS: connection established") != NULL) {
			backend_success++;
			continue;
		}

		if (strstr(line,"] TLS/SUCCESS: now speaking TLS") != NULL) {
			backend_tls_success++;
			continue;
		}

		if ((start = strstr(line,"per-ip overflow from ")) != NULL) {
			per_ips++;
			start += 21;
			end = strchr(start,'\n');
			if (end != NULL) {
				*end = '\0';
				increment(&per_ip,start);
			}
			continue;
		}

		if ((start = strstr(line,"client overflow from ")) != NULL) {
			overflows++;
			start += 21;
			end = strchr(start,'\n');
			if (end != NULL) {
				*end = '\0';
				increment(&overflow,start);
			}
			continue;
		}

		if (strstr(line,"none available to receive message") != NULL) {
			backend_overflows++;
			continue;
		}

		if (strstr(line,"none available to receive reject") != NULL) {
			backend_reject_overflows++;
			continue;
		}

		if ((start = strstr(line,"message body matches pattern for '")) != NULL) {
			filter_virus++;
			start += 34;
			end = strchr(start,'\'');
			if (end != NULL) {
				*end = '\0';
				increment(&virus,start);
			}
			continue;
		}

		if (strstr(line,"message contains no parts accepted at this address\n") != NULL) {
			filter_useless++;
			continue;
		}

		if (strstr(line,"unable to find boundary\n") != NULL) {
			filter_noboundary++;
			continue;
		}

		if (strstr(line,"too many parts\n") != NULL) {
			filter_toomanyparts++;
			continue;
		}

		if ((start = strstr(line,"illegal multipart encoding: ")) != NULL) {
			filter_illegalmultipart++;
			start += 28;
			end = strchr(start,'\n');
			if (end != NULL) {
				*end = '\0';
				increment(&illegal_multipart,start);
			}
			continue;
		}

		if ((start = strstr(line,"unknown encoding: ")) != NULL) {
			filter_unknownencoding++;
			start += 18;
			end = strchr(start,'\n');
			if (end != NULL) {
				*end = '\0';
				increment(&encoding,start);
			}
			continue;
		}

		if (strstr(line,"invalid quoted-printable encoding") != NULL) {
			filter_qp++;
			continue;
		}

		if (strstr(line,"invalid base64 encoding") != NULL) {
			filter_base64++;
			continue;
		}

		if (strstr(line,"client sent 8bit data to server in 7bit mode") != NULL) {
			client_8bit++;
			continue;
		}	
	}

	fprintf(stdout,"Client Connections: %d\n",connections);
	fprintf(stdout,"\tQUIT: %d\n",quit);
	fprintf(stdout,"\tDisconnect: %d\n",disconnect);
	fprintf(stdout,"\tDisconnect inside DATA: %d\n",disconnect_data);
	fprintf(stdout,"\tBare LF: %d\n",bare_lf);
	fprintf(stdout,"\tIdle Timeout: %d\n",idle);
	fprintf(stdout,"\tToo many errors: %d\n",errors);

	fprintf(stdout,"\n");

	fprintf(stdout,"Client TLS Attempts: %d\n",tls_attempt);
	fprintf(stdout,"\tSuccess: %d\n",tls_success);

	fprintf(stdout,"\n");

	fprintf(stdout,"Overflows: %d\n",overflows);
	print(overflow,"\t");
	fprintf(stdout,"Per-IP Overflows: %d\n",per_ips);
	print(per_ip,"\t");

	fprintf(stdout,"\n");

	fprintf(stdout,"Backend Overflows: %d\n",backend_overflows);
	fprintf(stdout,"Backend Rejection Overflows: %d\n",backend_reject_overflows);

	fprintf(stdout,"\n");

	fprintf(stdout,"Backend connection attempts: %d\n",backend_attempt);
	fprintf(stdout,"\tSuccess: %d\n",backend_success);
	fprintf(stdout,"\tTLS: %d\n",backend_tls_success);

	fprintf(stdout,"\n");

	fprintf(stdout,"Invalid MAIL characters: %d\n",invalid_mail);
	print(charfrom,"\t");
	fprintf(stdout,"Invalid RCPT characters: %d\n",invalid_rcpt);
	print(charto,"\t");

	fprintf(stdout,"\n");

	fprintf(stdout,"Client Messages: %d\n",messages);
	fprintf(stdout,"\tBare LF inside DATA: %d\n",bare_lf_data);
	fprintf(stdout,"\t8bit inside DATA: %d\n",client_8bit);
	fprintf(stdout,"\tRejected by Profile: %d\n",messages_reject);
	fprintf(stdout,"\tCompletely Received: %d\n",messages_received);
	fprintf(stdout,"\tSent to Backend: %d\n",backend_send);
	fprintf(stdout,"\tAccepted by Backend: %d\n",backend_accept);

	fprintf(stdout,"\n");

	fprintf(stdout,"Messages Rejected by Filter: %d\n",filter_reject);
	fprintf(stdout,"\tFailed To/CC: %d\n",filter_to_cc);
	print(to_cc,"\t\t");
	fprintf(stdout,"\tFailed From: %d\n",filter_from);
	print(from,"\t\t");
	fprintf(stdout,"\tMatched DNSBL: %d\n",filter_dnsbl);
	print(dnsbl,"\t\t");
	fprintf(stdout,"\tMatched Domain DNSBL: %d\n",filter_dnsbl_domain);
	print(dnsbl_domain,"\t\t");
	fprintf(stdout,"\tMatched DNSDCC: %d\n",filter_dnsdcc);
	print(dnsdcc,"\t\t");
	fprintf(stdout,"\tReverse Path MX/A lookup timed out: %d\n",filter_rmx_temp);
	print(rmx_temp,"\t\t");
	fprintf(stdout,"\tReverse DNS lookup timed out: %d\n",filter_rdns_temp);
	print(rdns_temp,"\t\t");
	fprintf(stdout,"\tFailed Reverse Path MX/A: %d\n",filter_rmx);
	print(rmx,"\t\t");
	fprintf(stdout,"\tFailed Reverse DNS: %d\n",filter_rdns);
	print(rdns,"\t\t");
	fprintf(stdout,"\tFailed Body check: %d\n",filter_body);
	print(body_text,"\t\t");
	fprintf(stdout,"\tFailed Header check: %d\n",filter_header);
	print(header,"\t\t");
	fprintf(stdout,"\tIllegal attachment filename: %d\n",filter_filename);
	print(filename,"\t\t");
	fprintf(stdout,"\tVirus: %d\n",filter_virus);
	print(virus,"\t\t");
	fprintf(stdout,"\tNo accepted MIME parts: %d\n",filter_useless);
	fprintf(stdout,"\tMissing MIME boundary: %d\n",filter_noboundary);
	fprintf(stdout,"\tToo many parts: %d\n",filter_toomanyparts);
	fprintf(stdout,"\tIllegal multipart encoding: %d\n",filter_illegalmultipart);
	print(illegal_multipart,"\t\t");
	fprintf(stdout,"\tUnknown MIME encoding: %d\n",filter_unknownencoding);
	print(encoding,"\t\t");
	fprintf(stdout,"\tInvalid QP encoding: %d\n",filter_qp);
	fprintf(stdout,"\tInvalid base64 encoding: %d\n",filter_base64);
        fprintf(stdout,"\n");

        fprintf(stdout,"Mail Traffic\n");
        fprintf(stdout,"\tBytes received: %ld\n",bytes_total);
        fprintf(stdout,"\tBytes rejected: %ld\n",bytes_total - bytes_delivered);
        fprintf(stdout,"\tBytes accepted: %ld\n",bytes_delivered);
	exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1