net-imap 0.3.7 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +46 -0
  3. data/.github/workflows/test.yml +5 -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/deprecated_client_options.rb +139 -0
  13. data/lib/net/imap/response_data.rb +46 -41
  14. data/lib/net/imap/response_parser/parser_utils.rb +230 -0
  15. data/lib/net/imap/response_parser.rb +665 -627
  16. data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
  17. data/lib/net/imap/sasl/authentication_exchange.rb +107 -0
  18. data/lib/net/imap/sasl/authenticators.rb +118 -0
  19. data/lib/net/imap/sasl/client_adapter.rb +72 -0
  20. data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +21 -11
  21. data/lib/net/imap/sasl/digest_md5_authenticator.rb +180 -0
  22. data/lib/net/imap/sasl/external_authenticator.rb +83 -0
  23. data/lib/net/imap/sasl/gs2_header.rb +80 -0
  24. data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +25 -16
  25. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
  26. data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
  27. data/lib/net/imap/sasl/protocol_adapters.rb +45 -0
  28. data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
  29. data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
  30. data/lib/net/imap/sasl/stringprep.rb +6 -66
  31. data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
  32. data/lib/net/imap/sasl.rb +144 -43
  33. data/lib/net/imap/sasl_adapter.rb +21 -0
  34. data/lib/net/imap/stringprep/nameprep.rb +70 -0
  35. data/lib/net/imap/stringprep/saslprep.rb +69 -0
  36. data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
  37. data/lib/net/imap/stringprep/tables.rb +146 -0
  38. data/lib/net/imap/stringprep/trace.rb +85 -0
  39. data/lib/net/imap/stringprep.rb +159 -0
  40. data/lib/net/imap.rb +976 -590
  41. data/net-imap.gemspec +1 -1
  42. data/rakelib/saslprep.rake +4 -4
  43. data/rakelib/string_prep_tables_generator.rb +82 -60
  44. metadata +30 -11
  45. data/lib/net/imap/authenticators/digest_md5.rb +0 -115
  46. data/lib/net/imap/authenticators/plain.rb +0 -41
  47. data/lib/net/imap/authenticators/xoauth2.rb +0 -20
  48. data/lib/net/imap/sasl/saslprep.rb +0 -55
  49. data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
  50. 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
@@ -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.7
4
+ version: 0.4.2
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: 2023-07-26 00:00:00.000000000 Z
12
+ date: 2023-10-20 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,7 +142,7 @@ 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
  - - ">="
@@ -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? and sparams['qop']
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