Script to call SpamAssassin and sa-learn

This section contains scripts that hMailServer has contributed with. hMailServer 4 is needed to use these.
Post Reply
brischt
New user
New user
Posts: 1
Joined: 2009-04-29 06:56

Script to call SpamAssassin and sa-learn

Post by brischt » 2009-04-29 07:23

Hi,

I spent a few hours working on a script that integrates hMailServer with SpamAssassin the "classic" way (via an OnDeliverMessage script). Some parts of this script were collected from posts all over this board, but others are new so I thought I would share it.

The script offers the following features:
  • Create a log file so you can trace what's going on in the script.
  • Suppress calling SpamAssassin if the mail was sent from your domain (either internally or outgoing).
  • Provide an easy way to learn new spam with pretty much any mail client (tested with Thunderbird).
  • Send confirmation mail when new spam was learnt.
Here's the script:

Code: Select all

Sub OnDeliverMessage(oMessage)
	fname = oMessage.filename	
	
	Dim debug
	Dim debugfile
	Dim hdebugfile, hlearnedfile
	Dim s1, SearchString	
	Dim infile, testfile
	Dim fso
	Dim cmd, rc, sh
	Dim replyMail
	Dim spamLearnAddress
	Dim learnDirectory
	Dim learnSendConfirmationMail
	Dim learnSendConfirmationMailAddress
	
	'CONFIGURATION
	'debug: Determines whether a log file should be written
	debug = true
	'debugfile: Name and path to the logfile
	debugfile = "C:\spam\log\debug.txt"
	'spamLearnAddress: The address any unrecognized spam mail should be forwarded to as attachment, so it can be learned
	spamLearnAddress = "spam@<yourdomain>"
	'learnDirectory: The directory where the attachments are saved to and sa-learn is called for. This directory must exist and contain a subdirectory called "log".
	'learnDirectory should end with a backslash.
	learnDirectory = "C:\spam\"
	'learnSendConfirmationMail: Determines whether an email with the result of sa-learn is sent or not.
	learnSendConfirmationMail = true
	'SearchString: This is the string which when found signals that it is an outgoing message and therefore does not need to be scanned.
	SearchString = "from [10.0.1."
	
	'CREATE/SET SOME COMMONLY USED VARIABLES/OBJECTS
	Set fso = createobject("Scripting.FileSystemObject")	
	
	If Not fso.fileexists(fname) then
		result.Value = 0
		Exit sub
	End If
		
	If debug = true then
		Set hdebugfile = fso.OpenTextFile(debugfile, 8, true, 0)
		hdebugfile.WriteLine ("--- Start OnDeliverMessage ---")
	End If
	
	infile = fso.getabsolutepathname(fname)
	testfile = infile & ".tmp"
	Set sh = CreateObject("WScript.Shell")
	
	
	'LEARN MAILS SENT TO THE CONFIGURED SPAM LEARN ADDRESS AS SPAM IF ANY ATTACHMENT IS FOUND
	if oMessage.Attachments.Count > 0 then
		if debug = true then
			hdebugfile.WriteLine ("Checking whether spamassassin should learn this message as spam")
		end if
		
		for iRecipient = 0 to oMessage.Recipients.Count-1
			if debug = true then
				hdebugfile.WriteLine ("Recipient: " & oMessage.Recipients(iRecipient).Address)
			end if
					
			if oMessage.Recipients(iRecipient).Address = spamLearnAddress then
				'save all attachments to disk and execute sa-learn
				if debug = true then
					hdebugfile.WriteLine ("This message was sent to the configured spam learn address " & spamLearnAddress)
				end if
				
				for iAttachment = 0 to oMessage.Attachments.Count-1
					oMessage.Attachments(iAttachment).SaveAs(learnDirectory & iAttachment & ".eml")
				next
				cmd = "cmd /c sa-learn --spam " & learnDirectory & " > " & learnDirectory & "log\learned.txt"
				rc = sh.Run(cmd, 0, true)
				for iAttachment = 0 to oMessage.Attachments.Count-1
					fso.DeleteFile (learnDirectory & iAttachment & ".eml")
				next
				
				if debug = true or learnSendConfirmationMail = true then			
					' Read the sa-learn result			
					Set hlearnedfile = fso.OpenTextFile(learnDirectory & "log\learned.txt", 1, true, 0)
					learnedResult = hlearnedfile.ReadLine
					hlearnedfile.Close
					Set hlearnedfile = Nothing
					
					if learnSendConfirmationMail = true then
						Set replyMail = CreateObject ("hMailServer.Message")
						replyMail.From = "hMailServer"
						replyMail.FromAddress = oMessage.FromAddress
						replyMail.Subject = "Learned SPAM"
						replyMail.AddRecipient "Admin", oMessage.FromAddress
						replyMail.Body = learnedResult	
						replyMail.Save
						Set replyMail = Nothing
					end if
				end if

				' Reject mail and terminate
				result.Value = 1
				if debug = true then
					hdebugfile.WriteLine ("Result from sa-learn: " & learnedResult)
					hdebugfile.WriteLine ("--- End OnDeliverMessage ---")
					hdebugfile.Close
					Set hdebugfile = Nothing
				end if
				Set fso = Nothing
				Set sh = Nothing
				Exit Sub
			end if
		next	
	else
		if debug = true then
			hdebugfile.WriteLine ("No attachment found - spam learn check skipped.")
		end if
	end if
		
	
	'SKIP SPAM CHECKS ON OUTGOING MAIL	
	if debug = true then
		hdebugfile.WriteLine ("Check whether this is an outgoing mail")
	end if
	s1 = oMessage.HeaderValue("Received")	

	If InStr(1, s1, SearchString, 1) > 0 Then
		if debug = true then
			hdebugfile.WriteLine ("Message was sent from local domain. Skipping spam check.")			
		end if
		
		'Comment out the next two lines if you don't want a header created
		oMessage.HeaderValue("X-SpamCheck-Status") = "Spam checks skipped on mail sent from local domain"
		oMessage.Save()		
		
		if debug = true then
			hdebugfile.WriteLine ("--- End OnDeliverMessage ---")
			hdebugfile.Close
			Set hdebugfile = Nothing
		end if
		
		Set fso = Nothing
		Set sh = Nothing
		Exit Sub
	End If	
		
	
	'CALL SPAMASSASSIN (modify as required)		
	if debug = true then
		hdebugfile.WriteLine ("Calling SpamAssassin for this message")
	end if
	
	'Copy file into a testfile
	fso.CopyFile infile, testfile
	if debug = true then
		hdebugfile.WriteLine ("File copied to " & testfile)
	end if
	
	cmd = "cmd /c perl -S -T -w spamassassin < """ & testfile & """ > """ & infile & """"
	
	rc = sh.Run( cmd , 0 , TRUE)
	If rc = 0 Then                           'Do something for HAM
		if debug = true then
			hdebugfile.WriteLine ("Message is HAM.")
		end if
	ElseIf rc = 1 Then                  'Do something for SPAM		
		if debug = true then
			hdebugfile.WriteLine ("Message is SPAM.")
		end if
		'Uncomment next two lines to reject spam
		' result.Value = 1
		' Exit Sub		
	End If

	If fso.GetFile(infile).Size = 0 Then      'Failsafe: In the event an error causes a zero byte msg
		fso.CopyFile testfile, infile
	End if

	fso.Deletefile testfile
	Set fso = Nothing
	Set sh = Nothing
	if debug = true then
		hdebugfile.WriteLine ("--- End OnDeliverMessage ---")
		hdebugfile.Close
		Set hdebugfile = Nothing
	end if
	result.value = 0
End Sub
As you can see, the first few lines provide an easy way to configure the script to do what you want. I have Spamassassin installed by "itself", i.e. not the spamd as a service (I read somewhere that this might not work on Win32, but this could be outdated information) but as a perl script.

If your users want to submit new spam mails to be learnt, they forward the unrecognized spam mail as an attachment to the address specified in spamLearnAddress. They can attach multiple spam mails as attachments. It is necessary to forward them as attachment because otherwise the sender will be their own account, and spamassassin might learn their address to be a spammer's address (actually, the script will skip learning if it doesn't find any attachments).

If learnSendConfirmationMail is set to true, the user submitting a new mail that should be learned will receive an email with the result of sa-learn.

Maybe this helps someone - it seems to be working okay on my machine. If you find this useful or have questions, let me know :wink:

Cheers,

Roland

Post Reply