net-imap 0.1.1 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +12 -5
- data/.gitignore +1 -0
- data/README.md +1 -1
- data/Rakefile +7 -0
- data/lib/net/imap/authenticators/cram_md5.rb +49 -0
- data/lib/net/imap/authenticators/digest_md5.rb +111 -0
- data/lib/net/imap/authenticators/login.rb +43 -0
- data/lib/net/imap/authenticators/plain.rb +41 -0
- data/lib/net/imap/authenticators.rb +44 -0
- data/lib/net/imap/command_data.rb +303 -0
- data/lib/net/imap/data_encoding.rb +106 -0
- data/lib/net/imap/errors.rb +59 -0
- data/lib/net/imap/flags.rb +234 -0
- data/lib/net/imap/response_data.rb +527 -0
- data/lib/net/imap/response_parser.rb +1526 -0
- data/lib/net/imap.rb +212 -2475
- data/net-imap.gemspec +2 -2
- metadata +15 -7
- data/Gemfile.lock +0 -23
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "errors"
|
4
|
+
|
5
|
+
module Net
|
6
|
+
class IMAP < Protocol
|
7
|
+
|
8
|
+
# Decode a string from modified UTF-7 format to UTF-8.
|
9
|
+
#
|
10
|
+
# UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
|
11
|
+
# slightly modified version of this to encode mailbox names
|
12
|
+
# containing non-ASCII characters; see [IMAP] section 5.1.3.
|
13
|
+
#
|
14
|
+
# Net::IMAP does _not_ automatically encode and decode
|
15
|
+
# mailbox names to and from UTF-7.
|
16
|
+
def self.decode_utf7(s)
|
17
|
+
return s.gsub(/&([^-]+)?-/n) {
|
18
|
+
if $1
|
19
|
+
($1.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
|
20
|
+
else
|
21
|
+
"&"
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Encode a string from UTF-8 format to modified UTF-7.
|
27
|
+
def self.encode_utf7(s)
|
28
|
+
return s.gsub(/(&)|[^\x20-\x7e]+/) {
|
29
|
+
if $1
|
30
|
+
"&-"
|
31
|
+
else
|
32
|
+
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
|
33
|
+
"&" + base64.delete("=").tr("/", ",") + "-"
|
34
|
+
end
|
35
|
+
}.force_encoding("ASCII-8BIT")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Formats +time+ as an IMAP-style date.
|
39
|
+
def self.format_date(time)
|
40
|
+
return time.strftime('%d-%b-%Y')
|
41
|
+
end
|
42
|
+
|
43
|
+
# Formats +time+ as an IMAP-style date-time.
|
44
|
+
def self.format_datetime(time)
|
45
|
+
return time.strftime('%d-%b-%Y %H:%M %z')
|
46
|
+
end
|
47
|
+
|
48
|
+
# Common validators of number and nz_number types
|
49
|
+
module NumValidator # :nodoc
|
50
|
+
module_function
|
51
|
+
|
52
|
+
# Check is passed argument valid 'number' in RFC 3501 terminology
|
53
|
+
def valid_number?(num)
|
54
|
+
# [RFC 3501]
|
55
|
+
# number = 1*DIGIT
|
56
|
+
# ; Unsigned 32-bit integer
|
57
|
+
# ; (0 <= n < 4,294,967,296)
|
58
|
+
num >= 0 && num < 4294967296
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check is passed argument valid 'nz_number' in RFC 3501 terminology
|
62
|
+
def valid_nz_number?(num)
|
63
|
+
# [RFC 3501]
|
64
|
+
# nz-number = digit-nz *DIGIT
|
65
|
+
# ; Non-zero unsigned 32-bit integer
|
66
|
+
# ; (0 < n < 4,294,967,296)
|
67
|
+
num != 0 && valid_number?(num)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Check is passed argument valid 'mod_sequence_value' in RFC 4551 terminology
|
71
|
+
def valid_mod_sequence_value?(num)
|
72
|
+
# mod-sequence-value = 1*DIGIT
|
73
|
+
# ; Positive unsigned 64-bit integer
|
74
|
+
# ; (mod-sequence)
|
75
|
+
# ; (1 <= n < 18,446,744,073,709,551,615)
|
76
|
+
num >= 1 && num < 18446744073709551615
|
77
|
+
end
|
78
|
+
|
79
|
+
# Ensure argument is 'number' or raise DataFormatError
|
80
|
+
def ensure_number(num)
|
81
|
+
return if valid_number?(num)
|
82
|
+
|
83
|
+
msg = "number must be unsigned 32-bit integer: #{num}"
|
84
|
+
raise DataFormatError, msg
|
85
|
+
end
|
86
|
+
|
87
|
+
# Ensure argument is 'nz_number' or raise DataFormatError
|
88
|
+
def ensure_nz_number(num)
|
89
|
+
return if valid_nz_number?(num)
|
90
|
+
|
91
|
+
msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
|
92
|
+
raise DataFormatError, msg
|
93
|
+
end
|
94
|
+
|
95
|
+
# Ensure argument is 'mod_sequence_value' or raise DataFormatError
|
96
|
+
def ensure_mod_sequence_value(num)
|
97
|
+
return if valid_mod_sequence_value?(num)
|
98
|
+
|
99
|
+
msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
|
100
|
+
raise DataFormatError, msg
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# Superclass of IMAP errors.
|
7
|
+
class Error < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
# Error raised when data is in the incorrect format.
|
11
|
+
class DataFormatError < Error
|
12
|
+
end
|
13
|
+
|
14
|
+
# Error raised when a response from the server is non-parseable.
|
15
|
+
class ResponseParseError < Error
|
16
|
+
end
|
17
|
+
|
18
|
+
# Superclass of all errors used to encapsulate "fail" responses
|
19
|
+
# from the server.
|
20
|
+
class ResponseError < Error
|
21
|
+
|
22
|
+
# The response that caused this error
|
23
|
+
attr_accessor :response
|
24
|
+
|
25
|
+
def initialize(response)
|
26
|
+
@response = response
|
27
|
+
|
28
|
+
super @response.data.text
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# Error raised upon a "NO" response from the server, indicating
|
34
|
+
# that the client command could not be completed successfully.
|
35
|
+
class NoResponseError < ResponseError
|
36
|
+
end
|
37
|
+
|
38
|
+
# Error raised upon a "BAD" response from the server, indicating
|
39
|
+
# that the client command violated the IMAP protocol, or an internal
|
40
|
+
# server failure has occurred.
|
41
|
+
class BadResponseError < ResponseError
|
42
|
+
end
|
43
|
+
|
44
|
+
# Error raised upon a "BYE" response from the server, indicating
|
45
|
+
# that the client is not being allowed to login, or has been timed
|
46
|
+
# out due to inactivity.
|
47
|
+
class ByeResponseError < ResponseError
|
48
|
+
end
|
49
|
+
|
50
|
+
# Error raised upon an unknown response from the server.
|
51
|
+
class UnknownResponseError < ResponseError
|
52
|
+
end
|
53
|
+
|
54
|
+
RESPONSE_ERRORS = Hash.new(ResponseError)
|
55
|
+
RESPONSE_ERRORS["NO"] = NoResponseError
|
56
|
+
RESPONSE_ERRORS["BAD"] = BadResponseError
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# -------------------------------------------------------------------------
|
7
|
+
# :section: Message Flags: system flags
|
8
|
+
#
|
9
|
+
# A message has a list of zero or more named tokens, known as "flags",
|
10
|
+
# associated with it. A flag is set by its addition to this list and is
|
11
|
+
# cleared by its removal. There are two types of flags in IMAP4rev2: system
|
12
|
+
# flags and keywords. A flag of either type can be permanent or
|
13
|
+
# session-only.
|
14
|
+
#
|
15
|
+
# A "system flag" is a message flag name that is predefined in the IMAP
|
16
|
+
# specification and begins with "\". +Net::IMAP+ returns all system flags
|
17
|
+
# as symbols, without the "\" prefix.
|
18
|
+
#
|
19
|
+
# The descriptions here were copied from the IMAP4rev2 specification:
|
20
|
+
# [RFC-9051 § 2.3.2](https://www.rfc-editor.org/rfc/rfc9051.html#section-2.3.2)
|
21
|
+
#
|
22
|
+
# See [RFC-3501 § 2.3.2](https://www.rfc-editor.org/rfc/rfc3501.html#section-2.3.2)
|
23
|
+
# for a description of the flags message attribute and system flag semantics
|
24
|
+
# in IMAP4rev1.
|
25
|
+
# -------------------------------------------------------------------------
|
26
|
+
|
27
|
+
# Flag indicating a message has been read.
|
28
|
+
SEEN = :Seen
|
29
|
+
|
30
|
+
# Flag indicating a message has been answered.
|
31
|
+
ANSWERED = :Answered
|
32
|
+
|
33
|
+
# A message flag indicating a message has been flagged for special or urgent
|
34
|
+
# attention.
|
35
|
+
#
|
36
|
+
# Also a mailbox special use attribute, which indicates that this mailbox
|
37
|
+
# presents all messages marked in some way as "important". When this
|
38
|
+
# special use is supported, it is likely to represent a virtual mailbox
|
39
|
+
# collecting messages (from other mailboxes) that are marked with the
|
40
|
+
# "\Flagged" message flag.
|
41
|
+
FLAGGED = :Flagged
|
42
|
+
|
43
|
+
# Flag indicating a message has been marked for deletion. This
|
44
|
+
# will occur when the mailbox is closed or expunged.
|
45
|
+
DELETED = :Deleted
|
46
|
+
|
47
|
+
# Flag indicating a message is only a draft or work-in-progress version.
|
48
|
+
DRAFT = :Draft
|
49
|
+
|
50
|
+
# Flag indicating that the message is "recent," meaning that this
|
51
|
+
# session is the first session in which the client has been notified
|
52
|
+
# of this message.
|
53
|
+
#
|
54
|
+
# This flag was defined by
|
55
|
+
# IMAP4rev1 [RFC-3501](https://www.rfc-editor.org/rfc/rfc3501.html),
|
56
|
+
# and has been deprecated by
|
57
|
+
# IMAP4rev2 [RFC-9051](https://www.rfc-editor.org/rfc/rfc9051.html).
|
58
|
+
RECENT = :Recent
|
59
|
+
|
60
|
+
# -------------------------------------------------------------------------
|
61
|
+
# :section: Mailbox Name Attributes, Base attributes
|
62
|
+
# Mailbox name attributes will be returned in LIST responses. Base
|
63
|
+
# attributes must be returned according to the server's capabilities.
|
64
|
+
#
|
65
|
+
# IMAP4 specifies that all mailbox name attributes, including future
|
66
|
+
# extensions, begin with "\". +Net::IMAP+ returns all mailbox attributes as
|
67
|
+
# symbols, without the "\" prefix.
|
68
|
+
#
|
69
|
+
# The descriptions here were copied from the IMAP4rev2 specification:
|
70
|
+
# [RFC9051 § 7.3.1](https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.1).
|
71
|
+
#
|
72
|
+
# Other mailbox name attributes can be found in the [IANA IMAP Mailbox Name
|
73
|
+
# Attributes registry](https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml)].
|
74
|
+
# -------------------------------------------------------------------------
|
75
|
+
|
76
|
+
# The "\NonExistent" attribute indicates that a mailbox name does not refer
|
77
|
+
# to an existing mailbox. Note that this attribute is not meaningful by
|
78
|
+
# itself, as mailbox names that match the canonical LIST pattern but don't
|
79
|
+
# exist must not be returned unless one of the two conditions listed below
|
80
|
+
# is also satisfied:
|
81
|
+
#
|
82
|
+
# 1. The mailbox name also satisfies the selection criteria (for example,
|
83
|
+
# it is subscribed and the "SUBSCRIBED" selection option has been
|
84
|
+
# specified).
|
85
|
+
#
|
86
|
+
# 2. "RECURSIVEMATCH" has been specified, and the mailbox name has at least
|
87
|
+
# one descendant mailbox name that does not match the LIST pattern and
|
88
|
+
# does match the selection criteria.
|
89
|
+
#
|
90
|
+
# In practice, this means that the "\NonExistent" attribute is usually
|
91
|
+
# returned with one or more of "\Subscribed", "\Remote", "\HasChildren", or
|
92
|
+
# the CHILDINFO extended data item.
|
93
|
+
#
|
94
|
+
# The client must treat the presence of the \NonExistent attribute as if the
|
95
|
+
# \NoSelect attribute was also sent by the server
|
96
|
+
NONEXISTENT = :NonExistent
|
97
|
+
|
98
|
+
# Mailbox attribute indicating it is not possible for any child levels of
|
99
|
+
# hierarchy to exist under this name; no child levels exist now and none can
|
100
|
+
# be created in the future children.
|
101
|
+
#
|
102
|
+
# The client must treat the presence of the \NoInferiors attribute as if the
|
103
|
+
# \HasNoChildren attribute was also sent by the server
|
104
|
+
NOINFERIORS = :Noinferiors
|
105
|
+
|
106
|
+
# Mailbox attribute indicating it is not possible to use this name as a
|
107
|
+
# selectable mailbox.
|
108
|
+
NOSELECT = :Noselect
|
109
|
+
|
110
|
+
# The presence of this attribute indicates that the mailbox has child
|
111
|
+
# mailboxes. A server SHOULD NOT set this attribute if there are child
|
112
|
+
# mailboxes and the user does not have permission to access any of them. In
|
113
|
+
# this case, \HasNoChildren SHOULD be used. In many cases, however, a server
|
114
|
+
# may not be able to efficiently compute whether a user has access to any
|
115
|
+
# child mailboxes. Note that even though the \HasChildren attribute for a
|
116
|
+
# mailbox must be correct at the time of processing the mailbox, a client
|
117
|
+
# must be prepared to deal with a situation when a mailbox is marked with
|
118
|
+
# the \HasChildren attribute, but no child mailbox appears in the response
|
119
|
+
# to the LIST command. This might happen, for example, due to child
|
120
|
+
# mailboxes being deleted or made inaccessible to the user (using access
|
121
|
+
# control) by another client before the server is able to list them.
|
122
|
+
#
|
123
|
+
# It is an error for the server to return both a \HasChildren and a
|
124
|
+
# \HasNoChildren attribute in the same LIST response. A client that
|
125
|
+
# encounters a LIST response with both \HasChildren and \HasNoChildren
|
126
|
+
# attributes present should act as if both are absent in the LIST response.
|
127
|
+
HAS_CHILDREN = :HasChildren
|
128
|
+
|
129
|
+
# The presence of this attribute indicates that the mailbox has NO child
|
130
|
+
# mailboxes that are accessible to the currently authenticated user.
|
131
|
+
#
|
132
|
+
# It is an error for the server to return both a \HasChildren and a
|
133
|
+
# \HasNoChildren attribute in the same LIST response. A client that
|
134
|
+
# encounters a LIST response with both \HasChildren and \HasNoChildren
|
135
|
+
# attributes present should act as if both are absent in the LIST response.
|
136
|
+
#
|
137
|
+
# Note: the \HasNoChildren attribute should not be confused with the
|
138
|
+
# \NoInferiors attribute, which indicates that no child mailboxes exist now
|
139
|
+
# and none can be created in the future.
|
140
|
+
HAS_NO_CHILDREN = :HasNoChildren
|
141
|
+
|
142
|
+
# The mailbox has been marked "interesting" by the server; the mailbox
|
143
|
+
# probably contains messages that have been added since the last time the
|
144
|
+
# mailbox was selected.
|
145
|
+
#
|
146
|
+
# If it is not feasible for the server to determine whether or not the
|
147
|
+
# mailbox is "interesting", the server SHOULD NOT send either \Marked or
|
148
|
+
# \Unmarked. The server MUST NOT send more than one of \Marked, \Unmarked,
|
149
|
+
# and \Noselect for a single mailbox, and it MAY send none of these.
|
150
|
+
MARKED = :Marked
|
151
|
+
|
152
|
+
# The mailbox does not contain any additional messages since the last time
|
153
|
+
# the mailbox was selected.
|
154
|
+
#
|
155
|
+
# If it is not feasible for the server to determine whether or not the
|
156
|
+
# mailbox is "interesting", the server SHOULD NOT send either \Marked or
|
157
|
+
# \Unmarked. The server MUST NOT send more than one of \Marked, \Unmarked,
|
158
|
+
# and \Noselect for a single mailbox, and it MAY send none of these.
|
159
|
+
UNMARKED = :Unmarked
|
160
|
+
|
161
|
+
# The mailbox name was subscribed to using the SUBSCRIBE command.
|
162
|
+
SUBSCRIBED = :Subscribed
|
163
|
+
|
164
|
+
# The mailbox is a remote mailbox.
|
165
|
+
REMOTE = :Remove
|
166
|
+
|
167
|
+
# -------------------------------------------------------------------------
|
168
|
+
# :section: Mailbox Name Attributes, Special Use
|
169
|
+
# Mailbox name attributes will be returned in LIST responses. In addition
|
170
|
+
# to the base mailbox name attributes defined above, an IMAP server MAY also
|
171
|
+
# include any or all of the following attributes that denote "role" (or
|
172
|
+
# "special-use") of a mailbox. These attributes are included along with base
|
173
|
+
# attributes defined above. A given mailbox may have none, one, or more than
|
174
|
+
# one of these attributes. In some cases, a special use is advice to a
|
175
|
+
# client about what to put in that mailbox. In other cases, it's advice to a
|
176
|
+
# client about what to expect to find there.
|
177
|
+
#
|
178
|
+
# IMAP4 specifies that all mailbox name attributes, including future
|
179
|
+
# extensions, begin with "\". +Net::IMAP+ returns all mailbox attributes as
|
180
|
+
# symbols, without the "\" prefix.
|
181
|
+
#
|
182
|
+
# The descriptions here were copied from the IMAP4rev2 specification:
|
183
|
+
# [RFC-9051 § 7.3.1](https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.1).
|
184
|
+
#
|
185
|
+
# Other mailbox name attributes can be found in the [IANA IMAP Mailbox Name
|
186
|
+
# Attributes registry](https://www.iana.org/assignments/imap-mailbox-name-attributes/imap-mailbox-name-attributes.xhtml)].
|
187
|
+
# -------------------------------------------------------------------------
|
188
|
+
|
189
|
+
# Mailbox attribute indicating that this mailbox presents all messages in
|
190
|
+
# the user's message store. Implementations MAY omit some messages, such as,
|
191
|
+
# perhaps, those in \Trash and \Junk. When this special use is supported, it
|
192
|
+
# is almost certain to represent a virtual mailbox
|
193
|
+
ALL = :All
|
194
|
+
|
195
|
+
# Mailbox attribute indicating that this mailbox is used to archive
|
196
|
+
# messages. The meaning of an "archival" mailbox is server dependent;
|
197
|
+
# typically, it will be used to get messages out of the inbox, or otherwise
|
198
|
+
# keep them out of the user's way, while still making them accessible
|
199
|
+
ARCHIVE = :Archive
|
200
|
+
|
201
|
+
# Mailbox attribute indicating that this mailbox is used to hold draft
|
202
|
+
# messages -- typically, messages that are being composed but have not yet
|
203
|
+
# been sent. In some server implementations, this might be a virtual
|
204
|
+
# mailbox, containing messages from other mailboxes that are marked with the
|
205
|
+
# "\Draft" message flag. Alternatively, this might just be advice that a
|
206
|
+
# client put drafts here
|
207
|
+
DRAFTS = :Drafts
|
208
|
+
|
209
|
+
# FLAGGED is defined with the system flags section.
|
210
|
+
|
211
|
+
# Mailbox attribute indicating that this mailbox is where messages deemed to
|
212
|
+
# be junk mail are held. Some server implementations might put messages here
|
213
|
+
# automatically. Alternatively, this might just be advice to a client-side
|
214
|
+
# spam filter.
|
215
|
+
JUNK = :Junk
|
216
|
+
|
217
|
+
# Mailbox attribute indicating that this mailbox is used to hold copies of
|
218
|
+
# messages that have been sent. Some server implementations might put
|
219
|
+
# messages here automatically. Alternatively, this might just be advice that
|
220
|
+
# a client save sent messages here.
|
221
|
+
SENT = :Sent
|
222
|
+
|
223
|
+
# Mailbox attribute indicating that this mailbox is used to hold messages
|
224
|
+
# that have been deleted or marked for deletion. In some server
|
225
|
+
# implementations, this might be a virtual mailbox, containing messages from
|
226
|
+
# other mailboxes that are marked with the "\Deleted" message flag.
|
227
|
+
# Alternatively, this might just be advice that a client that chooses not to
|
228
|
+
# use the IMAP "\Deleted" model should use as its trash location. In server
|
229
|
+
# implementations that strictly expect the IMAP "\Deleted" model, this
|
230
|
+
# special use is likely not to be supported.
|
231
|
+
TRASH = :Trash
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|