Commit 22443519e7c90588e25a059741140341d5b24c79:
direct mail delivery
Branch: refs/heads/master
Author: Sam Ruby <[email protected]>
Committer: Sam Ruby <[email protected]>
Pusher: rubys <[email protected]>
------------------------------------------------------------
www/secmail/deliver.rb | +++++++++++
www/secmail/models/mailbox.rb | +++++++ ------
www/secmail/models/message.rb | ++++++++ --
www/secmail/parsemail.rb | + ---
------------------------------------------------------------
96 changes: 66 additions, 30 deletions.
------------------------------------------------------------
diff --git a/www/secmail/deliver.rb b/www/secmail/deliver.rb
new file mode 100644
index 0000000..f583cde
--- /dev/null
+++ b/www/secmail/deliver.rb
@@ -0,0 +1,22 @@
+#
+# Process email as it is received
+#
+
+Dir.chdir File.dirname(File.expand_path(__FILE__))
+
+require_relative 'models/mailbox'
+
+# read and parse email
+STDIN.binmode
+email = STDIN.read
+hash = Message.hash(email)
+headers = Message.parse(email)
+
+# construct message
+month = (Time.parse(headers[:time]) rescue Time.now).strftime('%Y%m')
+mailbox = Mailbox.new(month)
+message = Message.new(mailbox, hash, headers, email)
+
+# write message to disk
+message.write_headers
+message.write_email
diff --git a/www/secmail/models/mailbox.rb b/www/secmail/models/mailbox.rb
index 63b4285..9091ec7 100644
--- a/www/secmail/models/mailbox.rb
+++ b/www/secmail/models/mailbox.rb
@@ -10,7 +10,6 @@
require_relative '../config.rb'
require_relative 'message.rb'
-require_relative 'attachment.rb'
class Mailbox
#
@@ -20,7 +19,7 @@ def self.fetch(mailboxes=nil)
options = %w(-av --no-motd)
if mailboxes == nil
- options += %w(--delete --exclude=*.yml)
+ options += %w(--delete --exclude=*.yml --exclude=*.mail)
source = "#{SOURCE}/"
elsif Array === mailboxes
host, path = SOURCE.split(':', 2)
@@ -38,13 +37,14 @@ def self.fetch(mailboxes=nil)
# Initialize a mailbox
#
def initialize(name)
- name = File.basename(name, '.yml') if name.end_with? '.yml'
+ name = File.basename(name, '.yml')
if name =~ /^\d+$/
- @filename = Dir["#{ARCHIVE}/#{name}", "#{ARCHIVE}/#{name}.gz"].first.
- untaint
+ @mbox = Dir["#{ARCHIVE}/#{name}", "#{ARCHIVE}/#{name}.gz"].first.untaint
+ @name = name.untaint
else
- @filename = name
+ @name = name.split('.').first
+ @mbox = "#{ARCHIVE}/#{name}"
end
end
@@ -70,22 +70,15 @@ def update
end
#
- # Determine whether or not the mailbox exists
- #
- def exist?
- @filename and File.exist?(@filename)
- end
-
- #
# Read a mailbox and split it into messages
#
def messages
return @messages if @messages
- return [] unless exist?
+ return [] unless @mbox and File.exist?(@mbox)
- mbox = File.read(@filename)
+ mbox = File.read(@mbox)
- if @filename.end_with? '.gz'
+ if @mbox.end_with? '.gz'
stream = StringIO.new(mbox)
reader = Zlib::GzipReader.new(stream)
mbox = reader.read
@@ -115,7 +108,13 @@ def self.find(message)
#
def find(hash)
headers = YAML.load_file(yaml_file) rescue {}
- email = messages.find {|message| Message.hash(message) == hash}
+
+ if Dir.exist? dir and File.exist? File.join(dir, hash)
+ email = File.read(File.join(dir, hash), encoding: Encoding::BINARY)
+ else
+ email = messages.find {|message| Message.hash(message) == hash}
+ end
+
Message.new(self, hash, headers[hash], email) if email
end
@@ -130,19 +129,24 @@ def each(&block)
# name of associated yaml file
#
def yaml_file
- source = File.basename(@filename, '.gz').untaint
- "#{ARCHIVE}/#{source}.yml"
+ "#{ARCHIVE}/#{@name}.yml"
+ end
+
+ #
+ # name of associated directory
+ #
+ def dir
+ "#{ARCHIVE}/#{@name}.mail"
end
#
# return headers
#
def headers
- source = File.basename(@filename, '.gz').untaint
messages = YAML.load_file(yaml_file) rescue {}
messages.delete :mtime
messages.each do |key, value|
- value[:source]=source
+ value[:source]=@name
end
end
@@ -182,11 +186,11 @@ def self.headers(part)
#
def parse
mbox = YAML.load_file(yaml_file) || {} rescue {}
- return if mbox[:mtime] == File.mtime(@filename)
+ return if mbox[:mtime] == File.mtime(@mbox)
# open the YAML file for real (locking it this time)
self.update do |mbox|
- mbox[:mtime] = File.mtime(@filename)
+ mbox[:mtime] = File.mtime(@mbox)
# process each message in the mailbox
self.each do |message|
diff --git a/www/secmail/models/message.rb b/www/secmail/models/message.rb
index 715484d..0394221 100644
--- a/www/secmail/models/message.rb
+++ b/www/secmail/models/message.rb
@@ -4,6 +4,9 @@
require 'digest'
require 'mail'
+require 'time'
+
+require_relative 'attachment.rb'
class Message
attr_reader :headers
@@ -85,7 +88,7 @@ def update_attachment name, values
attachment = find(name)
if attachment
attachment.headers.merge! values
- write
+ write_headers
end
end
@@ -94,7 +97,7 @@ def replace_attachment name, values
if attachment
index = @headers[:attachments].find_index(attachment.headers)
@headers[:attachments][index, 1] = Array(values)
- write
+ write_headers
end
end
@@ -103,20 +106,29 @@ def delete_attachment name
if attachment
@headers[:attachments].delete attachment.headers
@headers[:status] = :deleted if @headers[:attachments].empty?
- write
+ write_headers
end
end
#
# write updated headers to disk
#
- def write
+ def write_headers
@mailbox.update do |yaml|
yaml[@hash] = @headers
end
end
#
+ # write email to disk
+ #
+ def write_email
+ dir = @mailbox.dir
+ Dir.mkdir dir unless Dir.exist? dir
+ File.write File.join(dir, @hash), @email, encoding: Encoding::BINARY
+ end
+
+ #
# write one or more attachments to directory containing an svn checkout
#
def write_svn(repos, filename, *attachments)
diff --git a/www/secmail/parsemail.rb b/www/secmail/parsemail.rb
index 3d4074e..a426593 100644
--- a/www/secmail/parsemail.rb
+++ b/www/secmail/parsemail.rb
@@ -10,8 +10,6 @@
# * Invalid from addresses
#
-require 'time'
-
require_relative 'models/mailbox'
database = File.basename(SOURCE)
@@ -36,7 +34,7 @@
width = 0
Dir[File.join(database, '2*')].sort.each do |name|
# skip YAML files, update output showing latest file being processed
- next if name.end_with? '.yml'
+ next if name.end_with? '.yml' or name.end_with? '.mail'
next if ARGV.any? {|arg| arg =~ /^\d{6}$/} and
not ARGV.any? {|arg| name.include? "/#{arg}"}
print "#{name.ljust(width)}\r"