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.
data/lib/net/imap.rb CHANGED
@@ -85,29 +85,100 @@ module Net
85
85
  #
86
86
  # === Examples of Usage
87
87
  #
88
- # ==== List sender and subject of all recent messages in the default mailbox
88
+ # ==== Connect with TLS to port 993
89
+ #
90
+ # Use Net::IMAP.new to open a new connection, with <tt>ssl: true</tt> for TLS.
91
+ # <br>
92
+ # Use #authenticate to log in.
93
+ #
94
+ # hostname = "mail.example.com"
95
+ # username = "user@example.com"
96
+ # password = "correct-horse-battery-staple"
97
+ #
98
+ # imap = Net::IMAP.new(hostname, ssl: true)
99
+ # imap.authenticate(:plain, username, password)
100
+ #
101
+ # To authenticate with an OAuth2 access token:
102
+ # if imap.auth_capable?(:OAUTHBEARER)
103
+ # imap.authenticate(:OAUTHBEARER, oauth2_token:)
104
+ # elsif imap.auth_capable?(:XOAUTH2)
105
+ # imap.authenticate(:XOAUTH2, oauth2_token:)
106
+ # else
107
+ # raise "OAuth2 not supported?"
108
+ # end
109
+ #
110
+ # See #authenticate for other supported authentication mechanisms.
111
+ #
112
+ # ==== List sender and subject of recent messages
113
+ #
114
+ # Use #examine to open a mailbox with read-only access.<br>
115
+ # Use #uid_search for a list of UIDs (or #search for sequence numbers).<br>
116
+ # Use #uid_fetch (or #fetch) to read message attributes, such as "envelope".
117
+ #
118
+ # Search returns a SearchResult or ESearchResult, which is coercible to
119
+ # SequenceSet so it can be used directly as a message set argument for other
120
+ # commands. The first #uid_fetch argument is the set of message UIDs
121
+ # (sequence numbers for #fetch). Fetch returns an array of FetchData (or
122
+ # UIDFetchData when +UIDONLY+ is enabled).
89
123
  #
90
- # imap = Net::IMAP.new('mail.example.com')
91
- # imap.authenticate('PLAIN', 'joe_user', 'joes_password')
92
124
  # imap.examine('INBOX')
93
- # imap.search(["RECENT"]).each do |message_id|
94
- # envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
95
- # puts "#{envelope.from[0].name}: \t#{envelope.subject}"
125
+ # search_result = imap.uid_search(["SINCE", Date.today - 7])
126
+ # imap.uid_fetch(search_result, "ENVELOPE").each do |fetch_data|
127
+ # envelope = fetch_data.envelope
128
+ # puts "#{envelope.from.first.name}: \t#{envelope.subject}"
96
129
  # end
97
130
  #
98
- # ==== Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
131
+ # ==== Move messages between two dates to another mailbox
132
+ #
133
+ # Use #list to check if the destination mailbox exists.<br>
134
+ # Use #create to create a missing destination mailbox.<br>
135
+ # Use #select to open the source mailbox with read-write access.<br>
136
+ # Use #uid_search (or #search) to search for messages within a date range.<br>
137
+ # Use #uid_move (or #move) to atomically move messages to another mailbox.
138
+ #
139
+ # *NOTE:* Most servers support atomic +MOVE+, but not all do.
140
+ # source = "Mail/sent-mail"
141
+ # destination = "Mail/sent-apr03"
142
+ #
143
+ # # The "BEFORE" and "AFTER" search criteria are not inclusive.
144
+ # since = Date.parse("2003-04-01").prev_day
145
+ # before = Date.parse("2003-05-01")
99
146
  #
100
- # imap = Net::IMAP.new('mail.example.com')
101
- # imap.authenticate('PLAIN', 'joe_user', 'joes_password')
102
- # imap.select('Mail/sent-mail')
103
- # if not imap.list('Mail/', 'sent-apr03')
104
- # imap.create('Mail/sent-apr03')
147
+ # if imap.list("", destination).empty?
148
+ # imap.create(destination)
105
149
  # end
106
- # imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
107
- # imap.copy(message_id, "Mail/sent-apr03")
108
- # imap.store(message_id, "+FLAGS", [:Deleted])
150
+ # imap.select(source)
151
+ # search_result = imap.uid_search(["SINCE", since, "BEFORE", before])
152
+ # imap.uid_move(search_result, destination)
153
+ #
154
+ # When atomic +MOVE+ is not supported, the messages can be copied and deleted.
155
+ # \IMAP message deletion requires two steps: set <tt>\Deleted</tt> flag to
156
+ # mark a message for deletion, then expunge the <tt>\Deleted</tt> messages.
157
+ #
158
+ # Use #uid_copy (or #copy) to copy messages to another mailbox.<br>
159
+ # Use #uid_store (or #store) to mark messages for deletion.<br>
160
+ # Use #uid_expunge (or #expunge) to remove deleted messages.
161
+ #
162
+ # *NOTE:* #uid_expunge is not supported by every server, and #expunge removes
163
+ # _all_ <tt>\Deleted</tt> messages in the mailbox, even if the
164
+ # <tt>\Deleted</tt> flag was added by another session.
165
+ #
166
+ # if imap.capable?(:MOVE) || imap.capable?(:IMAP4rev2)
167
+ # imap.uid_move(search_result, destination)
168
+ # else
169
+ # # Atomic MOVE is not supported. Copy, delete, and expunge.
170
+ # imap.uid_copy(search_result, destination)
171
+ # imap.uid_store(search_result, "+FLAGS", [:Deleted])
172
+ # if imap.capable?(:UIDPLUS) || imap.capable?(:IMAP4rev2)
173
+ # imap.uid_expunge(search_result)
174
+ # else
175
+ # # NOTE: This may expunge _other_ deleted messages, too.
176
+ # imap.expunge
177
+ # end
109
178
  # end
110
- # imap.expunge
179
+ #
180
+ # Additional error handling may be required for non-atomic moves. Smaller
181
+ # batch sizes are recommended.
111
182
  #
112
183
  # == Capabilities
113
184
  #
@@ -289,7 +360,9 @@ module Net
289
360
  #
290
361
  # == What's here?
291
362
  #
292
- # * {Connection control}[rdoc-ref:Net::IMAP@Connection+control+methods]
363
+ # * {Client configuration}[rdoc-ref:Net::IMAP@Client+configuration]
364
+ # * {Connection control}[rdoc-ref:Net::IMAP@Connection+control]
365
+ # * {Connection attributes}[rdoc-ref:Net::IMAP@Connection+attributes]
293
366
  # * {Server capabilities}[rdoc-ref:Net::IMAP@Server+capabilities]
294
367
  # * {Handling server responses}[rdoc-ref:Net::IMAP@Handling+server+responses]
295
368
  # * {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands]
@@ -300,40 +373,76 @@ module Net
300
373
  # * {for the "logout" state}[rdoc-ref:Net::IMAP@Logout+state]
301
374
  # * {IMAP extension support}[rdoc-ref:Net::IMAP@IMAP+extension+support]
302
375
  #
303
- # === Connection control methods
376
+ # === Client configuration
377
+ # - #host: The hostname this client connected to.
378
+ # - #port: The port this client connected to.
379
+ # - #config: The client configuration. See Net::IMAP::Config.
380
+ # - #open_timeout: Delegates to {config.open_timeout}[rdoc-ref:Config#open_timeout].
381
+ # - #idle_response_timeout: Delegates to {config.idle_response_timeout}[rdoc-ref:Config#idle_response_timeout].
382
+ # - #max_response_size: Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size].
383
+ # - #ssl_ctx_params: Returns the params that were sent to {`ssl_ctx.set_params`}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
384
+ #
385
+ # <em>*NOTE:* Presence does _NOT_ indicate a secure TLS connection.</em>
386
+ #
387
+ # === Connection control
304
388
  #
305
389
  # - Net::IMAP.new: Creates a new \IMAP client which connects immediately and
306
390
  # waits for a successful server greeting before the method returns.
307
- # - #connection_state: Returns the connection state.
308
391
  # - #starttls: Asks the server to upgrade a clear-text connection to use TLS.
392
+ #
393
+ # <em>Requires the +STARTTLS+ capability.</em>
394
+ #
395
+ # <em>*NOTE:* Connecting to the implicit TLS port should be preferred.</em>
309
396
  # - #logout: Tells the server to end the session. Enters the +logout+ state.
397
+ # - #logout!: Calls #logout then #disconnect, converting most errors into
398
+ # warnings.
310
399
  # - #disconnect: Disconnects the connection (without sending #logout first).
400
+ #
401
+ # === Connection attributes
402
+ #
403
+ # - #greeting: The server's initial untagged response.
404
+ # - #connection_state: Returns the connection state.
311
405
  # - #disconnected?: True if the connection has been closed.
406
+ # - #tls_verified?: Returns whether TLS is used and #host has been verified.
407
+ # - #tls_connected?: Returns +true+ after TLS negotiation has completed.
408
+ #
409
+ # <em>*NOTE:* This does _NOT_ indicate a secure TLS connection.</em>
410
+ # - #tls_socket?: Returns +true+ after TLS negotiation has started.
411
+ #
412
+ # <em>*NOTE:* This does _NOT_ indicate a secure TLS connection.</em>
413
+ # - #ssl_ctx: Returns the {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
414
+ # after attempting to start TLS.
415
+ #
416
+ # <em>*NOTE:* Presence does _NOT_ indicate a secure TLS connection.</em>
312
417
  #
313
418
  # === Server capabilities
314
419
  #
420
+ # ==== Cached capabilities
315
421
  # - #capable?: Returns whether the server supports a given capability.
316
422
  # - #capabilities: Returns the server's capabilities as an array of strings.
423
+ # - #capabilities_cached?: Returns whether capabilities are cached.
424
+ # - #clear_cached_capabilities: Clears cached capabilities.
425
+ #
426
+ # *NOTE:* The cache is automatically cleared when capabilities can change.
427
+ #
428
+ # ==== \SASL Auth mechanisms
429
+ #
317
430
  # - #auth_capable?: Returns whether the server advertises support for a given
318
431
  # SASL mechanism, for use with #authenticate.
319
432
  # - #auth_mechanisms: Returns the #authenticate SASL mechanisms which
320
433
  # the server claims to support as an array of strings.
321
- # - #clear_cached_capabilities: Clears cached capabilities.
322
434
  #
323
- # <em>The capabilities cache is automatically cleared after completing
324
- # #starttls, #login, or #authenticate.</em>
325
- # - #capability: Sends the +CAPABILITY+ command and returns the #capabilities.
435
+ # ==== Enabled capabilities
326
436
  #
327
- # <em>In general, #capable? should be used rather than explicitly sending a
328
- # +CAPABILITY+ command to the server.</em>
437
+ # *NOTE:* The following require the +ENABLE+ or +IMAP4rev2+ server capability.
329
438
  # - #enable: Enables backwards incompatible server extensions.
330
- # <em>Requires the +ENABLE+ or +IMAP4rev2+ capability.</em>
331
439
  # - #enabled: Returns a set of enabled server extensions.
332
440
  # - #enabled?: Returns whether a server extension has been enabled.
333
441
  # - #utf8_enabled?: Returns whether UTF-8 string encoding has been enabled.
334
442
  #
335
443
  # === Handling server responses
336
444
  #
445
+ # ==== Stored responses methods
337
446
  # - #greeting: The server's initial untagged response, which can indicate a
338
447
  # pre-authenticated connection.
339
448
  # - #responses: Yields unhandled UntaggedResponse#data and <em>non-+nil+</em>
@@ -341,6 +450,8 @@ module Net
341
450
  # - #extract_responses: Removes and returns the responses for which the block
342
451
  # returns a true value.
343
452
  # - #clear_responses: Deletes unhandled data from #responses and returns it.
453
+ #
454
+ # ==== Response handler methods
344
455
  # - #add_response_handler: Add a block to be called inside the receiver thread
345
456
  # with every server response.
346
457
  # - #response_handlers: Returns the list of response handlers.
@@ -364,8 +475,9 @@ module Net
364
475
  #
365
476
  # - #capability: Returns the server's capabilities as an array of strings.
366
477
  #
367
- # <em>In general,</em> #capable? <em>should be used rather than explicitly
368
- # sending a +CAPABILITY+ command to the server.</em>
478
+ # <em>*NOTE:* Use {cached capabilities
479
+ # methods}[rdoc-ref:Net::IMAP@Server+Capabilities] instead, to avoid sending
480
+ # unnecessary commands to the server.</em>
369
481
  # - #noop: Allows the server to send unsolicited untagged #responses.
370
482
  # - #logout: Tells the server to end the session. Enters the +logout+ state.
371
483
  #
@@ -377,6 +489,8 @@ module Net
377
489
  # - #starttls: Upgrades a clear-text connection to use TLS.
378
490
  #
379
491
  # <em>Requires the +STARTTLS+ capability.</em>
492
+ #
493
+ # <em>*NOTE:* Connecting to the implicit TLS port should be preferred.</em>
380
494
  # - #authenticate: Identifies the client to the server using the given
381
495
  # {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
382
496
  # and credentials. Enters the +authenticated+ state.
@@ -453,10 +567,10 @@ module Net
453
567
  #
454
568
  # ==== RFC9051: +IMAP4rev2+
455
569
  #
456
- # Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
457
- # yet, Net::IMAP supports several extensions that have been folded into it:
458
- # +ENABLE+, +IDLE+, +LITERAL-+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+,
459
- # +UNSELECT+, <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
570
+ # Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not fully
571
+ # supported yet, Net::IMAP supports several extensions that have been folded
572
+ # into it: +ENABLE+, +IDLE+, +LITERAL-+, +MOVE+, +NAMESPACE+, +SASL-IR+,
573
+ # +UIDPLUS+, +UNSELECT+, <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
460
574
  # Commands for these extensions are listed with the {Core IMAP
461
575
  # commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
462
576
  #
@@ -819,7 +933,7 @@ module Net
819
933
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
820
934
  #
821
935
  class IMAP < Protocol
822
- VERSION = "0.6.6"
936
+ VERSION = "0.6.7"
823
937
 
824
938
  # Aliases for supported capabilities, to be used with the #enable command.
825
939
  ENABLE_ALIASES = {
@@ -927,10 +1041,15 @@ module Net
927
1041
 
928
1042
  # Returns the
929
1043
  # {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
930
- # used by the SSLSocket when TLS is attempted, even when the TLS handshake
931
- # is unsuccessful. The context object will be frozen.
1044
+ # used by the
1045
+ # {OpenSSL::SSL::SSLSocket}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLSocket.html].
1046
+ # when TLS is attempted, even when the TLS handshake is unsuccessful. The
1047
+ # context object will be frozen.
932
1048
  #
933
1049
  # Returns +nil+ for a plaintext connection.
1050
+ #
1051
+ # *NOTE:* The presence of this attribute does _NOT_ indicate that the
1052
+ # connection is using TLS.
934
1053
  attr_reader :ssl_ctx
935
1054
 
936
1055
  # Returns the parameters that were sent to #ssl_ctx
@@ -938,6 +1057,9 @@ module Net
938
1057
  # when the connection tries to use TLS (even when unsuccessful).
939
1058
  #
940
1059
  # Returns +false+ for a plaintext connection.
1060
+ #
1061
+ # *NOTE:* The presence of this attribute does _NOT_ indicate that the
1062
+ # connection is using TLS.
941
1063
  attr_reader :ssl_ctx_params
942
1064
 
943
1065
  # Returns the current connection state.
@@ -1126,7 +1248,7 @@ module Net
1126
1248
  @greeting = nil
1127
1249
  @capabilities = nil
1128
1250
  @enabled = Set.new
1129
- @tls_verified = false
1251
+ @tls_connected = @tls_verified = false
1130
1252
  @connection_state = ConnectionState::NotAuthenticated.new
1131
1253
 
1132
1254
  # Client Protocol Receiver
@@ -1185,12 +1307,11 @@ module Net
1185
1307
  end
1186
1308
 
1187
1309
  private def inspect_tls_state
1188
- if tls_verified?
1189
- "TLS"
1190
- elsif ssl_ctx && @sock.kind_of?(OpenSSL::SSL::SSLSocket)
1191
- "TLS (#{@sock.session ? "NOT VERIFIED" : "NOT ESTABLISHED"})"
1192
- else
1193
- "PLAINTEXT#{" (TLS NOT STARTED)" if ssl_ctx}"
1310
+ if tls_verified? then "TLS"
1311
+ elsif tls_connected? then "TLS (NOT VERIFIED)"
1312
+ elsif tls_socket? then "TLS (NOT ESTABLISHED)"
1313
+ elsif ssl_ctx then "PLAINTEXT (TLS NOT STARTED)"
1314
+ else "PLAINTEXT"
1194
1315
  end
1195
1316
  end
1196
1317
 
@@ -1199,6 +1320,30 @@ module Net
1199
1320
  # but peer verification was disabled.
1200
1321
  def tls_verified?; @tls_verified end
1201
1322
 
1323
+ # Returns +true+ after
1324
+ # {OpenSSL::SSL::SSLSocket#connect}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLSocket.html#method-i-connect]
1325
+ # completes successfully.
1326
+ #
1327
+ # <em>*NOTE:* This does _NOT_ indicate that the remote hostname has been
1328
+ # verified.</em>
1329
+ #
1330
+ # This does _not_ indicate current connection state. It will continue to
1331
+ # return +true+ even after a successful connection has disconnected.
1332
+ #
1333
+ # See #tls_verified?
1334
+ def tls_connected?; @tls_connected end
1335
+
1336
+ # Returns +true+ when the connection is a
1337
+ # {OpenSSL::SSL::SSLSocket}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLSocket.html]
1338
+ #
1339
+ # <em>*NOTE:* This does _NOT_ indicate that a TLS session has been
1340
+ # established or that remote hostname has been verified.</em>
1341
+ #
1342
+ # This only indicates that TLS negotiation has started.
1343
+ #
1344
+ # See #tls_verified?
1345
+ def tls_socket?; @sock.kind_of?(OpenSSL::SSL::SSLSocket) end
1346
+
1202
1347
  # Disconnects from the server.
1203
1348
  #
1204
1349
  # Waits for receiver thread to close before returning, except when called
@@ -3331,7 +3476,7 @@ module Net
3331
3476
  # Prints a warning and returns the mutable responses hash.
3332
3477
  # <em>This is not thread-safe.</em>
3333
3478
  #
3334
- # [+:frozen_dup+ <em>(planned default for +v0.6+)</em>]
3479
+ # [+:frozen_dup+ <em>(default since +v0.6+)</em>]
3335
3480
  # Returns a frozen copy of the unhandled responses hash, with frozen
3336
3481
  # array values.
3337
3482
  #
@@ -3432,7 +3577,7 @@ module Net
3432
3577
  def clear_responses(type = nil)
3433
3578
  synchronize {
3434
3579
  if type
3435
- @responses.delete(type) || []
3580
+ @responses.delete(type.to_s.upcase) || []
3436
3581
  else
3437
3582
  @responses.dup.transform_values(&:freeze)
3438
3583
  .tap { _1.default = [].freeze }
@@ -3901,6 +4046,9 @@ module Net
3901
4046
  raise ArgumentError, "partial can only be used with uid_fetch"
3902
4047
  end
3903
4048
  set = SequenceSet[set]
4049
+ mod in nil | Array or
4050
+ raise TypeError, "expected nil or array, got #{mod.class}"
4051
+ mod &&= mod.dup
3904
4052
  if partial
3905
4053
  mod ||= []
3906
4054
  mod << "PARTIAL" << PartialRange[partial]
@@ -3993,7 +4141,7 @@ module Net
3993
4141
 
3994
4142
  def build_ssl_ctx(ssl)
3995
4143
  if ssl
3996
- params = (Hash.try_convert(ssl) || {}).freeze
4144
+ params = (Hash.try_convert(ssl) || {}).clone(freeze: true)
3997
4145
  context = OpenSSL::SSL::SSLContext.new
3998
4146
  context.set_params(params)
3999
4147
  context.setup
@@ -4012,6 +4160,7 @@ module Net
4012
4160
  @sock.sync_close = true
4013
4161
  @sock.hostname = @host if @sock.respond_to? :hostname=
4014
4162
  ssl_socket_connect(@sock, open_timeout)
4163
+ @tls_connected = true
4015
4164
  if ssl_ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
4016
4165
  @sock.post_connection_check(@host)
4017
4166
  @tls_verified = true
data/rakelib/rdoc.rake CHANGED
@@ -6,6 +6,27 @@ require 'rdoc/rdoc' unless defined?(RDoc::Markup::ToHtml)
6
6
  module RDoc::Generator
7
7
  module NetIMAP
8
8
 
9
+ module FixPrismParserAttrVisitor
10
+
11
+ # Replaces the version in rdoc 8.0 with rdoc 7.2 behavior
12
+ def _visit_call_attr_reader_writer_accessor(call_node, rw)
13
+ return if @scanner.in_proc_block
14
+ names = initial_symbol_arguments(call_node) or return
15
+ @scanner.add_attributes(names.map(&:to_s), rw, call_node.location.start_line)
16
+ end
17
+
18
+ # Unlike #symbol_arguments, which is strict about _all_ arguments being
19
+ # symbol literals, this returns initial symbol args and ignores the rest.
20
+ def initial_symbol_arguments(call_node)
21
+ arguments_node = call_node.arguments or return
22
+ symbol_args = arguments_node.arguments
23
+ .slice_before {|arg| !arg.is_a?(Prism::SymbolNode) }
24
+ .first
25
+ symbol_args.map {|arg| arg.value.to_sym } if symbol_args.any?
26
+ end
27
+
28
+ end
29
+
9
30
  module RemoveRedundantParens
10
31
  def param_seq
11
32
  super.sub(/^\(\)\s*/, "")
@@ -45,6 +66,9 @@ class RDoc::Markup::ToHtml
45
66
  prepend RDoc::Generator::NetIMAP::LabelListTable
46
67
  end
47
68
 
69
+ (RDoc::Parser::Ruby::RDocVisitor rescue nil)
70
+ &.prepend RDoc::Generator::NetIMAP::FixPrismParserAttrVisitor
71
+
48
72
  RDoc::Task.new do |doc|
49
73
  doc.title = "net-imap #{Net::IMAP::VERSION}"
50
74
  doc.rdoc_dir = "doc"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.6
4
+ version: 0.6.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
@@ -132,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
132
  - !ruby/object:Gem::Version
133
133
  version: '0'
134
134
  requirements: []
135
- rubygems_version: 4.0.17
135
+ rubygems_version: 4.0.20
136
136
  specification_version: 4
137
137
  summary: Ruby client api for Internet Message Access Protocol
138
138
  test_files: []