rims-rfc822 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -1
- data/lib/rims/rfc822.rb +221 -97
- data/lib/rims/rfc822/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51ec6ae41467b2b68bd8d1bb23916ad42f27fb6b688f8769f64e61db5e8afd98
|
4
|
+
data.tar.gz: f48b88893f67da1da2824336bfe274b033131527420de1926bea05faa7036618
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77e6b92ea9e32a7a94972d493103e03face8eaf6872400a324242431b3c8ecd5211bf0bd4c77959c47e634d820bd26089f250cd49a5e54ccfdf496cbbdaf7e08
|
7
|
+
data.tar.gz: a94818fedb229ad9df8683d027aa9c0cacf57a9b5296ae279794d1132e310b0a93a7158bc2c25f72279117ce29c90090b945a2e79f552fab07b1b3a1467dedb2
|
data/README.md
CHANGED
@@ -41,10 +41,16 @@ p msg.body.raw_source
|
|
41
41
|
# type attributes
|
42
42
|
p msg.media_main_type
|
43
43
|
p msg.media_sub_type
|
44
|
+
p msg.media_subtype # alias of media_sub_type
|
44
45
|
p msg.content_type
|
45
|
-
p msg.
|
46
|
+
p msg.content_type_parameter(name)
|
47
|
+
p msg.content_type_parameter_list
|
46
48
|
p msg.charset
|
47
49
|
p msg.boundary
|
50
|
+
p msg.content_disposition
|
51
|
+
p msg.content_disposition_parameter(name)
|
52
|
+
p msg.content_disposition_parameter_list
|
53
|
+
p msg.content_language
|
48
54
|
|
49
55
|
# headear attributes
|
50
56
|
p msg.date
|
data/lib/rims/rfc822.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
1
|
+
# -*- coding: utf-8; frozen_string_literal: true -*-
|
2
2
|
|
3
3
|
require 'rims/rfc822/version'
|
4
4
|
require 'time'
|
@@ -7,8 +7,14 @@ module RIMS
|
|
7
7
|
module RFC822
|
8
8
|
def split_message(msg_txt)
|
9
9
|
header_txt, body_txt = msg_txt.lstrip.split(/\r?\n\r?\n/, 2)
|
10
|
-
|
11
|
-
|
10
|
+
if ($&) then
|
11
|
+
header_txt << $& if $&
|
12
|
+
else
|
13
|
+
body_txt = header_txt
|
14
|
+
header_txt = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
[ header_txt.freeze, body_txt.freeze ].freeze
|
12
18
|
end
|
13
19
|
module_function :split_message
|
14
20
|
|
@@ -24,7 +30,7 @@ module RIMS
|
|
24
30
|
)
|
25
31
|
}x)
|
26
32
|
|
27
|
-
for
|
33
|
+
for name, value in field_pair_list
|
28
34
|
value.strip!
|
29
35
|
name.freeze
|
30
36
|
value.freeze
|
@@ -34,59 +40,12 @@ module RIMS
|
|
34
40
|
end
|
35
41
|
module_function :parse_header
|
36
42
|
|
37
|
-
def parse_content_type(content_type_txt)
|
38
|
-
src_txt = content_type_txt.dup
|
39
|
-
if (src_txt.sub!(%r"\A \s* (?<main_type>\S+?) \s* / \s* (?<sub_type>\S+?) \s* (?:;|\Z)"x, '')) then
|
40
|
-
main_type = $~[:main_type]
|
41
|
-
sub_type = $~[:sub_type]
|
42
|
-
|
43
|
-
params = {}
|
44
|
-
src_txt.scan(%r'(?<name>\S+?) \s* = \s* (?: (?<quoted_string>".*?") | (?<token>\S+?) ) \s* (?:;|\Z)'x) do
|
45
|
-
name = $~[:name]
|
46
|
-
if ($~[:quoted_string]) then
|
47
|
-
quoted_value = $~[:quoted_string]
|
48
|
-
value = unquote_phrase(quoted_value)
|
49
|
-
else
|
50
|
-
value = $~[:token]
|
51
|
-
end
|
52
|
-
params[name.downcase] = [ name.freeze, value.freeze ].freeze
|
53
|
-
end
|
54
|
-
|
55
|
-
[ main_type.freeze, sub_type.freeze, params.freeze ].freeze
|
56
|
-
else
|
57
|
-
[ 'application'.freeze, 'octet-stream'.freeze, {}.freeze ].freeze
|
58
|
-
end
|
59
|
-
end
|
60
|
-
module_function :parse_content_type
|
61
|
-
|
62
|
-
def parse_multipart_body(boundary, body_txt)
|
63
|
-
delim = '--' + boundary
|
64
|
-
term = delim + '--'
|
65
|
-
body_txt2, _body_epilogue_txt = body_txt.split(term, 2)
|
66
|
-
if (body_txt2) then
|
67
|
-
_body_preamble_txt, body_parts_txt = body_txt2.split(delim, 2)
|
68
|
-
if (body_parts_txt) then
|
69
|
-
part_list = body_parts_txt.split(delim, -1)
|
70
|
-
for part_txt in part_list
|
71
|
-
part_txt.lstrip!
|
72
|
-
part_txt.chomp!("\n")
|
73
|
-
part_txt.chomp!("\r")
|
74
|
-
part_txt.freeze
|
75
|
-
end
|
76
|
-
return part_list.freeze
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
[].freeze
|
81
|
-
end
|
82
|
-
module_function :parse_multipart_body
|
83
|
-
|
84
43
|
def unquote_phrase(phrase_txt)
|
85
44
|
state = :raw
|
86
45
|
src_txt = phrase_txt.dup
|
87
46
|
dst_txt = ''.encode(phrase_txt.encoding)
|
88
47
|
|
89
|
-
while (src_txt.sub!(/\A(
|
48
|
+
while (src_txt.sub!(/\A (?: " | \( | \) | \\ | [^"\(\)\\]+ )/x, ''))
|
90
49
|
match_txt = $&
|
91
50
|
case (state)
|
92
51
|
when :raw
|
@@ -127,6 +86,111 @@ module RIMS
|
|
127
86
|
end
|
128
87
|
module_function :unquote_phrase
|
129
88
|
|
89
|
+
def parse_parameters(parameters_txt)
|
90
|
+
params = {}
|
91
|
+
parameters_txt.scan(%r'(?<name>\S+?) \s* = \s* (?: (?<quoted_string>".*?") | (?<token>\S+?) ) \s* (?:;|\Z)'x) do
|
92
|
+
name = $~[:name]
|
93
|
+
if ($~[:quoted_string]) then
|
94
|
+
quoted_value = $~[:quoted_string]
|
95
|
+
value = unquote_phrase(quoted_value)
|
96
|
+
else
|
97
|
+
value = $~[:token]
|
98
|
+
end
|
99
|
+
params[name.downcase.freeze] = [ name.freeze, value.freeze ].freeze
|
100
|
+
end
|
101
|
+
|
102
|
+
params.freeze
|
103
|
+
end
|
104
|
+
module_function :parse_parameters
|
105
|
+
|
106
|
+
def split_parameters(type_params_txt)
|
107
|
+
type, params_txt = type_params_txt.split(';', 2)
|
108
|
+
if (type) then
|
109
|
+
type.strip!
|
110
|
+
type.freeze
|
111
|
+
if (params_txt) then
|
112
|
+
params = parse_parameters(params_txt)
|
113
|
+
else
|
114
|
+
params = {}.freeze
|
115
|
+
end
|
116
|
+
[ type, params ].freeze
|
117
|
+
else
|
118
|
+
[ nil, {}.freeze ].freeze
|
119
|
+
end
|
120
|
+
end
|
121
|
+
module_function :split_parameters
|
122
|
+
|
123
|
+
def parse_content_type(type_txt)
|
124
|
+
media_type_txt, params = split_parameters(type_txt)
|
125
|
+
if (media_type_txt) then
|
126
|
+
main_type, sub_type = media_type_txt.split('/', 2)
|
127
|
+
if (main_type) then
|
128
|
+
main_type.strip!
|
129
|
+
main_type.freeze
|
130
|
+
if (sub_type) then
|
131
|
+
sub_type.strip!
|
132
|
+
sub_type.freeze
|
133
|
+
if (! main_type.empty? && ! sub_type.empty?) then
|
134
|
+
return [ main_type, sub_type, params ].freeze
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
[ 'application'.dup.force_encoding(type_txt.encoding).freeze,
|
141
|
+
'octet-stream'.dup.force_encoding(type_txt.encoding).freeze,
|
142
|
+
params
|
143
|
+
].freeze
|
144
|
+
end
|
145
|
+
module_function :parse_content_type
|
146
|
+
|
147
|
+
def parse_content_disposition(disposition_txt)
|
148
|
+
split_parameters(disposition_txt)
|
149
|
+
end
|
150
|
+
module_function :parse_content_disposition
|
151
|
+
|
152
|
+
def parse_content_language(language_tags_txt)
|
153
|
+
tag_list = language_tags_txt.split(',')
|
154
|
+
for tag in tag_list
|
155
|
+
tag.strip!
|
156
|
+
tag.freeze
|
157
|
+
end
|
158
|
+
tag_list.reject!(&:empty?)
|
159
|
+
|
160
|
+
tag_list.freeze
|
161
|
+
end
|
162
|
+
module_function :parse_content_language
|
163
|
+
|
164
|
+
def parse_multipart_body(boundary, body_txt)
|
165
|
+
delim = '--' + boundary
|
166
|
+
term = delim + '--'
|
167
|
+
body_txt2, _body_epilogue_txt = body_txt.split(term, 2)
|
168
|
+
if (body_txt2) then
|
169
|
+
_body_preamble_txt, body_parts_txt = body_txt2.split(delim, 2)
|
170
|
+
if (body_parts_txt) then
|
171
|
+
part_list = body_parts_txt.split(delim, -1)
|
172
|
+
for part_txt in part_list
|
173
|
+
part_txt.lstrip!
|
174
|
+
part_txt.chomp!("\n")
|
175
|
+
part_txt.chomp!("\r")
|
176
|
+
part_txt.freeze
|
177
|
+
end
|
178
|
+
return part_list.freeze
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
[].freeze
|
183
|
+
end
|
184
|
+
module_function :parse_multipart_body
|
185
|
+
|
186
|
+
Address = Struct.new(:display_name, :route, :local_part, :domain)
|
187
|
+
class Address
|
188
|
+
# compatible for Net::MAP::Address
|
189
|
+
alias name display_name
|
190
|
+
alias mailbox local_part
|
191
|
+
alias host domain
|
192
|
+
end
|
193
|
+
|
130
194
|
def parse_mail_address_list(address_list_txt)
|
131
195
|
addr_list = []
|
132
196
|
src_txt = address_list_txt.dup
|
@@ -142,9 +206,9 @@ module RIMS
|
|
142
206
|
then
|
143
207
|
display_name = $~[:display_name]
|
144
208
|
group_list = $~[:group_list]
|
145
|
-
addr_list <<
|
209
|
+
addr_list << Address.new( nil, nil, unquote_phrase(display_name), nil).freeze
|
146
210
|
addr_list.concat(parse_mail_address_list(group_list))
|
147
|
-
addr_list <<
|
211
|
+
addr_list << Address.new(nil, nil, nil, nil).freeze
|
148
212
|
elsif (src_txt.sub!(%r{
|
149
213
|
\A
|
150
214
|
\s*
|
@@ -153,7 +217,7 @@ module RIMS
|
|
153
217
|
,?
|
154
218
|
}x, ''))
|
155
219
|
then
|
156
|
-
addr_list <<
|
220
|
+
addr_list << Address.new(nil, nil, $~[:local_part].freeze, $~[:domain].freeze).freeze
|
157
221
|
elsif (src_txt.sub!(%r{
|
158
222
|
\A
|
159
223
|
\s*
|
@@ -178,7 +242,7 @@ module RIMS
|
|
178
242
|
route = $~[:route]
|
179
243
|
local_part = $~[:local_part]
|
180
244
|
domain = $~[:domain]
|
181
|
-
addr_list <<
|
245
|
+
addr_list << Address.new(unquote_phrase(display_name), route.freeze, local_part.freeze, domain.freeze).freeze
|
182
246
|
else
|
183
247
|
break
|
184
248
|
end
|
@@ -192,7 +256,7 @@ module RIMS
|
|
192
256
|
include Enumerable
|
193
257
|
|
194
258
|
def initialize(header_txt)
|
195
|
-
@raw_source = header_txt
|
259
|
+
@raw_source = header_txt
|
196
260
|
@field_list = nil
|
197
261
|
@field_map = nil
|
198
262
|
end
|
@@ -255,7 +319,7 @@ module RIMS
|
|
255
319
|
|
256
320
|
class Body
|
257
321
|
def initialize(body_txt)
|
258
|
-
@raw_source = body_txt
|
322
|
+
@raw_source = body_txt
|
259
323
|
end
|
260
324
|
|
261
325
|
attr_reader :raw_source
|
@@ -263,13 +327,13 @@ module RIMS
|
|
263
327
|
|
264
328
|
class Message
|
265
329
|
def initialize(msg_txt)
|
266
|
-
@raw_source = msg_txt.freeze
|
330
|
+
@raw_source = msg_txt.dup.freeze
|
267
331
|
@header = nil
|
268
332
|
@body = nil
|
269
333
|
@content_type = nil
|
270
|
-
@
|
334
|
+
@content_disposition = nil
|
335
|
+
@content_language = nil
|
271
336
|
@parts = nil
|
272
|
-
@is_message = nil
|
273
337
|
@message = nil
|
274
338
|
@date = nil
|
275
339
|
@from = nil
|
@@ -304,61 +368,122 @@ module RIMS
|
|
304
368
|
|
305
369
|
def setup_content_type
|
306
370
|
if (@content_type.nil?) then
|
307
|
-
@content_type = RFC822.parse_content_type(header['
|
371
|
+
@content_type = RFC822.parse_content_type(header['Content-Type'] || '')
|
308
372
|
self
|
309
373
|
end
|
310
374
|
end
|
311
375
|
private :setup_content_type
|
312
376
|
|
313
|
-
def to_upper(text_or_nil)
|
314
|
-
text_or_nil.upcase if text_or_nil
|
315
|
-
end
|
316
|
-
private :to_upper
|
317
|
-
|
318
377
|
def media_main_type
|
319
378
|
setup_content_type
|
320
379
|
@content_type[0]
|
321
380
|
end
|
322
381
|
|
323
|
-
def media_main_type_upcase
|
324
|
-
setup_content_type
|
325
|
-
to_upper(@content_type[0])
|
326
|
-
end
|
327
|
-
|
328
382
|
def media_sub_type
|
329
383
|
setup_content_type
|
330
384
|
@content_type[1]
|
331
385
|
end
|
332
386
|
|
333
|
-
|
334
|
-
setup_content_type
|
335
|
-
to_upper(@content_type[1])
|
336
|
-
end
|
387
|
+
alias media_subtype media_sub_type
|
337
388
|
|
338
389
|
def content_type
|
339
390
|
"#{media_main_type}/#{media_sub_type}"
|
340
391
|
end
|
341
392
|
|
342
|
-
def
|
343
|
-
|
393
|
+
def media_main_type_upcase
|
394
|
+
# not return `nil'
|
395
|
+
media_main_type.upcase
|
344
396
|
end
|
345
397
|
|
346
|
-
def
|
347
|
-
|
348
|
-
|
398
|
+
def media_sub_type_upcase
|
399
|
+
# not return `nil'
|
400
|
+
media_sub_type.upcase
|
349
401
|
end
|
350
402
|
|
351
|
-
|
403
|
+
alias media_subtype_upcase media_sub_type_upcase
|
404
|
+
|
405
|
+
def content_type_upcase
|
406
|
+
content_type.upcase
|
407
|
+
end
|
408
|
+
|
409
|
+
def content_type_parameter(name)
|
352
410
|
setup_content_type
|
353
|
-
if (name_value_pair = @content_type[2][
|
411
|
+
if (name_value_pair = @content_type[2][name.downcase]) then
|
354
412
|
name_value_pair[1]
|
355
413
|
end
|
356
414
|
end
|
357
415
|
|
416
|
+
def content_type_parameter_list
|
417
|
+
setup_content_type
|
418
|
+
@content_type[2].values
|
419
|
+
end
|
420
|
+
|
421
|
+
alias content_type_parameters content_type_parameter_list
|
422
|
+
|
423
|
+
def charset
|
424
|
+
content_type_parameter('charset')
|
425
|
+
end
|
426
|
+
|
358
427
|
def boundary
|
428
|
+
content_type_parameter('boundary')
|
429
|
+
end
|
430
|
+
|
431
|
+
def setup_content_disposition
|
432
|
+
if (header.key? 'Content-Disposition') then
|
433
|
+
if (@content_disposition.nil?) then
|
434
|
+
@content_disposition = RFC822.parse_content_disposition(header['Content-Disposition'])
|
435
|
+
self
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
private :setup_content_type
|
440
|
+
|
441
|
+
def content_disposition
|
442
|
+
setup_content_disposition
|
443
|
+
@content_disposition && @content_disposition[0]
|
444
|
+
end
|
445
|
+
|
446
|
+
def content_disposition_upcase
|
447
|
+
if (type = content_disposition) then
|
448
|
+
type.upcase
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def content_disposition_parameter(name)
|
453
|
+
setup_content_disposition
|
454
|
+
if (@content_disposition) then
|
455
|
+
if (name_value_pair = @content_disposition[1][name.downcase]) then
|
456
|
+
name_value_pair[1]
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
def content_disposition_parameter_list
|
359
462
|
setup_content_type
|
360
|
-
|
361
|
-
|
463
|
+
@content_disposition && @content_disposition[1].values
|
464
|
+
end
|
465
|
+
|
466
|
+
alias content_disposition_parameters content_disposition_parameter_list
|
467
|
+
|
468
|
+
def setup_content_language
|
469
|
+
if (header.key? 'Content-Language') then
|
470
|
+
if (@content_language.nil?) then
|
471
|
+
@content_language = header.field_value_list('Content-Language').map{|tags_txt| RFC822.parse_content_language(tags_txt) }.inject(:+)
|
472
|
+
@content_language.freeze
|
473
|
+
self
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
private :setup_content_language
|
478
|
+
|
479
|
+
def content_language
|
480
|
+
setup_content_language
|
481
|
+
@content_language
|
482
|
+
end
|
483
|
+
|
484
|
+
def content_language_upcase
|
485
|
+
if (tag_list = content_language) then
|
486
|
+
tag_list.map{|tag| tag.upcase }
|
362
487
|
end
|
363
488
|
end
|
364
489
|
|
@@ -367,10 +492,7 @@ module RIMS
|
|
367
492
|
end
|
368
493
|
|
369
494
|
def multipart?
|
370
|
-
|
371
|
-
@is_multipart = (media_main_type_upcase == 'MULTIPART')
|
372
|
-
end
|
373
|
-
@is_multipart
|
495
|
+
media_main_type_upcase == 'MULTIPART'
|
374
496
|
end
|
375
497
|
|
376
498
|
def parts
|
@@ -378,20 +500,19 @@ module RIMS
|
|
378
500
|
if (@parts.nil?) then
|
379
501
|
if (boundary = self.boundary) then
|
380
502
|
part_list = RFC822.parse_multipart_body(boundary, body.raw_source)
|
381
|
-
@parts = part_list.map{|msg_txt| Message.new(msg_txt) }
|
503
|
+
@parts = part_list.map{|msg_txt| Message.new(msg_txt) }
|
382
504
|
else
|
383
|
-
@parts = []
|
505
|
+
@parts = []
|
384
506
|
end
|
507
|
+
@parts.freeze
|
385
508
|
end
|
509
|
+
|
386
510
|
@parts
|
387
511
|
end
|
388
512
|
end
|
389
513
|
|
390
514
|
def message?
|
391
|
-
|
392
|
-
@is_message = (media_main_type_upcase == 'MESSAGE')
|
393
|
-
end
|
394
|
-
@is_message
|
515
|
+
media_main_type_upcase == 'MESSAGE'
|
395
516
|
end
|
396
517
|
|
397
518
|
def message
|
@@ -399,6 +520,7 @@ module RIMS
|
|
399
520
|
if (@message.nil?) then
|
400
521
|
@message = Message.new(body.raw_source)
|
401
522
|
end
|
523
|
+
|
402
524
|
@message
|
403
525
|
end
|
404
526
|
end
|
@@ -411,9 +533,10 @@ module RIMS
|
|
411
533
|
rescue ArgumentError
|
412
534
|
@date = Time.at(0)
|
413
535
|
end
|
536
|
+
@date.freeze
|
414
537
|
end
|
415
538
|
|
416
|
-
@date
|
539
|
+
@date
|
417
540
|
end
|
418
541
|
end
|
419
542
|
|
@@ -422,10 +545,11 @@ module RIMS
|
|
422
545
|
ivar_name = '@' + field_name.downcase.gsub('-', '_')
|
423
546
|
addr_list = instance_variable_get(ivar_name)
|
424
547
|
if (addr_list.nil?) then
|
425
|
-
addr_list = header.field_value_list(field_name).map{|
|
548
|
+
addr_list = header.field_value_list(field_name).map{|addr_list_txt| RFC822.parse_mail_address_list(addr_list_txt) }.inject(:+)
|
426
549
|
addr_list.freeze
|
427
550
|
instance_variable_set(ivar_name, addr_list)
|
428
551
|
end
|
552
|
+
|
429
553
|
addr_list
|
430
554
|
end
|
431
555
|
end
|
data/lib/rims/rfc822/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rims-rfc822
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TOKI Yoshinori
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|