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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3721bb4e1b0e4865de80a3756bc313a910180b207c76bbe4e25de30c2bb14f0b
4
- data.tar.gz: f1d6f3b4b1bc84d32644beeb89628c814be2b4674bc3e5c334ce2adcc8889a11
3
+ metadata.gz: 51ec6ae41467b2b68bd8d1bb23916ad42f27fb6b688f8769f64e61db5e8afd98
4
+ data.tar.gz: f48b88893f67da1da2824336bfe274b033131527420de1926bea05faa7036618
5
5
  SHA512:
6
- metadata.gz: cb1a1894bed18cfc81da85627620de46d1754b8dc50145cc40de4af0c2ed9d5a75ea7cff508b889442b06c6a513c2d607824be15ec470e3718d203d150092c61
7
- data.tar.gz: 42a2832fd4e246525c3e8932a7b7a1890b258862bb7adfd4077610cc8cf85fc05e277dfea02ecb93dea20f0b7894f31757a6ab5808fe626832f208099cefd453
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.content_type_parameters
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
- header_txt << $& if $&
11
- [ header_txt, body_txt ]
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 _name, value in field_pair_list
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(:? " | \( | \) | \\ | [^"\(\)\\]+ )/x, ''))
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 << [ nil, nil, unquote_phrase(display_name), nil ].freeze
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 << [ nil, nil, nil, nil ].freeze
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 << [ nil, nil, $~[:local_part].freeze, $~[:domain].freeze ].freeze
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 << [ unquote_phrase(display_name), route.freeze, local_part.freeze, domain.freeze ].freeze
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.freeze
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.freeze
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
- @is_multipart = nil
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['content-type'] || '')
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
- def media_sub_type_upcase
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 content_type_upcase
343
- to_upper("#{media_main_type}/#{media_sub_type}")
393
+ def media_main_type_upcase
394
+ # not return `nil'
395
+ media_main_type.upcase
344
396
  end
345
397
 
346
- def content_type_parameters
347
- setup_content_type
348
- @content_type[2].each_value.map{|name, value| [ name, value ] }
398
+ def media_sub_type_upcase
399
+ # not return `nil'
400
+ media_sub_type.upcase
349
401
  end
350
402
 
351
- def charset
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]['charset']) then
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
- if (name_value_pair = @content_type[2]['boundary']) then
361
- name_value_pair[1]
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
- if (@is_multipart.nil?) then
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) }.freeze
503
+ @parts = part_list.map{|msg_txt| Message.new(msg_txt) }
382
504
  else
383
- @parts = [].freeze
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
- if (@is_message.nil?) then
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.freeze
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{|addr_list_str| RFC822.parse_mail_address_list(addr_list_str) }.inject(:+)
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RIMS
4
4
  module RFC822
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
8
8
 
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.1.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-13 00:00:00.000000000 Z
11
+ date: 2019-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler