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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 473842922edfdfd112a9cd3fa96df2831efa0d4640a38c0969725d6c04f45dbe
4
- data.tar.gz: 7f93b8a1882f2225f404db0d175c29ead931de531f918e17cbb6f8515300b9e9
3
+ metadata.gz: 2bba73e8db611b37b7c5b9be540d99c2288e9188e7f12620259862b30db47815
4
+ data.tar.gz: fde1bcda99236beafea397d36768fb9fe185ec20ca2cd5d9c991b48cfdf51bd3
5
5
  SHA512:
6
- metadata.gz: d9470c52ca3b689a1dd314501a91f5651d1b11fd26cc5628cb6ccf4bf59aeb93ebdcca141ab12d0ec5e69b0e5aa5e29881c85433d9c6dcdeb5902591c1c62aaa
7
- data.tar.gz: 6caae2c7cf684d10ca54ac8b53b498acb5c8d210135d2f583d254fa3dbdc41b7e50382c43aeb05438713b723d9d5da7d48980569024e36d55683e9e5b041df8a
6
+ metadata.gz: 6ee43fab9eaea8c870940e0cc625e3223722e4b722e1055da6f3629707aaf12e580143139abb97ae1294757a99db977428c8aa23bc57d8e9a23ff9ab5eb695cf
7
+ data.tar.gz: 67a965fdd6b4af0480917241a8efad57c3356c82ec975bcc10373242e477718a1751e78cc9f0e51286b553e6222c26cddca119dbaa9dfef855c9611de6a55a64
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ module SASL
6
+
7
+ # This API is *experimental*, and may change.
8
+ #
9
+ # TODO: catch exceptions in #process and send #cancel_response.
10
+ # TODO: raise an error if the command succeeds after being canceled.
11
+ # TODO: use with more clients, to verify the API can accommodate them.
12
+ #
13
+ # Create an AuthenticationExchange from a client adapter and a mechanism
14
+ # authenticator:
15
+ # def authenticate(mechanism, ...)
16
+ # authenticator = SASL.authenticator(mechanism, ...)
17
+ # SASL::AuthenticationExchange.new(
18
+ # sasl_adapter, mechanism, authenticator
19
+ # ).authenticate
20
+ # end
21
+ #
22
+ # private
23
+ #
24
+ # def sasl_adapter = MyClientAdapter.new(self, &method(:send_command))
25
+ #
26
+ # Or delegate creation of the authenticator to ::build:
27
+ # def authenticate(...)
28
+ # SASL::AuthenticationExchange.build(sasl_adapter, ...)
29
+ # .authenticate
30
+ # end
31
+ #
32
+ # As a convenience, ::authenticate combines ::build and #authenticate:
33
+ # def authenticate(...)
34
+ # SASL::AuthenticationExchange.authenticate(sasl_adapter, ...)
35
+ # end
36
+ #
37
+ # Likewise, ClientAdapter#authenticate delegates to #authenticate:
38
+ # def authenticate(...) = sasl_adapter.authenticate(...)
39
+ #
40
+ class AuthenticationExchange
41
+ # Convenience method for <tt>build(...).authenticate</tt>
42
+ def self.authenticate(...) build(...).authenticate end
43
+
44
+ # Use +registry+ to override the global Authenticators registry.
45
+ def self.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block)
46
+ authenticator = SASL.authenticator(mechanism, *args, **kwargs, &block)
47
+ new(client, mechanism, authenticator, sasl_ir: sasl_ir)
48
+ end
49
+
50
+ attr_reader :mechanism, :authenticator
51
+
52
+ def initialize(client, mechanism, authenticator, sasl_ir: true)
53
+ @client = client
54
+ @mechanism = -mechanism.to_s.upcase.tr(?_, ?-)
55
+ @authenticator = authenticator
56
+ @sasl_ir = sasl_ir
57
+ @processed = false
58
+ end
59
+
60
+ # Call #authenticate to execute an authentication exchange for #client
61
+ # using #authenticator. Authentication failures will raise an
62
+ # exception. Any exceptions other than those in RESPONSE_ERRORS will
63
+ # drop the connection.
64
+ def authenticate
65
+ client.run_command(mechanism, initial_response) { process _1 }
66
+ .tap { raise AuthenticationIncomplete, _1 unless done? }
67
+ rescue *client.response_errors
68
+ raise # but don't drop the connection
69
+ rescue
70
+ client.drop_connection
71
+ raise
72
+ rescue Exception # rubocop:disable Lint/RescueException
73
+ client.drop_connection!
74
+ raise
75
+ end
76
+
77
+ def send_initial_response?
78
+ @sasl_ir &&
79
+ authenticator.respond_to?(:initial_response?) &&
80
+ authenticator.initial_response? &&
81
+ client.sasl_ir_capable? &&
82
+ client.auth_capable?(mechanism)
83
+ end
84
+
85
+ def done?
86
+ authenticator.respond_to?(:done?) ? authenticator.done? : @processed
87
+ end
88
+
89
+ private
90
+
91
+ attr_reader :client
92
+
93
+ def initial_response
94
+ return unless send_initial_response?
95
+ client.encode_ir authenticator.process nil
96
+ end
97
+
98
+ def process(challenge)
99
+ client.encode authenticator.process client.decode challenge
100
+ ensure
101
+ @processed = true
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+ end
@@ -65,7 +65,7 @@ module Net::IMAP::SASL
65
65
  # lazily loaded from <tt>Net::IMAP::SASL::#{name}Authenticator</tt> (case is
66
66
  # preserved and non-alphanumeric characters are removed..
67
67
  def add_authenticator(name, authenticator = nil)
68
- key = name.upcase.to_sym
68
+ key = -name.to_s.upcase.tr(?_, ?-)
69
69
  authenticator ||= begin
70
70
  class_name = "#{name.gsub(/[^a-zA-Z0-9]/, "")}Authenticator".to_sym
71
71
  auth_class = nil
@@ -79,10 +79,15 @@ module Net::IMAP::SASL
79
79
 
80
80
  # Removes the authenticator registered for +name+
81
81
  def remove_authenticator(name)
82
- key = name.upcase.to_sym
82
+ key = -name.to_s.upcase.tr(?_, ?-)
83
83
  @authenticators.delete(key)
84
84
  end
85
85
 
86
+ def mechanism?(name)
87
+ key = -name.to_s.upcase.tr(?_, ?-)
88
+ @authenticators.key?(key)
89
+ end
90
+
86
91
  # :call-seq:
87
92
  # authenticator(mechanism, ...) -> auth_session
88
93
  #
@@ -100,8 +105,9 @@ module Net::IMAP::SASL
100
105
  # only. Protocol client users should see refer to their client's
101
106
  # documentation, e.g. Net::IMAP#authenticate.
102
107
  def authenticator(mechanism, ...)
103
- auth = @authenticators.fetch(mechanism.upcase.to_sym) do
104
- raise ArgumentError, 'unknown auth type - "%s"' % mechanism
108
+ key = -mechanism.to_s.upcase.tr(?_, ?-)
109
+ auth = @authenticators.fetch(key) do
110
+ raise ArgumentError, 'unknown auth type - "%s"' % key
105
111
  end
106
112
  auth.respond_to?(:new) ? auth.new(...) : auth.call(...)
107
113
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ module SASL
6
+
7
+ # This API is *experimental*, and may change.
8
+ #
9
+ # TODO: use with more clients, to verify the API can accommodate them.
10
+ #
11
+ # An abstract base class for implementing a SASL authentication exchange.
12
+ # Different clients will each have their own adapter subclass, overridden
13
+ # to match their needs.
14
+ #
15
+ # Although the default implementations _may_ be sufficient, subclasses
16
+ # will probably need to override some methods. Additionally, subclasses
17
+ # may need to include a protocol adapter mixin, if the default
18
+ # ProtocolAdapters::Generic isn't sufficient.
19
+ class ClientAdapter
20
+ include ProtocolAdapters::Generic
21
+
22
+ attr_reader :client, :command_proc
23
+
24
+ # +command_proc+ can used to avoid exposing private methods on #client.
25
+ # It should run a command with the arguments sent to it, yield each
26
+ # continuation payload, respond to the server with the result of each
27
+ # yield, and return the result. Non-successful results *MUST* raise an
28
+ # exception. Exceptions in the block *MUST* cause the command to fail.
29
+ #
30
+ # Subclasses that override #run_command may use #command_proc for
31
+ # other purposes.
32
+ def initialize(client, &command_proc)
33
+ @client, @command_proc = client, command_proc
34
+ end
35
+
36
+ # Delegates to AuthenticationExchange.authenticate.
37
+ def authenticate(...) AuthenticationExchange.authenticate(self, ...) end
38
+
39
+ # Do the protocol and server both support an initial response?
40
+ def sasl_ir_capable?; client.sasl_ir_capable? end
41
+
42
+ # Does the server advertise support for the mechanism?
43
+ def auth_capable?(mechanism); client.auth_capable?(mechanism) end
44
+
45
+ # Runs the authenticate command with +mechanism+ and +initial_response+.
46
+ # When +initial_response+ is nil, an initial response must NOT be sent.
47
+ #
48
+ # Yields each continuation payload, responds to the server with the
49
+ # result of each yield, and returns the result. Non-successful results
50
+ # *MUST* raise an exception. Exceptions in the block *MUST* cause the
51
+ # command to fail.
52
+ #
53
+ # Subclasses that override this may use #command_proc differently.
54
+ def run_command(mechanism, initial_response = nil, &block)
55
+ command_proc or raise Error, "initialize with block or override"
56
+ args = [command_name, mechanism, initial_response].compact
57
+ command_proc.call(*args, &block)
58
+ end
59
+
60
+ # Returns an array of server responses errors raised by run_command.
61
+ # Exceptions in this array won't drop the connection.
62
+ def response_errors; [] end
63
+
64
+ # Drop the connection gracefully.
65
+ def drop_connection; client.drop_connection end
66
+
67
+ # Drop the connection abruptly.
68
+ def drop_connection!; client.drop_connection! end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ module SASL
6
+
7
+ module ProtocolAdapters
8
+ # This API is experimental, and may change.
9
+ module Generic
10
+ def command_name; "AUTHENTICATE" end
11
+ def service; raise "Implement in subclass or module" end
12
+ def host; client.host end
13
+ def port; client.port end
14
+ def encode_ir(string) string.empty? ? "=" : encode(string) end
15
+ def encode(string) [string].pack("m0") end
16
+ def decode(string) string.unpack1("m0") end
17
+ def cancel_response; "*" end
18
+ end
19
+
20
+ # See RFC-3501 (IMAP4rev1), RFC-4959 (SASL-IR capability),
21
+ # and RFC-9051 (IMAP4rev2).
22
+ module IMAP
23
+ include Generic
24
+ def service; "imap" end
25
+ end
26
+
27
+ # See RFC-4954 (AUTH capability).
28
+ module SMTP
29
+ include Generic
30
+ def command_name; "AUTH" end
31
+ def service; "smtp" end
32
+ end
33
+
34
+ # See RFC-5034 (SASL capability).
35
+ module POP
36
+ include Generic
37
+ def command_name; "AUTH" end
38
+ def service; "pop" end
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
45
+ end
data/lib/net/imap/sasl.rb CHANGED
@@ -135,6 +135,10 @@ module Net
135
135
  autoload :BidiStringError, sasl_stringprep_rb
136
136
 
137
137
  sasl_dir = File.expand_path("sasl", __dir__)
138
+ autoload :AuthenticationExchange, "#{sasl_dir}/authentication_exchange"
139
+ autoload :ClientAdapter, "#{sasl_dir}/client_adapter"
140
+ autoload :ProtocolAdapters, "#{sasl_dir}/protocol_adapters"
141
+
138
142
  autoload :Authenticators, "#{sasl_dir}/authenticators"
139
143
  autoload :GS2Header, "#{sasl_dir}/gs2_header"
140
144
  autoload :ScramAlgorithm, "#{sasl_dir}/scram_algorithm"
@@ -155,8 +159,10 @@ module Net
155
159
  # Returns the default global SASL::Authenticators instance.
156
160
  def self.authenticators; @authenticators ||= Authenticators.new end
157
161
 
158
- # Delegates to ::authenticators. See Authenticators#authenticator.
159
- def self.authenticator(...) authenticators.authenticator(...) end
162
+ # Delegates to <tt>registry.new</tt> See Authenticators#new.
163
+ def self.authenticator(*args, registry: authenticators, **kwargs, &block)
164
+ registry.new(*args, **kwargs, &block)
165
+ end
160
166
 
161
167
  # Delegates to ::authenticators. See Authenticators#add_authenticator.
162
168
  def self.add_authenticator(...) authenticators.add_authenticator(...) end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+
6
+ # Experimental
7
+ class SASLAdapter < SASL::ClientAdapter
8
+ include SASL::ProtocolAdapters::IMAP
9
+
10
+ RESPONSE_ERRORS = [NoResponseError, BadResponseError, ByeResponseError]
11
+ .freeze
12
+
13
+ def response_errors; RESPONSE_ERRORS end
14
+ def sasl_ir_capable?; client.capable?("SASL-IR") end
15
+ def auth_capable?(mechanism); client.auth_capable?(mechanism) end
16
+ def drop_connection; client.logout! end
17
+ def drop_connection!; client.disconnect end
18
+ end
19
+
20
+ end
21
+ end
data/lib/net/imap.rb CHANGED
@@ -127,7 +127,7 @@ module Net
127
127
  # end
128
128
  #
129
129
  # # Support for "UTF8=ACCEPT" implies support for "ENABLE"
130
- # imap.enable :utf8 if imap.auth_capable?("UTF8=ACCEPT")
130
+ # imap.enable :utf8 if imap.capable?("UTF8=ACCEPT")
131
131
  #
132
132
  # namespaces = imap.namespace if imap.capable?(:namespace)
133
133
  # mbox_prefix = namespaces&.personal&.first&.prefix || ""
@@ -662,7 +662,7 @@ module Net
662
662
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
663
663
  #
664
664
  class IMAP < Protocol
665
- VERSION = "0.4.0"
665
+ VERSION = "0.4.1"
666
666
 
667
667
  # Aliases for supported capabilities, to be used with the #enable command.
668
668
  ENABLE_ALIASES = {
@@ -670,8 +670,9 @@ module Net
670
670
  "UTF8=ONLY" => "UTF8=ACCEPT",
671
671
  }.freeze
672
672
 
673
- autoload :SASL, File.expand_path("imap/sasl", __dir__)
674
- autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
673
+ autoload :SASL, File.expand_path("imap/sasl", __dir__)
674
+ autoload :SASLAdapter, File.expand_path("imap/sasl_adapter", __dir__)
675
+ autoload :StringPrep, File.expand_path("imap/stringprep", __dir__)
675
676
 
676
677
  include MonitorMixin
677
678
  if defined?(OpenSSL::SSL)
@@ -1142,7 +1143,10 @@ module Net
1142
1143
  end
1143
1144
 
1144
1145
  # :call-seq:
1145
- # authenticate(mechanism, *, sasl_ir: true, **, &) -> ok_resp
1146
+ # authenticate(mechanism, *,
1147
+ # sasl_ir: true,
1148
+ # registry: Net::IMAP::SASL.authenticators,
1149
+ # **, &) -> ok_resp
1146
1150
  #
1147
1151
  # Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
1148
1152
  # to authenticate the client. If successful, the connection enters the
@@ -1886,8 +1890,7 @@ module Net
1886
1890
  # +attr+ is a list of attributes to fetch; see the documentation
1887
1891
  # for FetchData for a list of valid attributes.
1888
1892
  #
1889
- # The return value is an array of FetchData or nil
1890
- # (instead of an empty array) if there is no matching message.
1893
+ # The return value is an array of FetchData.
1891
1894
  #
1892
1895
  # Related: #uid_search, FetchData
1893
1896
  #
@@ -2747,6 +2750,10 @@ module Net
2747
2750
  end
2748
2751
  end
2749
2752
 
2753
+ def sasl_adapter
2754
+ SASLAdapter.new(self, &method(:send_command_with_continuations))
2755
+ end
2756
+
2750
2757
  #--
2751
2758
  # We could get the saslprep method by extending the SASLprep module
2752
2759
  # directly. It's done indirectly, so SASLprep can be lazily autoloaded,
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.4.0
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: 2023-10-04 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
@@ -100,7 +100,9 @@ files:
100
100
  - lib/net/imap/response_parser/parser_utils.rb
101
101
  - lib/net/imap/sasl.rb
102
102
  - lib/net/imap/sasl/anonymous_authenticator.rb
103
+ - lib/net/imap/sasl/authentication_exchange.rb
103
104
  - lib/net/imap/sasl/authenticators.rb
105
+ - lib/net/imap/sasl/client_adapter.rb
104
106
  - lib/net/imap/sasl/cram_md5_authenticator.rb
105
107
  - lib/net/imap/sasl/digest_md5_authenticator.rb
106
108
  - lib/net/imap/sasl/external_authenticator.rb
@@ -108,10 +110,12 @@ files:
108
110
  - lib/net/imap/sasl/login_authenticator.rb
109
111
  - lib/net/imap/sasl/oauthbearer_authenticator.rb
110
112
  - lib/net/imap/sasl/plain_authenticator.rb
113
+ - lib/net/imap/sasl/protocol_adapters.rb
111
114
  - lib/net/imap/sasl/scram_algorithm.rb
112
115
  - lib/net/imap/sasl/scram_authenticator.rb
113
116
  - lib/net/imap/sasl/stringprep.rb
114
117
  - lib/net/imap/sasl/xoauth2_authenticator.rb
118
+ - lib/net/imap/sasl_adapter.rb
115
119
  - lib/net/imap/stringprep.rb
116
120
  - lib/net/imap/stringprep/nameprep.rb
117
121
  - lib/net/imap/stringprep/saslprep.rb