net-imap 0.5.9 → 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 +7 -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 -14
- 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 +715 -326
- data/lib/net/imap/uidplus_data.rb +2 -63
- data/lib/net/imap/vanished_data.rb +10 -1
- data/lib/net/imap.rb +201 -86
- data/net-imap.gemspec +1 -1
- data/rakelib/rdoc.rake +1 -18
- 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 = {
|
|
@@ -805,10 +830,6 @@ module Net
|
|
|
805
830
|
autoload :StringPrep, "#{dir}/stringprep"
|
|
806
831
|
|
|
807
832
|
include MonitorMixin
|
|
808
|
-
if defined?(OpenSSL::SSL)
|
|
809
|
-
include OpenSSL
|
|
810
|
-
include SSL
|
|
811
|
-
end
|
|
812
833
|
|
|
813
834
|
# :call-seq:
|
|
814
835
|
# Net::IMAP::SequenceSet(set = nil) -> SequenceSet
|
|
@@ -1124,6 +1145,31 @@ module Net
|
|
|
1124
1145
|
start_imap_connection
|
|
1125
1146
|
end
|
|
1126
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
|
+
|
|
1127
1173
|
# Returns true after the TLS negotiation has completed and the remote
|
|
1128
1174
|
# hostname has been verified. Returns false when TLS has been established
|
|
1129
1175
|
# but peer verification was disabled.
|
|
@@ -1394,9 +1440,11 @@ module Net
|
|
|
1394
1440
|
#
|
|
1395
1441
|
def starttls(**options)
|
|
1396
1442
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
|
1443
|
+
handled = false
|
|
1397
1444
|
error = nil
|
|
1398
1445
|
ok = send_command("STARTTLS") do |resp|
|
|
1399
1446
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
|
1447
|
+
handled = true
|
|
1400
1448
|
clear_cached_capabilities
|
|
1401
1449
|
clear_responses
|
|
1402
1450
|
start_tls_session
|
|
@@ -1408,6 +1456,13 @@ module Net
|
|
|
1408
1456
|
disconnect
|
|
1409
1457
|
raise error
|
|
1410
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
|
|
1411
1466
|
ok
|
|
1412
1467
|
end
|
|
1413
1468
|
|
|
@@ -1522,6 +1577,7 @@ module Net
|
|
|
1522
1577
|
# completes. If the TaggedResponse to #authenticate includes updated
|
|
1523
1578
|
# capabilities, they will be cached.
|
|
1524
1579
|
def authenticate(*args, sasl_ir: config.sasl_ir, **props, &callback)
|
|
1580
|
+
sasl_ir = may_depend_on_capabilities_cached?(sasl_ir)
|
|
1525
1581
|
sasl_adapter.authenticate(*args, sasl_ir: sasl_ir, **props, &callback)
|
|
1526
1582
|
.tap do state_authenticated! _1 end
|
|
1527
1583
|
end
|
|
@@ -1573,7 +1629,7 @@ module Net
|
|
|
1573
1629
|
# When the +condstore+ keyword argument is true, the server is told to
|
|
1574
1630
|
# enable the extension. If +mailbox+ supports persistence of mod-sequences,
|
|
1575
1631
|
# the +HIGHESTMODSEQ+ ResponseCode will be sent as an untagged response to
|
|
1576
|
-
# #select and all
|
|
1632
|
+
# #select and all +FETCH+ responses will include FetchData#modseq.
|
|
1577
1633
|
# Otherwise, the +NOMODSEQ+ ResponseCode will be sent.
|
|
1578
1634
|
#
|
|
1579
1635
|
# A Net::IMAP::NoResponseError is raised if the mailbox does not
|
|
@@ -1828,12 +1884,18 @@ module Net
|
|
|
1828
1884
|
# to both admin and user. If this mailbox exists, it returns an array
|
|
1829
1885
|
# containing objects of type MailboxQuotaRoot and MailboxQuota.
|
|
1830
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
|
+
#
|
|
1831
1891
|
# Related: #getquota, #setquota, MailboxQuotaRoot, MailboxQuota
|
|
1832
1892
|
#
|
|
1833
1893
|
# ==== Capabilities
|
|
1834
1894
|
#
|
|
1835
|
-
#
|
|
1836
|
-
#
|
|
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.
|
|
1837
1899
|
def getquotaroot(mailbox)
|
|
1838
1900
|
synchronize do
|
|
1839
1901
|
send_command("GETQUOTAROOT", mailbox)
|
|
@@ -1845,41 +1907,59 @@ module Net
|
|
|
1845
1907
|
end
|
|
1846
1908
|
|
|
1847
1909
|
# Sends a {GETQUOTA command [RFC2087 §4.2]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.2]
|
|
1848
|
-
#
|
|
1849
|
-
# containing a MailboxQuota object is returned.
|
|
1850
|
-
#
|
|
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.
|
|
1851
1919
|
#
|
|
1852
1920
|
# Related: #getquotaroot, #setquota, MailboxQuota
|
|
1853
1921
|
#
|
|
1854
1922
|
# ==== Capabilities
|
|
1855
1923
|
#
|
|
1856
|
-
#
|
|
1857
|
-
#
|
|
1858
|
-
|
|
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)
|
|
1859
1929
|
synchronize do
|
|
1860
|
-
send_command("GETQUOTA",
|
|
1930
|
+
send_command("GETQUOTA", quota_root)
|
|
1861
1931
|
clear_responses("QUOTA")
|
|
1862
1932
|
end
|
|
1863
1933
|
end
|
|
1864
1934
|
|
|
1865
1935
|
# Sends a {SETQUOTA command [RFC2087 §4.1]}[https://www.rfc-editor.org/rfc/rfc2087#section-4.1]
|
|
1866
|
-
# along with the specified +
|
|
1867
|
-
# +
|
|
1868
|
-
#
|
|
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.
|
|
1869
1947
|
#
|
|
1870
1948
|
# Related: #getquota, #getquotaroot
|
|
1871
1949
|
#
|
|
1872
1950
|
# ==== Capabilities
|
|
1873
1951
|
#
|
|
1874
|
-
#
|
|
1875
|
-
#
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
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 = []
|
|
1879
1959
|
else
|
|
1880
|
-
|
|
1960
|
+
list = ["STORAGE", NumValidator.coerce_number64(storage_limit)]
|
|
1881
1961
|
end
|
|
1882
|
-
send_command("SETQUOTA",
|
|
1962
|
+
send_command("SETQUOTA", quota_root, list)
|
|
1883
1963
|
end
|
|
1884
1964
|
|
|
1885
1965
|
# Sends a {SETACL command [RFC4314 §3.1]}[https://www.rfc-editor.org/rfc/rfc4314#section-3.1]
|
|
@@ -1986,7 +2066,10 @@ module Net
|
|
|
1986
2066
|
# <tt>STATUS=SIZE</tt>
|
|
1987
2067
|
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
|
1988
2068
|
#
|
|
1989
|
-
# +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].
|
|
1990
2073
|
#
|
|
1991
2074
|
# +HIGHESTMODSEQ+ requires the server's capabilities to include +CONDSTORE+
|
|
1992
2075
|
# {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
|
|
@@ -2022,9 +2105,14 @@ module Net
|
|
|
2022
2105
|
#
|
|
2023
2106
|
# ==== Capabilities
|
|
2024
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
|
+
#
|
|
2025
2113
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
|
2026
2114
|
# supported and the destination supports persistent UIDs, the server's
|
|
2027
|
-
# response should include an +APPENDUID+ response code with
|
|
2115
|
+
# response should include an +APPENDUID+ response code with AppendUIDData.
|
|
2028
2116
|
# This will report the UIDVALIDITY of the destination mailbox and the
|
|
2029
2117
|
# assigned UID of the appended message.
|
|
2030
2118
|
#
|
|
@@ -2032,12 +2120,11 @@ module Net
|
|
|
2032
2120
|
# TODO: add MULTIAPPEND support
|
|
2033
2121
|
#++
|
|
2034
2122
|
def append(mailbox, message, flags = nil, date_time = nil)
|
|
2123
|
+
message = StringFormatter.literal_or_literal8(message, name: "message")
|
|
2035
2124
|
args = []
|
|
2036
|
-
if flags
|
|
2037
|
-
args.push(flags)
|
|
2038
|
-
end
|
|
2125
|
+
args.push(flags) if flags
|
|
2039
2126
|
args.push(date_time) if date_time
|
|
2040
|
-
args.push(
|
|
2127
|
+
args.push(message)
|
|
2041
2128
|
send_command("APPEND", mailbox, *args)
|
|
2042
2129
|
end
|
|
2043
2130
|
|
|
@@ -2110,8 +2197,8 @@ module Net
|
|
|
2110
2197
|
end
|
|
2111
2198
|
|
|
2112
2199
|
# call-seq:
|
|
2113
|
-
# uid_expunge
|
|
2114
|
-
# uid_expunge
|
|
2200
|
+
# uid_expunge(uid_set) -> array of message sequence numbers
|
|
2201
|
+
# uid_expunge(uid_set) -> VanishedData of UIDs
|
|
2115
2202
|
#
|
|
2116
2203
|
# Sends a {UID EXPUNGE command [RFC4315 §2.1]}[https://www.rfc-editor.org/rfc/rfc4315#section-2.1]
|
|
2117
2204
|
# {[IMAP4rev2 §6.4.9]}[https://www.rfc-editor.org/rfc/rfc9051#section-6.4.9]
|
|
@@ -2267,11 +2354,11 @@ module Net
|
|
|
2267
2354
|
# Encoded as an \IMAP date (see ::encode_date).
|
|
2268
2355
|
#
|
|
2269
2356
|
# [When +criteria+ is a String]
|
|
2270
|
-
# +criteria+ will be sent
|
|
2271
|
-
#
|
|
2357
|
+
# +criteria+ will be sent to the server <em>with minimal validation and no
|
|
2358
|
+
# encoding or formatting</em>.
|
|
2272
2359
|
#
|
|
2273
|
-
# <em>*WARNING:*
|
|
2274
|
-
#
|
|
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>
|
|
2275
2362
|
#
|
|
2276
2363
|
# ==== Supported return options
|
|
2277
2364
|
#
|
|
@@ -2592,6 +2679,13 @@ module Net
|
|
|
2592
2679
|
#
|
|
2593
2680
|
# +attr+ is a list of attributes to fetch; see FetchStruct documentation for
|
|
2594
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>
|
|
2595
2689
|
#
|
|
2596
2690
|
# +changedsince+ is an optional integer mod-sequence. It limits results to
|
|
2597
2691
|
# messages with a mod-sequence greater than +changedsince+.
|
|
@@ -2675,6 +2769,7 @@ module Net
|
|
|
2675
2769
|
# # fetch should return quickly and allocate little memory
|
|
2676
2770
|
# results.size # => 0..500
|
|
2677
2771
|
# break if results.empty?
|
|
2772
|
+
# results.sort_by!(&:uid) # server may return results out of order
|
|
2678
2773
|
# next_uid_to_fetch = results.last.uid + 1
|
|
2679
2774
|
# process results
|
|
2680
2775
|
# end
|
|
@@ -2780,7 +2875,7 @@ module Net
|
|
|
2780
2875
|
#
|
|
2781
2876
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
|
2782
2877
|
# supported, the server's response should include a +COPYUID+ response code
|
|
2783
|
-
# with
|
|
2878
|
+
# with CopyUIDData. This will report the UIDVALIDITY of the destination
|
|
2784
2879
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
|
2785
2880
|
# the moved messages.
|
|
2786
2881
|
#
|
|
@@ -2821,7 +2916,7 @@ module Net
|
|
|
2821
2916
|
#
|
|
2822
2917
|
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
|
|
2823
2918
|
# supported, the server's response should include a +COPYUID+ response code
|
|
2824
|
-
# with
|
|
2919
|
+
# with CopyUIDData. This will report the UIDVALIDITY of the destination
|
|
2825
2920
|
# mailbox, the UID set of the source messages, and the assigned UID set of
|
|
2826
2921
|
# the moved messages.
|
|
2827
2922
|
#
|
|
@@ -2961,6 +3056,18 @@ module Net
|
|
|
2961
3056
|
# command parameters defined by the extension will implicitly enable it.
|
|
2962
3057
|
# See {[RFC7162 §3.1]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1].
|
|
2963
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
|
+
#
|
|
2964
3071
|
# [+:utf8+ --- an alias for <tt>"UTF8=ACCEPT"</tt>]
|
|
2965
3072
|
#
|
|
2966
3073
|
# In a future release, <tt>enable(:utf8)</tt> will enable either
|
|
@@ -3067,6 +3174,7 @@ module Net
|
|
|
3067
3174
|
|
|
3068
3175
|
synchronize do
|
|
3069
3176
|
tag = Thread.current[:net_imap_tag] = generate_tag
|
|
3177
|
+
guard_against_tagged_response_skipping_handler!(tag, "IDLE")
|
|
3070
3178
|
put_string("#{tag} IDLE#{CRLF}")
|
|
3071
3179
|
|
|
3072
3180
|
begin
|
|
@@ -3220,7 +3328,7 @@ module Net
|
|
|
3220
3328
|
warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated)
|
|
3221
3329
|
when :frozen_dup
|
|
3222
3330
|
synchronize {
|
|
3223
|
-
responses = @responses.transform_values
|
|
3331
|
+
responses = @responses.transform_values { _1.dup.freeze }
|
|
3224
3332
|
responses.default_proc = nil
|
|
3225
3333
|
responses.default = [].freeze
|
|
3226
3334
|
return responses.freeze
|
|
@@ -3471,7 +3579,7 @@ module Net
|
|
|
3471
3579
|
raise BadResponseError, resp
|
|
3472
3580
|
else
|
|
3473
3581
|
disconnect
|
|
3474
|
-
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.
|
|
3582
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw_data.chomp]
|
|
3475
3583
|
end
|
|
3476
3584
|
end
|
|
3477
3585
|
|
|
@@ -3531,21 +3639,29 @@ module Net
|
|
|
3531
3639
|
put_string(" ")
|
|
3532
3640
|
send_data(i, tag)
|
|
3533
3641
|
end
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
end
|
|
3538
|
-
if block
|
|
3539
|
-
add_response_handler(&block)
|
|
3540
|
-
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
|
|
3541
3645
|
begin
|
|
3542
|
-
|
|
3646
|
+
put_string(CRLF)
|
|
3647
|
+
get_tagged_response(tag, cmd)
|
|
3543
3648
|
ensure
|
|
3544
|
-
if block
|
|
3545
|
-
remove_response_handler(block)
|
|
3546
|
-
end
|
|
3649
|
+
remove_response_handler(block) if block
|
|
3547
3650
|
end
|
|
3548
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
|
+
)
|
|
3549
3665
|
end
|
|
3550
3666
|
|
|
3551
3667
|
def generate_tag
|
|
@@ -3569,11 +3685,11 @@ module Net
|
|
|
3569
3685
|
end
|
|
3570
3686
|
|
|
3571
3687
|
def enforce_logindisabled?
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
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
|
|
3577
3693
|
end
|
|
3578
3694
|
|
|
3579
3695
|
def expunge_internal(...)
|
|
@@ -3672,6 +3788,9 @@ module Net
|
|
|
3672
3788
|
end
|
|
3673
3789
|
|
|
3674
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
|
|
3675
3794
|
set = SequenceSet[set]
|
|
3676
3795
|
if partial
|
|
3677
3796
|
mod ||= []
|
|
@@ -3696,7 +3815,7 @@ module Net
|
|
|
3696
3815
|
end
|
|
3697
3816
|
|
|
3698
3817
|
def store_internal(cmd, set, attr, flags, unchangedsince: nil)
|
|
3699
|
-
attr =
|
|
3818
|
+
attr = Atom.new(attr) if attr.instance_of?(String)
|
|
3700
3819
|
args = [SequenceSet.new(set)]
|
|
3701
3820
|
args << ["UNCHANGEDSINCE", Integer(unchangedsince)] if unchangedsince
|
|
3702
3821
|
args << attr << flags
|
|
@@ -3766,12 +3885,9 @@ module Net
|
|
|
3766
3885
|
def build_ssl_ctx(ssl)
|
|
3767
3886
|
if ssl
|
|
3768
3887
|
params = (Hash.try_convert(ssl) || {}).freeze
|
|
3769
|
-
context = SSLContext.new
|
|
3888
|
+
context = OpenSSL::SSL::SSLContext.new
|
|
3770
3889
|
context.set_params(params)
|
|
3771
|
-
|
|
3772
|
-
context.verify_callback = VerifyCallbackProc
|
|
3773
|
-
end
|
|
3774
|
-
context.freeze
|
|
3890
|
+
context.setup
|
|
3775
3891
|
[params, context]
|
|
3776
3892
|
else
|
|
3777
3893
|
false
|
|
@@ -3782,12 +3898,12 @@ module Net
|
|
|
3782
3898
|
raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
|
|
3783
3899
|
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
|
|
3784
3900
|
raise "cannot start TLS without SSLContext" unless ssl_ctx
|
|
3785
|
-
@sock = SSLSocket.new(@sock, ssl_ctx)
|
|
3901
|
+
@sock = OpenSSL::SSL::SSLSocket.new(@sock, ssl_ctx)
|
|
3786
3902
|
@reader = ResponseReader.new(self, @sock)
|
|
3787
3903
|
@sock.sync_close = true
|
|
3788
3904
|
@sock.hostname = @host if @sock.respond_to? :hostname=
|
|
3789
3905
|
ssl_socket_connect(@sock, open_timeout)
|
|
3790
|
-
if ssl_ctx.verify_mode != VERIFY_NONE
|
|
3906
|
+
if ssl_ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
|
3791
3907
|
@sock.post_connection_check(@host)
|
|
3792
3908
|
@tls_verified = true
|
|
3793
3909
|
end
|
|
@@ -3851,7 +3967,6 @@ require_relative "imap/errors"
|
|
|
3851
3967
|
require_relative "imap/config"
|
|
3852
3968
|
require_relative "imap/command_data"
|
|
3853
3969
|
require_relative "imap/data_encoding"
|
|
3854
|
-
require_relative "imap/data_lite"
|
|
3855
3970
|
require_relative "imap/flags"
|
|
3856
3971
|
require_relative "imap/response_data"
|
|
3857
3972
|
require_relative "imap/response_parser"
|
data/net-imap.gemspec
CHANGED
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
spec.summary = %q{Ruby client api for Internet Message Access Protocol}
|
|
17
17
|
spec.description = %q{Ruby client api for Internet Message Access Protocol}
|
|
18
18
|
spec.homepage = "https://github.com/ruby/net-imap"
|
|
19
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 3.
|
|
19
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
|
|
20
20
|
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
|
21
21
|
|
|
22
22
|
spec.metadata["homepage_uri"] = spec.homepage
|
data/rakelib/rdoc.rake
CHANGED
|
@@ -12,17 +12,6 @@ module RDoc::Generator
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# See https://github.com/ruby/rdoc/pull/936
|
|
16
|
-
module FixSectionComments
|
|
17
|
-
def markup(text)
|
|
18
|
-
@store ||= @parent&.store
|
|
19
|
-
super
|
|
20
|
-
end
|
|
21
|
-
def description; markup comment end
|
|
22
|
-
def comment; super || @comments&.first end
|
|
23
|
-
def parse(_comment_location = nil) super() end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
15
|
# render "[label] data" lists as tables. adapted from "hanna-nouveau" gem.
|
|
27
16
|
module LabelListTable
|
|
28
17
|
def list_item_start(list_item, list_type)
|
|
@@ -51,20 +40,14 @@ class RDoc::AnyMethod
|
|
|
51
40
|
prepend RDoc::Generator::NetIMAP::RemoveRedundantParens
|
|
52
41
|
end
|
|
53
42
|
|
|
54
|
-
class RDoc::Context::Section
|
|
55
|
-
prepend RDoc::Generator::NetIMAP::FixSectionComments
|
|
56
|
-
end
|
|
57
|
-
|
|
58
43
|
class RDoc::Markup::ToHtml
|
|
59
44
|
LIST_TYPE_TO_HTML[:NOTE] = ['<table class="rdoc-list note-list"><tbody>', '</tbody></table>']
|
|
60
45
|
prepend RDoc::Generator::NetIMAP::LabelListTable
|
|
61
46
|
end
|
|
62
47
|
|
|
63
48
|
RDoc::Task.new do |doc|
|
|
64
|
-
doc.main = "README.md"
|
|
65
49
|
doc.title = "net-imap #{Net::IMAP::VERSION}"
|
|
66
50
|
doc.rdoc_dir = "doc"
|
|
67
|
-
doc.rdoc_files = FileList.new %w[lib/**/*.rb *.rdoc *.md]
|
|
68
51
|
doc.options << "--template-stylesheets" << "docs/styles.css"
|
|
69
|
-
|
|
52
|
+
doc.generator = "darkfish" # TODO: fix issues with aliki
|
|
70
53
|
end
|