Sendmail + Cyrus + Procmail(?) + SpamAssassin

Andrew Swartz awswartz at acsalaska.net
Wed Jun 21 18:38:17 UTC 2006


I don't know if anyone is still interested, but I found a solution to 
this problem.  My mail goes Sendmail-->Procmail-->CyrusIMAP.

There are numerous discussions of how to configure the 
Sendmail-->Procmail portion, so I'll skip that.

The problem lies in the Procmail-->Cyrus part.  Sendmail and/or Procmail 
insists on adding a "From" line to the top of the message, and this 
causes an error when trying to pass the message from the procmailrc file 
to /usr/lib/cyrus-imapd/deliver.  So I wrote a little C-program that the 
email can be piped through (removing the "From" line); the resulting 
message is passed to /usr/lib/cyrus-imapd/deliver.

Here is my ~/.promailrc file.  Note that all mail is first delivered to 
the user's CyrusIMAP box, then it goes through the procmail checks.  
This is because if I don't do this, then all mail that fails all the 
conditions (recipes) gets shoved into /usr/local/mail/~, which is 
basically a black hole if you are using Cyrus.

#     This file is: ~/.procmailrc
#
#     REQUIREMENTS:
#        1) cyrus-imap (specifically, "/usr/lib/cyrus-imapd/deliver").
#        2) the C-program "rmfromln" in a reachable place (like 
/usr/local/bin).
#            rmfromln:  the email argument is piped in; if the email 
message
#            has been appended with starting "From" line, then this line is
#               removed, otherwise the message remains unchanged.

LOGFILE=/home/r2/procmail.log

###############################################################################
#     This is the default action; it delivers the email to the imap mailbox,
#     and thus this happens regardless of the success|failure of the 
recipes.
#     RATIONAL: every email MUST have a "To" field, otherwise sendmail would
#    not have passed it to us!?
:0 c
* ^To:.
| rmfromln | /usr/lib/cyrus-imapd/deliver $LOGNAME     
###############################################################################

:0
* ^Subject:.*(callalert|CALLALERT)
* ^Subject:.*(on|ON)
| /root/scripts/callalert on

:0
* ^Subject:.*(callalert|CALLALERT)
* ^Subject:.*(off|OFF)
| /root/scripts/callalert off


ALSO:  I've attached the C-program "rmfromln" (and it's source code) 
which removes the "From" line (if it is present).

-Andy Swartz

-------------- next part --------------
A non-text attachment was scrubbed...
Name: rmfromln
Type: application/octet-stream
Size: 12975 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-questions/attachments/20060621/0444f1aa/rmfromln.obj
-------------- next part --------------

/*  NEW plan:
			1) create a temp io-stream with tempfile()
			2) read in from stdin and out to temp until EOL (or EOF)
			3) dynamically create a string of the appropriate length 
				 (i.e. the number of characters written to temp-stream)
			4) copy temp-stream to the new string
			5) search the string for "From"
			6) if found, copy the string to stdout
			7) copy the remaining stdin to stdout until EOF reached.  
			*/

#include "stdio.h"
#include "string.h"
#include "stddef.h"
#include "stdlib.h"


// ###############################################

#define EOL '\n'

// ###############################################

main () {

char 		c;
int			length;
int			j;
FILE		*tempstream;
char		*tempstring;


// Create the temp-stream
tempstream = tmpfile();

// Read from stdin into tempstream until EOF or EOL is encountered. 
length = 0;
rewind(tempstream);
while ((c=fgetc(stdin)) && (c != EOL)) {
	fputc(c,tempstream);
	length++;
	}

// if no input, then just exit and do nothing.
if (length == 0)
	exit(0);
	
// the above code did not put the EOL in the temp-stream, so add it.
fputc(EOL,tempstream);
length++;

// create the tempSTRING and copy the tempstream into it.
// tempstring = new string[length];
tempstring = (char *) calloc(length,sizeof(char));
rewind(tempstream);
for(j=0;j<length;j++)
	tempstring[j] = fgetc(tempstream);

/* If we are at EOF, then there was no EOL, and thus no search for "From" is indicated;
 	 so simply write the temp-string to stdout and exit.  */
if (feof(stdin)) {
	for (j=0;j<length;j++)
			fputc(tempstring[j],stdout);
	exit(0);
	}
	
// If we got to here, we need to search temp-string for "From".
if (strstr(tempstring,"From") == NULL) {
		/* i.e. "From" was NOT in the 1st line, so this line needs to 
			 be output before the remaining stdin is transferred to stdout  */
	  for (j=0;j<length;j++)                                                      
				fputc(tempstring[j],stdout);
		}
// dynamically allocated string is done, so free up the memory.
free (tempstring);
		
// now just transfer the remaining stdin to stdout.
 c = getchar();
 while (c != EOF) {
 	  fputc(c,stdout);
 	  c = getchar();
    }


}  /* End of Main */


More information about the freebsd-questions mailing list