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 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