rims 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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: