net-imap 0.3.9 → 0.4.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/workflows/pages.yml +46 -0
- data/.github/workflows/test.yml +5 -12
- data/Gemfile +1 -0
- data/README.md +15 -4
- data/Rakefile +0 -7
- data/benchmarks/generate_parser_benchmarks +52 -0
- data/benchmarks/parser.yml +578 -0
- data/benchmarks/stringprep.yml +1 -1
- data/lib/net/imap/authenticators.rb +26 -57
- data/lib/net/imap/command_data.rb +13 -6
- data/lib/net/imap/deprecated_client_options.rb +139 -0
- data/lib/net/imap/errors.rb +0 -34
- data/lib/net/imap/response_data.rb +46 -41
- data/lib/net/imap/response_parser/parser_utils.rb +230 -0
- data/lib/net/imap/response_parser.rb +667 -649
- data/lib/net/imap/sasl/anonymous_authenticator.rb +68 -0
- data/lib/net/imap/sasl/authenticators.rb +112 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +15 -9
- data/lib/net/imap/{authenticators/digest_md5.rb → sasl/digest_md5_authenticator.rb} +74 -21
- data/lib/net/imap/sasl/external_authenticator.rb +62 -0
- data/lib/net/imap/sasl/gs2_header.rb +80 -0
- data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +19 -14
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +164 -0
- data/lib/net/imap/sasl/plain_authenticator.rb +93 -0
- data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
- data/lib/net/imap/sasl/scram_authenticator.rb +278 -0
- data/lib/net/imap/sasl/stringprep.rb +6 -66
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +88 -0
- data/lib/net/imap/sasl.rb +139 -44
- data/lib/net/imap/stringprep/nameprep.rb +70 -0
- data/lib/net/imap/stringprep/saslprep.rb +69 -0
- data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
- data/lib/net/imap/stringprep/tables.rb +146 -0
- data/lib/net/imap/stringprep/trace.rb +85 -0
- data/lib/net/imap/stringprep.rb +159 -0
- data/lib/net/imap.rb +987 -690
- data/net-imap.gemspec +1 -1
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +82 -60
- metadata +30 -13
- data/lib/net/imap/authenticators/plain.rb +0 -41
- data/lib/net/imap/authenticators/xoauth2.rb +0 -20
- data/lib/net/imap/response_reader.rb +0 -75
- data/lib/net/imap/sasl/saslprep.rb +0 -55
- data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
- data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# Regexps and utility methods for implementing stringprep profiles. The
|
7
|
+
# \StringPrep algorithm is defined by
|
8
|
+
# {RFC-3454}[https://www.rfc-editor.org/rfc/rfc3454.html]. Each
|
9
|
+
# codepoint table defined in the RFC-3454 appendices is matched by a Regexp
|
10
|
+
# defined in this module.
|
11
|
+
module StringPrep
|
12
|
+
autoload :NamePrep, File.expand_path("stringprep/nameprep", __dir__)
|
13
|
+
autoload :SASLprep, File.expand_path("stringprep/saslprep", __dir__)
|
14
|
+
autoload :Tables, File.expand_path("stringprep/tables", __dir__)
|
15
|
+
autoload :Trace, File.expand_path("stringprep/trace", __dir__)
|
16
|
+
|
17
|
+
# ArgumentError raised when +string+ is invalid for the stringprep
|
18
|
+
# +profile+.
|
19
|
+
class StringPrepError < ArgumentError
|
20
|
+
attr_reader :string, :profile
|
21
|
+
|
22
|
+
def initialize(*args, string: nil, profile: nil)
|
23
|
+
@string = -string.to_str unless string.nil?
|
24
|
+
@profile = -profile.to_str unless profile.nil?
|
25
|
+
super(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# StringPrepError raised when +string+ contains a codepoint prohibited by
|
30
|
+
# +table+.
|
31
|
+
class ProhibitedCodepoint < StringPrepError
|
32
|
+
attr_reader :table
|
33
|
+
|
34
|
+
def initialize(table, *args, **kwargs)
|
35
|
+
@table = table
|
36
|
+
details = (title = Tables::TITLES[table]) ?
|
37
|
+
"%s [%s]" % [title, table] : table
|
38
|
+
message = "String contains a prohibited codepoint: %s" % [details]
|
39
|
+
super(message, *args, **kwargs)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# StringPrepError raised when +string+ contains bidirectional characters
|
44
|
+
# which violate the StringPrep requirements.
|
45
|
+
class BidiStringError < StringPrepError
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a Regexp matching the given +table+ name.
|
49
|
+
def self.[](table)
|
50
|
+
Tables::REGEXPS.fetch(table)
|
51
|
+
end
|
52
|
+
|
53
|
+
module_function
|
54
|
+
|
55
|
+
# >>>
|
56
|
+
# 1. Map -- For each character in the input, check if it has a mapping
|
57
|
+
# and, if so, replace it with its mapping. This is described in
|
58
|
+
# section 3.
|
59
|
+
#
|
60
|
+
# 2. Normalize -- Possibly normalize the result of step 1 using Unicode
|
61
|
+
# normalization. This is described in section 4.
|
62
|
+
#
|
63
|
+
# 3. Prohibit -- Check for any characters that are not allowed in the
|
64
|
+
# output. If any are found, return an error. This is described in
|
65
|
+
# section 5.
|
66
|
+
#
|
67
|
+
# 4. Check bidi -- Possibly check for right-to-left characters, and if
|
68
|
+
# any are found, make sure that the whole string satisfies the
|
69
|
+
# requirements for bidirectional strings. If the string does not
|
70
|
+
# satisfy the requirements for bidirectional strings, return an
|
71
|
+
# error. This is described in section 6.
|
72
|
+
#
|
73
|
+
# The above steps MUST be performed in the order given to comply with
|
74
|
+
# this specification.
|
75
|
+
#
|
76
|
+
def stringprep(string,
|
77
|
+
maps:,
|
78
|
+
normalization:,
|
79
|
+
prohibited:,
|
80
|
+
**opts)
|
81
|
+
string = string.encode("UTF-8") # also dups (and raises invalid encoding)
|
82
|
+
map_tables!(string, *maps) if maps
|
83
|
+
string.unicode_normalize!(normalization) if normalization
|
84
|
+
check_prohibited!(string, *prohibited, **opts) if prohibited
|
85
|
+
string
|
86
|
+
end
|
87
|
+
|
88
|
+
def map_tables!(string, *tables)
|
89
|
+
tables.each do |table|
|
90
|
+
regexp, replacements = Tables::MAPPINGS.fetch(table)
|
91
|
+
string.gsub!(regexp, replacements)
|
92
|
+
end
|
93
|
+
string
|
94
|
+
end
|
95
|
+
|
96
|
+
# Checks +string+ for any codepoint in +tables+. Raises a
|
97
|
+
# ProhibitedCodepoint describing the first matching table.
|
98
|
+
#
|
99
|
+
# Also checks bidirectional characters, when <tt>bidi: true</tt>, which may
|
100
|
+
# raise a BidiStringError.
|
101
|
+
#
|
102
|
+
# +profile+ is an optional string which will be added to any exception that
|
103
|
+
# is raised (it does not affect behavior).
|
104
|
+
def check_prohibited!(string,
|
105
|
+
*tables,
|
106
|
+
bidi: false,
|
107
|
+
unassigned: "A.1",
|
108
|
+
stored: false,
|
109
|
+
profile: nil)
|
110
|
+
tables = Tables::TITLES.keys.grep(/^C/) if tables.empty?
|
111
|
+
tables |= [unassigned] if stored
|
112
|
+
tables |= %w[C.8] if bidi
|
113
|
+
table = tables.find {|t|
|
114
|
+
case t
|
115
|
+
when String then Tables::REGEXPS.fetch(t).match?(string)
|
116
|
+
when Regexp then t.match?(string)
|
117
|
+
else raise ArgumentError, "only table names and regexps can be checked"
|
118
|
+
end
|
119
|
+
}
|
120
|
+
if table
|
121
|
+
raise ProhibitedCodepoint.new(
|
122
|
+
table, string: string, profile: profile
|
123
|
+
)
|
124
|
+
end
|
125
|
+
check_bidi!(string, profile: profile) if bidi
|
126
|
+
end
|
127
|
+
|
128
|
+
# Checks that +string+ obeys all of the "Bidirectional Characters"
|
129
|
+
# requirements in RFC-3454, §6:
|
130
|
+
#
|
131
|
+
# * The characters in \StringPrep\[\"C.8\"] MUST be prohibited
|
132
|
+
# * If a string contains any RandALCat character, the string MUST NOT
|
133
|
+
# contain any LCat character.
|
134
|
+
# * If a string contains any RandALCat character, a RandALCat
|
135
|
+
# character MUST be the first character of the string, and a
|
136
|
+
# RandALCat character MUST be the last character of the string.
|
137
|
+
#
|
138
|
+
# This is usually combined with #check_prohibited!, so table "C.8" is only
|
139
|
+
# checked when <tt>c_8: true</tt>.
|
140
|
+
#
|
141
|
+
# Raises either ProhibitedCodepoint or BidiStringError unless all
|
142
|
+
# requirements are met. +profile+ is an optional string which will be
|
143
|
+
# added to any exception that is raised (it does not affect behavior).
|
144
|
+
def check_bidi!(string, c_8: false, profile: nil)
|
145
|
+
check_prohibited!(string, "C.8", profile: profile) if c_8
|
146
|
+
if Tables::BIDI_FAILS_REQ2.match?(string)
|
147
|
+
raise BidiStringError.new(
|
148
|
+
Tables::BIDI_DESC_REQ2, string: string, profile: profile,
|
149
|
+
)
|
150
|
+
elsif Tables::BIDI_FAILS_REQ3.match?(string)
|
151
|
+
raise BidiStringError.new(
|
152
|
+
Tables::BIDI_DESC_REQ3, string: string, profile: profile,
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|