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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a032f39f4325733e1c68595d730ae6eafe48702fd416480cfe37a0082f3ec340
4
- data.tar.gz: e7f0d60f53ed135dea9f1262814902735c537adc97198c1284f32cf4e599da8b
3
+ metadata.gz: 85c732bea9deedc8787a80daee38991be448a03e593f4653d91067f16d2ed699
4
+ data.tar.gz: 6a1880f7da062779af60bf190da72bc415b0f11ee5f04296a08d97bbf82233ba
5
5
  SHA512:
6
- metadata.gz: 65d363c721fec3061313cdbe6a4746612699cf296a6434853b14c66d850cbd843389ccf8f88b77e658c966be1d9692324b03e0c4b0074acac8595795bda456ce
7
- data.tar.gz: 03c8152b9fbd734ca4d5c1e6301a7d2c09bd3da1b5b5e937ce0b7c141ba7bce9f6ab3d4f291b6a4467dc88fdb02b1996579f9e404f3cd9f9d81b7eb69597609e
6
+ metadata.gz: '073296843ad22467cf1ab9e31adc6696f35a4fe8a81a3003e6ea0292a4fdb643f772355970d62531dae1bf53592ea1a99ae56e7ea0b4daf9c82719e6fefbf959'
7
+ data.tar.gz: c1aa2ed380ccf9adedca1bce427bc92629d2737f8439bccf697130460ef2b76e31ebf261563e505d0e206d075e6da52b278c772d3b768f12a14794b948a90667
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  /coverage/
5
5
  /doc/
6
6
  /pkg/
7
+ /rfcs
7
8
  /spec/reports/
8
9
  /tmp/
9
10
  /Gemfile.lock
data/Gemfile CHANGED
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gemspec
4
6
 
5
7
  gem "rake"
8
+ gem "rdoc"
6
9
  gem "test-unit"
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 Time
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
- DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
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 IMAP-style date.
39
- def self.format_date(time)
40
- return time.strftime('%d-%b-%Y')
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
- # Formats +time+ as an IMAP-style date-time.
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
- return time.strftime('%d-%b-%Y %H:%M %z')
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
@@ -51,7 +51,7 @@ module Net
51
51
  class UnknownResponseError < ResponseError
52
52
  end
53
53
 
54
- RESPONSE_ERRORS = Hash.new(ResponseError)
54
+ RESPONSE_ERRORS = Hash.new(ResponseError) # :nodoc:
55
55
  RESPONSE_ERRORS["NO"] = NoResponseError
56
56
  RESPONSE_ERRORS["BAD"] = BadResponseError
57
57