hermeneutics 1.11 → 1.13
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/README +11 -0
- data/bin/hermesmail +93 -58
- data/lib/hermeneutics/addrs.rb +6 -4
- data/lib/hermeneutics/boxes.rb +134 -119
- data/lib/hermeneutics/cgi.rb +16 -6
- data/lib/hermeneutics/cli/imap/commands.rb +283 -0
- data/lib/hermeneutics/cli/imap/parser.rb +245 -0
- data/lib/hermeneutics/cli/imap/utf7imap.rb +75 -0
- data/lib/hermeneutics/cli/imap.rb +240 -0
- data/lib/hermeneutics/cli/openssl.rb +11 -0
- data/lib/hermeneutics/cli/pop3.rb +257 -0
- data/lib/hermeneutics/cli/protocol.rb +141 -0
- data/lib/hermeneutics/cli/smtp.rb +218 -0
- data/lib/hermeneutics/color.rb +8 -8
- data/lib/hermeneutics/contents.rb +6 -0
- data/lib/hermeneutics/css.rb +10 -10
- data/lib/hermeneutics/escape.rb +30 -34
- data/lib/hermeneutics/html.rb +4 -4
- data/lib/hermeneutics/mail.rb +248 -63
- data/lib/hermeneutics/message.rb +237 -254
- data/lib/hermeneutics/types.rb +8 -7
- data/lib/hermeneutics/version.rb +3 -4
- metadata +26 -5
- data/lib/hermeneutics/cli/pop.rb +0 -102
- data/lib/hermeneutics/transports.rb +0 -230
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d06afea9e09641e4c0c05bc3fca3590c3b4002047092034a379ab9a096867a57
|
|
4
|
+
data.tar.gz: 21944a76c2279a66b7de8ea9d32ad6db9328898c7ff944a2b736e0c3500dd61c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 399dd807c48582880089a94580b1c939ad57fc5da148b9987dff1f77729e731b62863f2f44a8a7540aefc4edc9dd211ab8dfe5a759d654ab6f9de682c968f9e7
|
|
7
|
+
data.tar.gz: 61514f34f392828df42f4123e49c6d7af7aecaf0dd3a503241ee60d5ee811e5e8fecb957cebee7bca4a407afab656ce96922f5ebf7d6c683e584acc3c56bf70f
|
data/README
ADDED
data/bin/hermesmail
CHANGED
|
@@ -11,8 +11,7 @@ rescue LoadError
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
require "hermeneutics/version"
|
|
14
|
-
require "hermeneutics/
|
|
15
|
-
require "hermeneutics/cli/pop"
|
|
14
|
+
require "hermeneutics/mail"
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
module Hermeneutics
|
|
@@ -38,7 +37,7 @@ module Hermeneutics
|
|
|
38
37
|
|
|
39
38
|
# Forward by SMTP
|
|
40
39
|
def forward_smtp to
|
|
41
|
-
send nil, to
|
|
40
|
+
send! nil, to
|
|
42
41
|
done
|
|
43
42
|
end
|
|
44
43
|
|
|
@@ -50,45 +49,26 @@ module Hermeneutics
|
|
|
50
49
|
alias forward forward_smtp
|
|
51
50
|
|
|
52
51
|
|
|
53
|
-
self.logfile = "hermesmail.log"
|
|
54
|
-
self.loglevel = :ERR
|
|
55
|
-
|
|
56
52
|
@failed_process = "=failed-process"
|
|
53
|
+
@failed_parse = "=failed-parse"
|
|
57
54
|
|
|
58
55
|
class <<self
|
|
59
|
-
attr_accessor :failed_process
|
|
56
|
+
attr_accessor :failed_process, :failed_parse
|
|
60
57
|
def process input, debug = false
|
|
61
58
|
i = parse input
|
|
62
59
|
i.debug = debug
|
|
63
60
|
i.execute
|
|
64
61
|
rescue
|
|
65
62
|
raise if debug
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
log_exception "Error while parsing mail"
|
|
64
|
+
b = box @failed_parse
|
|
65
|
+
log :INF, "Saving to", b.path
|
|
66
|
+
b.store_raw input, nil, nil
|
|
70
67
|
end
|
|
71
68
|
def log_exception msg, *args
|
|
72
69
|
log :ERR, "#{msg}: #$! (#{$!.class})", *args
|
|
73
70
|
$!.backtrace.each { |b| log :INF, " #{b}" }
|
|
74
71
|
end
|
|
75
|
-
private
|
|
76
|
-
def open_failed
|
|
77
|
-
i = 0
|
|
78
|
-
d = expand_sysdir
|
|
79
|
-
w = Time.now.strftime "%Y%m%d%H%M%S"
|
|
80
|
-
begin
|
|
81
|
-
p = File.join d, "failed-#{w}-%05d" % i
|
|
82
|
-
File.open p, File::CREAT|File::EXCL|File::WRONLY do |f|
|
|
83
|
-
yield f
|
|
84
|
-
end
|
|
85
|
-
rescue Errno::ENOENT
|
|
86
|
-
Dir.mkdir! d and retry
|
|
87
|
-
rescue Errno::EEXIST
|
|
88
|
-
i +=1
|
|
89
|
-
retry
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
72
|
end
|
|
93
73
|
|
|
94
74
|
def execute
|
|
@@ -98,12 +78,12 @@ module Hermeneutics
|
|
|
98
78
|
rescue
|
|
99
79
|
raise if @debug
|
|
100
80
|
log_exception "Error while processing mail"
|
|
101
|
-
b =
|
|
81
|
+
b = self.class.box self.class.failed_process
|
|
102
82
|
save b
|
|
103
83
|
end
|
|
104
84
|
|
|
105
85
|
def log_exception msg, *args
|
|
106
|
-
|
|
86
|
+
self.class.log_exception msg, *args
|
|
107
87
|
end
|
|
108
88
|
|
|
109
89
|
end
|
|
@@ -112,44 +92,99 @@ module Hermeneutics
|
|
|
112
92
|
class Fetch
|
|
113
93
|
|
|
114
94
|
class <<self
|
|
95
|
+
|
|
115
96
|
private :new
|
|
116
97
|
def create *args, &block
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
98
|
+
@list = []
|
|
99
|
+
class_eval *args, &block
|
|
100
|
+
new @list
|
|
101
|
+
ensure
|
|
102
|
+
@list = nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def pop *args, **kwargs
|
|
106
|
+
access Pop, *args, **kwargs do yield end
|
|
107
|
+
end
|
|
108
|
+
def login *args
|
|
109
|
+
@access[ :logins].push args
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
def access type, *args, **kwargs
|
|
115
|
+
@access and raise "Access methods must not be nested."
|
|
116
|
+
@access = { type: type, args: args, kwargs: kwargs, logins: [] }
|
|
117
|
+
yield
|
|
118
|
+
@list.push @access
|
|
119
|
+
nil
|
|
120
|
+
ensure
|
|
121
|
+
@access = nil
|
|
128
122
|
end
|
|
129
|
-
end
|
|
130
123
|
|
|
131
|
-
def initialize
|
|
132
|
-
@list = []
|
|
133
124
|
end
|
|
134
125
|
|
|
135
|
-
def
|
|
136
|
-
|
|
126
|
+
def initialize list
|
|
127
|
+
@list = list
|
|
128
|
+
end
|
|
137
129
|
|
|
138
|
-
def
|
|
139
|
-
@
|
|
140
|
-
|
|
130
|
+
def each
|
|
131
|
+
@list.each { |a|
|
|
132
|
+
c = a[ :type].new *a[ :args], **a[ :kwargs]
|
|
133
|
+
a[ :logins].each { |l|
|
|
134
|
+
c.login *l do yield c end
|
|
135
|
+
}
|
|
136
|
+
}
|
|
141
137
|
end
|
|
142
138
|
|
|
139
|
+
class Keep < Exception ; end
|
|
140
|
+
|
|
143
141
|
private
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
|
|
144
|
+
class Pop
|
|
145
|
+
|
|
146
|
+
def initialize host, port = nil, ssl: nil
|
|
147
|
+
if not port and host =~ /:(\d+)\z/ then
|
|
148
|
+
host, port = $`, $1.to_i
|
|
149
|
+
end
|
|
150
|
+
@host, @port, @ssl = host, port, ssl
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def login user, password
|
|
154
|
+
require "hermeneutics/cli/pop3"
|
|
155
|
+
Cli::POP3.open @host, @port, ssl: @ssl do |pop|
|
|
156
|
+
@user, @password, @pop = user, password, pop
|
|
157
|
+
@pop.authenticate @user, @password
|
|
158
|
+
yield
|
|
159
|
+
@pop.quit
|
|
160
|
+
end
|
|
161
|
+
ensure
|
|
162
|
+
@user, @password, @pop = nil, nil, nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def name
|
|
166
|
+
@user or raise "Not logged in."
|
|
167
|
+
r = "#@user@#@host"
|
|
168
|
+
r << ":#@port" if @port
|
|
169
|
+
r
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def count
|
|
173
|
+
c, = @pop.stat
|
|
174
|
+
c
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def each
|
|
178
|
+
@pop.list.each { |k,|
|
|
179
|
+
text = @pop.retr k
|
|
180
|
+
begin
|
|
181
|
+
yield text
|
|
182
|
+
@pop.dele k
|
|
183
|
+
rescue Keep
|
|
184
|
+
end
|
|
185
|
+
}
|
|
186
|
+
end
|
|
187
|
+
|
|
153
188
|
end
|
|
154
189
|
|
|
155
190
|
end
|
|
@@ -231,7 +266,7 @@ module Hermeneutics
|
|
|
231
266
|
print "\r#{i}/#{c} " if @quiet < 1
|
|
232
267
|
i += 1
|
|
233
268
|
Processed.process m
|
|
234
|
-
raise
|
|
269
|
+
raise Fetch::Keep if @keep
|
|
235
270
|
}
|
|
236
271
|
puts "\rDone. " if @quiet < 1
|
|
237
272
|
}
|
data/lib/hermeneutics/addrs.rb
CHANGED
|
@@ -95,8 +95,6 @@ module Hermeneutics
|
|
|
95
95
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
attr_reader :mail, :real
|
|
99
|
-
|
|
100
98
|
def initialize mail, real
|
|
101
99
|
@mail, @real = mail, real
|
|
102
100
|
@mail.compact!
|
|
@@ -138,6 +136,8 @@ module Hermeneutics
|
|
|
138
136
|
tokenized.encode
|
|
139
137
|
end
|
|
140
138
|
|
|
139
|
+
private
|
|
140
|
+
|
|
141
141
|
def tokenized
|
|
142
142
|
r = Token[ :addr, [ Token[ :lang] , @mail, Token[ :rang]]]
|
|
143
143
|
if @real then
|
|
@@ -146,8 +146,6 @@ module Hermeneutics
|
|
|
146
146
|
r
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
-
private
|
|
150
|
-
|
|
151
149
|
def mk_plain
|
|
152
150
|
p = @mail.to_s
|
|
153
151
|
p.downcase!
|
|
@@ -592,6 +590,9 @@ module Hermeneutics
|
|
|
592
590
|
|
|
593
591
|
public
|
|
594
592
|
|
|
593
|
+
def empty? ; @list.empty? ; end
|
|
594
|
+
def notempty? ; self if @list.notempty? ; end
|
|
595
|
+
|
|
595
596
|
def push addrs
|
|
596
597
|
case addrs
|
|
597
598
|
when nil then
|
|
@@ -600,6 +601,7 @@ module Hermeneutics
|
|
|
600
601
|
else addrs.each { |a| push a }
|
|
601
602
|
end
|
|
602
603
|
end
|
|
604
|
+
alias << push
|
|
603
605
|
|
|
604
606
|
def inspect
|
|
605
607
|
"<#{self.class}: " + (@list.map { |a| a.inspect }.join ", ") + ">"
|
data/lib/hermeneutics/boxes.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
=begin rdoc
|
|
6
6
|
|
|
7
|
-
:section: Classes
|
|
7
|
+
:section: Classes defined here
|
|
8
8
|
|
|
9
9
|
Hermeneutics::Box is a general Mailbox.
|
|
10
10
|
|
|
@@ -18,7 +18,6 @@ Hermeneutics::Maildir is the maildir format.
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
require "supplement"
|
|
21
|
-
require "supplement/locked"
|
|
22
21
|
require "date"
|
|
23
22
|
|
|
24
23
|
|
|
@@ -31,15 +30,18 @@ module Hermeneutics
|
|
|
31
30
|
|
|
32
31
|
class <<self
|
|
33
32
|
|
|
33
|
+
attr_accessor :default_format
|
|
34
|
+
|
|
34
35
|
# :call-seq:
|
|
35
36
|
# Box.find( path, default = nil) -> box
|
|
36
37
|
#
|
|
37
38
|
# Create a Box object (some subclass of Box), depending on
|
|
38
|
-
# what type the box is found at
|
|
39
|
+
# what type the box is found at +path+.
|
|
39
40
|
#
|
|
40
41
|
def find path, default_format = nil
|
|
41
42
|
b = @boxes.find { |b| b.check path }
|
|
42
43
|
b ||= default_format
|
|
44
|
+
b ||= @default_format
|
|
43
45
|
b ||= if File.directory? path then
|
|
44
46
|
Maildir
|
|
45
47
|
elsif File.file? path then
|
|
@@ -71,7 +73,7 @@ module Hermeneutics
|
|
|
71
73
|
# :call-seq:
|
|
72
74
|
# Box.new( path) -> box
|
|
73
75
|
#
|
|
74
|
-
# Instantiate a Box object, just store the
|
|
76
|
+
# Instantiate a Box object, just store the +path+.
|
|
75
77
|
#
|
|
76
78
|
def initialize mailbox
|
|
77
79
|
@mailbox = mailbox
|
|
@@ -84,12 +86,39 @@ module Hermeneutics
|
|
|
84
86
|
# :call-seq:
|
|
85
87
|
# box.exists? -> true or false
|
|
86
88
|
#
|
|
87
|
-
# Test whether the
|
|
89
|
+
# Test whether the +Box+ exists.
|
|
88
90
|
#
|
|
89
91
|
def exists?
|
|
90
92
|
self.class.check @mailbox
|
|
91
93
|
end
|
|
92
94
|
|
|
95
|
+
# :call-seq:
|
|
96
|
+
# mbox.store( msg) -> nil
|
|
97
|
+
#
|
|
98
|
+
# Store the mail to the local +MBox+.
|
|
99
|
+
#
|
|
100
|
+
def store msg
|
|
101
|
+
store_raw msg.to_s, msg.plain_from, msg.created
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# :call-seq:
|
|
105
|
+
# mbox.each { |mail| ... } -> nil
|
|
106
|
+
#
|
|
107
|
+
# Iterate through +MBox+.
|
|
108
|
+
# Alias for +MBox#each_mail+.
|
|
109
|
+
#
|
|
110
|
+
def each &block ; each_mail &block ; end
|
|
111
|
+
include Enumerable
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def local_from
|
|
116
|
+
require "etc"
|
|
117
|
+
require "socket"
|
|
118
|
+
s = File.stat @mailbox
|
|
119
|
+
lfrom = "#{(Etc.getpwuid s.uid).name}@#{Socket.gethostname}"
|
|
120
|
+
end
|
|
121
|
+
|
|
93
122
|
end
|
|
94
123
|
|
|
95
124
|
class MBox < Box
|
|
@@ -102,7 +131,7 @@ module Hermeneutics
|
|
|
102
131
|
# :call-seq:
|
|
103
132
|
# MBox.check( path) -> true or false
|
|
104
133
|
#
|
|
105
|
-
# Check whether path is a
|
|
134
|
+
# Check whether path is a +MBox+.
|
|
106
135
|
#
|
|
107
136
|
def check path
|
|
108
137
|
if File.file? path then
|
|
@@ -114,48 +143,10 @@ module Hermeneutics
|
|
|
114
143
|
|
|
115
144
|
end
|
|
116
145
|
|
|
117
|
-
# :stopdoc:
|
|
118
|
-
class Region
|
|
119
|
-
class <<self
|
|
120
|
-
private :new
|
|
121
|
-
def open file, start, stop
|
|
122
|
-
t = file.tell
|
|
123
|
-
begin
|
|
124
|
-
i = new file, start, stop
|
|
125
|
-
yield i
|
|
126
|
-
ensure
|
|
127
|
-
file.seek t
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
def initialize file, start, stop
|
|
132
|
-
@file, @start, @stop = file, start, stop
|
|
133
|
-
rewind
|
|
134
|
-
end
|
|
135
|
-
def rewind ; @file.seek @start ; end
|
|
136
|
-
def read n = nil
|
|
137
|
-
m = @stop - @file.tell
|
|
138
|
-
n = m if not n or n > m
|
|
139
|
-
@file.read n
|
|
140
|
-
end
|
|
141
|
-
def to_s
|
|
142
|
-
rewind
|
|
143
|
-
read
|
|
144
|
-
end
|
|
145
|
-
def each_line
|
|
146
|
-
@file.each_line { |l|
|
|
147
|
-
break if @file.tell > @stop
|
|
148
|
-
yield l
|
|
149
|
-
}
|
|
150
|
-
end
|
|
151
|
-
alias eat_lines each_line
|
|
152
|
-
end
|
|
153
|
-
# :startdoc:
|
|
154
|
-
|
|
155
146
|
# :call-seq:
|
|
156
147
|
# mbox.create -> self
|
|
157
148
|
#
|
|
158
|
-
# Create the
|
|
149
|
+
# Create the +MBox+.
|
|
159
150
|
#
|
|
160
151
|
def create
|
|
161
152
|
d = File.dirname @mailbox
|
|
@@ -165,65 +156,69 @@ module Hermeneutics
|
|
|
165
156
|
end
|
|
166
157
|
|
|
167
158
|
# :call-seq:
|
|
168
|
-
# mbox.
|
|
159
|
+
# mbox.store_raw( text, from, created) -> nil
|
|
169
160
|
#
|
|
170
|
-
# Store
|
|
161
|
+
# Store some text that appears like a mail to the local +MBox+.
|
|
171
162
|
#
|
|
172
|
-
def
|
|
173
|
-
|
|
174
|
-
|
|
163
|
+
def store_raw text, from, created
|
|
164
|
+
from ||= local_from
|
|
165
|
+
created ||= Time.now
|
|
166
|
+
File.open @mailbox, "r+", encoding: Encoding::ASCII_8BIT do |f|
|
|
175
167
|
f.seek [ f.size - 4, 0].max
|
|
176
|
-
last =
|
|
168
|
+
last = nil
|
|
177
169
|
f.read.each_line { |l| last = l }
|
|
178
|
-
f.puts
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
170
|
+
f.puts if last and not last =~ RE_N
|
|
171
|
+
|
|
172
|
+
f.puts "From #{from.gsub ' ', '_'} #{created.to_time.gmtime.asctime}"
|
|
173
|
+
text.each_line { |l|
|
|
174
|
+
l.chomp!
|
|
175
|
+
f.print ">" if l =~ RE_F
|
|
176
|
+
f.puts l
|
|
177
|
+
}
|
|
184
178
|
f.puts
|
|
185
179
|
end
|
|
186
|
-
|
|
180
|
+
nil
|
|
187
181
|
end
|
|
188
182
|
|
|
189
183
|
# :call-seq:
|
|
190
|
-
# mbox.
|
|
184
|
+
# mbox.each_mail { |mail| ... } -> nil
|
|
191
185
|
#
|
|
192
|
-
# Iterate through
|
|
186
|
+
# Iterate through +MBox+.
|
|
193
187
|
#
|
|
194
|
-
def
|
|
188
|
+
def each_mail
|
|
195
189
|
File.open @mailbox, encoding: Encoding::ASCII_8BIT do |f|
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
nl_seen = false
|
|
191
|
+
from, created, text = nil, nil, nil
|
|
198
192
|
f.each_line { |l|
|
|
199
|
-
|
|
200
|
-
if
|
|
193
|
+
l.chomp!
|
|
194
|
+
if l =~ RE_F then
|
|
195
|
+
l = $'
|
|
196
|
+
yield text, from, created if text
|
|
197
|
+
length_tried = false
|
|
198
|
+
from, created = l.split nil, 2
|
|
201
199
|
begin
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
created = DateTime.parse created
|
|
201
|
+
rescue Date::Error
|
|
202
|
+
unless length_tried then
|
|
203
|
+
from = $'
|
|
204
|
+
created = from.slice! from.length-Time.now.ctime.length, from.length
|
|
205
|
+
from.strip!
|
|
206
|
+
length_tried = true
|
|
207
|
+
retry
|
|
208
|
+
end
|
|
209
|
+
raise "#@mailbox does not seem to be a mailbox: From line '#{l}'."
|
|
205
210
|
end
|
|
211
|
+
text, nl_seen = "", false
|
|
206
212
|
else
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
from or raise "#@mailbox does not seem to be a mailbox. No 'From' line."
|
|
214
|
+
text << "\n" if nl_seen
|
|
215
|
+
nl_seen = l =~ RE_N
|
|
216
|
+
nl_seen or text << l << "\n"
|
|
209
217
|
end
|
|
210
218
|
}
|
|
211
|
-
|
|
212
|
-
e ||= f.tell
|
|
213
|
-
m and Region.open f, m, e, &block
|
|
219
|
+
text and yield text, from, created
|
|
214
220
|
end
|
|
215
221
|
end
|
|
216
|
-
include Enumerable
|
|
217
|
-
|
|
218
|
-
private
|
|
219
|
-
|
|
220
|
-
def is_from_line? l
|
|
221
|
-
l =~ RE_F or return
|
|
222
|
-
addr, time = $'.split nil, 2
|
|
223
|
-
DateTime.parse time
|
|
224
|
-
addr =~ /@/
|
|
225
|
-
rescue ArgumentError, TypeError
|
|
226
|
-
end
|
|
227
222
|
|
|
228
223
|
end
|
|
229
224
|
|
|
@@ -237,7 +232,7 @@ module Hermeneutics
|
|
|
237
232
|
# :call-seq:
|
|
238
233
|
# Maildir.check( path) -> true or false
|
|
239
234
|
#
|
|
240
|
-
# Check whether path is a
|
|
235
|
+
# Check whether path is a +Maildir+.
|
|
241
236
|
#
|
|
242
237
|
def check mailbox
|
|
243
238
|
if File.directory? mailbox then
|
|
@@ -254,7 +249,7 @@ module Hermeneutics
|
|
|
254
249
|
# :call-seq:
|
|
255
250
|
# maildir.create -> self
|
|
256
251
|
#
|
|
257
|
-
# Create the
|
|
252
|
+
# Create the +Maildir+.
|
|
258
253
|
#
|
|
259
254
|
def create
|
|
260
255
|
Dir.mkdir! @mailbox
|
|
@@ -266,53 +261,73 @@ module Hermeneutics
|
|
|
266
261
|
end
|
|
267
262
|
|
|
268
263
|
# :call-seq:
|
|
269
|
-
# maildir.
|
|
264
|
+
# maildir.store_raw( text, from, created) -> nil
|
|
270
265
|
#
|
|
271
|
-
# Store
|
|
266
|
+
# Store some text that appears like a mail to the local +MBox+.
|
|
272
267
|
#
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
File.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
def store_raw text, from, created
|
|
269
|
+
filename = mkfilename from, created
|
|
270
|
+
tpath = File.join @mailbox, TMP, filename
|
|
271
|
+
File.open tpath, File::CREAT|File::EXCL|File::WRONLY do |f| f.puts text end
|
|
272
|
+
cpath = File.join @mailbox, NEW, filename
|
|
273
|
+
File.link tpath, cpath
|
|
274
|
+
filename
|
|
275
|
+
rescue Errno::EEXIST
|
|
276
|
+
File.unlink tpath rescue nil
|
|
277
|
+
retry
|
|
278
|
+
ensure
|
|
279
|
+
File.unlink tpath
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# :call-seq:
|
|
283
|
+
# mbox.each_file { |filename| ... } -> nil
|
|
284
|
+
#
|
|
285
|
+
# Iterate through +Maildir+.
|
|
286
|
+
#
|
|
287
|
+
def each_file new = nil
|
|
288
|
+
p = File.join @mailbox, new ? NEW : CUR
|
|
289
|
+
(Dir.new p).sort.each { |fn|
|
|
290
|
+
next if fn.starts_with? "."
|
|
291
|
+
path = File.join p, fn
|
|
292
|
+
yield path
|
|
293
|
+
}
|
|
281
294
|
end
|
|
282
295
|
|
|
283
296
|
# :call-seq:
|
|
284
297
|
# mbox.each { |mail| ... } -> nil
|
|
285
298
|
#
|
|
286
|
-
# Iterate through
|
|
299
|
+
# Iterate through +Maildir+.
|
|
287
300
|
#
|
|
288
|
-
def
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
def each_mail new = nil
|
|
302
|
+
lfrom = local_from
|
|
303
|
+
each_file new do |fn|
|
|
304
|
+
created = Time.at fn[ /\A(\d+)/, 1].to_i + fn[ /M(\d+)/, 1].to_i*0.000001
|
|
305
|
+
File.open fn, encoding: Encoding::ASCII_8BIT do |f|
|
|
306
|
+
from_host = fn[ /\.([.a-z0-9_+-]+)/, 1]
|
|
307
|
+
text = f.read
|
|
308
|
+
from = text[ /[a-z0-9.+-]+@#{Regexp.quote from_host}/]
|
|
309
|
+
yield text, from||lfrom, created
|
|
295
310
|
end
|
|
296
|
-
|
|
311
|
+
end
|
|
297
312
|
end
|
|
298
|
-
include Enumerable
|
|
299
313
|
|
|
300
314
|
private
|
|
301
315
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
c += 1
|
|
314
|
-
retry
|
|
316
|
+
@seq = 0
|
|
317
|
+
class <<self
|
|
318
|
+
def seq! ; @seq += 1 ; end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def mkfilename from, created
|
|
322
|
+
host = if from =~ /@/ then
|
|
323
|
+
$'
|
|
324
|
+
else
|
|
325
|
+
require "socket"
|
|
326
|
+
Socket.gethostname
|
|
315
327
|
end
|
|
328
|
+
created ||= Time.now
|
|
329
|
+
created = created.to_time
|
|
330
|
+
"#{created.to_i}M#{created.usec}P#$$Q#{self.class.seq!}.#{host}"
|
|
316
331
|
end
|
|
317
332
|
|
|
318
333
|
end
|