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.
Files changed (3) hide show
  1. data/bin/sievectl +238 -57
  2. data/lib/managesieve.rb +42 -19
  3. metadata +4 -3
data/bin/sievectl CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Copyright (c) 2004 Andre Nathan <andre@digirati.com.br>
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
- # $Id: sievectl,v 1.1.1.1 2004/12/20 17:49:51 andre Exp $
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
- each_script do |name, active|
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 ConfigFile < File
53
- def ConfigFile.open(*args)
54
- file = super(*args)
55
- conf = YAML::load(file)
56
- file.close
57
- return conf
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
- def usage
62
- STDERR.puts <<__EOF__
63
- Usage: #{File::basename($0)} <caps|list|show|activate|add|addactive|del> [script name]
64
- __EOF__
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
- puts <<__EOF__
70
- Usage: #{prog} <caps|list|show|activate|add|addactive|del> [script name]
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
- conf = ConfigFile::open(ENV['HOME'] + '/.sievectlrc')
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
- m = ManageSieve.new(
100
- :host => conf['host'],
101
- :port => conf['port'],
102
- :user => conf['user'],
103
- :euser => conf['euser'],
104
- :password => conf['password'],
105
- :auth => conf['auth']
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
- action, name, file = ARGV
109
-
110
- case action
111
- when /^cap(abilitie)?s$/
112
- m.print_capabilities
113
- when /^list$/
114
- m.print_scripts
115
- when /^show$/
116
- raise ArgumentError, "`show' requires a script name" unless name
117
- puts m.get_script(name)
118
- when /^act(ivate)?$/
119
- raise ArgumentError, "`activate' requires a script name" unless name
120
- m.set_active(name)
121
- when /^add$/
122
- raise ArgumentError, "`add' requires a script name" unless name
123
- script = file ? File.open(file).readlines : STDIN.readlines
124
- m.put_script(name, script.to_s)
125
- when /^addact(ive)?/
126
- raise ArgumentError, "`add' requires a script name" unless name
127
- script = file ? File.open(file).readlines : STDIN.readlines
128
- m.put_script(name, script.to_s)
129
- m.set_active(name)
130
- when /^del(ete)?$/
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.3 2004/12/20 18:34:32 andre Exp $
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.each_script do |name, active|
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
- # Calls the given block for each script stored on the server, passing
129
- # its name and status as parameters. The status is either 'ACTIVE' or
130
- # nil.
131
- def each_script
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 response
259
- m = /(OK|NO|BYE)( \((.*)\))?( (.*))?/.match(data)
260
- yield :response, m.captures.values_at(0, 3) and next if m
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 :response
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.1.0
7
- date: 2004-12-20
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, allowing remote management of Sieve scripts from ruby."
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