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 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