net-imap 0.5.8 → 0.6.4
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 +4 -4
- data/.document +3 -0
- data/.rdoc_options +7 -0
- data/Gemfile +8 -5
- data/README.md +1 -1
- data/lib/net/imap/command_data.rb +170 -80
- data/lib/net/imap/config/attr_accessors.rb +8 -9
- data/lib/net/imap/config/attr_inheritance.rb +64 -1
- data/lib/net/imap/config/attr_type_coercion.rb +18 -6
- data/lib/net/imap/config/attr_version_defaults.rb +90 -0
- data/lib/net/imap/config.rb +244 -122
- data/lib/net/imap/connection_state.rb +1 -1
- data/lib/net/imap/data_encoding.rb +126 -27
- data/lib/net/imap/errors.rb +189 -0
- data/lib/net/imap/esearch_result.rb +48 -3
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +110 -15
- data/lib/net/imap/response_parser/parser_utils.rb +14 -23
- data/lib/net/imap/response_parser.rb +40 -17
- data/lib/net/imap/response_reader.rb +25 -16
- data/lib/net/imap/sasl/scram_authenticator.rb +74 -0
- data/lib/net/imap/search_result.rb +13 -4
- data/lib/net/imap/sequence_set.rb +936 -397
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap/vanished_data.rb +10 -1
- data/lib/net/imap.rb +245 -100
- data/net-imap.gemspec +1 -1
- data/rakelib/rdoc.rake +1 -18
- data/rakelib/string_prep_tables_generator.rb +4 -2
- metadata +6 -4
- data/lib/net/imap/data_lite.rb +0 -226
data/lib/net/imap.rb
CHANGED
|
@@ -359,8 +359,8 @@ module Net
|
|
|
359
359
|
#
|
|
360
360
|
# - #capability: Returns the server's capabilities as an array of strings.
|
|
361
361
|
#
|
|
362
|
-
# <em>In general
|
|
363
|
-
# +CAPABILITY+ command to the server.</em>
|
|
362
|
+
# <em>In general,</em> #capable? <em>should be used rather than explicitly
|
|
363
|
+
# sending a +CAPABILITY+ command to the server.</em>
|
|
364
364
|
# - #noop: Allows the server to send unsolicited untagged #responses.
|
|
365
365
|
# - #logout: Tells the server to end the session. Enters the +logout+ state.
|
|
366
366
|
#
|
|
@@ -450,8 +450,8 @@ module Net
|
|
|
450
450
|
#
|
|
451
451
|
# Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
|
|
452
452
|
# yet, Net::IMAP supports several extensions that have been folded into it:
|
|
453
|
-
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+,
|
|
454
|
-
# <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
|
|
453
|
+
# +ENABLE+, +IDLE+, +LITERAL-+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+,
|
|
454
|
+
# +UNSELECT+, <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
|
|
455
455
|
# Commands for these extensions are listed with the {Core IMAP
|
|
456
456
|
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
|
|
457
457
|
#
|
|
@@ -459,9 +459,12 @@ module Net
|
|
|
459
459
|
# <em>The following are folded into +IMAP4rev2+ but are currently
|
|
460
460
|
# unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
|
|
461
461
|
# extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
|
|
462
|
-
#
|
|
462
|
+
# and +SPECIAL-USE+.</em>
|
|
463
463
|
#
|
|
464
464
|
# ==== RFC2087: +QUOTA+
|
|
465
|
+
# +NOTE:+ Only the +STORAGE+ quota resource type is currently supported.
|
|
466
|
+
# - Obsoleted by <tt>QUOTA=RES-*</tt> [RFC9208[https://www.rfc-editor.org/rfc/rfc9208]],
|
|
467
|
+
# although the commands are backward compatible.
|
|
465
468
|
# - #getquota: returns the resource usage and limits for a quota root
|
|
466
469
|
# - #getquotaroot: returns the list of quota roots for a mailbox, as well as
|
|
467
470
|
# their resource usage and limits.
|
|
@@ -486,9 +489,7 @@ module Net
|
|
|
486
489
|
# IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051].
|
|
487
490
|
# - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
|
|
488
491
|
# +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
|
|
489
|
-
#
|
|
490
|
-
# >>>
|
|
491
|
-
# *NOTE:* The binary extension the #append command is _not_ supported yet.
|
|
492
|
+
# - Updates #append to allow binary messages containing +NULL+ bytes.
|
|
492
493
|
#
|
|
493
494
|
# ==== RFC3691: +UNSELECT+
|
|
494
495
|
# Folded into IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] and also included
|
|
@@ -568,6 +569,15 @@ module Net
|
|
|
568
569
|
# - Updates #store and #uid_store with the +unchangedsince+ modifier and adds
|
|
569
570
|
# the +MODIFIED+ ResponseCode to the tagged response.
|
|
570
571
|
#
|
|
572
|
+
# ==== RFC7888: <tt>LITERAL+</tt>
|
|
573
|
+
# - Literal strings smaller than Config#max_non_synchronizing_literal bytes
|
|
574
|
+
# are sent without waiting for the server's continuation request.
|
|
575
|
+
#
|
|
576
|
+
# ==== RFC7888: +LITERAL-+
|
|
577
|
+
# - Literal strings smaller than 4096 bytes or
|
|
578
|
+
# Config#max_non_synchronizing_literal (whichever is smaller)
|
|
579
|
+
# are sent without waiting for the server's continuation request.
|
|
580
|
+
#
|
|
571
581
|
# ==== RFC8438: <tt>STATUS=SIZE</tt>
|
|
572
582
|
# - Updates #status with the +SIZE+ status attribute.
|
|
573
583
|
#
|
|
@@ -578,6 +588,16 @@ module Net
|
|
|
578
588
|
# See FetchData#emailid and FetchData#emailid.
|
|
579
589
|
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
|
580
590
|
#
|
|
591
|
+
# ==== RFC9208: <tt>QUOTA=RES-*</tt>
|
|
592
|
+
# +NOTE:+ Only the +STORAGE+ quota resource type is currently supported.
|
|
593
|
+
# - Obsoletes the +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
594
|
+
# extension and provides strict semantics for different resource types.
|
|
595
|
+
# - #getquota: returns the resource usage and limits for a quota root
|
|
596
|
+
# - #getquotaroot: returns the list of quota roots for a mailbox, as well as
|
|
597
|
+
# their resource usage and limits.
|
|
598
|
+
# - #setquota: sets the resource limits for a given quota root.
|
|
599
|
+
# - Updates #status with <tt>"DELETED"</tt> and +DELETED-STORAGE+ attributes.
|
|
600
|
+
#
|
|
581
601
|
# ==== RFC9394: +PARTIAL+
|
|
582
602
|
# - Updates #search, #uid_search with the +PARTIAL+ return option which adds
|
|
583
603
|
# ESearchResult#partial return data.
|
|
@@ -631,9 +651,9 @@ module Net
|
|
|
631
651
|
# RFC 5322, DOI 10.17487/RFC5322, October 2008,
|
|
632
652
|
# <https://www.rfc-editor.org/info/rfc5322>.
|
|
633
653
|
#
|
|
634
|
-
#
|
|
635
|
-
# RFC-2822[https://www.rfc-editor.org/rfc/rfc2822]
|
|
636
|
-
# RFC-822[https://www.rfc-editor.org/rfc/rfc822]
|
|
654
|
+
# *NOTE*: obsoletes
|
|
655
|
+
# RFC-2822[https://www.rfc-editor.org/rfc/rfc2822] (April 2001) and
|
|
656
|
+
# RFC-822[https://www.rfc-editor.org/rfc/rfc822] (August 1982).
|
|
637
657
|
#
|
|
638
658
|
# [CHARSET[https://www.rfc-editor.org/rfc/rfc2978]]::
|
|
639
659
|
# Freed, N. and J. Postel, "IANA Charset Registration Procedures", BCP 19,
|
|
@@ -698,13 +718,12 @@ module Net
|
|
|
698
718
|
#
|
|
699
719
|
# === \IMAP Extensions
|
|
700
720
|
#
|
|
701
|
-
# [QUOTA[https://www.rfc-editor.org/rfc/
|
|
702
|
-
#
|
|
703
|
-
#
|
|
721
|
+
# [QUOTA[https://www.rfc-editor.org/rfc/rfc2087]]::
|
|
722
|
+
# Myers, J., "IMAP4 QUOTA extension", RFC 2087, DOI 10.17487/RFC2087,
|
|
723
|
+
# January 1997, <https://www.rfc-editor.org/info/rfc2087>.
|
|
704
724
|
#
|
|
705
|
-
#
|
|
706
|
-
#
|
|
707
|
-
# <em>Net::IMAP does not fully support the RFC9208 updates yet.</em>
|
|
725
|
+
# *NOTE*: _obsoleted_ by RFC9208[https://www.rfc-editor.org/rfc/rfc9208]
|
|
726
|
+
# (March 2022).
|
|
708
727
|
# [IDLE[https://www.rfc-editor.org/rfc/rfc2177]]::
|
|
709
728
|
# Leiba, B., "IMAP4 IDLE command", RFC 2177, DOI 10.17487/RFC2177,
|
|
710
729
|
# June 1997, <https://www.rfc-editor.org/info/rfc2177>.
|
|
@@ -741,8 +760,8 @@ module Net
|
|
|
741
760
|
# Gulbrandsen, A. and N. Freed, Ed., "Internet Message Access Protocol
|
|
742
761
|
# (\IMAP) - MOVE Extension", RFC 6851, DOI 10.17487/RFC6851, January 2013,
|
|
743
762
|
# <https://www.rfc-editor.org/info/rfc6851>.
|
|
744
|
-
# [UTF8=ACCEPT[https://www.rfc-editor.org/rfc/rfc6855]]::
|
|
745
|
-
# [UTF8=ONLY[https://www.rfc-editor.org/rfc/rfc6855]]::
|
|
763
|
+
# [{UTF8=ACCEPT}[https://www.rfc-editor.org/rfc/rfc6855]]::
|
|
764
|
+
# [{UTF8=ONLY}[https://www.rfc-editor.org/rfc/rfc6855]]::
|
|
746
765
|
# Resnick, P., Ed., Newman, C., Ed., and S. Shen, Ed.,
|
|
747
766
|
# "IMAP Support for UTF-8", RFC 6855, DOI 10.17487/RFC6855, March 2013,
|
|
748
767
|
# <https://www.rfc-editor.org/info/rfc6855>.
|
|
@@ -756,6 +775,11 @@ module Net
|
|
|
756
775
|
# Gondwana, B., Ed., "IMAP Extension for Object Identifiers",
|
|
757
776
|
# RFC 8474, DOI 10.17487/RFC8474, September 2018,
|
|
758
777
|
# <https://www.rfc-editor.org/info/rfc8474>.
|
|
778
|
+
# [{QUOTA=RES-*}[https://www.rfc-editor.org/rfc/rfc9208]]::
|
|
779
|
+
# Melnikov, A., "IMAP QUOTA Extension", RFC 9208, DOI 10.17487/RFC9208,
|
|
780
|
+
# March 2022, <https://www.rfc-editor.org/info/rfc9208>.
|
|
781
|
+
#
|
|
782
|
+
# Obsoletes RFC2087[https://www.rfc-editor.org/rfc/rfc2087].
|
|
759
783
|
# [PARTIAL[https://www.rfc-editor.org/info/rfc9394]]::
|
|
760
784
|
# Melnikov, A., Achuthan, A., Nagulakonda, V., and L. Alves,
|
|
761
785
|
# "IMAP PARTIAL Extension for Paged SEARCH and FETCH", RFC 9394,
|
|
@@ -769,6 +793,7 @@ module Net
|
|
|
769
793
|
#
|
|
770
794
|
# === IANA registries
|
|
771
795
|
# * {IMAP Capabilities}[http://www.iana.org/assignments/imap4-capabilities]
|
|
796
|
+
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
|
772
797
|
# * {IMAP Response Codes}[https://www.iana.org/assignments/imap-response-codes/imap-response-codes.xhtml]
|
|
773
798
|
# * {IMAP Mailbox Name Attributes}[https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml]
|
|
774
799
|
# * {IMAP and JMAP Keywords}[https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml]
|
|
@@ -779,8 +804,8 @@ module Net
|
|
|
779
804
|
# * {GSSAPI/Kerberos/SASL Service Names}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml]:
|
|
780
805
|
# +imap+
|
|
781
806
|
# * {Character sets}[https://www.iana.org/assignments/character-sets/character-sets.xhtml]
|
|
807
|
+
#
|
|
782
808
|
# ==== For currently unsupported features:
|
|
783
|
-
# * {IMAP Quota Resource Types}[http://www.iana.org/assignments/imap4-capabilities#imap-capabilities-2]
|
|
784
809
|
# * {LIST-EXTENDED options and responses}[https://www.iana.org/assignments/imap-list-extended/imap-list-extended.xhtml]
|
|
785
810
|
# * {IMAP METADATA Server Entry and Mailbox Entry Registries}[https://www.iana.org/assignments/imap-metadata/imap-metadata.xhtml]
|
|
786
811
|
# * {IMAP ANNOTATE Extension Entries and Attributes}[https://www.iana.org/assignments/imap-annotate-extension/imap-annotate-extension.xhtml]
|
|
@@ -788,7 +813,7 @@ module Net
|
|
|
788
813
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
|
789
814
|
#
|
|
790
815
|
class IMAP < Protocol
|
|
791
|
-
VERSION = "0.
|
|
816
|
+
VERSION = "0.6.4"
|
|
792
817
|
|
|
793
818
|
# Aliases for supported capabilities, to be used with the #enable command.
|
|
794
819
|
ENABLE_ALIASES = {
|
|
@@ -801,12 +826,25 @@ module Net
|
|
|
801
826
|
autoload :ResponseReader, "#{dir}/response_reader"
|
|
802
827
|
autoload :SASL, "#{dir}/sasl"
|
|
803
828
|
autoload :SASLAdapter, "#{dir}/sasl_adapter"
|
|
829
|
+
autoload :SequenceSet, "#{dir}/sequence_set"
|
|
804
830
|
autoload :StringPrep, "#{dir}/stringprep"
|
|
805
831
|
|
|
806
832
|
include MonitorMixin
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
833
|
+
|
|
834
|
+
# :call-seq:
|
|
835
|
+
# Net::IMAP::SequenceSet(set = nil) -> SequenceSet
|
|
836
|
+
#
|
|
837
|
+
# Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
|
|
838
|
+
# SequenceSet.new.
|
|
839
|
+
#
|
|
840
|
+
# * When +set+ is a SequenceSet, that same set is returned.
|
|
841
|
+
# * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
|
|
842
|
+
# returned.
|
|
843
|
+
# * Otherwise, returns the result from calling SequenceSet.new with +set+.
|
|
844
|
+
#
|
|
845
|
+
# Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
|
|
846
|
+
def self.SequenceSet(set = nil)
|
|
847
|
+
SequenceSet.try_convert(set) || SequenceSet.new(set)
|
|
810
848
|
end
|
|
811
849
|
|
|
812
850
|
# Returns the global Config object
|
|
@@ -1107,6 +1145,31 @@ module Net
|
|
|
1107
1145
|
start_imap_connection
|
|
1108
1146
|
end
|
|
1109
1147
|
|
|
1148
|
+
# Returns a string representation of +self+, showing basic client state
|
|
1149
|
+
# information.
|
|
1150
|
+
#
|
|
1151
|
+
# imap = Net::IMAP.new(hostname, ssl: true)
|
|
1152
|
+
# imap.inspect #=> "#<Net::IMAP imap.example.net:993 TLS not_authenticated>"
|
|
1153
|
+
#
|
|
1154
|
+
# imap.authenticate(:oauthbearer, "user", token)
|
|
1155
|
+
# imap.inspect #=> "#<Net::IMAP imap.example.net:993 TLS authenticated>"
|
|
1156
|
+
#
|
|
1157
|
+
# imap.select("INBOX")
|
|
1158
|
+
# imap.inspect #=> "#<Net::IMAP imap.example.net:993 TLS selected>"
|
|
1159
|
+
#
|
|
1160
|
+
# imap.logout
|
|
1161
|
+
# imap.inspect #=> "#<Net::IMAP imap.example.net:993 TLS logout>"
|
|
1162
|
+
#
|
|
1163
|
+
def inspect
|
|
1164
|
+
tls_state = tls_verified? ? "TLS" :
|
|
1165
|
+
ssl_ctx ? "TLS (NOT VERIFIED)" :
|
|
1166
|
+
"PLAINTEXT"
|
|
1167
|
+
conn_state = disconnected? ? "disconnected" : connection_state.to_sym
|
|
1168
|
+
"#<%s:0x%08x %s:%s %s %s>" % [
|
|
1169
|
+
self.class.name, __id__, host, port, tls_state, conn_state
|
|
1170
|
+
]
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1110
1173
|
# Returns true after the TLS negotiation has completed and the remote
|
|
1111
1174
|
# hostname has been verified. Returns false when TLS has been established
|
|
1112
1175
|
# but peer verification was disabled.
|
|
@@ -1114,28 +1177,27 @@ module Net
|
|
|
1114
1177
|
|
|
1115
1178
|
# Disconnects from the server.
|
|
1116
1179
|
#
|
|
1180
|
+
# Waits for receiver thread to close before returning. Slow or stuck
|
|
1181
|
+
# response handlers can cause #disconnect to hang until they complete.
|
|
1182
|
+
#
|
|
1117
1183
|
# Related: #logout, #logout!
|
|
1118
1184
|
def disconnect
|
|
1185
|
+
in_logout_state = try_state_logout?
|
|
1119
1186
|
return if disconnected?
|
|
1120
|
-
state_logout!
|
|
1121
1187
|
begin
|
|
1122
|
-
|
|
1123
|
-
# try to call SSL::SSLSocket#io.
|
|
1124
|
-
@sock.io.shutdown
|
|
1125
|
-
rescue NoMethodError
|
|
1126
|
-
# @sock is not an SSL::SSLSocket.
|
|
1127
|
-
@sock.shutdown
|
|
1128
|
-
end
|
|
1188
|
+
@sock.to_io.shutdown
|
|
1129
1189
|
rescue Errno::ENOTCONN
|
|
1130
1190
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
|
1131
1191
|
rescue Exception => e
|
|
1132
1192
|
@receiver_thread.raise(e)
|
|
1133
1193
|
end
|
|
1194
|
+
@sock.close
|
|
1134
1195
|
@receiver_thread.join
|
|
1135
|
-
synchronize do
|
|
1136
|
-
@sock.close
|
|
1137
|
-
end
|
|
1138
1196
|
raise e if e
|
|
1197
|
+
ensure
|
|
1198
|
+
# Try again after shutting down the receiver thread. With no reciever
|
|
1199
|
+
# left to wait for, any remaining locks should be _very_ brief.
|
|
1200
|
+
state_logout! unless in_logout_state
|
|
1139
1201
|
end
|
|
1140
1202
|
|
|
1141
1203
|
# Returns true if disconnected from the server.
|
|
@@ -1378,9 +1440,11 @@ module Net
|
|
|
1378
1440
|
#
|
|
1379
1441
|
def starttls(**options)
|
|
1380
1442
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
|
1443
|
+
handled = false
|
|
1381
1444
|
error = nil
|
|
1382
1445
|
ok = send_command("STARTTLS") do |resp|
|
|
1383
1446
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
|
1447
|
+
handled = true
|
|
1384
1448
|
clear_cached_capabilities
|
|
1385
1449
|
clear_responses
|
|
1386
1450
|
start_tls_session
|
|
@@ -1392,6 +1456,13 @@ module Net
|
|
|
1392
1456
|
disconnect
|
|
1393
1457
|
raise error
|
|
1394
1458
|
end
|
|
1459
|
+
unless handled
|
|
1460
|
+
disconnect
|
|
1461
|
+
raise InvalidResponseError,
|
|
1462
|
+
"STARTTLS handler was bypassed, although server responded %p" % [
|
|
1463
|
+
ok.raw_data.chomp
|
|
1464
|
+
]
|
|
1465
|
+
end
|
|
1395
1466
|
ok
|
|
1396
1467
|
end
|
|
1397
1468
|
|
|
@@ -1506,6 +1577,7 @@ module Net
|
|
|
1506
1577
|
# completes. If the TaggedResponse to #authenticate includes updated
|
|
1507
1578
|
# capabilities, they will be cached.
|
|
1508
1579
|
def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
|
|
1580
|
+
sasl_ir = may_depend_on_capabilities_cached?(sasl_ir)
|
|
1509
1581
|
sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
|
|
1510
1582
|
.tap do state_authenticated! _1 end
|
|
1511
1583
|
end
|
|
@@ -1557,7 +1629,7 @@ module Net
|
|
|
1557
1629
|
# When the +condstore+ keyword argument is true, the server is told to
|
|
1558
1630
|
# enable the extension. If +mailbox+ supports persistence of mod-sequences,
|
|
1559
1631
|
# the +HIGHESTMODSEQ+ ResponseCode will be sent as an untagged response to
|
|
1560
|
-
# #select and all
|
|
1632
|
+
# #select and all +FETCH+ responses will include FetchData#modseq.
|
|
1561
1633
|
# Otherwise, the +NOMODSEQ+ ResponseCode will be sent.
|
|
1562
1634
|
#
|
|
1563
1635
|
# A Net::IMAP::NoResponseError is raised if the mailbox does not
|
|
@@ -1812,12 +1884,18 @@ module Net
|
|
|
1812
1884
|
# to both admin and user. If this mailbox exists, it returns an array
|
|
1813
1885
|
# containing objects of type MailboxQuotaRoot and MailboxQuota.
|
|
1814
1886
|
#
|
|
1887
|
+
# *NOTE:* Currently, Net::IMAP only supports +QUOTA+ responses with a single
|
|
1888
|
+
# resource type. This is usually +STORAGE+, but you may need to verify this
|
|
1889
|
+
# with UntaggedResponse#raw_data.
|
|
1890
|
+
#
|
|
1815
1891
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
|
1816
1892
|
#
|
|
1817
1893
|
# ==== Capabilities
|
|
1818
1894
|
#
|
|
1819
|
-
#
|
|
1820
|
-
#
|
|
1895
|
+
# Requires +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
1896
|
+
# capability, or a capability prefixed with <tt>QUOTA=RES-*</tt>
|
|
1897
|
+
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208] for each supported
|
|
1898
|
+
# resource type.
|
|
1821
1899
|
def getquotaroot(mailbox)
|
|
1822
1900
|
synchronize do
|
|
1823
1901
|
send_command("GETQUOTAROOT", mailbox)
|
|
@@ -1829,41 +1907,59 @@ module Net
|
|
|
1829
1907
|
end
|
|
1830
1908
|
|
|
1831
1909
|
# Sends a {GETQUOTA command [RFC2087 §4.2]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.2]
|
|
1832
|
-
#
|
|
1833
|
-
# containing a MailboxQuota object is returned.
|
|
1834
|
-
#
|
|
1910
|
+
# for the +quota_root+. If this quota root exists, then an array
|
|
1911
|
+
# containing a MailboxQuota object is returned.
|
|
1912
|
+
#
|
|
1913
|
+
# The names of quota roots that are applicable to a particular mailbox can
|
|
1914
|
+
# be discovered with #getquotaroot.
|
|
1915
|
+
#
|
|
1916
|
+
# *NOTE:* Currently, Net::IMAP only supports +QUOTA+ responses with a single
|
|
1917
|
+
# resource type. This is usually +STORAGE+, but you may need to verify this
|
|
1918
|
+
# with UntaggedResponse#raw_data.
|
|
1835
1919
|
#
|
|
1836
1920
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
|
1837
1921
|
#
|
|
1838
1922
|
# ==== Capabilities
|
|
1839
1923
|
#
|
|
1840
|
-
#
|
|
1841
|
-
#
|
|
1842
|
-
|
|
1924
|
+
# Requires +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
1925
|
+
# capability, or a capability prefixed with <tt>QUOTA=RES-*</tt>
|
|
1926
|
+
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208] for each supported
|
|
1927
|
+
# resource type.
|
|
1928
|
+
def getquota(quota_root)
|
|
1843
1929
|
synchronize do
|
|
1844
|
-
send_command("GETQUOTA",
|
|
1930
|
+
send_command("GETQUOTA", quota_root)
|
|
1845
1931
|
clear_responses("QUOTA")
|
|
1846
1932
|
end
|
|
1847
1933
|
end
|
|
1848
1934
|
|
|
1849
1935
|
# Sends a {SETQUOTA command [RFC2087 §4.1]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.1]
|
|
1850
|
-
# along with the specified +
|
|
1851
|
-
# +
|
|
1852
|
-
#
|
|
1936
|
+
# along with the specified +quota_root+ and +storage_limit+. If
|
|
1937
|
+
# +storage_limit+ is +nil+, resource limits are unset for that quota root.
|
|
1938
|
+
# If +storage_limit+ is a number, it sets the +STORAGE+ resource limit.
|
|
1939
|
+
#
|
|
1940
|
+
# imap.setquota "#user/alice", 100
|
|
1941
|
+
# imap.getquota "#user/alice"
|
|
1942
|
+
# # => [#<struct Net::IMAP::MailboxQuota mailbox="#user/alice" usage=54 quota=100>]
|
|
1943
|
+
#
|
|
1944
|
+
# Typically one needs to be logged in as a server admin for this to work.
|
|
1945
|
+
#
|
|
1946
|
+
# *NOTE:* Currently, Net::IMAP only supports setting +STORAGE+ quota limits.
|
|
1853
1947
|
#
|
|
1854
1948
|
# Related: #getquota, #getquotaroot
|
|
1855
1949
|
#
|
|
1856
1950
|
# ==== Capabilities
|
|
1857
1951
|
#
|
|
1858
|
-
#
|
|
1859
|
-
#
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1952
|
+
# Requires +QUOTA+ [RFC2087[https://www.rfc-editor.org/rfc/rfc2087]]
|
|
1953
|
+
# capability, or a capability prefixed with <tt>QUOTA=RES-*</tt>
|
|
1954
|
+
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208] for each supported
|
|
1955
|
+
# resource type.
|
|
1956
|
+
def setquota(quota_root, storage_limit)
|
|
1957
|
+
if storage_limit.nil?
|
|
1958
|
+
list = []
|
|
1863
1959
|
else
|
|
1864
|
-
|
|
1960
|
+
list = ["STORAGE", NumValidator.coerce_number64(storage_limit)]
|
|
1865
1961
|
end
|
|
1866
|
-
send_command("SETQUOTA",
|
|
1962
|
+
send_command("SETQUOTA", quota_root, list)
|
|
1867
1963
|
end
|
|
1868
1964
|
|
|
1869
1965
|
# Sends a {SETACL command [RFC4314 §3.1]}[https://www.rfc-editor.org/rfc/rfc4314#section-3.1]
|
|
@@ -1970,7 +2066,10 @@ module Net
|
|
|
1970
2066
|
# <tt>STATUS=SIZE</tt>
|
|
1971
2067
|
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
|
1972
2068
|
#
|
|
1973
|
-
# +DELETED+
|
|
2069
|
+
# +DELETED+ must be supported when the server's capabilities includes
|
|
2070
|
+
# +IMAP4rev2+.
|
|
2071
|
+
# or <tt>QUOTA=RES-MESSAGES</tt>
|
|
2072
|
+
# {[RFC9208]}[https://www.rfc-editor.org/rfc/rfc9208.html].
|
|
1974
2073
|
#
|
|
1975
2074
|
# +HIGHESTMODSEQ+ requires the server's capabilities to include +CONDSTORE+
|
|
1976
2075
|
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
|
@@ -2006,9 +2105,14 @@ module Net
|
|
|
2006
2105
|
#
|
|
2007
2106
|
# ==== Capabilities
|
|
2008
2107
|
#
|
|
2108
|
+
# If +BINARY+ [RFC3516[https://www.rfc-editor.org/rfc/rfc3516.html]] is
|
|
2109
|
+
# supported by the server, +message+ may contain +NULL+ characters and
|
|
2110
|
+
# be sent as a binary literal. Otherwise, binary message parts must be
|
|
2111
|
+
# encoded appropriately (for example, +base64+).
|
|
2112
|
+
#
|
|
2009
2113
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
|
2010
2114
|
# supported and the destination supports persistent UIDs, the server's
|
|
2011
|
-
# response should include an +APPENDUID+ response code with
|
|
2115
|
+
# response should include an +APPENDUID+ response code with AppendUIDData.
|
|
2012
2116
|
# This will report the UIDVALIDITY of the destination mailbox and the
|
|
2013
2117
|
# assigned UID of the appended message.
|
|
2014
2118
|
#
|
|
@@ -2016,12 +2120,11 @@ module Net
|
|
|
2016
2120
|
# TODO: add MULTIAPPEND support
|
|
2017
2121
|
#++
|
|
2018
2122
|
def append(mailbox, message, flags = nil, date_time = nil)
|
|
2123
|
+
message = StringFormatter.literal_or_literal8(message, name: "message")
|
|
2019
2124
|
args = []
|
|
2020
|
-
if flags
|
|
2021
|
-
args.push(flags)
|
|
2022
|
-
end
|
|
2125
|
+
args.push(flags) if flags
|
|
2023
2126
|
args.push(date_time) if date_time
|
|
2024
|
-
args.push(
|
|
2127
|
+
args.push(message)
|
|
2025
2128
|
send_command("APPEND", mailbox, *args)
|
|
2026
2129
|
end
|
|
2027
2130
|
|
|
@@ -2094,8 +2197,8 @@ module Net
|
|
|
2094
2197
|
end
|
|
2095
2198
|
|
|
2096
2199
|
# call-seq:
|
|
2097
|
-
# uid_expunge
|
|
2098
|
-
# uid_expunge
|
|
2200
|
+
# uid_expunge(uid_set) -> array of message sequence numbers
|
|
2201
|
+
# uid_expunge(uid_set) -> VanishedData of UIDs
|
|
2099
2202
|
#
|
|
2100
2203
|
# Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
|
|
2101
2204
|
# {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
|
|
@@ -2251,11 +2354,11 @@ module Net
|
|
|
2251
2354
|
# Encoded as an \IMAP date (see ::encode_date).
|
|
2252
2355
|
#
|
|
2253
2356
|
# [When +criteria+ is a String]
|
|
2254
|
-
# +criteria+ will be sent
|
|
2255
|
-
#
|
|
2357
|
+
# +criteria+ will be sent to the server <em>with minimal validation and no
|
|
2358
|
+
# encoding or formatting</em>.
|
|
2256
2359
|
#
|
|
2257
|
-
# <em>*WARNING:*
|
|
2258
|
-
#
|
|
2360
|
+
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
2361
|
+
# types of attribute injection attack if unvetted user input is used.</em>
|
|
2259
2362
|
#
|
|
2260
2363
|
# ==== Supported return options
|
|
2261
2364
|
#
|
|
@@ -2576,6 +2679,13 @@ module Net
|
|
|
2576
2679
|
#
|
|
2577
2680
|
# +attr+ is a list of attributes to fetch; see FetchStruct documentation for
|
|
2578
2681
|
# a list of supported attributes.
|
|
2682
|
+
# >>>
|
|
2683
|
+
# When +attr+ is a String, it will be sent <em>with minimal validation and
|
|
2684
|
+
# no encoding or formatting</em>. When +attr+ is an Array, each String in
|
|
2685
|
+
# +attr+ will be sent this way.
|
|
2686
|
+
#
|
|
2687
|
+
# <em>*WARNING:* Although CRLF is prohibited, this is vulnerable to other
|
|
2688
|
+
# types of attribute injection attack if unvetted user input is used.</em>
|
|
2579
2689
|
#
|
|
2580
2690
|
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
|
2581
2691
|
# messages with a mod-sequence greater than +changedsince+.
|
|
@@ -2659,6 +2769,7 @@ module Net
|
|
|
2659
2769
|
# # fetch should return quickly and allocate little memory
|
|
2660
2770
|
# results.size # => 0..500
|
|
2661
2771
|
# break if results.empty?
|
|
2772
|
+
# results.sort_by!(&:uid) # server may return results out of order
|
|
2662
2773
|
# next_uid_to_fetch = results.last.uid + 1
|
|
2663
2774
|
# process results
|
|
2664
2775
|
# end
|
|
@@ -2764,7 +2875,7 @@ module Net
|
|
|
2764
2875
|
#
|
|
2765
2876
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
|
2766
2877
|
# supported, the server's response should include a +COPYUID+ response code
|
|
2767
|
-
# with
|
|
2878
|
+
# with CopyUIDData. This will report the UIDVALIDITY of the destination
|
|
2768
2879
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
|
2769
2880
|
# the moved messages.
|
|
2770
2881
|
#
|
|
@@ -2805,7 +2916,7 @@ module Net
|
|
|
2805
2916
|
#
|
|
2806
2917
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
|
2807
2918
|
# supported, the server's response should include a +COPYUID+ response code
|
|
2808
|
-
# with
|
|
2919
|
+
# with CopyUIDData. This will report the UIDVALIDITY of the destination
|
|
2809
2920
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
|
2810
2921
|
# the moved messages.
|
|
2811
2922
|
#
|
|
@@ -2945,6 +3056,18 @@ module Net
|
|
|
2945
3056
|
# command parameters defined by the extension will implicitly enable it.
|
|
2946
3057
|
# See {[RFC7162 §3.1]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1].
|
|
2947
3058
|
#
|
|
3059
|
+
# [+QRESYNC+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]]
|
|
3060
|
+
# *NOTE:* Enabling QRESYNC will replace +EXPUNGE+ with +VANISHED+, but
|
|
3061
|
+
# the extension arguments to #select, #examine, and #uid_fetch are not
|
|
3062
|
+
# supported yet.
|
|
3063
|
+
#
|
|
3064
|
+
# Adds quick resynchronization options to #select, #examine, and
|
|
3065
|
+
# #uid_fetch. +QRESYNC+ _must_ be explicitly enabled before using any of
|
|
3066
|
+
# the extension's command parameters. All +EXPUNGE+ responses will be
|
|
3067
|
+
# replaced with +VANISHED+ responses. Enabling +QRESYNC+ implicitly
|
|
3068
|
+
# enables +CONDSTORE+ as well.
|
|
3069
|
+
# See {[RFC7162 §3.2]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.2].
|
|
3070
|
+
#
|
|
2948
3071
|
# [+:utf8+ --- an alias for <tt>"UTF8=ACCEPT"</tt>]
|
|
2949
3072
|
#
|
|
2950
3073
|
# In a future release, <tt>enable(:utf8)</tt> will enable either
|
|
@@ -3051,6 +3174,7 @@ module Net
|
|
|
3051
3174
|
|
|
3052
3175
|
synchronize do
|
|
3053
3176
|
tag = Thread.current[:net_imap_tag] = generate_tag
|
|
3177
|
+
guard_against_tagged_response_skipping_handler!(tag, "IDLE")
|
|
3054
3178
|
put_string("#{tag} IDLE#{CRLF}")
|
|
3055
3179
|
|
|
3056
3180
|
begin
|
|
@@ -3062,8 +3186,8 @@ module Net
|
|
|
3062
3186
|
raise @exception || Net::IMAP::Error.new("connection closed")
|
|
3063
3187
|
end
|
|
3064
3188
|
ensure
|
|
3189
|
+
remove_response_handler(response_handler)
|
|
3065
3190
|
unless @receiver_thread_terminating
|
|
3066
|
-
remove_response_handler(response_handler)
|
|
3067
3191
|
put_string("DONE#{CRLF}")
|
|
3068
3192
|
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
|
3069
3193
|
end
|
|
@@ -3204,7 +3328,7 @@ module Net
|
|
|
3204
3328
|
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
|
3205
3329
|
when :frozen_dup
|
|
3206
3330
|
synchronize {
|
|
3207
|
-
responses = @responses.transform_values
|
|
3331
|
+
responses = @responses.transform_values { _1.dup.freeze }
|
|
3208
3332
|
responses.default_proc = nil
|
|
3209
3333
|
responses.default = [].freeze
|
|
3210
3334
|
return responses.freeze
|
|
@@ -3346,8 +3470,6 @@ module Net
|
|
|
3346
3470
|
rescue Exception => ex
|
|
3347
3471
|
@receiver_thread_exception = ex
|
|
3348
3472
|
# don't exit the thread with an exception
|
|
3349
|
-
ensure
|
|
3350
|
-
state_logout!
|
|
3351
3473
|
end
|
|
3352
3474
|
end
|
|
3353
3475
|
|
|
@@ -3429,6 +3551,8 @@ module Net
|
|
|
3429
3551
|
@idle_done_cond.signal
|
|
3430
3552
|
end
|
|
3431
3553
|
end
|
|
3554
|
+
ensure
|
|
3555
|
+
state_logout!
|
|
3432
3556
|
end
|
|
3433
3557
|
|
|
3434
3558
|
def get_tagged_response(tag, cmd, timeout = nil)
|
|
@@ -3455,7 +3579,7 @@ module Net
|
|
|
3455
3579
|
raise BadResponseError, resp
|
|
3456
3580
|
else
|
|
3457
3581
|
disconnect
|
|
3458
|
-
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.
|
|
3582
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw_data.chomp]
|
|
3459
3583
|
end
|
|
3460
3584
|
end
|
|
3461
3585
|
|
|
@@ -3515,21 +3639,29 @@ module Net
|
|
|
3515
3639
|
put_string(" ")
|
|
3516
3640
|
send_data(i, tag)
|
|
3517
3641
|
end
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
end
|
|
3522
|
-
if block
|
|
3523
|
-
add_response_handler(&block)
|
|
3524
|
-
end
|
|
3642
|
+
@logout_command_tag = tag if cmd == "LOGOUT"
|
|
3643
|
+
guard_against_tagged_response_skipping_handler!(tag, cmd)
|
|
3644
|
+
add_response_handler(&block) if block
|
|
3525
3645
|
begin
|
|
3526
|
-
|
|
3646
|
+
put_string(CRLF)
|
|
3647
|
+
get_tagged_response(tag, cmd)
|
|
3527
3648
|
ensure
|
|
3528
|
-
if block
|
|
3529
|
-
remove_response_handler(block)
|
|
3530
|
-
end
|
|
3649
|
+
remove_response_handler(block) if block
|
|
3531
3650
|
end
|
|
3532
3651
|
end
|
|
3652
|
+
rescue InvalidResponseError
|
|
3653
|
+
disconnect
|
|
3654
|
+
raise
|
|
3655
|
+
end
|
|
3656
|
+
|
|
3657
|
+
def guard_against_tagged_response_skipping_handler!(tag, cmd)
|
|
3658
|
+
return unless (resp = @tagged_responses[tag])&.name&.upcase == "OK"
|
|
3659
|
+
raise InvalidResponseError, format(
|
|
3660
|
+
"Received tagged 'OK' to incomplete %s command (tag=%s). " \
|
|
3661
|
+
"This could indicate a malicious server, a man-in-the-middle, or " \
|
|
3662
|
+
"client-side command injection. Disconnecting.",
|
|
3663
|
+
cmd, tag
|
|
3664
|
+
)
|
|
3533
3665
|
end
|
|
3534
3666
|
|
|
3535
3667
|
def generate_tag
|
|
@@ -3553,11 +3685,11 @@ module Net
|
|
|
3553
3685
|
end
|
|
3554
3686
|
|
|
3555
3687
|
def enforce_logindisabled?
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3688
|
+
may_depend_on_capabilities_cached?(config.enforce_logindisabled)
|
|
3689
|
+
end
|
|
3690
|
+
|
|
3691
|
+
def may_depend_on_capabilities_cached?(value)
|
|
3692
|
+
value == :when_capabilities_cached ? capabilities_cached? : value
|
|
3561
3693
|
end
|
|
3562
3694
|
|
|
3563
3695
|
def expunge_internal(...)
|
|
@@ -3656,6 +3788,9 @@ module Net
|
|
|
3656
3788
|
end
|
|
3657
3789
|
|
|
3658
3790
|
def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
|
|
3791
|
+
if partial && !cmd.start_with?("UID ")
|
|
3792
|
+
raise ArgumentError, "partial can only be used with uid_fetch"
|
|
3793
|
+
end
|
|
3659
3794
|
set = SequenceSet[set]
|
|
3660
3795
|
if partial
|
|
3661
3796
|
mod ||= []
|
|
@@ -3680,7 +3815,7 @@ module Net
|
|
|
3680
3815
|
end
|
|
3681
3816
|
|
|
3682
3817
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
|
3683
|
-
attr =
|
|
3818
|
+
attr = Atom.new(attr) if attr.instance_of?(String)
|
|
3684
3819
|
args = [SequenceSet.new(set)]
|
|
3685
3820
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
|
3686
3821
|
args << attr << flags
|
|
@@ -3750,12 +3885,9 @@ module Net
|
|
|
3750
3885
|
def build_ssl_ctx(ssl)
|
|
3751
3886
|
if ssl
|
|
3752
3887
|
params = (Hash.try_convert(ssl) || {}).freeze
|
|
3753
|
-
context = SSLContext.new
|
|
3888
|
+
context = OpenSSL::SSL::SSLContext.new
|
|
3754
3889
|
context.set_params(params)
|
|
3755
|
-
|
|
3756
|
-
context.verify_callback = VerifyCallbackProc
|
|
3757
|
-
end
|
|
3758
|
-
context.freeze
|
|
3890
|
+
context.setup
|
|
3759
3891
|
[params, context]
|
|
3760
3892
|
else
|
|
3761
3893
|
false
|
|
@@ -3766,12 +3898,12 @@ module Net
|
|
|
3766
3898
|
raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
|
|
3767
3899
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
|
3768
3900
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
|
3769
|
-
@sock = SSLSocket.new(@sock, ssl_ctx)
|
|
3901
|
+
@sock = OpenSSL::SSL::SSLSocket.new(@sock, ssl_ctx)
|
|
3770
3902
|
@reader = ResponseReader.new(self, @sock)
|
|
3771
3903
|
@sock.sync_close = true
|
|
3772
3904
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
|
3773
3905
|
ssl_socket_connect(@sock, open_timeout)
|
|
3774
|
-
if ssl_ctx.verify_mode != VERIFY_NONE
|
|
3906
|
+
if ssl_ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
|
3775
3907
|
@sock.post_connection_check(@host)
|
|
3776
3908
|
@tls_verified = true
|
|
3777
3909
|
end
|
|
@@ -3791,15 +3923,29 @@ module Net
|
|
|
3791
3923
|
end
|
|
3792
3924
|
|
|
3793
3925
|
def state_unselected!
|
|
3794
|
-
|
|
3926
|
+
synchronize do
|
|
3927
|
+
state_authenticated! if connection_state.to_sym == :selected
|
|
3928
|
+
end
|
|
3795
3929
|
end
|
|
3796
3930
|
|
|
3797
3931
|
def state_logout!
|
|
3932
|
+
return true if connection_state in [:logout, *]
|
|
3798
3933
|
synchronize do
|
|
3934
|
+
return true if connection_state in [:logout, *]
|
|
3799
3935
|
@connection_state = ConnectionState::Logout.new
|
|
3800
3936
|
end
|
|
3801
3937
|
end
|
|
3802
3938
|
|
|
3939
|
+
# don't wait to aqcuire the lock
|
|
3940
|
+
def try_state_logout?
|
|
3941
|
+
return true if connection_state in [:logout, *]
|
|
3942
|
+
return false unless acquired_lock = mon_try_enter
|
|
3943
|
+
state_logout!
|
|
3944
|
+
true
|
|
3945
|
+
ensure
|
|
3946
|
+
mon_exit if acquired_lock
|
|
3947
|
+
end
|
|
3948
|
+
|
|
3803
3949
|
def sasl_adapter
|
|
3804
3950
|
SASLAdapter.new(self, &method(:send_command_with_continuations))
|
|
3805
3951
|
end
|
|
@@ -3821,7 +3967,6 @@ require_relative "imap/errors"
|
|
|
3821
3967
|
require_relative "imap/config"
|
|
3822
3968
|
require_relative "imap/command_data"
|
|
3823
3969
|
require_relative "imap/data_encoding"
|
|
3824
|
-
require_relative "imap/data_lite"
|
|
3825
3970
|
require_relative "imap/flags"
|
|
3826
3971
|
require_relative "imap/response_data"
|
|
3827
3972
|
require_relative "imap/response_parser"
|