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

Potentially problematic release.


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

Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/COPYING +56 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +3 -22
  6. data/README.md +25 -8
  7. data/Rakefile +0 -7
  8. data/docs/styles.css +72 -23
  9. data/lib/net/imap/authenticators.rb +26 -57
  10. data/lib/net/imap/command_data.rb +74 -54
  11. data/lib/net/imap/config/attr_accessors.rb +75 -0
  12. data/lib/net/imap/config/attr_inheritance.rb +90 -0
  13. data/lib/net/imap/config/attr_type_coercion.rb +61 -0
  14. data/lib/net/imap/config.rb +470 -0
  15. data/lib/net/imap/data_encoding.rb +18 -6
  16. data/lib/net/imap/data_lite.rb +226 -0
  17. data/lib/net/imap/deprecated_client_options.rb +142 -0
  18. data/lib/net/imap/errors.rb +27 -35
  19. data/lib/net/imap/esearch_result.rb +180 -0
  20. data/lib/net/imap/fetch_data.rb +597 -0
  21. data/lib/net/imap/flags.rb +1 -1
  22. data/lib/net/imap/response_data.rb +250 -440
  23. data/lib/net/imap/response_parser/parser_utils.rb +245 -0
  24. data/lib/net/imap/response_parser.rb +1873 -1210
  25. data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
  26. data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
  27. data/lib/net/imap/sasl/authenticators.rb +122 -0
  28. data/lib/net/imap/sasl/client_adapter.rb +123 -0
  29. data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
  30. data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
  31. data/lib/net/imap/sasl/external_authenticator.rb +83 -0
  32. data/lib/net/imap/sasl/gs2_header.rb +80 -0
  33. data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
  34. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
  35. data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
  36. data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
  37. data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
  38. data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
  39. data/lib/net/imap/sasl/stringprep.rb +6 -66
  40. data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
  41. data/lib/net/imap/sasl.rb +148 -44
  42. data/lib/net/imap/sasl_adapter.rb +20 -0
  43. data/lib/net/imap/search_result.rb +146 -0
  44. data/lib/net/imap/sequence_set.rb +1565 -0
  45. data/lib/net/imap/stringprep/nameprep.rb +70 -0
  46. data/lib/net/imap/stringprep/saslprep.rb +69 -0
  47. data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
  48. data/lib/net/imap/stringprep/tables.rb +146 -0
  49. data/lib/net/imap/stringprep/trace.rb +85 -0
  50. data/lib/net/imap/stringprep.rb +159 -0
  51. data/lib/net/imap/uidplus_data.rb +244 -0
  52. data/lib/net/imap/vanished_data.rb +56 -0
  53. data/lib/net/imap.rb +2109 -924
  54. data/net-imap.gemspec +7 -8
  55. data/rakelib/benchmarks.rake +91 -0
  56. data/rakelib/rfcs.rake +2 -0
  57. data/rakelib/saslprep.rake +4 -4
  58. data/rakelib/string_prep_tables_generator.rb +84 -60
  59. data/sample/net-imap.rb +167 -0
  60. metadata +45 -47
  61. data/.github/dependabot.yml +0 -6
  62. data/.github/workflows/test.yml +0 -38
  63. data/.gitignore +0 -10
  64. data/benchmarks/stringprep.yml +0 -65
  65. data/benchmarks/table-regexps.yml +0 -39
  66. data/lib/net/imap/authenticators/digest_md5.rb +0 -115
  67. data/lib/net/imap/authenticators/plain.rb +0 -41
  68. data/lib/net/imap/authenticators/xoauth2.rb +0 -20
  69. data/lib/net/imap/response_reader.rb +0 -75
  70. data/lib/net/imap/sasl/saslprep.rb +0 -55
  71. data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
  72. data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.9
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  - nicholas a. evans
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2025-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-protocol
@@ -38,76 +38,73 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: digest
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: strscan
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
41
  description: Ruby client api for Internet Message Access Protocol
70
42
  email:
71
43
  - shugo@ruby-lang.org
72
- - nick@ekenosen.net
44
+ - nick@rubinick.dev
73
45
  executables: []
74
46
  extensions: []
75
47
  extra_rdoc_files: []
76
48
  files:
77
- - ".github/dependabot.yml"
78
- - ".github/workflows/test.yml"
79
- - ".gitignore"
49
+ - BSDL
50
+ - COPYING
80
51
  - Gemfile
81
52
  - LICENSE.txt
82
53
  - README.md
83
54
  - Rakefile
84
- - benchmarks/stringprep.yml
85
- - benchmarks/table-regexps.yml
86
55
  - docs/styles.css
87
56
  - lib/net/imap.rb
88
57
  - lib/net/imap/authenticators.rb
89
- - lib/net/imap/authenticators/cram_md5.rb
90
- - lib/net/imap/authenticators/digest_md5.rb
91
- - lib/net/imap/authenticators/login.rb
92
- - lib/net/imap/authenticators/plain.rb
93
- - lib/net/imap/authenticators/xoauth2.rb
94
58
  - lib/net/imap/command_data.rb
59
+ - lib/net/imap/config.rb
60
+ - lib/net/imap/config/attr_accessors.rb
61
+ - lib/net/imap/config/attr_inheritance.rb
62
+ - lib/net/imap/config/attr_type_coercion.rb
95
63
  - lib/net/imap/data_encoding.rb
64
+ - lib/net/imap/data_lite.rb
65
+ - lib/net/imap/deprecated_client_options.rb
96
66
  - lib/net/imap/errors.rb
67
+ - lib/net/imap/esearch_result.rb
68
+ - lib/net/imap/fetch_data.rb
97
69
  - lib/net/imap/flags.rb
98
70
  - lib/net/imap/response_data.rb
99
71
  - lib/net/imap/response_parser.rb
100
- - lib/net/imap/response_reader.rb
72
+ - lib/net/imap/response_parser/parser_utils.rb
101
73
  - lib/net/imap/sasl.rb
102
- - lib/net/imap/sasl/saslprep.rb
103
- - lib/net/imap/sasl/saslprep_tables.rb
74
+ - lib/net/imap/sasl/anonymous_authenticator.rb
75
+ - lib/net/imap/sasl/authentication_exchange.rb
76
+ - lib/net/imap/sasl/authenticators.rb
77
+ - lib/net/imap/sasl/client_adapter.rb
78
+ - lib/net/imap/sasl/cram_md5_authenticator.rb
79
+ - lib/net/imap/sasl/digest_md5_authenticator.rb
80
+ - lib/net/imap/sasl/external_authenticator.rb
81
+ - lib/net/imap/sasl/gs2_header.rb
82
+ - lib/net/imap/sasl/login_authenticator.rb
83
+ - lib/net/imap/sasl/oauthbearer_authenticator.rb
84
+ - lib/net/imap/sasl/plain_authenticator.rb
85
+ - lib/net/imap/sasl/protocol_adapters.rb
86
+ - lib/net/imap/sasl/scram_algorithm.rb
87
+ - lib/net/imap/sasl/scram_authenticator.rb
104
88
  - lib/net/imap/sasl/stringprep.rb
105
- - lib/net/imap/sasl/stringprep_tables.rb
89
+ - lib/net/imap/sasl/xoauth2_authenticator.rb
90
+ - lib/net/imap/sasl_adapter.rb
91
+ - lib/net/imap/search_result.rb
92
+ - lib/net/imap/sequence_set.rb
93
+ - lib/net/imap/stringprep.rb
94
+ - lib/net/imap/stringprep/nameprep.rb
95
+ - lib/net/imap/stringprep/saslprep.rb
96
+ - lib/net/imap/stringprep/saslprep_tables.rb
97
+ - lib/net/imap/stringprep/tables.rb
98
+ - lib/net/imap/stringprep/trace.rb
99
+ - lib/net/imap/uidplus_data.rb
100
+ - lib/net/imap/vanished_data.rb
106
101
  - net-imap.gemspec
102
+ - rakelib/benchmarks.rake
107
103
  - rakelib/rdoc.rake
108
104
  - rakelib/rfcs.rake
109
105
  - rakelib/saslprep.rake
110
106
  - rakelib/string_prep_tables_generator.rb
107
+ - sample/net-imap.rb
111
108
  homepage: https://github.com/ruby/net-imap
112
109
  licenses:
113
110
  - Ruby
@@ -115,6 +112,7 @@ licenses:
115
112
  metadata:
116
113
  homepage_uri: https://github.com/ruby/net-imap
117
114
  source_code_uri: https://github.com/ruby/net-imap
115
+ changelog_uri: https://github.com/ruby/net-imap/releases
118
116
  rdoc_options: []
119
117
  require_paths:
120
118
  - lib
@@ -122,14 +120,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
122
120
  requirements:
123
121
  - - ">="
124
122
  - !ruby/object:Gem::Version
125
- version: 2.6.0
123
+ version: 3.1.0
126
124
  required_rubygems_version: !ruby/object:Gem::Requirement
127
125
  requirements:
128
126
  - - ">="
129
127
  - !ruby/object:Gem::Version
130
128
  version: '0'
131
129
  requirements: []
132
- rubygems_version: 3.6.8
130
+ rubygems_version: 3.6.2
133
131
  specification_version: 4
134
132
  summary: Ruby client api for Internet Message Access Protocol
135
133
  test_files: []
@@ -1,6 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: 'github-actions'
4
- directory: '/'
5
- schedule:
6
- interval: 'weekly'
@@ -1,38 +0,0 @@
1
- name: ubuntu
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- ruby-versions:
7
- uses: ruby/actions/.github/workflows/ruby_versions.yml@master
8
- with:
9
- engine: cruby
10
- min_version: 2.6
11
-
12
- build:
13
- needs: ruby-versions
14
- name: build (${{ matrix.ruby }} / ${{ matrix.os }})
15
- strategy:
16
- matrix:
17
- ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
18
- os: [ ubuntu-latest, macos-latest ]
19
- experimental: [false]
20
- include:
21
- # - ruby: 2.6
22
- # os: ubuntu-latest
23
- # experimental: true
24
- - ruby: 2.6
25
- os: macos-latest
26
- experimental: false
27
- runs-on: ${{ matrix.os }}
28
- continue-on-error: ${{ matrix.experimental }}
29
- steps:
30
- - uses: actions/checkout@v3
31
- - name: Set up Ruby
32
- uses: ruby/setup-ruby@v1
33
- with:
34
- ruby-version: ${{ matrix.ruby }}
35
- - name: Install dependencies
36
- run: bundle install
37
- - name: Run test
38
- run: rake test
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /rfcs
8
- /spec/reports/
9
- /tmp/
10
- /Gemfile.lock
@@ -1,65 +0,0 @@
1
- ---
2
- prelude: |
3
- begin
4
- require "mongo" # gem install mongo
5
- require "idn" # gem install idn-ruby
6
- rescue LoadError
7
- warn "You must 'gem install mongo idn-ruby' for this benchmark."
8
- raise
9
- end
10
-
11
- MStrPrep = Mongo::Auth::StringPrep
12
-
13
- # this indirection will slow it down a little bit
14
- def mongo_saslprep(string)
15
- MStrPrep.prepare(string,
16
- MStrPrep::Profiles::SASL::MAPPINGS,
17
- MStrPrep::Profiles::SASL::PROHIBITED,
18
- normalize: true,
19
- bidi: true)
20
- rescue Mongo::Error::FailedStringPrepValidation
21
- nil
22
- end
23
-
24
- $LOAD_PATH.unshift "./lib"
25
- require "net/imap"
26
- def net_imap_saslprep(string)
27
- Net::IMAP::SASL::SASLprep.saslprep string, exception: false
28
- end
29
-
30
- def libidn_saslprep(string)
31
- IDN::Stringprep.with_profile(string, "SASLprep")
32
- rescue IDN::Stringprep::StringprepError
33
- nil
34
- end
35
-
36
- benchmark:
37
- - net_imap_saslprep "I\u00ADX" # RFC example 1. IX
38
- - net_imap_saslprep "user" # RFC example 2. user
39
- - net_imap_saslprep "USER" # RFC example 3. user
40
- - net_imap_saslprep "\u00aa" # RFC example 4. a
41
- - net_imap_saslprep "\u2168" # RFC example 5. IX
42
- - net_imap_saslprep "\u0007" # RFC example 6. Error - prohibited character
43
- - net_imap_saslprep "\u0627\u0031" # RFC example 7. Error - bidirectional check
44
- - net_imap_saslprep "I\u2000X" # map to space: I X
45
- - net_imap_saslprep "a longer string, e.g. a password"
46
-
47
- - libidn_saslprep "I\u00ADX" # RFC example 1. IX
48
- - libidn_saslprep "user" # RFC example 2. user
49
- - libidn_saslprep "USER" # RFC example 3. user
50
- - libidn_saslprep "\u00aa" # RFC example 4. a
51
- - libidn_saslprep "\u2168" # RFC example 5. IX
52
- - libidn_saslprep "\u0007" # RFC example 6. Error - prohibited character
53
- - libidn_saslprep "\u0627\u0031" # RFC example 7. Error - bidirectional check
54
- - libidn_saslprep "I\u2000X" # map to space: I X
55
- - libidn_saslprep "a longer string, e.g. a password"
56
-
57
- - mongo_saslprep "I\u00ADX" # RFC example 1. IX
58
- - mongo_saslprep "user" # RFC example 2. user
59
- - mongo_saslprep "USER" # RFC example 3. user
60
- - mongo_saslprep "\u00aa" # RFC example 4. a
61
- - mongo_saslprep "\u2168" # RFC example 5. IX
62
- - mongo_saslprep "\u0007" # RFC example 6. Error - prohibited character
63
- - mongo_saslprep "\u0627\u0031" # RFC example 7. Error - bidirectional check
64
- - mongo_saslprep "I\u2000X" # map to space: I X
65
- - mongo_saslprep "a longer string, e.g. a password"
@@ -1,39 +0,0 @@
1
- prelude: |
2
- require "json"
3
- require "set"
4
-
5
- all_codepoints = (0..0x10ffff).map{_1.chr("UTF-8") rescue nil}.compact
6
-
7
- rfc3454_tables = Dir["rfcs/rfc3454*.json"]
8
- .first
9
- .then{File.read _1}
10
- .then{JSON.parse _1}
11
- titles = rfc3454_tables.delete("titles")
12
-
13
- sets = rfc3454_tables
14
- .transform_values{|t|t.keys rescue t}
15
- .transform_values{|table|
16
- table
17
- .map{_1.split(?-).map{|i|Integer i, 16}}
18
- .flat_map{_2 ? (_1.._2).to_a : _1}
19
- .to_set
20
- }
21
-
22
- TABLE_A1_SET = sets.fetch "A.1"
23
- ASSIGNED_3_2 = /\p{AGE=3.2}/
24
- UNASSIGNED_3_2 = /\P{AGE=3.2}/
25
- TABLE_A1_REGEX = /(?-mix:[\u{0000}-\u{001f}\u{007f}-\u{00a0}\u{0340}-\u{0341}\u{06dd}\u{070f}\u{1680}\u{180e}\u{2000}-\u{200f}\u{2028}-\u{202f}\u{205f}-\u{2063}\u{206a}-\u{206f}\u{2ff0}-\u{2ffb}\u{3000}\u{e000}-\u{f8ff}\u{fdd0}-\u{fdef}\u{feff}\u{fff9}-\u{ffff}\u{1d173}-\u{1d17a}\u{1fffe}-\u{1ffff}\u{2fffe}-\u{2ffff}\u{3fffe}-\u{3ffff}\u{4fffe}-\u{4ffff}\u{5fffe}-\u{5ffff}\u{6fffe}-\u{6ffff}\u{7fffe}-\u{7ffff}\u{8fffe}-\u{8ffff}\u{9fffe}-\u{9ffff}\u{afffe}-\u{affff}\u{bfffe}-\u{bffff}\u{cfffe}-\u{cffff}\u{dfffe}-\u{dffff}\u{e0001}\u{e0020}-\u{e007f}\u{efffe}-\u{10ffff}])|(?-mix:\p{Cs})/.freeze
26
-
27
- benchmark:
28
-
29
- # matches A.1
30
- - script: "all_codepoints.grep(TABLE_A1_SET)"
31
- - script: "all_codepoints.grep(TABLE_A1_REGEX)"
32
- - script: "all_codepoints.grep(UNASSIGNED_3_2)"
33
- - script: "all_codepoints.grep_v(ASSIGNED_3_2)"
34
-
35
- # doesn't match A.1
36
- - script: "all_codepoints.grep_v(TABLE_A1_SET)"
37
- - script: "all_codepoints.grep_v(TABLE_A1_REGEX)"
38
- - script: "all_codepoints.grep_v(UNASSIGNED_3_2)"
39
- - script: "all_codepoints.grep(ASSIGNED_3_2)"
@@ -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,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Net
4
- class IMAP
5
- # See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2
6
- class ResponseReader # :nodoc:
7
- attr_reader :client
8
-
9
- def initialize(client, sock)
10
- @client, @sock = client, sock
11
- end
12
-
13
- def read_response_buffer
14
- @buff = String.new
15
- catch :eof do
16
- while true
17
- read_line
18
- break unless (@literal_size = get_literal_size)
19
- read_literal
20
- end
21
- end
22
- buff
23
- ensure
24
- @buff = nil
25
- end
26
-
27
- private
28
-
29
- attr_reader :buff, :literal_size
30
-
31
- def bytes_read; buff.bytesize end
32
- def empty?; buff.empty? end
33
- def done?; line_done? && !get_literal_size end
34
- def line_done?; buff.end_with?(CRLF) end
35
- def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end
36
-
37
- def read_line
38
- buff << (@sock.gets(CRLF, read_limit) or throw :eof)
39
- max_response_remaining! unless line_done?
40
- end
41
-
42
- def read_literal
43
- # check before allocating memory for literal
44
- max_response_remaining!
45
- literal = String.new(capacity: literal_size)
46
- buff << (@sock.read(read_limit(literal_size), literal) or throw :eof)
47
- ensure
48
- @literal_size = nil
49
- end
50
-
51
- def read_limit(limit = nil)
52
- [limit, max_response_remaining!].compact.min
53
- end
54
-
55
- def max_response_size; client.max_response_size end
56
- def max_response_remaining; max_response_size &.- bytes_read end
57
- def response_too_large?; max_response_size &.< min_response_size end
58
- def min_response_size; bytes_read + min_response_remaining end
59
-
60
- def min_response_remaining
61
- empty? ? 3 : done? ? 0 : (literal_size || 0) + 2
62
- end
63
-
64
- def max_response_remaining!
65
- return max_response_remaining unless response_too_large?
66
- raise ResponseTooLargeError.new(
67
- max_response_size: max_response_size,
68
- bytes_read: bytes_read,
69
- literal_size: literal_size,
70
- )
71
- end
72
-
73
- end
74
- end
75
- 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