hermeneutics 1.21 → 1.23
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/LICENSE +1 -1
- data/lib/hermeneutics/cli/imap.rb +1 -2
- data/lib/hermeneutics/cli/lmtp.rb +183 -0
- data/lib/hermeneutics/cli/pop3.rb +0 -1
- data/lib/hermeneutics/cli/protocol.rb +12 -2
- data/lib/hermeneutics/cli/smtp.rb +1 -8
- data/lib/hermeneutics/escape.rb +1 -1
- data/lib/hermeneutics/mail.rb +17 -17
- data/lib/hermeneutics/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 81d01a3292ca2c19c8a0a7f285262c44584bb7d91d658dc5650774f9828bcd92
|
|
4
|
+
data.tar.gz: 92bf5d6e7c81f27da58666bba8e5df0716bafcefef2daec6b0dca1f716f2b20d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b97a17139583cd4955b413e09affd7349544d36ff0228d9c4238879567e9ed83c922f5f0d2a5cb41009afc7ebe94330ff7187d8d75c0452dc7e17b5e3bb23757
|
|
7
|
+
data.tar.gz: 198822fd194ff176303b2d05ab599d662458e6e9100ee0d1c711ce819ddb7da324c6b86377a148fce0ff0a1e16492e8947eed8e894d88512a73fef9d2cf120b4
|
data/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
= Hermeneutics -- Ruby mail and CGI handling
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2011-
|
|
3
|
+
Copyright (c) 2011-2024, Bertram Scharpf <software@bertram-scharpf.de>.
|
|
4
4
|
All rights reserved.
|
|
5
5
|
|
|
6
6
|
Redistribution and use in source and binary forms, with or without
|
|
@@ -16,7 +16,6 @@ module Hermeneutics
|
|
|
16
16
|
|
|
17
17
|
PORT, PORT_SSL = 143, 993
|
|
18
18
|
|
|
19
|
-
class Error < StandardError ; end
|
|
20
19
|
class UnspecResponse < Error ; end
|
|
21
20
|
class ServerBye < Error ; end
|
|
22
21
|
class ServerError < Error ; end
|
|
@@ -37,7 +36,7 @@ module Hermeneutics
|
|
|
37
36
|
|
|
38
37
|
def initialize *args
|
|
39
38
|
super
|
|
40
|
-
@tag = "
|
|
39
|
+
@tag = "%s%04d" % [ TAG_PREFIX, 0]
|
|
41
40
|
@info = [ get_response]
|
|
42
41
|
start_watch
|
|
43
42
|
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#
|
|
2
|
+
# lib/hermeneutics/cli/lmtp.rb -- LMTP client
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
require "hermeneutics/cli/protocol"
|
|
6
|
+
|
|
7
|
+
module Hermeneutics
|
|
8
|
+
|
|
9
|
+
module Cli
|
|
10
|
+
|
|
11
|
+
class LMTP < Protocol
|
|
12
|
+
|
|
13
|
+
CRLF = true
|
|
14
|
+
|
|
15
|
+
class UnspecError < Error ; end
|
|
16
|
+
class ServerNotReady < Error ; end
|
|
17
|
+
class NotOk < Error ; end
|
|
18
|
+
class NotReadyForData < Error ; end
|
|
19
|
+
class Unused < Error ; end
|
|
20
|
+
class Uncaught < Error ; end
|
|
21
|
+
|
|
22
|
+
class <<self
|
|
23
|
+
private :new
|
|
24
|
+
def open socketfile
|
|
25
|
+
UNIXSocket.open socketfile do |s|
|
|
26
|
+
i = new s, nil
|
|
27
|
+
yield i
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attr_reader :domain, :greet
|
|
33
|
+
attr_reader :advertised
|
|
34
|
+
attr_reader :last_response
|
|
35
|
+
|
|
36
|
+
def initialize *args
|
|
37
|
+
super
|
|
38
|
+
get_response.ok? or raise ServerNotReady, @last_response.msg
|
|
39
|
+
@rcpt = 0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def size
|
|
43
|
+
@advertised && @advertised[ :SIZE]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def lhlo host = nil
|
|
48
|
+
@advertised = {}
|
|
49
|
+
write_cmd "LHLO", host||Socket.gethostname
|
|
50
|
+
get_response_ok do |code,msg|
|
|
51
|
+
unless @domain then
|
|
52
|
+
@domain, @greet = msg.split nil, 2
|
|
53
|
+
next
|
|
54
|
+
end
|
|
55
|
+
keyword, param = msg.split nil, 2
|
|
56
|
+
keyword.upcase!
|
|
57
|
+
keyword = keyword.to_sym
|
|
58
|
+
case keyword
|
|
59
|
+
when :SIZE then param = Integer param
|
|
60
|
+
when :AUTH then param = param.split.map { |p| p.upcase! ; p.to_sym }
|
|
61
|
+
end
|
|
62
|
+
@advertised[ keyword] = param || true
|
|
63
|
+
end
|
|
64
|
+
unless @domain then
|
|
65
|
+
@domain, @greet = @last_response.msg.split nil, 2
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def mail_from from
|
|
70
|
+
cmd "MAIL", "FROM:<#{from}>"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def rcpt_to to
|
|
74
|
+
cmd "RCPT", "TO:<#{to}>"
|
|
75
|
+
@rcpt += 1
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def data reader
|
|
79
|
+
write_cmd "DATA"
|
|
80
|
+
get_response.waiting? or raise NotReadyForData, @last_response.msg
|
|
81
|
+
reader.each_line { |l|
|
|
82
|
+
l =~ /\A\./ and l = ".#{l}"
|
|
83
|
+
writeline l
|
|
84
|
+
}
|
|
85
|
+
writeline "."
|
|
86
|
+
get_response_rcpts
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def bdat data
|
|
90
|
+
data.each { |d|
|
|
91
|
+
write_cmd "BDAT", d.bytesize
|
|
92
|
+
write d
|
|
93
|
+
get_response_ok
|
|
94
|
+
}
|
|
95
|
+
write_cmd "BDAT", 0, "LAST"
|
|
96
|
+
get_response_rcpts
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def rset
|
|
100
|
+
cmd "RSET"
|
|
101
|
+
ensure
|
|
102
|
+
@rcpt = 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def noop str = nil
|
|
106
|
+
cmd "NOOP"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def quit
|
|
110
|
+
cmd "QUIT"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def cmd name, *args, &block
|
|
117
|
+
write_cmd name, *args
|
|
118
|
+
get_response_ok &block
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def write_cmd name, *args
|
|
122
|
+
l = [ name, *args].join " "
|
|
123
|
+
writeline l
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Response
|
|
127
|
+
|
|
128
|
+
attr_reader :code, :msg
|
|
129
|
+
|
|
130
|
+
def initialize code, msg
|
|
131
|
+
@code, @msg = code, msg
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def kat ; code / 100 ; end
|
|
135
|
+
|
|
136
|
+
def to_s ; "%03d %s" % [ @code, @msg] ; end
|
|
137
|
+
|
|
138
|
+
def prelim? ; kat == 1 ; end
|
|
139
|
+
def ok? ; kat == 2 ; end
|
|
140
|
+
def waiting? ; kat == 3 ; end
|
|
141
|
+
def error? ; kat == 4 ; end
|
|
142
|
+
def fatal? ; kat == 5 ; end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def get_response_ok &block
|
|
147
|
+
get_response &block
|
|
148
|
+
@last_response.ok? or raise NotOk, @last_response.msg
|
|
149
|
+
true
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def get_response
|
|
153
|
+
loop do
|
|
154
|
+
r = readline
|
|
155
|
+
if r =~ /\A(\d\d\d) / then
|
|
156
|
+
@last_response = Response.new $1.to_i, $'
|
|
157
|
+
break @last_response
|
|
158
|
+
elsif r =~ /\A(\d\d\d)-/ then
|
|
159
|
+
block_given? or raise Uncaught, r
|
|
160
|
+
yield $1.to_i, $'
|
|
161
|
+
else
|
|
162
|
+
raise UnspecError, r
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def get_response_rcpts
|
|
168
|
+
r = []
|
|
169
|
+
@rcpt.times {
|
|
170
|
+
r.push get_response
|
|
171
|
+
@last_response.ok? or raise NotOk, @last_response.msg
|
|
172
|
+
}
|
|
173
|
+
r
|
|
174
|
+
ensure
|
|
175
|
+
@rcpt = 0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
|
|
@@ -22,8 +22,12 @@ module Hermeneutics
|
|
|
22
22
|
|
|
23
23
|
module Cli
|
|
24
24
|
|
|
25
|
+
|
|
25
26
|
class Protocol
|
|
26
27
|
|
|
28
|
+
class Error < StandardError ; end
|
|
29
|
+
class Timeout < Error ; end
|
|
30
|
+
|
|
27
31
|
class <<self
|
|
28
32
|
private :new
|
|
29
33
|
def open host, port, timeout: nil, ssl: false
|
|
@@ -80,7 +84,7 @@ module Hermeneutics
|
|
|
80
84
|
end
|
|
81
85
|
|
|
82
86
|
def readline
|
|
83
|
-
|
|
87
|
+
wait
|
|
84
88
|
r = @socket.readline
|
|
85
89
|
r.chomp!
|
|
86
90
|
@trace and $stderr.puts "S: #{r}"
|
|
@@ -94,7 +98,7 @@ module Hermeneutics
|
|
|
94
98
|
end
|
|
95
99
|
|
|
96
100
|
def read bytes
|
|
97
|
-
|
|
101
|
+
wait
|
|
98
102
|
r = @socket.read bytes
|
|
99
103
|
@trace and $stderr.puts "S- #{r.inspect}"
|
|
100
104
|
r
|
|
@@ -105,6 +109,12 @@ module Hermeneutics
|
|
|
105
109
|
not @socket.ready?
|
|
106
110
|
end
|
|
107
111
|
|
|
112
|
+
def wait
|
|
113
|
+
if @timeout then
|
|
114
|
+
raise Timeout unless @socket.wait @timeout
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
108
118
|
end
|
|
109
119
|
|
|
110
120
|
|
|
@@ -14,7 +14,6 @@ module Hermeneutics
|
|
|
14
14
|
|
|
15
15
|
PORT, PORT_SSL = 25, 465
|
|
16
16
|
|
|
17
|
-
class Error < StandardError ; end
|
|
18
17
|
class UnspecError < Error ; end
|
|
19
18
|
class ServerNotReady < Error ; end
|
|
20
19
|
class NotOk < Error ; end
|
|
@@ -194,7 +193,7 @@ module Hermeneutics
|
|
|
194
193
|
r = readline
|
|
195
194
|
if r =~ /\A(\d\d\d) / then
|
|
196
195
|
@last_response = Response.new $1.to_i, $'
|
|
197
|
-
break
|
|
196
|
+
break @last_response
|
|
198
197
|
elsif r =~ /\A(\d\d\d)-/ then
|
|
199
198
|
block_given? or raise Uncaught, r
|
|
200
199
|
yield $1.to_i, $'
|
|
@@ -202,12 +201,6 @@ module Hermeneutics
|
|
|
202
201
|
raise UnspecError, r
|
|
203
202
|
end
|
|
204
203
|
end
|
|
205
|
-
@last_response
|
|
206
|
-
ensure
|
|
207
|
-
unless done? then
|
|
208
|
-
r = readline
|
|
209
|
-
r and raise Unused, "Unexpected data: #{r.inspect}"
|
|
210
|
-
end
|
|
211
204
|
end
|
|
212
205
|
|
|
213
206
|
end
|
data/lib/hermeneutics/escape.rb
CHANGED
|
@@ -235,7 +235,7 @@ module Hermeneutics
|
|
|
235
235
|
# representing the ASCII code. Eight bit characters should be masked the
|
|
236
236
|
# same way.
|
|
237
237
|
#
|
|
238
|
-
#
|
|
238
|
+
# A URL line does not store encoding information by itself. A locator may
|
|
239
239
|
# either say one of these:
|
|
240
240
|
#
|
|
241
241
|
# http://www.example.com/subdir/index.html?umlfield=%C3%BCber+alles
|
data/lib/hermeneutics/mail.rb
CHANGED
|
@@ -248,7 +248,7 @@ module Hermeneutics
|
|
|
248
248
|
}
|
|
249
249
|
open_smtp conn do |smtp|
|
|
250
250
|
log :INF, "Sending to", *tos
|
|
251
|
-
smtp.mail_from headers.from.first.plain
|
|
251
|
+
headers.from.empty? or smtp.mail_from headers.from.first.plain
|
|
252
252
|
tos.each { |t| smtp.rcpt_to t }
|
|
253
253
|
smtp.data m
|
|
254
254
|
end
|
|
@@ -261,22 +261,22 @@ module Hermeneutics
|
|
|
261
261
|
def open_smtp arg, &block
|
|
262
262
|
if [ :mail_from, :rcpt_to, :data].map { |m| arg.respond_to? m }.all? then
|
|
263
263
|
yield arg
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
264
|
+
else
|
|
265
|
+
a = {}
|
|
266
|
+
case arg
|
|
267
|
+
when nil then h, p = "localhost", nil
|
|
268
|
+
when String then h, p = arg.split ":" ; p &&= Integer p
|
|
269
|
+
when Array then h, p = *arg
|
|
270
|
+
when Hash then a = arg.clone ; h, p = (a.delete :host), (a.delete :port)
|
|
271
|
+
else h, p = arg.host, arg.port ; arg.scheme == "smtps" and a[ :ssl] = true
|
|
272
|
+
end
|
|
273
|
+
require "hermeneutics/cli/smtp"
|
|
274
|
+
Cli::SMTP.open h, p, **a do |smtp|
|
|
275
|
+
smtp.helo
|
|
276
|
+
yield smtp
|
|
277
|
+
ensure
|
|
278
|
+
smtp.quit
|
|
279
|
+
end
|
|
280
280
|
end
|
|
281
281
|
end
|
|
282
282
|
|
data/lib/hermeneutics/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hermeneutics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.23'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bertram Scharpf
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-10-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: supplement
|
|
@@ -59,6 +59,7 @@ files:
|
|
|
59
59
|
- lib/hermeneutics/cli/imap/commands.rb
|
|
60
60
|
- lib/hermeneutics/cli/imap/parser.rb
|
|
61
61
|
- lib/hermeneutics/cli/imap/utf7imap.rb
|
|
62
|
+
- lib/hermeneutics/cli/lmtp.rb
|
|
62
63
|
- lib/hermeneutics/cli/openssl.rb
|
|
63
64
|
- lib/hermeneutics/cli/pop3.rb
|
|
64
65
|
- lib/hermeneutics/cli/protocol.rb
|
|
@@ -97,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
97
98
|
version: '0'
|
|
98
99
|
requirements:
|
|
99
100
|
- Ruby, at least 3.0
|
|
100
|
-
rubygems_version: 3.5.
|
|
101
|
+
rubygems_version: 3.5.21
|
|
101
102
|
signing_key:
|
|
102
103
|
specification_version: 4
|
|
103
104
|
summary: CGI and mail handling
|