net-imap 0.3.7 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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/response_data.rb +46 -41
- data/lib/net/imap/response_parser/parser_utils.rb +230 -0
- data/lib/net/imap/response_parser.rb +665 -627
- data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
- data/lib/net/imap/sasl/authentication_exchange.rb +107 -0
- data/lib/net/imap/sasl/authenticators.rb +118 -0
- data/lib/net/imap/sasl/client_adapter.rb +72 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +21 -11
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +180 -0
- data/lib/net/imap/sasl/external_authenticator.rb +83 -0
- data/lib/net/imap/sasl/gs2_header.rb +80 -0
- data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +25 -16
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
- data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
- data/lib/net/imap/sasl/protocol_adapters.rb +45 -0
- data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
- data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
- data/lib/net/imap/sasl/stringprep.rb +6 -66
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
- data/lib/net/imap/sasl.rb +144 -43
- data/lib/net/imap/sasl_adapter.rb +21 -0
- 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 +976 -590
- data/net-imap.gemspec +1 -1
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +82 -60
- metadata +30 -11
- data/lib/net/imap/authenticators/digest_md5.rb +0 -115
- data/lib/net/imap/authenticators/plain.rb +0 -41
- data/lib/net/imap/authenticators/xoauth2.rb +0 -20
- 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
|