net-imap 0.2.5 → 0.3.0
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 +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yml +2 -2
- data/LICENSE.txt +62 -0
- data/lib/net/imap/authenticators/cram_md5.rb +5 -3
- data/lib/net/imap/authenticators/digest_md5.rb +11 -7
- data/lib/net/imap/authenticators/login.rb +4 -1
- data/lib/net/imap/authenticators.rb +10 -7
- data/lib/net/imap/errors.rb +0 -34
- data/lib/net/imap.rb +43 -111
- data/net-imap.gemspec +4 -4
- metadata +11 -6
- data/lib/net/imap/response_reader.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a91d908ae16db12c507a6cf5c0e5eef57e18c97473b2a13a0ab14cf655e9bcb7
|
4
|
+
data.tar.gz: 1bdab36cf0779d14e2f414e00aee90d95d2584bfc8d591c11d5104b4071bebb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65133026b0746efbdc55a64bd1091da0524e5b922672767f334cec1d0357b31b973380bc8e787ff2430b831375f52050004d718b6308fa9203c9f6710ac2444b
|
7
|
+
data.tar.gz: 7e5e9fcac352549585e38b5a86b0198cc1c5996ad129cf9e53ca92f2ef63ea4b37d26ab8c5efd641b19711b2ea04e42622ead203c3afcaa8d420ceaaaa9893c8
|
data/.github/workflows/test.yml
CHANGED
@@ -7,7 +7,7 @@ jobs:
|
|
7
7
|
name: build (${{ matrix.ruby }} / ${{ matrix.os }})
|
8
8
|
strategy:
|
9
9
|
matrix:
|
10
|
-
ruby: [ head, '3.
|
10
|
+
ruby: [ head, '3.1', '3.0', '2.7' ]
|
11
11
|
os: [ ubuntu-latest, macos-latest ]
|
12
12
|
experimental: [false]
|
13
13
|
include:
|
@@ -20,7 +20,7 @@ jobs:
|
|
20
20
|
runs-on: ${{ matrix.os }}
|
21
21
|
continue-on-error: ${{ matrix.experimental }}
|
22
22
|
steps:
|
23
|
-
- uses: actions/checkout@
|
23
|
+
- uses: actions/checkout@v3
|
24
24
|
- name: Set up Ruby
|
25
25
|
uses: ruby/setup-ruby@v1
|
26
26
|
with:
|
data/LICENSE.txt
CHANGED
@@ -20,3 +20,65 @@ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
20
20
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
21
21
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
22
22
|
SUCH DAMAGE.
|
23
|
+
|
24
|
+
-------------------------------------------------------------------------
|
25
|
+
|
26
|
+
This software includes documentation which has been copied from the relevant
|
27
|
+
RFCs. The copied documentation is covered by the following licenses:
|
28
|
+
|
29
|
+
RFC 3501 (Editor: M. Crispin)
|
30
|
+
Full Copyright Statement
|
31
|
+
|
32
|
+
Copyright (C) The Internet Society (2003). All Rights Reserved.
|
33
|
+
|
34
|
+
This document and translations of it may be copied and furnished to
|
35
|
+
others, and derivative works that comment on or otherwise explain it
|
36
|
+
or assist in its implementation may be prepared, copied, published
|
37
|
+
and distributed, in whole or in part, without restriction of any
|
38
|
+
kind, provided that the above copyright notice and this paragraph are
|
39
|
+
included on all such copies and derivative works. However, this
|
40
|
+
document itself may not be modified in any way, such as by removing
|
41
|
+
the copyright notice or references to the Internet Society or other
|
42
|
+
Internet organizations, except as needed for the purpose of
|
43
|
+
developing Internet standards in which case the procedures for
|
44
|
+
copyrights defined in the Internet Standards process must be
|
45
|
+
followed, or as required to translate it into languages other than
|
46
|
+
English.
|
47
|
+
|
48
|
+
The limited permissions granted above are perpetual and will not be
|
49
|
+
revoked by the Internet Society or its successors or assigns. v This
|
50
|
+
document and the information contained herein is provided on an "AS
|
51
|
+
IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK
|
52
|
+
FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
53
|
+
LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
|
54
|
+
NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY
|
55
|
+
OR FITNESS FOR A PARTICULAR PURPOSE.
|
56
|
+
|
57
|
+
|
58
|
+
RFC9051 (Editors: A. Melnikov, B. Leiba)
|
59
|
+
Copyright Notice
|
60
|
+
|
61
|
+
Copyright (c) 2021 IETF Trust and the persons identified as the
|
62
|
+
document authors. All rights reserved.
|
63
|
+
|
64
|
+
This document is subject to BCP 78 and the IETF Trust's Legal
|
65
|
+
Provisions Relating to IETF Documents
|
66
|
+
(https://trustee.ietf.org/license-info) in effect on the date of
|
67
|
+
publication of this document. Please review these documents
|
68
|
+
carefully, as they describe your rights and restrictions with respect
|
69
|
+
to this document. Code Components extracted from this document must
|
70
|
+
include Simplified BSD License text as described in Section 4.e of
|
71
|
+
the Trust Legal Provisions and are provided without warranty as
|
72
|
+
described in the Simplified BSD License.
|
73
|
+
|
74
|
+
This document may contain material from IETF Documents or IETF
|
75
|
+
Contributions published or made publicly available before November
|
76
|
+
10, 2008. The person(s) controlling the copyright in some of this
|
77
|
+
material may not have granted the IETF Trust the right to allow
|
78
|
+
modifications of such material outside the IETF Standards Process.
|
79
|
+
Without obtaining an adequate license from the person(s) controlling
|
80
|
+
the copyright in such materials, this document may not be modified
|
81
|
+
outside the IETF Standards Process, and derivative works of it may
|
82
|
+
not be created outside the IETF Standards Process, except to format
|
83
|
+
it for publication as an RFC or to translate it into languages other
|
84
|
+
than English.
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "digest/md5"
|
4
|
-
|
5
3
|
# Authenticator for the "+CRAM-MD5+" SASL mechanism, specified in
|
6
4
|
# RFC2195[https://tools.ietf.org/html/rfc2195]. See Net::IMAP#authenticate.
|
7
5
|
#
|
@@ -23,7 +21,11 @@ class Net::IMAP::CramMD5Authenticator
|
|
23
21
|
|
24
22
|
private
|
25
23
|
|
26
|
-
def initialize(user, password)
|
24
|
+
def initialize(user, password, warn_deprecation: true, **_ignored)
|
25
|
+
if warn_deprecation
|
26
|
+
warn "WARNING: CRAM-MD5 mechanism is deprecated." # TODO: recommend SCRAM
|
27
|
+
end
|
28
|
+
require "digest/md5"
|
27
29
|
@user = user
|
28
30
|
@password = password
|
29
31
|
end
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "digest/md5"
|
4
|
-
require "strscan"
|
5
|
-
|
6
3
|
# Net::IMAP authenticator for the "`DIGEST-MD5`" SASL mechanism type, specified
|
7
4
|
# in RFC2831(https://tools.ietf.org/html/rfc2831). See Net::IMAP#authenticate.
|
8
5
|
#
|
@@ -29,8 +26,8 @@ class Net::IMAP::DigestMD5Authenticator
|
|
29
26
|
sparams[k] = v
|
30
27
|
end
|
31
28
|
|
32
|
-
raise DataFormatError, "Bad Challenge: '#{challenge}'" unless c.
|
33
|
-
raise Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth")
|
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")
|
34
31
|
|
35
32
|
response = {
|
36
33
|
:nonce => sparams['nonce'],
|
@@ -77,11 +74,18 @@ class Net::IMAP::DigestMD5Authenticator
|
|
77
74
|
end
|
78
75
|
end
|
79
76
|
|
80
|
-
def initialize(user, password, authname = nil)
|
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"
|
81
84
|
@user, @password, @authname = user, password, authname
|
82
85
|
@nc, @stage = {}, STAGE_ONE
|
83
86
|
end
|
84
87
|
|
88
|
+
|
85
89
|
private
|
86
90
|
|
87
91
|
STAGE_ONE = :stage_one
|
@@ -100,7 +104,7 @@ class Net::IMAP::DigestMD5Authenticator
|
|
100
104
|
def qdval(k, v)
|
101
105
|
return if k.nil? or v.nil?
|
102
106
|
if %w"username authzid realm nonce cnonce digest-uri qop".include? k
|
103
|
-
v.gsub
|
107
|
+
v = v.gsub(/([\\"])/, "\\\1")
|
104
108
|
return '%s="%s"' % [k, v]
|
105
109
|
else
|
106
110
|
return '%s=%s' % [k, v]
|
@@ -33,7 +33,10 @@ class Net::IMAP::LoginAuthenticator
|
|
33
33
|
STATE_USER = :USER
|
34
34
|
STATE_PASSWORD = :PASSWORD
|
35
35
|
|
36
|
-
def initialize(user, password)
|
36
|
+
def initialize(user, password, warn_deprecation: true, **_ignored)
|
37
|
+
if warn_deprecation
|
38
|
+
warn "WARNING: LOGIN SASL mechanism is deprecated. Use PLAIN instead."
|
39
|
+
end
|
37
40
|
@user = user
|
38
41
|
@password = password
|
39
42
|
@state = STATE_USER
|
@@ -19,13 +19,15 @@ module Net::IMAP::Authenticators
|
|
19
19
|
|
20
20
|
# Builds an authenticator for Net::IMAP#authenticate. +args+ will be passed
|
21
21
|
# directly to the chosen authenticator's +#initialize+.
|
22
|
-
def authenticator(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
def authenticator(mechanism, *authargs, **properties, &callback)
|
23
|
+
authenticator = authenticators.fetch(mechanism.upcase) do
|
24
|
+
raise ArgumentError, 'unknown auth type - "%s"' % mechanism
|
25
|
+
end
|
26
|
+
if authenticator.respond_to?(:new)
|
27
|
+
authenticator.new(*authargs, **properties, &callback)
|
28
|
+
else
|
29
|
+
authenticator.call(*authargs, **properties, &callback)
|
27
30
|
end
|
28
|
-
authenticators[auth_type].new(*args)
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
@@ -38,7 +40,8 @@ end
|
|
38
40
|
|
39
41
|
Net::IMAP.extend Net::IMAP::Authenticators
|
40
42
|
|
41
|
-
require_relative "authenticators/login"
|
42
43
|
require_relative "authenticators/plain"
|
44
|
+
|
45
|
+
require_relative "authenticators/login"
|
43
46
|
require_relative "authenticators/cram_md5"
|
44
47
|
require_relative "authenticators/digest_md5"
|
data/lib/net/imap/errors.rb
CHANGED
@@ -11,40 +11,6 @@ module Net
|
|
11
11
|
class DataFormatError < Error
|
12
12
|
end
|
13
13
|
|
14
|
-
# Error raised when the socket cannot be read, due to a configured limit.
|
15
|
-
class ResponseReadError < Error
|
16
|
-
end
|
17
|
-
|
18
|
-
# Error raised when a response is larger than IMAP#max_response_size.
|
19
|
-
class ResponseTooLargeError < ResponseReadError
|
20
|
-
attr_reader :bytes_read, :literal_size
|
21
|
-
attr_reader :max_response_size
|
22
|
-
|
23
|
-
def initialize(msg = nil, *args,
|
24
|
-
bytes_read: nil,
|
25
|
-
literal_size: nil,
|
26
|
-
max_response_size: nil,
|
27
|
-
**kwargs)
|
28
|
-
@bytes_read = bytes_read
|
29
|
-
@literal_size = literal_size
|
30
|
-
@max_response_size = max_response_size
|
31
|
-
msg ||= [
|
32
|
-
"Response size", response_size_msg, "exceeds max_response_size",
|
33
|
-
max_response_size && "(#{max_response_size}B)",
|
34
|
-
].compact.join(" ")
|
35
|
-
return super(msg, *args) if kwargs.empty? # ruby 2.6 compatibility
|
36
|
-
super(msg, *args, **kwargs)
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def response_size_msg
|
42
|
-
if bytes_read && literal_size
|
43
|
-
"(#{bytes_read}B read + #{literal_size}B literal)"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
14
|
# Error raised when a response from the server is non-parseable.
|
49
15
|
class ResponseParseError < Error
|
50
16
|
end
|
data/lib/net/imap.rb
CHANGED
@@ -106,41 +106,6 @@ module Net
|
|
106
106
|
#
|
107
107
|
# This script invokes the FETCH command and the SEARCH command concurrently.
|
108
108
|
#
|
109
|
-
# When running multiple commands, care must be taken to avoid ambiguity. For
|
110
|
-
# example, SEARCH responses are ambiguous about which command they are
|
111
|
-
# responding to, so search commands should not run simultaneously, unless the
|
112
|
-
# server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or
|
113
|
-
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051
|
114
|
-
# §5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for
|
115
|
-
# other examples of command sequences which should not be pipelined.
|
116
|
-
#
|
117
|
-
# == Unbounded memory use
|
118
|
-
#
|
119
|
-
# Net::IMAP reads server responses in a separate receiver thread per client.
|
120
|
-
# Unhandled response data is saved to #responses, and response_handlers run
|
121
|
-
# inside the receiver thread. See the list of methods for {handling server
|
122
|
-
# responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below.
|
123
|
-
#
|
124
|
-
# Because the receiver thread continuously reads and saves new responses, some
|
125
|
-
# scenarios must be careful to avoid unbounded memory use:
|
126
|
-
#
|
127
|
-
# * Commands such as #list or #fetch can have an enormous number of responses.
|
128
|
-
# * Commands such as #fetch can result in an enormous size per response.
|
129
|
-
# * Long-lived connections will gradually accumulate unsolicited server
|
130
|
-
# responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses.
|
131
|
-
# * A buggy or untrusted server could send inappropriate responses, which
|
132
|
-
# could be very numerous, very large, and very rapid.
|
133
|
-
#
|
134
|
-
# Use paginated or limited versions of commands whenever possible.
|
135
|
-
#
|
136
|
-
# Use #max_response_size to impose a limit on incoming server responses
|
137
|
-
# as they are being read. <em>This is especially important for untrusted
|
138
|
-
# servers.</em>
|
139
|
-
#
|
140
|
-
# Use #add_response_handler to handle responses after each one is received.
|
141
|
-
# Use the +response_handlers+ argument to ::new to assign response handlers
|
142
|
-
# before the receiver thread is started.
|
143
|
-
#
|
144
109
|
# == Errors
|
145
110
|
#
|
146
111
|
# An IMAP server can send three different types of responses to indicate
|
@@ -255,9 +220,7 @@ module Net
|
|
255
220
|
# Unicode", RFC-2152[https://tools.ietf.org/html/rfc2152], May 1997.
|
256
221
|
#
|
257
222
|
class IMAP < Protocol
|
258
|
-
VERSION = "0.
|
259
|
-
|
260
|
-
autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__)
|
223
|
+
VERSION = "0.3.0"
|
261
224
|
|
262
225
|
include MonitorMixin
|
263
226
|
if defined?(OpenSSL::SSL)
|
@@ -288,40 +251,6 @@ module Net
|
|
288
251
|
# Seconds to wait until an IDLE response is received.
|
289
252
|
attr_reader :idle_response_timeout
|
290
253
|
|
291
|
-
# The maximum allowed server response size. When +nil+, there is no limit
|
292
|
-
# on response size.
|
293
|
-
#
|
294
|
-
# The default value is _unlimited_ (after +v0.5.8+, the default is 512 MiB).
|
295
|
-
# A _much_ lower value should be used with untrusted servers (for example,
|
296
|
-
# when connecting to a user-provided hostname). When using a lower limit,
|
297
|
-
# message bodies should be fetched in chunks rather than all at once.
|
298
|
-
#
|
299
|
-
# <em>Please Note:</em> this only limits the size per response. It does
|
300
|
-
# not prevent a flood of individual responses and it does not limit how
|
301
|
-
# many unhandled responses may be stored on the responses hash. See
|
302
|
-
# Net::IMAP@Unbounded+memory+use.
|
303
|
-
#
|
304
|
-
# Socket reads are limited to the maximum remaining bytes for the current
|
305
|
-
# response: max_response_size minus the bytes that have already been read.
|
306
|
-
# When the limit is reached, or reading a +literal+ _would_ go over the
|
307
|
-
# limit, ResponseTooLargeError is raised and the connection is closed.
|
308
|
-
# See also #socket_read_limit.
|
309
|
-
#
|
310
|
-
# Note that changes will not take effect immediately, because the receiver
|
311
|
-
# thread may already be waiting for the next response using the previous
|
312
|
-
# value. Net::IMAP#noop can force a response and enforce the new setting
|
313
|
-
# immediately.
|
314
|
-
#
|
315
|
-
# ==== Versioned Defaults
|
316
|
-
#
|
317
|
-
# Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
|
318
|
-
# attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to a config
|
319
|
-
# attribute.</em>
|
320
|
-
#
|
321
|
-
# * original: +nil+ <em>(no limit)</em>
|
322
|
-
# * +0.5+: 512 MiB
|
323
|
-
attr_accessor :max_response_size
|
324
|
-
|
325
254
|
# The thread to receive exceptions.
|
326
255
|
attr_accessor :client_thread
|
327
256
|
|
@@ -449,28 +378,37 @@ module Net
|
|
449
378
|
# Sends an AUTHENTICATE command to authenticate the client.
|
450
379
|
# The +auth_type+ parameter is a string that represents
|
451
380
|
# the authentication mechanism to be used. Currently Net::IMAP
|
452
|
-
# supports the
|
453
|
-
#
|
454
|
-
#
|
455
|
-
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
463
|
-
# the
|
464
|
-
#
|
465
|
-
#
|
466
|
-
#
|
467
|
-
#
|
381
|
+
# supports the following mechanisms:
|
382
|
+
#
|
383
|
+
# PLAIN:: Login using cleartext user and password. Secure with TLS.
|
384
|
+
# See Net::IMAP::PlainAuthenticator.
|
385
|
+
# CRAM-MD5:: DEPRECATED: Use PLAIN (or DIGEST-MD5) with TLS.
|
386
|
+
# DIGEST-MD5:: DEPRECATED by RFC6331. Must be secured using TLS.
|
387
|
+
# See Net::IMAP::DigestMD5Authenticator.
|
388
|
+
# LOGIN:: DEPRECATED: Use PLAIN.
|
389
|
+
#
|
390
|
+
# Most mechanisms require two args: authentication identity (e.g. username)
|
391
|
+
# and credentials (e.g. a password). But each mechanism requires and allows
|
392
|
+
# different arguments; please consult the documentation for the specific
|
393
|
+
# mechanisms you are using. <em>Several obsolete mechanisms are available
|
394
|
+
# for backwards compatibility. Using deprecated mechanisms will issue
|
395
|
+
# warnings.</em>
|
396
|
+
#
|
397
|
+
# Servers do not support all mechanisms and clients must not attempt to use
|
398
|
+
# a mechanism unless "AUTH=#{mechanism}" is listed as a #capability.
|
399
|
+
# Clients must not attempt to authenticate or #login when +LOGINDISABLED+ is
|
400
|
+
# listed with the capabilities. Server capabilities, especially auth
|
401
|
+
# mechanisms, do change after calling #starttls so they need to be checked
|
402
|
+
# again.
|
468
403
|
#
|
469
404
|
# For example:
|
470
405
|
#
|
471
|
-
# imap.authenticate('
|
406
|
+
# imap.authenticate('PLAIN', user, password)
|
472
407
|
#
|
473
408
|
# A Net::IMAP::NoResponseError is raised if authentication fails.
|
409
|
+
#
|
410
|
+
# See +Net::IMAP::Authenticators+ for more information on plugging in your
|
411
|
+
# own authenticator.
|
474
412
|
def authenticate(auth_type, *args)
|
475
413
|
authenticator = self.class.authenticator(auth_type, *args)
|
476
414
|
send_command("AUTHENTICATE", auth_type) do |resp|
|
@@ -1026,11 +964,6 @@ module Net
|
|
1026
964
|
# end
|
1027
965
|
# }
|
1028
966
|
#
|
1029
|
-
# Response handlers can also be added when the client is created before the
|
1030
|
-
# receiver thread is started, by the +response_handlers+ argument to ::new.
|
1031
|
-
# This ensures every server response is handled, including the #greeting.
|
1032
|
-
#
|
1033
|
-
# Related: #remove_response_handler, #response_handlers
|
1034
967
|
def add_response_handler(handler = nil, &block)
|
1035
968
|
raise ArgumentError, "two Procs are passed" if handler && block
|
1036
969
|
@response_handlers.push(block || handler)
|
@@ -1146,13 +1079,6 @@ module Net
|
|
1146
1079
|
# OpenSSL::SSL::SSLContext#set_params as parameters.
|
1147
1080
|
# open_timeout:: Seconds to wait until a connection is opened
|
1148
1081
|
# idle_response_timeout:: Seconds to wait until an IDLE response is received
|
1149
|
-
# response_handlers:: A list of response handlers to be added before the
|
1150
|
-
# receiver thread is started. This ensures every server
|
1151
|
-
# response is handled, including the #greeting. Note
|
1152
|
-
# that the greeting is handled in the current thread,
|
1153
|
-
# but all other responses are handled in the receiver
|
1154
|
-
# thread.
|
1155
|
-
# max_response_size:: See #max_response_size.
|
1156
1082
|
#
|
1157
1083
|
# The most common errors are:
|
1158
1084
|
#
|
@@ -1183,10 +1109,8 @@ module Net
|
|
1183
1109
|
@tagno = 0
|
1184
1110
|
@open_timeout = options[:open_timeout] || 30
|
1185
1111
|
@idle_response_timeout = options[:idle_response_timeout] || 5
|
1186
|
-
@max_response_size = options[:max_response_size]
|
1187
1112
|
@parser = ResponseParser.new
|
1188
1113
|
@sock = tcp_socket(@host, @port)
|
1189
|
-
@reader = ResponseReader.new(self, @sock)
|
1190
1114
|
begin
|
1191
1115
|
if options[:ssl]
|
1192
1116
|
start_tls_session(options[:ssl])
|
@@ -1197,7 +1121,6 @@ module Net
|
|
1197
1121
|
@responses = Hash.new([].freeze)
|
1198
1122
|
@tagged_responses = {}
|
1199
1123
|
@response_handlers = []
|
1200
|
-
options[:response_handlers]&.each do |h| add_response_handler(h) end
|
1201
1124
|
@tagged_response_arrival = new_cond
|
1202
1125
|
@continued_command_tag = nil
|
1203
1126
|
@continuation_request_arrival = new_cond
|
@@ -1214,7 +1137,6 @@ module Net
|
|
1214
1137
|
if @greeting.name == "BYE"
|
1215
1138
|
raise ByeResponseError, @greeting
|
1216
1139
|
end
|
1217
|
-
@response_handlers.each do |handler| handler.call(@greeting) end
|
1218
1140
|
|
1219
1141
|
@client_thread = Thread.current
|
1220
1142
|
@receiver_thread = Thread.start {
|
@@ -1338,14 +1260,25 @@ module Net
|
|
1338
1260
|
end
|
1339
1261
|
|
1340
1262
|
def get_response
|
1341
|
-
buff =
|
1263
|
+
buff = String.new
|
1264
|
+
while true
|
1265
|
+
s = @sock.gets(CRLF)
|
1266
|
+
break unless s
|
1267
|
+
buff.concat(s)
|
1268
|
+
if /\{(\d+)\}\r\n/n =~ s
|
1269
|
+
s = @sock.read($1.to_i)
|
1270
|
+
buff.concat(s)
|
1271
|
+
else
|
1272
|
+
break
|
1273
|
+
end
|
1274
|
+
end
|
1342
1275
|
return nil if buff.length == 0
|
1343
|
-
|
1344
|
-
|
1276
|
+
if @@debug
|
1277
|
+
$stderr.print(buff.gsub(/^/n, "S: "))
|
1278
|
+
end
|
1279
|
+
return @parser.parse(buff)
|
1345
1280
|
end
|
1346
1281
|
|
1347
|
-
#############################
|
1348
|
-
|
1349
1282
|
def record_response(name, data)
|
1350
1283
|
unless @responses.has_key?(name)
|
1351
1284
|
@responses[name] = []
|
@@ -1523,7 +1456,6 @@ module Net
|
|
1523
1456
|
context.verify_callback = VerifyCallbackProc
|
1524
1457
|
end
|
1525
1458
|
@sock = SSLSocket.new(@sock, context)
|
1526
|
-
@reader = ResponseReader.new(self, @sock)
|
1527
1459
|
@sock.sync_close = true
|
1528
1460
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
1529
1461
|
ssl_socket_connect(@sock, @open_timeout)
|
data/net-imap.gemspec
CHANGED
@@ -10,8 +10,8 @@ end
|
|
10
10
|
Gem::Specification.new do |spec|
|
11
11
|
spec.name = name
|
12
12
|
spec.version = version
|
13
|
-
spec.authors = ["Shugo Maeda"]
|
14
|
-
spec.email = ["shugo@ruby-lang.org"]
|
13
|
+
spec.authors = ["Shugo Maeda", "nicholas a. evans"]
|
14
|
+
spec.email = ["shugo@ruby-lang.org", "nick@ekenosen.net"]
|
15
15
|
|
16
16
|
spec.summary = %q{Ruby client api for Internet Message Access Protocol}
|
17
17
|
spec.description = %q{Ruby client api for Internet Message Access Protocol}
|
@@ -32,6 +32,6 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.require_paths = ["lib"]
|
33
33
|
|
34
34
|
spec.add_dependency "net-protocol"
|
35
|
-
spec.
|
36
|
-
spec.
|
35
|
+
spec.add_development_dependency "digest"
|
36
|
+
spec.add_development_dependency "strscan"
|
37
37
|
end
|
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-imap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
8
|
+
- nicholas a. evans
|
9
|
+
autorequire:
|
8
10
|
bindir: exe
|
9
11
|
cert_chain: []
|
10
|
-
date:
|
12
|
+
date: 2022-09-28 00:00:00.000000000 Z
|
11
13
|
dependencies:
|
12
14
|
- !ruby/object:Gem::Dependency
|
13
15
|
name: net-protocol
|
@@ -30,7 +32,7 @@ dependencies:
|
|
30
32
|
- - ">="
|
31
33
|
- !ruby/object:Gem::Version
|
32
34
|
version: '0'
|
33
|
-
type: :
|
35
|
+
type: :development
|
34
36
|
prerelease: false
|
35
37
|
version_requirements: !ruby/object:Gem::Requirement
|
36
38
|
requirements:
|
@@ -44,7 +46,7 @@ dependencies:
|
|
44
46
|
- - ">="
|
45
47
|
- !ruby/object:Gem::Version
|
46
48
|
version: '0'
|
47
|
-
type: :
|
49
|
+
type: :development
|
48
50
|
prerelease: false
|
49
51
|
version_requirements: !ruby/object:Gem::Requirement
|
50
52
|
requirements:
|
@@ -54,10 +56,12 @@ dependencies:
|
|
54
56
|
description: Ruby client api for Internet Message Access Protocol
|
55
57
|
email:
|
56
58
|
- shugo@ruby-lang.org
|
59
|
+
- nick@ekenosen.net
|
57
60
|
executables: []
|
58
61
|
extensions: []
|
59
62
|
extra_rdoc_files: []
|
60
63
|
files:
|
64
|
+
- ".github/dependabot.yml"
|
61
65
|
- ".github/workflows/test.yml"
|
62
66
|
- ".gitignore"
|
63
67
|
- Gemfile
|
@@ -76,7 +80,6 @@ files:
|
|
76
80
|
- lib/net/imap/flags.rb
|
77
81
|
- lib/net/imap/response_data.rb
|
78
82
|
- lib/net/imap/response_parser.rb
|
79
|
-
- lib/net/imap/response_reader.rb
|
80
83
|
- net-imap.gemspec
|
81
84
|
homepage: https://github.com/ruby/net-imap
|
82
85
|
licenses:
|
@@ -85,6 +88,7 @@ licenses:
|
|
85
88
|
metadata:
|
86
89
|
homepage_uri: https://github.com/ruby/net-imap
|
87
90
|
source_code_uri: https://github.com/ruby/net-imap
|
91
|
+
post_install_message:
|
88
92
|
rdoc_options: []
|
89
93
|
require_paths:
|
90
94
|
- lib
|
@@ -99,7 +103,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
103
|
- !ruby/object:Gem::Version
|
100
104
|
version: '0'
|
101
105
|
requirements: []
|
102
|
-
rubygems_version: 3.
|
106
|
+
rubygems_version: 3.4.0.dev
|
107
|
+
signing_key:
|
103
108
|
specification_version: 4
|
104
109
|
summary: Ruby client api for Internet Message Access Protocol
|
105
110
|
test_files: []
|
@@ -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
|