rubymta 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/lib/rubymta/contact.rb +2 -0
- data/lib/rubymta/item_of_mail.rb +35 -10
- data/lib/rubymta/queue_runner.rb +21 -12
- data/lib/rubymta/receiver.rb +8 -3
- data/lib/rubymta/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1601cb4b77cb390064b8a90aaec523a6bdf3c993
|
4
|
+
data.tar.gz: 764b1ccf31bfbb1b3386f9ec42a61510ee5a7edc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eda93abc4e139bec74e5ef868ff3ed2449832557c24948856158a51ad170ec0e0dde2e88feb81dadad1b6e71d160646ed09501d118bebbe14e018556255cb88f
|
7
|
+
data.tar.gz: f1c860ab3f0e7b44da1bc34109e6a2583a641521a30a64eed331894168ca05f42a831e6c0d652d6f0d16a445a88401d1b1eb27bf6a137b89a1605c0dabdfbb73
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# v0.0.10
|
2
|
+
|
3
|
+
#### Warning: This version changes the queue file format. Previous queue files will *not* be compatible with this version.
|
4
|
+
|
5
|
+
* Added a test for valid UTF-8 encoding. On a 777 character Greek text, a `valid_encoding?` takes only in 3.6ns, so checking every line of text is not expensive.
|
6
|
+
* Revised the way emails are stored on disk: the main structure has the mail[:data][:text] removed and stored following the main structure. The reason for this change is that `eval` doesn't handle foreign UTF-8 character sequences well.
|
7
|
+
- *__New Disk Format in `queue`:__*
|
8
|
+
- Number of lines in main mail structure;
|
9
|
+
- Main mail structure;
|
10
|
+
- Unmodified text; this is added back to the main structure at `mail[:data][:text]` after the queue file is read.
|
11
|
+
* Added a Quit at the moment the connecting IP is placed under prohibition. The reason for this is that some senders don't stop sending when they receive a 500+ level message.
|
12
|
+
* If there is a `parcel` record, but no matching disk file, the parcel record is marked as delivery='none'. This prevents the QueueRunner from looping when an undelivered queue file is manually deleted.
|
13
|
+
* Added checks at the end of delivery in QueueRunner to test for the message level (should be 200+ if accepted).
|
14
|
+
* Added another check for the client abruptly closing the connection.
|
15
|
+
* Added '=' to the list of acceptable characters in a local part of an email address.
|
16
|
+
|
1
17
|
# v0.0.9
|
2
18
|
* Added a `rescue OpenSSL::SSL::SSLError` to catch sender certificate violations.
|
3
19
|
* Added a `LogLevel` into the Config file (config.rb) to control the logger output. I use LOGGER::INFO as my default.
|
data/lib/rubymta/contact.rb
CHANGED
data/lib/rubymta/item_of_mail.rb
CHANGED
@@ -87,13 +87,19 @@ class ItemOfMail < Hash
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def save_mail_into_queue_folder
|
90
|
+
# split the ItemOfMail into a hash and a text (which may contain any UTF-8)
|
91
|
+
hash = self.dup
|
92
|
+
text = hash[:data].delete(:text)
|
90
93
|
begin
|
91
|
-
# save the mail
|
94
|
+
# save the mail into the Queue folder
|
92
95
|
File::open("#{MailQueue}/#{self[:mail_id]}","w") do |f|
|
93
|
-
|
94
|
-
f.write(
|
96
|
+
tmp = hash.pretty_inspect
|
97
|
+
f.write("#{tmp.lines.count}\n")
|
98
|
+
f.write(tmp)
|
99
|
+
f.write("\n")
|
100
|
+
f.write(text.join("\n"))
|
95
101
|
end
|
96
|
-
|
102
|
+
self[:saved] = true
|
97
103
|
rescue => e
|
98
104
|
LOG.error(self[:mail_id]) {e.to_s}
|
99
105
|
e.backtrace.each { |line| LOG.error(self[:mail_id]) {line} }
|
@@ -104,14 +110,33 @@ class ItemOfMail < Hash
|
|
104
110
|
def self::retrieve_mail_from_queue_folder(mail_id)
|
105
111
|
item = nil
|
106
112
|
begin
|
107
|
-
|
108
|
-
File::
|
109
|
-
|
110
|
-
|
113
|
+
# Make sure the file that matches the parcel record exists
|
114
|
+
if !File::exist?("#{MailQueue}/#{mail_id}")
|
115
|
+
# file missing: mark the parcels none and date them
|
116
|
+
parcels = S3DB[:parcels].where(:mail_id=>mail_id).all
|
117
|
+
parcels.each do |parcel|
|
118
|
+
S3DB[:parcels].where(:id=>parcel[:id]).update(:delivery=>'none', :delivery_at=>Time.now)
|
119
|
+
end
|
120
|
+
return nil
|
111
121
|
end
|
122
|
+
|
123
|
+
tmp = nil; File::open("#{MailQueue}/#{mail_id}","r") { |f| tmp = f.read }
|
124
|
+
data = tmp.split("\n")
|
125
|
+
|
126
|
+
# get the number of lines in the hash, and cut that out first
|
127
|
+
n = data[0].to_i
|
128
|
+
a = data[1..n]
|
129
|
+
b = data[n+1..-1]
|
130
|
+
|
131
|
+
# convert the hash
|
132
|
+
mail = eval(a.join("\n"))
|
133
|
+
|
134
|
+
# create the ItemOfMail structure and insert the text
|
135
|
+
item = ItemOfMail::new(mail)
|
136
|
+
item[:data][:text] = b
|
112
137
|
rescue => e
|
113
|
-
LOG.error(
|
114
|
-
e.backtrace.each { |line| LOG.error(
|
138
|
+
LOG.error(mail_id) {e.to_s}
|
139
|
+
e.backtrace.each { |line| LOG.error(mail_id) {line} }
|
115
140
|
end
|
116
141
|
return item
|
117
142
|
end
|
data/lib/rubymta/queue_runner.rb
CHANGED
@@ -65,6 +65,7 @@ class QueueRunner
|
|
65
65
|
# sqlite3 has a bug: "<=" doesn't work with time, ex. "retry_at<='#{Time.now}'"
|
66
66
|
# we have to add 1 second and use "<"; ex. "retry_at<'#{Time.now+1}'"
|
67
67
|
parcels = S3DB[:parcels].where(Sequel.lit("(delivery<>'none') and (delivery_at is null) and ((retry_at is null) or (retry_at<'#{Time.now + 1}'))")).all
|
68
|
+
|
68
69
|
return if parcels.empty?
|
69
70
|
|
70
71
|
# aggregate the emails by destination domain
|
@@ -81,21 +82,17 @@ class QueueRunner
|
|
81
82
|
# handled in the respective mail routine
|
82
83
|
mail = {}
|
83
84
|
deliver.each do |mail_id, domains|
|
84
|
-
(mail = ItemOfMail::retrieve_mail_from_queue_folder(mail_id)) if mail[:mail_id]!=mail_id
|
85
|
-
domains.each do |domain, parcels|
|
85
|
+
(mail = ItemOfMail::retrieve_mail_from_queue_folder(mail_id)) if mail && mail[:mail_id]!=mail_id
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
if mail.nil?
|
88
|
+
LOG.info(Time.now.strftime("%Y-%m-%d %H:%M:%S")) {"Skipping mail #{mail_id} because there's no associated queue file"}
|
89
|
+
next
|
90
|
+
end
|
91
91
|
|
92
|
+
domains.each do |domain, parcels|
|
92
93
|
@mail_id = mail[:mail_id]
|
93
94
|
deliver_and_save_status(mail, domain, parcels.values)
|
94
95
|
@mail_id = nil
|
95
|
-
|
96
|
-
#===================================================================
|
97
|
-
#parcels.values.each { |parcel| puts "--> *4* #{parcel.inspect}" }
|
98
|
-
#===================================================================
|
99
96
|
end
|
100
97
|
end
|
101
98
|
if (n-=1)<0
|
@@ -289,7 +286,13 @@ class QueueRunner
|
|
289
286
|
|
290
287
|
# get one final message for all parcels (recipients)
|
291
288
|
ok, lines = recv_text
|
292
|
-
|
289
|
+
if ok=='2'
|
290
|
+
ret = mark_parcels(parcels, lines)
|
291
|
+
LOG.info(@mail_id) {"Mail for #{parcels[0][:to_url]}, et.al. delivered remotely"}
|
292
|
+
else
|
293
|
+
LOG.info(@mail_id) {"Mail for #{parcels[0][:to_url]}, et.al. failed delivery remotely, #{lines.last}"}
|
294
|
+
end
|
295
|
+
return ret
|
293
296
|
end
|
294
297
|
|
295
298
|
# SAMPLE LMTP TRANSFER
|
@@ -371,7 +374,13 @@ class QueueRunner
|
|
371
374
|
parcels.each do |parcel|
|
372
375
|
# get the response from DoveCot
|
373
376
|
ok, lines = recv_text
|
374
|
-
|
377
|
+
if ok=='2'
|
378
|
+
ret = mark_parcels([parcel], lines)
|
379
|
+
LOG.info(@mail_id) {"Mail for #{parcel[:to_url]} delivered locally"}
|
380
|
+
else
|
381
|
+
LOG.info(@mail_id) {"Mail for #{parcel[:to_url]} failed delivery locally, #{lines.last}"}
|
382
|
+
end
|
383
|
+
return ret
|
375
384
|
end
|
376
385
|
end
|
377
386
|
|
data/lib/rubymta/receiver.rb
CHANGED
@@ -76,9 +76,14 @@ class Receiver
|
|
76
76
|
Timeout.timeout(ReceiverTimeout) do
|
77
77
|
begin
|
78
78
|
temp = @connection.gets
|
79
|
-
|
79
|
+
case
|
80
|
+
when temp.nil?
|
80
81
|
LOG.warn(@mail[:mail_id]) {"The client abruptly closed the connection"}
|
81
82
|
text = nil
|
83
|
+
when !temp.valid_encoding?
|
84
|
+
LOG.warn(@mail[:mail_id]) {"The client sent non-UTF-8 text"}
|
85
|
+
send_text("500 5.5.1 non-UTF-8 text detected")
|
86
|
+
raise Quit
|
82
87
|
else
|
83
88
|
text = temp.chomp
|
84
89
|
end
|
@@ -138,7 +143,7 @@ class Receiver
|
|
138
143
|
# Character . provided that it is not the first or last character,
|
139
144
|
# and provided also that it does not appear two or more times consecutively.
|
140
145
|
part[:dot_error] = true if (local_part[0]=='.' || local_part[-1]=='.' || local_part.index('..'))
|
141
|
-
m = local_part.match(/^[a-zA-Z0-9\!\#\$%&'*+-\/?^_`{|}
|
146
|
+
m = local_part.match(/^[a-zA-Z0-9\!\#\$%&'*+-\/?^_`{|}~=]+$/)
|
142
147
|
part[:char_error] = m.nil?
|
143
148
|
|
144
149
|
# lookup the email to see if it's one of ours
|
@@ -352,7 +357,7 @@ class Receiver
|
|
352
357
|
"550 5.1.7 beginning or ending '.' or 2 or more '.'s in a row" \
|
353
358
|
if from[:dot_error]
|
354
359
|
return "550-5.1.7 #{from[:local_part].inspect} can only", \
|
355
|
-
"550 5.1.7 contain a-z, A_Z, 0-9, and !#\$%&'*+-/?^_`{|}
|
360
|
+
"550 5.1.7 contain a-z, A_Z, 0-9, and !#\$%&'*+-/?^_`{|}~.=" \
|
356
361
|
if from[:char_error]
|
357
362
|
|
358
363
|
LOG.info(@mail[:mail_id]) {"Receiving mail from sender #{from[:url]}"}
|
data/lib/rubymta/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubymta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael J. Welch, Ph.D.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: RubyMta is an experimental mail transport agent written in Ruby. See
|
14
14
|
the README.
|