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.
- checksums.yaml +4 -4
- data/ChangeLog +60 -0
- data/lib/rims/cmd.rb +44 -28
- data/lib/rims/protocol/decoder.rb +53 -29
- data/lib/rims/protocol/parser.rb +136 -109
- data/lib/rims/service.rb +21 -21
- data/lib/rims/test.rb +21 -3
- data/lib/rims/version.rb +1 -1
- data/rims.gemspec +1 -0
- data/test/cmd/test_command.rb +114 -22
- data/test/test_protocol_decoder.rb +190 -46
- data/test/test_protocol_fetch.rb +380 -189
- data/test/test_protocol_search.rb +1 -0
- metadata +16 -5
- data/lib/rims/rfc822.rb +0 -463
- data/test/test_rfc822.rb +0 -696
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f51adf08f317981cdec3bb1067edd7a6c4befcf2f3e3e9c3ab4ac97702dad80
|
4
|
+
data.tar.gz: d0089e4150ad0768b0267c3c34980fad6a5251f0953997bd75a8c00e590120bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4be9394ccaa4e2081dcc4040f197f375444b04953706c050aad5b1e255121cfec2d49753f31160f0dc458dcac139296b37c5efe0685f749904e5a0d74c705b24
|
7
|
+
data.tar.gz: a1a1a3f16b3069b2fd0e2ee9016349fbb2bb43401220ed9d00beec44914b4ef0206582c41a2bfb94c895d91ea6eb62ba76dcdd342338aa5becb8670777a3343a
|
data/ChangeLog
CHANGED
@@ -1,3 +1,63 @@
|
|
1
|
+
2019-07-08 TOKI Yoshinori <toki@freedom.ne.jp>
|
2
|
+
|
3
|
+
* lib/rims/cmd.rb: disable plug-in name list on plug-in command
|
4
|
+
options.
|
5
|
+
|
6
|
+
* lib/rims/cmd.rb: environment command is defined.
|
7
|
+
|
8
|
+
2019-07-02 TOKI Yoshinori <toki@freedom.ne.jp>
|
9
|
+
|
10
|
+
* lib/rims/protocol/decoder.rb: ALERT message notified to client
|
11
|
+
immediately.
|
12
|
+
|
13
|
+
2019-07-01 TOKI Yoshinori <toki@freedom.ne.jp>
|
14
|
+
|
15
|
+
* lib/rims/protocol/decoder.rb, lib/rims/protocol/parser.rb: skip
|
16
|
+
encoding error on SEARCH command.
|
17
|
+
|
18
|
+
* lib/rims/protocol/decoder.rb: BADCHARSET response code for
|
19
|
+
unknown charset on SERCH command.
|
20
|
+
|
21
|
+
See RFC 3501 / 6.4.4. SEARCH Command
|
22
|
+
<https://tools.ietf.org/html/rfc3501#section-6.4.4>
|
23
|
+
|
24
|
+
If the server does not support the specified [CHARSET], it MUST
|
25
|
+
return a tagged NO response (not a BAD). This response SHOULD
|
26
|
+
contain the BADCHARSET response code, which MAY list the
|
27
|
+
[CHARSET]s supported by the server.
|
28
|
+
|
29
|
+
2019-06-29 TOKI Yoshinori <toki@freedom.ne.jp>
|
30
|
+
|
31
|
+
* lib/rims/protocol/decoder.rb: line break before error response
|
32
|
+
interrupting search command.
|
33
|
+
|
34
|
+
2019-06-25 TOKI Yoshinori <toki@freedom.ne.jp>
|
35
|
+
|
36
|
+
* lib/rims/protocol/parser.rb: delete unnecessary private methods.
|
37
|
+
|
38
|
+
2019-06-23 TOKI Yoshinori <toki@freedom.ne.jp>
|
39
|
+
|
40
|
+
* lib/rims/protocol/parser.rb: add extension data to
|
41
|
+
bodystructure.
|
42
|
+
|
43
|
+
2019-06-21 TOKI Yoshinori <toki@freedom.ne.jp>
|
44
|
+
|
45
|
+
* lib/rims/rfc822.rb, rims.gemspec, test/test_rfc822.rb: repladced
|
46
|
+
`RIMS::RFC822' to a external rubygem.
|
47
|
+
|
48
|
+
2019-06-15 TOKI Yoshinori <toki@freedom.ne.jp>
|
49
|
+
|
50
|
+
* lib/rims/protocol/parser.rb: not allowed empty body field
|
51
|
+
parameters at attribute parser.
|
52
|
+
|
53
|
+
when body field parameters are empty, a command to fetch
|
54
|
+
`bodystructure' should response `NIL' instead of `()'.
|
55
|
+
|
56
|
+
see RFC 3501 / 9. Formal Syntax:
|
57
|
+
<https://tools.ietf.org/html/rfc3501#section-9>
|
58
|
+
|
59
|
+
body-fld-param = "(" string SP string *(SP string SP string) ")" / nil
|
60
|
+
|
1
61
|
2019-06-10 TOKI Yoshinori <toki@freedom.ne.jp>
|
2
62
|
|
3
63
|
* RIMS version 0.2.5 is released.
|
data/lib/rims/cmd.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
require 'json'
|
4
|
-
require 'logger'
|
5
4
|
require 'net/imap'
|
6
5
|
require 'optparse'
|
7
6
|
require 'pp'if $DEBUG
|
8
7
|
require 'riser'
|
9
|
-
require 'syslog'
|
10
|
-
require 'syslog/logger'
|
11
8
|
require 'yaml'
|
12
9
|
|
13
10
|
OptionParser.accept(JSON) do |json_data, *_|
|
@@ -61,7 +58,7 @@ module RIMS
|
|
61
58
|
puts "commands:"
|
62
59
|
w = CMDs.keys.map{|k| k.length }.max + 4
|
63
60
|
fmt = " %- #{w}s%s"
|
64
|
-
CMDs.each do |cmd_name, cmd_entry|
|
61
|
+
CMDs.sort_by{|cmd_name, _| cmd_name }.each do |cmd_name, cmd_entry|
|
65
62
|
if ((! show_debug_command) && (cmd_name =~ /\A debug/x)) then
|
66
63
|
next
|
67
64
|
end
|
@@ -519,13 +516,7 @@ module RIMS
|
|
519
516
|
end
|
520
517
|
options.on('--meta-kvs-type=TYPE',
|
521
518
|
KeyValueStore::FactoryBuilder.plug_in_names,
|
522
|
-
"Choose key-value store type of mailbox meta-data database" +
|
523
|
-
if (KeyValueStore::FactoryBuilder.plug_in_names.length > 1) then
|
524
|
-
' (' + KeyValueStore::FactoryBuilder.plug_in_names.join(' ') + ')'
|
525
|
-
else
|
526
|
-
''
|
527
|
-
end +
|
528
|
-
". default is `" +
|
519
|
+
"Choose key-value store type of mailbox meta-data database. default is `" +
|
529
520
|
KeyValueStore::FactoryBuilder.plug_in_names[0] +
|
530
521
|
"'."
|
531
522
|
) do |kvs_type|
|
@@ -568,13 +559,7 @@ module RIMS
|
|
568
559
|
end
|
569
560
|
options.on('--text-kvs-type=TYPE',
|
570
561
|
KeyValueStore::FactoryBuilder.plug_in_names,
|
571
|
-
"Choose key-value store type of mailbox text-data database" +
|
572
|
-
if (KeyValueStore::FactoryBuilder.plug_in_names.length > 1) then
|
573
|
-
' (' + KeyValueStore::FactoryBuilder.plug_in_names.join(' ') + ')'
|
574
|
-
else
|
575
|
-
''
|
576
|
-
end +
|
577
|
-
". default is `" +
|
562
|
+
"Choose key-value store type of mailbox text-data database. default is `" +
|
578
563
|
KeyValueStore::FactoryBuilder.plug_in_names[0] +
|
579
564
|
"'."
|
580
565
|
) do |kvs_type|
|
@@ -627,7 +612,7 @@ module RIMS
|
|
627
612
|
end
|
628
613
|
options.on('--passwd-config=TYPE_JSONDATA',
|
629
614
|
/([^:]+)(?::(.*))?/,
|
630
|
-
"Password source type
|
615
|
+
"Password source type and configuration. format is `[type]:[json_data]'."
|
631
616
|
) do |_, type, json_data|
|
632
617
|
build.chain{|c|
|
633
618
|
c.load(authentication: {
|
@@ -641,7 +626,7 @@ module RIMS
|
|
641
626
|
end
|
642
627
|
options.on('--passwd-file=TYPE_FILE',
|
643
628
|
/([^:]+):(.+)/,
|
644
|
-
"Password source type
|
629
|
+
"Password source type and configuration file. format is `[type]:[file]'."
|
645
630
|
) do |_, type, path|
|
646
631
|
build.chain{|c|
|
647
632
|
c.load(authentication: {
|
@@ -907,14 +892,7 @@ module RIMS
|
|
907
892
|
def key_value_store_option
|
908
893
|
@conf[:key_value_store_type] = GDBM_KeyValueStore
|
909
894
|
@options.on('--kvs-type=TYPE',
|
910
|
-
|
911
|
-
"Choose key-value store type of mailbox database" +
|
912
|
-
if (KeyValueStore::FactoryBuilder.plug_in_names.length > 1) then
|
913
|
-
' (' + KeyValueStore::FactoryBuilder.plug_in_names.join(' ') + ')'
|
914
|
-
else
|
915
|
-
''
|
916
|
-
end +
|
917
|
-
". default is `" +
|
895
|
+
"Choose key-value store type of mailbox database. default is `" +
|
918
896
|
KeyValueStore::FactoryBuilder.plug_in_names[0] +
|
919
897
|
"'."
|
920
898
|
) do |kvs_type|
|
@@ -1145,6 +1123,44 @@ module RIMS
|
|
1145
1123
|
end
|
1146
1124
|
command_function :cmd_daemon, "Daemon start/stop/status tool."
|
1147
1125
|
|
1126
|
+
def cmd_environment(options, args)
|
1127
|
+
format = {
|
1128
|
+
yaml: lambda{|env|
|
1129
|
+
YAML.dump(env)
|
1130
|
+
},
|
1131
|
+
json: lambda{|env|
|
1132
|
+
JSON.pretty_generate(env)
|
1133
|
+
}
|
1134
|
+
}
|
1135
|
+
|
1136
|
+
conf = Config.new(options,
|
1137
|
+
[ [ :format_type,
|
1138
|
+
format.keys.first,
|
1139
|
+
'--format=FORMAT',
|
1140
|
+
format.keys,
|
1141
|
+
"Choose display format (#{format.keys.join(' ')})."
|
1142
|
+
]
|
1143
|
+
])
|
1144
|
+
conf.required_feature_option
|
1145
|
+
conf.setup_option_list
|
1146
|
+
conf.parse_options!(args)
|
1147
|
+
|
1148
|
+
env = {
|
1149
|
+
'RIMS Environment' => [
|
1150
|
+
{ 'RUBY VERSION' => RUBY_DESCRIPTION },
|
1151
|
+
{ 'RIMS VERSION' => RIMS::VERSION },
|
1152
|
+
{ 'AUTHENTICATION PLUG-IN' => Authentication.plug_in_names },
|
1153
|
+
{ 'KEY-VALUE STORE PLUG-IN' => KeyValueStore::FactoryBuilder.plug_in_names }
|
1154
|
+
]
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
formatter = format[conf[:format_type]]
|
1158
|
+
puts formatter.call(env)
|
1159
|
+
|
1160
|
+
0
|
1161
|
+
end
|
1162
|
+
command_function :cmd_environment, 'Show rims environment.'
|
1163
|
+
|
1148
1164
|
def imap_append(imap, mailbox, message, store_flags: [], date_time: nil, verbose: false)
|
1149
1165
|
puts "message date: #{date_time}" if (verbose && date_time)
|
1150
1166
|
store_flags = nil if store_flags.empty?
|
@@ -26,9 +26,13 @@ module RIMS
|
|
26
26
|
output_write = lambda{|res|
|
27
27
|
last_line = nil
|
28
28
|
for data in res
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
if (data == :flush) then
|
30
|
+
output.flush
|
31
|
+
else
|
32
|
+
logger.debug("response data: #{Protocol.io_data_log(data)}") if logger.debug?
|
33
|
+
output << data
|
34
|
+
last_line = data
|
35
|
+
end
|
32
36
|
end
|
33
37
|
output.flush
|
34
38
|
|
@@ -252,13 +256,13 @@ module RIMS
|
|
252
256
|
end
|
253
257
|
private :imap_command
|
254
258
|
|
255
|
-
def make_engine_and_recovery_if_needed(
|
259
|
+
def make_engine_and_recovery_if_needed(drb_services, username,
|
256
260
|
logger: Logger.new(STDOUT))
|
257
261
|
unique_user_id = Authentication.unique_user_id(username)
|
258
262
|
logger.debug("unique user ID: #{username} -> #{unique_user_id}") if logger.debug?
|
259
263
|
|
260
264
|
logger.info("open mail store: #{unique_user_id} [ #{username} ]")
|
261
|
-
engine =
|
265
|
+
engine = drb_services[:engine, unique_user_id]
|
262
266
|
|
263
267
|
begin
|
264
268
|
engine.recovery_if_needed(username) {|msg| yield(msg) }
|
@@ -321,10 +325,10 @@ module RIMS
|
|
321
325
|
private :imap_command
|
322
326
|
end
|
323
327
|
|
324
|
-
def initialize(
|
328
|
+
def initialize(drb_services, auth, logger,
|
325
329
|
mail_delivery_user: Service::DEFAULT_CONFIG.mail_delivery_user)
|
326
330
|
super(auth, logger)
|
327
|
-
@
|
331
|
+
@drb_services = drb_services
|
328
332
|
@mail_delivery_user = mail_delivery_user
|
329
333
|
end
|
330
334
|
|
@@ -360,9 +364,9 @@ module RIMS
|
|
360
364
|
case (username)
|
361
365
|
when @mail_delivery_user
|
362
366
|
@logger.info("mail delivery user: #{username}")
|
363
|
-
MailDeliveryDecoder.new(self, @
|
367
|
+
MailDeliveryDecoder.new(self, @drb_services, @auth, @logger)
|
364
368
|
else
|
365
|
-
engine = self.class.make_engine_and_recovery_if_needed(@
|
369
|
+
engine = self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|msg| yield(msg) }
|
366
370
|
UserMailboxDecoder.new(self, engine, @auth, @logger)
|
367
371
|
end
|
368
372
|
end
|
@@ -375,7 +379,7 @@ module RIMS
|
|
375
379
|
if (username != :*) then
|
376
380
|
yield response_stream(tag) {|res|
|
377
381
|
@logger.info("authentication OK: #{username}")
|
378
|
-
@next_decoder = accept_authentication(username) {|msg| res << msg }
|
382
|
+
@next_decoder = accept_authentication(username) {|msg| res << msg << :flush }
|
379
383
|
res << "#{tag} OK AUTHENTICATE #{auth_type} success\r\n"
|
380
384
|
}
|
381
385
|
else
|
@@ -392,7 +396,7 @@ module RIMS
|
|
392
396
|
if (@auth.authenticate_login(username, password)) then
|
393
397
|
yield response_stream(tag) {|res|
|
394
398
|
@logger.info("login authentication OK: #{username}")
|
395
|
-
@next_decoder = accept_authentication(username) {|msg| res << msg }
|
399
|
+
@next_decoder = accept_authentication(username) {|msg| res << msg << :flush }
|
396
400
|
res << "#{tag} OK LOGIN completed\r\n"
|
397
401
|
}
|
398
402
|
else
|
@@ -1242,7 +1246,12 @@ module RIMS
|
|
1242
1246
|
cond_args.shift
|
1243
1247
|
charset_string = cond_args.shift or raise SyntaxError, 'need for a charset string of CHARSET'
|
1244
1248
|
charset_string.is_a? String or raise SyntaxError, "CHARSET charset string expected as <String> but was <#{charset_string.class}>."
|
1245
|
-
|
1249
|
+
begin
|
1250
|
+
parser.charset = charset_string
|
1251
|
+
rescue ArgumentError
|
1252
|
+
@logger.warn("unknown charset: #{charset_string}")
|
1253
|
+
return yield([ "#{tag} NO [BADCHARSET (#{Encoding.list.map(&:to_s).join(' ')})] unknown charset\r\n" ])
|
1254
|
+
end
|
1246
1255
|
end
|
1247
1256
|
|
1248
1257
|
if (cond_args.empty?) then
|
@@ -1278,21 +1287,36 @@ module RIMS
|
|
1278
1287
|
}
|
1279
1288
|
|
1280
1289
|
res << '* SEARCH'
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1290
|
+
begin
|
1291
|
+
begin
|
1292
|
+
for msg in msg_src
|
1293
|
+
begin
|
1294
|
+
if (cond.call(msg)) then
|
1295
|
+
if (uid) then
|
1296
|
+
res << " #{msg.uid}"
|
1297
|
+
else
|
1298
|
+
res << " #{msg.num}"
|
1299
|
+
end
|
1300
|
+
if (res.length >= @bulk_response_count) then
|
1301
|
+
yield(res)
|
1302
|
+
res = []
|
1303
|
+
end
|
1304
|
+
end
|
1305
|
+
rescue EncodingError
|
1306
|
+
@logger.warn("encoding error at the message: uidvalidity(#{folder.mbox_id}) uid(#{msg.uid})")
|
1307
|
+
@logger.warn("#{$!} (#{$!.class})")
|
1308
|
+
end
|
1291
1309
|
end
|
1310
|
+
ensure
|
1311
|
+
res << "\r\n"
|
1292
1312
|
end
|
1313
|
+
rescue
|
1314
|
+
# flush bulk response
|
1315
|
+
yield(res)
|
1316
|
+
res = []
|
1317
|
+
raise
|
1293
1318
|
end
|
1294
1319
|
|
1295
|
-
res << "\r\n"
|
1296
1320
|
res << "#{tag} OK SEARCH completed\r\n"
|
1297
1321
|
yield(res)
|
1298
1322
|
end
|
@@ -1756,10 +1780,10 @@ module RIMS
|
|
1756
1780
|
end
|
1757
1781
|
|
1758
1782
|
class MailDeliveryDecoder < AuthenticatedDecoder
|
1759
|
-
def initialize(parent_decoder,
|
1783
|
+
def initialize(parent_decoder, drb_services, auth, logger)
|
1760
1784
|
super(auth, logger)
|
1761
1785
|
@parent_decoder = parent_decoder
|
1762
|
-
@
|
1786
|
+
@drb_services = drb_services
|
1763
1787
|
@auth = auth
|
1764
1788
|
@last_user_cache_key_username = nil
|
1765
1789
|
@last_user_cache_value_engine = nil
|
@@ -1802,7 +1826,7 @@ module RIMS
|
|
1802
1826
|
private :release_engine_cache
|
1803
1827
|
|
1804
1828
|
def auth?
|
1805
|
-
@
|
1829
|
+
@drb_services != nil
|
1806
1830
|
end
|
1807
1831
|
|
1808
1832
|
def selected?
|
@@ -1811,7 +1835,7 @@ module RIMS
|
|
1811
1835
|
|
1812
1836
|
def cleanup
|
1813
1837
|
release_engine_cache
|
1814
|
-
@
|
1838
|
+
@drb_services = nil unless @drb_services.nil?
|
1815
1839
|
@auth = nil unless @auth.nil?
|
1816
1840
|
|
1817
1841
|
unless (@parent_decoder.nil?) then
|
@@ -1924,9 +1948,9 @@ module RIMS
|
|
1924
1948
|
engine = engine_cache(username)
|
1925
1949
|
deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, res)
|
1926
1950
|
else
|
1927
|
-
res =
|
1951
|
+
res = response_stream(tag) {|stream_res|
|
1928
1952
|
engine = store_engine_cache(username) {
|
1929
|
-
self.class.make_engine_and_recovery_if_needed(@
|
1953
|
+
self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|msg| stream_res << msg << :flush }
|
1930
1954
|
}
|
1931
1955
|
deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, stream_res)
|
1932
1956
|
}
|
data/lib/rims/protocol/parser.rb
CHANGED
@@ -244,8 +244,21 @@ module RIMS
|
|
244
244
|
end
|
245
245
|
private :get_mail
|
246
246
|
|
247
|
-
|
247
|
+
attr_reader :charset
|
248
248
|
|
249
|
+
def charset=(new_charset)
|
250
|
+
@charset = Encoding.find(new_charset)
|
251
|
+
end
|
252
|
+
|
253
|
+
def force_string_charset(string)
|
254
|
+
string = string.dup
|
255
|
+
string.force_encoding(@charset)
|
256
|
+
string.valid_encoding? or raise SyntaxError, "invalid #{@charset} string: #{string.inspect}"
|
257
|
+
string
|
258
|
+
end
|
259
|
+
private :force_string_charset
|
260
|
+
|
261
|
+
# should set `search_string' encoding to `@charset'
|
249
262
|
def string_include?(search_string, text)
|
250
263
|
if (search_string.ascii_only?) then
|
251
264
|
unless (text.encoding.ascii_compatible?) then
|
@@ -253,7 +266,6 @@ module RIMS
|
|
253
266
|
end
|
254
267
|
else
|
255
268
|
if (@charset) then
|
256
|
-
search_string = search_string.dup.force_encoding(@charset)
|
257
269
|
text = text.encode(@charset)
|
258
270
|
end
|
259
271
|
end
|
@@ -266,7 +278,7 @@ module RIMS
|
|
266
278
|
if (mail.text? || mail.message?) then
|
267
279
|
body_txt = mail.body.raw_source
|
268
280
|
if (charset = mail.charset) then
|
269
|
-
if (body_txt.encoding !=
|
281
|
+
if (body_txt.encoding != charset) then
|
270
282
|
body_txt = body_txt.dup.force_encoding(charset)
|
271
283
|
end
|
272
284
|
end
|
@@ -310,6 +322,7 @@ module RIMS
|
|
310
322
|
private :parse_msg_flag_enabled
|
311
323
|
|
312
324
|
def parse_search_header(field_name, search_string)
|
325
|
+
search_string = force_string_charset(search_string) if @charset
|
313
326
|
proc{|next_cond|
|
314
327
|
proc{|msg|
|
315
328
|
mail = get_mail(msg)
|
@@ -359,6 +372,7 @@ module RIMS
|
|
359
372
|
private :parse_mail_bytesize
|
360
373
|
|
361
374
|
def parse_body(search_string)
|
375
|
+
search_string = force_string_charset(search_string) if @charset
|
362
376
|
proc{|next_cond|
|
363
377
|
proc{|msg|
|
364
378
|
if (text = mail_body_text(get_mail(msg))) then
|
@@ -421,6 +435,7 @@ module RIMS
|
|
421
435
|
private :parse_or
|
422
436
|
|
423
437
|
def parse_text(search_string)
|
438
|
+
search_string = force_string_charset(search_string) if @charset
|
424
439
|
search_text = proc{|message_text| string_include?(search_string, message_text) }
|
425
440
|
search_mail = proc{|mail|
|
426
441
|
if (mail.multipart?) then
|
@@ -690,14 +705,10 @@ module RIMS
|
|
690
705
|
if ((array.length > 0) && (array.first.is_a? Array)) then
|
691
706
|
s = '('.b
|
692
707
|
array = array.dup
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
else
|
698
|
-
s << ' '.b << encode_value(object)
|
699
|
-
end
|
700
|
-
end
|
708
|
+
begin
|
709
|
+
s << encode_bodystructure(array.shift)
|
710
|
+
end while ((array.length > 0) && (array.first.is_a? Array))
|
711
|
+
s << ' '.b << array.map{|i| encode_value(i) }.join(' '.b)
|
701
712
|
s << ')'.b
|
702
713
|
elsif ((array.length > 0) && (array.first.upcase == 'MESSAGE')) then
|
703
714
|
msg_body_list = array[0..7].map{|v| encode_value(v) }
|
@@ -760,7 +771,6 @@ module RIMS
|
|
760
771
|
def initialize(mail_store, folder)
|
761
772
|
@mail_store = mail_store
|
762
773
|
@folder = folder
|
763
|
-
@charset = nil
|
764
774
|
@mail_cache = Hash.new{|hash, uid|
|
765
775
|
if (msg_txt = @mail_store.msg_text(@folder.mbox_id, uid)) then
|
766
776
|
hash[uid] = RFC822::Message.new(msg_txt)
|
@@ -773,29 +783,6 @@ module RIMS
|
|
773
783
|
end
|
774
784
|
private :get_mail
|
775
785
|
|
776
|
-
def make_array(value)
|
777
|
-
if (value) then
|
778
|
-
if (value.is_a? Array) then
|
779
|
-
list = value
|
780
|
-
else
|
781
|
-
list = [ value ]
|
782
|
-
end
|
783
|
-
|
784
|
-
if (block_given?) then
|
785
|
-
yield(list)
|
786
|
-
else
|
787
|
-
list
|
788
|
-
end
|
789
|
-
end
|
790
|
-
end
|
791
|
-
private :make_array
|
792
|
-
|
793
|
-
def make_address_list(email_address)
|
794
|
-
mailbox, host = email_address.split(/@/, 2)
|
795
|
-
[ nil, nil, mailbox, host ]
|
796
|
-
end
|
797
|
-
private :make_address_list
|
798
|
-
|
799
786
|
def expand_macro(cmd_list)
|
800
787
|
func_list = cmd_list.map{|name| parse_cached(name) }
|
801
788
|
proc{|msg|
|
@@ -804,76 +791,116 @@ module RIMS
|
|
804
791
|
end
|
805
792
|
private :expand_macro
|
806
793
|
|
807
|
-
def
|
808
|
-
if (
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
794
|
+
def make_body_params(name_value_pair_list)
|
795
|
+
if (name_value_pair_list && ! name_value_pair_list.empty?) then
|
796
|
+
name_value_pair_list.flatten
|
797
|
+
else
|
798
|
+
# not allowed empty body field parameters.
|
799
|
+
# RFC 3501 / 9. Formal Syntax:
|
800
|
+
# body-fld-param = "(" string SP string *(SP string SP string) ")" / nil
|
801
|
+
nil
|
802
|
+
end
|
803
|
+
end
|
804
|
+
private :make_body_params
|
805
|
+
|
806
|
+
def get_body_disposition(mail)
|
807
|
+
if (disposition_type = mail.content_disposition_upcase) then
|
808
|
+
[ disposition_type,
|
809
|
+
make_body_params(mail.content_disposition_parameter_list)
|
810
|
+
]
|
814
811
|
else
|
815
|
-
|
812
|
+
# not allowed empty body field disposition.
|
813
|
+
# RFC 3501 / 9. Formal Syntax:
|
814
|
+
# body-fld-dsp = "(" string SP body-fld-param ")" / nil
|
815
|
+
nil
|
816
816
|
end
|
817
817
|
end
|
818
|
-
private :
|
818
|
+
private :get_body_disposition
|
819
819
|
|
820
|
-
def
|
820
|
+
def get_body_lang(mail)
|
821
|
+
if (tag_list = mail.content_language_upcase) then
|
822
|
+
unless (tag_list.empty?) then
|
823
|
+
if (tag_list.length == 1) then
|
824
|
+
tag_list[0]
|
825
|
+
else
|
826
|
+
tag_list
|
827
|
+
end
|
828
|
+
end
|
829
|
+
end
|
830
|
+
end
|
831
|
+
private :get_body_lang
|
832
|
+
|
833
|
+
def get_bodystructure_data(mail, extension: false)
|
834
|
+
body_data = []
|
821
835
|
if (mail.multipart?) then # body_type_mpart
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
836
|
+
body_data.concat(mail.parts.map{|part_msg| get_bodystructure_data(part_msg, extension: extension) })
|
837
|
+
body_data << mail.media_sub_type_upcase
|
838
|
+
|
839
|
+
# body_ext_mpart
|
840
|
+
if (extension) then
|
841
|
+
body_data << make_body_params(mail.content_type_parameter_list)
|
842
|
+
body_data << get_body_disposition(mail)
|
843
|
+
body_data << get_body_lang(mail)
|
844
|
+
body_data << mail.header['Content-Location']
|
845
|
+
end
|
846
|
+
else
|
847
|
+
if (mail.text?) then # body_type_text
|
848
|
+
# media_text
|
849
|
+
body_data << mail.media_main_type_upcase
|
850
|
+
body_data << mail.media_sub_type_upcase
|
851
|
+
|
852
|
+
# body_fields
|
853
|
+
body_data << make_body_params(mail.content_type_parameter_list)
|
854
|
+
body_data << mail.header['Content-Id']
|
855
|
+
body_data << mail.header['Content-Description']
|
856
|
+
body_data << mail.header.fetch_upcase('Content-Transfer-Encoding')
|
857
|
+
body_data << mail.raw_source.bytesize
|
858
|
+
|
859
|
+
# body_fld_lines
|
860
|
+
body_data << mail.raw_source.each_line.count
|
861
|
+
elsif (mail.message?) then # body_type_msg
|
862
|
+
# message_media
|
863
|
+
body_data << mail.media_main_type_upcase
|
864
|
+
body_data << mail.media_sub_type_upcase
|
865
|
+
|
866
|
+
# body_fields
|
867
|
+
body_data << make_body_params(mail.content_type_parameter_list)
|
868
|
+
body_data << mail.header['Content-Id']
|
869
|
+
body_data << mail.header['Content-Description']
|
870
|
+
body_data << mail.header.fetch_upcase('Content-Transfer-Encoding')
|
871
|
+
body_data << mail.raw_source.bytesize
|
872
|
+
|
873
|
+
# envelope
|
874
|
+
body_data << get_envelope_data(mail.message)
|
875
|
+
|
876
|
+
# body
|
877
|
+
body_data << get_bodystructure_data(mail.message, extension: extension)
|
878
|
+
|
879
|
+
# body_fld_lines
|
880
|
+
body_data << mail.raw_source.each_line.count
|
881
|
+
else # body_type_basic
|
882
|
+
# media_basic
|
883
|
+
body_data << mail.media_main_type_upcase
|
884
|
+
body_data << mail.media_sub_type_upcase
|
885
|
+
|
886
|
+
# body_fields
|
887
|
+
body_data << make_body_params(mail.content_type_parameter_list)
|
888
|
+
body_data << mail.header['Content-Id']
|
889
|
+
body_data << mail.header['Content-Description']
|
890
|
+
body_data << mail.header.fetch_upcase('Content-Transfer-Encoding')
|
891
|
+
body_data << mail.raw_source.bytesize
|
892
|
+
end
|
893
|
+
|
894
|
+
# body_ext_1part
|
895
|
+
if (extension) then
|
896
|
+
body_data << mail.header['Content-MD5']
|
897
|
+
body_data << get_body_disposition(mail)
|
898
|
+
body_data << get_body_lang(mail)
|
899
|
+
body_data << mail.header['Content-Location']
|
900
|
+
end
|
876
901
|
end
|
902
|
+
|
903
|
+
body_data
|
877
904
|
end
|
878
905
|
private :get_bodystructure_data
|
879
906
|
|
@@ -881,12 +908,12 @@ module RIMS
|
|
881
908
|
env_data = []
|
882
909
|
env_data << mail.header['Date']
|
883
910
|
env_data << mail.header['Subject']
|
884
|
-
env_data << mail.from
|
885
|
-
env_data << mail.sender
|
886
|
-
env_data << mail.reply_to
|
887
|
-
env_data << mail.to
|
888
|
-
env_data << mail.cc
|
889
|
-
env_data << mail.bcc
|
911
|
+
env_data << mail.from&.map(&:to_a)
|
912
|
+
env_data << mail.sender&.map(&:to_a)
|
913
|
+
env_data << mail.reply_to&.map(&:to_a)
|
914
|
+
env_data << mail.to&.map(&:to_a)
|
915
|
+
env_data << mail.cc&.map(&:to_a)
|
916
|
+
env_data << mail.bcc&.map(&:to_a)
|
890
917
|
env_data << mail.header['In-Reply-To']
|
891
918
|
env_data << mail.header['Message-Id']
|
892
919
|
end
|
@@ -1034,9 +1061,9 @@ module RIMS
|
|
1034
1061
|
end
|
1035
1062
|
private :parse_body
|
1036
1063
|
|
1037
|
-
def parse_bodystructure(msg_att_name)
|
1064
|
+
def parse_bodystructure(msg_att_name, extension: false)
|
1038
1065
|
proc{|msg|
|
1039
|
-
''.b << msg_att_name << ' '.b << encode_bodystructure(get_bodystructure_data(get_mail(msg)))
|
1066
|
+
''.b << msg_att_name << ' '.b << encode_bodystructure(get_bodystructure_data(get_mail(msg), extension: extension))
|
1040
1067
|
}
|
1041
1068
|
end
|
1042
1069
|
private :parse_bodystructure
|
@@ -1095,9 +1122,9 @@ module RIMS
|
|
1095
1122
|
when 'ALL'
|
1096
1123
|
fetch = expand_macro(%w[ FLAGS INTERNALDATE RFC822.SIZE ENVELOPE ])
|
1097
1124
|
when 'BODY'
|
1098
|
-
fetch = parse_bodystructure(fetch_att)
|
1125
|
+
fetch = parse_bodystructure(fetch_att, extension: false)
|
1099
1126
|
when 'BODYSTRUCTURE'
|
1100
|
-
fetch = parse_bodystructure(fetch_att)
|
1127
|
+
fetch = parse_bodystructure(fetch_att, extension: true)
|
1101
1128
|
when 'ENVELOPE'
|
1102
1129
|
fetch = parse_envelope(fetch_att)
|
1103
1130
|
when 'FAST'
|