Skip to content

Commit c115a95

Browse files
committed
📚 Update capabilities documentation
* Document capabilities at the class-level, linking to #capability for details. * Add "Basic IMAP4rev1 capabilities" and "Using IMAP4rev1 extensions" sections to the #capability rdoc. * Add relevant capabilities to the rdoc for every command extension. * Update existing capability docs to the same consistent format. * Add TODO comment for unsupported LIST-EXTENDED
1 parent 864b797 commit c115a95

File tree

1 file changed

+163
-23
lines changed

1 file changed

+163
-23
lines changed

lib/net/imap.rb

Lines changed: 163 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ module Net
7070
# UIDs have to be reassigned. An \IMAP client thus cannot
7171
# rearrange message orders.
7272
#
73+
# === Server capabilities and protocol extensions
74+
#
75+
# Net::IMAP <em>does not modify its behavior</em> according to server
76+
# #capability. Users of the class must check for required capabilities before
77+
# issuing commands. Special care should be taken to follow all #capability
78+
# requirements for #starttls, #login, and #authenticate.
79+
#
80+
# See the #capability method for more information.
81+
#
7382
# == Examples of Usage
7483
#
7584
# === List sender and subject of all recent messages in the default mailbox
@@ -428,16 +437,55 @@ def disconnected?
428437
# Sends a CAPABILITY command, and returns an array of
429438
# capabilities that the server supports. Each capability
430439
# is a string.
431-
# See the {IANA IMAP capabilities registry}[https://www.iana.org/assignments/imap-capabilities/imap-capabilities.xhtml]
432-
# for a list of possible capabilities and their RFCs.
440+
#
441+
# See the {IANA IMAP4 capabilities
442+
# registry}[http://www.iana.org/assignments/imap4-capabilities] for a list
443+
# of all standard capabilities, and their reference RFCs.
433444
#
434445
# >>>
435-
# <em>*Note* that the Net::IMAP class does not modify its
446+
# <em>*Note* that Net::IMAP does not currently modify its
436447
# behaviour according to the capabilities of the server;
437448
# it is up to the user of the class to ensure that
438449
# a certain capability is supported by a server before
439450
# using it.</em>
440451
#
452+
# Capability requirements—other than +IMAP4rev1+—are listed in the
453+
# documentation for each command method.
454+
#
455+
# ===== Basic IMAP4rev1 capabilities
456+
#
457+
# All IMAP4rev1 servers must include +IMAP4rev1+ in their capabilities list.
458+
# All IMAP4rev1 servers must _implement_ the +STARTTLS+,
459+
# <tt>AUTH=PLAIN</tt>, and +LOGINDISABLED+ capabilities, and clients must
460+
# respect their presence or absence. See the capabilites requirements on
461+
# #starttls, #login, and #authenticate.
462+
#
463+
# ===== Using IMAP4rev1 extensions
464+
#
465+
# IMAP4rev1 servers must not activate incompatible behavior until an
466+
# explicit client action invokes a capability, e.g. sending a command or
467+
# command argument specific to that capability. Extensions with backward
468+
# compatible behavior, such as response codes or mailbox attributes, may
469+
# be sent at any time.
470+
#
471+
# Invoking capabilities which are unknown to Net::IMAP may cause unexpected
472+
# behavior and errors, for example ResponseParseError is raised when unknown
473+
# response syntax is received. Invoking commands or command parameters that
474+
# are unsupported by the server may raise NoResponseError, BadResponseError,
475+
# or cause other unexpected behavior.
476+
#
477+
# ===== Caching +CAPABILITY+ responses
478+
#
479+
# Servers may send their capability list, unsolicited, using the
480+
# +CAPABILITY+ response code or an untagged +CAPABILITY+ response. These
481+
# responses can be retrieved and cached using #responses or
482+
# #add_response_handler.
483+
#
484+
# But cached capabilities _must_ be discarded after #starttls, #login, or
485+
# #authenticate. The OK TaggedResponse to #login and #authenticate may
486+
# include +CAPABILITY+ response code data, but the TaggedResponse for
487+
# #starttls is sent clear-text and cannot be trusted.
488+
#
441489
def capability
442490
synchronize do
443491
send_command("CAPABILITY")
@@ -462,6 +510,11 @@ def capability
462510
# end
463511
#
464512
# See [ID[https://tools.ietf.org/html/rfc2971]] for field definitions.
513+
#
514+
# ===== Capabilities
515+
#
516+
# The server's capabilities must include +ID+
517+
# [RFC2971[https://tools.ietf.org/html/rfc2971]]
465518
def id(client_id=nil)
466519
synchronize do
467520
send_command("ID", ClientID.new(client_id))
@@ -481,6 +534,20 @@ def logout
481534
end
482535

483536
# Sends a STARTTLS command to start TLS session.
537+
# Sends a STARTTLS command to start a TLS session.
538+
#
539+
# ===== Capability
540+
#
541+
# The server's capabilities must include +STARTTLS+.
542+
#
543+
# Server capabilities may change after #starttls, #login, and #authenticate.
544+
# Cached capabilities _must_ be invalidated after this method completes.
545+
#
546+
# The TaggedResponse to #starttls is sent clear-text, so the server <em>must
547+
# *not*</em> send capabilities in the #starttls response and clients <em>must
548+
# not</em> use them if they are sent. Servers will generally send an
549+
# unsolicited untagged response immeditely _after_ #starttls completes.
550+
#
484551
def starttls(options = {}, verify = true)
485552
send_command("STARTTLS") do |resp|
486553
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
@@ -529,6 +596,20 @@ def starttls(options = {}, verify = true)
529596
#
530597
# See Net::IMAP::Authenticators for more information on plugging in your
531598
# own authenticator.
599+
#
600+
# ==== Capabilities
601+
#
602+
# Clients MUST NOT attempt to #authenticate or #login when +LOGINDISABLED+
603+
# is listed with the capabilities.
604+
#
605+
# Clients MUST NOT attempt to authenticate with a mechanism unless
606+
# <tt>"AUTH=#{mechanism}"</tt> for that mechanism is a server capability.
607+
#
608+
# Server capabilities may change after #starttls, #login, and #authenticate.
609+
# Cached capabilities _must_ be invalidated after this method completes.
610+
# The TaggedResponse to #authenticate may include updated capabilities in
611+
# its ResponseCode.
612+
#
532613
def authenticate(auth_type, *args)
533614
authenticator = self.class.authenticator(auth_type, *args)
534615
send_command("AUTHENTICATE", auth_type) do |resp|
@@ -547,6 +628,16 @@ def authenticate(auth_type, *args)
547628
# of "LOGIN", #login does *not* use the login authenticator.
548629
#
549630
# A Net::IMAP::NoResponseError is raised if authentication fails.
631+
#
632+
# ==== Capabilities
633+
# Clients MUST NOT attempt to #authenticate or #login when +LOGINDISABLED+
634+
# is listed with the capabilities.
635+
#
636+
# Server capabilities may change after #starttls, #login, and #authenticate.
637+
# Cached capabilities _must_ be invalidated after this method completes.
638+
# The TaggedResponse to #login may include updated capabilities in its
639+
# ResponseCode.
640+
#
550641
def login(user, password)
551642
send_command("LOGIN", user, password)
552643
end
@@ -661,6 +752,10 @@ def unsubscribe(mailbox)
661752
# #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
662753
# #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
663754
# #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
755+
#
756+
#--
757+
# TODO: support LIST-EXTENDED extension [RFC5258]. Needed for IMAP4rev2.
758+
#++
664759
def list(refname, mailbox)
665760
synchronize do
666761
send_command("LIST", refname, mailbox)
@@ -717,7 +812,10 @@ def list(refname, mailbox)
717812
# end
718813
# end
719814
#
720-
# The NAMESPACE extension is described in [NAMESPACE[https://tools.ietf.org/html/rfc2342]]
815+
# ===== Capabilities
816+
#
817+
# The server's capabilities must include +NAMESPACE+
818+
# [RFC2342[https://tools.ietf.org/html/rfc2342]]
721819
def namespace
722820
synchronize do
723821
send_command("NAMESPACE")
@@ -750,6 +848,16 @@ def namespace
750848
# #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
751849
# #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
752850
# #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
851+
#
852+
# ===== Capabilities
853+
#
854+
# The server's capabilities must include +XLIST+,
855+
# a deprecated Gmail extension (replaced by +SPECIAL-USE+).
856+
#--
857+
# TODO: Net::IMAP doesn't yet have full SPECIAL-USE support. Supporting
858+
# servers MAY return SPECIAL-USE attributes, but are not *required* to
859+
# unless the SPECIAL-USE return option is supplied.
860+
#++
753861
def xlist(refname, mailbox)
754862
synchronize do
755863
send_command("XLIST", refname, mailbox)
@@ -762,7 +870,10 @@ def xlist(refname, mailbox)
762870
# If this mailbox exists, it returns an array containing objects of type
763871
# MailboxQuotaRoot and MailboxQuota.
764872
#
765-
# The QUOTA extension is described in [QUOTA[https://tools.ietf.org/html/rfc2087]]
873+
# ===== Capabilities
874+
#
875+
# The server's capabilities must include +QUOTA+
876+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
766877
def getquotaroot(mailbox)
767878
synchronize do
768879
send_command("GETQUOTAROOT", mailbox)
@@ -778,7 +889,10 @@ def getquotaroot(mailbox)
778889
# MailboxQuota object is returned. This
779890
# command is generally only available to server admin.
780891
#
781-
# The QUOTA extension is described in [QUOTA[https://tools.ietf.org/html/rfc2087]]
892+
# ===== Capabilities
893+
#
894+
# The server's capabilities must include +QUOTA+
895+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
782896
def getquota(mailbox)
783897
synchronize do
784898
send_command("GETQUOTA", mailbox)
@@ -791,7 +905,10 @@ def getquota(mailbox)
791905
# mailbox. Typically one needs to be logged in as a server admin
792906
# for this to work.
793907
#
794-
# The QUOTA extension is described in [QUOTA[https://tools.ietf.org/html/rfc2087]]
908+
# ===== Capabilities
909+
#
910+
# The server's capabilities must include +QUOTA+
911+
# [RFC2087[https://tools.ietf.org/html/rfc2087]].
795912
def setquota(mailbox, quota)
796913
if quota.nil?
797914
data = '()'
@@ -805,7 +922,10 @@ def setquota(mailbox, quota)
805922
# +rights+ that user is to have on that mailbox. If +rights+ is nil,
806923
# then that user will be stripped of any rights to that mailbox.
807924
#
808-
# The ACL extension is described in [ACL[https://tools.ietf.org/html/rfc4314]]
925+
# ===== Capabilities
926+
#
927+
# The server's capabilities must include +ACL+
928+
# [RFC4314[https://tools.ietf.org/html/rfc4314]].
809929
def setacl(mailbox, user, rights)
810930
if rights.nil?
811931
send_command("SETACL", mailbox, user, "")
@@ -818,7 +938,10 @@ def setacl(mailbox, user, rights)
818938
# If this mailbox exists, an array containing objects of
819939
# MailboxACLItem will be returned.
820940
#
821-
# The ACL extension is described in [ACL[https://tools.ietf.org/html/rfc4314]]
941+
# ===== Capabilities
942+
#
943+
# The server's capabilities must include +ACL+
944+
# [RFC4314[https://tools.ietf.org/html/rfc4314]].
822945
def getacl(mailbox)
823946
synchronize do
824947
send_command("GETACL", mailbox)
@@ -959,10 +1082,10 @@ def expunge
9591082
# #responses and this method returns them as an array of
9601083
# <em>sequence number</em> integers.
9611084
#
962-
# ===== Capability requirement
1085+
# ===== Capabilities
9631086
#
964-
# +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] must be
965-
# supported by the server.
1087+
# The server's capabilities must include +UIDPLUS+
1088+
# [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]].
9661089
def uid_expunge(uid_set)
9671090
synchronize do
9681091
send_command("UID EXPUNGE", MessageSet.new(uid_set))
@@ -1121,10 +1244,10 @@ def uid_copy(set, mailbox)
11211244
# a number, an array of numbers, or a Range object. The number is
11221245
# a message sequence number.
11231246
#
1124-
# ===== Capabilities requirements
1247+
# ===== Capabilities
11251248
#
1126-
# +MOVE+ [RFC6851[https://tools.ietf.org/html/rfc6851]] must be supported by
1127-
# the server.
1249+
# The server's capabilities must include +MOVE+
1250+
# [RFC6851[https://tools.ietf.org/html/rfc6851]].
11281251
#
11291252
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
11301253
# also supported, the server's response should include a +COPYUID+ response
@@ -1137,11 +1260,11 @@ def move(set, mailbox)
11371260

11381261
# Similar to #move, but +set+ contains unique identifiers.
11391262
#
1140-
# ===== Capabilities requirements
1263+
# ===== Capabilities
11411264
#
1142-
# Same as #move: +MOVE+ [RFC6851[https://tools.ietf.org/html/rfc6851]] must
1143-
# be supported by the server. +UIDPLUS+ also affects #uid_move the same way
1144-
# it affects #move.
1265+
# Same as #move: The server's capabilities must include +MOVE+
1266+
# [RFC6851[https://tools.ietf.org/html/rfc6851]]. +UIDPLUS+ also affects
1267+
# #uid_move the same way it affects #move.
11451268
def uid_move(set, mailbox)
11461269
copy_internal("UID MOVE", set, mailbox)
11471270
end
@@ -1154,14 +1277,20 @@ def uid_move(set, mailbox)
11541277
# p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
11551278
# #=> [6, 7, 8, 1]
11561279
#
1157-
# The SORT extension is described in [SORT[https://tools.ietf.org/html/rfc5256]].
1280+
# ===== Capabilities
1281+
#
1282+
# The server's capabilities must include +SORT+
1283+
# [RFC5256[https://tools.ietf.org/html/rfc5256]].
11581284
def sort(sort_keys, search_keys, charset)
11591285
return sort_internal("SORT", sort_keys, search_keys, charset)
11601286
end
11611287

11621288
# Similar to #sort, but returns an array of unique identifiers.
11631289
#
1164-
# The SORT extension is described in [SORT[https://tools.ietf.org/html/rfc5256]].
1290+
# ===== Capabilities
1291+
#
1292+
# The server's capabilities must include +SORT+
1293+
# [RFC5256[https://tools.ietf.org/html/rfc5256]].
11651294
def uid_sort(sort_keys, search_keys, charset)
11661295
return sort_internal("UID SORT", sort_keys, search_keys, charset)
11671296
end
@@ -1199,15 +1328,21 @@ def remove_response_handler(handler)
11991328
# Unlike #search, +charset+ is a required argument. US-ASCII
12001329
# and UTF-8 are sample values.
12011330
#
1202-
# The THREAD extension is described in [THREAD[https://tools.ietf.org/html/rfc5256]].
1331+
# ===== Capabilities
1332+
#
1333+
# The server's capabilities must include +THREAD+
1334+
# [RFC5256[https://tools.ietf.org/html/rfc5256]].
12031335
def thread(algorithm, search_keys, charset)
12041336
return thread_internal("THREAD", algorithm, search_keys, charset)
12051337
end
12061338

12071339
# Similar to #thread, but returns unique identifiers instead of
12081340
# message sequence numbers.
12091341
#
1210-
# The THREAD extension is described in [THREAD[https://tools.ietf.org/html/rfc5256]].
1342+
# ===== Capabilities
1343+
#
1344+
# The server's capabilities must include +THREAD+
1345+
# [RFC5256[https://tools.ietf.org/html/rfc5256]].
12111346
def uid_thread(algorithm, search_keys, charset)
12121347
return thread_internal("UID THREAD", algorithm, search_keys, charset)
12131348
end
@@ -1226,6 +1361,11 @@ def uid_thread(algorithm, search_keys, charset)
12261361
# ...
12271362
# end
12281363
# end
1364+
#
1365+
# ===== Capabilities
1366+
#
1367+
# The server's capabilities must include +IDLE+
1368+
# [RFC2177[https://tools.ietf.org/html/rfc2177]].
12291369
def idle(timeout = nil, &response_handler)
12301370
raise LocalJumpError, "no block given" unless response_handler
12311371

0 commit comments

Comments
 (0)