mailbounce 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +20 -14
- data/lib/mailbounce/report.rb +29 -14
- data/lib/mailbounce/version.rb +1 -1
- data/lib/mailbounce.rb +2 -2
- data/mailbounce.gemspec +2 -3
- metadata +4 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f99ac991751ae3b826fb20a5e9e9f13c0871ef50b8957abab82daddccad242a
|
|
4
|
+
data.tar.gz: 5568d4cfaf4644bf4c984116276e0142ec1d4842bf79c6fed565f5dd965c2484
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19e7c1b2748c550f40c283841e869de952724c29defc24404a2c7d0f251f515150e59383f304261c2b71ffb4d6e66d1f587952dd2e35ac86bc4c4edc9f11b6d5
|
|
7
|
+
data.tar.gz: febb828d483008e5abcd932ed760f86db3f8cde575f32ef47ae901a89d62b66a67c8349515aadababf0950bd85c9ca256f52c6dbfc57bb293d2f88677057f0ab
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# mailbounce
|
|
2
2
|
|
|
3
|
-
📭 Email delivery failures for Ruby
|
|
3
|
+
📭 Email delivery failures for Ruby.
|
|
4
4
|
|
|
5
5
|
Answers one question about a rejected message: whose fault was it?
|
|
6
6
|
|
|
7
|
-
**mailbounce** reads the two ways mail comes back: a rejection during the SMTP session, and a delivery status notification (RFC 3464) arriving afterwards.
|
|
7
|
+
**mailbounce** reads the two ways mail comes back: a rejection during the SMTP session, and a delivery status notification (RFC 3464) arriving afterwards.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -24,6 +24,8 @@ Answers one question about a rejected message: whose fault was it?
|
|
|
24
24
|
- [Reading a Report](#reading-a-report)
|
|
25
25
|
- [Testing](#testing)
|
|
26
26
|
- [History](#history)
|
|
27
|
+
- [Contributing](#contributing)
|
|
28
|
+
- [License](#license)
|
|
27
29
|
|
|
28
30
|
## Getting Started
|
|
29
31
|
|
|
@@ -52,13 +54,13 @@ rejection.about_us? # => false
|
|
|
52
54
|
|
|
53
55
|
## Categories
|
|
54
56
|
|
|
55
|
-
| Category
|
|
56
|
-
|
|
|
57
|
-
| `:invalid`
|
|
58
|
-
| `:full`
|
|
59
|
-
| `:oversized` | This message was too large; another might not be
|
|
60
|
-
| `:blocked`
|
|
61
|
-
| `:unknown`
|
|
57
|
+
| Category | What it means |
|
|
58
|
+
| ------------ | ------------------------------------------------------------------------------------------ |
|
|
59
|
+
| `:invalid` | The mailbox doesn't exist - the one category that says something lasting about the address |
|
|
60
|
+
| `:full` | The mailbox exists and is over quota |
|
|
61
|
+
| `:oversized` | This message was too large; another might not be |
|
|
62
|
+
| `:blocked` | Policy, reputation, or the network - about the exchange, not the address |
|
|
63
|
+
| `:unknown` | Nothing matched |
|
|
62
64
|
|
|
63
65
|
Classification reads the enhanced status code first, then falls back to wording, since plenty of servers pair a bare `5.0.0` with a diagnostic that names the cause. The wording it knows is what Postfix, Exim, and Sendmail say by default - there's no per-provider table, since those go stale silently.
|
|
64
66
|
|
|
@@ -108,6 +110,12 @@ Reports announce delays and successes by the same route and in the same shape, s
|
|
|
108
110
|
|
|
109
111
|
A report naming several recipients speaks for a given address only when it names it; one covering a single recipient needn't name anyone. Nothing raises: a report with no machine-readable part, or bytes that aren't a message at all, reports nothing.
|
|
110
112
|
|
|
113
|
+
Bounce are cappted to 1 MB. Past that a report is refused like any other it can't read. Lower it where you'd rather hold less:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
MailBounce.parse(raw_bounce, max_size: 64 * 1024)
|
|
117
|
+
```
|
|
118
|
+
|
|
111
119
|
## Testing
|
|
112
120
|
|
|
113
121
|
```sh
|
|
@@ -115,21 +123,19 @@ bundle install
|
|
|
115
123
|
rake
|
|
116
124
|
```
|
|
117
125
|
|
|
118
|
-
Nothing touches the network. Fixtures in `test/fixtures/` are complete bounce messages covering permanent failure, delay, transient failure, several recipients, an unnamed recipient, and a report with no machine-readable part at all.
|
|
119
|
-
|
|
120
126
|
## History
|
|
121
127
|
|
|
122
|
-
View the changelog
|
|
128
|
+
View the [changelog](CHANGELOG.md).
|
|
123
129
|
|
|
124
130
|
## Contributing
|
|
125
131
|
|
|
126
132
|
Everyone is encouraged to help improve this project:
|
|
127
133
|
|
|
128
|
-
- [Report bugs](https://github.com/
|
|
134
|
+
- [Report bugs](https://github.com/mailpiece/mailbounce/issues)
|
|
129
135
|
- Fix bugs and submit pull requests
|
|
130
136
|
- Write, clarify, or fix documentation
|
|
131
137
|
- Suggest or add new features
|
|
132
138
|
|
|
133
139
|
## License
|
|
134
140
|
|
|
135
|
-
MIT. See [LICENSE](LICENSE).
|
|
141
|
+
MIT. See [LICENSE](LICENSE).
|
data/lib/mailbounce/report.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "mail"
|
|
2
|
+
require "set"
|
|
2
3
|
|
|
3
4
|
require "mailbounce/recipient"
|
|
4
5
|
|
|
@@ -13,6 +14,10 @@ module MailBounce
|
|
|
13
14
|
|
|
14
15
|
FIELD_NAME = /\A(?<name>[A-Za-z][A-Za-z0-9\-]*)[ \t]*:/
|
|
15
16
|
|
|
17
|
+
# A bounce is a notice, not a mailbox — Postfix caps its own at 50 KB.
|
|
18
|
+
# Anything past this is not a report anyone meant to send.
|
|
19
|
+
MAX_SIZE = 1024 * 1024
|
|
20
|
+
|
|
16
21
|
# Value stays on its line so an empty field cannot swallow the next.
|
|
17
22
|
FIELDS = {
|
|
18
23
|
action: /^Action[ \t]*:[ \t]*(?<value>.+)$/i,
|
|
@@ -23,12 +28,14 @@ module MailBounce
|
|
|
23
28
|
remote_mta: /^Remote-MTA[ \t]*:[ \t]*(?<value>.+)$/i
|
|
24
29
|
}.freeze
|
|
25
30
|
|
|
26
|
-
def self.parse(raw)
|
|
27
|
-
new(raw)
|
|
31
|
+
def self.parse(raw, max_size: MAX_SIZE)
|
|
32
|
+
new(raw, max_size: max_size)
|
|
28
33
|
end
|
|
29
34
|
|
|
30
|
-
def initialize(raw)
|
|
31
|
-
|
|
35
|
+
def initialize(raw, max_size: MAX_SIZE)
|
|
36
|
+
report = raw.to_s
|
|
37
|
+
|
|
38
|
+
@mail = Mail.new(report) if report.bytesize <= max_size
|
|
32
39
|
rescue StandardError
|
|
33
40
|
@mail = nil
|
|
34
41
|
end
|
|
@@ -83,22 +90,30 @@ module MailBounce
|
|
|
83
90
|
end
|
|
84
91
|
|
|
85
92
|
# A block ends when a field it already has reappears (order is not fixed).
|
|
93
|
+
# The names are kept rather than matched for again: a block grows, and
|
|
94
|
+
# reading all of it per line costs the square of its length.
|
|
86
95
|
def per_recipient(chunk)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
blocks, named = [], Set.new
|
|
97
|
+
|
|
98
|
+
chunk.lines.each do |line|
|
|
99
|
+
name = field_name(line)
|
|
100
|
+
|
|
101
|
+
if blocks.empty? || named.include?(name)
|
|
91
102
|
blocks << +line
|
|
103
|
+
named = Set.new
|
|
104
|
+
else
|
|
105
|
+
blocks.last << line
|
|
92
106
|
end
|
|
107
|
+
|
|
108
|
+
named << name if name
|
|
93
109
|
end
|
|
110
|
+
|
|
111
|
+
blocks
|
|
94
112
|
end
|
|
95
113
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
else
|
|
100
|
-
true
|
|
101
|
-
end
|
|
114
|
+
# Field names are case-insensitive; a continuation line has none.
|
|
115
|
+
def field_name(line)
|
|
116
|
+
line[FIELD_NAME, :name]&.downcase
|
|
102
117
|
end
|
|
103
118
|
|
|
104
119
|
def recipient_from(block)
|
data/lib/mailbounce/version.rb
CHANGED
data/lib/mailbounce.rb
CHANGED
data/mailbounce.gemspec
CHANGED
|
@@ -7,10 +7,9 @@ Gem::Specification.new do |spec|
|
|
|
7
7
|
spec.platform = Gem::Platform::RUBY
|
|
8
8
|
spec.required_ruby_version = ">= 3.3.4"
|
|
9
9
|
spec.authors = [ "Simon Lev" ]
|
|
10
|
-
spec.email = [ "support@postrider.dev" ]
|
|
11
10
|
|
|
12
|
-
spec.summary = "
|
|
13
|
-
spec.description = "Answers one question about a rejected message: whose fault was it?"
|
|
11
|
+
spec.summary = "Email delivery failures for Ruby."
|
|
12
|
+
spec.description = "Answers one question about a rejected message: whose fault was it? Parses bounces, NDRs, and DSN reports to classify delivery failures."
|
|
14
13
|
|
|
15
14
|
spec.homepage = "https://github.com/mailpiece/mailbounce"
|
|
16
15
|
spec.license = "MIT"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mailbounce
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Lev
|
|
@@ -23,9 +23,8 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '2.7'
|
|
26
|
-
description: 'Answers one question about a rejected message: whose fault was it?
|
|
27
|
-
|
|
28
|
-
- support@postrider.dev
|
|
26
|
+
description: 'Answers one question about a rejected message: whose fault was it? Parses
|
|
27
|
+
bounces, NDRs, and DSN reports to classify delivery failures.'
|
|
29
28
|
executables: []
|
|
30
29
|
extensions: []
|
|
31
30
|
extra_rdoc_files: []
|
|
@@ -64,5 +63,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
64
63
|
requirements: []
|
|
65
64
|
rubygems_version: 4.0.3
|
|
66
65
|
specification_version: 4
|
|
67
|
-
summary:
|
|
66
|
+
summary: Email delivery failures for Ruby.
|
|
68
67
|
test_files: []
|