rims-rfc822 0.1.0 → 0.2.0
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.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
|