/*
 * sma -- Sendmail log analyser
 *
 * Copyright (c) 2000 - 2003 Jarkko Turkulainen. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JARKKO TURKULAINEN ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JARKKO TURKULAINEN BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Date: 2003/04/27 15:43:33 $
 */

#include "sma.h"
#include <ctype.h>

/* Delivery status flags */
#define STATUS_SENT	1
#define STATUS_DEF	2	
#define STATUS_USER	3
#define STATUS_QUEUE	4
#define STATUS_HOST	5
#define STATUS_SERV	6
#define STATUS_OTHER	7	

/* Message ID flags: */
#define NOT_DELIVERED	0
#define FIRST_DELIVERY	1
#define DELIVERED	2

/* Message ID checking mode */
#define KEEP	0
#define REMOVE	1

/* Action flags */
#define	ACTION_FROM	0
#define	ACTION_TO	1	
#define	ACTION_SYSERR	2
#define	ACTION_RULESET	3
#define	ACTION_DAEMON	4
#define	ACTION_DATAB	5
#define	ACTION_DSN	6
#define	ACTION_USER	7
#define ACTION_UNKNOWN	100

/* ACTION_SYSERR flags */
#define	OTHER		0
#define	HOPCOUNT	1
#define	LOOP		2

#ifdef USE_REGEXP
	int check_filter(const char *, regex_t *, const char *);
#else
	int check_filter(const char *, const char *);
#endif

/*
 * Do parsing for each input file
 */
void
parse(FILE *fp, const char *file) {

	/* input buffer */
	char line[2048];

	/* plenty of pointers for line splitting: */
	static const char *env_rec[128];
	char *head[64];
	char *info[64];

	/* collecting */
	char *action, *dsn, *idptr;
	char *messageid = NULL;
	const char *tmphost;
	int logformat, hcount, icount;
	int status = 0, idsize = 0, gf, pf = 0, actn = 0, sunos = 0; 
	int actflag, year, mon, day, hh, min, sec;
	struct msgid *tmsgid = NULL;

	/* general purpose: */
	const char *p = NULL;
	const char *s = NULL;
	int i, k, lcount;

	/* status and ruleset pointers */
	const char *rulestr = NULL;
	const char *relstr = NULL;
	const char *statstr = NULL;

	/* temporary pointer to host struct: */
	struct host *hptr;

	/* Start read file line by line: */
	lcount = 0;
	while (fgets(line, 2048, fp)) {
		lcount++;

		/* null-terminate: */
		line[strlen(line)-1] = '\0';
		memset(head, 0, 64);
		memset(info, 0, 64);

		/* split line initially with ',': */
		info[0] = line;
		for (i = 1, k = 0; line[k] != '\0'; )  {
			if (line[k] == ',') {
				line[k] = '\0';
				k++;
				if (line[k] == ' ')
					k++;
				info[i] = &line[k];
				i++;
			} else
				k++;
		}
		icount = i;

		/* split first info with ' ': */
		head[0] = info[0];
		for (i = 1, k = 0; info[0][k] != '\0'; )  {
			if (info[0][k] == ' ') {
				info[0][k] = '\0';
				k++;
				while(info[0][k] == ' ')
					k++;
				head[i] = &info[0][k];
				i++;
			} else
				k++;
		}
		hcount = i;

		/*
		 * Initial sanity check:
		 * Try to figure out the input file format
		 * Currently supported are syslog and NT
		 *
		 * Sendmail for NT
		 * Assume that the second field is "sendmail" ..
		 * This may be wrong, don't know NT version enough.
		 */
		idptr = NULL; action = NULL; actn = 0; dsn = NULL;
		if (head[5] && !strncmp("sendmail", head[2], 7)) {
			logformat = 1;
			if (sscanf(head[1], "%d:%d:%d", &hh, &min, &sec) != 3) {
				if (!qflag) fprintf(stderr, 
					  "%s: not NT log, skipping line %d, "
					  "file %s\n", pname, lcount, file);
				continue;
			}
			if (sscanf(head[0], "%d/%d/%d",&mon,&day,&year) != 3) {
				if (!qflag) fprintf(stderr, 
					  "%s: not NT log, skipping line %d, "
					  "file %s\n", pname, lcount, file);
				continue;
			}
			/* We assume things fixed with NT log.. */
			if (hcount >= 6) {
				idptr = head[4];
				action = head[5];
				actn = 5; dsn = head[6];
				mon--;
			}
		/*
		 * Normal syslog
		 */
		} else {
			logformat = 0;
			if (!head[5] || (Lchar && strncmp(Lchar, head[4],
				strlen(Lchar)))) {
				if (!qflag) fprintf(stderr, 
					  "%s: not syslog, skipping line %d, "
					  "file %s\n", pname, lcount, file);
				continue;
			}
			if (sscanf(head[2], "%d:%d:%d", &hh, &min, &sec) != 3) {
				if (!qflag) fprintf(stderr, 
					  "%s: not syslog, skipping line %d, "
					  "file %s\n", pname, lcount, file);
				continue;
			}
			day = atoi(head[1]);
			mon = conv_mon(head[0]);
			
			/* SunOS 5.x with ID generation enabled: */
			if (!strncmp("[ID", head[5], 3)) {
				sunos = 3;
			} else sunos = 0;
			/* 
			 * Now, we run through the first info-vector (head)
			 * and try to find useful keywords. If found, copy
			 * the pointers as idptr, action and dsn. Action
			 * flags are later set according to these keywords.
			 */
			for (k = 6+sunos; k < hcount; k++ ) {
				/* "normal" delivery lines */
				if (!strncmp("daemon", head[k], 6) ||
				    !strncmp("ruleset", head[k], 7) || 
				    !strncmp("to=", head[k], 3) || 
				    !strncmp("from=", head[k], 5) || 
				    !strncmp("SYSERR", head[k], 6) || 
				    !strncmp("database", head[k], 8)) {
					actn = k;
					idptr = head[k-1];
					action = head[k];
					break;

				/* DSN, and other strange stuff: */
				} else if (!strncmp("DSN", head[k], 3) ||
				    !strncmp("User", head[k], 4) || 
				    !strncmp("postmaster", head[k],10) || 
				    !strncmp("return", head[k], 6)) {
					idptr = head[k-2];
					action = head[k-1];
					dsn = head[k];
					break;
				}
			}
		}
		/* 
		 * Permitted actions are defined here.
		 *
		 * We test the action, dsn, idptr and other fields for
		 * keywords and set the flag actflag according to a
		 * defined action. These actions are later performed
		 * with a switch(actflag) statement.
		 *
		 */
		actflag = ACTION_UNKNOWN;
		if (idptr && !strncmp("ruleset", idptr, 7)) {
			actflag = ACTION_RULESET;
		} else if (action && !strncmp("ruleset", action, 7)) {
			actflag = ACTION_RULESET;
		} else if (action && !strncmp("from=", action, 5)) {
			actflag = ACTION_FROM;
		} else if (action && !strncmp("to=", action, 3)) {
			actflag = ACTION_TO;
		} else if (action && !strncmp("SYSERR", action, 6)) {
			actflag = ACTION_SYSERR;
		} else if (action && !strncmp("daemon", action, 6)) {
			actflag = ACTION_DAEMON;
		} else if (action && !strncmp("database", action, 8)) {
			actflag = ACTION_DATAB;
		} else if (dsn && (!strncmp("User", dsn, 4))) {
			actflag = ACTION_USER;
		} else if (dsn) {
			actflag = ACTION_DSN;
		} else continue;

		/*
		 * Initialize the host structure
		 * If the hostname is not given, use syslog or 
		 * (in case of NT) default string
		 */
		if (Hchar)
			tmphost = Hchar;
		else if (logformat)
			tmphost = HOSTNAME;
		else
			tmphost = head[3];
		hptr = init_host(tmphost);

		/* 
		 * Rejected before SMTP "MAIL FROM"
		 * This should be under switch(actflag) but isn't really
		 * a delivery, so we test it here..
		 */
		if (!strncmp("ruleset", idptr, 7)) {
                        hptr->rule++;
			/* Find the reject-field: */
			for (k = 1; k < icount; k++ ) {
				if (!strncmp("relay=", info[k], 6))
					relstr = get_relay_name(info[k]+6);
				else if (!strncmp("reject=", info[k], 7))
					rulestr = info[k]+7;
			}
			if (!rulestr) continue;
			if (!relstr) relstr = BOUNCE_HOST;
			if (rsnum) update_ruleset(hptr, rulestr, relstr);
			continue;
		}

		/* Take the current time: */
		hptr->cdtime = (time_t)conv_time(mon, day, hh, min, sec);

		/* Check if we are allowed to proceed: */
		if (sstime && (hptr->cdtime < sstime))
			continue;
		if (eetime && (hptr->cdtime > eetime))
			continue;

		/* is the host brand new? */
		if (hptr->fhost == 0) {
			/* Make the first time as ftime, cdtime and ltime: */
			hptr->fhost = 1;
			hptr->ftime = hptr->cdtime;
			hptr->ltime = hptr->ftime;
		}

		/* Check ftime and ltime: */
		if (hptr->cdtime > hptr->ltime)
			hptr->ltime = hptr->cdtime;
		if (hptr->cdtime < hptr->ftime)
			hptr->ftime = hptr->cdtime;

		/* Calculate weekday: */
		hptr->lday = (localtime(&hptr->cdtime))->tm_wday;

		/* Debug */
		if (vflag && (actflag == ACTION_DSN || actflag == ACTION_FROM ||
		    actflag == ACTION_TO || actflag == ACTION_RULESET ||
		    actflag == ACTION_USER)) {
			fprintf(stderr, "\n<< DEBUG >> file %s, line %d\n",
				file, lcount);
			fprintf(stderr, "  msg id=%s\n", idptr);
		}

		/* 
		 * Switch the possible actions
		 * DSN stuff
		 * These include DSN, postmaster notify, return to sender...
		 */
		switch(actflag) {
		case ACTION_DSN:
			if (vflag) fprintf(stderr, "  DSN: %s\n", idptr);

			/* Find the message ID */
			tmsgid = get_msgid(hptr, idptr);
			if (tmsgid == NULL)
				continue;
			/* Remove it */
			remove_msgid(hptr, idptr);

			/* This is a completely new mail */
			p = bechar;
			s = BOUNCE_HOST;

			/*
			 * Sender Envelope and Relay filtering
			 */
#ifdef USE_REGEXP
			if (check_filter(sef, &csef, p) &&
				check_filter(srf, &csrf, s)) {
#else
			if (check_filter(sef, p) && check_filter(srf, s)) {
#endif
				/* Message ID */
				update_msgid(hptr, p, s, action, hh, 
					day, 1, 200, "<DSN>");
			/* Nope, we are filtered: */
			} else
				continue;
			break;

		/* User unkown: */
		case ACTION_USER:
			if (vflag) fprintf(stderr, 
			  "  User unkown: %s\n", idptr);
			update_omsgid(hptr, idptr, 0);
			break;

		/* "from" envelope:  */
		case ACTION_FROM:
			p = NULL;
			s = NULL;

			/* check omsgidtab */
			if (check_omsgid(hptr, idptr))
				continue;

			/* fetch the name: */
			p = get_name(action+5);
			if (vflag) fprintf(stderr, "  from=%s\n", p);

			/* follow on: */
			for (k = 1; k < icount; k++) {

				/* size of the message: */
				if (!strncmp("size", info[k], 4)) {
					hptr->lsize = atoi(info[k]+5);

				/* msgid */
				} else if (!strncmp("msgid", info[k], 5)) {
					messageid = info[k]+6;

				/* input relay */
				} else if (!strncmp("relay", info[k], 5)) {
					s = get_relay_name(info[k]+6);

				/* no of recipients to follow */
				} else if (!strncmp("nrcpts", info[k], 6))
					idsize = atoi(info[k]+7);
			}
			/* Check relay */
			if (!s) s = BOUNCE_HOST;
			if (vflag) fprintf(stderr, "  relay=%s\n", s);

			/* Initialize messageid */
			if (!messageid) messageid = "";

			/*
			 * Sender Envelope and Relay filtering
			 */
#ifdef USE_REGEXP
			if (check_filter(sef, &csef, p) &&
				check_filter(srf, &csrf, s)) {
#else
			if (check_filter(sef, p) && check_filter(srf, s)) {
#endif
				update_msgid(hptr, p, s, idptr, hh, day,
					idsize, hptr->lsize, messageid);
			/* Nope, we are filtered: */
			} else
				continue;
			break;

		/* "to" envelpe: */
		case ACTION_TO:
			p = NULL;
			s = NULL;

			/* Check msgidtab */
			tmsgid = get_msgid(hptr, idptr);
			if (tmsgid == NULL)
				/* Bogus address or (from=) side filter */
				continue;
			hptr->lsize = tmsgid->size;

			/* Check delivery flag: */
			if (tmsgid->flag == NOT_DELIVERED)
				/* This is a fresh one: */
				tmsgid->flag = FIRST_DELIVERY;

			/* fetch name and make entry: */
			i = 0;
			memset(env_rec, 0, (size_t)sizeof(char) * 128);
			p = get_name(action+3);
			env_rec[i] = p;

			for (k = 1; k < icount; k++) {
				if (!strncmp("delay", info[k], 5) ||
				    !strncmp("ctladdr", info[k], 7))
					break;
				else 
					/* more recipients: */
					env_rec[++i] = get_name(info[k]);
			}
			/* now for the rest of the cases: */
			for (; k < icount; k++ ) {
				if (!strncmp("stat", info[k], 4)) {
					statstr = info[k]+5;
					if (!strncmp("Sent", info[k]+5, 4) ||
					    !strncmp("sent", info[k]+5, 4))
						status = STATUS_SENT;
					else if (!strncmp("Deferred", 
					  info[k]+5, 8)) 
						status = STATUS_DEF;
					else if (!strncmp("User", 
					  info[k]+5, 4))
						status = STATUS_USER;
					else if (!strncmp("Host", 
					  info[k]+5, 4))
						status = STATUS_HOST;
					else if (!strncmp("queued", 
					  info[k]+5, 6))
						status = STATUS_QUEUE;
					else if (!strncmp("Service",
					  info[k]+5, 7))
						status = STATUS_SERV;
					else
						status = STATUS_OTHER;

				} else if (!strncmp("relay", info[k], 5)) {

					/* output relay */
					s = get_relay_name(info[k]+6);
				}
			} 
			/* If no relay host, make as bouncehost: */
			if (!s) s = BOUNCE_HOST;
			if (vflag) fprintf(stderr, "  relay=%s\n", s);

			i = 0;
			gf = 0;
			while (env_rec[i]) {
				if (vflag) fprintf(stderr, 
					"  to=%s\n", env_rec[i]);
				/*
				 * Recipient Envelope and Relay filtering
				 */
#ifdef USE_REGEXP
				if (check_filter(ref, &cref, env_rec[i]) && 
				  check_filter(rrf, &crrf, s)) {
					gf = 1;
					pf = 1;
#else
				if (check_filter(ref, env_rec[i]) && 
				  check_filter(rrf, s)) {
					gf = 1;
					pf = 1;
#endif
				} else pf = 0;
				if (pf) {
					/*
					 * Update output side stats if
					 * the format is ASCII or HTML:
					 */
					if (Oflag != FORMAT_CLOG) {
						if (status == STATUS_SENT) {
							update_out(hptr,
							  env_rec[i],
							  hptr->lsize);
							if (epnum)
							update_envpair(hptr,
							  tmsgid->sender,
							  env_rec[i],
							  hptr->lsize);
						} else {
							update_out(hptr, 
							  env_rec[i], 0);
							if (epnum)
							update_envpair(hptr,
							  tmsgid->sender,
							  env_rec[i], 0);
						}
						hptr->onum++;
					/* Custom log: */
					} else if (!clsflag || (clsflag &&
						   (status == STATUS_SENT))) {
						printclog(
							hptr->cdtime,
							tmphost,
							(int)hptr->lsize,
							tmsgid->msgid,
							tmsgid->sender,
							tmsgid->relay,
							env_rec[i], s,
							(status == STATUS_SENT)
							  ? 1 : 0);
					}
					/* Checkout msgid */
					check_msgid(hptr, idptr, KEEP);
				}
				i++;
			}
			/* Remove msgid if not ASCII or HTML: */
			if (Oflag == FORMAT_CLOG) {
				if (gf) {
					if (status == STATUS_SENT)
					  check_msgid(hptr, idptr, REMOVE);
				} else
					/* Remove useless message ID: */
					remove_msgid(hptr, idptr);

				continue;
			}

			/* 
			 * Update global stats only once per delivery
			 */
			if (gf) {
				/* Update output relay: */
				hptr->ronum++;
				if (status == STATUS_SENT) {
					update_rout(hptr, s, hptr->lsize);
					/* update relay pairs */
					if (rpnum) update_relpair(hptr,
					  tmsgid->relay,
					  s, hptr->lsize);
				} else {
					update_rout(hptr, s, 0);
					if (rpnum) update_relpair(hptr,
					  tmsgid->relay, s, 0);
				}

				/* Update the global stats: */
				hptr->gonum++;
				hptr->ohh[hh]++;
				hptr->odd[hptr->lday]++;
				if (status == STATUS_SENT)
					hptr->osize += hptr->lsize;

				/* Check status flag: */
				if (status == STATUS_SENT) {
					hptr->sent++;
					if (stnum) update_status(hptr, "Sent");
				} else {
					if (stnum) update_status(hptr, statstr);
					switch(status) {
						case STATUS_DEF:
							hptr->defe++;
							break;
						case STATUS_HOST:
							hptr->hunk++;
							break;
						case STATUS_USER:
							hptr->uunk++;
							break;
						case STATUS_QUEUE:
							hptr->queu++;
							break;
						case STATUS_SERV:
							hptr->service++;
							break;
						case STATUS_OTHER:
							hptr->other++;
							break;
					}
				}

				/*
				 * If this is a first delivery,  update
				 * input side statistics:
				 */
				if (tmsgid->flag == FIRST_DELIVERY) {
					/* Add envelope: */	
					hptr->inum++;
					update_in(hptr, tmsgid->sender, 
						hptr->lsize);

					/* Add relay: */
					hptr->rinum++;
					update_rin(hptr, tmsgid->relay, 
						hptr->lsize);

					/* Update statistics: */
					hptr->ihh[tmsgid->hh]++;
					hptr->idd[hptr->lday]++;
					hptr->size += hptr->lsize;
					hptr->isize += hptr->lsize;

					tmsgid->flag = DELIVERED;
				}

				/* 
				 * If the message was sent, remove useless
				 * message ID from the chain
				 */
				if (status == STATUS_SENT)
					check_msgid(hptr, idptr, REMOVE);

			} else
				/* Remove useless message ID: */
				remove_msgid(hptr, idptr);
			break;

		/* SYSERR: */
		case ACTION_SYSERR:
			if (vflag) {
				fprintf(stderr, "\n<< DEBUG >> file %s, line %d\n",
					file, lcount);
				fprintf(stderr, "  SYSERR");
			}
			for (i = 0, k = actn; k < hcount; k++) {

				/* Too many hops: */
				if (!strncmp("Too", head[k], 3)) {
					if (!head[k+2])
						continue;
					if (!strncmp("many", head[k+1], 4) &&
					    !strncmp("hops", head[k+2], 4))
						i = HOPCOUNT;

				/* Local configuration error: */
				} else if (!strncmp("config", head[k], 6)) {
					if (!head[k+1])
						continue;
					if (!strncmp("error:", head[k+1], 6))
						i = LOOP;
				} 
				/* Other SYSERR (i = 0)*/

			}
			switch(i) {
				case HOPCOUNT:
					if (vflag)
					  fprintf(stderr, " (hop count)\n");
					hptr->hopc++;
					break;
				case LOOP:
					if (vflag)
					  fprintf(stderr, " (mail loop)\n");
					hptr->lcerror++;
					break;
				case OTHER:
					if (vflag)
					  fprintf(stderr, " (other)\n");
					hptr->oserror++;
					break;
			}
			break;

		/* ruleset based rejection: */
		case ACTION_RULESET:
			if (vflag) fprintf(stderr, "  ruleset: %s\n", idptr);
			update_omsgid(hptr, idptr, 0);
			hptr->rule++;
			
			/* Find the reject-field: */
			for (k = 1; k < icount; k++ ) {
				if (!strncmp("relay=", info[k], 6))
					relstr = get_relay_name(info[k]+6);
				else if (!strncmp("reject=", info[k], 7))
					rulestr = info[k]+7;
			}
			if (!rulestr) continue;
			if (!relstr) relstr = BOUNCE_HOST;
			if (rsnum) update_ruleset(hptr, rulestr, relstr);
			break;

		/* sendmail daemon started: */
		case ACTION_DAEMON:
			if (vflag) {
				fprintf(stderr, "\n<< DEBUG >> file %s, line %d\n",
					file, lcount);
				fprintf(stderr, "  daemon restart\n");
			}
			hptr->dstart++;
			break;

		/* alias database rebuild: */
		case ACTION_DATAB:
			if (vflag) {
				fprintf(stderr, "\n<< DEBUG >> file %s, line %d\n",
					file, lcount);
				fprintf(stderr, "  alias table rebuilt\n");
			}	
			hptr->alias++;
			break;

		default:
			continue;
		}
	}
}

/*
 * get_name - strip sender/recipient name/domain.
 */

const char *
get_name(char *name) {
	char *tmp, *tmp2;
	int i;

	/* downcase everything if requested */
	if (dcaddrflag) {
		for ( i=0; name[i]; i++) {
			name[i] = tolower(name[i]);
		}
	}
	/* null-sender? */
	if (!strncmp("<>", name, 2))
		return("<>");

	/* find the separating '@'-char: */
	while (*name == '<' || *name == '"' || *name == '\'')
		name++;
	tmp = tmp2 = name;

	while (*name++ != '@' && *name)
		;
	if (!*name)
		name = tmp;

	/* find the first char of the return pointer: */
	if (dflag)
		tmp = name;
	else {
		while (name != tmp2) {
			if ((*name == '<') || (*name == '"') ||
			  (*name == '\'') || (*name == ' ')) {
				name++;
				break;
			} else
				name--;
		}
		tmp = name;
	}

	/* cut the string: */
	while (*name) {
		if ((*name == '>') || (*name == '"') ||
		  (*name == '\'') || (*name == ' ')) {
			*name = '\0';
			break;
		} else
			name++;
	}
	return tmp;
}

/* get relay name: */
const char *
get_relay_name(char *name) {
	char *tmp;

	tmp = name;
	/* 
	 * Search for space or (in case of identd)
	 * '@'-char. Make the return string point
	 * to next char.
	 */
	while (*name) {
		if (*name == '@') {
			tmp = name + 1;
			break;
		}
		name++;
	}
	if (!*name)
		name = tmp;

	/*
	 * Do not take the IP-address in brackets
	 */
	while (*name) {
		if ((*name == ' ') || (*name == ',')) {
			*name = '\0';
			break;
		} else
			name++;
	}
	if (*(name-1) == '.')
		*(name-1) = '\0';

	return tmp;
}

#ifdef USE_REGEXP
int
check_filter(const char *s, regex_t *rgp, const char *text) {

	if (!s)
		return(1);
	else if (*s == '!') {
		if (!regexec(rgp, text, 0, NULL, 0))
			return(0);
		else
			return(1);
	} else if (!regexec(rgp, text, 0, NULL, 0))
		return(1);
	else
		return(0);
}
#else
int
check_filter(const char *s, const char *text) {

	if (!s)
		return(1);
	else if (*s == '!') {
		if (strstr(text, s+1))
			return(0);
		else
			return(1);
	} else if (strstr(text, s))
		return(1);
	else
		return(0);
}
#endif

/*
 * Printing routine for Custom Log format
 */
void
printclog(time_t sma_time, const char *host, int size, const char *id,
	const char *from, const char *frelay, const char *to,
	const char *trelay, int status) {

	/* time struct: */
	struct tm *tmptime;

	/* Month tab */
	const char *montab[] = {
		"Jan", "Feb", "Mar", "Apr", "May",
		"Jun", "Jul", "Aug", "Sep", "Oct",
		"Nov", "Dec"
	};

	/* Temporary pointer */
	const char *frm;

	/* default format: */
	if (!cfchar) {
	    fprintf(ofp, "%d %s %d %s %s %s %s %d\n",
		(int)sma_time, host, size, from, frelay, to, trelay, status);

	/* We have the format string */
	} else {
		/* Break down the time struct: */
		tmptime = localtime(&sma_time);

		for (frm = cfchar; *frm != '\0'; frm++)
		switch (*frm) {
			/*
			 * If the char after '%'-char is something we know
			 * print it, otherwise print literally
			 */
			case '%':
				if (*(frm+1))
					switch (*(frm+1)) {
						case 'd':
							fprintf(ofp,"%.2d",
							  tmptime->tm_mday);
							frm++;
							break;
						case 'D':
							fprintf(ofp,"%s",
							  stripn(asctime(tmptime)));
							frm++;
							break;
						case 'f':
							fprintf(ofp,"%s", from);
							frm++;
							break;
						case 'F':
							fprintf(ofp, "%s",
								frelay);
							frm++;
							break;
						case 'h':
							fprintf(ofp,"%.2d",
							  tmptime->tm_hour);
							frm++;
							break;
						case 'H':
							fprintf(ofp,"%s", host);
							frm++;
							break;
						case 'i':
							fprintf(ofp,"%s", id);
							frm++;
							break;
						case 'm':
							fprintf(ofp,"%.2d",
							  tmptime->tm_mon+1);
							frm++;
							break;
						case 'n':
							fprintf(ofp,"%.2d",
							  tmptime->tm_min);
							frm++;
							break;
						case 'M':
							fprintf(ofp,"%s",
							   montab[tmptime->tm_mon]);
							frm++;
							break;
						case 's':
							fprintf(ofp,"%.2d",
							  tmptime->tm_sec);
							frm++;
							break;
						case 'S':
							fprintf(ofp,"%d",
								status);
							frm++;
							break;
						case 't':
							fprintf(ofp,"%s", to);
							frm++;
							break;
						case 'T':
							fprintf(ofp,"%s",
								trelay);
							frm++;
							break;
						case 'U':
							fprintf(ofp,"%d",
								(int)sma_time);
							frm++;
							break;
						case 'z':
							fprintf(ofp,"%d", size);
							frm++;
							break;
						case 'y':
							fprintf(ofp,"%d",
							  tmptime->tm_year + 1900);
							frm++;
							break;
						case '%':
							fputc('%', ofp);	
							frm++;
							break;
						default:
							fputc('%', ofp);
							break;
					}
				break;

			/* backslashed special characters: */
			case '\\':
				if (*(frm+1))
					switch (*(frm+1)) {
						case 'n':
							fputc('\n', ofp);
							frm++;
							break;
						case 't':
							fputc('\t', ofp);
							frm++;
							break;
						case '\\':
							fputc('\\', ofp);	
							frm++;
							break;
						default:
							fputc(*(frm+1), ofp);
							frm++;
							break;
					}
				break;
			/*
			 * If not flag or any other special char, copy the
			 * format string character to stream:
			 */
			default:
				fputc(*frm, ofp);
				break;
		}
	/* End line always with newline: */
	fputc('\n', ofp);
	/* Flush output stream */
	fflush(ofp);
	}
}




syntax highlighted by Code2HTML, v. 0.9.1