ruby-managesieve 0.1.0 → 0.2.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.
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