rims 0.2.1

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