net-imap 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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