ruby-managesieve 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/sievectl +238 -57
- data/lib/managesieve.rb +42 -19
- metadata +4 -3
data/bin/sievectl
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
|
-
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2004, 2005 Andre Nathan <andre@digirati.com.br>
|
4
5
|
#
|
5
6
|
# Permission to use, copy, modify, and distribute this software for any
|
6
7
|
# purpose with or without fee is hereby granted, provided that the above
|
@@ -13,8 +14,69 @@
|
|
13
14
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
14
15
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
15
16
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
17
|
+
#++
|
16
18
|
#
|
17
|
-
#
|
19
|
+
# = Overview
|
20
|
+
#
|
21
|
+
# Sievectl is a utility that allows for management of
|
22
|
+
# Sieve[http://www.cyrusoft.com/sieve/] scripts from the command line. It
|
23
|
+
# supports multiple accounts, configured in the .sievectlrc file located in
|
24
|
+
# the user's home directory.
|
25
|
+
#
|
26
|
+
# == Configuration file example
|
27
|
+
#
|
28
|
+
# accountA:
|
29
|
+
# host: sieve.accounta.tld
|
30
|
+
# port: 2000
|
31
|
+
# user: johndoe
|
32
|
+
# euser: johndoe
|
33
|
+
# password: secret
|
34
|
+
# auth: PLAIN
|
35
|
+
#
|
36
|
+
# accountB:
|
37
|
+
# host: mail.accountb.tld
|
38
|
+
# user: john
|
39
|
+
# password: secret
|
40
|
+
# auth: PLAIN
|
41
|
+
#
|
42
|
+
# The +port+ and +euser+ parameters can be ommited, and will respectively
|
43
|
+
# default to 2000 and the value of the +user+ parameter. If the +auth+
|
44
|
+
# parameter is ommited, it will default to +ANONYMOUS+.
|
45
|
+
#
|
46
|
+
# == Usage and examples
|
47
|
+
#
|
48
|
+
# $ sievectl help
|
49
|
+
# Usage: sievectl <account> <action> [script name]
|
50
|
+
# Action is one of:
|
51
|
+
# capabilities, list, show, activate, deactivate, add, addactive, delete
|
52
|
+
#
|
53
|
+
# Short forms for some actions are also accepted:
|
54
|
+
# caps (capabilities), act (activate), deact (deactivate), addact (addactive),
|
55
|
+
# del (delete)
|
56
|
+
#
|
57
|
+
# Examples:
|
58
|
+
# List server capabilities:
|
59
|
+
# sievectl caps
|
60
|
+
#
|
61
|
+
# List available scripts:
|
62
|
+
# sievectl list
|
63
|
+
#
|
64
|
+
# Show contents of a script:
|
65
|
+
# sievectl show scriptname
|
66
|
+
#
|
67
|
+
# Add a script:
|
68
|
+
# sievectl add scriptname script.txt
|
69
|
+
# or
|
70
|
+
# sievectl add scriptname < script.txt
|
71
|
+
# or
|
72
|
+
# cat script.txt | sievectl add scriptname
|
73
|
+
#
|
74
|
+
# Delete a script:
|
75
|
+
# sievectl del scriptname
|
76
|
+
#
|
77
|
+
#--
|
78
|
+
# $Id: sievectl,v 1.15 2005/01/21 14:45:30 andre Exp $
|
79
|
+
#++
|
18
80
|
#
|
19
81
|
|
20
82
|
begin
|
@@ -26,18 +88,18 @@ end
|
|
26
88
|
require 'managesieve'
|
27
89
|
require 'yaml'
|
28
90
|
|
29
|
-
class ManageSieve
|
91
|
+
class ManageSieve # :nodoc:
|
30
92
|
def print_capabilities
|
31
93
|
puts 'Capabilities:'
|
32
|
-
@capabilities.each { |cap| puts " - #{cap}" }
|
94
|
+
@capabilities.sort.each { |cap| puts " - #{cap}" }
|
33
95
|
|
34
96
|
puts 'Login Mechanisms:'
|
35
|
-
@login_mechs.each { |mech| puts " - #{mech}" }
|
97
|
+
@login_mechs.sort.each { |mech| puts " - #{mech}" }
|
36
98
|
end
|
37
99
|
|
38
100
|
def print_scripts
|
39
101
|
puts 'Available scripts:'
|
40
|
-
|
102
|
+
scripts.sort.each do |name, active|
|
41
103
|
print " - #{name}"
|
42
104
|
print active ? " (active)\n" : "\n"
|
43
105
|
end
|
@@ -49,25 +111,124 @@ class ManageSieve
|
|
49
111
|
end
|
50
112
|
end
|
51
113
|
|
52
|
-
class
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
114
|
+
class TemplateError # :nodoc:
|
115
|
+
end
|
116
|
+
|
117
|
+
class ConfigFile < File # :nodoc:
|
118
|
+
def ConfigFile.open(name)
|
119
|
+
begin
|
120
|
+
file = super(name)
|
121
|
+
conf = YAML::load(file)
|
122
|
+
file.close
|
123
|
+
return conf
|
124
|
+
rescue Errno::ENOENT
|
125
|
+
ConfigFile::create_template(name)
|
126
|
+
exit 0
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def ConfigFile.create_template(name)
|
132
|
+
STDERR.puts <<-__EOF__
|
133
|
+
* Could not find configuration file #{name}.
|
134
|
+
* A template file will be created. Please edit the values to fit your
|
135
|
+
* local configuration and run `#{File::basename $0}' again.
|
136
|
+
__EOF__
|
137
|
+
|
138
|
+
begin
|
139
|
+
file = File::open(name, 'w+')
|
140
|
+
file.puts <<-__EOF__
|
141
|
+
accountname:
|
142
|
+
host: servername
|
143
|
+
port: port
|
144
|
+
user: username
|
145
|
+
euser: effectiveusername
|
146
|
+
password: password
|
147
|
+
auth: authmethod
|
148
|
+
__EOF__
|
149
|
+
rescue => e
|
150
|
+
raise TemplateError, e
|
151
|
+
end
|
58
152
|
end
|
59
153
|
end
|
60
154
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
155
|
+
#
|
156
|
+
# The SieveCtl class is a simple set of wrapper methods around the ones
|
157
|
+
# available on the #ManageSieve class.
|
158
|
+
#
|
159
|
+
class SieveCtl
|
160
|
+
def initialize(conf)
|
161
|
+
@manage_sieve = ManageSieve.new(conf)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Prints the server capabilities.
|
165
|
+
def capabilities
|
166
|
+
@manage_sieve.print_capabilities
|
167
|
+
end
|
168
|
+
|
169
|
+
# Lists the available scripts, specifying which one is active.
|
170
|
+
def list
|
171
|
+
@manage_sieve.print_scripts
|
172
|
+
end
|
173
|
+
|
174
|
+
# Shows the contents of +script+.
|
175
|
+
def show(script)
|
176
|
+
raise ArgumentError, "`show' requires a script name" unless script
|
177
|
+
puts @manage_sieve.get_script(script)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Activates +script+.
|
181
|
+
def activate(script)
|
182
|
+
raise ArgumentError, "`activate' requires a script name" unless script
|
183
|
+
@manage_sieve.set_active(script)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Deactivates +script+. There is no +DEACTIVATE+ command in the MANAGESIEVE
|
187
|
+
# draft, so we fetch the script, remove it and upload it again.
|
188
|
+
def deactivate(script)
|
189
|
+
raise ArgumentError, "`deactivate' requires a script name" unless script
|
190
|
+
data = @manage_sieve.get_script(script)
|
191
|
+
@manage_sieve.delete_script(script)
|
192
|
+
@manage_sieve.put_script(script, data)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Adds a script named +script+, from file +file+. If +file+ is +nil+, read
|
196
|
+
# the script from +STDIN+. Activates the script is +active+ is true.
|
197
|
+
def add(script, file=nil, active=false)
|
198
|
+
action = "add#{active ? 'active' : ''}"
|
199
|
+
raise ArgumentError, "`#{action}' requires a script name" unless script
|
200
|
+
data = file ? File.open(file).readlines : STDIN.readlines
|
201
|
+
@manage_sieve.put_script(script, data.to_s)
|
202
|
+
activate script if active
|
203
|
+
end
|
204
|
+
|
205
|
+
# Deletes +script+
|
206
|
+
def delete(script)
|
207
|
+
raise ArgumentError, "`activate' requires a script name" unless script
|
208
|
+
@manage_sieve.delete_script(script)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def usage(quit=true) # :nodoc: #
|
213
|
+
prog = File::basename $0
|
214
|
+
STDERR.puts <<-__EOF__
|
215
|
+
Usage: #{prog} <account> <action> [script name]
|
216
|
+
Action is one of:
|
217
|
+
capabilities, list, show, activate, deactivate, add, addactive, delete
|
218
|
+
|
219
|
+
You can also try `#{prog} help' for usage examples.
|
220
|
+
__EOF__
|
221
|
+
exit 1 if quit
|
65
222
|
end
|
66
223
|
|
67
|
-
def help
|
224
|
+
def help # :nodoc:
|
68
225
|
prog = File::basename $0
|
69
|
-
|
70
|
-
|
226
|
+
usage(false)
|
227
|
+
puts <<-__EOF__
|
228
|
+
|
229
|
+
Short forms for some actions are also accepted:
|
230
|
+
caps (capabilities), act (activate), deact (deactivate), addact (addactive),
|
231
|
+
del (delete)
|
71
232
|
|
72
233
|
Examples:
|
73
234
|
List server capabilities:
|
@@ -82,11 +243,14 @@ Examples:
|
|
82
243
|
Add a script:
|
83
244
|
#{prog} add scriptname script.txt
|
84
245
|
or
|
246
|
+
#{prog} add scriptname < script.txt
|
247
|
+
or
|
85
248
|
cat script.txt | #{prog} add scriptname
|
86
249
|
|
87
250
|
Delete a script:
|
88
251
|
#{prog} del scriptname
|
89
|
-
__EOF__
|
252
|
+
__EOF__
|
253
|
+
exit 0
|
90
254
|
end
|
91
255
|
|
92
256
|
|
@@ -94,44 +258,61 @@ end
|
|
94
258
|
# Main
|
95
259
|
#
|
96
260
|
|
97
|
-
|
261
|
+
help if ARGV[0] =~ /^h(elp)?$/i
|
262
|
+
|
263
|
+
account, action, name, file = ARGV
|
264
|
+
usage if action.nil?
|
265
|
+
|
266
|
+
begin
|
267
|
+
conf = ConfigFile::open(ENV['HOME'] + '/.sievectlrc')
|
268
|
+
rescue TemplateError => e
|
269
|
+
STDERR.puts "Cannot create template configuration file: #{e}"
|
270
|
+
exit 1
|
271
|
+
rescue => e
|
272
|
+
STDERR.puts "Cannot load configuration file: `#{e}'"
|
273
|
+
exit 1
|
274
|
+
end
|
98
275
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
276
|
+
unless conf.has_key? account
|
277
|
+
STDERR.puts <<-__EOF__
|
278
|
+
* Configuration for account `#{account}' not found.
|
279
|
+
* Maybe your configuration file is in the old format?
|
280
|
+
__EOF__
|
281
|
+
exit 1
|
282
|
+
end
|
283
|
+
|
284
|
+
info = conf[account]
|
285
|
+
|
286
|
+
sievectl = SieveCtl.new(
|
287
|
+
:host => info['host'],
|
288
|
+
:port => info['port'],
|
289
|
+
:user => info['user'],
|
290
|
+
:euser => info['euser'],
|
291
|
+
:password => info['password'],
|
292
|
+
:auth => info['auth']
|
106
293
|
)
|
107
294
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
when /^
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
raise ArgumentError, "`activate' requires a script name" unless name
|
132
|
-
m.delete_script(name)
|
133
|
-
when /^h(elp)?$/
|
134
|
-
help
|
135
|
-
else
|
136
|
-
usage
|
295
|
+
begin
|
296
|
+
case action
|
297
|
+
when /^act(ivate)?$/
|
298
|
+
sievectl.activate(name)
|
299
|
+
when /^add$/
|
300
|
+
sievectl.add(name, file)
|
301
|
+
when /^addact(ive)?/
|
302
|
+
sievectl.add(name, file, true)
|
303
|
+
when /^cap(abilitie)?s$/
|
304
|
+
sievectl.capabilities
|
305
|
+
when /^deact(ivate)?$/
|
306
|
+
sievectl.deactivate(name)
|
307
|
+
when /^del(ete)?$/
|
308
|
+
sievectl.delete(name)
|
309
|
+
when /^list$/
|
310
|
+
sievectl.list
|
311
|
+
when /^show$/
|
312
|
+
sievectl.show(name)
|
313
|
+
else
|
314
|
+
usage
|
315
|
+
end
|
316
|
+
rescue SieveCommandError => e
|
317
|
+
STDERR.puts "* #{e}"
|
137
318
|
end
|
data/lib/managesieve.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
#
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2004 Andre Nathan <andre@digirati.com.br>
|
4
|
+
# Copyright (c) 2004, 2005 Andre Nathan <andre@digirati.com.br>
|
5
5
|
#
|
6
6
|
# Permission to use, copy, modify, and distribute this software for any
|
7
7
|
# purpose with or without fee is hereby granted, provided that the above
|
@@ -25,13 +25,24 @@
|
|
25
25
|
# See the ManageSieve class for documentation and examples.
|
26
26
|
#
|
27
27
|
#--
|
28
|
-
# $Id: managesieve.rb,v 1.
|
28
|
+
# $Id: managesieve.rb,v 1.10 2005/01/17 11:25:56 andre Exp $
|
29
29
|
#++
|
30
30
|
#
|
31
31
|
|
32
32
|
require 'base64'
|
33
33
|
require 'socket'
|
34
34
|
|
35
|
+
#
|
36
|
+
# Define our own Base64.encode64 for compatibility with ruby <= 1.8.1, which
|
37
|
+
# defines encode64() at the top level.
|
38
|
+
#
|
39
|
+
module Base64 # :nodoc:
|
40
|
+
def encode64(s)
|
41
|
+
[s].pack('m')
|
42
|
+
end
|
43
|
+
module_function :encode64
|
44
|
+
end
|
45
|
+
|
35
46
|
class SieveAuthError < Exception; end
|
36
47
|
class SieveCommandError < Exception; end
|
37
48
|
class SieveResponseError < Exception; end
|
@@ -65,7 +76,7 @@ class SieveResponseError < Exception; end
|
|
65
76
|
# )
|
66
77
|
#
|
67
78
|
# # List installed scripts
|
68
|
-
# m.
|
79
|
+
# m.scripts.sort do |name, active|
|
69
80
|
# print name
|
70
81
|
# print active ? " (active)\n" : "\n"
|
71
82
|
# end
|
@@ -125,17 +136,20 @@ class ManageSieve
|
|
125
136
|
end
|
126
137
|
|
127
138
|
|
128
|
-
#
|
129
|
-
# its name and status as parameters.
|
130
|
-
#
|
131
|
-
|
139
|
+
# If a block is given, calls it for each script stored on the server,
|
140
|
+
# passing its name and status as parameters. Else, and array
|
141
|
+
# of [ +name+, +status+ ] arrays is returned. The status is either
|
142
|
+
# 'ACTIVE' or nil.
|
143
|
+
def scripts
|
132
144
|
begin
|
133
145
|
scripts = send_command('LISTSCRIPTS')
|
134
146
|
rescue SieveCommandError => e
|
135
|
-
raise e, "Cannot list scripts"
|
147
|
+
raise e, "Cannot list scripts: #{e}"
|
136
148
|
end
|
149
|
+
return scripts unless block_given?
|
137
150
|
scripts.each { |name, status| yield(name, status) }
|
138
151
|
end
|
152
|
+
alias :each_script :scripts
|
139
153
|
|
140
154
|
# Returns the contents of +script+ as a string.
|
141
155
|
def get_script(script)
|
@@ -144,7 +158,7 @@ class ManageSieve
|
|
144
158
|
rescue SieveCommandError => e
|
145
159
|
raise e, "Cannot get script: #{e}"
|
146
160
|
end
|
147
|
-
return data.to_s
|
161
|
+
return data.to_s.chomp
|
148
162
|
end
|
149
163
|
|
150
164
|
# Uploads +script+ to the server, using +data+ as its contents.
|
@@ -206,15 +220,15 @@ class ManageSieve
|
|
206
220
|
def auth_plain(euser, user, pass) # :nodoc:
|
207
221
|
args = [ euser, user, pass ]
|
208
222
|
params = sieve_name('PLAIN') + ' '
|
209
|
-
params += sieve_name(encode64(args.join(0.chr)).gsub(/\n/, ''))
|
223
|
+
params += sieve_name(Base64.encode64(args.join(0.chr)).gsub(/\n/, ''))
|
210
224
|
send_command('AUTHENTICATE', params)
|
211
225
|
end
|
212
226
|
|
213
227
|
private
|
214
228
|
def auth_login(user, pass) # :nodoc:
|
215
229
|
send_command('AUTHENTICATE', sieve_name('LOGIN'), false)
|
216
|
-
send_command(sieve_name(encode64(user)).gsub(/\n/, ''), nil, false)
|
217
|
-
send_command(sieve_name(encode64(pass)).gsub(/\n/, ''))
|
230
|
+
send_command(sieve_name(Base64.encode64(user)).gsub(/\n/, ''), nil, false)
|
231
|
+
send_command(sieve_name(Base64.encode64(pass)).gsub(/\n/, ''))
|
218
232
|
end
|
219
233
|
|
220
234
|
private
|
@@ -255,10 +269,19 @@ class ManageSieve
|
|
255
269
|
loop do
|
256
270
|
data = get_line
|
257
271
|
|
258
|
-
# server
|
259
|
-
m =
|
260
|
-
yield :
|
261
|
-
|
272
|
+
# server ok
|
273
|
+
m = /^OK$/.match(data)
|
274
|
+
yield :ok, m.captures.values_at(0, 3) and next if m
|
275
|
+
|
276
|
+
# server error
|
277
|
+
m = /^(NO|BYE)(.*)?$/.match(data)
|
278
|
+
if m
|
279
|
+
err, msg = m.captures
|
280
|
+
size = msg.scan(/\{(\d+)\+?\}/).to_s.to_i
|
281
|
+
yield :error, @socket.read(size.to_i + 2) and next if size > 0
|
282
|
+
yield :error, msg and next
|
283
|
+
end
|
284
|
+
|
262
285
|
# quoted text
|
263
286
|
m = /"([^"]*)"(\s"?([^"]*)"?)?$/.match(data)
|
264
287
|
yield :quoted, m.captures.values_at(0,2) and next if m
|
@@ -278,10 +301,10 @@ class ManageSieve
|
|
278
301
|
response = []
|
279
302
|
parse_each_line do |flag, data|
|
280
303
|
case flag
|
281
|
-
when :
|
282
|
-
type, error = data
|
283
|
-
raise SieveResponseError, error unless type == 'OK'
|
304
|
+
when :ok
|
284
305
|
return response
|
306
|
+
when :error
|
307
|
+
raise SieveResponseError, data.strip.gsub(/\r\n/, ' ')
|
285
308
|
else
|
286
309
|
response << data
|
287
310
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ruby-managesieve
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2005-01-21
|
8
8
|
summary: A Ruby library for the MANAGESIEVE protocol
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -12,7 +12,8 @@ author: Andre Nathan
|
|
12
12
|
email: andre@digirati.com.br
|
13
13
|
homepage: http://managesieve.rubyforge.org
|
14
14
|
rubyforge_project: ruby-managesieve
|
15
|
-
description: "ruby-managesieve is a pure-ruby implementation of the MANAGESIEVE protocol,
|
15
|
+
description: "ruby-managesieve is a pure-ruby implementation of the MANAGESIEVE protocol,
|
16
|
+
allowing remote management of Sieve scripts from ruby."
|
16
17
|
autorequire: managesieve
|
17
18
|
default_executable:
|
18
19
|
bindir: bin
|