net-imap 0.3.7 → 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.
Files changed (71) 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 -1
  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 +1867 -1184
  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 +2090 -823
  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 -49
  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/sasl/saslprep.rb +0 -55
  70. data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
  71. data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30f6313663bc3f6e896e6166ad97a16296b52e59f9f2ef93f5b6ceb8a3031caa
4
- data.tar.gz: e916c89df8b3d7bbf7c310c0299a6b7134caf71a0514091f4f87a171247750a2
3
+ metadata.gz: cdbdda0ed73da899ec338f66022a16104562d3701c568b0a6d4897270a608ac5
4
+ data.tar.gz: b6a7ec70776b32f8eb57d01a0869503eb5d76f719ac091eb92e0206608e936e9
5
5
  SHA512:
6
- metadata.gz: f19ca9b2808b3d293b1a6ffef2c8b3287f1f69471c914bf775d51b3259d3dc837cee635d647c575dab70795b06b54c37214d2c51e9138a2093df5d3e20d5b478
7
- data.tar.gz: d6c5b5c78de3f328486d4e107f1d6c336bb13be4738ad0d1b4e3b7b9b6af89f1a7bc77d09a7a27bd9f61160f38f4a5dc6c3a1ec5ae3f9f33705d6c52188e41f5
6
+ metadata.gz: 381bf2428719ed8decb5d241fda0e19f28031dd4a77980b3717bb29c37bed1c927f00e5b57862e209ecf24b2e9b38c01088d6e1a90fc4b4cc026cdd9e6611100
7
+ data.tar.gz: 513c6a77d46b6d2cf67aea4511023acc76c69940e3b1a0d0eae7223b53ff63bc8e6e009f51fef826b09f76f6ad1d92e84243e8457f59d3912db7e74bf69d3d1b
data/BSDL ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
data/COPYING ADDED
@@ -0,0 +1,56 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the
3
+ 2-clause BSDL (see the file BSDL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a. place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b. use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c. give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d. make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a. distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b. accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c. give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d. make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
data/Gemfile CHANGED
@@ -4,6 +4,20 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
+ gem "digest"
8
+ gem "strscan"
9
+ gem "base64"
10
+
11
+ gem "irb"
7
12
  gem "rake"
8
13
  gem "rdoc"
9
14
  gem "test-unit"
15
+ gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
16
+
17
+ gem "benchmark-driver", require: false
18
+
19
+ group :test do
20
+ gem "simplecov", require: false
21
+ gem "simplecov-html", require: false
22
+ gem "simplecov-json", require: false
23
+ end
data/LICENSE.txt CHANGED
@@ -1,25 +1,6 @@
1
- Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
-
3
- Redistribution and use in source and binary forms, with or without
4
- modification, are permitted provided that the following conditions
5
- are met:
6
- 1. Redistributions of source code must retain the above copyright
7
- notice, this list of conditions and the following disclaimer.
8
- 2. Redistributions in binary form must reproduce the above copyright
9
- notice, this list of conditions and the following disclaimer in the
10
- documentation and/or other materials provided with the distribution.
11
-
12
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
- SUCH DAMAGE.
1
+ All the files in this distribution are covered under either the Ruby license or
2
+ the BSD-2-Clause license (see the file COPYING) except some documentation mentioned
3
+ below.
23
4
 
24
5
  -------------------------------------------------------------------------
25
6
 
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Net::IMAP
2
2
 
3
3
  Net::IMAP implements Internet Message Access Protocol (IMAP) client
4
- functionality. The protocol is described in [IMAP](https://tools.ietf.org/html/rfc3501).
4
+ functionality. The protocol is described in
5
+ [RFC3501](https://www.rfc-editor.org/rfc/rfc3501),
6
+ [RFC9051](https://www.rfc-editor.org/rfc/rfc9051) and various extensions.
5
7
 
6
8
  ## Installation
7
9
 
@@ -21,11 +23,24 @@ Or install it yourself as:
21
23
 
22
24
  ## Usage
23
25
 
26
+ ### Connect with TLS to port 993
27
+
28
+ ```ruby
29
+ imap = Net::IMAP.new('mail.example.com', ssl: true)
30
+ imap.port => 993
31
+ imap.tls_verified? => true
32
+ case imap.greeting.name
33
+ in /OK/i
34
+ # The client is connected in the "Not Authenticated" state.
35
+ imap.authenticate("PLAIN", "joe_user", "joes_password")
36
+ in /PREAUTH/i
37
+ # The client is connected in the "Authenticated" state.
38
+ end
39
+ ```
40
+
24
41
  ### List sender and subject of all recent messages in the default mailbox
25
42
 
26
43
  ```ruby
27
- imap = Net::IMAP.new('mail.example.com')
28
- imap.authenticate('LOGIN', 'joe_user', 'joes_password')
29
44
  imap.examine('INBOX')
30
45
  imap.search(["RECENT"]).each do |message_id|
31
46
  envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
@@ -36,15 +51,17 @@ end
36
51
  ### Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
37
52
 
38
53
  ```ruby
39
- imap = Net::IMAP.new('mail.example.com')
40
- imap.authenticate('LOGIN', 'joe_user', 'joes_password')
41
54
  imap.select('Mail/sent-mail')
42
- if not imap.list('Mail/', 'sent-apr03')
55
+ if imap.list('Mail/', 'sent-apr03').empty?
43
56
  imap.create('Mail/sent-apr03')
44
57
  end
45
58
  imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
46
- imap.copy(message_id, "Mail/sent-apr03")
47
- imap.store(message_id, "+FLAGS", [:Deleted])
59
+ if imap.capable?(:move) || imap.capable?(:IMAP4rev2)
60
+ imap.move(message_id, "Mail/sent-apr03")
61
+ else
62
+ imap.copy(message_id, "Mail/sent-apr03")
63
+ imap.store(message_id, "+FLAGS", [:Deleted])
64
+ end
48
65
  end
49
66
  imap.expunge
50
67
  ```
data/Rakefile CHANGED
@@ -10,11 +10,4 @@ Rake::TestTask.new(:test) do |t|
10
10
  t.test_files = FileList["test/**/test_*.rb"]
11
11
  end
12
12
 
13
- task :sync_tool do
14
- require 'fileutils'
15
- FileUtils.cp "../ruby/tool/lib/core_assertions.rb", "./test/lib"
16
- FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib"
17
- FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib"
18
- end
19
-
20
13
  task :default => :test
data/docs/styles.css CHANGED
@@ -1,36 +1,85 @@
1
1
  /* this is a work in progress. :) */
2
2
 
3
- main .method-header {
4
- background: rgba(27,31,35,0.05);
5
- border: 1px solid #6C8C22;
3
+ /***********************************************
4
+ * Method descriptions
5
+ ***********************************************/
6
+
7
+ main .method-detail {
8
+ display: grid;
9
+ grid-template-columns: 1fr auto;
10
+ justify-content: space-between;
11
+ }
12
+
13
+ main .method-header,
14
+ main .method-controls,
15
+ .attribute-method-heading {
6
16
  padding: 0.5em;
7
- border-radius: 4px;
8
- /* padding: 0 0.5em; */
9
- /* border-width: 0 1px; */
10
- /* border-color: #6C8C22; */
11
- /* border-style: solid; */
17
+ /* border: 1px solid var(--highlight-color); */
18
+ background: var(--table-header-background-color);
19
+ line-height: 1.6;
20
+ }
21
+
22
+ .attribute-method-heading .attribute-access-type {
23
+ float: right;
24
+ }
25
+
26
+ main .method-header {
27
+ border-right: none;
28
+ border-radius: 4px 0 0 4px;
29
+ }
30
+
31
+ main .method-heading :any-link {
32
+ text-decoration: none;
33
+ }
34
+
35
+ main .method-controls {
36
+ border-left: none;
37
+ border-radius: 0 4px 4px 0;
12
38
  }
13
39
 
14
40
  main .method-description, main .aliases {
41
+ grid-column: 1 / span 2;
15
42
  padding-left: 1em;
16
43
  }
17
44
 
18
- body {
19
- /*
20
- * The default (300) can be too low contrast. Also, many fonts don't
21
- * distinguish between 300->400, so <em>...</em> had no effect.
22
- */
23
- font-weight: 400;
45
+ @media (max-width: 700px) {
46
+ main .method-header, main .method-controls, main .method-description {
47
+ grid-column: 1 / span 2;
48
+ margin: 0;
49
+ }
50
+ main .method-controls {
51
+ background: none;
52
+ }
24
53
  }
25
54
 
26
- @media only screen and (min-width: 600px) {
27
- nav {
28
- height: 100%;
29
- position: fixed;
30
- overflow-y: scroll;
31
- }
55
+ /***********************************************
56
+ * Description lists
57
+ ***********************************************/
32
58
 
33
- nav #class-metadata {
34
- margin-bottom: 5em;
35
- }
59
+ main dt {
60
+ margin-bottom: 0; /* override rdoc 6.8 */
61
+ float: unset; /* override rdoc 6.8 */
62
+ line-height: 1.5; /* matches `main p` */
63
+ }
64
+
65
+ main dl.note-list dt {
66
+ margin-right: 1em;
67
+ float: left;
68
+ }
69
+
70
+ main dl.note-list dt:has(+ dt) {
71
+ margin-right: 0.25em;
72
+ }
73
+
74
+ main dl.note-list dt:has(+ dt)::after {
75
+ content: ', ';
76
+ font-weight: normal;
77
+ }
78
+
79
+ main dd {
80
+ margin: 0 0 1em 1em;
81
+ }
82
+
83
+ main dd p:first-child {
84
+ margin-top: 0;
36
85
  }
@@ -1,68 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Registry for SASL authenticators used by Net::IMAP.
3
+ # Backward compatible delegators from Net::IMAP to Net::IMAP::SASL.
4
4
  module Net::IMAP::Authenticators
5
5
 
6
- # Adds an authenticator for Net::IMAP#authenticate to use. +mechanism+ is the
7
- # {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
8
- # implemented by +authenticator+ (for instance, <tt>"PLAIN"</tt>).
9
- #
10
- # The +authenticator+ must respond to +#new+ (or #call), receiving the
11
- # authenticator configuration and return a configured authentication session.
12
- # The authenticator session must respond to +#process+, receiving the server's
13
- # challenge and returning the client's response.
14
- #
15
- # See PlainAuthenticator, XOauth2Authenticator, and DigestMD5Authenticator for
16
- # examples.
17
- def add_authenticator(auth_type, authenticator)
18
- authenticators[auth_type] = authenticator
6
+ # Deprecated. Use Net::IMAP::SASL.add_authenticator instead.
7
+ def add_authenticator(...)
8
+ warn(
9
+ "%s.%s is deprecated. Use %s.%s instead." % [
10
+ Net::IMAP, __method__, Net::IMAP::SASL, __method__
11
+ ],
12
+ uplevel: 1, category: :deprecated
13
+ )
14
+ Net::IMAP::SASL.add_authenticator(...)
19
15
  end
20
16
 
21
- # :call-seq:
22
- # authenticator(mechanism, ...) -> authenticator
23
- # authenticator(mech, *creds, **props) {|prop, auth| val } -> authenticator
24
- # authenticator(mechanism, authnid, creds, authzid=nil) -> authenticator
25
- # authenticator(mechanism, **properties) -> authenticator
26
- # authenticator(mechanism) {|propname, authctx| value } -> authenticator
27
- #
28
- # Builds a new authentication session context for +mechanism+.
29
- #
30
- # [Note]
31
- # This method is intended for internal use by connection protocol code only.
32
- # Protocol client users should see refer to their client's documentation,
33
- # e.g. Net::IMAP#authenticate for Net::IMAP.
34
- #
35
- # The call signatures documented for this method are recommendations for
36
- # authenticator implementors. All arguments (other than +mechanism+) are
37
- # forwarded to the registered authenticator's +#new+ (or +#call+) method, and
38
- # each authenticator must document its own arguments.
39
- #
40
- # The returned object represents a single authentication exchange and <em>must
41
- # not</em> be reused for multiple authentication attempts.
42
- def authenticator(mechanism, *authargs, **properties, &callback)
43
- authenticator = authenticators.fetch(mechanism.upcase) do
44
- raise ArgumentError, 'unknown auth type - "%s"' % mechanism
45
- end
46
- if authenticator.respond_to?(:new)
47
- authenticator.new(*authargs, **properties, &callback)
48
- else
49
- authenticator.call(*authargs, **properties, &callback)
50
- end
51
- end
52
-
53
- private
54
-
55
- def authenticators
56
- @authenticators ||= {}
17
+ # Deprecated. Use Net::IMAP::SASL.authenticator instead.
18
+ def authenticator(...)
19
+ warn(
20
+ "%s.%s is deprecated. Use %s.%s instead." % [
21
+ Net::IMAP, __method__, Net::IMAP::SASL, __method__
22
+ ],
23
+ uplevel: 1, category: :deprecated
24
+ )
25
+ Net::IMAP::SASL.authenticator(...)
57
26
  end
58
27
 
28
+ Net::IMAP.extend self
59
29
  end
60
30
 
61
- Net::IMAP.extend Net::IMAP::Authenticators
31
+ class Net::IMAP
32
+ PlainAuthenticator = SASL::PlainAuthenticator # :nodoc:
33
+ deprecate_constant :PlainAuthenticator
62
34
 
63
- require_relative "authenticators/plain"
64
-
65
- require_relative "authenticators/login"
66
- require_relative "authenticators/cram_md5"
67
- require_relative "authenticators/digest_md5"
68
- require_relative "authenticators/xoauth2"
35
+ XOauth2Authenticator = SASL::XOAuth2Authenticator # :nodoc:
36
+ deprecate_constant :XOauth2Authenticator
37
+ end
@@ -3,6 +3,7 @@
3
3
  require "date"
4
4
 
5
5
  require_relative "errors"
6
+ require_relative "data_lite"
6
7
 
7
8
  module Net
8
9
  class IMAP < Protocol
@@ -40,10 +41,10 @@ module Net
40
41
  send_number_data(data)
41
42
  when Array
42
43
  send_list_data(data, tag)
43
- when Date
44
- send_date_data(data)
45
44
  when Time, DateTime
46
45
  send_time_data(data)
46
+ when Date
47
+ send_date_data(data)
47
48
  when Symbol
48
49
  send_symbol_data(data)
49
50
  else
@@ -52,13 +53,20 @@ module Net
52
53
  end
53
54
 
54
55
  def send_string_data(str, tag = nil)
55
- case str
56
- when ""
56
+ if str.empty?
57
57
  put_string('""')
58
- when /[\x80-\xff\r\n]/n
59
- # literal
58
+ elsif str.match?(/[\r\n]/n)
59
+ # literal, because multiline
60
60
  send_literal(str, tag)
61
- when /[(){ \x00-\x1f\x7f%*"\\]/n
61
+ elsif !str.ascii_only?
62
+ if @utf8_strings
63
+ # quoted string
64
+ send_quoted_string(str)
65
+ else
66
+ # literal, because of non-ASCII bytes
67
+ send_literal(str, tag)
68
+ end
69
+ elsif str.match?(/[(){ \x00-\x1f\x7f%*"\\]/n)
62
70
  # quoted string
63
71
  send_quoted_string(str)
64
72
  else
@@ -67,7 +75,7 @@ module Net
67
75
  end
68
76
 
69
77
  def send_quoted_string(str)
70
- put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
78
+ put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
71
79
  end
72
80
 
73
81
  def send_literal(str, tag = nil)
@@ -112,79 +120,95 @@ module Net
112
120
  put_string("\\" + symbol.to_s)
113
121
  end
114
122
 
115
- class RawData # :nodoc:
123
+ CommandData = Data.define(:data) do # :nodoc:
116
124
  def send_data(imap, tag)
117
- imap.__send__(:put_string, @data)
125
+ raise NoMethodError, "#{self.class} must implement #{__method__}"
118
126
  end
119
127
 
120
128
  def validate
121
129
  end
122
-
123
- private
124
-
125
- def initialize(data)
126
- @data = data
127
- end
128
130
  end
129
131
 
130
- class Atom # :nodoc:
132
+ class RawData < CommandData # :nodoc:
131
133
  def send_data(imap, tag)
132
- imap.__send__(:put_string, @data)
133
- end
134
-
135
- def validate
136
- end
137
-
138
- private
139
-
140
- def initialize(data)
141
- @data = data
134
+ imap.__send__(:put_string, data)
142
135
  end
143
136
  end
144
137
 
145
- class QuotedString # :nodoc:
138
+ class Atom < CommandData # :nodoc:
146
139
  def send_data(imap, tag)
147
- imap.__send__(:send_quoted_string, @data)
140
+ imap.__send__(:put_string, data)
148
141
  end
142
+ end
149
143
 
150
- def validate
151
- end
152
-
153
- private
154
-
155
- def initialize(data)
156
- @data = data
144
+ class QuotedString < CommandData # :nodoc:
145
+ def send_data(imap, tag)
146
+ imap.__send__(:send_quoted_string, data)
157
147
  end
158
148
  end
159
149
 
160
- class Literal # :nodoc:
150
+ class Literal < CommandData # :nodoc:
161
151
  def send_data(imap, tag)
162
- imap.__send__(:send_literal, @data, tag)
152
+ imap.__send__(:send_literal, data, tag)
163
153
  end
154
+ end
164
155
 
165
- def validate
156
+ class PartialRange < CommandData # :nodoc:
157
+ uint32_max = 2**32 - 1
158
+ POS_RANGE = 1..uint32_max
159
+ NEG_RANGE = -uint32_max..-1
160
+ Positive = ->{ (_1 in Range) and POS_RANGE.cover?(_1) }
161
+ Negative = ->{ (_1 in Range) and NEG_RANGE.cover?(_1) }
162
+
163
+ def initialize(data:)
164
+ min, max = case data
165
+ in Range
166
+ data.minmax.map { Integer _1 }
167
+ in ResponseParser::Patterns::PARTIAL_RANGE
168
+ data.split(":").map { Integer _1 }.minmax
169
+ else
170
+ raise ArgumentError, "invalid partial range input: %p" % [data]
171
+ end
172
+ data = min..max
173
+ unless data in Positive | Negative
174
+ raise ArgumentError, "invalid partial-range: %p" % [data]
175
+ end
176
+ super
177
+ rescue TypeError, RangeError
178
+ raise ArgumentError, "expected range min/max to be Integers"
166
179
  end
167
180
 
168
- private
181
+ def formatted = "%d:%d" % data.minmax
169
182
 
170
- def initialize(data)
171
- @data = data
183
+ def send_data(imap, tag)
184
+ imap.__send__(:put_string, formatted)
172
185
  end
173
186
  end
174
187
 
175
- class MessageSet # :nodoc:
188
+ # *DEPRECATED*. Replaced by SequenceSet.
189
+ class MessageSet < CommandData # :nodoc:
176
190
  def send_data(imap, tag)
177
- imap.__send__(:put_string, format_internal(@data))
191
+ imap.__send__(:put_string, format_internal(data))
178
192
  end
179
193
 
180
194
  def validate
181
- validate_internal(@data)
195
+ validate_internal(data)
182
196
  end
183
197
 
184
198
  private
185
199
 
186
- def initialize(data)
187
- @data = data
200
+ def initialize(data:)
201
+ super
202
+ warn("DEPRECATED: #{MessageSet} should be replaced with #{SequenceSet}.",
203
+ uplevel: 1, category: :deprecated)
204
+ begin
205
+ # to ensure the input works with SequenceSet, too
206
+ SequenceSet.new(data)
207
+ rescue
208
+ warn "MessageSet input is incompatible with SequenceSet: [%s] %s" % [
209
+ $!.class, $!.message
210
+ ]
211
+ end
188
212
  end
189
213
 
190
214
  def format_internal(data)
@@ -228,22 +252,18 @@ module Net
228
252
  end
229
253
  end
230
254
 
231
- class ClientID # :nodoc:
255
+ class ClientID < CommandData # :nodoc:
232
256
 
233
257
  def send_data(imap, tag)
234
- imap.__send__(:send_data, format_internal(@data), tag)
258
+ imap.__send__(:send_data, format_internal(data), tag)
235
259
  end
236
260
 
237
261
  def validate
238
- validate_internal(@data)
262
+ validate_internal(data)
239
263
  end
240
264
 
241
265
  private
242
266
 
243
- def initialize(data)
244
- @data = data
245
- end
246
-
247
267
  def validate_internal(client_id)
248
268
  client_id.to_h.each do |k,v|
249
269
  unless StringFormatter.valid_string?(k)