net-imap 0.3.4 → 0.4.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.

Potentially problematic release.


This version of net-imap might be problematic. Click here for more details.

Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +46 -0
  3. data/.github/workflows/test.yml +12 -12
  4. data/Gemfile +1 -0
  5. data/README.md +15 -4
  6. data/Rakefile +0 -7
  7. data/benchmarks/generate_parser_benchmarks +52 -0
  8. data/benchmarks/parser.yml +578 -0
  9. data/benchmarks/stringprep.yml +1 -1
  10. data/lib/net/imap/authenticators.rb +26 -57
  11. data/lib/net/imap/command_data.rb +13 -6
  12. data/lib/net/imap/data_encoding.rb +3 -3
  13. data/lib/net/imap/deprecated_client_options.rb +139 -0
  14. data/lib/net/imap/response_data.rb +46 -41
  15. data/lib/net/imap/response_parser/parser_utils.rb +230 -0
  16. data/lib/net/imap/response_parser.rb +665 -627
  17. data/lib/net/imap/sasl/anonymous_authenticator.rb +68 -0
  18. data/lib/net/imap/sasl/authentication_exchange.rb +107 -0
  19. data/lib/net/imap/sasl/authenticators.rb +118 -0
  20. data/lib/net/imap/sasl/client_adapter.rb +72 -0
  21. data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +15 -9
  22. data/lib/net/imap/sasl/digest_md5_authenticator.rb +168 -0
  23. data/lib/net/imap/sasl/external_authenticator.rb +62 -0
  24. data/lib/net/imap/sasl/gs2_header.rb +80 -0
  25. data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +19 -14
  26. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +164 -0
  27. data/lib/net/imap/sasl/plain_authenticator.rb +93 -0
  28. data/lib/net/imap/sasl/protocol_adapters.rb +45 -0
  29. data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
  30. data/lib/net/imap/sasl/scram_authenticator.rb +278 -0
  31. data/lib/net/imap/sasl/stringprep.rb +6 -66
  32. data/lib/net/imap/sasl/xoauth2_authenticator.rb +88 -0
  33. data/lib/net/imap/sasl.rb +144 -43
  34. data/lib/net/imap/sasl_adapter.rb +21 -0
  35. data/lib/net/imap/stringprep/nameprep.rb +70 -0
  36. data/lib/net/imap/stringprep/saslprep.rb +69 -0
  37. data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
  38. data/lib/net/imap/stringprep/tables.rb +146 -0
  39. data/lib/net/imap/stringprep/trace.rb +85 -0
  40. data/lib/net/imap/stringprep.rb +159 -0
  41. data/lib/net/imap.rb +976 -590
  42. data/net-imap.gemspec +2 -2
  43. data/rakelib/saslprep.rake +4 -4
  44. data/rakelib/string_prep_tables_generator.rb +82 -60
  45. metadata +31 -12
  46. data/lib/net/imap/authenticators/digest_md5.rb +0 -115
  47. data/lib/net/imap/authenticators/plain.rb +0 -41
  48. data/lib/net/imap/authenticators/xoauth2.rb +0 -20
  49. data/lib/net/imap/sasl/saslprep.rb +0 -55
  50. data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
  51. data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
data/net-imap.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
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(">= 2.6.0")
19
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.3")
20
20
  spec.licenses = ["Ruby", "BSD-2-Clause"]
21
21
 
22
22
  spec.metadata["homepage_uri"] = spec.homepage
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  # Specify which files should be added to the gem when it is released.
26
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
27
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
- `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features)/}) }
28
+ `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features|rfcs)/}) }
29
29
  end
30
30
  spec.bindir = "exe"
31
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -10,17 +10,17 @@ end
10
10
 
11
11
  directory "lib/net/imap/sasl"
12
12
 
13
- file "lib/net/imap/sasl/stringprep_tables.rb" => generator.rb_deps do |t|
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/sasl/saslprep_tables.rb" => generator.rb_deps do |t|
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/sasl/stringprep_tables.rb",
23
- "lib/net/imap/sasl/saslprep_tables.rb",
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
@@ -62,9 +62,9 @@ class StringPrepTablesGenerator
62
62
  # This file is generated from RFC3454, by rake. Don't edit directly.
63
63
  #++
64
64
 
65
- module Net::IMAP::SASL
65
+ module Net::IMAP::StringPrep
66
66
 
67
- module StringPrep
67
+ module Tables
68
68
 
69
69
  #{asgn_table "A.1"}
70
70
 
@@ -74,6 +74,12 @@ class StringPrepTablesGenerator
74
74
 
75
75
  #{asgn_table "B.3"}
76
76
 
77
+ #{asgn_mapping "B.1", ""}
78
+
79
+ #{asgn_mapping "B.2"}
80
+
81
+ #{asgn_mapping "B.3"}
82
+
77
83
  #{asgn_table "C.1.1"}
78
84
 
79
85
  #{asgn_table "C.1.2"}
@@ -105,14 +111,16 @@ class StringPrepTablesGenerator
105
111
 
106
112
  BIDI_DESC_REQ2 = "A string with RandALCat characters must not contain LCat characters."
107
113
 
108
- # Bidirectional Characters [StringPrep, §6], Requirement 2::
114
+ # Bidirectional Characters [StringPrep, §6], Requirement 2
115
+ # >>>
109
116
  # If a string contains any RandALCat character, the string MUST NOT
110
117
  # contain any LCat character.
111
118
  BIDI_FAILS_REQ2 = #{bidi_fails_req2.inspect}.freeze
112
119
 
113
120
  BIDI_DESC_REQ3 = "A string with RandALCat characters must start and end with RandALCat characters."
114
121
 
115
- # Bidirectional Characters [StringPrep, §6], Requirement 3::
122
+ # Bidirectional Characters [StringPrep, §6], Requirement 3
123
+ # >>>
116
124
  # If a string contains any RandALCat character, a RandALCat
117
125
  # character MUST be the first character of the string, and a
118
126
  # RandALCat character MUST be the last character of the string.
@@ -122,15 +130,21 @@ class StringPrepTablesGenerator
122
130
  BIDI_FAILURE = #{bidi_failure_regexp.inspect}.freeze
123
131
 
124
132
  # Names of each codepoint table in the RFC-3454 appendices
125
- TABLE_TITLES = {
133
+ TITLES = {
126
134
  #{table_titles_rb}
127
135
  }.freeze
128
136
 
129
137
  # Regexps matching each codepoint table in the RFC-3454 appendices
130
- TABLE_REGEXPS = {
138
+ REGEXPS = {
131
139
  #{table_regexps_rb}
132
140
  }.freeze
133
141
 
142
+ MAPPINGS = {
143
+ "B.1" => [IN_B_1, MAP_B_1].freeze,
144
+ "B.2" => [IN_B_2, MAP_B_2].freeze,
145
+ "B.3" => [IN_B_3, MAP_B_3].freeze,
146
+ }.freeze
147
+
134
148
  end
135
149
  end
136
150
  RUBY
@@ -157,27 +171,30 @@ class StringPrepTablesGenerator
157
171
  # This file is generated from RFC3454, by rake. Don't edit directly.
158
172
  #++
159
173
 
160
- module Net::IMAP::SASL
174
+ module Net::IMAP::StringPrep
161
175
 
162
176
  module SASLprep
163
177
 
164
178
  # RFC4013 §2.1 Mapping - mapped to space
165
- # * non-ASCII space characters (\\StringPrep\\[\\"C.1.2\\"]) that can be
166
- # mapped to SPACE (U+0020), and
179
+ # >>>
180
+ # non-ASCII space characters (\\StringPrep\\[\\"C.1.2\\"]) that can
181
+ # be mapped to SPACE (U+0020)
167
182
  #
168
183
  # Equal to \\StringPrep\\[\\"C.1.2\\"].
169
- # Redefined here to avoid loading the StringPrep module.
184
+ # Redefined here to avoid loading StringPrep::Tables unless necessary.
170
185
  MAP_TO_SPACE = #{regex_str "C.1.2"}
171
186
 
172
187
  # RFC4013 §2.1 Mapping - mapped to nothing
173
- # the "commonly mapped to nothing" characters (\\StringPrep\\[\\"B.1\\"])
174
- # that can be mapped to nothing.
188
+ # >>>
189
+ # the "commonly mapped to nothing" characters
190
+ # (\\StringPrep\\[\\"B.1\\"]) that can be mapped to nothing.
175
191
  #
176
192
  # Equal to \\StringPrep\\[\\"B.1\\"].
177
- # Redefined here to avoid loading the StringPrep module.
193
+ # Redefined here to avoid loading StringPrep::Tables unless necessary.
178
194
  MAP_TO_NOTHING = #{regex_str "B.1"}
179
195
 
180
- # RFC4013 §2.3 Prohibited Output::
196
+ # RFC4013 §2.3 Prohibited Output
197
+ # >>>
181
198
  # * Non-ASCII space characters — \\StringPrep\\[\\"C.1.2\\"]
182
199
  # * ASCII control characters — \\StringPrep\\[\\"C.2.1\\"]
183
200
  # * Non-ASCII control characters — \\StringPrep\\[\\"C.2.2\\"]
@@ -192,45 +209,52 @@ class StringPrepTablesGenerator
192
209
 
193
210
  # Adds unassigned (by Unicode 3.2) codepoints to TABLES_PROHIBITED.
194
211
  #
195
- # RFC4013 §2.5 Unassigned Code Points::
196
- # This profile specifies the \\StringPrep\\[\\"A.1\\"] table as its list of
197
- # unassigned code points.
212
+ # RFC4013 §2.5 Unassigned Code Points
213
+ # >>>
214
+ # This profile specifies the \\StringPrep\\[\\"A.1\\"] table as its
215
+ # list of unassigned code points.
198
216
  TABLES_PROHIBITED_STORED = ["A.1", *TABLES_PROHIBITED].freeze
199
217
 
200
- # Matches codepoints prohibited by RFC4013 §2.3.
218
+ # A Regexp matching codepoints prohibited by RFC4013 §2.3.
201
219
  #
202
- # See TABLES_PROHIBITED.
203
- #
204
- # Equal to +Regexp.union+ of the TABLES_PROHIBITED tables. Redefined
205
- # here to avoid loading the StringPrep module unless necessary.
220
+ # This combines all of the TABLES_PROHIBITED tables.
206
221
  PROHIBITED_OUTPUT = #{regex_str(*SASL_TABLES_PROHIBITED)}
207
222
 
208
- # RFC4013 §2.5 Unassigned Code Points::
209
- # This profile specifies the \\StringPrep\\[\\"A.1\\"] table as its list of
210
- # unassigned code points.
223
+ # RFC4013 §2.5 Unassigned Code Points
224
+ # >>>
225
+ # This profile specifies the \\StringPrep\\[\\"A.1\\"] table as its
226
+ # list of unassigned code points.
227
+ #
228
+ # Equal to \\StringPrep\\[\\"A.1\\"].
229
+ # Redefined here to avoid loading StringPrep::Tables unless necessary.
211
230
  UNASSIGNED = #{regex_str "A.1"}
212
231
 
213
- # Matches codepoints prohibited by RFC4013 §2.3 and §2.5.
232
+ # A Regexp matching codepoints prohibited by RFC4013 §2.3 and §2.5.
214
233
  #
215
- # See TABLES_PROHIBITED_STORED.
234
+ # This combines PROHIBITED_OUTPUT and UNASSIGNED.
216
235
  PROHIBITED_OUTPUT_STORED = Regexp.union(
217
236
  UNASSIGNED, PROHIBITED_OUTPUT
218
237
  ).freeze
219
238
 
220
239
  # Bidirectional Characters [StringPrep, §6]
240
+ #
241
+ # A Regexp for strings that don't satisfy StringPrep's Bidirectional
242
+ # Characters rules.
243
+ #
244
+ # Equal to StringPrep::Tables::BIDI_FAILURE.
245
+ # Redefined here to avoid loading StringPrep::Tables unless necessary.
221
246
  BIDI_FAILURE = #{bidi_failure_regexp.inspect}.freeze
222
247
 
223
- # Matches strings prohibited by RFC4013 §2.3 and §2.4.
248
+ # A Regexp matching strings prohibited by RFC4013 §2.3 and §2.4.
224
249
  #
225
- # This checks prohibited output and bidirectional characters.
250
+ # This combines PROHIBITED_OUTPUT and BIDI_FAILURE.
226
251
  PROHIBITED = Regexp.union(
227
252
  PROHIBITED_OUTPUT, BIDI_FAILURE,
228
253
  )
229
254
 
230
- # Matches strings prohibited by RFC4013 §2.3, §2.4, and §2.5.
255
+ # A Regexp matching strings prohibited by RFC4013 §2.3, §2.4, and §2.5.
231
256
  #
232
- # This checks prohibited output, bidirectional characters, and
233
- # unassigned codepoints.
257
+ # This combines PROHIBITED_OUTPUT_STORED and BIDI_FAILURE.
234
258
  PROHIBITED_STORED = Regexp.union(
235
259
  PROHIBITED_OUTPUT_STORED, BIDI_FAILURE,
236
260
  )
@@ -284,6 +308,15 @@ class StringPrepTablesGenerator
284
308
  .map{|s,e| s..(e || s)}
285
309
  end
286
310
 
311
+ # TODO: DRY with unicode_normalize
312
+ def to_map(table)
313
+ table = table.to_hash
314
+ .transform_keys { Integer _1, 16 }
315
+ .transform_keys { [_1].pack("U*") }
316
+ .transform_values {|cps| cps.map { Integer _1, 16 } }
317
+ .transform_values { _1.pack("U*") }
318
+ end
319
+
287
320
  # Starting from a codepoints array (rather than ranges) to deduplicate merged
288
321
  # tables.
289
322
  def to_regexp(codepoints, negate: false)
@@ -352,6 +385,13 @@ class StringPrepTablesGenerator
352
385
  asgn_regex(name, regexp_for(name, negate: negate), negate: negate)
353
386
  end
354
387
 
388
+ def asgn_mapping(name, replacement = to_map(tables[name]))
389
+ cname = name.tr(?., ?_).upcase
390
+ "# Replacements for %s\n%s%s = %p.freeze" % [
391
+ "IN_#{name}", " " * 2, "MAP_#{cname}", replacement,
392
+ ]
393
+ end
394
+
355
395
  def regexp_const_desc(name, negate: false)
356
396
  if negate then "Matches the negation of the %s table" % [name]
357
397
  else %q{%s \\StringPrep\\[\\"%s\\"]} % [titles.fetch(name), name]
@@ -376,40 +416,22 @@ class StringPrepTablesGenerator
376
416
  def bidi_L ; regexp_for "D.2" end
377
417
 
378
418
  def bidi_fails_req2
379
- / # RandALCat followed by LCat
380
- (?<r_and_al_cat>#{bidi_R_AL.source})
381
- .*?
382
- (?<l_cat>#{bidi_L.source})
383
- | # RandALCat preceded by LCat
384
- \g<l_cat> .*? \g<r_and_al_cat>
385
- /mux
419
+ Regexp.union(
420
+ /#{bidi_R_AL}.*?#{bidi_L}/mu, # RandALCat followed by LCat
421
+ /#{bidi_L}.*?#{bidi_R_AL}/mu, # RandALCat preceded by LCat
422
+ )
386
423
  end
387
424
 
388
425
  def bidi_fails_req3
389
- / # contains RandALCat but doesn't start with RandALCat
390
- \A(?<not_r_nor_al>#{bidi_not_R_AL})
391
- .*?
392
- (?<r_and_al_cat>#{bidi_R_AL})
393
- | # contains RandALCat but doesn't end with RandALCat
394
- \g<r_and_al_cat> .*? \g<not_r_nor_al>\z
395
- /mux
426
+ # contains RandALCat:
427
+ Regexp.union(
428
+ /\A#{bidi_not_R_AL}.*?#{bidi_R_AL}/mu, # but doesn't start with RandALCat
429
+ /#{bidi_R_AL}.*?#{bidi_not_R_AL}\z/mu, # but doesn't end with RandALCat
430
+ )
396
431
  end
397
432
 
398
- # shares the bidi_R_AL definition between both req2 and req3
399
433
  def bidi_failure_regexp
400
- req3_with_backref = bidi_fails_req3.source
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
434
+ Regexp.union(bidi_fails_req2, bidi_fails_req3)
413
435
  end
414
436
 
415
437
  SASL_TABLES_PROHIBITED = %w[
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-12-23 00:00:00.000000000 Z
12
+ date: 2023-10-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-protocol
@@ -76,33 +76,52 @@ extensions: []
76
76
  extra_rdoc_files: []
77
77
  files:
78
78
  - ".github/dependabot.yml"
79
+ - ".github/workflows/pages.yml"
79
80
  - ".github/workflows/test.yml"
80
81
  - ".gitignore"
81
82
  - Gemfile
82
83
  - LICENSE.txt
83
84
  - README.md
84
85
  - Rakefile
86
+ - benchmarks/generate_parser_benchmarks
87
+ - benchmarks/parser.yml
85
88
  - benchmarks/stringprep.yml
86
89
  - benchmarks/table-regexps.yml
87
90
  - docs/styles.css
88
91
  - lib/net/imap.rb
89
92
  - lib/net/imap/authenticators.rb
90
- - lib/net/imap/authenticators/cram_md5.rb
91
- - lib/net/imap/authenticators/digest_md5.rb
92
- - lib/net/imap/authenticators/login.rb
93
- - lib/net/imap/authenticators/plain.rb
94
- - lib/net/imap/authenticators/xoauth2.rb
95
93
  - lib/net/imap/command_data.rb
96
94
  - lib/net/imap/data_encoding.rb
95
+ - lib/net/imap/deprecated_client_options.rb
97
96
  - lib/net/imap/errors.rb
98
97
  - lib/net/imap/flags.rb
99
98
  - lib/net/imap/response_data.rb
100
99
  - lib/net/imap/response_parser.rb
100
+ - lib/net/imap/response_parser/parser_utils.rb
101
101
  - lib/net/imap/sasl.rb
102
- - lib/net/imap/sasl/saslprep.rb
103
- - lib/net/imap/sasl/saslprep_tables.rb
102
+ - lib/net/imap/sasl/anonymous_authenticator.rb
103
+ - lib/net/imap/sasl/authentication_exchange.rb
104
+ - lib/net/imap/sasl/authenticators.rb
105
+ - lib/net/imap/sasl/client_adapter.rb
106
+ - lib/net/imap/sasl/cram_md5_authenticator.rb
107
+ - lib/net/imap/sasl/digest_md5_authenticator.rb
108
+ - lib/net/imap/sasl/external_authenticator.rb
109
+ - lib/net/imap/sasl/gs2_header.rb
110
+ - lib/net/imap/sasl/login_authenticator.rb
111
+ - lib/net/imap/sasl/oauthbearer_authenticator.rb
112
+ - lib/net/imap/sasl/plain_authenticator.rb
113
+ - lib/net/imap/sasl/protocol_adapters.rb
114
+ - lib/net/imap/sasl/scram_algorithm.rb
115
+ - lib/net/imap/sasl/scram_authenticator.rb
104
116
  - lib/net/imap/sasl/stringprep.rb
105
- - lib/net/imap/sasl/stringprep_tables.rb
117
+ - lib/net/imap/sasl/xoauth2_authenticator.rb
118
+ - lib/net/imap/sasl_adapter.rb
119
+ - lib/net/imap/stringprep.rb
120
+ - lib/net/imap/stringprep/nameprep.rb
121
+ - lib/net/imap/stringprep/saslprep.rb
122
+ - lib/net/imap/stringprep/saslprep_tables.rb
123
+ - lib/net/imap/stringprep/tables.rb
124
+ - lib/net/imap/stringprep/trace.rb
106
125
  - net-imap.gemspec
107
126
  - rakelib/rdoc.rake
108
127
  - rakelib/rfcs.rake
@@ -123,14 +142,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
142
  requirements:
124
143
  - - ">="
125
144
  - !ruby/object:Gem::Version
126
- version: 2.6.0
145
+ version: 2.7.3
127
146
  required_rubygems_version: !ruby/object:Gem::Requirement
128
147
  requirements:
129
148
  - - ">="
130
149
  - !ruby/object:Gem::Version
131
150
  version: '0'
132
151
  requirements: []
133
- rubygems_version: 3.4.0.dev
152
+ rubygems_version: 3.4.10
134
153
  signing_key:
135
154
  specification_version: 4
136
155
  summary: Ruby client api for Internet Message Access Protocol
@@ -1,115 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Net::IMAP authenticator for the "`DIGEST-MD5`" SASL mechanism type, specified
4
- # in RFC2831(https://tools.ietf.org/html/rfc2831). See Net::IMAP#authenticate.
5
- #
6
- # == Deprecated
7
- #
8
- # "+DIGEST-MD5+" has been deprecated by
9
- # {RFC6331}[https://tools.ietf.org/html/rfc6331] and should not be relied on for
10
- # security. It is included for compatibility with existing servers.
11
- class Net::IMAP::DigestMD5Authenticator
12
- def process(challenge)
13
- case @stage
14
- when STAGE_ONE
15
- @stage = STAGE_TWO
16
- sparams = {}
17
- c = StringScanner.new(challenge)
18
- while c.scan(/(?:\s*,)?\s*(\w+)=("(?:[^\\"]+|\\.)*"|[^,]+)\s*/)
19
- k, v = c[1], c[2]
20
- if v =~ /^"(.*)"$/
21
- v = $1
22
- if v =~ /,/
23
- v = v.split(',')
24
- end
25
- end
26
- sparams[k] = v
27
- end
28
-
29
- raise Net::IMAP::DataFormatError, "Bad Challenge: '#{challenge}'" unless c.eos?
30
- raise Net::IMAP::Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth")
31
-
32
- response = {
33
- :nonce => sparams['nonce'],
34
- :username => @user,
35
- :realm => sparams['realm'],
36
- :cnonce => Digest::MD5.hexdigest("%.15f:%.15f:%d" % [Time.now.to_f, rand, Process.pid.to_s]),
37
- :'digest-uri' => 'imap/' + sparams['realm'],
38
- :qop => 'auth',
39
- :maxbuf => 65535,
40
- :nc => "%08d" % nc(sparams['nonce']),
41
- :charset => sparams['charset'],
42
- }
43
-
44
- response[:authzid] = @authname unless @authname.nil?
45
-
46
- # now, the real thing
47
- a0 = Digest::MD5.digest( [ response.values_at(:username, :realm), @password ].join(':') )
48
-
49
- a1 = [ a0, response.values_at(:nonce,:cnonce) ].join(':')
50
- a1 << ':' + response[:authzid] unless response[:authzid].nil?
51
-
52
- a2 = "AUTHENTICATE:" + response[:'digest-uri']
53
- a2 << ":00000000000000000000000000000000" if response[:qop] and response[:qop] =~ /^auth-(?:conf|int)$/
54
-
55
- response[:response] = Digest::MD5.hexdigest(
56
- [
57
- Digest::MD5.hexdigest(a1),
58
- response.values_at(:nonce, :nc, :cnonce, :qop),
59
- Digest::MD5.hexdigest(a2)
60
- ].join(':')
61
- )
62
-
63
- return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',')
64
- when STAGE_TWO
65
- @stage = nil
66
- # if at the second stage, return an empty string
67
- if challenge =~ /rspauth=/
68
- return ''
69
- else
70
- raise ResponseParseError, challenge
71
- end
72
- else
73
- raise ResponseParseError, challenge
74
- end
75
- end
76
-
77
- def initialize(user, password, authname = nil, warn_deprecation: true)
78
- if warn_deprecation
79
- warn "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC6331."
80
- # TODO: recommend SCRAM instead.
81
- end
82
- require "digest/md5"
83
- require "strscan"
84
- @user, @password, @authname = user, password, authname
85
- @nc, @stage = {}, STAGE_ONE
86
- end
87
-
88
-
89
- private
90
-
91
- STAGE_ONE = :stage_one
92
- STAGE_TWO = :stage_two
93
-
94
- def nc(nonce)
95
- if @nc.has_key? nonce
96
- @nc[nonce] = @nc[nonce] + 1
97
- else
98
- @nc[nonce] = 1
99
- end
100
- return @nc[nonce]
101
- end
102
-
103
- # some responses need quoting
104
- def qdval(k, v)
105
- return if k.nil? or v.nil?
106
- if %w"username authzid realm nonce cnonce digest-uri qop".include? k
107
- v = v.gsub(/([\\"])/, "\\\1")
108
- return '%s="%s"' % [k, v]
109
- else
110
- return '%s=%s' % [k, v]
111
- end
112
- end
113
-
114
- Net::IMAP.add_authenticator "DIGEST-MD5", self
115
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Authenticator for the "+PLAIN+" SASL mechanism, specified in
4
- # RFC4616[https://tools.ietf.org/html/rfc4616]. See Net::IMAP#authenticate.
5
- #
6
- # +PLAIN+ authentication sends the password in cleartext.
7
- # RFC3501[https://tools.ietf.org/html/rfc3501] encourages servers to disable
8
- # cleartext authentication until after TLS has been negotiated.
9
- # RFC8314[https://tools.ietf.org/html/rfc8314] recommends TLS version 1.2 or
10
- # greater be used for all traffic, and deprecate cleartext access ASAP. +PLAIN+
11
- # can be secured by TLS encryption.
12
- class Net::IMAP::PlainAuthenticator
13
-
14
- def process(data)
15
- return "#@authzid\0#@username\0#@password"
16
- end
17
-
18
- # :nodoc:
19
- NULL = -"\0".b
20
-
21
- private
22
-
23
- # +username+ is the authentication identity, the identity whose +password+ is
24
- # used. +username+ is referred to as +authcid+ by
25
- # RFC4616[https://tools.ietf.org/html/rfc4616].
26
- #
27
- # +authzid+ is the authorization identity (identity to act as). It can
28
- # usually be left blank. When +authzid+ is left blank (nil or empty string)
29
- # the server will derive an identity from the credentials and use that as the
30
- # authorization identity.
31
- def initialize(username, password, authzid: nil)
32
- raise ArgumentError, "username contains NULL" if username&.include?(NULL)
33
- raise ArgumentError, "password contains NULL" if password&.include?(NULL)
34
- raise ArgumentError, "authzid contains NULL" if authzid&.include?(NULL)
35
- @username = username
36
- @password = password
37
- @authzid = authzid
38
- end
39
-
40
- Net::IMAP.add_authenticator "PLAIN", self
41
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Net::IMAP::XOauth2Authenticator
4
- def process(_data)
5
- build_oauth2_string(@user, @oauth2_token)
6
- end
7
-
8
- private
9
-
10
- def initialize(user, oauth2_token)
11
- @user = user
12
- @oauth2_token = oauth2_token
13
- end
14
-
15
- def build_oauth2_string(user, oauth2_token)
16
- format("user=%s\1auth=Bearer %s\1\1", user, oauth2_token)
17
- end
18
-
19
- Net::IMAP.add_authenticator 'XOAUTH2', self
20
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "saslprep_tables"
4
-
5
- module Net::IMAP::SASL
6
-
7
- # SASLprep#saslprep can be used to prepare a string according to [RFC4013].
8
- #
9
- # \SASLprep maps characters three ways: to nothing, to space, and Unicode
10
- # normalization form KC. \SASLprep prohibits codepoints from nearly all
11
- # standard StringPrep tables (RFC3454, Appendix "C"), and uses \StringPrep's
12
- # standard bidirectional characters requirements (Appendix "D"). \SASLprep
13
- # also uses \StringPrep's definition of "Unassigned" codepoints (Appendix "A").
14
- module SASLprep
15
-
16
- # Used to short-circuit strings that don't need preparation.
17
- ASCII_NO_CTRLS = /\A[\x20-\x7e]*\z/u.freeze
18
-
19
- module_function
20
-
21
- # Prepares a UTF-8 +string+ for comparison, using the \SASLprep profile
22
- # RFC4013 of the StringPrep algorithm RFC3454.
23
- #
24
- # By default, prohibited strings will return +nil+. When +exception+ is
25
- # +true+, a StringPrepError describing the violation will be raised.
26
- #
27
- # When +stored+ is +true+, "unassigned" codepoints will be prohibited. For
28
- # \StringPrep and the \SASLprep profile, "unassigned" refers to Unicode 3.2,
29
- # and not later versions. See RFC3454 §7 for more information.
30
- #
31
- def saslprep(str, stored: false, exception: false)
32
- return str if ASCII_NO_CTRLS.match?(str) # raises on incompatible encoding
33
- str = str.encode("UTF-8") # also dups (and raises for invalid encoding)
34
- str.gsub!(MAP_TO_SPACE, " ")
35
- str.gsub!(MAP_TO_NOTHING, "")
36
- str.unicode_normalize!(:nfkc)
37
- # These regexps combine the prohibited and bidirectional checks
38
- return str unless str.match?(stored ? PROHIBITED_STORED : PROHIBITED)
39
- return nil unless exception
40
- # raise helpful errors to indicate *why* it failed:
41
- tables = stored ? TABLES_PROHIBITED_STORED : TABLES_PROHIBITED
42
- StringPrep.check_prohibited! str, *tables, bidi: true, profile: "SASLprep"
43
- raise StringPrep::InvalidStringError.new(
44
- "unknown error", string: string, profile: "SASLprep"
45
- )
46
- rescue ArgumentError, Encoding::CompatibilityError => ex
47
- if /invalid byte sequence|incompatible encoding/.match? ex.message
48
- return nil unless exception
49
- raise StringPrepError.new(ex.message, string: str, profile: "saslprep")
50
- end
51
- raise ex
52
- end
53
-
54
- end
55
- end