net-imap 0.4.1 → 0.4.4
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/.gitignore +1 -0
- data/Gemfile +2 -0
- data/lib/net/imap/errors.rb +20 -0
- data/lib/net/imap/response_data.rb +46 -6
- data/lib/net/imap/response_parser/parser_utils.rb +14 -4
- data/lib/net/imap/response_parser.rb +609 -368
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -2
- data/lib/net/imap/sasl/authenticators.rb +2 -2
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +7 -3
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +20 -8
- data/lib/net/imap/sasl/external_authenticator.rb +26 -5
- data/lib/net/imap/sasl/login_authenticator.rb +7 -3
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +73 -38
- data/lib/net/imap/sasl/plain_authenticator.rb +19 -11
- data/lib/net/imap/sasl/scram_authenticator.rb +19 -10
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +34 -16
- data/lib/net/imap.rb +26 -28
- data/net-imap.gemspec +3 -2
- data/rakelib/benchmarks.rake +98 -0
- metadata +3 -6
- data/benchmarks/generate_parser_benchmarks +0 -52
- data/benchmarks/parser.yml +0 -578
- data/benchmarks/stringprep.yml +0 -65
- data/benchmarks/table-regexps.yml +0 -39
data/lib/net/imap.rb
CHANGED
@@ -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.
|
665
|
+
VERSION = "0.4.4"
|
666
666
|
|
667
667
|
# Aliases for supported capabilities, to be used with the #enable command.
|
668
668
|
ENABLE_ALIASES = {
|
@@ -1045,7 +1045,7 @@ module Net
|
|
1045
1045
|
# ===== Capabilities
|
1046
1046
|
#
|
1047
1047
|
# The server's capabilities must include +ID+
|
1048
|
-
# [RFC2971[https://tools.ietf.org/html/rfc2971]]
|
1048
|
+
# [RFC2971[https://tools.ietf.org/html/rfc2971]].
|
1049
1049
|
def id(client_id=nil)
|
1050
1050
|
synchronize do
|
1051
1051
|
send_command("ID", ClientID.new(client_id))
|
@@ -1058,7 +1058,7 @@ module Net
|
|
1058
1058
|
#
|
1059
1059
|
# This allows the server to send unsolicited untagged EXPUNGE #responses,
|
1060
1060
|
# but does not execute any client request. \IMAP servers are permitted to
|
1061
|
-
# send unsolicited untagged responses at any time, except for
|
1061
|
+
# send unsolicited untagged responses at any time, except for +EXPUNGE+:
|
1062
1062
|
#
|
1063
1063
|
# * +EXPUNGE+ can only be sent while a command is in progress.
|
1064
1064
|
# * +EXPUNGE+ must _not_ be sent during #fetch, #store, or #search.
|
@@ -1143,10 +1143,7 @@ module Net
|
|
1143
1143
|
end
|
1144
1144
|
|
1145
1145
|
# :call-seq:
|
1146
|
-
# authenticate(mechanism, *,
|
1147
|
-
# sasl_ir: true,
|
1148
|
-
# registry: Net::IMAP::SASL.authenticators,
|
1149
|
-
# **, &) -> ok_resp
|
1146
|
+
# authenticate(mechanism, *, sasl_ir: true, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
1150
1147
|
#
|
1151
1148
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
1152
1149
|
# to authenticate the client. If successful, the connection enters the
|
@@ -1330,7 +1327,7 @@ module Net
|
|
1330
1327
|
# the server may return an untagged "NO" response with a "UIDNOTSTICKY"
|
1331
1328
|
# response code indicating that the mailstore does not support persistent
|
1332
1329
|
# UIDs:
|
1333
|
-
#
|
1330
|
+
# imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"
|
1334
1331
|
def select(mailbox)
|
1335
1332
|
synchronize do
|
1336
1333
|
@responses.clear
|
@@ -1420,10 +1417,10 @@ module Net
|
|
1420
1417
|
# to the client. +refname+ provides a context (for instance, a base
|
1421
1418
|
# directory in a directory-based mailbox hierarchy). +mailbox+ specifies a
|
1422
1419
|
# mailbox or (via wildcards) mailboxes under that context. Two wildcards
|
1423
|
-
# may be used in +mailbox+:
|
1424
|
-
# the hierarchy delimiter (for instance,
|
1425
|
-
# directory-based mailbox hierarchy); and
|
1426
|
-
# *except* the hierarchy delimiter.
|
1420
|
+
# may be used in +mailbox+: <tt>"*"</tt>, which matches all characters
|
1421
|
+
# *including* the hierarchy delimiter (for instance, "/" on a UNIX-hosted
|
1422
|
+
# directory-based mailbox hierarchy); and <tt>"%"</tt>, which matches all
|
1423
|
+
# characters *except* the hierarchy delimiter.
|
1427
1424
|
#
|
1428
1425
|
# If +refname+ is empty, +mailbox+ is used directly to determine
|
1429
1426
|
# which mailboxes to match. If +mailbox+ is empty, the root
|
@@ -1471,16 +1468,16 @@ module Net
|
|
1471
1468
|
# servers, then folder creation (and listing, moving, etc) can lead to
|
1472
1469
|
# errors.
|
1473
1470
|
#
|
1474
|
-
# From RFC2342:
|
1475
|
-
#
|
1476
|
-
# Although typically a server will support only a single Personal
|
1471
|
+
# From RFC2342[https://tools.ietf.org/html/rfc2342]:
|
1472
|
+
# >>>
|
1473
|
+
# <em>Although typically a server will support only a single Personal
|
1477
1474
|
# Namespace, and a single Other User's Namespace, circumstances exist
|
1478
1475
|
# where there MAY be multiples of these, and a client MUST be prepared
|
1479
1476
|
# for them. If a client is configured such that it is required to create
|
1480
1477
|
# a certain mailbox, there can be circumstances where it is unclear which
|
1481
1478
|
# Personal Namespaces it should create the mailbox in. In these
|
1482
1479
|
# situations a client SHOULD let the user select which namespaces to
|
1483
|
-
# create the mailbox in
|
1480
|
+
# create the mailbox in.</em>
|
1484
1481
|
#
|
1485
1482
|
# Related: #list, Namespaces, Namespace
|
1486
1483
|
#
|
@@ -1668,9 +1665,9 @@ module Net
|
|
1668
1665
|
# or more attributes whose statuses are to be requested. Supported
|
1669
1666
|
# attributes include:
|
1670
1667
|
#
|
1671
|
-
#
|
1672
|
-
#
|
1673
|
-
#
|
1668
|
+
# MESSAGES:: the number of messages in the mailbox.
|
1669
|
+
# RECENT:: the number of recent messages in the mailbox.
|
1670
|
+
# UNSEEN:: the number of unseen messages in the mailbox.
|
1674
1671
|
#
|
1675
1672
|
# The return value is a hash of attributes. For example:
|
1676
1673
|
#
|
@@ -1935,11 +1932,11 @@ module Net
|
|
1935
1932
|
# to alter data associated with messages in the mailbox, in particular their
|
1936
1933
|
# flags. The +set+ parameter is a number, an array of numbers, or a Range
|
1937
1934
|
# object. Each number is a message sequence number. +attr+ is the name of a
|
1938
|
-
# data item to store:
|
1939
|
-
# provided one,
|
1940
|
-
# remove them. +flags+ is a list of flags.
|
1935
|
+
# data item to store: <tt>"FLAGS"</tt> will replace the message's flag list
|
1936
|
+
# with the provided one, <tt>"+FLAGS"</tt> will add the provided flags, and
|
1937
|
+
# <tt>"-FLAGS"</tt> will remove them. +flags+ is a list of flags.
|
1941
1938
|
#
|
1942
|
-
# The return value is an array of FetchData
|
1939
|
+
# The return value is an array of FetchData.
|
1943
1940
|
#
|
1944
1941
|
# Related: #uid_store
|
1945
1942
|
#
|
@@ -2191,7 +2188,7 @@ module Net
|
|
2191
2188
|
.join(' ')
|
2192
2189
|
synchronize do
|
2193
2190
|
send_command("ENABLE #{capabilities}")
|
2194
|
-
result = clear_responses("ENABLED").last
|
2191
|
+
result = clear_responses("ENABLED").last || []
|
2195
2192
|
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
2196
2193
|
@utf8_strings ||= result.include? "IMAP4REV2"
|
2197
2194
|
result
|
@@ -2522,7 +2519,8 @@ module Net
|
|
2522
2519
|
when /\A(?:BAD)\z/ni
|
2523
2520
|
raise BadResponseError, resp
|
2524
2521
|
else
|
2525
|
-
|
2522
|
+
disconnect
|
2523
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
|
2526
2524
|
end
|
2527
2525
|
end
|
2528
2526
|
|
@@ -2644,7 +2642,7 @@ module Net
|
|
2644
2642
|
else
|
2645
2643
|
send_command(cmd, *keys)
|
2646
2644
|
end
|
2647
|
-
clear_responses("SEARCH").last
|
2645
|
+
clear_responses("SEARCH").last || []
|
2648
2646
|
end
|
2649
2647
|
end
|
2650
2648
|
|
@@ -2693,7 +2691,7 @@ module Net
|
|
2693
2691
|
normalize_searching_criteria(search_keys)
|
2694
2692
|
synchronize do
|
2695
2693
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2696
|
-
clear_responses("SORT").last
|
2694
|
+
clear_responses("SORT").last || []
|
2697
2695
|
end
|
2698
2696
|
end
|
2699
2697
|
|
@@ -2706,7 +2704,7 @@ module Net
|
|
2706
2704
|
normalize_searching_criteria(search_keys)
|
2707
2705
|
synchronize do
|
2708
2706
|
send_command(cmd, algorithm, charset, *search_keys)
|
2709
|
-
clear_responses("THREAD").last
|
2707
|
+
clear_responses("THREAD").last || []
|
2710
2708
|
end
|
2711
2709
|
end
|
2712
2710
|
|
data/net-imap.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
name = File.basename(__FILE__, ".gemspec")
|
4
4
|
version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
|
5
|
-
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
|
5
|
+
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb"), :encoding=> 'utf-8') do |line|
|
6
6
|
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
|
7
7
|
end rescue nil
|
8
8
|
end
|
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
# Specify which files should be added to the gem when it is released.
|
26
26
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
27
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
28
|
-
`git ls-files -z 2>/dev/null`.split("\x0")
|
28
|
+
`git ls-files -z 2>/dev/null`.split("\x0")
|
29
|
+
.reject {|f| f.match(%r{^(bin|test|spec|benchmarks|features|rfcs)/}) }
|
29
30
|
end
|
30
31
|
spec.bindir = "exe"
|
31
32
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
PARSER_TEST_FIXTURES = FileList.new "test/net/imap/fixtures/response_parser/*.yml"
|
4
|
+
CLOBBER.include "benchmarks/parser.yml"
|
5
|
+
CLEAN.include "benchmarks/Gemfile-*"
|
6
|
+
|
7
|
+
BENCHMARK_INIT = <<RUBY
|
8
|
+
require "yaml"
|
9
|
+
require "net/imap"
|
10
|
+
|
11
|
+
def load_response(file, name)
|
12
|
+
YAML.unsafe_load_file(file).dig(:tests, name, :response)
|
13
|
+
.force_encoding "ASCII-8BIT" \\
|
14
|
+
or abort "ERRORO: missing %p fixture data in %p" % [name, file]
|
15
|
+
end
|
16
|
+
|
17
|
+
parser = Net::IMAP::ResponseParser.new
|
18
|
+
RUBY
|
19
|
+
|
20
|
+
PER_BENCHMARK_PRELUDE = <<RUBY
|
21
|
+
response = load_response(%p,
|
22
|
+
%p)
|
23
|
+
RUBY
|
24
|
+
|
25
|
+
file "benchmarks/parser.yml" => PARSER_TEST_FIXTURES do |t|
|
26
|
+
require "yaml"
|
27
|
+
require "pathname"
|
28
|
+
require "net/imap"
|
29
|
+
|
30
|
+
path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
|
31
|
+
files = path.glob("*.yml")
|
32
|
+
tests = files.flat_map {|file|
|
33
|
+
file.read
|
34
|
+
.gsub(%r{([-:]) !ruby/struct:\S+}) { $1 }
|
35
|
+
.then {
|
36
|
+
YAML.safe_load(_1, filename: file,
|
37
|
+
permitted_classes: [Symbol, Regexp], aliases: true)
|
38
|
+
}
|
39
|
+
.fetch(:tests)
|
40
|
+
.select {|test_name, test|
|
41
|
+
:parser_assert_equal == test.fetch(:test_type) {
|
42
|
+
test.key?(:expected) ? :parser_assert_equal : :parser_pending
|
43
|
+
}
|
44
|
+
}
|
45
|
+
.map {|test_name, _|
|
46
|
+
[file.relative_path_from(__dir__).to_s, test_name.to_s]
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
benchmarks = tests.map {|file, fixture_name|
|
51
|
+
{"name" => fixture_name.delete_prefix("test_"),
|
52
|
+
"prelude" => PER_BENCHMARK_PRELUDE % [file, fixture_name],
|
53
|
+
"script" => "parser.parse(response)"}
|
54
|
+
}
|
55
|
+
.sort_by { _1["name"] }
|
56
|
+
|
57
|
+
YAML.dump({"prelude" => BENCHMARK_INIT, "benchmark" => benchmarks})
|
58
|
+
.then { File.write t.name, _1 }
|
59
|
+
end
|
60
|
+
|
61
|
+
namespace :benchmarks do
|
62
|
+
desc "Generate benchmarks from fixture data"
|
63
|
+
task :generate => "benchmarks/parser.yml"
|
64
|
+
|
65
|
+
desc "run the parser benchmarks comparing multiple gem versions"
|
66
|
+
task :compare => :generate do |task, args|
|
67
|
+
cd Pathname.new(__dir__) + ".."
|
68
|
+
current = `git describe --tags --dirty`.chomp
|
69
|
+
current = "dev" if current.empty?
|
70
|
+
versions = args.to_a
|
71
|
+
if versions.empty?
|
72
|
+
latest = %x{git describe --tags --abbrev=0 --match 'v*.*.*'}.chomp
|
73
|
+
versions = latest.empty? ? [] : [latest.delete_prefix("v")]
|
74
|
+
end
|
75
|
+
versions = versions.to_h { [_1, "Gemfile-v#{_1}"] }
|
76
|
+
cd "benchmarks" do
|
77
|
+
versions.each do |version, gemfile|
|
78
|
+
File.write gemfile, <<~RUBY
|
79
|
+
# frozen_string_literal: true
|
80
|
+
source "https://rubygems.org"
|
81
|
+
gem "net-imap", #{version.dump}
|
82
|
+
RUBY
|
83
|
+
end
|
84
|
+
versions = {current => "../Gemfile" , **versions}.map {
|
85
|
+
"%s::/usr/bin/env BUNDLE_GEMFILE=%s ruby" % _1
|
86
|
+
}.join(";")
|
87
|
+
|
88
|
+
extra = ENV.fetch("BENCHMARK_ARGS", "").shellsplit
|
89
|
+
|
90
|
+
sh("benchmark-driver",
|
91
|
+
"--bundler",
|
92
|
+
"-e", versions,
|
93
|
+
"parser.yml",
|
94
|
+
*extra)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
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.
|
4
|
+
version: 0.4.4
|
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-
|
12
|
+
date: 2023-11-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-protocol
|
@@ -83,10 +83,6 @@ files:
|
|
83
83
|
- LICENSE.txt
|
84
84
|
- README.md
|
85
85
|
- Rakefile
|
86
|
-
- benchmarks/generate_parser_benchmarks
|
87
|
-
- benchmarks/parser.yml
|
88
|
-
- benchmarks/stringprep.yml
|
89
|
-
- benchmarks/table-regexps.yml
|
90
86
|
- docs/styles.css
|
91
87
|
- lib/net/imap.rb
|
92
88
|
- lib/net/imap/authenticators.rb
|
@@ -123,6 +119,7 @@ files:
|
|
123
119
|
- lib/net/imap/stringprep/tables.rb
|
124
120
|
- lib/net/imap/stringprep/trace.rb
|
125
121
|
- net-imap.gemspec
|
122
|
+
- rakelib/benchmarks.rake
|
126
123
|
- rakelib/rdoc.rake
|
127
124
|
- rakelib/rfcs.rake
|
128
125
|
- rakelib/saslprep.rake
|
@@ -1,52 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "yaml"
|
4
|
-
require "pathname"
|
5
|
-
require "net/imap"
|
6
|
-
|
7
|
-
path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
|
8
|
-
files = path.glob("*.yml")
|
9
|
-
tests = files.flat_map {|file|
|
10
|
-
file.to_s
|
11
|
-
.then { YAML.unsafe_load_file _1 }
|
12
|
-
.fetch(:tests)
|
13
|
-
.select {|test_name, test|
|
14
|
-
:parser_assert_equal == test.fetch(:test_type) {
|
15
|
-
test.key?(:expected) ? :parser_assert_equal : :parser_pending
|
16
|
-
}
|
17
|
-
}
|
18
|
-
.map {|test_name, _|
|
19
|
-
[
|
20
|
-
file.relative_path_from(__dir__).to_s,
|
21
|
-
test_name.to_s,
|
22
|
-
]
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
26
|
-
init = <<RUBY
|
27
|
-
require "yaml"
|
28
|
-
require "net/imap"
|
29
|
-
|
30
|
-
def load_response(file, name)
|
31
|
-
YAML.unsafe_load_file(file).dig(:tests, name, :response)
|
32
|
-
.force_encoding "ASCII-8BIT" \\
|
33
|
-
or abort "ERRORO: missing %p fixture data in %p" % [name, file]
|
34
|
-
end
|
35
|
-
|
36
|
-
parser = Net::IMAP::ResponseParser.new
|
37
|
-
RUBY
|
38
|
-
|
39
|
-
prelude = <<RUBY
|
40
|
-
response = load_response(%p,
|
41
|
-
%p)
|
42
|
-
RUBY
|
43
|
-
script = "parser.parse(response)"
|
44
|
-
|
45
|
-
benchmarks = tests.map {|file, fixture_name|
|
46
|
-
name = fixture_name.delete_prefix("test_")
|
47
|
-
{name:, prelude: prelude % [file, fixture_name], script:}
|
48
|
-
.transform_keys(&:to_s)
|
49
|
-
}
|
50
|
-
.sort_by { _1["name"] }
|
51
|
-
|
52
|
-
puts YAML.dump({"prelude" => init, "benchmark" => benchmarks})
|