"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "bogom-1.9.2/milter.c" of archive bogom-1.9.2.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /* $Id: milter.c,v 1.40 2008/06/25 21:41:42 reidrac Exp reidrac $ */
    2 
    3 /*
    4 * bogom, simple sendmail milter to interface bogofilter
    5 * Copyright (C) 2004-2007 Juan J. Martinez <jjm*at*usebox*dot*net>
    6 *
    7 * This program is free software; you can redistribute it and/or modify
    8 * it under the terms of the GNU General Public License Version 2 as
    9 * published by the Free Software Foundation.
   10 *
   11 * This program is distributed in the hope that it will be useful,
   12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 * GNU General Public License for more details.
   15 *
   16 * You should have received a copy of the GNU General Public License
   17 * along with this program; if not, write to the Free Software
   18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   19 *
   20 */
   21 
   22 #include <sys/param.h>
   23 #include <sys/stat.h>
   24 #include <sys/wait.h>
   25 #include <sys/socket.h>
   26 #include <sys/time.h>
   27 #include <netinet/in.h>
   28 #include <arpa/inet.h>
   29 #include <errno.h>
   30 #include <stdio.h>
   31 #include <stdlib.h>
   32 #include <string.h>
   33 #include <unistd.h>
   34 #include <pwd.h>
   35 #include <syslog.h>
   36 #include <regex.h>
   37 #include <time.h>
   38 #include <netdb.h>
   39 
   40 #ifdef __sun__
   41 #include <fcntl.h>
   42 #endif
   43 
   44 #include "libmilter/mfapi.h"
   45 #include "conf.h"
   46 
   47 /* defaults */
   48 #ifndef DEF_USER
   49 #define DEF_USER	"bogofilter"
   50 #endif
   51 #ifndef DEF_CONN
   52 #define DEF_CONN	"unix:/var/spool/bogofilter/milter.sock"
   53 #endif
   54 #ifndef DEF_CONF
   55 #define DEF_CONF	"/etc/bogom.conf"
   56 #endif
   57 #ifndef DEF_PIDFILE
   58 #define DEF_PIDFILE	"/var/spool/bogofilter/bogom.pid"
   59 #endif
   60 
   61 struct mlfiPriv
   62 {
   63 	FILE *f;
   64 	char *fullpath;
   65 	char *filename;
   66 	char *subject;
   67 	int eom;
   68 	size_t bodylen;
   69 	int old_headers;
   70 };
   71 
   72 sfsistat mlfi_connect(SMFICTX *, char *, _SOCK_ADDR *);
   73 sfsistat mlfi_envfrom(SMFICTX *, char **);
   74 sfsistat mlfi_envrcpt(SMFICTX *, char **);
   75 sfsistat mlfi_header(SMFICTX *, char *, char *);
   76 sfsistat mlfi_eoh(SMFICTX *);
   77 sfsistat mlfi_body(SMFICTX *, unsigned char *, size_t);
   78 sfsistat mlfi_eom(SMFICTX *);
   79 sfsistat mlfi_abort(SMFICTX *);
   80 sfsistat mlfi_close(SMFICTX *);
   81 void mlfi_clean(SMFICTX *);
   82 void usage(const char *);
   83 int to_maildir(char *, char *);
   84 char *hostname_tmp();
   85 
   86 #ifdef __sun__
   87 int daemon(int, int);
   88 #endif
   89 
   90 struct smfiDesc smfilter=
   91 {
   92 	"bogom",	/* filter name */
   93 	SMFI_VERSION,	/* version code -- do not change */
   94 	SMFIF_ADDHDRS | SMFIF_CHGHDRS | /* flags -- add and modify headers */
   95 	SMFIF_ADDRCPT,	/* -- add rcpt */
   96 	mlfi_connect,	/* connection info filter */
   97 	NULL,		/* SMTP HELO command filter */
   98 	mlfi_envfrom,	/* envelope sender filter */
   99 	mlfi_envrcpt,	/* envelope recipient filter */
  100 	mlfi_header,	/* header filter */
  101 	mlfi_eoh,	/* end of header */
  102 	mlfi_body,	/* body block filter */
  103 	mlfi_eom,	/* end of message */
  104 	mlfi_abort,	/* message aborted */
  105 	mlfi_close	/* connection cleanup */
  106 };
  107 
  108 struct re_list
  109 {
  110 	regex_t p;
  111 	const char *pat;
  112 	struct re_list *n;
  113 };
  114 
  115 #define new_re_list(x) do {\
  116 		x=(struct re_list *) \
  117 			malloc(sizeof(struct re_list));\
  118 		x->n=NULL;\
  119 	} while(0)
  120 
  121 static const char 	rcsid[]="$Id: milter.c,v 1.40 2008/06/25 21:41:42 reidrac Exp reidrac $";
  122 
  123 static int		mode=SMFIS_CONTINUE;
  124 static int		train=0;
  125 static int		verbose=0;
  126 static int		debug=0;
  127 static int		spamicity=0;
  128 static size_t		bodylimit=0;
  129 static const char 	*bogo="/usr/local/bin/bogofilter";
  130 static const char	*exclude=NULL;
  131 static const char	*subj_tag=NULL;
  132 static const char	*forward_spam=NULL;
  133 static char		*quarantine_mdir=NULL;
  134 
  135 static char		*reject=NULL;
  136 
  137 static struct re_list	*re_c=NULL;	/* re connection */
  138 static struct re_list	*re_f=NULL;	/* re envfrom */
  139 static struct re_list	*re_r=NULL;	/* re envrcpt */
  140 
  141 #ifdef __sun__
  142 int
  143 daemon(int nochdir, int noclose)
  144 {
  145 	int fd;
  146 
  147 	switch(fork())
  148 	{
  149 		case 0:
  150 			break;
  151 
  152 		case -1:
  153 			return -1;
  154 
  155 		default:
  156 			_exit(0);
  157 	}
  158 
  159 	if(setsid()==-1)
  160 		return -1;
  161 
  162 	if(!nochdir && chdir("/"))
  163 		return -1;
  164 
  165 	if(!noclose)
  166 	{
  167 		fd=open("/dev/null", O_RDWR, 0);
  168 		if(fd==-1)
  169 			return -1;
  170 
  171 		dup2(fd, fileno(stdin));
  172 		dup2(fd, fileno(stdout));
  173 		dup2(fd, fileno(stderr));
  174 	}
  175 
  176 	return 0;
  177 }
  178 #endif
  179 
  180 sfsistat
  181 mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
  182 {
  183 	struct mlfiPriv *priv;
  184 	struct re_list *tre;	/* temporal iterator */
  185 
  186 	const void *mysaddr=NULL;
  187 	char host[INET6_ADDRSTRLEN];
  188 
  189 	if(hostaddr)
  190 	{
  191 		switch(hostaddr->sa_family)
  192 		{
  193 			default:
  194 				syslog(LOG_ERR, "mlfi_connet: unsupported sa_family");
  195 				break;
  196 
  197 			case AF_INET:
  198 				mysaddr=(const void *)&((struct sockaddr_in *)hostaddr)
  199 					->sin_addr.s_addr;
  200 				break;
  201 
  202 			case AF_INET6:
  203 				mysaddr=(const void *)&((struct sockaddr_in6 *)hostaddr)
  204 					->sin6_addr;
  205 				break;
  206 		}
  207 
  208 		if(!inet_ntop(hostaddr->sa_family, mysaddr, host, sizeof(host)))
  209 		{
  210 			syslog(LOG_ERR, "mlfi_connect: inet_ntop failed");
  211 			strcpy(host, "*");
  212 		}
  213 	}
  214 	else
  215 		strcpy(host, "*");
  216 
  217 	if(debug)
  218 		syslog(LOG_DEBUG, "connection from %s [ %s ]", hostname, host);
  219 
  220 	for(tre=re_c; tre; tre=tre->n)
  221 	{
  222 		if(!regexec(&tre->p, hostname, 0, NULL, 0))
  223 		{
  224 			if(verbose)
  225 				syslog(LOG_INFO,
  226 				"accepted due pattern match (connect): %s",
  227 						tre->pat);
  228 
  229 			return SMFIS_ACCEPT;
  230 		}
  231 
  232 		if(!regexec(&tre->p, host, 0, NULL, 0))
  233 		{
  234 			if(verbose)
  235 				syslog(LOG_INFO,
  236 				"accepted due pattern match (connect): %s",
  237 						tre->pat);
  238 			return SMFIS_ACCEPT;
  239 		}
  240 	}
  241 
  242 	priv=(struct mlfiPriv *)malloc(sizeof(struct mlfiPriv));
  243 	if(!priv)
  244 	{
  245 		syslog(LOG_ERR, "Unable to get memory: %s",
  246 			strerror(errno));
  247 		return SMFIS_TEMPFAIL;
  248 	}
  249 
  250 	priv->fullpath=NULL;
  251 	priv->filename=NULL;
  252 	priv->subject=NULL;
  253 	priv->f=NULL;
  254 	priv->eom=1;
  255 	priv->old_headers=0;
  256 
  257 	if(smfi_setpriv(ctx, priv)!=MI_SUCCESS)
  258 	{
  259 		syslog(LOG_ERR, "on mlfi_connect: smfi_setpriv");
  260 		return SMFIS_ACCEPT;
  261 	}
  262 
  263 	return SMFIS_CONTINUE;
  264 }
  265 
  266 sfsistat
  267 mlfi_envfrom(SMFICTX *ctx, char **argv)
  268 {
  269 	struct re_list *tre;	/* temporal iterator */
  270 
  271 	if(debug)
  272 		syslog(LOG_DEBUG, "envfrom %s", argv[0]);
  273 
  274 	for(tre=re_f; tre; tre=tre->n)
  275 		if(!regexec(&tre->p, argv[0], 0, NULL, 0))
  276 		{
  277 			if(verbose)
  278 				syslog(LOG_INFO,
  279 				"accepted due pattern match (envfrom): %s",
  280 						tre->pat);
  281 			return SMFIS_ACCEPT;
  282 		}
  283 
  284 	return SMFIS_CONTINUE;
  285 }
  286 
  287 sfsistat
  288 mlfi_envrcpt(SMFICTX *ctx, char **argv)
  289 {
  290 	struct mlfiPriv *priv;
  291 	struct re_list *tre;	/* temporal iterator */
  292 	int fd=-1;
  293 	char *tmp=NULL;
  294 
  295 	if(debug)
  296 		syslog(LOG_DEBUG, "envrcpt %s", argv[0]);
  297 
  298 	for(tre=re_r; tre; tre=tre->n)
  299 		if(!regexec(&tre->p, argv[0], 0, NULL, 0))
  300 		{
  301 			if(verbose)
  302 				syslog(LOG_INFO,
  303 				"accepted due pattern match (envrcpt): %s",
  304 						tre->pat);
  305 			return SMFIS_ACCEPT;
  306 		}
  307 
  308 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  309 	if(!priv)
  310 	{
  311 		syslog(LOG_ERR, "on mlfi_header: smfi_getpriv");
  312 		return SMFIS_ACCEPT;
  313 	}
  314 
  315 	if(priv->eom)
  316 	{
  317 		/* use tmp/ from quarantine maildir if available */
  318 		if(quarantine_mdir)
  319 		{
  320 			tmp=hostname_tmp();
  321 			if(!tmp)
  322 			{
  323 				syslog(LOG_ERR, "Unable to get memory: %s",
  324                                 	strerror(errno));
  325                         	return SMFIS_TEMPFAIL;
  326 			}
  327 
  328 			priv->fullpath=(char *)calloc(strlen(quarantine_mdir)
  329 				+strlen(tmp)+6, sizeof(char));
  330 		}
  331 		else
  332 			priv->fullpath=strdup("/tmp/bogom-msg.XXXXXXXXXX");
  333 
  334 		if(!priv->fullpath)
  335 		{
  336 			syslog(LOG_ERR, "Unable to get memory: %s",
  337 				strerror(errno));
  338 			if(tmp)
  339 				free(tmp);
  340 			return SMFIS_TEMPFAIL;
  341 		}
  342 
  343 		if(quarantine_mdir)
  344 		{
  345 			snprintf(priv->fullpath, strlen(quarantine_mdir)+
  346 				strlen(tmp)+6, "%s/tmp/%s", quarantine_mdir,
  347 					tmp);
  348 			priv->filename=priv->fullpath+strlen(quarantine_mdir)
  349 				+5;
  350 			free(tmp);
  351 		}
  352 
  353 		fd=mkstemp(priv->fullpath);
  354 		if(fd==-1)
  355 		{
  356 			syslog(LOG_ERR, "Unable to create tmp file in %s: %s",
  357 				priv->fullpath, strerror(errno));
  358 
  359 			mlfi_clean(ctx);
  360 			return SMFIS_TEMPFAIL;
  361 		}
  362 
  363 #ifdef __sun__
  364 		priv->f=fdopen(fd, "w+F");
  365 #else
  366 		priv->f=fdopen(fd, "w+");
  367 #endif
  368 		if(!priv->f)
  369 		{
  370 			syslog(LOG_ERR, "Unable to create tmp file in %s: %s",
  371 				priv->fullpath, strerror(errno));
  372 
  373 			if(fd!=-1)
  374 				close(fd);
  375 
  376 			mlfi_clean(ctx);
  377 			return SMFIS_TEMPFAIL;
  378 		}
  379 
  380 		priv->eom=0;
  381 		priv->bodylen=0;
  382 
  383 		if(debug)
  384 			syslog(LOG_DEBUG, "message begin...");
  385 	}
  386 
  387 	return SMFIS_CONTINUE;
  388 }
  389 
  390 sfsistat
  391 mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
  392 {
  393 	struct mlfiPriv *priv;
  394 
  395 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  396 	if(!priv)
  397 	{
  398 		syslog(LOG_ERR, "on mlfi_header: smfi_getpriv");
  399 		return SMFIS_ACCEPT;
  400 	}
  401 
  402 	if(exclude && headerv)
  403 		if(!strcasecmp(headerf, "Subject"))
  404 			if(strstr(headerv, exclude))
  405 			{
  406 				if(verbose)
  407 					syslog(LOG_INFO,
  408 						"exclude string found: '%s'",
  409 						headerv);
  410 				mlfi_clean(ctx);
  411 				return SMFIS_ACCEPT;
  412 			}
  413 
  414 	if(debug)
  415 		syslog(LOG_DEBUG, "header %s [%s]", headerf, headerv);
  416 
  417 	if(headerv && !strcasecmp(headerf, "X-Bogosity"))
  418 		priv->old_headers++;
  419 
  420 	if(subj_tag && headerv)
  421 		if(!strcasecmp(headerf, "Subject"))
  422 		{
  423 			if(priv->subject)
  424 				syslog(LOG_INFO,
  425 					"Subject header not unique");
  426 			else
  427 			{
  428 				priv->subject=strdup(headerv);
  429 				if(!priv->subject)
  430 					syslog(LOG_ERR,
  431 						"Unable to get memory (subject"
  432 						" tag): %s",
  433 						strerror(errno));
  434 			}
  435 		}
  436 
  437 	if(fprintf(priv->f, "%s: %s\n", headerf, headerv)==EOF)
  438 	{
  439 		syslog(LOG_ERR, "failed to write into %s: %s",
  440 			priv->fullpath, strerror(errno));
  441 		mlfi_clean(ctx);
  442 		return SMFIS_TEMPFAIL;
  443 	}
  444 
  445 	return SMFIS_CONTINUE;
  446 }
  447 
  448 sfsistat
  449 mlfi_eoh(SMFICTX *ctx)
  450 {
  451 	struct mlfiPriv *priv;
  452 
  453 	if(debug)
  454 		syslog(LOG_DEBUG, "headers ok");
  455 
  456 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  457 	if(!priv)
  458 	{
  459 		syslog(LOG_ERR, "on mlfi_eoh: smfi_getpriv");
  460 		return SMFIS_ACCEPT;
  461 	}
  462 
  463 	if(fprintf(priv->f, "\n")==EOF)
  464 	{
  465 		syslog(LOG_ERR, "failed to write into %s: %s",
  466 			priv->fullpath, strerror(errno));
  467 		mlfi_clean(ctx);
  468 		return SMFIS_TEMPFAIL;
  469 	}
  470 
  471 	return SMFIS_CONTINUE;
  472 }
  473 
  474 sfsistat
  475 mlfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t bodylen)
  476 {
  477 	struct mlfiPriv *priv;
  478 
  479 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  480 	if(!priv)
  481 	{
  482 		syslog(LOG_ERR, "on mlfi_body: smfi_getpriv");
  483 		return SMFIS_ACCEPT;
  484 	}
  485 
  486 	if(bodylimit)
  487 	{
  488 		if(bodylimit==priv->bodylen)
  489 		{
  490 			if(debug)
  491 				syslog(LOG_DEBUG, "body_limit reached, "
  492 						" %d bytes discarded", bodylen);
  493 
  494 			bodylen=0;
  495 		}
  496 		else
  497 			if(priv->bodylen+bodylen>bodylimit)
  498 			{
  499 				if(debug)
  500 					syslog(LOG_DEBUG, "body_limit reached, "
  501 						" %d bytes discarded",
  502 					bodylen-(bodylimit-priv->bodylen));
  503 
  504 				bodylen=bodylimit-priv->bodylen;
  505 			}
  506 	}
  507 
  508 	if(bodylen>0)
  509 	{
  510 		if(fwrite(bodyp, bodylen, 1, priv->f)!=1)
  511 		{
  512 			syslog(LOG_ERR, "failed to write into %s: %s",
  513 				priv->fullpath, strerror(errno));
  514 			mlfi_clean(ctx);
  515 			return SMFIS_TEMPFAIL;
  516 		}
  517 		else
  518 		{
  519 			if(debug)
  520 				syslog(LOG_DEBUG, "%d body bytes written",
  521 						bodylen);
  522 
  523 			priv->bodylen+=bodylen;
  524 		}
  525 	}
  526 
  527 	return SMFIS_CONTINUE;
  528 }
  529 
  530 sfsistat
  531 mlfi_eom(SMFICTX *ctx)
  532 {
  533 	struct mlfiPriv *priv;
  534 	int status, i;
  535 	char *bogocl, header[64];
  536 	float spamicity_val;
  537 	char *tmp_subj;
  538 	FILE *proc;
  539 
  540 	if(debug)
  541 		syslog(LOG_DEBUG, "...end of message");
  542 
  543 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  544 	if(!priv)
  545 	{
  546 		syslog(LOG_ERR, "on mlfi_eom: smfi_getpriv");
  547 		return SMFIS_ACCEPT;
  548 	}
  549 
  550 	fclose(priv->f);
  551 	priv->f=NULL;
  552 
  553 	bogocl=(char *)malloc(strlen(bogo)+strlen(priv->fullpath)+16);
  554 	if(!bogocl)
  555 	{
  556 		syslog(LOG_ERR, "on mlfi_eom: %s", strerror(errno));
  557 		mlfi_clean(ctx);
  558 		return SMFIS_CONTINUE;
  559 	}
  560 
  561 	sprintf(bogocl, "%s -", bogo);
  562 
  563 	if(train)
  564 		strcat(bogocl, "u");
  565 
  566 	if(verbose)
  567 		strcat(bogocl, "l");
  568 
  569 	if(spamicity)
  570 		strcat(bogocl, "TT");
  571 
  572 	strcat(bogocl, "B ");
  573 	strcat(bogocl, priv->fullpath);
  574 
  575 #ifdef __sun__
  576 	proc=popen(bogocl, "rF");
  577 #else
  578 	proc=popen(bogocl, "r");
  579 #endif
  580 	if(!proc)
  581 	{
  582 		syslog(LOG_ERR, "failed to exec bogofilter: %s",
  583 			strerror(errno));
  584 		free(bogocl);
  585 		mlfi_clean(ctx);
  586 		return SMFIS_CONTINUE;
  587 	}
  588 	free(bogocl);
  589 
  590 	if(spamicity)
  591 	{
  592 		/* FIXME: spaces in the path will cause trouble */
  593 		if(fscanf(proc, "%*[^ ] %f\n", &spamicity_val)!=1)
  594 		{
  595 			syslog(LOG_ERR, "failed to get bogofilter spamicity "
  596 				"value");
  597 			spamicity_val=-1;
  598 		}
  599 		else
  600 			if(debug)
  601 				syslog(LOG_DEBUG, "spamicity value: %f",
  602 					spamicity_val);
  603 	}
  604 
  605 	status=pclose(proc);
  606 
  607 	if(!WIFEXITED(status))
  608 	{
  609 		syslog(LOG_ERR, "bogofilter didn't exit normally");
  610 		mlfi_clean(ctx);
  611 		return SMFIS_CONTINUE;
  612 	}
  613 
  614 	switch(WEXITSTATUS(status))
  615 	{
  616 		case 3:
  617 		case -1:
  618 			syslog(LOG_ERR, "bogofilter reply: I/O error");
  619 			mlfi_clean(ctx);
  620 			return SMFIS_CONTINUE;
  621 		case 0:
  622 			if(spamicity && spamicity_val!=-1)
  623 				snprintf(header, 64, "Spam, spamicity=%.6f",
  624 					spamicity_val);
  625 			else
  626 				strcpy(header, "Yes, tests=bogofilter");
  627 			smfi_insheader(ctx, 0, "X-Bogosity", header);
  628 
  629 			priv->old_headers++;
  630 
  631 			if(forward_spam)
  632 			{
  633 				if(smfi_addrcpt(ctx, (char *)forward_spam)
  634 					!=MI_SUCCESS)
  635 					syslog(LOG_ERR, "forward_spam failed:"
  636 						" '%s'", forward_spam);
  637 				else
  638 					if(debug)
  639 						syslog(LOG_DEBUG,
  640 						"forward_spam rcpt added: "
  641 						"'%s'", forward_spam);
  642 			}
  643 
  644 			if(subj_tag && priv->subject)
  645 			{
  646 				tmp_subj=(char *)calloc(strlen(subj_tag)+
  647 					strlen(priv->subject)+2, sizeof(char));
  648 
  649 				if(!tmp_subj)
  650 					syslog(LOG_ERR, "Unable to get memory:"
  651 						" %s", strerror(errno));
  652 				else
  653 				{
  654 					snprintf(tmp_subj, strlen(subj_tag)+
  655 						strlen(priv->subject)+2,
  656 						"%s %s", subj_tag,
  657 						priv->subject);
  658 
  659 					/* truncate if needed and be nice
  660 						with RFC */
  661 					if(strlen(tmp_subj)>998)
  662 						tmp_subj[998]=0;
  663 
  664 					if(smfi_chgheader(ctx, "Subject", 1,
  665 						tmp_subj)!=MI_SUCCESS)
  666 						syslog(LOG_ERR, "subject_tag"
  667 							" failed: '%s'",
  668 								tmp_subj);
  669 					else
  670 						if(debug)
  671 							syslog(LOG_DEBUG,
  672 							"subject_tag"
  673 							" added: '%s'",
  674 								tmp_subj);
  675 					free(tmp_subj);
  676 				}
  677 			}
  678 
  679 			if(verbose)
  680 			{
  681 				if(mode==SMFIS_CONTINUE)
  682 					syslog(LOG_NOTICE,
  683 						"bogofilter reply: spam");
  684 				else
  685 					if(mode==SMFIS_REJECT)
  686 						syslog(LOG_NOTICE,
  687 							"spam rejected");
  688 					else
  689 						syslog(LOG_NOTICE,
  690 							"spam discarded");
  691 			}
  692 
  693 			if(mode==SMFIS_REJECT && reject)
  694 				smfi_setreply(ctx, "554", "5.7.1", reject);
  695 
  696 			if(quarantine_mdir)
  697 			{
  698 				if(debug)
  699 					syslog(LOG_DEBUG, "copying message "
  700 						"to quarantine_mdir");
  701 
  702 				if(chdir(quarantine_mdir)==-1)
  703 					syslog(LOG_ERR, "failed to chdir to "
  704 						"quarantine_mdir: %s\n",
  705 							strerror(errno));
  706 				else
  707 					if(to_maildir(priv->fullpath,
  708 						priv->filename)==-1)
  709 						syslog(LOG_ERR, "failed to"
  710 						" copy message to "
  711 						"quarantine_mdir");
  712 			}
  713 
  714 			mlfi_clean(ctx);
  715 			return mode;
  716 		case 1:
  717 			if(spamicity && spamicity_val!=-1)
  718 				snprintf(header, 64, "Ham, spamicity=%.6f",
  719 					spamicity_val);
  720 			else
  721 				strcpy(header, "No, tests=bogofilter");
  722 			smfi_insheader(ctx, 0, "X-Bogosity", header);
  723 
  724 			priv->old_headers++;
  725 
  726 			if(verbose)
  727 				syslog(LOG_NOTICE, "bogofilter reply: ham");
  728 			break;
  729 		case 2:
  730 			if(spamicity && spamicity_val!=-1)
  731 				snprintf(header, 64, "Unsure, spamicity=%.6f",
  732 					spamicity_val);
  733 			else
  734 				strcpy(header, "Unsure, tests=bogofilter");
  735 			smfi_insheader(ctx, 0, "X-Bogosity", header);
  736 
  737 			priv->old_headers++;
  738 
  739 			if(verbose)
  740 				syslog(LOG_NOTICE, "bogofilter reply: unsure");
  741 			break;
  742 		default:
  743 			syslog(LOG_ERR, "bogofilter reply is unknown");
  744 			break;
  745 	}
  746 
  747 	if(priv->old_headers>1)
  748 		for(i=2, priv->old_headers++;i<priv->old_headers+1;i++)
  749 		{
  750 			smfi_chgheader(ctx, "X-Bogosity", i, NULL);
  751 			if(debug)
  752 				syslog(LOG_DEBUG, "previous header removed");
  753 		}
  754 
  755 	mlfi_clean(ctx);
  756 	return SMFIS_CONTINUE;
  757 }
  758 
  759 sfsistat
  760 mlfi_abort(SMFICTX *ctx)
  761 {
  762 	if(debug)
  763 		syslog(LOG_DEBUG, "message ABORTED");
  764 
  765 	mlfi_clean(ctx);
  766 
  767 	return SMFIS_CONTINUE;
  768 }
  769 
  770 sfsistat
  771 mlfi_close(SMFICTX *ctx)
  772 {
  773 	struct mlfiPriv *priv;
  774 
  775 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  776 	if(!priv)
  777 		return SMFIS_CONTINUE;
  778 
  779 	if(!priv->eom)
  780 		mlfi_clean(ctx);
  781 
  782 	smfi_setpriv(ctx, NULL);
  783 	free(priv);
  784 
  785 	if(debug)
  786 		syslog(LOG_DEBUG, "connection closed");
  787 
  788 	return SMFIS_CONTINUE;
  789 }
  790 
  791 void
  792 mlfi_clean(SMFICTX *ctx)
  793 {
  794 	struct mlfiPriv *priv;
  795 
  796 	if(debug)
  797 		syslog(LOG_DEBUG, "cleaning message...");
  798 
  799 	priv=(struct mlfiPriv *)smfi_getpriv(ctx);
  800 
  801 	if(!priv)
  802 		return;
  803 
  804 	if(priv->f)
  805 	{
  806 		if(debug)
  807 			syslog(LOG_DEBUG, "closing tmp file");
  808 		fclose(priv->f);
  809 		priv->f=NULL;
  810 	}
  811 
  812 	if(priv->fullpath)
  813 	{
  814 		if(debug)
  815 			syslog(LOG_DEBUG, "removing tmp file");
  816 		unlink(priv->fullpath);
  817 		free(priv->fullpath);
  818 		priv->fullpath=NULL;
  819 	}
  820 
  821 	if(priv->subject)
  822 	{
  823 		free(priv->subject);
  824 		priv->subject=NULL;
  825 	}
  826 
  827 	priv->eom=1;
  828 	priv->old_headers=0;
  829 
  830 	if(debug)
  831 		syslog(LOG_DEBUG, "...cleaning done");
  832 
  833 	return;
  834 }
  835 
  836 char *
  837 hostname_tmp()
  838 {
  839 	char *p;
  840 	char myhostname[MAXHOSTNAMELEN+128];
  841 	struct timeval tp;
  842 
  843 	if(gettimeofday(&tp, NULL)==-1)
  844 		tp.tv_sec=time(NULL);
  845 
  846 	/* time + hostname to make a unique filename NFS friendly */
  847 	snprintf(myhostname, 117, "bogom_%lu.%lu.", tp.tv_sec, tp.tv_usec);
  848 
  849 	if(gethostname(myhostname+strlen(myhostname), MAXHOSTNAMELEN)==-1)
  850 	{
  851 		syslog(LOG_NOTICE, "failed to get my hostname");
  852 		strcpy(myhostname, "unknown_hostname");
  853 	}
  854 
  855 	p=myhostname;
  856 	while((p=strstr(p, "/")))
  857 		*p='\057';
  858 
  859 	p=myhostname;
  860 	while((p=strstr(p, ":")))
  861 		*p='\072';
  862 
  863 	strcat(myhostname, ".XXXXXXXXXX");
  864 
  865 	return strdup(myhostname);
  866 }
  867 
  868 int
  869 to_maildir(char *origin, char *filename)
  870 {
  871 	char *p;
  872 	struct stat st;
  873 
  874 	/* caller must chdir to quarantine_mdir */
  875 
  876 	if(stat("new", &st)==-1 && errno==ENOENT)
  877 		if(mkdir("new", 0700)==-1)
  878 		{
  879 			syslog(LOG_ERR, "quarantine_mdir failed to "
  880 				"create new/: %s", strerror(errno));
  881 			return -1;
  882 		}
  883 
  884 	p=(char *)calloc(strlen(filename)+strlen(quarantine_mdir)+6,
  885 		sizeof(char));
  886 	if(!p)
  887 	{
  888 		syslog(LOG_ERR, "quarantine_mdir failed to get memory: %s",
  889 			strerror(errno));
  890 		return -1;
  891 	}
  892 
  893 	snprintf(p, strlen(filename)+strlen(quarantine_mdir)+6,
  894 		"%s/new/%s\n", quarantine_mdir, filename);
  895 
  896  	if(link(origin, p)==-1)
  897 	{
  898 		syslog(LOG_ERR, "quarantine_mdir failed to link file: %s",
  899 			strerror(errno));
  900 		free(p);
  901 		return -1;
  902 	}
  903 
  904 	free(p);
  905 
  906 	return 0;
  907 }
  908 
  909 void
  910 usage(const char *argv0)
  911 {
  912 	fprintf(stderr, "usage: %s\t[-R | -D] [-t] [-v] [-u user] [-s conn]\n"
  913 		"\t\t[-b bogo_path ] [-x exclude_string] "
  914  		"[-c conf_file]\n\t\t[-l body_limit] [-p pidfile] "
  915 		"[-f forward_spam]\n"
  916 		"\t\t[-q quarantine_mdir] [-S] [-d]\n", argv0);
  917 
  918 	return;
  919 }
  920 
  921 int
  922 main(int argc, char *argv[])
  923 {
  924 	const char *user=DEF_USER;
  925 	const char *conn=DEF_CONN;
  926 	const char *pipe=NULL;
  927 	const char *conffile=DEF_CONF;
  928 	const char *pidfile=DEF_PIDFILE;
  929 
  930 	FILE *pidfile_fd;
  931 	int result;
  932 
  933 	struct re_list *tre;
  934  	struct string_list *tsl, *tsl2;
  935 
  936  	/* configuration tokens */
  937  	struct conftoken conf[]=
  938  	{
  939  		{ "verbose", REQ_BOOL, NULL, -1, NULL },
  940  		{ "training", REQ_BOOL, NULL, -1, NULL },
  941  		{ "user", REQ_QSTRING, NULL, 0, NULL },
  942  		{ "connection", REQ_QSTRING, NULL, 0, NULL },
  943  		{ "exclude_string", REQ_QSTRING, NULL, 0, NULL },
  944  		{ "re_envfrom", REQ_LSTQSTRING, NULL, 0, NULL },
  945  		{ "bogofilter", REQ_QSTRING, NULL, 0, NULL },
  946  		{ "policy", REQ_STRING, NULL, 0, NULL },
  947  		{ "reject", REQ_QSTRING, NULL, 0, NULL },
  948  		{ "re_connection", REQ_LSTQSTRING, NULL, 0, NULL },
  949  		{ "re_envrcpt", REQ_LSTQSTRING, NULL, 0, NULL },
  950  		{ "body_limit", REQ_STRING, NULL, 0, NULL },
  951  		{ "pidfile", REQ_QSTRING, NULL, 0, NULL },
  952  		{ "subject_tag", REQ_QSTRING, NULL, 0, NULL },
  953  		{ "forward_spam", REQ_QSTRING, NULL, 0, NULL },
  954  		{ "quarantine_mdir", REQ_QSTRING, NULL, 0, NULL },
  955  		{ "spamicity_header", REQ_BOOL, NULL, -1, NULL },
  956  		{ NULL, 0, NULL, 0, NULL }
  957 	};
  958 
  959 	int opt;
  960 	const char *opts="hu:p:b:RDtvx:w:c:l:ds:f:q:S";
  961 
  962 	struct passwd *pw=NULL;
  963 	struct stat st;
  964 
  965 	while((opt=getopt(argc, argv, opts))!=-1)
  966 		switch(opt)
  967 		{
  968 			case 'h':
  969 			default:
  970 				usage(argv[0]