tmail 1.1.1
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.
- data/LICENSE +21 -0
- data/README +157 -0
- data/bat/changelog +19 -0
- data/bat/clobber/package +10 -0
- data/bat/compile +42 -0
- data/bat/config.yaml +8 -0
- data/bat/prepare +8 -0
- data/bat/publish +51 -0
- data/bat/rdoc +42 -0
- data/bat/release +12 -0
- data/bat/setup +1616 -0
- data/bat/stats +138 -0
- data/bat/tag +25 -0
- data/bat/test +25 -0
- data/ext/tmail/Makefile +25 -0
- data/ext/tmail/base64/MANIFEST +4 -0
- data/ext/tmail/base64/base64.c +264 -0
- data/ext/tmail/base64/depend +1 -0
- data/ext/tmail/base64/extconf.rb +38 -0
- data/ext/tmail/scanner_c/MANIFEST +4 -0
- data/ext/tmail/scanner_c/depend +1 -0
- data/ext/tmail/scanner_c/extconf.rb +38 -0
- data/ext/tmail/scanner_c/scanner_c.c +582 -0
- data/lib/tmail.rb +4 -0
- data/lib/tmail/Makefile +19 -0
- data/lib/tmail/address.rb +245 -0
- data/lib/tmail/attachments.rb +47 -0
- data/lib/tmail/base64.rb +75 -0
- data/lib/tmail/compat.rb +39 -0
- data/lib/tmail/config.rb +71 -0
- data/lib/tmail/core_extensions.rb +67 -0
- data/lib/tmail/encode.rb +524 -0
- data/lib/tmail/header.rb +931 -0
- data/lib/tmail/index.rb +8 -0
- data/lib/tmail/interface.rb +540 -0
- data/lib/tmail/loader.rb +1 -0
- data/lib/tmail/mail.rb +507 -0
- data/lib/tmail/mailbox.rb +435 -0
- data/lib/tmail/mbox.rb +1 -0
- data/lib/tmail/net.rb +282 -0
- data/lib/tmail/obsolete.rb +137 -0
- data/lib/tmail/parser.rb +1475 -0
- data/lib/tmail/parser.y +381 -0
- data/lib/tmail/port.rb +379 -0
- data/lib/tmail/quoting.rb +142 -0
- data/lib/tmail/require_arch.rb +56 -0
- data/lib/tmail/scanner.rb +44 -0
- data/lib/tmail/scanner_r.rb +263 -0
- data/lib/tmail/stringio.rb +279 -0
- data/lib/tmail/tmail.rb +1 -0
- data/lib/tmail/utils.rb +281 -0
- data/lib/tmail/version.rb +38 -0
- data/meta/icli.yaml +16 -0
- data/meta/tmail-1.1.1.roll +24 -0
- data/sample/data/multipart +23 -0
- data/sample/data/normal +29 -0
- data/sample/data/sendtest +5 -0
- data/sample/data/simple +14 -0
- data/sample/data/test +27 -0
- data/sample/extract-attachements.rb +33 -0
- data/sample/from-check.rb +26 -0
- data/sample/multipart.rb +26 -0
- data/sample/parse-bench.rb +68 -0
- data/sample/parse-test.rb +19 -0
- data/sample/sendmail.rb +94 -0
- data/test/extctrl.rb +6 -0
- data/test/fixtures/raw_base64_decoded_string +0 -0
- data/test/fixtures/raw_base64_email +83 -0
- data/test/fixtures/raw_base64_encoded_string +1 -0
- data/test/fixtures/raw_email +14 -0
- data/test/fixtures/raw_email10 +20 -0
- data/test/fixtures/raw_email11 +34 -0
- data/test/fixtures/raw_email12 +32 -0
- data/test/fixtures/raw_email13 +29 -0
- data/test/fixtures/raw_email2 +114 -0
- data/test/fixtures/raw_email3 +70 -0
- data/test/fixtures/raw_email4 +59 -0
- data/test/fixtures/raw_email5 +19 -0
- data/test/fixtures/raw_email6 +20 -0
- data/test/fixtures/raw_email7 +66 -0
- data/test/fixtures/raw_email8 +47 -0
- data/test/fixtures/raw_email9 +28 -0
- data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
- data/test/fixtures/raw_email_simple +11 -0
- data/test/fixtures/raw_email_with_illegal_boundary +58 -0
- data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
- data/test/fixtures/raw_email_with_nested_attachment +100 -0
- data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
- data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
- data/test/kcode.rb +14 -0
- data/test/test_address.rb +1128 -0
- data/test/test_attachments.rb +35 -0
- data/test/test_base64.rb +63 -0
- data/test/test_encode.rb +77 -0
- data/test/test_header.rb +885 -0
- data/test/test_helper.rb +2 -0
- data/test/test_mail.rb +623 -0
- data/test/test_mbox.rb +126 -0
- data/test/test_port.rb +430 -0
- data/test/test_scanner.rb +209 -0
- data/test/test_utils.rb +37 -0
- metadata +205 -0
@@ -0,0 +1,279 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= String handling class
|
4
|
+
|
5
|
+
=end
|
6
|
+
#--
|
7
|
+
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
#
|
28
|
+
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
29
|
+
# with permission of Minero Aoki.
|
30
|
+
#++
|
31
|
+
|
32
|
+
class StringInput#:nodoc:
|
33
|
+
|
34
|
+
include Enumerable
|
35
|
+
|
36
|
+
class << self
|
37
|
+
|
38
|
+
def new( str )
|
39
|
+
if block_given?
|
40
|
+
begin
|
41
|
+
f = super
|
42
|
+
yield f
|
43
|
+
ensure
|
44
|
+
f.close if f
|
45
|
+
end
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
alias open new
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize( str )
|
56
|
+
@src = str
|
57
|
+
@pos = 0
|
58
|
+
@closed = false
|
59
|
+
@lineno = 0
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :lineno
|
63
|
+
|
64
|
+
def string
|
65
|
+
@src
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
def close
|
73
|
+
stream_check!
|
74
|
+
@pos = nil
|
75
|
+
@closed = true
|
76
|
+
end
|
77
|
+
|
78
|
+
def closed?
|
79
|
+
@closed
|
80
|
+
end
|
81
|
+
|
82
|
+
def pos
|
83
|
+
stream_check!
|
84
|
+
[@pos, @src.size].min
|
85
|
+
end
|
86
|
+
|
87
|
+
alias tell pos
|
88
|
+
|
89
|
+
def seek( offset, whence = IO::SEEK_SET )
|
90
|
+
stream_check!
|
91
|
+
case whence
|
92
|
+
when IO::SEEK_SET
|
93
|
+
@pos = offset
|
94
|
+
when IO::SEEK_CUR
|
95
|
+
@pos += offset
|
96
|
+
when IO::SEEK_END
|
97
|
+
@pos = @src.size - offset
|
98
|
+
else
|
99
|
+
raise ArgumentError, "unknown seek flag: #{whence}"
|
100
|
+
end
|
101
|
+
@pos = 0 if @pos < 0
|
102
|
+
@pos = [@pos, @src.size + 1].min
|
103
|
+
offset
|
104
|
+
end
|
105
|
+
|
106
|
+
def rewind
|
107
|
+
stream_check!
|
108
|
+
@pos = 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def eof?
|
112
|
+
stream_check!
|
113
|
+
@pos > @src.size
|
114
|
+
end
|
115
|
+
|
116
|
+
def each( &block )
|
117
|
+
stream_check!
|
118
|
+
begin
|
119
|
+
@src.each(&block)
|
120
|
+
ensure
|
121
|
+
@pos = 0
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def gets
|
126
|
+
stream_check!
|
127
|
+
if idx = @src.index(?\n, @pos)
|
128
|
+
idx += 1 # "\n".size
|
129
|
+
line = @src[ @pos ... idx ]
|
130
|
+
@pos = idx
|
131
|
+
@pos += 1 if @pos == @src.size
|
132
|
+
else
|
133
|
+
line = @src[ @pos .. -1 ]
|
134
|
+
@pos = @src.size + 1
|
135
|
+
end
|
136
|
+
@lineno += 1
|
137
|
+
|
138
|
+
line
|
139
|
+
end
|
140
|
+
|
141
|
+
def getc
|
142
|
+
stream_check!
|
143
|
+
ch = @src[@pos]
|
144
|
+
@pos += 1
|
145
|
+
@pos += 1 if @pos == @src.size
|
146
|
+
ch
|
147
|
+
end
|
148
|
+
|
149
|
+
def read( len = nil )
|
150
|
+
stream_check!
|
151
|
+
return read_all unless len
|
152
|
+
str = @src[@pos, len]
|
153
|
+
@pos += len
|
154
|
+
@pos += 1 if @pos == @src.size
|
155
|
+
str
|
156
|
+
end
|
157
|
+
|
158
|
+
alias sysread read
|
159
|
+
|
160
|
+
def read_all
|
161
|
+
stream_check!
|
162
|
+
return nil if eof?
|
163
|
+
rest = @src[@pos ... @src.size]
|
164
|
+
@pos = @src.size + 1
|
165
|
+
rest
|
166
|
+
end
|
167
|
+
|
168
|
+
def stream_check!
|
169
|
+
@closed and raise IOError, 'closed stream'
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
class StringOutput#:nodoc:
|
176
|
+
|
177
|
+
class << self
|
178
|
+
|
179
|
+
def new( str = '' )
|
180
|
+
if block_given?
|
181
|
+
begin
|
182
|
+
f = super
|
183
|
+
yield f
|
184
|
+
ensure
|
185
|
+
f.close if f
|
186
|
+
end
|
187
|
+
else
|
188
|
+
super
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
alias open new
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
def initialize( str = '' )
|
197
|
+
@dest = str
|
198
|
+
@closed = false
|
199
|
+
end
|
200
|
+
|
201
|
+
def close
|
202
|
+
@closed = true
|
203
|
+
end
|
204
|
+
|
205
|
+
def closed?
|
206
|
+
@closed
|
207
|
+
end
|
208
|
+
|
209
|
+
def string
|
210
|
+
@dest
|
211
|
+
end
|
212
|
+
|
213
|
+
alias value string
|
214
|
+
alias to_str string
|
215
|
+
|
216
|
+
def size
|
217
|
+
@dest.size
|
218
|
+
end
|
219
|
+
|
220
|
+
alias pos size
|
221
|
+
|
222
|
+
def inspect
|
223
|
+
"#<#{self.class}:#{@dest ? 'open' : 'closed'},#{object_id}>"
|
224
|
+
end
|
225
|
+
|
226
|
+
def print( *args )
|
227
|
+
stream_check!
|
228
|
+
raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
|
229
|
+
args.each do |s|
|
230
|
+
raise ArgumentError, 'nil not allowed' if s.nil?
|
231
|
+
@dest << s.to_s
|
232
|
+
end
|
233
|
+
nil
|
234
|
+
end
|
235
|
+
|
236
|
+
def puts( *args )
|
237
|
+
stream_check!
|
238
|
+
args.each do |str|
|
239
|
+
@dest << (s = str.to_s)
|
240
|
+
@dest << "\n" unless s[-1] == ?\n
|
241
|
+
end
|
242
|
+
@dest << "\n" if args.empty?
|
243
|
+
nil
|
244
|
+
end
|
245
|
+
|
246
|
+
def putc( ch )
|
247
|
+
stream_check!
|
248
|
+
@dest << ch.chr
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
|
252
|
+
def printf( *args )
|
253
|
+
stream_check!
|
254
|
+
@dest << sprintf(*args)
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
|
258
|
+
def write( str )
|
259
|
+
stream_check!
|
260
|
+
s = str.to_s
|
261
|
+
@dest << s
|
262
|
+
s.size
|
263
|
+
end
|
264
|
+
|
265
|
+
alias syswrite write
|
266
|
+
|
267
|
+
def <<( str )
|
268
|
+
stream_check!
|
269
|
+
@dest << str.to_s
|
270
|
+
self
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
def stream_check!
|
276
|
+
@closed and raise IOError, 'closed stream'
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
data/lib/tmail/tmail.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'tmail'
|
data/lib/tmail/utils.rb
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= General Purpose TMail Utilities
|
4
|
+
|
5
|
+
=end
|
6
|
+
#--
|
7
|
+
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
#
|
28
|
+
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
29
|
+
# with permission of Minero Aoki.
|
30
|
+
#++
|
31
|
+
|
32
|
+
module TMail
|
33
|
+
|
34
|
+
class SyntaxError < StandardError; end
|
35
|
+
|
36
|
+
|
37
|
+
def TMail.new_boundary
|
38
|
+
'mimepart_' + random_tag
|
39
|
+
end
|
40
|
+
|
41
|
+
def TMail.new_message_id( fqdn = nil )
|
42
|
+
fqdn ||= ::Socket.gethostname
|
43
|
+
"<#{random_tag()}@#{fqdn}.tmail>"
|
44
|
+
end
|
45
|
+
|
46
|
+
def TMail.random_tag
|
47
|
+
@uniq += 1
|
48
|
+
t = Time.now
|
49
|
+
sprintf('%x%x_%x%x%d%x',
|
50
|
+
t.to_i, t.tv_usec,
|
51
|
+
$$, Thread.current.object_id, @uniq, rand(255))
|
52
|
+
end
|
53
|
+
private_class_method :random_tag
|
54
|
+
|
55
|
+
@uniq = 0
|
56
|
+
|
57
|
+
module TextUtils
|
58
|
+
# Defines characters per RFC that are OK for TOKENs, ATOMs, PHRASEs and CONTROL characters.
|
59
|
+
|
60
|
+
aspecial = '()<>[]:;.\\,"'
|
61
|
+
tspecial = '()<>[];:\\,"/?='
|
62
|
+
lwsp = " \t\r\n"
|
63
|
+
control = '\x00-\x1f\x7f-\xff'
|
64
|
+
|
65
|
+
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
|
66
|
+
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
|
67
|
+
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
|
68
|
+
CONTROL_CHAR = /[#{control}]/n
|
69
|
+
|
70
|
+
def atom_safe?( str )
|
71
|
+
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
72
|
+
not ATOM_UNSAFE === str
|
73
|
+
end
|
74
|
+
|
75
|
+
def quote_atom( str )
|
76
|
+
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
77
|
+
# in double quotes, otherwise returns the string unmodified
|
78
|
+
(ATOM_UNSAFE === str) ? dquote(str) : str
|
79
|
+
end
|
80
|
+
|
81
|
+
def quote_phrase( str )
|
82
|
+
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
83
|
+
# in double quotes, otherwise returns the string unmodified
|
84
|
+
(PHRASE_UNSAFE === str) ? dquote(str) : str
|
85
|
+
end
|
86
|
+
|
87
|
+
def token_safe?( str )
|
88
|
+
# Returns true if the string supplied is free from characters not allowed as a TOKEN
|
89
|
+
not TOKEN_UNSAFE === str
|
90
|
+
end
|
91
|
+
|
92
|
+
def quote_token( str )
|
93
|
+
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
94
|
+
# in double quotes, otherwise returns the string unmodified
|
95
|
+
(TOKEN_UNSAFE === str) ? dquote(str) : str
|
96
|
+
end
|
97
|
+
|
98
|
+
def dquote( str )
|
99
|
+
# Wraps supplied string in double quotes unless it is already wrapped
|
100
|
+
# Returns double quoted string
|
101
|
+
unless str =~ /^".*?"$/
|
102
|
+
'"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
|
103
|
+
else
|
104
|
+
str
|
105
|
+
end
|
106
|
+
end
|
107
|
+
private :dquote
|
108
|
+
|
109
|
+
def unquote( str )
|
110
|
+
# Unwraps supplied string from inside double quotes
|
111
|
+
# Returns unquoted string
|
112
|
+
str =~ /^"(.*?)"$/ ? $1 : str
|
113
|
+
end
|
114
|
+
|
115
|
+
def join_domain( arr )
|
116
|
+
arr.map {|i|
|
117
|
+
if /\A\[.*\]\z/ === i
|
118
|
+
i
|
119
|
+
else
|
120
|
+
quote_atom(i)
|
121
|
+
end
|
122
|
+
}.join('.')
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
ZONESTR_TABLE = {
|
127
|
+
'jst' => 9 * 60,
|
128
|
+
'eet' => 2 * 60,
|
129
|
+
'bst' => 1 * 60,
|
130
|
+
'met' => 1 * 60,
|
131
|
+
'gmt' => 0,
|
132
|
+
'utc' => 0,
|
133
|
+
'ut' => 0,
|
134
|
+
'nst' => -(3 * 60 + 30),
|
135
|
+
'ast' => -4 * 60,
|
136
|
+
'edt' => -4 * 60,
|
137
|
+
'est' => -5 * 60,
|
138
|
+
'cdt' => -5 * 60,
|
139
|
+
'cst' => -6 * 60,
|
140
|
+
'mdt' => -6 * 60,
|
141
|
+
'mst' => -7 * 60,
|
142
|
+
'pdt' => -7 * 60,
|
143
|
+
'pst' => -8 * 60,
|
144
|
+
'a' => -1 * 60,
|
145
|
+
'b' => -2 * 60,
|
146
|
+
'c' => -3 * 60,
|
147
|
+
'd' => -4 * 60,
|
148
|
+
'e' => -5 * 60,
|
149
|
+
'f' => -6 * 60,
|
150
|
+
'g' => -7 * 60,
|
151
|
+
'h' => -8 * 60,
|
152
|
+
'i' => -9 * 60,
|
153
|
+
# j not use
|
154
|
+
'k' => -10 * 60,
|
155
|
+
'l' => -11 * 60,
|
156
|
+
'm' => -12 * 60,
|
157
|
+
'n' => 1 * 60,
|
158
|
+
'o' => 2 * 60,
|
159
|
+
'p' => 3 * 60,
|
160
|
+
'q' => 4 * 60,
|
161
|
+
'r' => 5 * 60,
|
162
|
+
's' => 6 * 60,
|
163
|
+
't' => 7 * 60,
|
164
|
+
'u' => 8 * 60,
|
165
|
+
'v' => 9 * 60,
|
166
|
+
'w' => 10 * 60,
|
167
|
+
'x' => 11 * 60,
|
168
|
+
'y' => 12 * 60,
|
169
|
+
'z' => 0 * 60
|
170
|
+
}
|
171
|
+
|
172
|
+
def timezone_string_to_unixtime( str )
|
173
|
+
# Takes a time zone string from an EMail and converts it to Unix Time (seconds)
|
174
|
+
if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
|
175
|
+
sec = (m[2].to_i * 60 + m[3].to_i) * 60
|
176
|
+
m[1] == '-' ? -sec : sec
|
177
|
+
else
|
178
|
+
min = ZONESTR_TABLE[str.downcase] or
|
179
|
+
raise SyntaxError, "wrong timezone format '#{str}'"
|
180
|
+
min * 60
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG )
|
186
|
+
MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun
|
187
|
+
Jul Aug Sep Oct Nov Dec TMailBUG )
|
188
|
+
|
189
|
+
def time2str( tm )
|
190
|
+
# [ruby-list:7928]
|
191
|
+
gmt = Time.at(tm.to_i)
|
192
|
+
gmt.gmtime
|
193
|
+
offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i
|
194
|
+
|
195
|
+
# DO NOT USE strftime: setlocale() breaks it
|
196
|
+
sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d',
|
197
|
+
WDAY[tm.wday], tm.mday, MONTH[tm.month],
|
198
|
+
tm.year, tm.hour, tm.min, tm.sec,
|
199
|
+
*(offset / 60).divmod(60)
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
|
204
|
+
|
205
|
+
def message_id?( str )
|
206
|
+
MESSAGE_ID === str
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i
|
211
|
+
|
212
|
+
def mime_encoded?( str )
|
213
|
+
MIME_ENCODED === str
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
def decode_params( hash )
|
218
|
+
new = Hash.new
|
219
|
+
encoded = nil
|
220
|
+
hash.each do |key, value|
|
221
|
+
if m = /\*(?:(\d+)\*)?\z/.match(key)
|
222
|
+
((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value
|
223
|
+
else
|
224
|
+
new[key] = to_kcode(value)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
if encoded
|
228
|
+
encoded.each do |key, strings|
|
229
|
+
new[key] = decode_RFC2231(strings.join(''))
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
new
|
234
|
+
end
|
235
|
+
|
236
|
+
NKF_FLAGS = {
|
237
|
+
'EUC' => '-e -m',
|
238
|
+
'SJIS' => '-s -m'
|
239
|
+
}
|
240
|
+
|
241
|
+
def to_kcode( str )
|
242
|
+
flag = NKF_FLAGS[$KCODE] or return str
|
243
|
+
NKF.nkf(flag, str)
|
244
|
+
end
|
245
|
+
|
246
|
+
RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
|
247
|
+
|
248
|
+
def decode_RFC2231( str )
|
249
|
+
m = RFC2231_ENCODED.match(str) or return str
|
250
|
+
begin
|
251
|
+
NKF.nkf(NKF_FLAGS[$KCODE],
|
252
|
+
m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
|
253
|
+
rescue
|
254
|
+
m.post_match.gsub(/%[\da-f]{2}/in, "")
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def quote_boundary
|
259
|
+
# Make sure the Content-Type boundary= parameter is quoted if it contains illegal characters
|
260
|
+
# (to ensure any special characters in the boundary text are escaped from the parser
|
261
|
+
# (such as = in MS Outlook's boundary text))
|
262
|
+
if @body =~ /^(.*)boundary=(.*)$/m
|
263
|
+
preamble = $1
|
264
|
+
remainder = $2
|
265
|
+
if remainder =~ /;/
|
266
|
+
remainder =~ /^(.*)(;.*)$/m
|
267
|
+
boundary_text = $1
|
268
|
+
post = $2.chomp
|
269
|
+
else
|
270
|
+
boundary_text = remainder.chomp
|
271
|
+
end
|
272
|
+
if boundary_text =~ /[\/\?\=]/
|
273
|
+
boundary_text = "\"#{boundary_text}\"" unless boundary_text =~ /^".*?"$/
|
274
|
+
@body = "#{preamble}boundary=#{boundary_text}#{post}"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|