Project: Mailvisa
Code Location: git://repo.or.cz/mailvisa.gitmaster
Browse
/
Download File
mailvisad.rb
require 'socket'
require 'wordlist'
require 'tokenize'

### Defaults

confdir = ENV['HOME'] + '/settings/mailvisa'
sockpath = 'mailvisad.sock'
scorefile = 'scores'
pidfile = 'mailvisad.pid'
logfile = 'mailvisad.log'

### Functions

def get_score word
	score = $scores[word]
	if score == nil
		0.4
	else
		score
	end
end

def most_interesting words
	## Sort words by extremity
	interesting = words.sort do |x, y|
		(0.5 - get_score(y)).abs <=> (0.5 - get_score(x)).abs
	end
	# Return the most interesting words
	interesting[0,20]
end

def spam_probability scores
	prod = 1
	scores.each { |x| prod = prod * x }
	div = 1
	scores.each { |x| div = div * (1 - x) }
	prod / (prod + div)
end

usage = 'USAGE: ' + $0 + ' [options]'

help = <<EOT
Valid options are:

-c <path>	Look for files in <path> (default: $HOME/settings/mailvisa)
-f <path>	Load scores from <path> (default: scores)
-l <path>	Log to <path> (default: mailvisad.log)
-p <path>	Use <path> as pidfile (default: mailvisad.pid)
-s <path>	Use <path> as socket (default: mailvisad.sock)
EOT

### Main program

## Process command line
i = 0
while i < ARGV.length
	case ARGV[i]
	when '-h'
		puts usage
		print "\n" + help
		exit
	when '-c'
		i = i + 1
		confdir = ARGV[i]
	when '-s'
		i = i + 1
		sockpath = ARGV[i]
	when '-f'
		i = i + 1
		scorefile = ARGV[i]
	when '-l'
		i = i + 1
		logfile = ARGV[i]
	when '-p'
		i = i + 1
		pidfile = ARGV[i]
	else
		$stderr.puts 'Invalid option: ' + ARGV[i]
		$stderr.puts usage
		$stderr.puts 'Use "' + $0 + ' -h" for help'
		exit 0x80
	end
	i = i + 1
end

sockpath = confdir + '/' + sockpath if sockpath.index('/') == nil
scorefile = confdir + '/' + scorefile if scorefile.index('/') == nil
pidfile = confdir + '/' + pidfile if pidfile.index('/') == nil
logfile = confdir + '/' + logfile if logfile.index('/') == nil

## Define procedure to load configuration
load_config = lambda do
	db = load_wordlist open(scorefile)
	$scores = db[:words]
end

# Set SIGHUP handler
trap('SIGHUP') { load_config.call }

# Open scorefile
scorefh = open scorefile

## Open socket
begin
	sock = UNIXServer.open sockpath
rescue Errno::EADDRINUSE
	## Figure out if the socket is actually live
	begin
		sock = UNIXSocket.new sockpath
		# Aye, inform user and exit
		$stderr.puts 'mailvisad already bound to ' + sockpath
		exit
	rescue
		# Nope, remove and try again
		if File.exists?(sockpath) && File.ftype(sockpath) == 'socket'
			File.unlink sockpath
			sock = UNIXServer.open sockpath
		end
	end
end

# Open logfile
$stderr = open logfile, 'a'
$stdout = $stderr

# Fork child, exit parent
exit if fork	# Fork off a child, exit parent

# Set at_exit handler
at_exit {
	sock.close
	File.unlink sockpath if
		File.exists?(sockpath) && File.ftype(sockpath) == 'socket'
}

## Detach from terminal
$stdin.close
pid = Process.setsid

## Create pidfile
fh = open pidfile, 'w'
fh.puts pid
fh.close

# Load words
$scores = load_wordlist(scorefh)[:words]

while true
	conn = sock.accept

	begin
		message = conn.read 16384
		words = most_interesting tokenize(message)
		scores = words.map { |x| get_score x }
		probability = (spam_probability(scores) * 100).to_i / 100.0

		conn.puts probability
	rescue
	ensure
		conn.close
	end
end