rims 0.2.5 → 0.2.6

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.
@@ -952,6 +952,7 @@ Content-Type: text/html
952
952
  def test_parse_charset_text
953
953
  make_search_parser(charset: 'utf-8') {
954
954
  add_msg("Content-Type: text/plain\r\n" +
955
+ "\r\n" +
955
956
  "foo")
956
957
  add_msg("Content-Type: text/plain; charset=utf-8\r\n" +
957
958
  "X-foo: dummy\r\n" +
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rims
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - TOKI Yoshinori
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-10 00:00:00.000000000 Z
11
+ date: 2019-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rims-rfc822
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: riser
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -130,7 +144,6 @@ files:
130
144
  - lib/rims/protocol/connection.rb
131
145
  - lib/rims/protocol/decoder.rb
132
146
  - lib/rims/protocol/parser.rb
133
- - lib/rims/rfc822.rb
134
147
  - lib/rims/service.rb
135
148
  - lib/rims/test.rb
136
149
  - lib/rims/version.rb
@@ -156,7 +169,6 @@ files:
156
169
  - test/test_protocol_fetch.rb
157
170
  - test/test_protocol_request.rb
158
171
  - test/test_protocol_search.rb
159
- - test/test_rfc822.rb
160
172
  - test/test_service.rb
161
173
  homepage: https://github.com/y10k/rims
162
174
  licenses:
@@ -202,5 +214,4 @@ test_files:
202
214
  - test/test_protocol_fetch.rb
203
215
  - test/test_protocol_request.rb
204
216
  - test/test_protocol_search.rb
205
- - test/test_rfc822.rb
206
217
  - test/test_service.rb
@@ -1,463 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- require 'time'
4
-
5
- module RIMS
6
- module RFC822
7
- def split_message(msg_txt)
8
- header_txt, body_txt = msg_txt.lstrip.split(/\r?\n\r?\n/, 2)
9
- header_txt << $& if $&
10
- [ header_txt, body_txt ]
11
- end
12
- module_function :split_message
13
-
14
- def parse_header(header_txt)
15
- field_pair_list = header_txt.scan(%r{
16
- ^
17
- ((?#name) \S+? )
18
- \s* : \s*
19
- (
20
- (?#value)
21
- .*? (?: \n|\z)
22
- (?: ^\s .*? (?: \n|\z) )*
23
- )
24
- }x)
25
-
26
- for _name, value in field_pair_list
27
- value.strip!
28
- name.freeze
29
- value.freeze
30
- end
31
-
32
- field_pair_list.freeze
33
- end
34
- module_function :parse_header
35
-
36
- def parse_content_type(content_type_txt)
37
- src_txt = content_type_txt.dup
38
- if (src_txt.sub!(%r"\A \s* (?<main_type>\S+?) \s* / \s* (?<sub_type>\S+?) \s* (?:;|\Z)"x, '')) then
39
- main_type = $~[:main_type]
40
- sub_type = $~[:sub_type]
41
-
42
- params = {}
43
- src_txt.scan(%r'(?<name>\S+?) \s* = \s* (?: (?<quoted_string>".*?") | (?<token>\S+?) ) \s* (?:;|\Z)'x) do
44
- name = $~[:name]
45
- if ($~[:quoted_string]) then
46
- quoted_value = $~[:quoted_string]
47
- value = unquote_phrase(quoted_value)
48
- else
49
- value = $~[:token]
50
- end
51
- params[name.downcase] = [ name.freeze, value.freeze ].freeze
52
- end
53
-
54
- [ main_type.freeze, sub_type.freeze, params.freeze ].freeze
55
- else
56
- [ 'application'.freeze, 'octet-stream'.freeze, {}.freeze ].freeze
57
- end
58
- end
59
- module_function :parse_content_type
60
-
61
- def parse_multipart_body(boundary, body_txt)
62
- delim = '--' + boundary
63
- term = delim + '--'
64
- body_txt2, _body_epilogue_txt = body_txt.split(term, 2)
65
- if (body_txt2) then
66
- _body_preamble_txt, body_parts_txt = body_txt2.split(delim, 2)
67
- if (body_parts_txt) then
68
- part_list = body_parts_txt.split(delim, -1)
69
- for part_txt in part_list
70
- part_txt.lstrip!
71
- part_txt.chomp!("\n")
72
- part_txt.chomp!("\r")
73
- part_txt.freeze
74
- end
75
- return part_list.freeze
76
- end
77
- end
78
-
79
- [].freeze
80
- end
81
- module_function :parse_multipart_body
82
-
83
- def unquote_phrase(phrase_txt)
84
- state = :raw
85
- src_txt = phrase_txt.dup
86
- dst_txt = ''.encode(phrase_txt.encoding)
87
-
88
- while (src_txt.sub!(/\A(:? " | \( | \) | \\ | [^"\(\)\\]+ )/x, ''))
89
- match_txt = $&
90
- case (state)
91
- when :raw
92
- case (match_txt)
93
- when '"'
94
- state = :quote
95
- when '('
96
- state = :comment
97
- when "\\"
98
- src_txt.sub!(/\A./, '') and dst_txt << $&
99
- else
100
- dst_txt << match_txt
101
- end
102
- when :quote
103
- case (match_txt)
104
- when '"'
105
- state = :raw
106
- when "\\"
107
- src_txt.sub!(/\A./, '') && dst_txt << $&
108
- else
109
- dst_txt << match_txt
110
- end
111
- when :comment
112
- case (match_txt)
113
- when ')'
114
- state = :raw
115
- when "\\"
116
- src_txt.sub!(/\A./, '')
117
- else
118
- # ignore comment text.
119
- end
120
- else
121
- raise "internal error: unknown state #{state}"
122
- end
123
- end
124
-
125
- dst_txt.freeze
126
- end
127
- module_function :unquote_phrase
128
-
129
- def parse_mail_address_list(address_list_txt)
130
- addr_list = []
131
- src_txt = address_list_txt.dup
132
-
133
- while (true)
134
- if (src_txt.sub!(%r{
135
- \A
136
- \s*
137
- (?<display_name>\S.*?) \s* : (?<group_list>.*?) ;
138
- \s*
139
- ,?
140
- }x, ''))
141
- then
142
- display_name = $~[:display_name]
143
- group_list = $~[:group_list]
144
- addr_list << [ nil, nil, unquote_phrase(display_name), nil ].freeze
145
- addr_list.concat(parse_mail_address_list(group_list))
146
- addr_list << [ nil, nil, nil, nil ].freeze
147
- elsif (src_txt.sub!(%r{
148
- \A
149
- \s*
150
- (?<local_part>[^<>@",\s]+) \s* @ \s* (?<domain>[^<>@",\s]+)
151
- \s*
152
- ,?
153
- }x, ''))
154
- then
155
- addr_list << [ nil, nil, $~[:local_part].freeze, $~[:domain].freeze ].freeze
156
- elsif (src_txt.sub!(%r{
157
- \A
158
- \s*
159
- (?<display_name>\S.*?)
160
- \s*
161
- <
162
- \s*
163
- (?:
164
- (?<route>@[^<>@",]* (?:, \s* @[^<>@",]*)*)
165
- \s*
166
- :
167
- )?
168
- \s*
169
- (?<local_part>[^<>@",\s]+) \s* @ \s* (?<domain>[^<>@",\s]+)
170
- \s*
171
- >
172
- \s*
173
- ,?
174
- }x, ''))
175
- then
176
- display_name = $~[:display_name]
177
- route = $~[:route]
178
- local_part = $~[:local_part]
179
- domain = $~[:domain]
180
- addr_list << [ unquote_phrase(display_name), route.freeze, local_part.freeze, domain.freeze ].freeze
181
- else
182
- break
183
- end
184
- end
185
-
186
- addr_list.freeze
187
- end
188
- module_function :parse_mail_address_list
189
-
190
- class Header
191
- include Enumerable
192
-
193
- def initialize(header_txt)
194
- @raw_source = header_txt.freeze
195
- @field_list = nil
196
- @field_map = nil
197
- end
198
-
199
- attr_reader :raw_source
200
-
201
- def setup_header
202
- if (@field_list.nil? || @field_map.nil?) then
203
- @field_list = RFC822.parse_header(@raw_source)
204
- @field_map = {}
205
- for name, value in @field_list
206
- key = name.downcase
207
- @field_map[key] = [] unless (@field_map.key? key)
208
- @field_map[key] << value
209
- end
210
- @field_map.each_value do |value_list|
211
- value_list.freeze
212
- end
213
- @field_map.freeze
214
- self
215
- end
216
- end
217
- private :setup_header
218
-
219
- def each
220
- setup_header
221
- return enum_for(:each) unless block_given?
222
- for name, value in @field_list
223
- yield(name, value)
224
- end
225
- self
226
- end
227
-
228
- def key?(name)
229
- setup_header
230
- @field_map.key? name.downcase
231
- end
232
-
233
- def [](name)
234
- setup_header
235
- if (value_list = @field_map[name.downcase]) then
236
- value_list[0]
237
- end
238
- end
239
-
240
- def fetch_upcase(name)
241
- setup_header
242
- if (value_list = @field_map[name.downcase]) then
243
- if (value = value_list[0]) then
244
- value.upcase
245
- end
246
- end
247
- end
248
-
249
- def field_value_list(name)
250
- setup_header
251
- @field_map[name.downcase]
252
- end
253
- end
254
-
255
- class Body
256
- def initialize(body_txt)
257
- @raw_source = body_txt.freeze
258
- end
259
-
260
- attr_reader :raw_source
261
- end
262
-
263
- class Message
264
- def initialize(msg_txt)
265
- @raw_source = msg_txt.freeze
266
- @header = nil
267
- @body = nil
268
- @content_type = nil
269
- @is_multipart = nil
270
- @parts = nil
271
- @is_message = nil
272
- @message = nil
273
- @date = nil
274
- @from = nil
275
- @sender = nil
276
- @reply_to = nil
277
- @to = nil
278
- @cc = nil
279
- @bcc = nil
280
- end
281
-
282
- attr_reader :raw_source
283
-
284
- def setup_message
285
- if (@header.nil? || @body.nil?) then
286
- header_txt, body_txt = RFC822.split_message(@raw_source)
287
- @header = Header.new(header_txt || '')
288
- @body = Body.new(body_txt || '')
289
- self
290
- end
291
- end
292
- private :setup_message
293
-
294
- def header
295
- setup_message
296
- @header
297
- end
298
-
299
- def body
300
- setup_message
301
- @body
302
- end
303
-
304
- def setup_content_type
305
- if (@content_type.nil?) then
306
- @content_type = RFC822.parse_content_type(header['content-type'] || '')
307
- self
308
- end
309
- end
310
- private :setup_content_type
311
-
312
- def to_upper(text_or_nil)
313
- text_or_nil.upcase if text_or_nil
314
- end
315
- private :to_upper
316
-
317
- def media_main_type
318
- setup_content_type
319
- @content_type[0]
320
- end
321
-
322
- def media_main_type_upcase
323
- setup_content_type
324
- to_upper(@content_type[0])
325
- end
326
-
327
- def media_sub_type
328
- setup_content_type
329
- @content_type[1]
330
- end
331
-
332
- def media_sub_type_upcase
333
- setup_content_type
334
- to_upper(@content_type[1])
335
- end
336
-
337
- def content_type
338
- "#{media_main_type}/#{media_sub_type}"
339
- end
340
-
341
- def content_type_upcase
342
- to_upper("#{media_main_type}/#{media_sub_type}")
343
- end
344
-
345
- def content_type_parameters
346
- setup_content_type
347
- @content_type[2].each_value.map{|name, value| [ name, value ] }
348
- end
349
-
350
- def charset
351
- setup_content_type
352
- if (name_value_pair = @content_type[2]['charset']) then
353
- name_value_pair[1]
354
- end
355
- end
356
-
357
- def boundary
358
- setup_content_type
359
- if (name_value_pair = @content_type[2]['boundary']) then
360
- name_value_pair[1]
361
- end
362
- end
363
-
364
- def text?
365
- media_main_type_upcase == 'TEXT'
366
- end
367
-
368
- def multipart?
369
- if (@is_multipart.nil?) then
370
- @is_multipart = (media_main_type_upcase == 'MULTIPART')
371
- end
372
- @is_multipart
373
- end
374
-
375
- def parts
376
- if (multipart?) then
377
- if (@parts.nil?) then
378
- if (boundary = self.boundary) then
379
- part_list = RFC822.parse_multipart_body(boundary, body.raw_source)
380
- @parts = part_list.map{|msg_txt| Message.new(msg_txt) }.freeze
381
- else
382
- @parts = [].freeze
383
- end
384
- end
385
- @parts
386
- end
387
- end
388
-
389
- def message?
390
- if (@is_message.nil?) then
391
- @is_message = (media_main_type_upcase == 'MESSAGE')
392
- end
393
- @is_message
394
- end
395
-
396
- def message
397
- if (message?) then
398
- if (@message.nil?) then
399
- @message = Message.new(body.raw_source)
400
- end
401
- @message
402
- end
403
- end
404
-
405
- def date
406
- if (header.key? 'Date') then
407
- if (@date.nil?) then
408
- begin
409
- @date = Time.parse(header['Date'])
410
- rescue ArgumentError
411
- @date = Time.at(0)
412
- end
413
- end
414
-
415
- @date.freeze
416
- end
417
- end
418
-
419
- def mail_address_header_field(field_name)
420
- if (header.key? field_name) then
421
- ivar_name = '@' + field_name.downcase.gsub('-', '_')
422
- addr_list = instance_variable_get(ivar_name)
423
- if (addr_list.nil?) then
424
- addr_list = header.field_value_list(field_name).map{|addr_list_str| RFC822.parse_mail_address_list(addr_list_str) }.inject(:+)
425
- addr_list.freeze
426
- instance_variable_set(ivar_name, addr_list)
427
- end
428
- addr_list
429
- end
430
- end
431
- private :mail_address_header_field
432
-
433
- def from
434
- mail_address_header_field('from')
435
- end
436
-
437
- def sender
438
- mail_address_header_field('sender')
439
- end
440
-
441
- def reply_to
442
- mail_address_header_field('reply-to')
443
- end
444
-
445
- def to
446
- mail_address_header_field('to')
447
- end
448
-
449
- def cc
450
- mail_address_header_field('cc')
451
- end
452
-
453
- def bcc
454
- mail_address_header_field('bcc')
455
- end
456
- end
457
- end
458
- end
459
-
460
- # Local Variables:
461
- # mode: Ruby
462
- # indent-tabs-mode: nil
463
- # End: