imapcli 2.0.1 → 3.0.0
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/CHANGELOG.md +17 -0
- data/Gemfile.lock +12 -6
- data/README.md +23 -1
- data/imapcli.gemspec +1 -0
- data/lib/imapcli/cli.rb +20 -15
- data/lib/imapcli/client.rb +37 -15
- data/lib/imapcli/command.rb +6 -1
- data/lib/imapcli/version.rb +1 -1
- data/logoutput.json +527 -0
- data/spec/lib/imapcli/command_spec.rb +5 -0
- metadata +18 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a1983fc995c34ea7b3a8d03cdbc45d644090aa0cc60527fe824cd7ae5227300
|
4
|
+
data.tar.gz: f688ac2337ae9c3f0cb5d6d62df60a345bb5fabc0dab771d518be5d1ce1b18a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23e6aaf015e5ffb731e4f5a6e6d926ff0b8988ba3ea92503df7615aff9217e1b1b55a4c5236a9aabade45dffe53f7bf2912e6084886c25ec0f0d83202fc220f9
|
7
|
+
data.tar.gz: 5cae5a577598c723f2c9621ad5ad173aa246bf93ffaff740f57e5ab6ce4f3c21b0d124daddf55b801f22b60e677a37a93ae1d20a913da82eff3d644818899833
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [Version 3.0.0 (2025-04-21)][v3.0.0]
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
- **BREAKING:** Removed the `-v`/`--verbose` switch which was not functional
|
13
|
+
anyway and added a new option `-O`/`--log-output` flag to write IMAP responses
|
14
|
+
to a file in JSON format.
|
15
|
+
|
16
|
+
## [Version 2.1.0 (2025-03-16)][v2.1.0]
|
17
|
+
|
18
|
+
### Added
|
19
|
+
|
20
|
+
- New command-line flag `-l`/`--limit` to limit the statistics output to
|
21
|
+
a given number of mailboxes (folders).
|
22
|
+
|
8
23
|
## [Version 2.0.1 (2025-03-11)][v2.0.1]
|
9
24
|
|
10
25
|
### Fixed
|
@@ -72,6 +87,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
72
87
|
- Fix: Docker image.
|
73
88
|
- Fix: Options error message.
|
74
89
|
|
90
|
+
[v3.0.0]: https://github.com/bovender/imapcli/releases/tag/v3.0.0
|
91
|
+
[v2.1.0]: https://github.com/bovender/imapcli/releases/tag/v2.1.0
|
75
92
|
[v2.0.1]: https://github.com/bovender/imapcli/releases/tag/v2.0.1
|
76
93
|
[v2.0.0]: https://github.com/bovender/imapcli/releases/tag/v2.0.0
|
77
94
|
[v1.0.7]: https://github.com/bovender/imapcli/releases/tag/v1.0.7
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
imapcli (2.
|
4
|
+
imapcli (2.1.0)
|
5
5
|
activesupport (~> 8.0)
|
6
6
|
csv
|
7
7
|
descriptive_statistics (~> 2.5)
|
8
8
|
dotenv (~> 3.1)
|
9
9
|
gli (~> 2.22)
|
10
|
+
multi_json (~> 1.15)
|
10
11
|
net-imap
|
12
|
+
oj (~> 3.16)
|
11
13
|
tty-progressbar (~> 0.18)
|
12
14
|
tty-prompt (~> 0.23)
|
13
15
|
tty-table (~> 0.12)
|
@@ -16,7 +18,7 @@ PATH
|
|
16
18
|
GEM
|
17
19
|
remote: https://rubygems.org/
|
18
20
|
specs:
|
19
|
-
activesupport (8.0.
|
21
|
+
activesupport (8.0.2)
|
20
22
|
base64
|
21
23
|
benchmark (>= 0.3)
|
22
24
|
bigdecimal
|
@@ -83,7 +85,7 @@ GEM
|
|
83
85
|
pp (>= 0.6.0)
|
84
86
|
rdoc (>= 4.0.0)
|
85
87
|
reline (>= 0.4.2)
|
86
|
-
json (2.10.
|
88
|
+
json (2.10.2)
|
87
89
|
language_server-protocol (3.17.0.4)
|
88
90
|
lint_roller (1.1.0)
|
89
91
|
listen (3.9.0)
|
@@ -92,7 +94,8 @@ GEM
|
|
92
94
|
logger (1.6.6)
|
93
95
|
lumberjack (1.2.10)
|
94
96
|
method_source (1.1.0)
|
95
|
-
minitest (5.25.
|
97
|
+
minitest (5.25.5)
|
98
|
+
multi_json (1.15.0)
|
96
99
|
nenv (0.3.0)
|
97
100
|
net-imap (0.5.6)
|
98
101
|
date
|
@@ -102,6 +105,9 @@ GEM
|
|
102
105
|
notiffany (0.1.3)
|
103
106
|
nenv (~> 0.1)
|
104
107
|
shellany (~> 0.0)
|
108
|
+
oj (3.16.10)
|
109
|
+
bigdecimal (>= 3.0)
|
110
|
+
ostruct (>= 0.2)
|
105
111
|
ostruct (0.6.1)
|
106
112
|
parallel (1.26.3)
|
107
113
|
parser (3.3.7.1)
|
@@ -142,7 +148,7 @@ GEM
|
|
142
148
|
diff-lcs (>= 1.2.0, < 2.0)
|
143
149
|
rspec-support (~> 3.13.0)
|
144
150
|
rspec-support (3.13.2)
|
145
|
-
rubocop (1.
|
151
|
+
rubocop (1.74.0)
|
146
152
|
json (~> 2.3)
|
147
153
|
language_server-protocol (~> 3.17.0.2)
|
148
154
|
lint_roller (~> 1.1.0)
|
@@ -153,7 +159,7 @@ GEM
|
|
153
159
|
rubocop-ast (>= 1.38.0, < 2.0)
|
154
160
|
ruby-progressbar (~> 1.7)
|
155
161
|
unicode-display_width (>= 2.4.0, < 4.0)
|
156
|
-
rubocop-ast (1.
|
162
|
+
rubocop-ast (1.39.0)
|
157
163
|
parser (>= 3.3.1.0)
|
158
164
|
rubocop-performance (1.24.0)
|
159
165
|
lint_roller (~> 1.1)
|
data/README.md
CHANGED
@@ -263,12 +263,34 @@ keyword, as shown below.
|
|
263
263
|
|
264
264
|
Example:
|
265
265
|
|
266
|
-
|
266
|
+
docker run -it bin/imapcli -s yourserver.example.com -u myusername -P stats -r -o max_size Archive
|
267
|
+
|
268
|
+
#### Limiting the number of mailboxes
|
269
|
+
|
270
|
+
You can limit the number of mailboxes (folders) in the output table with the
|
271
|
+
`-l`/`--limit` flag. This flag takes an argument that indicates the maximum
|
272
|
+
number of mailboxes. THe default value for this argument is 10.
|
273
|
+
|
274
|
+
Use case: Say you have a very large IMAP account, and you would like to know the
|
275
|
+
5 folders that contain the largest messages. The command should be:
|
276
|
+
|
277
|
+
imapcli -s yourserver.example.com -u myusername -P stats -o max_size --limit 5
|
267
278
|
|
268
279
|
#### Obtaining comma-separated values (CSV)
|
269
280
|
|
270
281
|
Use the `--csv` flag.
|
271
282
|
|
283
|
+
### Writing IMAP responses to file
|
284
|
+
|
285
|
+
Using the `-O`/`--log-output` flag, you can have `imapcli` write the IMAP responses
|
286
|
+
to a file in JSON format:
|
287
|
+
|
288
|
+
```bash
|
289
|
+
imapcli -s yourserver.example.com -u username -P -O output_file.json stats Inbox
|
290
|
+
```
|
291
|
+
|
292
|
+
**WARNING:** The output file will be overwritten without notice!
|
293
|
+
|
272
294
|
## Alternative resources
|
273
295
|
|
274
296
|
While researching command-line tools for IMAP servers, I came across the
|
data/imapcli.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_dependency('dotenv', '~> 3.1')
|
29
29
|
s.add_dependency('gli', '~> 2.22')
|
30
30
|
s.add_dependency('net-imap')
|
31
|
+
s.add_dependency('oj', '~> 3.16')
|
31
32
|
s.add_dependency('tty-progressbar', '~> 0.18')
|
32
33
|
s.add_dependency('tty-prompt', '~> 0.23')
|
33
34
|
s.add_dependency('tty-table', '~> 0.12')
|
data/lib/imapcli/cli.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'multi_json'
|
4
|
+
require 'oj'
|
5
|
+
|
3
6
|
module Imapcli
|
4
7
|
class Cli # rubocop:disable Metrics/ClassLength,Style/Documentation
|
5
8
|
extend GLI::App
|
@@ -31,8 +34,8 @@ module Imapcli
|
|
31
34
|
desc 'Prompt for password'
|
32
35
|
switch %i[P prompt], negatable: false
|
33
36
|
|
34
|
-
desc '
|
35
|
-
|
37
|
+
desc 'Output responses from Rubys Net::IMAP to file in JSON format. File will be overwritten if it exists!'
|
38
|
+
flag %i[O log-output]
|
36
39
|
|
37
40
|
desc 'Tests if the server is available and log-in succeeds with the credentials'
|
38
41
|
command :check do |c|
|
@@ -74,6 +77,10 @@ module Imapcli
|
|
74
77
|
default: 'total_size'
|
75
78
|
c.switch %i[reverse], desc: 'Reverse sort order (largest first)', negatable: false
|
76
79
|
c.switch [:csv], desc: 'Output comma-separated values (CSV)'
|
80
|
+
c.flag %i[l limit],
|
81
|
+
desc: 'Limit the results to n folders (IMAP mailboxes)',
|
82
|
+
arg_name: 'max_lines',
|
83
|
+
default: 10
|
77
84
|
|
78
85
|
c.action do |_global_options, options, args| # rubocop:disable Metrics/BlockLength
|
79
86
|
raise unless @validator.stats_options_valid?(options, args)
|
@@ -85,7 +92,7 @@ module Imapcli
|
|
85
92
|
if progress_bar
|
86
93
|
progress_bar.advance
|
87
94
|
else
|
88
|
-
@prompt.
|
95
|
+
@prompt.warn "info: collecting stats for #{n} folders" if n > 1
|
89
96
|
progress_bar = TTY::ProgressBar.new(
|
90
97
|
'collecting stats... :current/:total (:percent, :eta remaining)',
|
91
98
|
total: n, clear: true
|
@@ -107,9 +114,9 @@ module Imapcli
|
|
107
114
|
formatted_body = formatted_body.insert(0, :separator).insert(-2, :separator)
|
108
115
|
|
109
116
|
if options[:human]
|
110
|
-
@prompt.
|
117
|
+
@prompt.warn "notice: -H/--human flag present, message sizes are given with SI prefixes"
|
111
118
|
else
|
112
|
-
@prompt.
|
119
|
+
@prompt.warn "notice: message sizes are given in bytes"
|
113
120
|
end
|
114
121
|
|
115
122
|
table = TTY::Table.new(head, formatted_body)
|
@@ -139,27 +146,25 @@ module Imapcli
|
|
139
146
|
global[:p] = @prompt.mask 'Enter password:' if global[:P]
|
140
147
|
global[:p] ||= ENV.fetch('IMAP_PASS', nil)
|
141
148
|
|
142
|
-
client = Imapcli::Client.new(global[:s], global[:u], global[:p])
|
149
|
+
@client = Imapcli::Client.new(global[:s], global[:u], global[:p])
|
143
150
|
@prompt.say "server: #{global[:s]}"
|
144
151
|
@prompt.say "user: #{global[:u]}"
|
145
|
-
raise 'invalid server name' unless client.server_valid?
|
146
|
-
raise 'invalid user name' unless client.user_valid?
|
152
|
+
raise 'invalid server name' unless @client.server_valid?
|
153
|
+
raise 'invalid user name' unless @client.user_valid?
|
147
154
|
|
148
155
|
@prompt.warn 'warning: no password was provided (missing -p/-P option)' unless global[:p]
|
149
|
-
raise 'unable to connect to server' unless client.connection
|
156
|
+
raise 'unable to connect to server' unless @client.connection
|
150
157
|
|
151
|
-
@command = Imapcli::Command.new(client)
|
158
|
+
@command = Imapcli::Command.new(@client)
|
152
159
|
|
153
160
|
true
|
154
161
|
end
|
155
162
|
|
156
163
|
post do |global, _command, _options, _args|
|
157
164
|
@client&.logout
|
158
|
-
if global[:
|
159
|
-
@prompt.
|
160
|
-
@client.responses
|
161
|
-
@prompt.say response
|
162
|
-
end
|
165
|
+
if file_name = global[:o]
|
166
|
+
@prompt.warn "notice: writing IMAP responses to #{file_name.dump}"
|
167
|
+
File.write(file_name, Oj.dump(@client.responses, mode: :object, indent: 2, escape: :unicode_xss, symbolize_names: false))
|
163
168
|
end
|
164
169
|
end
|
165
170
|
|
data/lib/imapcli/client.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Imapcli
|
4
6
|
# Wrapper for Net::IMAP
|
5
7
|
class Client # rubocop:disable Metrics/ClassLength
|
6
8
|
attr_accessor :port, :user, :pass
|
7
|
-
attr_reader :responses
|
8
9
|
|
9
10
|
## Initializs the Client class.
|
10
11
|
##
|
@@ -17,7 +18,7 @@ module Imapcli
|
|
17
18
|
self.server = server_with_optional_port
|
18
19
|
@user = user
|
19
20
|
@pass = pass
|
20
|
-
|
21
|
+
clear_responses
|
21
22
|
end
|
22
23
|
|
23
24
|
# Attribute reader for the server domain name
|
@@ -57,10 +58,15 @@ module Imapcli
|
|
57
58
|
end
|
58
59
|
|
59
60
|
# Clears the server response log
|
60
|
-
def
|
61
|
+
def clear_responses
|
61
62
|
@log = []
|
62
63
|
end
|
63
64
|
|
65
|
+
# Returns the IMAP server response log
|
66
|
+
def responses
|
67
|
+
@log
|
68
|
+
end
|
69
|
+
|
64
70
|
# Returns the last response from the server
|
65
71
|
def last_response
|
66
72
|
@log.last
|
@@ -96,17 +102,17 @@ module Imapcli
|
|
96
102
|
# Returns the server's greeting (which may reveal the server software name
|
97
103
|
# such as 'Dovecot').
|
98
104
|
def greeting
|
99
|
-
query_server { connection.greeting.data.text.strip }
|
105
|
+
query_server('greeting') { connection.greeting.data.text.strip }
|
100
106
|
end
|
101
107
|
|
102
108
|
# Returns the server's capabilities.
|
103
109
|
def capability
|
104
|
-
@capability ||= query_server { connection.capability }
|
110
|
+
@capability ||= query_server('capability') { connection.capability }
|
105
111
|
end
|
106
112
|
|
107
113
|
# Returns the character that is used to separate nested mailbox names.
|
108
114
|
def separator
|
109
|
-
@separator ||= query_server { connection.list('', '')[0].delim }
|
115
|
+
@separator ||= query_server("list('')") { connection.list('', '')[0].delim }
|
110
116
|
end
|
111
117
|
|
112
118
|
# Returns true if the server supports the IMAP QUOTA extension.
|
@@ -120,7 +126,7 @@ module Imapcli
|
|
120
126
|
return unless supports_quota
|
121
127
|
|
122
128
|
@quota ||= begin
|
123
|
-
info = query_server { @connection.getquotaroot('INBOX')[1] }
|
129
|
+
info = query_server("getquotaroot('INBOX')") { @connection.getquotaroot('INBOX')[1] }
|
124
130
|
percent = info.quota.to_i.positive? ? info.usage.to_i.fdiv(info.quota.to_i) * 100 : nil
|
125
131
|
[info.usage, info.quota, percent]
|
126
132
|
end
|
@@ -130,8 +136,8 @@ module Imapcli
|
|
130
136
|
#
|
131
137
|
# The value is currently NOT cached.
|
132
138
|
def messages(mailbox)
|
133
|
-
query_server { connection.examine(mailbox) }
|
134
|
-
query_server { connection.search('ALL') }
|
139
|
+
query_server("examine('#{mailbox}')") { connection.examine(mailbox) }
|
140
|
+
query_server("search('ALL')") { connection.search('ALL') }
|
135
141
|
end
|
136
142
|
|
137
143
|
# Examines a mailbox and returns statistics about the messages in it.
|
@@ -149,7 +155,7 @@ module Imapcli
|
|
149
155
|
if messages.empty?
|
150
156
|
[]
|
151
157
|
else
|
152
|
-
query_server do
|
158
|
+
query_server('fetch(...)') do
|
153
159
|
messages.each_slice(1000).map do |some_messages|
|
154
160
|
connection.fetch(some_messages, 'RFC822.SIZE').map do |f|
|
155
161
|
f.attr['RFC822.SIZE']
|
@@ -168,7 +174,7 @@ module Imapcli
|
|
168
174
|
#
|
169
175
|
# The value is cached.
|
170
176
|
def mailboxes
|
171
|
-
@mailboxes ||= query_server { @connection.list('', '*') }
|
177
|
+
@mailboxes ||= query_server('list') { @connection.list('', '*') }
|
172
178
|
end
|
173
179
|
|
174
180
|
# Returns a tree of +Imapcli::Mailbox+ objects.
|
@@ -188,12 +194,10 @@ module Imapcli
|
|
188
194
|
private
|
189
195
|
|
190
196
|
def response_ok?(response)
|
191
|
-
@log << response
|
192
197
|
response.name == 'OK'
|
193
198
|
end
|
194
199
|
|
195
200
|
def log_error(error)
|
196
|
-
@log << error
|
197
201
|
false
|
198
202
|
end
|
199
203
|
|
@@ -203,13 +207,31 @@ module Imapcli
|
|
203
207
|
# error if not. The code that queries the server must be contained in the
|
204
208
|
# +block+, and the +block+'s return value is returned by this function.
|
205
209
|
# The +connection+'s responses are logged.
|
206
|
-
def query_server
|
210
|
+
def query_server(imap_command)
|
207
211
|
raise('no connection to a server') unless connection
|
208
212
|
|
209
213
|
result = yield
|
210
|
-
@log <<
|
214
|
+
@log << {
|
215
|
+
'imap_command': imap_command,
|
216
|
+
'imap_response': connection.responses.to_h
|
217
|
+
}
|
211
218
|
result
|
212
219
|
end
|
213
220
|
|
221
|
+
# Recursively convert structs in an array of hashes to hashes
|
222
|
+
# Inspired by https://stackoverflow.com/a/62804063/270712
|
223
|
+
# and rephrased to improve readability a bit
|
224
|
+
def to_h_recursively(hash)
|
225
|
+
hash.map do |value|
|
226
|
+
if value.is_a?(Arry)
|
227
|
+
to_h_recursively(value)
|
228
|
+
elsif value.is_a?(Struct)
|
229
|
+
value.to_h
|
230
|
+
else
|
231
|
+
value
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
214
236
|
end
|
215
237
|
end
|
data/lib/imapcli/command.rb
CHANGED
@@ -68,7 +68,12 @@ module Imapcli
|
|
68
68
|
yield current_count if block_given?
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
|
+
output = if options[:limit]
|
73
|
+
sorted_list(list, options).last(options[:limit].to_i)
|
74
|
+
else
|
75
|
+
sorted_list(list, options)
|
76
|
+
end.map do |mailbox|
|
72
77
|
stats_to_table(mailbox.full_name, mailbox.stats)
|
73
78
|
end
|
74
79
|
output << stats_to_table('Total', total_stats) if list.length > 1
|
data/lib/imapcli/version.rb
CHANGED
data/logoutput.json
ADDED
@@ -0,0 +1,527 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
":imap_command":"list",
|
4
|
+
":imap_response":{
|
5
|
+
"FLAGS":[
|
6
|
+
[
|
7
|
+
":Answered",
|
8
|
+
":Flagged",
|
9
|
+
":Deleted",
|
10
|
+
":Seen",
|
11
|
+
":Draft",
|
12
|
+
"NonJunk",
|
13
|
+
"Junk",
|
14
|
+
"$label2",
|
15
|
+
"$forwarded",
|
16
|
+
"$label5",
|
17
|
+
"Old",
|
18
|
+
"$label4",
|
19
|
+
"$label3",
|
20
|
+
"$SIGNED",
|
21
|
+
"$JUNK",
|
22
|
+
"$ATTACHMENT",
|
23
|
+
"$NOTJUNK",
|
24
|
+
"$INVITATION",
|
25
|
+
"$label1",
|
26
|
+
"$TODO"
|
27
|
+
]
|
28
|
+
],
|
29
|
+
"OK":[
|
30
|
+
{
|
31
|
+
"^u":[
|
32
|
+
"Net::IMAP::ResponseText",
|
33
|
+
{
|
34
|
+
"^u":[
|
35
|
+
"Net::IMAP::ResponseCode",
|
36
|
+
"PERMANENTFLAGS",
|
37
|
+
[]]},
|
38
|
+
"Read-only mailbox."]},
|
39
|
+
{
|
40
|
+
"^u":[
|
41
|
+
"Net::IMAP::ResponseText",
|
42
|
+
{
|
43
|
+
"^u":[
|
44
|
+
"Net::IMAP::ResponseCode",
|
45
|
+
"UNSEEN",
|
46
|
+
6]},
|
47
|
+
"First unseen."]},
|
48
|
+
{
|
49
|
+
"^u":[
|
50
|
+
"Net::IMAP::ResponseText",
|
51
|
+
{
|
52
|
+
"^u":[
|
53
|
+
"Net::IMAP::ResponseCode",
|
54
|
+
"UIDVALIDITY",
|
55
|
+
1435851445]},
|
56
|
+
"UIDs valid"]},
|
57
|
+
{
|
58
|
+
"^u":[
|
59
|
+
"Net::IMAP::ResponseText",
|
60
|
+
{
|
61
|
+
"^u":[
|
62
|
+
"Net::IMAP::ResponseCode",
|
63
|
+
"UIDNEXT",
|
64
|
+
104489]},
|
65
|
+
"Predicted next UID"]},
|
66
|
+
{
|
67
|
+
"^u":[
|
68
|
+
"Net::IMAP::ResponseText",
|
69
|
+
{
|
70
|
+
"^u":[
|
71
|
+
"Net::IMAP::ResponseCode",
|
72
|
+
"HIGHESTMODSEQ",
|
73
|
+
309489]},
|
74
|
+
"Highest"]}
|
75
|
+
],
|
76
|
+
"PERMANENTFLAGS":[
|
77
|
+
[]
|
78
|
+
],
|
79
|
+
"EXISTS":[
|
80
|
+
6
|
81
|
+
],
|
82
|
+
"RECENT":[
|
83
|
+
0
|
84
|
+
],
|
85
|
+
"UNSEEN":[
|
86
|
+
6
|
87
|
+
],
|
88
|
+
"UIDVALIDITY":[
|
89
|
+
1435851445
|
90
|
+
],
|
91
|
+
"UIDNEXT":[
|
92
|
+
104489
|
93
|
+
],
|
94
|
+
"HIGHESTMODSEQ":[
|
95
|
+
309489
|
96
|
+
],
|
97
|
+
"ESEARCH":[],
|
98
|
+
"BYE":[
|
99
|
+
{
|
100
|
+
"^u":[
|
101
|
+
"Net::IMAP::ResponseText",
|
102
|
+
null,
|
103
|
+
"Logging out"]}
|
104
|
+
]
|
105
|
+
}
|
106
|
+
},
|
107
|
+
{
|
108
|
+
":imap_command":"list('')",
|
109
|
+
":imap_response":{
|
110
|
+
"FLAGS":[
|
111
|
+
[
|
112
|
+
":Answered",
|
113
|
+
":Flagged",
|
114
|
+
":Deleted",
|
115
|
+
":Seen",
|
116
|
+
":Draft",
|
117
|
+
"NonJunk",
|
118
|
+
"Junk",
|
119
|
+
"$label2",
|
120
|
+
"$forwarded",
|
121
|
+
"$label5",
|
122
|
+
"Old",
|
123
|
+
"$label4",
|
124
|
+
"$label3",
|
125
|
+
"$SIGNED",
|
126
|
+
"$JUNK",
|
127
|
+
"$ATTACHMENT",
|
128
|
+
"$NOTJUNK",
|
129
|
+
"$INVITATION",
|
130
|
+
"$label1",
|
131
|
+
"$TODO"
|
132
|
+
]
|
133
|
+
],
|
134
|
+
"OK":[
|
135
|
+
{
|
136
|
+
"^u":[
|
137
|
+
"Net::IMAP::ResponseText",
|
138
|
+
{
|
139
|
+
"^u":[
|
140
|
+
"Net::IMAP::ResponseCode",
|
141
|
+
"PERMANENTFLAGS",
|
142
|
+
[]]},
|
143
|
+
"Read-only mailbox."]},
|
144
|
+
{
|
145
|
+
"^u":[
|
146
|
+
"Net::IMAP::ResponseText",
|
147
|
+
{
|
148
|
+
"^u":[
|
149
|
+
"Net::IMAP::ResponseCode",
|
150
|
+
"UNSEEN",
|
151
|
+
6]},
|
152
|
+
"First unseen."]},
|
153
|
+
{
|
154
|
+
"^u":[
|
155
|
+
"Net::IMAP::ResponseText",
|
156
|
+
{
|
157
|
+
"^u":[
|
158
|
+
"Net::IMAP::ResponseCode",
|
159
|
+
"UIDVALIDITY",
|
160
|
+
1435851445]},
|
161
|
+
"UIDs valid"]},
|
162
|
+
{
|
163
|
+
"^u":[
|
164
|
+
"Net::IMAP::ResponseText",
|
165
|
+
{
|
166
|
+
"^u":[
|
167
|
+
"Net::IMAP::ResponseCode",
|
168
|
+
"UIDNEXT",
|
169
|
+
104489]},
|
170
|
+
"Predicted next UID"]},
|
171
|
+
{
|
172
|
+
"^u":[
|
173
|
+
"Net::IMAP::ResponseText",
|
174
|
+
{
|
175
|
+
"^u":[
|
176
|
+
"Net::IMAP::ResponseCode",
|
177
|
+
"HIGHESTMODSEQ",
|
178
|
+
309489]},
|
179
|
+
"Highest"]}
|
180
|
+
],
|
181
|
+
"PERMANENTFLAGS":[
|
182
|
+
[]
|
183
|
+
],
|
184
|
+
"EXISTS":[
|
185
|
+
6
|
186
|
+
],
|
187
|
+
"RECENT":[
|
188
|
+
0
|
189
|
+
],
|
190
|
+
"UNSEEN":[
|
191
|
+
6
|
192
|
+
],
|
193
|
+
"UIDVALIDITY":[
|
194
|
+
1435851445
|
195
|
+
],
|
196
|
+
"UIDNEXT":[
|
197
|
+
104489
|
198
|
+
],
|
199
|
+
"HIGHESTMODSEQ":[
|
200
|
+
309489
|
201
|
+
],
|
202
|
+
"ESEARCH":[],
|
203
|
+
"BYE":[
|
204
|
+
{
|
205
|
+
"^u":[
|
206
|
+
"Net::IMAP::ResponseText",
|
207
|
+
null,
|
208
|
+
"Logging out"]}
|
209
|
+
]
|
210
|
+
}
|
211
|
+
},
|
212
|
+
{
|
213
|
+
":imap_command":"examine('INBOX')",
|
214
|
+
":imap_response":{
|
215
|
+
"FLAGS":[
|
216
|
+
[
|
217
|
+
":Answered",
|
218
|
+
":Flagged",
|
219
|
+
":Deleted",
|
220
|
+
":Seen",
|
221
|
+
":Draft",
|
222
|
+
"NonJunk",
|
223
|
+
"Junk",
|
224
|
+
"$label2",
|
225
|
+
"$forwarded",
|
226
|
+
"$label5",
|
227
|
+
"Old",
|
228
|
+
"$label4",
|
229
|
+
"$label3",
|
230
|
+
"$SIGNED",
|
231
|
+
"$JUNK",
|
232
|
+
"$ATTACHMENT",
|
233
|
+
"$NOTJUNK",
|
234
|
+
"$INVITATION",
|
235
|
+
"$label1",
|
236
|
+
"$TODO"
|
237
|
+
]
|
238
|
+
],
|
239
|
+
"OK":[
|
240
|
+
{
|
241
|
+
"^u":[
|
242
|
+
"Net::IMAP::ResponseText",
|
243
|
+
{
|
244
|
+
"^u":[
|
245
|
+
"Net::IMAP::ResponseCode",
|
246
|
+
"PERMANENTFLAGS",
|
247
|
+
[]]},
|
248
|
+
"Read-only mailbox."]},
|
249
|
+
{
|
250
|
+
"^u":[
|
251
|
+
"Net::IMAP::ResponseText",
|
252
|
+
{
|
253
|
+
"^u":[
|
254
|
+
"Net::IMAP::ResponseCode",
|
255
|
+
"UNSEEN",
|
256
|
+
6]},
|
257
|
+
"First unseen."]},
|
258
|
+
{
|
259
|
+
"^u":[
|
260
|
+
"Net::IMAP::ResponseText",
|
261
|
+
{
|
262
|
+
"^u":[
|
263
|
+
"Net::IMAP::ResponseCode",
|
264
|
+
"UIDVALIDITY",
|
265
|
+
1435851445]},
|
266
|
+
"UIDs valid"]},
|
267
|
+
{
|
268
|
+
"^u":[
|
269
|
+
"Net::IMAP::ResponseText",
|
270
|
+
{
|
271
|
+
"^u":[
|
272
|
+
"Net::IMAP::ResponseCode",
|
273
|
+
"UIDNEXT",
|
274
|
+
104489]},
|
275
|
+
"Predicted next UID"]},
|
276
|
+
{
|
277
|
+
"^u":[
|
278
|
+
"Net::IMAP::ResponseText",
|
279
|
+
{
|
280
|
+
"^u":[
|
281
|
+
"Net::IMAP::ResponseCode",
|
282
|
+
"HIGHESTMODSEQ",
|
283
|
+
309489]},
|
284
|
+
"Highest"]}
|
285
|
+
],
|
286
|
+
"PERMANENTFLAGS":[
|
287
|
+
[]
|
288
|
+
],
|
289
|
+
"EXISTS":[
|
290
|
+
6
|
291
|
+
],
|
292
|
+
"RECENT":[
|
293
|
+
0
|
294
|
+
],
|
295
|
+
"UNSEEN":[
|
296
|
+
6
|
297
|
+
],
|
298
|
+
"UIDVALIDITY":[
|
299
|
+
1435851445
|
300
|
+
],
|
301
|
+
"UIDNEXT":[
|
302
|
+
104489
|
303
|
+
],
|
304
|
+
"HIGHESTMODSEQ":[
|
305
|
+
309489
|
306
|
+
],
|
307
|
+
"ESEARCH":[],
|
308
|
+
"BYE":[
|
309
|
+
{
|
310
|
+
"^u":[
|
311
|
+
"Net::IMAP::ResponseText",
|
312
|
+
null,
|
313
|
+
"Logging out"]}
|
314
|
+
]
|
315
|
+
}
|
316
|
+
},
|
317
|
+
{
|
318
|
+
":imap_command":"search('ALL')",
|
319
|
+
":imap_response":{
|
320
|
+
"FLAGS":[
|
321
|
+
[
|
322
|
+
":Answered",
|
323
|
+
":Flagged",
|
324
|
+
":Deleted",
|
325
|
+
":Seen",
|
326
|
+
":Draft",
|
327
|
+
"NonJunk",
|
328
|
+
"Junk",
|
329
|
+
"$label2",
|
330
|
+
"$forwarded",
|
331
|
+
"$label5",
|
332
|
+
"Old",
|
333
|
+
"$label4",
|
334
|
+
"$label3",
|
335
|
+
"$SIGNED",
|
336
|
+
"$JUNK",
|
337
|
+
"$ATTACHMENT",
|
338
|
+
"$NOTJUNK",
|
339
|
+
"$INVITATION",
|
340
|
+
"$label1",
|
341
|
+
"$TODO"
|
342
|
+
]
|
343
|
+
],
|
344
|
+
"OK":[
|
345
|
+
{
|
346
|
+
"^u":[
|
347
|
+
"Net::IMAP::ResponseText",
|
348
|
+
{
|
349
|
+
"^u":[
|
350
|
+
"Net::IMAP::ResponseCode",
|
351
|
+
"PERMANENTFLAGS",
|
352
|
+
[]]},
|
353
|
+
"Read-only mailbox."]},
|
354
|
+
{
|
355
|
+
"^u":[
|
356
|
+
"Net::IMAP::ResponseText",
|
357
|
+
{
|
358
|
+
"^u":[
|
359
|
+
"Net::IMAP::ResponseCode",
|
360
|
+
"UNSEEN",
|
361
|
+
6]},
|
362
|
+
"First unseen."]},
|
363
|
+
{
|
364
|
+
"^u":[
|
365
|
+
"Net::IMAP::ResponseText",
|
366
|
+
{
|
367
|
+
"^u":[
|
368
|
+
"Net::IMAP::ResponseCode",
|
369
|
+
"UIDVALIDITY",
|
370
|
+
1435851445]},
|
371
|
+
"UIDs valid"]},
|
372
|
+
{
|
373
|
+
"^u":[
|
374
|
+
"Net::IMAP::ResponseText",
|
375
|
+
{
|
376
|
+
"^u":[
|
377
|
+
"Net::IMAP::ResponseCode",
|
378
|
+
"UIDNEXT",
|
379
|
+
104489]},
|
380
|
+
"Predicted next UID"]},
|
381
|
+
{
|
382
|
+
"^u":[
|
383
|
+
"Net::IMAP::ResponseText",
|
384
|
+
{
|
385
|
+
"^u":[
|
386
|
+
"Net::IMAP::ResponseCode",
|
387
|
+
"HIGHESTMODSEQ",
|
388
|
+
309489]},
|
389
|
+
"Highest"]}
|
390
|
+
],
|
391
|
+
"PERMANENTFLAGS":[
|
392
|
+
[]
|
393
|
+
],
|
394
|
+
"EXISTS":[
|
395
|
+
6
|
396
|
+
],
|
397
|
+
"RECENT":[
|
398
|
+
0
|
399
|
+
],
|
400
|
+
"UNSEEN":[
|
401
|
+
6
|
402
|
+
],
|
403
|
+
"UIDVALIDITY":[
|
404
|
+
1435851445
|
405
|
+
],
|
406
|
+
"UIDNEXT":[
|
407
|
+
104489
|
408
|
+
],
|
409
|
+
"HIGHESTMODSEQ":[
|
410
|
+
309489
|
411
|
+
],
|
412
|
+
"ESEARCH":[],
|
413
|
+
"BYE":[
|
414
|
+
{
|
415
|
+
"^u":[
|
416
|
+
"Net::IMAP::ResponseText",
|
417
|
+
null,
|
418
|
+
"Logging out"]}
|
419
|
+
]
|
420
|
+
}
|
421
|
+
},
|
422
|
+
{
|
423
|
+
":imap_command":"fetch(...)",
|
424
|
+
":imap_response":{
|
425
|
+
"FLAGS":[
|
426
|
+
[
|
427
|
+
":Answered",
|
428
|
+
":Flagged",
|
429
|
+
":Deleted",
|
430
|
+
":Seen",
|
431
|
+
":Draft",
|
432
|
+
"NonJunk",
|
433
|
+
"Junk",
|
434
|
+
"$label2",
|
435
|
+
"$forwarded",
|
436
|
+
"$label5",
|
437
|
+
"Old",
|
438
|
+
"$label4",
|
439
|
+
"$label3",
|
440
|
+
"$SIGNED",
|
441
|
+
"$JUNK",
|
442
|
+
"$ATTACHMENT",
|
443
|
+
"$NOTJUNK",
|
444
|
+
"$INVITATION",
|
445
|
+
"$label1",
|
446
|
+
"$TODO"
|
447
|
+
]
|
448
|
+
],
|
449
|
+
"OK":[
|
450
|
+
{
|
451
|
+
"^u":[
|
452
|
+
"Net::IMAP::ResponseText",
|
453
|
+
{
|
454
|
+
"^u":[
|
455
|
+
"Net::IMAP::ResponseCode",
|
456
|
+
"PERMANENTFLAGS",
|
457
|
+
[]]},
|
458
|
+
"Read-only mailbox."]},
|
459
|
+
{
|
460
|
+
"^u":[
|
461
|
+
"Net::IMAP::ResponseText",
|
462
|
+
{
|
463
|
+
"^u":[
|
464
|
+
"Net::IMAP::ResponseCode",
|
465
|
+
"UNSEEN",
|
466
|
+
6]},
|
467
|
+
"First unseen."]},
|
468
|
+
{
|
469
|
+
"^u":[
|
470
|
+
"Net::IMAP::ResponseText",
|
471
|
+
{
|
472
|
+
"^u":[
|
473
|
+
"Net::IMAP::ResponseCode",
|
474
|
+
"UIDVALIDITY",
|
475
|
+
1435851445]},
|
476
|
+
"UIDs valid"]},
|
477
|
+
{
|
478
|
+
"^u":[
|
479
|
+
"Net::IMAP::ResponseText",
|
480
|
+
{
|
481
|
+
"^u":[
|
482
|
+
"Net::IMAP::ResponseCode",
|
483
|
+
"UIDNEXT",
|
484
|
+
104489]},
|
485
|
+
"Predicted next UID"]},
|
486
|
+
{
|
487
|
+
"^u":[
|
488
|
+
"Net::IMAP::ResponseText",
|
489
|
+
{
|
490
|
+
"^u":[
|
491
|
+
"Net::IMAP::ResponseCode",
|
492
|
+
"HIGHESTMODSEQ",
|
493
|
+
309489]},
|
494
|
+
"Highest"]}
|
495
|
+
],
|
496
|
+
"PERMANENTFLAGS":[
|
497
|
+
[]
|
498
|
+
],
|
499
|
+
"EXISTS":[
|
500
|
+
6
|
501
|
+
],
|
502
|
+
"RECENT":[
|
503
|
+
0
|
504
|
+
],
|
505
|
+
"UNSEEN":[
|
506
|
+
6
|
507
|
+
],
|
508
|
+
"UIDVALIDITY":[
|
509
|
+
1435851445
|
510
|
+
],
|
511
|
+
"UIDNEXT":[
|
512
|
+
104489
|
513
|
+
],
|
514
|
+
"HIGHESTMODSEQ":[
|
515
|
+
309489
|
516
|
+
],
|
517
|
+
"ESEARCH":[],
|
518
|
+
"BYE":[
|
519
|
+
{
|
520
|
+
"^u":[
|
521
|
+
"Net::IMAP::ResponseText",
|
522
|
+
null,
|
523
|
+
"Logging out"]}
|
524
|
+
]
|
525
|
+
}
|
526
|
+
}
|
527
|
+
]
|
@@ -133,6 +133,11 @@ RSpec.describe Imapcli::Command do
|
|
133
133
|
expect(output).to be_a Array
|
134
134
|
expect(output[0][0]).to eq 'Inbox/Smallest/Messages'
|
135
135
|
end
|
136
|
+
|
137
|
+
it 'returns only the first n mailboxes' do
|
138
|
+
output = command.stats('Inbox', depth: -1, limit: 2)
|
139
|
+
expect(output.length).to eq 3 # including summary line
|
140
|
+
end
|
136
141
|
end
|
137
142
|
|
138
143
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imapcli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Kraus (bovender)
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-04-21 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activesupport
|
@@ -94,6 +93,20 @@ dependencies:
|
|
94
93
|
- - ">="
|
95
94
|
- !ruby/object:Gem::Version
|
96
95
|
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: oj
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.16'
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '3.16'
|
97
110
|
- !ruby/object:Gem::Dependency
|
98
111
|
name: tty-progressbar
|
99
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,7 +163,6 @@ dependencies:
|
|
150
163
|
- - "~>"
|
151
164
|
- !ruby/object:Gem::Version
|
152
165
|
version: 2.7.0
|
153
|
-
description:
|
154
166
|
email: bovender@bovender.de
|
155
167
|
executables:
|
156
168
|
- imapcli
|
@@ -186,6 +198,7 @@ files:
|
|
186
198
|
- lib/imapcli/option_validator.rb
|
187
199
|
- lib/imapcli/stats.rb
|
188
200
|
- lib/imapcli/version.rb
|
201
|
+
- logoutput.json
|
189
202
|
- spec/lib/imapcli/client_spec.rb
|
190
203
|
- spec/lib/imapcli/command_spec.rb
|
191
204
|
- spec/lib/imapcli/mailbox_spec.rb
|
@@ -195,7 +208,6 @@ homepage: https://github.com/bovender/imapcli
|
|
195
208
|
licenses:
|
196
209
|
- Apache-2.0
|
197
210
|
metadata: {}
|
198
|
-
post_install_message:
|
199
211
|
rdoc_options:
|
200
212
|
- "--title"
|
201
213
|
- imapcli
|
@@ -215,8 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
215
227
|
- !ruby/object:Gem::Version
|
216
228
|
version: '0'
|
217
229
|
requirements: []
|
218
|
-
rubygems_version: 3.
|
219
|
-
signing_key:
|
230
|
+
rubygems_version: 3.6.2
|
220
231
|
specification_version: 4
|
221
232
|
summary: Command-line tool to query IMAP servers
|
222
233
|
test_files: []
|