/*
 *   $Id$
 *   Copyright (C) 2002 Olafur Osvaldsson
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   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.
 */

/* The following defines must be externally defined to have any effect:
 *
 * ADD_HEADER - add a X-Filter header to the message
 *
 * LOG_ENVFROM - when a mail msg is rejected, log the envelope from address
 *		(otherwise just the attachment filename and pattern are logged).
 *
 * DEBUG - attempt to create a copy of each mail msg in /tmp, and remove
 *		it when this filter terminates normally.  If this filter
 *		crashes, the triggering (presumed) msg is available for debugging.
 *
 * DMALLOC - When DEBUG is also defined, use a debugging malloc, so as to
 *		detect memory leaks.
 *
 * MUST_ARCHIVE - When DEBUG is also defined, abort this filter with TEMPFAIL,
 *		if we can't create and open a msg archive file in /tmp.  If
 *		undefined (the default) we just syslog the inability to have
 *		an archive copy.
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <signal.h>
#include <regex.h>

#include <syslog.h>

#if defined(DEBUG) && defined(DMALLOC)
#include <dmalloc.h>
#endif				/* DEBUG && DMALLOC */

#ifdef MFAPI_H
#include <mfapi.h>
#else
#include <libmilter/mfapi.h>
#endif				/* MFAPI_H */

#include "rfc2047.h"
#include "rfc2231.h"

#if     HAVE_CONFIG_H
#include        "config.h"
#endif

#define CRLF "\r\n"
#define PATTERN_HASH_SIZE 1024

#define MAX_LINE_LENGTH 1024
#define OVERLAP_SIZE MAX_LINE_LENGTH
#define BUFFER_SIZE (MILTER_CHUNK_SIZE+OVERLAP_SIZE)
#define MAX_ADDRESS_SIZE 63

#ifndef true
#ifndef SMFIF_QUARANTINE
#ifndef __bool_true_false_are_defined
typedef int	bool;		/* kludge: typedef unneeded in 8.13.0, when
				 * quarantining was introduced */
#endif
#endif
#define false  0
#define true   1
#endif				/* ! true */

static const char rcsid[] = "$Id$";

char           *progname;

struct mlfiPriv {
    u_char	    buffer[BUFFER_SIZE + 1];
    u_char	    overlap[OVERLAP_SIZE + 1];
#ifdef LOG_ENVFROM
    u_char	    from_address[MAX_ADDRESS_SIZE + 1];
#endif
#ifdef DEBUG
    char           *mlfi_fname;
    FILE           *mlfi_fp;
#endif				/* DEBUG */
};


#define MLFIPRIV        ((struct mlfiPriv *) smfi_getpriv(ctx))

extern sfsistat	mlfi_cleanup(SMFICTX *, bool);

char           *patterns[PATTERN_HASH_SIZE];	/* patterns */
int		pattern_count = 0;


/*
 * Check if the pointer is NULL and free it otherwise
 */
void
safe_free(void *p)
{
    if (p) {
	free(p);
	p = 0;
    }
}

#if !HAVE_STRCASESTR
/*
 * This is the same as strstr function just ignores case
 * I got this from the ht://Dig package
 */
char           *
strcasestr(const char *s, const char *pattern)
{
    int		    length = strlen(pattern);

    while (*s) {
	if (strncasecmp(s, pattern, length) == 0)
	    return (char *)s;
	s++;
    }
    return 0;
}
#endif				/* !HAVE_STRCASESTR */

/*
 * Match string against the extended regular expression in pattern, treating
 * errors as no match.
 *
 * return 1 for match, 0 for no match
 */
int
match(string, pattern)
    const char     *string;
    char           *pattern;
{
    int		    status;
    regex_t	    re;
    int		    cflags = 0;
    char           *pat = NULL;
    char           *args = NULL;
    char           *tmp_pat;

    tmp_pat = pattern;

    /*
     * Here we need to parse a string wich looks like "/pattern/args" args
     * can be empty or a series of letters, only 'i' supported now
     */
    switch (*pattern) {
    case '/':
	pattern++;
	pat = pattern;
	break;
    default:
	/* Exit with error if the string doesn't start with a slash */
	safe_free(tmp_pat);
	return 0;
    }

    while (*pattern) {
	if (*pattern == '/') {
	    args = (pattern + 1);
	    *pattern = '\0';
	    break;
	}
	pattern++;
    }

    while (*args) {
	switch (*args) {
	case 'i':
	    cflags |= REG_ICASE;
	    break;
	}
	args++;
    }


    if (regcomp(&re, pat, cflags | REG_EXTENDED | REG_NOSUB) != 0) {
	safe_free(tmp_pat);
	return (0);		/* report error */
    }
    status = regexec(&re, string, (size_t) 0, NULL, 0);
    regfree(&re);
    if (status != 0) {
	safe_free(tmp_pat);
	return (0);		/* report error */
    }

    safe_free(tmp_pat);
    return (1);
}

/* Return: 0 => no match, 1 => match, so reject msg, -1 => error, temp-fail msg */
int
check_line(ctx, line)
    SMFICTX        *ctx;
    const char     *line;
{
    char           *namestr;	/* Filename string (filename.vbs) */
    char           *end_quote;	/* The search for the second quote */
    char           *encoded_name;	/* Encoded filename */
    char           *decoded_name;	/* Decoded filename */
    char	    m      [MAX_LINE_LENGTH + 1];	/* Error message to be
							 * returned */
    char	    charset[128];	/* The encoding charset */
    int		    got_name = 0;	/* Set to 1 if we've found a name to
					 * check */
    int		    i;

    decoded_name = NULL;

    if (strncasecmp(line, "Content-", 8) == 0) {
	namestr = strcasestr(line, "name");

	if ((namestr != NULL) && (strlen(namestr) > 4)) {
	    namestr += 4;	/* move closer to the filename */

	    /* Some progs put * in front of the = */
	    if (*namestr == '*')
		namestr++;

	    /* Some progs put space in front of the = */
	    while (*namestr == ' ')
		namestr++;

	    /* This we don't need */
	    if (*namestr == '=')
		namestr++;

	    /* Some progs put space behind the = */
	    while (*namestr == ' ')
		namestr++;

	    /* Get rid of the first quote if its there */
	    if (*namestr == '\"') {
		namestr++;

		/* There should be a second quote if everything is ok */
		end_quote = strchr(namestr, '\"');
		if (end_quote == NULL) {
		    /* None found, something is wrong, just give it an OK */
		    return 0;
		}
		else {
		    /* Get rid of the quote */
		    *end_quote = '\0';
		}
	    }

	    if (strncasecmp(namestr, "=?", 2) == 0) {
		/* This should return the same string if not encoded */
		decoded_name = rfc2047_decode_simple(namestr);
	    }
	    else {
		encoded_name = rfc2231_get_charset(namestr, charset, sizeof(charset));
		if ((decoded_name = malloc(strlen(encoded_name) + 1)) == NULL) {
		    return -1;
		}
		rfc2231_decode(decoded_name, encoded_name);
	    }

	    got_name = 1;
	}
    }
    else if (strncasecmp(line, "begin ", 6) == 0) {
	/* Find the first whitespace */
	namestr = strchr(line, ' ');

	/* If we don't find a whitespace, we are in the wrong place */
	if (namestr == NULL)
	    return 0;

	/* Move ahead one char */
	namestr++;

	/* Find the next whitespace */
	namestr = strchr(namestr, ' ');

	/* If we don't find a whitespace, we are in the wrong place */
	if (namestr == NULL)
	    return 0;

	/* Move ahead one char */
	namestr++;

	/* Now we should have our filename from here to the end of line */

	/* Get rid of the first quote if its there */
	if (*namestr == '\"') {
	    namestr++;

	    /* There should be a second quote if everything is ok */
	    end_quote = strchr(namestr, '\"');
	    if (end_quote == NULL) {
		/* None found, something is wrong, just give it an OK */
		return 0;
	    }
	    else {
		/* Get rid of the quote */
		*end_quote = '\0';
	    }
	}

	if (strncasecmp(namestr, "=?", 2) == 0) {
	    /* This should return the same string if not encoded */
	    decoded_name = rfc2047_decode_simple(namestr);
	}
	else {
	    encoded_name = rfc2231_get_charset(namestr, charset, sizeof(charset));
	    if ((decoded_name = malloc(strlen(encoded_name) + 1)) == NULL) {
		return -1;
	    }
	    rfc2231_decode(decoded_name, encoded_name);
	}

	got_name = 1;
    }
    else {
	return 0;
    }

    if (got_name == 1) {
	for (i = 0; i < pattern_count; i++) {

	    if (match(decoded_name, strdup(patterns[i])) == 1) {
		/*
		 * trim long decode_name; print the end: we expect patterns
		 * to match suffixes
		 */
		char           *start_of_output = decoded_name;
		int		length = strlen(decoded_name);
		if (length > 100) {
		    start_of_output = decoded_name + length - 90;
		    strncpy(start_of_output, "...", 3);
		}
#ifdef LOG_ENVFROM
		syslog(LOG_INFO, "rejecting attachment \"%.100s\" from %s on pattern %d: \"%.30s\"",
		   start_of_output, MLFIPRIV->from_address, i, patterns[i]);
#else
		syslog(LOG_INFO, "rejecting attachment \"%.100s\" on pattern %d: \"%.30s\"",
		       start_of_output, i, patterns[i]);
#endif
		sprintf(m, "Sorry, I can't accept messages with proscribed attachments. (%.100s)", start_of_output);
		smfi_setreply(ctx, "554", "5.6.1", m);
		safe_free(decoded_name);
		return 1;
	    }
	}

    }

    safe_free(decoded_name);
    return 0;
}

int
read_pattern_file(void)
{
    FILE           *pat_file = NULL;	/* Input file */
    char	    line   [255];	/* Input line */
    char           *result;	/* result from fgets */
    int		    count = 0;
    int		    i;

    /*
     * Empty the list of patterns
     */
    for (i = 0; i < pattern_count; i++) {
	safe_free(patterns[i]);
	patterns[i] = NULL;
    }

    /*
     * Read in data
     */
    pat_file = fopen(PAT_FILE, "r");
    if (pat_file == NULL) {
	fprintf(stderr, "Unable to open pattern file\n");
	return (8);
    }

    while (1) {
	result = fgets(line, sizeof(line), pat_file);

	if (result == NULL) {
	    if (!feof(pat_file)) {
		fprintf(stderr,
			"Error while reading pattern file!\n");
		return (8);
	    }
	    else {
		break;
	    }
	}

	if (line[0] != '#') {

	    if (strlen(line) > 1) {
		while ((line[strlen(line) - 1] == '\n') ||
		       (line[strlen(line) - 1] == '\r')) {
		    line[strlen(line) - 1] = '\0';
		}

		patterns[count] = strdup(line);
		count++;
	    }
	}
    }

    pattern_count = count;
    fclose(pat_file);
    syslog(LOG_INFO, "Read %d patterns from file %s\n", count, PAT_FILE);
    return (0);
}

void
handler(sig)
    int		    sig;
{
    read_pattern_file();
}

int
write_pid_file(void)
{
    FILE           *pid_file;	/* output file */
    static pid_t    pid = 0;	/* Our PID */

    pid_file = fopen(PID_FILE, "w");
    if (pid_file == NULL) {
	fprintf(stderr, "Unable to open pid file\n");
	exit(8);
    }

    pid = getpid();

    fprintf(pid_file, "%d", (int)pid);

    fclose(pid_file);
    return (0);
}

void
usage(program_name)
    char           *program_name;
{
    char           *real_name = NULL;
    char           *s;

    real_name = strdup(program_name);

    while (1) {
	s = strchr(real_name, '/');

	if (s == '\0')
	    break;
	else
	    real_name = (s + 1);
    }

    fprintf(stderr, "Usage is: %s [options]\n", real_name);
    fprintf(stderr, "\n");
    fprintf(stderr, "Options:\n");
    fprintf(stderr, "  -h             Usage Information\n");
    fprintf(stderr, "  -n             Don't fork\n");
    fprintf(stderr, "  -p <portinfo>  Port to listen to\n");
    fprintf(stderr, "\n");
    fprintf(stderr, "Examples:\n");
    fprintf(stderr,
      "  %s -p inet:26@localhost   (Listen to ipv4 port 26 on localhost)\n",
	    real_name);
    //fprintf(stderr,
    //"  %s -p inet6:26@localhost  (Listen to ipv6 port 26 on localhost)\n",
	      //real_name);
    fprintf(stderr,
      "  %s -p local:/var/run/noattach.sock    (Listen on a unix socket)\n",
	    real_name);
    fprintf(stderr, "\n");
    exit(8);
}

sfsistat
mlfi_envfrom(ctx, envfrom)
    SMFICTX        *ctx;
    char          **envfrom;
{
    struct mlfiPriv *priv;
#ifdef DEBUG
    int		    fd = -1;

#ifdef DMALLOC
    dmalloc_debug(0x20403);
#endif
#endif				/* DEBUG */

    /* allocate some private memory */
    if ((priv = malloc(sizeof *priv)) == NULL) {
	/* can't accept this message right now */
	(void)mlfi_cleanup(ctx, false);
	return SMFIS_TEMPFAIL;
    }
    memset(priv, '\0', sizeof *priv);

    strcpy(priv->buffer, "");
    strcpy(priv->overlap, "");
#ifdef LOG_ENVFROM
    if (envfrom != NULL && envfrom[0] != NULL) {
	strncpy(priv->from_address, envfrom[0], MAX_ADDRESS_SIZE);
	priv->from_address[MAX_ADDRESS_SIZE] = '\0';	/* in case maximal len */
    }
    else
	strcpy(priv->from_address, "");
#endif

#ifdef DEBUG
    priv->mlfi_fp = NULL;
    /* try to open a file to store this message */
    priv->mlfi_fname = strdup("/tmp/noattach.XXXXXXXX");
    if (priv->mlfi_fname == NULL) {
#ifdef MUST_ARCHIVE
	(void)mlfi_cleanup(ctx, false);
	safe_free(priv);
	return SMFIS_TEMPFAIL;
#else
	syslog(LOG_INFO, "Cannot allocate memory for debug filename");
#endif
    }
    else if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
	     (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) {
	if (fd >= 0)
	    (void)close(fd);
	safe_free(priv->mlfi_fname);
#ifdef MUST_ARCHIVE
	(void)mlfi_cleanup(ctx, false);
	safe_free(priv);
	return SMFIS_TEMPFAIL;
#else
	syslog(LOG_INFO, "Cannot create/open debug filename");
#endif
    }
#endif				/* DEBUG */

    /* save the private data */
    smfi_setpriv(ctx, priv);

    /* continue processing */
    return SMFIS_CONTINUE;
}

sfsistat
mlfi_header(ctx, headerf, headerv)
    SMFICTX        *ctx;
    char           *headerf;
    char           *headerv;
{
#ifdef DEBUG
    /* write the header to the log file */
    if (MLFIPRIV->mlfi_fp != NULL)
	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
#endif				/* DEBUG */

    /* continue processing */
    return SMFIS_CONTINUE;
}

sfsistat
mlfi_eoh(ctx)
    SMFICTX        *ctx;
{
#ifdef DEBUG
    /* output the blank line between the header and the body */
    if (MLFIPRIV->mlfi_fp != NULL)
	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
#endif				/* DEBUG */

    /* continue processing */
    return SMFIS_CONTINUE;
}

sfsistat
mlfi_body(ctx, bodyp, bodylen)
    SMFICTX        *ctx;
    u_char         *bodyp;
    size_t	    bodylen;
{
    struct mlfiPriv *priv = MLFIPRIV;
    int		    max_lines = 1024;	/* Default maximum, will be increased */
    u_char        **lines;	/* The lines we are working with */
    u_char         *line;	/* Current line we are working with */
    u_char         *last_line = NULL;	/* The last line we got */
    char           *last;	/* Last line found with strtok_r() */
    int		    num_lines = 0;	/* Number of lines */
    int		    line_len = 0;	/* Length of line for realloc */
    int		    i     , cl_err;

    if ((ctx == NULL) || (bodyp == NULL))
	return SMFIS_CONTINUE;

    if (priv->overlap != NULL) {
	strncpy(priv->buffer, priv->overlap, BUFFER_SIZE);
	strncat(priv->buffer, bodyp, bodylen);
    }
    else {
	size_t		copy_size = (bodylen < BUFFER_SIZE ? bodylen : BUFFER_SIZE);
	strncpy(priv->buffer, bodyp, copy_size);
    }

#ifdef DEBUG
    /* output body block to log file */
    if (priv->mlfi_fp != NULL && fwrite(bodyp, bodylen, 1, priv->mlfi_fp) <= 0) {
	/* write failed */
	(void)mlfi_cleanup(ctx, false);
	return SMFIS_TEMPFAIL;
    }
#endif				/* DEBUG */

    if ((lines = malloc(max_lines * sizeof(u_char *))) == NULL) {
	/* can't accept this message right now */
	(void)mlfi_cleanup(ctx, false);
	return SMFIS_TEMPFAIL;
    }

    for (line = (u_char *) strtok_r(priv->buffer, CRLF, &last);
	 line != NULL;
	 line = (u_char *) strtok_r(NULL, CRLF, &last)) {
	if (num_lines >= max_lines) {
	    max_lines += 1024;
	    if ((lines = realloc(lines,
				 (max_lines * sizeof(u_char *)))) == NULL) {
		/* can't accept this message right now */
		for (i = 0; i < num_lines; i++)
		    safe_free(lines[i]);
		safe_free(lines);
		(void)mlfi_cleanup(ctx, false);
		return SMFIS_TEMPFAIL;
	    }
	}
	if ((lines[num_lines] = malloc(MAX_LINE_LENGTH + 1)) == NULL) {
	    /* can't accept this message right now */
	    for (i = 0; i < num_lines; i++)
		safe_free(lines[i]);
	    safe_free(lines);
	    (void)mlfi_cleanup(ctx, false);
	    return SMFIS_TEMPFAIL;
	}
	strncpy(lines[num_lines], line, MAX_LINE_LENGTH);
	lines[num_lines][MAX_LINE_LENGTH] = '\0';	/* ensure NULL for
							 * maximal lines */

	num_lines++;
    }

    for (i = 0; i < num_lines; i++) {
	line = lines[i];

	if ((last_line != NULL) && isspace(*line) &&
	    (strncasecmp(last_line, "Content-", 8) == 0)) {
	    /*
	     * This looks like a continued MIME header line So we append it
	     * to the previous line
	     */
	    line_len = ((strlen(last_line) + 1) + 1 + (strlen(line) + 1));
	    if ((last_line = realloc(last_line, line_len)) == NULL) {
		/* can't accept this message right now */
		for (; i < num_lines; i++)
		    safe_free(lines[i]);
		safe_free(lines);
		(void)mlfi_cleanup(ctx, false);
		return SMFIS_TEMPFAIL;
	    }
	    sprintf(last_line, "%s %s", last_line, line);
	    safe_free(line);
	    continue;
	}
	else {
	    if ((last_line != NULL) && (strlen(last_line) > 1)) {
		/* Check the line */
		cl_err = check_line(ctx, last_line);

		if (cl_err == 1) {
		    safe_free(last_line);
		    for (; i < num_lines; i++)
			safe_free(lines[i]);
		    safe_free(lines);
		    (void)mlfi_cleanup(ctx, true);
		    return SMFIS_REJECT;
		}
		else if (cl_err == -1) {
		    /* can't accept this message right now */
		    safe_free(last_line);
		    for (; i < num_lines; i++)
			safe_free(lines[i]);
		    safe_free(lines);
		    (void)mlfi_cleanup(ctx, false);
		    return SMFIS_TEMPFAIL;
		}
	    }
	}

	safe_free(last_line);
	last_line = line;
    }

    safe_free(lines);

    /*
     * if we get a full 64k buffer the last line is probably continued in the
     * next buffer
     */
    if ((last_line != NULL) && (bodylen == MILTER_CHUNK_SIZE)) {
	strncpy(priv->overlap, last_line, OVERLAP_SIZE);
    }
    else if (last_line != NULL) {
	/* We need to check the last line */
	cl_err = check_line(ctx, last_line);

	if (cl_err == 1) {
	    safe_free(last_line);
	    (void)mlfi_cleanup(ctx, true);
	    return SMFIS_REJECT;
	}
	else if (cl_err == -1) {
	    /* can't accept this message right now */
	    safe_free(last_line);
	    (void)mlfi_cleanup(ctx, false);
	    return SMFIS_TEMPFAIL;
	}
    }

    safe_free(last_line);

    /* continue processing */
    return SMFIS_CONTINUE;
}

sfsistat
mlfi_eom(ctx)
    SMFICTX        *ctx;
{
    return mlfi_cleanup(ctx, true);
}

sfsistat
mlfi_close(ctx)
    SMFICTX        *ctx;
{
    return SMFIS_ACCEPT;
}

sfsistat
mlfi_abort(ctx)
    SMFICTX        *ctx;
{
    return mlfi_cleanup(ctx, false);
}

sfsistat
mlfi_cleanup(ctx, ok)
    SMFICTX        *ctx;
    bool	    ok;
{
    /*
     * If we get here everything must have succeeded so we finish up and
     * delete the file if needed
     */
    sfsistat	    rstat = SMFIS_CONTINUE;
    struct mlfiPriv *priv = MLFIPRIV;
#if defined(ADD_HEADER)
    char	    host   [512];
    char	    hbuf   [1024];
#endif

    if (priv == NULL)
	return rstat;

#ifdef DEBUG
    /* close the archive file */
    if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) {
	/* failed; we have to wait until later */
	rstat = SMFIS_TEMPFAIL;
	(void)unlink(priv->mlfi_fname);
    }
    else if (ok) {
	/* we only keep those that crashed us -- delete the archive file */
	(void)unlink(priv->mlfi_fname);

    }
    else {
	/* message was aborted -- delete the archive file */
	(void)unlink(priv->mlfi_fname);
    }

    /* release private memory */
    safe_free(priv->mlfi_fname);

#endif				/* DEBUG */

#if defined(ADD_HEADER)
    /* add a header to the message announcing our presence */
    if (gethostname(host, sizeof host) < 0)
	snprintf(host, sizeof host, "localhost");
    snprintf(hbuf, sizeof hbuf, "%s (%s) on %s", PACKAGE_NAME, PACKAGE_VERSION, host);
    smfi_addheader(ctx, "X-Filter", hbuf);
#endif

    /* release private memory */
    free(priv);
    smfi_setpriv(ctx, NULL);

#if defined(DEBUG) && defined(DMALLOC)
    dmalloc_shutdown();
#endif				/* DEBUG && DMALLOC */

    /* return status */
    return rstat;
}

struct smfiDesc	smfilter =
{
    "noattach-Filter",		/* filter name */
    SMFI_VERSION,		/* version code -- do not change */
    SMFIF_ADDHDRS,		/* flags */
    NULL,			/* connection info filter */
    NULL,			/* SMTP HELO command filter */
    mlfi_envfrom,		/* envelope sender filter */
    NULL,			/* envelope recipient filter */
    mlfi_header,		/* header filter */
    mlfi_eoh,			/* end of header */
    mlfi_body,			/* body block filter */
    mlfi_eom,			/* end of message */
    mlfi_abort,			/* message aborted */
    mlfi_close			/* connection cleanup */
};


char		cmd_line  [1024];

int
main(argc, argv)
    int		    argc;
    char           *argv[];
{
    int		    c;
    int		    return_code;/* Return code from smfi_main */
    int		    lets_fork = 1;	/* Should we fork or not */
    const char     *args = "hnp:";

    struct sigaction act;
    struct sigaction oldact;

    act.sa_handler = &handler;
    sigfillset(&act.sa_mask);
    act.sa_flags = SA_RESTART;

    if (sigaction(SIGUSR1, &act, &oldact) == -1) {
	perror("Error from sigaction() - SIGUSR1 cannot be used to reread pattern file");
    }

    progname = strrchr(argv[0], '/');
    if (progname)
	progname++;
    else
	progname = argv[0];
    progname = strdup(progname);

    /* Process command line options */
    while ((c = getopt(argc, argv, args)) != -1) {
	switch (c) {
	case 'n':
	    lets_fork = 0;
	    break;

	case 'p':
	    if (optarg == NULL || *optarg == '\0') {
		(void)fprintf(stderr, "Illegal conn: %s\n\n",
			      optarg);
		usage(argv[0]);
		exit(EX_USAGE);
	    }
	    if (smfi_setconn(optarg) != MI_SUCCESS) {	/* sets errno? */
		fprintf(stderr, "%s: connection %s: %s\n", progname,
			optarg, strerror(errno));
		exit(EX_USAGE);
	    }
	    break;

	case 'h':
	    usage(argv[0]);

	default:
	    usage(argv[0]);

	}
    }

    if (lets_fork == 1) {
	switch (fork()) {
	case -1:
	    fprintf(stderr, "ERROR: fork()\n");
	    exit(3);
	case 0:
	    close(STDIN_FILENO);
	    close(STDOUT_FILENO);
	    close(STDERR_FILENO);
	    if (setsid() == -1)
		exit(4);
	    break;
	default:
	    return 0;
	}

	/* Don't write the pid file unless we are forking */
	write_pid_file();
    }

    openlog(progname, LOG_PID, LOG_MAIL);
    syslog(LOG_INFO, "starting");

    if (smfi_register(smfilter) == MI_FAILURE) {
	fprintf(stderr, "smfi_register failed\n");
	if (lets_fork == 1)
	    unlink(PID_FILE);
	exit(EX_UNAVAILABLE);
    }

    return_code = read_pattern_file();

    if (return_code > 0) {
	if (lets_fork == 1)
	    unlink(PID_FILE);
	exit(return_code);
    }

    return_code = smfi_main();

    if (lets_fork == 1)
	unlink(PID_FILE);

    return return_code;
}


syntax highlighted by Code2HTML, v. 0.9.1