net-imap 0.3.1 → 0.3.2
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 +3 -0
- data/Rakefile +4 -1
- data/benchmarks/stringprep.yml +65 -0
- data/benchmarks/table-regexps.yml +39 -0
- data/docs/styles.css +36 -0
- data/lib/net/imap/command_data.rb +8 -11
- data/lib/net/imap/data_encoding.rb +101 -5
- data/lib/net/imap/errors.rb +1 -1
- data/lib/net/imap/flags.rb +104 -77
- data/lib/net/imap/response_data.rb +1077 -317
- data/lib/net/imap/response_parser.rb +66 -1
- data/lib/net/imap/sasl/saslprep.rb +55 -0
- data/lib/net/imap/sasl/saslprep_tables.rb +98 -0
- data/lib/net/imap/sasl/stringprep.rb +68 -0
- data/lib/net/imap/sasl/stringprep_tables.rb +153 -0
- data/lib/net/imap/sasl.rb +78 -0
- data/lib/net/imap.rb +272 -77
- data/net-imap.gemspec +2 -0
- data/rakelib/rdoc.rake +70 -0
- data/rakelib/rfcs.rake +166 -0
- data/rakelib/saslprep.rake +30 -0
- data/rakelib/string_prep_tables_generator.rb +423 -0
- metadata +28 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85c732bea9deedc8787a80daee38991be448a03e593f4653d91067f16d2ed699
|
4
|
+
data.tar.gz: 6a1880f7da062779af60bf190da72bc415b0f11ee5f04296a08d97bbf82233ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '073296843ad22467cf1ab9e31adc6696f35a4fe8a81a3003e6ea0292a4fdb643f772355970d62531dae1bf53592ea1a99ae56e7ea0b4daf9c82719e6fefbf959'
|
7
|
+
data.tar.gz: c1aa2ed380ccf9adedca1bce427bc92629d2737f8439bccf697130460ef2b76e31ebf261563e505d0e206d075e6da52b278c772d3b768f12a14794b948a90667
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
2
4
|
require "rake/testtask"
|
5
|
+
require "rake/clean"
|
3
6
|
|
4
7
|
Rake::TestTask.new(:test) do |t|
|
5
8
|
t.libs << "test/lib"
|
6
|
-
t.ruby_opts << "-rhelper"
|
9
|
+
t.ruby_opts << "-rbundler/setup -rhelper"
|
7
10
|
t.test_files = FileList["test/**/test_*.rb"]
|
8
11
|
end
|
9
12
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
---
|
2
|
+
prelude: |
|
3
|
+
begin
|
4
|
+
require "mongo" # gem install mongo
|
5
|
+
require "idn" # gem install idn-ruby
|
6
|
+
rescue LoadError
|
7
|
+
warn "You must 'gem install mongo idn-ruby' for this benchmark."
|
8
|
+
raise
|
9
|
+
end
|
10
|
+
|
11
|
+
MStrPrep = Mongo::Auth::StringPrep
|
12
|
+
|
13
|
+
# this indirection will slow it down a little bit
|
14
|
+
def mongo_saslprep(string)
|
15
|
+
MStrPrep.prepare(string,
|
16
|
+
MStrPrep::Profiles::SASL::MAPPINGS,
|
17
|
+
MStrPrep::Profiles::SASL::PROHIBITED,
|
18
|
+
normalize: true,
|
19
|
+
bidi: true)
|
20
|
+
rescue Mongo::Error::FailedStringPrepValidation
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
$LOAD_PATH.unshift "./lib"
|
25
|
+
require "net/imap"
|
26
|
+
def net_imap_saslprep(string)
|
27
|
+
Net::IMAP::SASL::SASLprep.saslprep string, exception: false
|
28
|
+
end
|
29
|
+
|
30
|
+
def libidn_saslprep(string)
|
31
|
+
IDN::Stringprep.with_profile(string, "SASLprep")
|
32
|
+
rescue IDN::Stringprep::StringprepError
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
benchmark:
|
37
|
+
- net_imap_saslprep "I\u00ADX" # RFC example 1. IX
|
38
|
+
- net_imap_saslprep "user" # RFC example 2. user
|
39
|
+
- net_imap_saslprep "USER" # RFC example 3. user
|
40
|
+
- net_imap_saslprep "\u00aa" # RFC example 4. a
|
41
|
+
- net_imap_saslprep "\u2168" # RFC example 5. IX
|
42
|
+
- net_imap_saslprep "\u0007" # RFC example 6. Error - prohibited character
|
43
|
+
- net_imap_saslprep "\u0627\u0031" # RFC example 7. Error - bidirectional check
|
44
|
+
- net_imap_saslprep "I\u2000X" # map to space: I X
|
45
|
+
- net_imap_saslprep "a longer string, e.g. a password"
|
46
|
+
|
47
|
+
- libidn_saslprep "I\u00ADX" # RFC example 1. IX
|
48
|
+
- libidn_saslprep "user" # RFC example 2. user
|
49
|
+
- libidn_saslprep "USER" # RFC example 3. user
|
50
|
+
- libidn_saslprep "\u00aa" # RFC example 4. a
|
51
|
+
- libidn_saslprep "\u2168" # RFC example 5. IX
|
52
|
+
- libidn_saslprep "\u0007" # RFC example 6. Error - prohibited character
|
53
|
+
- libidn_saslprep "\u0627\u0031" # RFC example 7. Error - bidirectional check
|
54
|
+
- libidn_saslprep "I\u2000X" # map to space: I X
|
55
|
+
- libidn_saslprep "a longer string, e.g. a password"
|
56
|
+
|
57
|
+
- mongo_saslprep "I\u00ADX" # RFC example 1. IX
|
58
|
+
- mongo_saslprep "user" # RFC example 2. user
|
59
|
+
- mongo_saslprep "USER" # RFC example 3. user
|
60
|
+
- mongo_saslprep "\u00aa" # RFC example 4. a
|
61
|
+
- mongo_saslprep "\u2168" # RFC example 5. IX
|
62
|
+
- mongo_saslprep "\u0007" # RFC example 6. Error - prohibited character
|
63
|
+
- mongo_saslprep "\u0627\u0031" # RFC example 7. Error - bidirectional check
|
64
|
+
- mongo_saslprep "I\u2000X" # map to space: I X
|
65
|
+
- mongo_saslprep "a longer string, e.g. a password"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
prelude: |
|
2
|
+
require "json"
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
all_codepoints = (0..0x10ffff).map{_1.chr("UTF-8") rescue nil}.compact
|
6
|
+
|
7
|
+
rfc3454_tables = Dir["rfcs/rfc3454*.json"]
|
8
|
+
.first
|
9
|
+
.then{File.read _1}
|
10
|
+
.then{JSON.parse _1}
|
11
|
+
titles = rfc3454_tables.delete("titles")
|
12
|
+
|
13
|
+
sets = rfc3454_tables
|
14
|
+
.transform_values{|t|t.keys rescue t}
|
15
|
+
.transform_values{|table|
|
16
|
+
table
|
17
|
+
.map{_1.split(?-).map{|i|Integer i, 16}}
|
18
|
+
.flat_map{_2 ? (_1.._2).to_a : _1}
|
19
|
+
.to_set
|
20
|
+
}
|
21
|
+
|
22
|
+
TABLE_A1_SET = sets.fetch "A.1"
|
23
|
+
ASSIGNED_3_2 = /\p{AGE=3.2}/
|
24
|
+
UNASSIGNED_3_2 = /\P{AGE=3.2}/
|
25
|
+
TABLE_A1_REGEX = /(?-mix:[\u{0000}-\u{001f}\u{007f}-\u{00a0}\u{0340}-\u{0341}\u{06dd}\u{070f}\u{1680}\u{180e}\u{2000}-\u{200f}\u{2028}-\u{202f}\u{205f}-\u{2063}\u{206a}-\u{206f}\u{2ff0}-\u{2ffb}\u{3000}\u{e000}-\u{f8ff}\u{fdd0}-\u{fdef}\u{feff}\u{fff9}-\u{ffff}\u{1d173}-\u{1d17a}\u{1fffe}-\u{1ffff}\u{2fffe}-\u{2ffff}\u{3fffe}-\u{3ffff}\u{4fffe}-\u{4ffff}\u{5fffe}-\u{5ffff}\u{6fffe}-\u{6ffff}\u{7fffe}-\u{7ffff}\u{8fffe}-\u{8ffff}\u{9fffe}-\u{9ffff}\u{afffe}-\u{affff}\u{bfffe}-\u{bffff}\u{cfffe}-\u{cffff}\u{dfffe}-\u{dffff}\u{e0001}\u{e0020}-\u{e007f}\u{efffe}-\u{10ffff}])|(?-mix:\p{Cs})/.freeze
|
26
|
+
|
27
|
+
benchmark:
|
28
|
+
|
29
|
+
# matches A.1
|
30
|
+
- script: "all_codepoints.grep(TABLE_A1_SET)"
|
31
|
+
- script: "all_codepoints.grep(TABLE_A1_REGEX)"
|
32
|
+
- script: "all_codepoints.grep(UNASSIGNED_3_2)"
|
33
|
+
- script: "all_codepoints.grep_v(ASSIGNED_3_2)"
|
34
|
+
|
35
|
+
# doesn't match A.1
|
36
|
+
- script: "all_codepoints.grep_v(TABLE_A1_SET)"
|
37
|
+
- script: "all_codepoints.grep_v(TABLE_A1_REGEX)"
|
38
|
+
- script: "all_codepoints.grep_v(UNASSIGNED_3_2)"
|
39
|
+
- script: "all_codepoints.grep(ASSIGNED_3_2)"
|
data/docs/styles.css
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
/* this is a work in progress. :) */
|
2
|
+
|
3
|
+
main .method-header {
|
4
|
+
background: rgba(27,31,35,0.05);
|
5
|
+
border: 1px solid #6C8C22;
|
6
|
+
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; */
|
12
|
+
}
|
13
|
+
|
14
|
+
main .method-description, main .aliases {
|
15
|
+
padding-left: 1em;
|
16
|
+
}
|
17
|
+
|
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;
|
24
|
+
}
|
25
|
+
|
26
|
+
@media only screen and (min-width: 600px) {
|
27
|
+
nav {
|
28
|
+
height: 100%;
|
29
|
+
position: fixed;
|
30
|
+
overflow-y: scroll;
|
31
|
+
}
|
32
|
+
|
33
|
+
nav #class-metadata {
|
34
|
+
margin-bottom: 5em;
|
35
|
+
}
|
36
|
+
}
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "date"
|
4
|
+
|
3
5
|
require_relative "errors"
|
4
6
|
|
5
7
|
module Net
|
@@ -21,7 +23,7 @@ module Net
|
|
21
23
|
validate_data(i)
|
22
24
|
end
|
23
25
|
end
|
24
|
-
when Time
|
26
|
+
when Time, Date, DateTime
|
25
27
|
when Symbol
|
26
28
|
else
|
27
29
|
data.validate
|
@@ -38,7 +40,9 @@ module Net
|
|
38
40
|
send_number_data(data)
|
39
41
|
when Array
|
40
42
|
send_list_data(data, tag)
|
41
|
-
when
|
43
|
+
when Date
|
44
|
+
send_date_data(data)
|
45
|
+
when Time, DateTime
|
42
46
|
send_time_data(data)
|
43
47
|
when Symbol
|
44
48
|
send_symbol_data(data)
|
@@ -101,15 +105,8 @@ module Net
|
|
101
105
|
put_string(")")
|
102
106
|
end
|
103
107
|
|
104
|
-
|
105
|
-
|
106
|
-
def send_time_data(time)
|
107
|
-
t = time.dup.gmtime
|
108
|
-
s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"',
|
109
|
-
t.day, DATE_MONTH[t.month - 1], t.year,
|
110
|
-
t.hour, t.min, t.sec)
|
111
|
-
put_string(s)
|
112
|
-
end
|
108
|
+
def send_date_data(date) put_string Net::IMAP.encode_date(date) end
|
109
|
+
def send_time_data(time) put_string Net::IMAP.encode_time(time) end
|
113
110
|
|
114
111
|
def send_symbol_data(symbol)
|
115
112
|
put_string("\\" + symbol.to_s)
|
@@ -1,10 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "date"
|
4
|
+
|
3
5
|
require_relative "errors"
|
4
6
|
|
5
7
|
module Net
|
6
8
|
class IMAP < Protocol
|
7
9
|
|
10
|
+
# strftime/strptime format for an IMAP4 +date+, excluding optional dquotes.
|
11
|
+
# Use via the encode_date and decode_date methods.
|
12
|
+
#
|
13
|
+
# date = date-text / DQUOTE date-text DQUOTE
|
14
|
+
# date-text = date-day "-" date-month "-" date-year
|
15
|
+
#
|
16
|
+
# date-day = 1*2DIGIT
|
17
|
+
# ; Day of month
|
18
|
+
# date-month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
|
19
|
+
# "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
|
20
|
+
# date-year = 4DIGIT
|
21
|
+
STRFDATE = "%d-%b-%Y"
|
22
|
+
|
23
|
+
# strftime/strptime format for an IMAP4 +date-time+, including dquotes.
|
24
|
+
# See the encode_datetime and decode_datetime methods.
|
25
|
+
#
|
26
|
+
# date-time = DQUOTE date-day-fixed "-" date-month "-" date-year
|
27
|
+
# SP time SP zone DQUOTE
|
28
|
+
#
|
29
|
+
# date-day-fixed = (SP DIGIT) / 2DIGIT
|
30
|
+
# ; Fixed-format version of date-day
|
31
|
+
# date-month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
|
32
|
+
# "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
|
33
|
+
# date-year = 4DIGIT
|
34
|
+
# time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
|
35
|
+
# ; Hours minutes seconds
|
36
|
+
# zone = ("+" / "-") 4DIGIT
|
37
|
+
# ; Signed four-digit value of hhmm representing
|
38
|
+
# ; hours and minutes east of Greenwich (that is,
|
39
|
+
# ; the amount that the given time differs from
|
40
|
+
# ; Universal Time). Subtracting the timezone
|
41
|
+
# ; from the given time will give the UT form.
|
42
|
+
# ; The Universal Time zone is "+0000".
|
43
|
+
#
|
44
|
+
# Note that Time.strptime <tt>"%d"</tt> flexibly parses either space or zero
|
45
|
+
# padding. However, the DQUOTEs are *not* optional.
|
46
|
+
STRFTIME = '"%d-%b-%Y %H:%M:%S %z"'
|
47
|
+
|
8
48
|
# Decode a string from modified UTF-7 format to UTF-8.
|
9
49
|
#
|
10
50
|
# UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
|
@@ -35,14 +75,70 @@ module Net
|
|
35
75
|
}.force_encoding("ASCII-8BIT")
|
36
76
|
end
|
37
77
|
|
38
|
-
# Formats +time+ as an
|
39
|
-
def self.
|
40
|
-
|
78
|
+
# Formats +time+ as an IMAP4 date.
|
79
|
+
def self.encode_date(date)
|
80
|
+
date.to_date.strftime STRFDATE
|
81
|
+
end
|
82
|
+
|
83
|
+
# :call-seq: decode_date(string) -> Date
|
84
|
+
#
|
85
|
+
# Decodes +string+ as an IMAP formatted "date".
|
86
|
+
#
|
87
|
+
# Double quotes are optional. Day of month may be padded with zero or
|
88
|
+
# space. See STRFDATE.
|
89
|
+
def self.decode_date(string)
|
90
|
+
string = string.delete_prefix('"').delete_suffix('"')
|
91
|
+
Date.strptime(string, STRFDATE)
|
92
|
+
end
|
93
|
+
|
94
|
+
# :call-seq: encode_datetime(time) -> string
|
95
|
+
#
|
96
|
+
# Formats +time+ as an IMAP4 date-time.
|
97
|
+
def self.encode_datetime(time)
|
98
|
+
time.to_datetime.strftime STRFTIME
|
41
99
|
end
|
42
100
|
|
43
|
-
#
|
101
|
+
# :call-seq: decode_datetime(string) -> DateTime
|
102
|
+
#
|
103
|
+
# Decodes +string+ as an IMAP4 formatted "date-time".
|
104
|
+
#
|
105
|
+
# Note that double quotes are not optional. See STRFTIME.
|
106
|
+
def self.decode_datetime(string)
|
107
|
+
DateTime.strptime(string, STRFTIME)
|
108
|
+
end
|
109
|
+
|
110
|
+
# :call-seq: decode_time(string) -> Time
|
111
|
+
#
|
112
|
+
# Decodes +string+ as an IMAP4 formatted "date-time".
|
113
|
+
#
|
114
|
+
# Same as +decode_datetime+, but returning a Time instead.
|
115
|
+
def self.decode_time(string)
|
116
|
+
decode_datetime(string).to_time
|
117
|
+
end
|
118
|
+
|
119
|
+
class << self
|
120
|
+
alias encode_time encode_datetime
|
121
|
+
alias format_date encode_date
|
122
|
+
alias format_time encode_time
|
123
|
+
alias parse_date decode_date
|
124
|
+
alias parse_datetime decode_datetime
|
125
|
+
alias parse_time decode_time
|
126
|
+
|
127
|
+
# alias format_datetime encode_datetime # n.b. this is overridden below...
|
128
|
+
end
|
129
|
+
|
130
|
+
# DEPRECATED:: The original version returned incorrectly formatted strings.
|
131
|
+
# Strings returned by encode_datetime or format_time use the
|
132
|
+
# correct IMAP4rev1 syntax for "date-time".
|
133
|
+
#
|
134
|
+
# This invalid format has been temporarily retained for backward
|
135
|
+
# compatibility. A future release will change this method to return the
|
136
|
+
# correct format.
|
44
137
|
def self.format_datetime(time)
|
45
|
-
|
138
|
+
warn("#{self}.format_datetime incorrectly formats IMAP date-time. " \
|
139
|
+
"Convert to #{self}.encode_datetime or #{self}.format_time instead.",
|
140
|
+
uplevel: 1, category: :deprecated)
|
141
|
+
time.strftime("%d-%b-%Y %H:%M %z")
|
46
142
|
end
|
47
143
|
|
48
144
|
# Common validators of number and nz_number types
|
data/lib/net/imap/errors.rb
CHANGED