net-imap 0.6.6 → 0.6.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fc8fe0589b4b3cf22687ee9731136d4bb4fae32bceadffdad5cb4fdc225741b
4
- data.tar.gz: 6c72c3817f3bd9c1ba7b6edbf01c5808e7d9c785effcc09294435944ccbdd33b
3
+ metadata.gz: 5300636497a02d1c8bc10b528bf1d3fe8aad9898ba02ba3ab40f2329f9306baf
4
+ data.tar.gz: 449230117bc3289dc13cb1531d95a2ae530558a21a405d8f4effb958773da88d
5
5
  SHA512:
6
- metadata.gz: 2e24f88144b2329c18a03c9fcc5630ac6a6c50b88f14107c09abd29762eaa94467ab345fb356edf8d891b244b1c8d3dcc2a4033166ddc6bbde7131ffba3e0d59
7
- data.tar.gz: bc71d03a81f9d268f0c3c359939150fd78c8fbe06dc5dba7e5de77d2f10d21f1155acafd8f670afae6aafea77e0fbd4f150f8cdcbf4e1301ba7a9695a1bba9d9
6
+ metadata.gz: b034f4ab8fc449890b3eb29c2049c4af1ed5aac42e8943f0a5eb20d4f9eae910740e0c8d590590f3379121c30ac347ce49f5387dcd4985e2dd551292545bd2de
7
+ data.tar.gz: 996ab44d2c1f4bf6d5d2e3d141b1890d25a38cddf1e0032726257c63346fc7175bcdd05f3ed97cce3a3ce63d8939154c4d9c4b9e931b035a46d9c4fa1a518450
data/.simplecov CHANGED
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  SimpleCov.configure do
4
- formatter SimpleCov::Formatter::HTMLFormatter
5
-
6
4
  enable_coverage :branch
7
5
  enable_coverage :method
8
6
  enable_coverage :eval
9
7
 
8
+ # eval branch coverage varies too much between runtime environments
9
+ ignore_branches :eval_generated
10
+
10
11
  skip "/test/"
11
12
  skip "/rakelib/"
12
13
  cover "lib/**/*.rb"
data/Gemfile CHANGED
@@ -13,10 +13,10 @@ gem "irb"
13
13
  gem "rake"
14
14
  gem "rdoc", ">= 7.2.0"
15
15
  gem "test-unit"
16
- gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
16
+ gem "test-unit-ruby-core"
17
17
 
18
18
  gem "benchmark", require: false
19
- gem "benchmark-driver", require: false
19
+ gem "benchmark_driver", require: false
20
20
  gem "vernier", require: false, platform: :mri
21
21
 
22
22
  group :test do
data/README.md CHANGED
@@ -26,41 +26,61 @@ Or install it yourself as:
26
26
  ### Connect with TLS to port 993
27
27
 
28
28
  ```ruby
29
- imap = Net::IMAP.new('mail.example.com', ssl: true)
30
- imap.port => 993
31
- imap.tls_verified? => true
32
- case imap.greeting.name
33
- in /OK/i
34
- # The client is connected in the "Not Authenticated" state.
35
- imap.authenticate("PLAIN", "joe_user", "joes_password")
36
- in /PREAUTH/i
37
- # The client is connected in the "Authenticated" state.
29
+ hostname = "mail.example.com"
30
+ username = "user@example.com"
31
+ password = "correct-horse-battery-staple"
32
+
33
+ imap = Net::IMAP.new(hostname, ssl: true)
34
+ imap.authenticate(:plain, username, password)
35
+ ```
36
+
37
+ To authenticate with an OAuth2 access token:
38
+ ```ruby
39
+ if imap.auth_capable?(:OAUTHBEARER)
40
+ imap.authenticate(:OAUTHBEARER, username, oauth2_token)
41
+ elsif imap.auth_capable?(:XOAUTH2)
42
+ imap.authenticate(:XOAUTH2, username, oauth2_token)
43
+ else
44
+ raise "OAuth2 not supported?"
38
45
  end
39
46
  ```
40
47
 
41
- ### List sender and subject of all recent messages in the default mailbox
48
+ ### List sender and subject of recent messages
42
49
 
43
50
  ```ruby
44
51
  imap.examine('INBOX')
45
- imap.search(["RECENT"]).each do |message_id|
46
- envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
47
- puts "#{envelope.from[0].name}: \t#{envelope.subject}"
52
+ search_result = imap.uid_search(["SINCE", Date.today - 7])
53
+ imap.uid_fetch(search_result, "ENVELOPE").each do |fetch_data|
54
+ envelope = fetch_data.envelope
55
+ puts "#{envelope.from.first.name}: \t#{envelope.subject}"
48
56
  end
49
57
  ```
50
58
 
51
- ### Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
59
+ ### Move messages between two dates to another mailbox
52
60
 
53
61
  ```ruby
54
- imap.select('Mail/sent-mail')
55
- if imap.list('Mail/', 'sent-apr03').empty?
56
- imap.create('Mail/sent-apr03')
62
+ source = "Mail/sent-mail"
63
+ destination = "Mail/sent-apr03"
64
+
65
+ # The "BEFORE" and "AFTER" search criteria are not inclusive.
66
+ since = Date.parse("2003-04-01").prev_day
67
+ before = Date.parse("2003-05-01")
68
+
69
+ if imap.list("", destination).empty?
70
+ imap.create(destination)
57
71
  end
58
- imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
59
- if imap.capable?(:move) || imap.capable?(:IMAP4rev2)
60
- imap.move(message_id, "Mail/sent-apr03")
72
+ imap.select(source)
73
+ search_result = imap.uid_search(["SINCE", since, "BEFORE", before])
74
+ if imap.capable?(:MOVE) || imap.capable?(:IMAP4rev2)
75
+ imap.uid_move(search_result, destination)
76
+ else
77
+ # Atomic MOVE is not supported. Copy, delete, and expunge.
78
+ imap.uid_copy(search_result, destination)
79
+ imap.uid_store(search_result, "+FLAGS", [:Deleted])
80
+ if imap.capable?(:UIDPLUS) || imap.capable?(:IMAP4rev2)
81
+ imap.uid_expunge(search_result)
61
82
  else
62
- imap.copy(message_id, "Mail/sent-apr03")
63
- imap.store(message_id, "+FLAGS", [:Deleted])
83
+ # NOTE: This may expunge _other_ deleted messages, too.
64
84
  imap.expunge
65
85
  end
66
86
  end
@@ -68,9 +88,15 @@ end
68
88
 
69
89
  ## Development
70
90
 
71
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
91
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
92
+ `bin/test` to run the tests. You can also run `bin/console` for an interactive
93
+ prompt that will allow you to experiment.
72
94
 
73
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
95
+ To install this gem onto your local machine, run `bundle exec rake install`. To
96
+ release a new version, update the version number in `version.rb`, and then run
97
+ `bundle exec rake release`, which will create a git tag for the version, push
98
+ git commits and tags, and push the `.gem` file to
99
+ [rubygems.org](https://rubygems.org).
74
100
 
75
101
  ## Contributing
76
102
 
data/Rakefile CHANGED
@@ -12,13 +12,60 @@ end
12
12
 
13
13
  task :default => :test
14
14
 
15
- desc "Output coverage data report, and error when threshholds aren't met"
16
- task "coverage:report" do
15
+ desc "Output HTML coverage data report, and error when threshholds aren't met"
16
+ task "test:coverage:report" do
17
17
  require "simplecov"
18
+
18
19
  SimpleCov.collate "coverage/.resultset.json" do
20
+ formatter SimpleCov::Formatter::HTMLFormatter
21
+
19
22
  coverage(:line) do
20
- minimum 90
21
- minimum_per_file 40
23
+ minimum 95
24
+
25
+ minimum_per_group 98, only: "Config"
26
+ minimum_per_group 97, only: "StringPrep"
27
+ minimum_per_group 97, only: "SASL"
28
+ minimum_per_group 95, only: "Data Types"
29
+ minimum_per_group 94, only: "Parser"
30
+ minimum_per_group 92, only: "Client"
31
+
32
+ minimum_per_file 80
33
+ minimum_per_file 55, only: "lib/net/imap/search_result.rb"
34
+ end
35
+
36
+ # NOTE: branch coverage varies more widely between ruby versions
37
+ coverage(:branch) do
38
+ minimum 80
39
+
40
+ minimum_per_group 90, only: "Data Types"
41
+ minimum_per_group 85, only: "Config"
42
+ minimum_per_group 80, only: "Client"
43
+ minimum_per_group 80, only: "Parser"
44
+ minimum_per_group 70, only: "SASL"
45
+ minimum_per_group 70, only: "StringPrep"
46
+
47
+ minimum_per_file 60
48
+ minimum_per_file 50, only: "lib/net/imap/sasl/authenticators.rb"
49
+ minimum_per_file 50, only: "lib/net/imap/config/attr_accessors.rb"
50
+ end
51
+
52
+ coverage(:method) do
53
+ minimum 88
54
+
55
+ minimum_per_group 100, only: "Config"
56
+ minimum_per_group 90, only: "Data Types"
57
+ minimum_per_group 90, only: "StringPrep"
58
+ minimum_per_group 85, only: "Client"
59
+ minimum_per_group 80, only: "Parser"
60
+ minimum_per_group 80, only: "SASL"
61
+
62
+ minimum_per_file 65
63
+ minimum_per_file 60, only: "lib/net/imap/response_parser/parser_utils.rb"
64
+ minimum_per_file 55, only: "lib/net/imap/sasl/authenticators.rb"
65
+ minimum_per_file 50, only: "lib/net/imap/authenticators.rb"
66
+ minimum_per_file 50, only: "lib/net/imap/sasl/anonymous_authenticator.rb"
67
+ minimum_per_file 35, only: "lib/net/imap/sasl/protocol_adapters.rb"
68
+ minimum_per_file 20, only: "lib/net/imap/response_data.rb"
22
69
  end
23
70
  end
24
71
  end
@@ -265,23 +265,26 @@ module Net
265
265
  def self.split(data)
266
266
  data = data.b # dups and ensures BINARY encoding
267
267
  parts = []
268
- while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
269
- text, binary, bytesize, non_sync, data = $`, !!$1, $2, !!$3, $'
268
+ text_start = 0
269
+ while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n, text_start)
270
+ text, binary, bytesize, non_sync, literal_start =
271
+ data.byteslice(text_start...$~.begin(0)), !!$1, $2, !!$3, $~.end(0)
270
272
  bytesize = NumValidator.coerce_number64 bytesize
273
+ text_start = literal_start + bytesize
271
274
  parts << RawText[text] unless text.empty?
272
- parts << extract_literal(data, binary:, bytesize:, non_sync:)
273
- data.bytesplice(0, bytesize, "")
275
+ parts << extract_literal(data, literal_start, bytesize, binary:, non_sync:)
274
276
  end
275
- parts << RawText[data] unless data.empty?
277
+ parts << RawText[data.byteslice(text_start..)] if text_start < data.bytesize
276
278
  parts
277
279
  end
278
280
 
279
- def self.extract_literal(data, binary:, bytesize:, non_sync:)
280
- if data.bytesize < bytesize
281
+ def self.extract_literal(data, offset, bytesize, binary:, non_sync:)
282
+ remaining = data.bytesize - offset
283
+ if remaining < bytesize
281
284
  raise DataFormatError, "Too few bytes in string for literal, " \
282
- "expected: %s, remaining: %s" % [bytesize, data.bytesize]
285
+ "expected: %s, remaining: %s" % [bytesize, remaining]
283
286
  end
284
- literal = data.byteslice(0, bytesize)
287
+ literal = data.byteslice(offset, bytesize)
285
288
  (binary ? Literal8 : Literal).new(data: literal, non_sync:)
286
289
  end
287
290
  private_class_method :extract_literal
@@ -459,7 +462,7 @@ module Net
459
462
  # coerces using +to_s+
460
463
  def string(str)
461
464
  str = str.to_s
462
- if str =~ LITERAL_REGEX
465
+ if str.b.match?(LITERAL_REGEX)
463
466
  Literal.new(str)
464
467
  else
465
468
  QuotedString.new(str)
@@ -58,9 +58,9 @@ module Net
58
58
 
59
59
  private
60
60
 
61
- def initialize_clone(other)
61
+ def initialize_clone(other, **kwargs)
62
62
  super
63
- @data = other.data.clone
63
+ @data = other.data.clone(**kwargs)
64
64
  end
65
65
 
66
66
  def initialize_dup(other)
@@ -120,6 +120,66 @@ module Net
120
120
  #
121
121
  # *NOTE:* Updates to config objects are not synchronized for thread-safety.
122
122
  #
123
+ # == What's here?
124
+ #
125
+ # === \Config attributes
126
+ #
127
+ # ==== Timeouts and other limits
128
+ #
129
+ # * #open_timeout: seconds to wait for connection to open or start TLS
130
+ # * #idle_response_timeout: seconds to wait for +IDLE+ command to complete
131
+ # * max_response_size: Maximum allowed server response size.
132
+ #
133
+ # ==== Server capabilities
134
+ #
135
+ # * #sasl_ir: Controls +SASL-IR+ behavior for Net::IMAP#authenticate.
136
+ # * #enforce_logindisabled: Controls +LOGINDISABLED+ behavior in
137
+ # Net::IMAP#login.
138
+ # * max_non_synchronizing_literal: maximum bytesize for <tt>LITERAL+</tt> /
139
+ # <tt>LITERAL-</tt> non-synchronizing literals.
140
+ #
141
+ # ==== Inherited defaults
142
+ # {Versioned defaults}[rdoc-ref:Net::IMAP@Versioned+defaults] inherit these
143
+ # from ::global and #load_defaults doesn't update them.
144
+ #
145
+ # * #debug (aliased as #debug?): whether debug mode is enabled
146
+ #
147
+ # ==== Backward compatibility
148
+ # These attributes will be removed by some future release.
149
+ #
150
+ # * #responses_without_block: Controls the behavior of Net::IMAP#responses
151
+ # when called without any arguments (+type+ or +block+).
152
+ # * #parser_use_deprecated_uidplus_data: <em>Ignored since +v0.6.0+.</em>
153
+ # * #parser_max_deprecated_uidplus_data_size: <em>Ignored since +v0.6.0+.</em>
154
+ #
155
+ # === Getting a new or existing config
156
+ # * ::global: The global config, used as the default #parent.
157
+ # * ::default: The hardcoded frozen default config, and parent of ::global.
158
+ # * ::version_defaults: Hard-coded frozen default configurations, indexed
159
+ # by version.
160
+ # * ::[]: Returns a config from ::version_defaults or created by ::new.
161
+ # * ::new: Return a new Config which inherits from a given +parent+.
162
+ # * #new: Return a new Config which inherits from +self+.
163
+ #
164
+ # === Updating multiple attributes
165
+ # * #load_defaults: Sets attributes to a given +version+'s default values.
166
+ # * #update: Assigns multiple attribute values to +self+.
167
+ # * #reset: Resets attributes to inherit from #parent.
168
+ #
169
+ # === Exporting multiple attributes
170
+ # * #to_h: Return a hash with all attributes.
171
+ # * #inspect (aliased as #to_s): Returns a string representation of
172
+ # overriden config attributes and the config inheritance chain.
173
+ # * #pretty_print: Used by PP[https://docs.ruby-lang.org/en/master/PP.html]
174
+ # to create a string representation of all config attributes and the
175
+ # inheritance chain.
176
+ #
177
+ # === Inheritance inspection
178
+ # * #parent: Returns the parent config object.
179
+ # * #inherited?: Returns whether all attributes inherit from #parent.
180
+ # * #inherits_defaults?: Returns whether all attributes inherit from a default config.
181
+ # * #overrides?: Returns whether any attributes override the #parent value.
182
+ #
123
183
  class Config
124
184
  # Array of attribute names that are _not_ loaded by #load_defaults.
125
185
  DEFAULT_TO_INHERIT = %i[debug].freeze
@@ -196,7 +256,7 @@ module Net
196
256
  # #load_defaults will not override #debug.
197
257
  attr_accessor :debug, type: :boolean, default: false
198
258
 
199
- # method: debug?
259
+ # :method: debug?
200
260
  # :call-seq: debug? -> boolean
201
261
  #
202
262
  # Alias for #debug
@@ -312,7 +372,7 @@ module Net
312
372
  # * +0.6+: 16 KiB
313
373
  attr_accessor :max_non_synchronizing_literal, type: Integer, defaults: {
314
374
  0.0r => -1,
315
- 0.6r => 16 << 16, # 16 KiB
375
+ 0.6r => 16 << 10, # 16 KiB
316
376
  }
317
377
 
318
378
  # The maximum allowed server response size. When +nil+, there is no limit
@@ -365,7 +425,7 @@ module Net
365
425
  # Prints a warning and returns the mutable responses hash.
366
426
  # <em>This is not thread-safe.</em>
367
427
  #
368
- # [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
428
+ # [+:frozen_dup+ <em>(default since +v0.6+)</em>]
369
429
  # Returns a frozen copy of the unhandled responses hash, with frozen
370
430
  # array values.
371
431
  #
@@ -193,11 +193,11 @@ module Net
193
193
  end
194
194
 
195
195
  ##
196
- # method: range
196
+ # :method: range
197
197
  # :call-seq: range -> range
198
198
 
199
199
  ##
200
- # method: results
200
+ # :method: results
201
201
  # :call-seq: results -> sequence set or nil
202
202
 
203
203
  # Converts #results to an array of integers.
@@ -104,7 +104,7 @@ module Net
104
104
  #
105
105
  class FetchStruct < Struct
106
106
  ##
107
- # method: attr
107
+ # :method: attr
108
108
  # :call-seq: attr -> hash
109
109
  #
110
110
  # Each key specifies a message attribute, and the value is the
@@ -518,7 +518,7 @@ module Net
518
518
  # See FetchStruct documentation for a list of standard message attributes.
519
519
  class FetchData < FetchStruct.new(:seqno, :attr)
520
520
  ##
521
- # method: seqno
521
+ # :method: seqno
522
522
  # :call-seq: seqno -> Integer
523
523
  #
524
524
  # The message sequence number.
@@ -532,7 +532,7 @@ module Net
532
532
  # UIDFetchData will raise a NoMethodError.
533
533
 
534
534
  ##
535
- # method: attr
535
+ # :method: attr
536
536
  # :call-seq: attr -> hash
537
537
  #
538
538
  # Each key specifies a message attribute, and the value is the
@@ -558,7 +558,7 @@ module Net
558
558
  # See FetchStruct documentation for a list of standard message attributes.
559
559
  class UIDFetchData < FetchStruct.new(:uid, :attr)
560
560
  ##
561
- # method: uid
561
+ # :method: uid
562
562
  # call-seq: uid -> Integer
563
563
  #
564
564
  # A number expressing the unique identifier of the message.
@@ -568,7 +568,7 @@ module Net
568
568
  # returns the uniqueid at the beginning of the +UIDFETCH+ response.
569
569
 
570
570
  ##
571
- # method: attr
571
+ # :method: attr
572
572
  # call-seq: attr -> hash
573
573
  #
574
574
  # Each key specifies a message attribute, and the value is the