net-imap 0.3.7 → 0.5.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/BSDL +22 -0
- data/COPYING +56 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +3 -22
- data/README.md +25 -8
- data/Rakefile +0 -7
- data/docs/styles.css +72 -23
- data/lib/net/imap/authenticators.rb +26 -57
- data/lib/net/imap/command_data.rb +74 -54
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +61 -0
- data/lib/net/imap/config.rb +470 -0
- data/lib/net/imap/data_encoding.rb +18 -6
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +142 -0
- data/lib/net/imap/errors.rb +27 -1
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +597 -0
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +250 -440
- data/lib/net/imap/response_parser/parser_utils.rb +245 -0
- data/lib/net/imap/response_parser.rb +1867 -1184
- data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
- data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
- data/lib/net/imap/sasl/authenticators.rb +122 -0
- data/lib/net/imap/sasl/client_adapter.rb +123 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
- data/lib/net/imap/sasl/external_authenticator.rb +83 -0
- data/lib/net/imap/sasl/gs2_header.rb +80 -0
- data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
- data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
- data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
- data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
- data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
- data/lib/net/imap/sasl/stringprep.rb +6 -66
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
- data/lib/net/imap/sasl.rb +148 -44
- data/lib/net/imap/sasl_adapter.rb +20 -0
- data/lib/net/imap/search_result.rb +146 -0
- data/lib/net/imap/sequence_set.rb +1565 -0
- data/lib/net/imap/stringprep/nameprep.rb +70 -0
- data/lib/net/imap/stringprep/saslprep.rb +69 -0
- data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
- data/lib/net/imap/stringprep/tables.rb +146 -0
- data/lib/net/imap/stringprep/trace.rb +85 -0
- data/lib/net/imap/stringprep.rb +159 -0
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +2090 -823
- data/net-imap.gemspec +7 -8
- data/rakelib/benchmarks.rake +91 -0
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +84 -60
- data/sample/net-imap.rb +167 -0
- metadata +45 -49
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/test.yml +0 -38
- data/.gitignore +0 -10
- data/benchmarks/stringprep.yml +0 -65
- data/benchmarks/table-regexps.yml +0 -39
- data/lib/net/imap/authenticators/digest_md5.rb +0 -115
- data/lib/net/imap/authenticators/plain.rb +0 -41
- data/lib/net/imap/authenticators/xoauth2.rb +0 -20
- data/lib/net/imap/sasl/saslprep.rb +0 -55
- data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
- data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
data/net-imap.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
name = File.basename(__FILE__, ".gemspec")
|
4
4
|
version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
|
5
|
-
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
|
5
|
+
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb"), :encoding=> 'utf-8') do |line|
|
6
6
|
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
|
7
7
|
end rescue nil
|
8
8
|
end
|
@@ -11,21 +11,23 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.name = name
|
12
12
|
spec.version = version
|
13
13
|
spec.authors = ["Shugo Maeda", "nicholas a. evans"]
|
14
|
-
spec.email = ["shugo@ruby-lang.org", "nick@
|
14
|
+
spec.email = ["shugo@ruby-lang.org", "nick@rubinick.dev"]
|
15
15
|
|
16
16
|
spec.summary = %q{Ruby client api for Internet Message Access Protocol}
|
17
17
|
spec.description = %q{Ruby client api for Internet Message Access Protocol}
|
18
18
|
spec.homepage = "https://github.com/ruby/net-imap"
|
19
|
-
spec.required_ruby_version = Gem::Requirement.new(">=
|
19
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
|
20
20
|
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
21
21
|
|
22
22
|
spec.metadata["homepage_uri"] = spec.homepage
|
23
23
|
spec.metadata["source_code_uri"] = spec.homepage
|
24
|
+
spec.metadata["changelog_uri"] = spec.homepage + "/releases"
|
24
25
|
|
25
26
|
# Specify which files should be added to the gem when it is released.
|
26
27
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
|
-
spec.files = Dir.chdir(
|
28
|
-
`git ls-files -z 2>/dev/null`.split("\x0")
|
28
|
+
spec.files = Dir.chdir(__dir__) do
|
29
|
+
`git ls-files -z 2>/dev/null`.split("\x0")
|
30
|
+
.grep_v(%r{^(\.git(ignore)?|\.mailmap|(\.github|bin|test|spec|benchmarks|features|rfcs)/)})
|
29
31
|
end
|
30
32
|
spec.bindir = "exe"
|
31
33
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -33,7 +35,4 @@ Gem::Specification.new do |spec|
|
|
33
35
|
|
34
36
|
spec.add_dependency "net-protocol"
|
35
37
|
spec.add_dependency "date"
|
36
|
-
|
37
|
-
spec.add_development_dependency "digest"
|
38
|
-
spec.add_development_dependency "strscan"
|
39
38
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
PARSER_TEST_FIXTURES = FileList.new "test/net/imap/fixtures/response_parser/*.yml"
|
4
|
+
CLOBBER.include "benchmarks/parser.yml"
|
5
|
+
CLEAN.include "benchmarks/Gemfile-*"
|
6
|
+
|
7
|
+
BENCHMARK_INIT = <<RUBY
|
8
|
+
require "yaml"
|
9
|
+
require "net/imap"
|
10
|
+
|
11
|
+
def load_response(file, name)
|
12
|
+
YAML.unsafe_load_file(file).dig(:tests, name, :response)
|
13
|
+
.force_encoding "ASCII-8BIT" \\
|
14
|
+
or abort "ERRORO: missing %p fixture data in %p" % [name, file]
|
15
|
+
end
|
16
|
+
|
17
|
+
parser = Net::IMAP::ResponseParser.new
|
18
|
+
RUBY
|
19
|
+
|
20
|
+
file "benchmarks/parser.yml" => PARSER_TEST_FIXTURES do |t|
|
21
|
+
require "yaml"
|
22
|
+
require "pathname"
|
23
|
+
require "net/imap"
|
24
|
+
|
25
|
+
path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
|
26
|
+
files = path.glob("*.yml")
|
27
|
+
tests = files.flat_map {|file|
|
28
|
+
file.read
|
29
|
+
.gsub(%r{([-:]) !ruby/(object|struct|array):\S+}) { $1 }
|
30
|
+
.then {
|
31
|
+
YAML.safe_load(_1, filename: file,
|
32
|
+
permitted_classes: [Symbol, Regexp], aliases: true)
|
33
|
+
}
|
34
|
+
.fetch(:tests)
|
35
|
+
.select {|test_name, test|
|
36
|
+
:parser_assert_equal == test.fetch(:test_type) {
|
37
|
+
test.key?(:expected) ? :parser_assert_equal : :parser_pending
|
38
|
+
}
|
39
|
+
}
|
40
|
+
.map {|test_name, test| [test_name.to_s, test.fetch(:response)] }
|
41
|
+
}
|
42
|
+
|
43
|
+
benchmarks = tests.map {|fixture_name, response|
|
44
|
+
{"name" => fixture_name.delete_prefix("test_"),
|
45
|
+
"prelude" => "response = -%s.b" % [response.dump],
|
46
|
+
"script" => "parser.parse(response)"}
|
47
|
+
}
|
48
|
+
.sort_by { _1["name"] }
|
49
|
+
|
50
|
+
YAML.dump({"prelude" => BENCHMARK_INIT, "benchmark" => benchmarks})
|
51
|
+
.then { File.write t.name, _1 }
|
52
|
+
end
|
53
|
+
|
54
|
+
namespace :benchmarks do
|
55
|
+
desc "Generate benchmarks from fixture data"
|
56
|
+
task :generate => "benchmarks/parser.yml"
|
57
|
+
|
58
|
+
desc "run the parser benchmarks comparing multiple gem versions"
|
59
|
+
task :compare => :generate do |task, args|
|
60
|
+
cd Pathname.new(__dir__) + ".."
|
61
|
+
current = `git describe --tags --dirty`.chomp
|
62
|
+
current = "dev" if current.empty?
|
63
|
+
versions = args.to_a
|
64
|
+
if versions.empty?
|
65
|
+
latest = %x{git describe --tags --abbrev=0 --match 'v*.*.*'}.chomp
|
66
|
+
versions = latest.empty? ? [] : [latest.delete_prefix("v")]
|
67
|
+
end
|
68
|
+
versions = versions.to_h { [_1, "Gemfile-v#{_1}"] }
|
69
|
+
cd "benchmarks" do
|
70
|
+
versions.each do |version, gemfile|
|
71
|
+
File.write gemfile, <<~RUBY
|
72
|
+
# frozen_string_literal: true
|
73
|
+
source "https://rubygems.org"
|
74
|
+
gem "net-imap", #{version.dump}
|
75
|
+
RUBY
|
76
|
+
end
|
77
|
+
versions = {current => "../Gemfile" , **versions}.map {
|
78
|
+
"%s::/usr/bin/env BUNDLE_GEMFILE=%s ruby" % _1
|
79
|
+
}.join(";")
|
80
|
+
|
81
|
+
extra = ENV.fetch("BENCHMARK_ARGS", "").shellsplit
|
82
|
+
|
83
|
+
sh("benchmark-driver",
|
84
|
+
"--bundler",
|
85
|
+
"-e", versions,
|
86
|
+
"parser.yml",
|
87
|
+
*extra)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/rakelib/rfcs.rake
CHANGED
data/rakelib/saslprep.rake
CHANGED
@@ -10,17 +10,17 @@ end
|
|
10
10
|
|
11
11
|
directory "lib/net/imap/sasl"
|
12
12
|
|
13
|
-
file "lib/net/imap/
|
13
|
+
file "lib/net/imap/stringprep/tables.rb" => generator.rb_deps do |t|
|
14
14
|
File.write t.name, generator.stringprep_rb
|
15
15
|
end
|
16
16
|
|
17
|
-
file "lib/net/imap/
|
17
|
+
file "lib/net/imap/stringprep/saslprep_tables.rb" => generator.rb_deps do |t|
|
18
18
|
File.write t.name, generator.saslprep_rb
|
19
19
|
end
|
20
20
|
|
21
21
|
GENERATED_RUBY = FileList.new(
|
22
|
-
"lib/net/imap/
|
23
|
-
"lib/net/imap/
|
22
|
+
"lib/net/imap/stringprep/tables.rb",
|
23
|
+
"lib/net/imap/stringprep/saslprep_tables.rb",
|
24
24
|
)
|
25
25
|
|
26
26
|
CLEAN.include generator.clean_deps
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set" unless defined?(::Set)
|
4
|
+
|
3
5
|
# Generator for stringprep regexps.
|
4
6
|
#
|
5
7
|
# Combines Unicode character classes with generated tables. Generated regexps
|
@@ -62,9 +64,9 @@ class StringPrepTablesGenerator
|
|
62
64
|
# This file is generated from RFC3454, by rake. Don't edit directly.
|
63
65
|
#++
|
64
66
|
|
65
|
-
module Net::IMAP::
|
67
|
+
module Net::IMAP::StringPrep
|
66
68
|
|
67
|
-
module
|
69
|
+
module Tables
|
68
70
|
|
69
71
|
#{asgn_table "A.1"}
|
70
72
|
|
@@ -74,6 +76,12 @@ class StringPrepTablesGenerator
|
|
74
76
|
|
75
77
|
#{asgn_table "B.3"}
|
76
78
|
|
79
|
+
#{asgn_mapping "B.1", ""}
|
80
|
+
|
81
|
+
#{asgn_mapping "B.2"}
|
82
|
+
|
83
|
+
#{asgn_mapping "B.3"}
|
84
|
+
|
77
85
|
#{asgn_table "C.1.1"}
|
78
86
|
|
79
87
|
#{asgn_table "C.1.2"}
|
@@ -105,14 +113,16 @@ class StringPrepTablesGenerator
|
|
105
113
|
|
106
114
|
BIDI_DESC_REQ2 = "A string with RandALCat characters must not contain LCat characters."
|
107
115
|
|
108
|
-
# Bidirectional Characters [StringPrep, §6], Requirement 2
|
116
|
+
# Bidirectional Characters [StringPrep, §6], Requirement 2
|
117
|
+
# >>>
|
109
118
|
# If a string contains any RandALCat character, the string MUST NOT
|
110
119
|
# contain any LCat character.
|
111
120
|
BIDI_FAILS_REQ2 = #{bidi_fails_req2.inspect}.freeze
|
112
121
|
|
113
122
|
BIDI_DESC_REQ3 = "A string with RandALCat characters must start and end with RandALCat characters."
|
114
123
|
|
115
|
-
# Bidirectional Characters [StringPrep, §6], Requirement 3
|
124
|
+
# Bidirectional Characters [StringPrep, §6], Requirement 3
|
125
|
+
# >>>
|
116
126
|
# If a string contains any RandALCat character, a RandALCat
|
117
127
|
# character MUST be the first character of the string, and a
|
118
128
|
# RandALCat character MUST be the last character of the string.
|
@@ -122,15 +132,21 @@ class StringPrepTablesGenerator
|
|
122
132
|
BIDI_FAILURE = #{bidi_failure_regexp.inspect}.freeze
|
123
133
|
|
124
134
|
# Names of each codepoint table in the RFC-3454 appendices
|
125
|
-
|
135
|
+
TITLES = {
|
126
136
|
#{table_titles_rb}
|
127
137
|
}.freeze
|
128
138
|
|
129
139
|
# Regexps matching each codepoint table in the RFC-3454 appendices
|
130
|
-
|
140
|
+
REGEXPS = {
|
131
141
|
#{table_regexps_rb}
|
132
142
|
}.freeze
|
133
143
|
|
144
|
+
MAPPINGS = {
|
145
|
+
"B.1" => [IN_B_1, MAP_B_1].freeze,
|
146
|
+
"B.2" => [IN_B_2, MAP_B_2].freeze,
|
147
|
+
"B.3" => [IN_B_3, MAP_B_3].freeze,
|
148
|
+
}.freeze
|
149
|
+
|
134
150
|
end
|
135
151
|
end
|
136
152
|
RUBY
|
@@ -157,27 +173,30 @@ class StringPrepTablesGenerator
|
|
157
173
|
# This file is generated from RFC3454, by rake. Don't edit directly.
|
158
174
|
#++
|
159
175
|
|
160
|
-
module Net::IMAP::
|
176
|
+
module Net::IMAP::StringPrep
|
161
177
|
|
162
178
|
module SASLprep
|
163
179
|
|
164
180
|
# RFC4013 §2.1 Mapping - mapped to space
|
165
|
-
#
|
166
|
-
#
|
181
|
+
# >>>
|
182
|
+
# non-ASCII space characters (\\StringPrep\\[\\"C.1.2\\"]) that can
|
183
|
+
# be mapped to SPACE (U+0020)
|
167
184
|
#
|
168
185
|
# Equal to \\StringPrep\\[\\"C.1.2\\"].
|
169
|
-
# Redefined here to avoid loading
|
186
|
+
# Redefined here to avoid loading StringPrep::Tables unless necessary.
|
170
187
|
MAP_TO_SPACE = #{regex_str "C.1.2"}
|
171
188
|
|
172
189
|
# RFC4013 §2.1 Mapping - mapped to nothing
|
173
|
-
#
|
174
|
-
#
|
190
|
+
# >>>
|
191
|
+
# the "commonly mapped to nothing" characters
|
192
|
+
# (\\StringPrep\\[\\"B.1\\"]) that can be mapped to nothing.
|
175
193
|
#
|
176
194
|
# Equal to \\StringPrep\\[\\"B.1\\"].
|
177
|
-
# Redefined here to avoid loading
|
195
|
+
# Redefined here to avoid loading StringPrep::Tables unless necessary.
|
178
196
|
MAP_TO_NOTHING = #{regex_str "B.1"}
|
179
197
|
|
180
|
-
# RFC4013 §2.3 Prohibited Output
|
198
|
+
# RFC4013 §2.3 Prohibited Output
|
199
|
+
# >>>
|
181
200
|
# * Non-ASCII space characters — \\StringPrep\\[\\"C.1.2\\"]
|
182
201
|
# * ASCII control characters — \\StringPrep\\[\\"C.2.1\\"]
|
183
202
|
# * Non-ASCII control characters — \\StringPrep\\[\\"C.2.2\\"]
|
@@ -192,45 +211,52 @@ class StringPrepTablesGenerator
|
|
192
211
|
|
193
212
|
# Adds unassigned (by Unicode 3.2) codepoints to TABLES_PROHIBITED.
|
194
213
|
#
|
195
|
-
# RFC4013 §2.5 Unassigned Code Points
|
196
|
-
#
|
197
|
-
#
|
214
|
+
# RFC4013 §2.5 Unassigned Code Points
|
215
|
+
# >>>
|
216
|
+
# This profile specifies the \\StringPrep\\[\\"A.1\\"] table as its
|
217
|
+
# list of unassigned code points.
|
198
218
|
TABLES_PROHIBITED_STORED = ["A.1", *TABLES_PROHIBITED].freeze
|
199
219
|
|
200
|
-
#
|
220
|
+
# A Regexp matching codepoints prohibited by RFC4013 §2.3.
|
201
221
|
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
# Equal to +Regexp.union+ of the TABLES_PROHIBITED tables. Redefined
|
205
|
-
# here to avoid loading the StringPrep module unless necessary.
|
222
|
+
# This combines all of the TABLES_PROHIBITED tables.
|
206
223
|
PROHIBITED_OUTPUT = #{regex_str(*SASL_TABLES_PROHIBITED)}
|
207
224
|
|
208
|
-
# RFC4013 §2.5 Unassigned Code Points
|
209
|
-
#
|
210
|
-
#
|
225
|
+
# RFC4013 §2.5 Unassigned Code Points
|
226
|
+
# >>>
|
227
|
+
# This profile specifies the \\StringPrep\\[\\"A.1\\"] table as its
|
228
|
+
# list of unassigned code points.
|
229
|
+
#
|
230
|
+
# Equal to \\StringPrep\\[\\"A.1\\"].
|
231
|
+
# Redefined here to avoid loading StringPrep::Tables unless necessary.
|
211
232
|
UNASSIGNED = #{regex_str "A.1"}
|
212
233
|
|
213
|
-
#
|
234
|
+
# A Regexp matching codepoints prohibited by RFC4013 §2.3 and §2.5.
|
214
235
|
#
|
215
|
-
#
|
236
|
+
# This combines PROHIBITED_OUTPUT and UNASSIGNED.
|
216
237
|
PROHIBITED_OUTPUT_STORED = Regexp.union(
|
217
238
|
UNASSIGNED, PROHIBITED_OUTPUT
|
218
239
|
).freeze
|
219
240
|
|
220
241
|
# Bidirectional Characters [StringPrep, §6]
|
242
|
+
#
|
243
|
+
# A Regexp for strings that don't satisfy StringPrep's Bidirectional
|
244
|
+
# Characters rules.
|
245
|
+
#
|
246
|
+
# Equal to StringPrep::Tables::BIDI_FAILURE.
|
247
|
+
# Redefined here to avoid loading StringPrep::Tables unless necessary.
|
221
248
|
BIDI_FAILURE = #{bidi_failure_regexp.inspect}.freeze
|
222
249
|
|
223
|
-
#
|
250
|
+
# A Regexp matching strings prohibited by RFC4013 §2.3 and §2.4.
|
224
251
|
#
|
225
|
-
# This
|
252
|
+
# This combines PROHIBITED_OUTPUT and BIDI_FAILURE.
|
226
253
|
PROHIBITED = Regexp.union(
|
227
254
|
PROHIBITED_OUTPUT, BIDI_FAILURE,
|
228
255
|
)
|
229
256
|
|
230
|
-
#
|
257
|
+
# A Regexp matching strings prohibited by RFC4013 §2.3, §2.4, and §2.5.
|
231
258
|
#
|
232
|
-
# This
|
233
|
-
# unassigned codepoints.
|
259
|
+
# This combines PROHIBITED_OUTPUT_STORED and BIDI_FAILURE.
|
234
260
|
PROHIBITED_STORED = Regexp.union(
|
235
261
|
PROHIBITED_OUTPUT_STORED, BIDI_FAILURE,
|
236
262
|
)
|
@@ -284,6 +310,15 @@ class StringPrepTablesGenerator
|
|
284
310
|
.map{|s,e| s..(e || s)}
|
285
311
|
end
|
286
312
|
|
313
|
+
# TODO: DRY with unicode_normalize
|
314
|
+
def to_map(table)
|
315
|
+
table = table.to_hash
|
316
|
+
.transform_keys { Integer _1, 16 }
|
317
|
+
.transform_keys { [_1].pack("U*") }
|
318
|
+
.transform_values {|cps| cps.map { Integer _1, 16 } }
|
319
|
+
.transform_values { _1.pack("U*") }
|
320
|
+
end
|
321
|
+
|
287
322
|
# Starting from a codepoints array (rather than ranges) to deduplicate merged
|
288
323
|
# tables.
|
289
324
|
def to_regexp(codepoints, negate: false)
|
@@ -352,6 +387,13 @@ class StringPrepTablesGenerator
|
|
352
387
|
asgn_regex(name, regexp_for(name, negate: negate), negate: negate)
|
353
388
|
end
|
354
389
|
|
390
|
+
def asgn_mapping(name, replacement = to_map(tables[name]))
|
391
|
+
cname = name.tr(?., ?_).upcase
|
392
|
+
"# Replacements for %s\n%s%s = %p.freeze" % [
|
393
|
+
"IN_#{name}", " " * 2, "MAP_#{cname}", replacement,
|
394
|
+
]
|
395
|
+
end
|
396
|
+
|
355
397
|
def regexp_const_desc(name, negate: false)
|
356
398
|
if negate then "Matches the negation of the %s table" % [name]
|
357
399
|
else %q{%s \\StringPrep\\[\\"%s\\"]} % [titles.fetch(name), name]
|
@@ -376,40 +418,22 @@ class StringPrepTablesGenerator
|
|
376
418
|
def bidi_L ; regexp_for "D.2" end
|
377
419
|
|
378
420
|
def bidi_fails_req2
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
| # RandALCat preceded by LCat
|
384
|
-
\g<l_cat> .*? \g<r_and_al_cat>
|
385
|
-
/mux
|
421
|
+
Regexp.union(
|
422
|
+
/#{bidi_R_AL}.*?#{bidi_L}/mu, # RandALCat followed by LCat
|
423
|
+
/#{bidi_L}.*?#{bidi_R_AL}/mu, # RandALCat preceded by LCat
|
424
|
+
)
|
386
425
|
end
|
387
426
|
|
388
427
|
def bidi_fails_req3
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
\g<r_and_al_cat> .*? \g<not_r_nor_al>\z
|
395
|
-
/mux
|
428
|
+
# contains RandALCat:
|
429
|
+
Regexp.union(
|
430
|
+
/\A#{bidi_not_R_AL}.*?#{bidi_R_AL}/mu, # but doesn't start with RandALCat
|
431
|
+
/#{bidi_R_AL}.*?#{bidi_not_R_AL}\z/mu, # but doesn't end with RandALCat
|
432
|
+
)
|
396
433
|
end
|
397
434
|
|
398
|
-
# shares the bidi_R_AL definition between both req2 and req3
|
399
435
|
def bidi_failure_regexp
|
400
|
-
|
401
|
-
.gsub(%r{\(\?\<r_and_al_cat\>\(.*?\)\)}, "\g<r_and_al_cat>")
|
402
|
-
.then{|re|"(?mx-i:#{re})"}
|
403
|
-
/#{bidi_fails_req2} | #{req3_with_backref}/mux
|
404
|
-
end
|
405
|
-
|
406
|
-
def bidi_consts
|
407
|
-
<<~RUBY
|
408
|
-
#############
|
409
|
-
# Bidirectional checks.
|
410
|
-
#
|
411
|
-
|
412
|
-
RUBY
|
436
|
+
Regexp.union(bidi_fails_req2, bidi_fails_req3)
|
413
437
|
end
|
414
438
|
|
415
439
|
SASL_TABLES_PROHIBITED = %w[
|
data/sample/net-imap.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'net/imap'
|
2
|
+
require "getoptlong"
|
3
|
+
|
4
|
+
$stdout.sync = true
|
5
|
+
$port = nil
|
6
|
+
$user = ENV["USER"] || ENV["LOGNAME"]
|
7
|
+
$auth = "login"
|
8
|
+
$ssl = false
|
9
|
+
$starttls = false
|
10
|
+
|
11
|
+
def usage
|
12
|
+
<<EOF
|
13
|
+
usage: #{$0} [options] <host>
|
14
|
+
|
15
|
+
--help print this message
|
16
|
+
--port=PORT specifies port
|
17
|
+
--user=USER specifies user
|
18
|
+
--auth=AUTH specifies auth type
|
19
|
+
--starttls use starttls
|
20
|
+
--ssl use ssl
|
21
|
+
EOF
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'io/console'
|
26
|
+
rescue LoadError
|
27
|
+
def _noecho(&block)
|
28
|
+
system("stty", "-echo")
|
29
|
+
begin
|
30
|
+
yield STDIN
|
31
|
+
ensure
|
32
|
+
system("stty", "echo")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
else
|
36
|
+
def _noecho(&block)
|
37
|
+
STDIN.noecho(&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_password
|
42
|
+
print "password: "
|
43
|
+
begin
|
44
|
+
return _noecho(&:gets).chomp
|
45
|
+
ensure
|
46
|
+
puts
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_command
|
51
|
+
printf("%s@%s> ", $user, $host)
|
52
|
+
if line = gets
|
53
|
+
return line.strip.split(/\s+/)
|
54
|
+
else
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
parser = GetoptLong.new
|
60
|
+
parser.set_options(['--debug', GetoptLong::NO_ARGUMENT],
|
61
|
+
['--help', GetoptLong::NO_ARGUMENT],
|
62
|
+
['--port', GetoptLong::REQUIRED_ARGUMENT],
|
63
|
+
['--user', GetoptLong::REQUIRED_ARGUMENT],
|
64
|
+
['--auth', GetoptLong::REQUIRED_ARGUMENT],
|
65
|
+
['--starttls', GetoptLong::NO_ARGUMENT],
|
66
|
+
['--ssl', GetoptLong::NO_ARGUMENT])
|
67
|
+
begin
|
68
|
+
parser.each_option do |name, arg|
|
69
|
+
case name
|
70
|
+
when "--port"
|
71
|
+
$port = arg
|
72
|
+
when "--user"
|
73
|
+
$user = arg
|
74
|
+
when "--auth"
|
75
|
+
$auth = arg
|
76
|
+
when "--ssl"
|
77
|
+
$ssl = true
|
78
|
+
when "--starttls"
|
79
|
+
$starttls = true
|
80
|
+
when "--debug"
|
81
|
+
Net::IMAP.debug = true
|
82
|
+
when "--help"
|
83
|
+
usage
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
end
|
87
|
+
rescue
|
88
|
+
abort usage
|
89
|
+
end
|
90
|
+
|
91
|
+
$host = ARGV.shift
|
92
|
+
unless $host
|
93
|
+
abort usage
|
94
|
+
end
|
95
|
+
|
96
|
+
imap = Net::IMAP.new($host, :port => $port, :ssl => $ssl)
|
97
|
+
begin
|
98
|
+
imap.starttls if $starttls
|
99
|
+
class << password = method(:get_password)
|
100
|
+
alias to_str call
|
101
|
+
end
|
102
|
+
imap.authenticate($auth, $user, password)
|
103
|
+
while true
|
104
|
+
cmd, *args = get_command
|
105
|
+
break unless cmd
|
106
|
+
begin
|
107
|
+
case cmd
|
108
|
+
when "list"
|
109
|
+
for mbox in imap.list("", args[0] || "*")
|
110
|
+
if mbox.attr.include?(Net::IMAP::NOSELECT)
|
111
|
+
prefix = "!"
|
112
|
+
elsif mbox.attr.include?(Net::IMAP::MARKED)
|
113
|
+
prefix = "*"
|
114
|
+
else
|
115
|
+
prefix = " "
|
116
|
+
end
|
117
|
+
print prefix, mbox.name, "\n"
|
118
|
+
end
|
119
|
+
when "select"
|
120
|
+
imap.select(args[0] || "inbox")
|
121
|
+
print "ok\n"
|
122
|
+
when "close"
|
123
|
+
imap.close
|
124
|
+
print "ok\n"
|
125
|
+
when "summary"
|
126
|
+
unless messages = imap.responses["EXISTS"][-1]
|
127
|
+
puts "not selected"
|
128
|
+
next
|
129
|
+
end
|
130
|
+
if messages > 0
|
131
|
+
for data in imap.fetch(1..-1, ["ENVELOPE"])
|
132
|
+
print data.seqno, ": ", data.attr["ENVELOPE"].subject, "\n"
|
133
|
+
end
|
134
|
+
else
|
135
|
+
puts "no message"
|
136
|
+
end
|
137
|
+
when "fetch"
|
138
|
+
if args[0]
|
139
|
+
data = imap.fetch(args[0].to_i, ["RFC822.HEADER", "RFC822.TEXT"])[0]
|
140
|
+
puts data.attr["RFC822.HEADER"]
|
141
|
+
puts data.attr["RFC822.TEXT"]
|
142
|
+
else
|
143
|
+
puts "missing argument"
|
144
|
+
end
|
145
|
+
when "logout", "exit", "quit"
|
146
|
+
break
|
147
|
+
when "help", "?"
|
148
|
+
print <<EOF
|
149
|
+
list [pattern] list mailboxes
|
150
|
+
select [mailbox] select mailbox
|
151
|
+
close close mailbox
|
152
|
+
summary display summary
|
153
|
+
fetch [msgno] display message
|
154
|
+
logout logout
|
155
|
+
help, ? display help message
|
156
|
+
EOF
|
157
|
+
else
|
158
|
+
print "unknown command: ", cmd, "\n"
|
159
|
+
end
|
160
|
+
rescue Net::IMAP::Error
|
161
|
+
puts $!
|
162
|
+
end
|
163
|
+
end
|
164
|
+
ensure
|
165
|
+
imap.logout
|
166
|
+
imap.disconnect
|
167
|
+
end
|