net-imap 0.4.2 → 0.4.5
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.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/docs/styles.css +0 -12
- data/lib/net/imap/data_encoding.rb +14 -2
- data/lib/net/imap/errors.rb +20 -0
- data/lib/net/imap/fetch_data.rb +518 -0
- data/lib/net/imap/response_data.rb +70 -211
- data/lib/net/imap/response_parser/parser_utils.rb +15 -5
- data/lib/net/imap/response_parser.rb +922 -428
- data/lib/net/imap/sasl/authenticators.rb +2 -2
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +1 -1
- data/lib/net/imap/sequence_set.rb +67 -0
- data/lib/net/imap.rb +98 -41
- data/net-imap.gemspec +3 -2
- data/rakelib/benchmarks.rake +91 -0
- metadata +5 -6
- data/benchmarks/generate_parser_benchmarks +0 -52
- data/benchmarks/parser.yml +0 -578
- data/benchmarks/stringprep.yml +0 -65
- data/benchmarks/table-regexps.yml +0 -39
@@ -58,8 +58,8 @@ module Net::IMAP::SASL
|
|
58
58
|
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
59
59
|
# implemented by +authenticator_class+ (for instance, <tt>"PLAIN"</tt>).
|
60
60
|
#
|
61
|
-
# If +mechanism+ refers to an existing authenticator,
|
62
|
-
#
|
61
|
+
# If +mechanism+ refers to an existing authenticator,
|
62
|
+
# the old authenticator will be replaced.
|
63
63
|
#
|
64
64
|
# When only a single argument is given, the authenticator class will be
|
65
65
|
# lazily loaded from <tt>Net::IMAP::SASL::#{name}Authenticator</tt> (case is
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# Although this mechanism was never standardized and has been obsoleted by
|
15
15
|
# "+OAUTHBEARER+", it is still very widely supported.
|
16
16
|
#
|
17
|
-
# See Net::IMAP::SASL::
|
17
|
+
# See Net::IMAP::SASL::OAuthBearerAuthenticator.
|
18
18
|
class Net::IMAP::SASL::XOAuth2Authenticator
|
19
19
|
|
20
20
|
# It is unclear from {Google's original XOAUTH2
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP
|
5
|
+
|
6
|
+
##
|
7
|
+
# An IMAP {sequence
|
8
|
+
# set}[https://www.rfc-editor.org/rfc/rfc9051.html#section-4.1.1],
|
9
|
+
# is a set of message sequence numbers or unique identifier numbers
|
10
|
+
# ("UIDs"). It contains numbers and ranges of numbers. The numbers are all
|
11
|
+
# non-zero unsigned 32-bit integers and one special value, <tt>*</tt>, that
|
12
|
+
# represents the largest value in the mailbox.
|
13
|
+
#
|
14
|
+
# *NOTE:* This SequenceSet class is currently a placeholder for unhandled
|
15
|
+
# extension data. All it does now is validate. It will be expanded to a
|
16
|
+
# full API in a future release.
|
17
|
+
class SequenceSet
|
18
|
+
|
19
|
+
def self.[](str) new(str).freeze end
|
20
|
+
|
21
|
+
def initialize(input)
|
22
|
+
@atom = -String.try_convert(input)
|
23
|
+
validate
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the IMAP string representation. In the IMAP grammar,
|
27
|
+
# +sequence-set+ is a subset of +atom+ which is a subset of +astring+.
|
28
|
+
attr_accessor :atom
|
29
|
+
|
30
|
+
# Returns #atom. In the IMAP grammar, +atom+ is a subset of +astring+.
|
31
|
+
alias astring atom
|
32
|
+
|
33
|
+
# Returns the value of #atom
|
34
|
+
alias to_s atom
|
35
|
+
|
36
|
+
# Hash equality requires the same encoded #atom representation.
|
37
|
+
#
|
38
|
+
# Net::IMAP::SequenceSet["1:3"] .eql? Net::IMAP::SequenceSet["1:3"] # => true
|
39
|
+
# Net::IMAP::SequenceSet["1,2,3"].eql? Net::IMAP::SequenceSet["1:3"] # => false
|
40
|
+
# Net::IMAP::SequenceSet["1,3"] .eql? Net::IMAP::SequenceSet["3,1"] # => false
|
41
|
+
# Net::IMAP::SequenceSet["9,1:*"].eql? Net::IMAP::SequenceSet["1:*"] # => false
|
42
|
+
#
|
43
|
+
def eql?(other) self.class == other.class && atom == other.atom end
|
44
|
+
alias == eql?
|
45
|
+
|
46
|
+
# See #eql?
|
47
|
+
def hash; [self.class. atom].hash end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
(frozen? ? "%s[%p]" : "#<%s %p>") % [self.class, to_s]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Unstable API, for internal use only (Net::IMAP#validate_data)
|
54
|
+
def validate # :nodoc:
|
55
|
+
ResponseParser::Patterns::SEQUENCE_SET_STR.match?(@atom) or
|
56
|
+
raise ArgumentError, "invalid sequence-set: %p" % [input]
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Unstable API, for internal use only (Net::IMAP#send_data)
|
61
|
+
def send_data(imap, tag) # :nodoc:
|
62
|
+
imap.__send__(:put_string, atom)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/net/imap.rb
CHANGED
@@ -404,18 +404,18 @@ module Net
|
|
404
404
|
#
|
405
405
|
# Although IMAP4rev2[https://tools.ietf.org/html/rfc9051] is not supported
|
406
406
|
# yet, Net::IMAP supports several extensions that have been folded into it:
|
407
|
-
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+,
|
407
|
+
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+, and
|
408
|
+
# the fetch side of +BINARY+.
|
408
409
|
# Commands for these extensions are listed with the {Core IMAP
|
409
410
|
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
|
410
411
|
#
|
411
412
|
# >>>
|
412
413
|
# <em>The following are folded into +IMAP4rev2+ but are currently
|
413
414
|
# unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
|
414
|
-
# extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+,
|
415
|
-
# +
|
416
|
-
#
|
417
|
-
#
|
418
|
-
# <tt>STATUS=DELETED</tt>.</em>
|
415
|
+
# extensions, +ESEARCH+, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
|
416
|
+
# +LITERAL-+, and +SPECIAL-USE+. The following extensions are implicitly
|
417
|
+
# supported, but will be updated with more direct support: RFC5530 response
|
418
|
+
# codes, <tt>STATUS=SIZE</tt>, and <tt>STATUS=DELETED</tt>.</em>
|
419
419
|
#
|
420
420
|
# ==== RFC2087: +QUOTA+
|
421
421
|
# - #getquota: returns the resource usage and limits for a quota root
|
@@ -437,6 +437,15 @@ module Net
|
|
437
437
|
# ==== RFC2971: +ID+
|
438
438
|
# - #id: exchanges client and server implementation information.
|
439
439
|
#
|
440
|
+
# ==== RFC3516: +BINARY+
|
441
|
+
# The fetch side of +BINARY+ has been folded into
|
442
|
+
# IMAP4rev2[https://tools.ietf.org/html/rfc9051].
|
443
|
+
# - Updates #fetch and #uid_fetch with the +BINARY+, +BINARY.PEEK+, and
|
444
|
+
# +BINARY.SIZE+ items. See FetchData#binary and FetchData#binary_size.
|
445
|
+
#
|
446
|
+
# >>>
|
447
|
+
# *NOTE:* The binary extension the #append command is _not_ supported yet.
|
448
|
+
#
|
440
449
|
# ==== RFC3691: +UNSELECT+
|
441
450
|
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
442
451
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
@@ -474,9 +483,17 @@ module Net
|
|
474
483
|
# which arranges the results into ordered groups or threads according to a
|
475
484
|
# chosen algorithm.
|
476
485
|
#
|
477
|
-
# ==== +
|
486
|
+
# ==== +X-GM-EXT-1+
|
487
|
+
# +X-GM-EXT-1+ is a non-standard Gmail extension. See {Google's
|
488
|
+
# documentation}[https://developers.google.com/gmail/imap/imap-extensions].
|
489
|
+
# - Updates #fetch and #uid_fetch with support for +X-GM-MSGID+ (unique
|
490
|
+
# message ID), +X-GM-THRID+ (thread ID), and +X-GM-LABELS+ (Gmail labels).
|
491
|
+
# - Updates #search with the +X-GM-RAW+ search attribute.
|
478
492
|
# - #xlist: replaced by +SPECIAL-USE+ attributes in #list responses.
|
479
493
|
#
|
494
|
+
# *NOTE:* The +OBJECTID+ extension should replace +X-GM-MSGID+ and
|
495
|
+
# +X-GM-THRID+, but Gmail does not support it (as of 2023-11-10).
|
496
|
+
#
|
480
497
|
# ==== RFC6851: +MOVE+
|
481
498
|
# Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051] and also included
|
482
499
|
# above with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands].
|
@@ -487,6 +504,13 @@ module Net
|
|
487
504
|
#
|
488
505
|
# - See #enable for information about support for UTF-8 string encoding.
|
489
506
|
#
|
507
|
+
# ==== RFC8474: +OBJECTID+
|
508
|
+
# - Adds +MAILBOXID+ ResponseCode to #create tagged response.
|
509
|
+
# - Adds +MAILBOXID+ ResponseCode to #select and #examine untagged response.
|
510
|
+
# - Updates #fetch and #uid_fetch with the +EMAILID+ and +THREADID+ items.
|
511
|
+
# See FetchData#emailid and FetchData#emailid.
|
512
|
+
# - Updates #status with support for the +MAILBOXID+ status attribute.
|
513
|
+
#
|
490
514
|
# == References
|
491
515
|
#
|
492
516
|
# [{IMAP4rev1}[https://www.rfc-editor.org/rfc/rfc3501.html]]::
|
@@ -612,6 +636,10 @@ module Net
|
|
612
636
|
# [ID[https://tools.ietf.org/html/rfc2971]]::
|
613
637
|
# Showalter, T., "IMAP4 ID extension", RFC 2971, DOI 10.17487/RFC2971,
|
614
638
|
# October 2000, <https://www.rfc-editor.org/info/rfc2971>.
|
639
|
+
# [BINARY[https://tools.ietf.org/html/rfc3516]]::
|
640
|
+
# Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
|
641
|
+
# DOI 10.17487/RFC3516, April 2003,
|
642
|
+
# <https://www.rfc-editor.org/info/rfc3516>.
|
615
643
|
# [ACL[https://tools.ietf.org/html/rfc4314]]::
|
616
644
|
# Melnikov, A., "IMAP4 Access Control List (ACL) Extension", RFC 4314,
|
617
645
|
# DOI 10.17487/RFC4314, December 2005,
|
@@ -662,7 +690,7 @@ module Net
|
|
662
690
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
663
691
|
#
|
664
692
|
class IMAP < Protocol
|
665
|
-
VERSION = "0.4.
|
693
|
+
VERSION = "0.4.5"
|
666
694
|
|
667
695
|
# Aliases for supported capabilities, to be used with the #enable command.
|
668
696
|
ENABLE_ALIASES = {
|
@@ -1045,7 +1073,7 @@ module Net
|
|
1045
1073
|
# ===== Capabilities
|
1046
1074
|
#
|
1047
1075
|
# The server's capabilities must include +ID+
|
1048
|
-
# [RFC2971[https://tools.ietf.org/html/rfc2971]]
|
1076
|
+
# [RFC2971[https://tools.ietf.org/html/rfc2971]].
|
1049
1077
|
def id(client_id=nil)
|
1050
1078
|
synchronize do
|
1051
1079
|
send_command("ID", ClientID.new(client_id))
|
@@ -1058,7 +1086,7 @@ module Net
|
|
1058
1086
|
#
|
1059
1087
|
# This allows the server to send unsolicited untagged EXPUNGE #responses,
|
1060
1088
|
# but does not execute any client request. \IMAP servers are permitted to
|
1061
|
-
# send unsolicited untagged responses at any time, except for
|
1089
|
+
# send unsolicited untagged responses at any time, except for +EXPUNGE+:
|
1062
1090
|
#
|
1063
1091
|
# * +EXPUNGE+ can only be sent while a command is in progress.
|
1064
1092
|
# * +EXPUNGE+ must _not_ be sent during #fetch, #store, or #search.
|
@@ -1143,10 +1171,7 @@ module Net
|
|
1143
1171
|
end
|
1144
1172
|
|
1145
1173
|
# :call-seq:
|
1146
|
-
# authenticate(mechanism, *,
|
1147
|
-
# sasl_ir: true,
|
1148
|
-
# registry: Net::IMAP::SASL.authenticators,
|
1149
|
-
# **, &) -> ok_resp
|
1174
|
+
# authenticate(mechanism, *, sasl_ir: true, registry: Net::IMAP::SASL.authenticators, **, &) -> ok_resp
|
1150
1175
|
#
|
1151
1176
|
# Sends an {AUTHENTICATE command [IMAP4rev1 §6.2.2]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.2]
|
1152
1177
|
# to authenticate the client. If successful, the connection enters the
|
@@ -1330,7 +1355,7 @@ module Net
|
|
1330
1355
|
# the server may return an untagged "NO" response with a "UIDNOTSTICKY"
|
1331
1356
|
# response code indicating that the mailstore does not support persistent
|
1332
1357
|
# UIDs:
|
1333
|
-
#
|
1358
|
+
# imap.responses("NO", &:last)&.code&.name == "UIDNOTSTICKY"
|
1334
1359
|
def select(mailbox)
|
1335
1360
|
synchronize do
|
1336
1361
|
@responses.clear
|
@@ -1420,10 +1445,10 @@ module Net
|
|
1420
1445
|
# to the client. +refname+ provides a context (for instance, a base
|
1421
1446
|
# directory in a directory-based mailbox hierarchy). +mailbox+ specifies a
|
1422
1447
|
# mailbox or (via wildcards) mailboxes under that context. Two wildcards
|
1423
|
-
# may be used in +mailbox+:
|
1424
|
-
# the hierarchy delimiter (for instance,
|
1425
|
-
# directory-based mailbox hierarchy); and
|
1426
|
-
# *except* the hierarchy delimiter.
|
1448
|
+
# may be used in +mailbox+: <tt>"*"</tt>, which matches all characters
|
1449
|
+
# *including* the hierarchy delimiter (for instance, "/" on a UNIX-hosted
|
1450
|
+
# directory-based mailbox hierarchy); and <tt>"%"</tt>, which matches all
|
1451
|
+
# characters *except* the hierarchy delimiter.
|
1427
1452
|
#
|
1428
1453
|
# If +refname+ is empty, +mailbox+ is used directly to determine
|
1429
1454
|
# which mailboxes to match. If +mailbox+ is empty, the root
|
@@ -1471,16 +1496,16 @@ module Net
|
|
1471
1496
|
# servers, then folder creation (and listing, moving, etc) can lead to
|
1472
1497
|
# errors.
|
1473
1498
|
#
|
1474
|
-
# From RFC2342:
|
1475
|
-
#
|
1476
|
-
# Although typically a server will support only a single Personal
|
1499
|
+
# From RFC2342[https://tools.ietf.org/html/rfc2342]:
|
1500
|
+
# >>>
|
1501
|
+
# <em>Although typically a server will support only a single Personal
|
1477
1502
|
# Namespace, and a single Other User's Namespace, circumstances exist
|
1478
1503
|
# where there MAY be multiples of these, and a client MUST be prepared
|
1479
1504
|
# for them. If a client is configured such that it is required to create
|
1480
1505
|
# a certain mailbox, there can be circumstances where it is unclear which
|
1481
1506
|
# Personal Namespaces it should create the mailbox in. In these
|
1482
1507
|
# situations a client SHOULD let the user select which namespaces to
|
1483
|
-
# create the mailbox in
|
1508
|
+
# create the mailbox in.</em>
|
1484
1509
|
#
|
1485
1510
|
# Related: #list, Namespaces, Namespace
|
1486
1511
|
#
|
@@ -1665,21 +1690,52 @@ module Net
|
|
1665
1690
|
|
1666
1691
|
# Sends a {STATUS commands [IMAP4rev1 §6.3.10]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.3.10]
|
1667
1692
|
# and returns the status of the indicated +mailbox+. +attr+ is a list of one
|
1668
|
-
# or more attributes whose statuses are to be requested.
|
1669
|
-
#
|
1693
|
+
# or more attributes whose statuses are to be requested.
|
1694
|
+
#
|
1695
|
+
# The return value is a hash of attributes.
|
1696
|
+
#
|
1697
|
+
# A Net::IMAP::NoResponseError is raised if status values
|
1698
|
+
# for +mailbox+ cannot be returned; for instance, because it
|
1699
|
+
# does not exist.
|
1700
|
+
#
|
1701
|
+
# ===== Supported attributes:
|
1702
|
+
#
|
1703
|
+
# +MESSAGES+:: The number of messages in the mailbox.
|
1670
1704
|
#
|
1671
|
-
#
|
1672
|
-
# RECENT:: the number of recent messages in the mailbox.
|
1673
|
-
# UNSEEN:: the number of unseen messages in the mailbox.
|
1705
|
+
# +UIDNEXT+:: The next unique identifier value of the mailbox.
|
1674
1706
|
#
|
1675
|
-
# The
|
1707
|
+
# +UIDVALIDITY+:: The unique identifier validity value of the mailbox.
|
1708
|
+
#
|
1709
|
+
# +UNSEEN+:: The number of messages without the <tt>\Seen</tt> flag.
|
1710
|
+
#
|
1711
|
+
# +DELETED+:: The number of messages with the <tt>\Deleted</tt> flag.
|
1712
|
+
#
|
1713
|
+
# +SIZE+::
|
1714
|
+
# The approximate size of the mailbox---must be greater than or equal to
|
1715
|
+
# the sum of all messages' +RFC822.SIZE+ fetch item values.
|
1716
|
+
#
|
1717
|
+
# +MAILBOXID+::
|
1718
|
+
# A server-allocated unique identifier for the mailbox.
|
1719
|
+
# See +OBJECTID+
|
1720
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html#section-4].
|
1721
|
+
#
|
1722
|
+
# +RECENT+::
|
1723
|
+
# The number of messages with the <tt>\Recent</tt> flag.
|
1724
|
+
# _NOTE:_ +RECENT+ was removed from IMAP4rev2.
|
1725
|
+
#
|
1726
|
+
# ===== For example:
|
1676
1727
|
#
|
1677
1728
|
# p imap.status("inbox", ["MESSAGES", "RECENT"])
|
1678
1729
|
# #=> {"RECENT"=>0, "MESSAGES"=>44}
|
1679
1730
|
#
|
1680
|
-
#
|
1681
|
-
#
|
1682
|
-
#
|
1731
|
+
# ===== Capabilities
|
1732
|
+
#
|
1733
|
+
# +SIZE+ requires the server's capabilities to include either +IMAP4rev2+ or
|
1734
|
+
# <tt>STATUS=SIZE</tt>
|
1735
|
+
# {[RFC8483]}[https://www.rfc-editor.org/rfc/rfc8483.html].
|
1736
|
+
#
|
1737
|
+
# +MAILBOXID+ requires the server's capabilities to include +OBJECTID+
|
1738
|
+
# {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
|
1683
1739
|
def status(mailbox, attr)
|
1684
1740
|
synchronize do
|
1685
1741
|
send_command("STATUS", mailbox, attr)
|
@@ -1935,11 +1991,11 @@ module Net
|
|
1935
1991
|
# to alter data associated with messages in the mailbox, in particular their
|
1936
1992
|
# flags. The +set+ parameter is a number, an array of numbers, or a Range
|
1937
1993
|
# object. Each number is a message sequence number. +attr+ is the name of a
|
1938
|
-
# data item to store:
|
1939
|
-
# provided one,
|
1940
|
-
# remove them. +flags+ is a list of flags.
|
1994
|
+
# data item to store: <tt>"FLAGS"</tt> will replace the message's flag list
|
1995
|
+
# with the provided one, <tt>"+FLAGS"</tt> will add the provided flags, and
|
1996
|
+
# <tt>"-FLAGS"</tt> will remove them. +flags+ is a list of flags.
|
1941
1997
|
#
|
1942
|
-
# The return value is an array of FetchData
|
1998
|
+
# The return value is an array of FetchData.
|
1943
1999
|
#
|
1944
2000
|
# Related: #uid_store
|
1945
2001
|
#
|
@@ -2191,7 +2247,7 @@ module Net
|
|
2191
2247
|
.join(' ')
|
2192
2248
|
synchronize do
|
2193
2249
|
send_command("ENABLE #{capabilities}")
|
2194
|
-
result = clear_responses("ENABLED").last
|
2250
|
+
result = clear_responses("ENABLED").last || []
|
2195
2251
|
@utf8_strings ||= result.include? "UTF8=ACCEPT"
|
2196
2252
|
@utf8_strings ||= result.include? "IMAP4REV2"
|
2197
2253
|
result
|
@@ -2522,7 +2578,8 @@ module Net
|
|
2522
2578
|
when /\A(?:BAD)\z/ni
|
2523
2579
|
raise BadResponseError, resp
|
2524
2580
|
else
|
2525
|
-
|
2581
|
+
disconnect
|
2582
|
+
raise InvalidResponseError, "invalid tagged resp: %p" % [resp.raw.chomp]
|
2526
2583
|
end
|
2527
2584
|
end
|
2528
2585
|
|
@@ -2644,7 +2701,7 @@ module Net
|
|
2644
2701
|
else
|
2645
2702
|
send_command(cmd, *keys)
|
2646
2703
|
end
|
2647
|
-
clear_responses("SEARCH").last
|
2704
|
+
clear_responses("SEARCH").last || []
|
2648
2705
|
end
|
2649
2706
|
end
|
2650
2707
|
|
@@ -2693,7 +2750,7 @@ module Net
|
|
2693
2750
|
normalize_searching_criteria(search_keys)
|
2694
2751
|
synchronize do
|
2695
2752
|
send_command(cmd, sort_keys, charset, *search_keys)
|
2696
|
-
clear_responses("SORT").last
|
2753
|
+
clear_responses("SORT").last || []
|
2697
2754
|
end
|
2698
2755
|
end
|
2699
2756
|
|
@@ -2706,7 +2763,7 @@ module Net
|
|
2706
2763
|
normalize_searching_criteria(search_keys)
|
2707
2764
|
synchronize do
|
2708
2765
|
send_command(cmd, algorithm, charset, *search_keys)
|
2709
|
-
clear_responses("THREAD").last
|
2766
|
+
clear_responses("THREAD").last || []
|
2710
2767
|
end
|
2711
2768
|
end
|
2712
2769
|
|
data/net-imap.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
name = File.basename(__FILE__, ".gemspec")
|
4
4
|
version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
|
5
|
-
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
|
5
|
+
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb"), :encoding=> 'utf-8') do |line|
|
6
6
|
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
|
7
7
|
end rescue nil
|
8
8
|
end
|
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
# Specify which files should be added to the gem when it is released.
|
26
26
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
27
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
28
|
-
`git ls-files -z 2>/dev/null`.split("\x0")
|
28
|
+
`git ls-files -z 2>/dev/null`.split("\x0")
|
29
|
+
.reject {|f| f.match(%r{^(bin|test|spec|benchmarks|features|rfcs)/}) }
|
29
30
|
end
|
30
31
|
spec.bindir = "exe"
|
31
32
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
PARSER_TEST_FIXTURES = FileList.new "test/net/imap/fixtures/response_parser/*.yml"
|
4
|
+
CLOBBER.include "benchmarks/parser.yml"
|
5
|
+
CLEAN.include "benchmarks/Gemfile-*"
|
6
|
+
|
7
|
+
BENCHMARK_INIT = <<RUBY
|
8
|
+
require "yaml"
|
9
|
+
require "net/imap"
|
10
|
+
|
11
|
+
def load_response(file, name)
|
12
|
+
YAML.unsafe_load_file(file).dig(:tests, name, :response)
|
13
|
+
.force_encoding "ASCII-8BIT" \\
|
14
|
+
or abort "ERRORO: missing %p fixture data in %p" % [name, file]
|
15
|
+
end
|
16
|
+
|
17
|
+
parser = Net::IMAP::ResponseParser.new
|
18
|
+
RUBY
|
19
|
+
|
20
|
+
file "benchmarks/parser.yml" => PARSER_TEST_FIXTURES do |t|
|
21
|
+
require "yaml"
|
22
|
+
require "pathname"
|
23
|
+
require "net/imap"
|
24
|
+
|
25
|
+
path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
|
26
|
+
files = path.glob("*.yml")
|
27
|
+
tests = files.flat_map {|file|
|
28
|
+
file.read
|
29
|
+
.gsub(%r{([-:]) !ruby/(object|struct):\S+}) { $1 }
|
30
|
+
.then {
|
31
|
+
YAML.safe_load(_1, filename: file,
|
32
|
+
permitted_classes: [Symbol, Regexp], aliases: true)
|
33
|
+
}
|
34
|
+
.fetch(:tests)
|
35
|
+
.select {|test_name, test|
|
36
|
+
:parser_assert_equal == test.fetch(:test_type) {
|
37
|
+
test.key?(:expected) ? :parser_assert_equal : :parser_pending
|
38
|
+
}
|
39
|
+
}
|
40
|
+
.map {|test_name, test| [test_name.to_s, test.fetch(:response)] }
|
41
|
+
}
|
42
|
+
|
43
|
+
benchmarks = tests.map {|fixture_name, response|
|
44
|
+
{"name" => fixture_name.delete_prefix("test_"),
|
45
|
+
"prelude" => "response = %s" % [response.dump],
|
46
|
+
"script" => "parser.parse(response)"}
|
47
|
+
}
|
48
|
+
.sort_by { _1["name"] }
|
49
|
+
|
50
|
+
YAML.dump({"prelude" => BENCHMARK_INIT, "benchmark" => benchmarks})
|
51
|
+
.then { File.write t.name, _1 }
|
52
|
+
end
|
53
|
+
|
54
|
+
namespace :benchmarks do
|
55
|
+
desc "Generate benchmarks from fixture data"
|
56
|
+
task :generate => "benchmarks/parser.yml"
|
57
|
+
|
58
|
+
desc "run the parser benchmarks comparing multiple gem versions"
|
59
|
+
task :compare => :generate do |task, args|
|
60
|
+
cd Pathname.new(__dir__) + ".."
|
61
|
+
current = `git describe --tags --dirty`.chomp
|
62
|
+
current = "dev" if current.empty?
|
63
|
+
versions = args.to_a
|
64
|
+
if versions.empty?
|
65
|
+
latest = %x{git describe --tags --abbrev=0 --match 'v*.*.*'}.chomp
|
66
|
+
versions = latest.empty? ? [] : [latest.delete_prefix("v")]
|
67
|
+
end
|
68
|
+
versions = versions.to_h { [_1, "Gemfile-v#{_1}"] }
|
69
|
+
cd "benchmarks" do
|
70
|
+
versions.each do |version, gemfile|
|
71
|
+
File.write gemfile, <<~RUBY
|
72
|
+
# frozen_string_literal: true
|
73
|
+
source "https://rubygems.org"
|
74
|
+
gem "net-imap", #{version.dump}
|
75
|
+
RUBY
|
76
|
+
end
|
77
|
+
versions = {current => "../Gemfile" , **versions}.map {
|
78
|
+
"%s::/usr/bin/env BUNDLE_GEMFILE=%s ruby" % _1
|
79
|
+
}.join(";")
|
80
|
+
|
81
|
+
extra = ENV.fetch("BENCHMARK_ARGS", "").shellsplit
|
82
|
+
|
83
|
+
sh("benchmark-driver",
|
84
|
+
"--bundler",
|
85
|
+
"-e", versions,
|
86
|
+
"parser.yml",
|
87
|
+
*extra)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
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.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-11-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-protocol
|
@@ -83,10 +83,6 @@ files:
|
|
83
83
|
- LICENSE.txt
|
84
84
|
- README.md
|
85
85
|
- Rakefile
|
86
|
-
- benchmarks/generate_parser_benchmarks
|
87
|
-
- benchmarks/parser.yml
|
88
|
-
- benchmarks/stringprep.yml
|
89
|
-
- benchmarks/table-regexps.yml
|
90
86
|
- docs/styles.css
|
91
87
|
- lib/net/imap.rb
|
92
88
|
- lib/net/imap/authenticators.rb
|
@@ -94,6 +90,7 @@ files:
|
|
94
90
|
- lib/net/imap/data_encoding.rb
|
95
91
|
- lib/net/imap/deprecated_client_options.rb
|
96
92
|
- lib/net/imap/errors.rb
|
93
|
+
- lib/net/imap/fetch_data.rb
|
97
94
|
- lib/net/imap/flags.rb
|
98
95
|
- lib/net/imap/response_data.rb
|
99
96
|
- lib/net/imap/response_parser.rb
|
@@ -116,6 +113,7 @@ files:
|
|
116
113
|
- lib/net/imap/sasl/stringprep.rb
|
117
114
|
- lib/net/imap/sasl/xoauth2_authenticator.rb
|
118
115
|
- lib/net/imap/sasl_adapter.rb
|
116
|
+
- lib/net/imap/sequence_set.rb
|
119
117
|
- lib/net/imap/stringprep.rb
|
120
118
|
- lib/net/imap/stringprep/nameprep.rb
|
121
119
|
- lib/net/imap/stringprep/saslprep.rb
|
@@ -123,6 +121,7 @@ files:
|
|
123
121
|
- lib/net/imap/stringprep/tables.rb
|
124
122
|
- lib/net/imap/stringprep/trace.rb
|
125
123
|
- net-imap.gemspec
|
124
|
+
- rakelib/benchmarks.rake
|
126
125
|
- rakelib/rdoc.rake
|
127
126
|
- rakelib/rfcs.rake
|
128
127
|
- rakelib/saslprep.rake
|
@@ -1,52 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "yaml"
|
4
|
-
require "pathname"
|
5
|
-
require "net/imap"
|
6
|
-
|
7
|
-
path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
|
8
|
-
files = path.glob("*.yml")
|
9
|
-
tests = files.flat_map {|file|
|
10
|
-
file.to_s
|
11
|
-
.then { YAML.unsafe_load_file _1 }
|
12
|
-
.fetch(:tests)
|
13
|
-
.select {|test_name, test|
|
14
|
-
:parser_assert_equal == test.fetch(:test_type) {
|
15
|
-
test.key?(:expected) ? :parser_assert_equal : :parser_pending
|
16
|
-
}
|
17
|
-
}
|
18
|
-
.map {|test_name, _|
|
19
|
-
[
|
20
|
-
file.relative_path_from(__dir__).to_s,
|
21
|
-
test_name.to_s,
|
22
|
-
]
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
26
|
-
init = <<RUBY
|
27
|
-
require "yaml"
|
28
|
-
require "net/imap"
|
29
|
-
|
30
|
-
def load_response(file, name)
|
31
|
-
YAML.unsafe_load_file(file).dig(:tests, name, :response)
|
32
|
-
.force_encoding "ASCII-8BIT" \\
|
33
|
-
or abort "ERRORO: missing %p fixture data in %p" % [name, file]
|
34
|
-
end
|
35
|
-
|
36
|
-
parser = Net::IMAP::ResponseParser.new
|
37
|
-
RUBY
|
38
|
-
|
39
|
-
prelude = <<RUBY
|
40
|
-
response = load_response(%p,
|
41
|
-
%p)
|
42
|
-
RUBY
|
43
|
-
script = "parser.parse(response)"
|
44
|
-
|
45
|
-
benchmarks = tests.map {|file, fixture_name|
|
46
|
-
name = fixture_name.delete_prefix("test_")
|
47
|
-
{name:, prelude: prelude % [file, fixture_name], script:}
|
48
|
-
.transform_keys(&:to_s)
|
49
|
-
}
|
50
|
-
.sort_by { _1["name"] }
|
51
|
-
|
52
|
-
puts YAML.dump({"prelude" => init, "benchmark" => benchmarks})
|