#!/usr/bin/env ruby ########################################################### # Notice of Public Domain Status ########################################################### # The author, Ari Johnson, hereby relinquishes any copyright privileges # to the content of this file. It is hereby placed in the public domain # free of any copyright restrictions. ########################################################### ########################################################### # Configuration ########################################################### # You will have to set these to match your system. Note # that this script will figure out which database, user, # and password to use but it cannot figure out what query # to use in listing all your mailboxes. ########################################################### $postconf = '/usr/sbin/postconf' $mailfilter = 'mailreaver.crm' $crmdir = '/var/mail/crm/' $query = 'SELECT mailbox, uid, gid FROM account_view' require 'dbi' ########################################################### # Utility functions ########################################################### def postconf(parameter) return `#{$postconf} #{parameter}`.sub(/^[^=]*\s*=\s*(.*)$/, '\1').strip end def find_map(path) method, path = path.split(':', 2) if method == 'proxy' method, path = path.split(':', 2) end if ! path path = method method = postconf('default_database_type') end return method, path end def each_account mbmeth, mbpath = find_map($mailbox_maps) uidmeth, uidpath = find_map($uid_maps) gidmeth, gidpath = find_map($gid_maps) if mbmeth != 'pgsql' or uidmeth != 'pgsql' or gidmeth != 'pgsql' $stderr.puts("designed only for postgres") exit 1 end mbparm = read_map_file(mbpath) uidparm = read_map_file(uidpath) gidparm = read_map_file(gidpath) dbh = DBI.connect("DBI:Pg:#{mbparm['dbname']}:#{mbparm['hosts']}", mbparm['user'], mbparm['password']) count = 0 dbh.select_all($query) do |row| count += 1 yield row['mailbox'], row['uid'], row['gid'] end return count end def read_map_file(path) lines = IO.readlines(path) if ! lines $stderr.puts("could not read file #{path}") exit 1 end retval = Hash.new lines.each do |line| line.scan(/^([^=]*)\s*=\s*(.*)$/) do |parameter, value| if parameter and value retval[parameter.strip] = value.strip end end end return retval end def process_spam(mailbox, uid, gid) fork do begin Process.egid = gid Process.euid = uid Dir.chdir($mailbox_base) Dir.chdir(mailbox) spam_new = Dir.new(".Spam/new") spam_cur = Dir.new(".Spam/cur") fneg_new = Dir.new(".Spam.False Negatives/new") fneg_cur = Dir.new(".Spam.False Negatives/cur") fpos_new = Dir.new(".Spam.False Positives/new") fpos_cur = Dir.new(".Spam.False Positives/cur") if ! spam_new or ! spam_cur or ! fneg_new or ! fneg_cur or ! fpos_new or ! fpos_cur return end fneg_new.each do |file| if File.ftype(".Spam.False Negatives/new/#{file}") == "file" learn_spam(".Spam.False Negatives/new/#{file}") File.rename(".Spam.False Negatives/new/#{file}", ".Spam/new/#{file}") end end fneg_cur.each do |file| if File.ftype(".Spam.False Negatives/cur/#{file}") == "file" learn_spam(".Spam.False Negatives/cur/#{file}") File.rename(".Spam.False Negatives/cur/#{file}", ".Spam/new/#{file}") end end fpos_new.each do |file| if File.ftype(".Spam.False Positives/new/#{file}") == "file" learn_nonspam(".Spam.False Positives/new/#{file}") File.rename(".Spam.False Positives/new/#{file}", "new/#{file}") end end fpos_cur.each do |file| if File.ftype(".Spam.False Positives/cur/#{file}") == "file" learn_nonspam(".Spam.False Positives/cur/#{file}") File.rename(".Spam.False Positives/cur/#{file}", "new/#{file}") end end rescue Exception end end Process.wait end def learn_spam(message) `crm -u #{$crmdir} #{$mailfilter} --spam < '#{message}'`.strip end def learn_nonspam(message) `crm -u #{$crmdir} #{$mailfilter} --good < '#{message}'`.strip end ########################################################### # Main processing code ########################################################### # Load in the necessary postfix configuration $mailbox_base = postconf('virtual_mailbox_base') $mailbox_maps = postconf('virtual_mailbox_maps') $uid_maps = postconf('virtual_uid_maps') $gid_maps = postconf('virtual_gid_maps') each_account do |mailbox, uid, gid| process_spam(mailbox, uid, gid) end