net-imap 0.3.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
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 +21 -9
  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 -31
  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: 7942e0dcda47ac417806fb03aec58b66b8bac3ccf51f53f2f7ac2c6a588e35af
4
- data.tar.gz: 38530b6af862085a9d1ff3c59c6536152acc15bb7d925d8547cd4feebea42872
3
+ metadata.gz: cdbdda0ed73da899ec338f66022a16104562d3701c568b0a6d4897270a608ac5
4
+ data.tar.gz: b6a7ec70776b32f8eb57d01a0869503eb5d76f719ac091eb92e0206608e936e9
5
5
  SHA512:
6
- metadata.gz: 5bd29fb28b2b3ed9b1a0589e38f8844c47e671006d50deb4462fe5e8cfd97ad923ef0af3114a094d31cbb5943ff0ac9a15e549e67ac08be68eb04a0915eae67f
7
- data.tar.gz: 7562e44288caa3538203d415f4d0b1dc4a44a77428e40c4b44046b86fb26b1abc2a4226229f4f494e54f21240cc96dea9106afded971829ec5873f507adab7c9
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)