Project

General

Profile

Download (14.4 KB) Statistics
| Branch: | Tag: | Revision:
89d8bebc Marc Dequènes (Duck)
#!/usr/bin/ruby -Ku

#--
# LdapShadows, a Medium-level LDAP Access Library and Tool.
bc2c2691 Marc Dequènes (Duck)
# Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
89d8bebc Marc Dequènes (Duck)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#++

# to allow in-place run for test
$: << File.join(File.dirname(__FILE__), "..", "lib")

bf23ac8c Marc Dequènes (Duck)
# get locale from shell
locale = ENV['LANGUAGE'] || ENV['LC_ALL'] || ENV['LC_MESSAGES'] || ENV['LANG']

89d8bebc Marc Dequènes (Duck)
require 'ldap_shadows'
2bd92292 Marc Dequènes (Duck)
require 'ldap_shadows/display_helper'
89d8bebc Marc Dequènes (Duck)
require 'cmdparse2'

cd48b9c9 Marc Dequènes (Duck)
# beware of precedance !
# (SyntaxError is the core Ruby class, not LdapShadows::SyntaxError as expected for example)
bbb82941 Marc Dequènes (Duck)
include LdapShadows

bf23ac8c Marc Dequènes (Duck)
set_locale(locale)
b0304d30 Marc Dequènes (Duck)
304a503d Marc Dequènes (Duck)
52697bc9 Marc Dequènes (Duck)
# defaults
$program_options = {
:admin => false,
:debug => false,
:expert => false,
:handles => false,
5174b503 Marc Dequènes (Duck)
:world_name => nil,
52697bc9 Marc Dequènes (Duck)
:shadow_name => nil
}
bbb82941 Marc Dequènes (Duck)
89d8bebc Marc Dequènes (Duck)
cmdparser = CmdParse::CommandParser.new(true)
bbb82941 Marc Dequènes (Duck)
cmdparser.banner = PRODUCT
5ca3fa56 Marc Dequènes (Duck)
cmdparser.program_name = File.basename(__FILE__)
bbb82941 Marc Dequènes (Duck)
cmdparser.program_version = VERSION.split(".")
89d8bebc Marc Dequènes (Duck)
cmdparser.options = CmdParse::OptionParserWrapper.new do |opt|
opt.separator "Global options:"
b52f0f7d Marc Dequènes (Duck)
opt.on("-a", "--admin", "Output extra info for administrators") {|t| $program_options[:admin] = true }
97d3e6a1 Marc Dequènes (Duck)
opt.on("-d", "--debug", "Output debug info without being formated") {|t| $program_options[:debug] = true }
opt.on("-e", "--expert", "Output extra info for expert users") {|t| $program_options[:expert] = true }
opt.on("-H", "--handles", "Output with handles (objects/field/... keys used for manipulations)") {|t| $program_options[:handles] = true }
5174b503 Marc Dequènes (Duck)
opt.on("-w WORLD", "--world WORLD", "Choose world to enter") {|t| $program_options[:world_name] = t }
0df6346a Marc Dequènes (Duck)
opt.on("-s SHADOW", "--shadow SHADOW", "Choose shadow to travel through") {|t| $program_options[:shadow_name] = t }
89d8bebc Marc Dequènes (Duck)
end

cmdparser.add_command(CmdParse::HelpCommand.new)
cmdparser.add_command(CmdParse::VersionCommand.new)

304a503d Marc Dequènes (Duck)
class Command < CmdParse::Command
35b342c1 Marc Dequènes (Duck)
attr_accessor :usages_params, :usage_extra_info
304a503d Marc Dequènes (Duck)
def usage
35b342c1 Marc Dequènes (Duck)
usage_str = if @usages_params
304a503d Marc Dequènes (Duck)
base_usage_str = super.sub("[ARGS]", "")
usages = @usages_params.collect {|params| base_usage_str + params }
usages.join("\n")
else
super
6089b33a Marc Dequènes (Duck)
end
35b342c1 Marc Dequènes (Duck)
usage_str+= "\n" + @usage_extra_info if @usage_extra_info
usage_str
304a503d Marc Dequènes (Duck)
end
f4aab4ba Marc Dequènes (Duck)
0df6346a Marc Dequènes (Duck)
def execute(args)
1f9bfec2 Marc Dequènes (Duck)
config = LdapShadows::Config.instance
5174b503 Marc Dequènes (Duck)
config.load_world_config($program_options[:world_name], [File.join(ENV['HOME'], ".shadowwalker")])
0df6346a Marc Dequènes (Duck)
begin
4ca7cd95 Marc Dequènes (Duck)
@shadow = config.load_shadow($program_options[:shadow_name])
0df6346a Marc Dequènes (Duck)
rescue ActiveLdap::Error => e
STDERR.puts _("LDAP connection error: %s") % e.to_s
exit 2
rescue
STDERR.puts _("Shadow initialization failed: %s") % $!
exit 2
end
end

f4aab4ba Marc Dequènes (Duck)
protected

38e5c5db Marc Dequènes (Duck)
def params_shift_item_handle_full_handle(args, fetch_item = true, name = "item full-handle")
cd48b9c9 Marc Dequènes (Duck)
raise LdapShadows::SyntaxError, _("no %s given") % name if args.empty?
38e5c5db Marc Dequènes (Duck)
f695b998 Marc Dequènes (Duck)
full_handle = args.shift
020c18fe Marc Dequènes (Duck)
m = LdapShadows::Manipulation.looks_like_full_handle?(full_handle)
cd48b9c9 Marc Dequènes (Duck)
raise LdapShadows::SyntaxError, _("bad %s") % name unless m
020c18fe Marc Dequènes (Duck)
obj_hdl = m[1]
f43a4cb1 Marc Dequènes (Duck)
item_hdl = m[2]
38e5c5db Marc Dequènes (Duck)
if fetch_item
4ca7cd95 Marc Dequènes (Duck)
item = LdapShadows::Manipulation.find_item_by_full_handle(@shadow, full_handle, $program_options[:admin])
020c18fe Marc Dequènes (Duck)
[item.class, item]
38e5c5db Marc Dequènes (Duck)
else
020c18fe Marc Dequènes (Duck)
obj_klass = params_shift_object_handle([obj_hdl])
38e5c5db Marc Dequènes (Duck)
[obj_klass, item_hdl]
end
af19cdc5 Marc Dequènes (Duck)
end

38e5c5db Marc Dequènes (Duck)
def params_shift_object_handle(args)
cd48b9c9 Marc Dequènes (Duck)
raise LdapShadows::SyntaxError, _("no object name given") if args.empty?
af19cdc5 Marc Dequènes (Duck)
obj_hdl = args.shift.downcase.singularize
4ca7cd95 Marc Dequènes (Duck)
obj_klass = @shadow.get_object(obj_hdl)
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("No such object '%s'") % obj_hdl if obj_klass.nil?
af19cdc5 Marc Dequènes (Duck)
obj_klass
end
304a503d Marc Dequènes (Duck)
end

class ListCommand < Command
def initialize
super('list', false)

a24bc5b1 Marc Dequènes (Duck)
self.short_desc = "list objects, and items/aspects/fields of an object"
self.usages_params = [
304a503d Marc Dequènes (Duck)
":objects",
a24bc5b1 Marc Dequènes (Duck)
"<object>",
"<object> :aspects",
"<object> :fields"
304a503d Marc Dequènes (Duck)
]
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

cd48b9c9 Marc Dequènes (Duck)
raise LdapShadows::SyntaxError, _("no object name given") if args.empty?
6089b33a Marc Dequènes (Duck)
a24bc5b1 Marc Dequènes (Duck)
obj_hdl = args.shift.downcase.singularize
304a503d Marc Dequènes (Duck)
case obj_hdl
when ':object'
4ca7cd95 Marc Dequènes (Duck)
list = @shadow.objects
26a3ca45 Marc Dequènes (Duck)
puts "=== List of LDAP objects (#{list.size}) ==="
list.each do |obj_name|
304a503d Marc Dequènes (Duck)
puts " - #{obj_name}"
end
else
38e5c5db Marc Dequènes (Duck)
obj_klass = params_shift_object_handle([obj_hdl])
89d8bebc Marc Dequènes (Duck)
a24bc5b1 Marc Dequènes (Duck)
if args.empty?
obj_human_name = Translator.translate_object_name(obj_hdl)
af19cdc5 Marc Dequènes (Duck)
Display.display_item_list("List of #{obj_human_name.pluralize}", obj_klass.find(:all), $program_options)
a24bc5b1 Marc Dequènes (Duck)
else
subobj_hdl = args.shift.downcase.singularize

obj_human_name = Translator.translate_object_name(obj_hdl)

case subobj_hdl
when ':aspect'
list = obj_klass.possible_aspects
puts "=== List of LDAP Aspects of #{obj_human_name.pluralize} (#{list.size}) ==="
list.each do |aspect|
str = Translator.translate_aspect_name(aspect)
str += " [#{aspect}]" if $program_options[:handles]
puts " - #{str}"
end
when ':field'
list = obj_klass.possible_attributes
puts "=== List of LDAP Fields of #{obj_human_name.pluralize} (#{list.size}) ==="
list.each do |field|
str = Translator.translate_field_name(field)
str += " [#{field}]" if $program_options[:handles]
puts " - #{str}"
end
obj_klass.possible_aspects.each do |aspect|
5b1a6342 Marc Dequènes (Duck)
aspect_human_name = Translator.translate_aspect_name(aspect.handle)
a24bc5b1 Marc Dequènes (Duck)
puts "--- Sublist of LDAP Fields for Aspect #{aspect_human_name} (#{list.size}) ---"
5b1a6342 Marc Dequènes (Duck)
aspect.possible_attributes.each do |field|
a24bc5b1 Marc Dequènes (Duck)
str = Translator.translate_field_name(field)
str += " [#{field}]" if $program_options[:handles]
puts " - #{str}"
end
end
else
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("No such subobject '%s'") % subobj_hdl
a24bc5b1 Marc Dequènes (Duck)
end
end
304a503d Marc Dequènes (Duck)
end
89d8bebc Marc Dequènes (Duck)
end
304a503d Marc Dequènes (Duck)
end
cmdparser.add_command(ListCommand.new)

class ShowCommand < Command
def initialize
super('show', false)

self.short_desc = "show item information"
self.usages_params = [
38e5c5db Marc Dequènes (Duck)
"<object>/<item>",
"<object>/<item> <field>#? [<filename>]",
"<object>/<item> <field>[#<index>] [<filename>]"
304a503d Marc Dequènes (Duck)
]
66ccfd46 Marc Dequènes (Duck)
self.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-f', '--family', 'Display family members.' ) { $program_options[:show_family_members] = true }
end
89d8bebc Marc Dequènes (Duck)
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

38e5c5db Marc Dequènes (Duck)
obj_klass, item = params_shift_item_handle_full_handle(args)
ca51664c Marc Dequènes (Duck)
304a503d Marc Dequènes (Duck)
if args.empty?
$program_options[:skip_binary] = true
Display.display_item(item, $program_options)
ca51664c Marc Dequènes (Duck)
else
304a503d Marc Dequènes (Duck)
field_info = args.shift
field_name, file_no = field_info.split("#")
ca51664c Marc Dequènes (Duck)
5d28ad6e Marc Dequènes (Duck)
unless item.has_field?(field_name)
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("No such field '%s' in object '%s'") % [field_name, obj_klass.handle]
ca51664c Marc Dequènes (Duck)
end
304a503d Marc Dequènes (Duck)
unless item.attribute_present?(field_name)
38e5c5db Marc Dequènes (Duck)
raise PreProcessingError, _("Field '%s' in item '%s' is not present") %
[field_name, item.full_handle]
ca51664c Marc Dequènes (Duck)
end

5d28ad6e Marc Dequènes (Duck)
field_data = item.send(field_name, true)
ca51664c Marc Dequènes (Duck)
11c9a6c9 Marc Dequènes (Duck)
result_is_binary = false
304a503d Marc Dequènes (Duck)
if file_no == "?"
result = field_data.size
else
file_no_i = file_no.to_i
file_no = (file_no_i.to_s == file_no) ? file_no_i : nil
ca51664c Marc Dequènes (Duck)
304a503d Marc Dequènes (Duck)
attr_info = ActiveLdap::Base.schema.attribute(field_name)

field_single = attr_info.single_value?
if file_no.nil? and not field_single
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("Field '%s' in object '%s' has multiple values, but you didn't select one") %
[field_name, obj_klass.handle]
ca51664c Marc Dequènes (Duck)
end
304a503d Marc Dequènes (Duck)
file_no = 0 if file_no.nil?
if file_no > 0 and field_single
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("Field '%s' in object '%s' has only a single value") %
[field_name, obj_klass.handle]
304a503d Marc Dequènes (Duck)
end
if file_no < 0 or file_no >= field_data.size
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("Field '%s' in object '%s' doesn't have such file number '%s' (select in range [0, %s])") %
[field_name, obj_klass.handle, file_no, field_data.size - 1]
304a503d Marc Dequènes (Duck)
end

11c9a6c9 Marc Dequènes (Duck)
result = field_data[file_no]
result_is_binary = attr_info.binary?
aa4e021c Marc Dequènes (Duck)
unless result_is_binary or $program_options[:debug]
4ca7cd95 Marc Dequènes (Duck)
result = LdapShadows::Manipulation.interpret_field_value(@shadow, attr_info.syntax.to_param, result)
f43a4cb1 Marc Dequènes (Duck)
if result.nil?
raise PreProcessingError, _("Field '%s' in object '%s' has a weird value") % [field_name, obj_klass.handle]
end
aa4e021c Marc Dequènes (Duck)
end
ca51664c Marc Dequènes (Duck)
end

304a503d Marc Dequènes (Duck)
if args.empty?
11c9a6c9 Marc Dequènes (Duck)
if result_is_binary
# output binary data without newline, so it can be pipped or redirected easily
STDOUT.write result
else
puts result
end
304a503d Marc Dequènes (Duck)
else
file_name = args.shift
if File.exists?(file_name)
bf23ac8c Marc Dequènes (Duck)
raise PreProcessingError, _("File '%s' already exists, and i won't overwrite it") % file_name
304a503d Marc Dequènes (Duck)
end

begin
open(file_name, "w") do |fp|
fp.write result
end
rescue
bf23ac8c Marc Dequènes (Duck)
raise ProcessingError, _("Cannot save file: %s") % $!
304a503d Marc Dequènes (Duck)
end

puts "File saved."
end
ca51664c Marc Dequènes (Duck)
end
end
89d8bebc Marc Dequènes (Duck)
end
304a503d Marc Dequènes (Duck)
cmdparser.add_command(ShowCommand.new)
89d8bebc Marc Dequènes (Duck)
304a503d Marc Dequènes (Duck)
class TreeCommand < Command
def initialize
super('tree', false)
0d75a0b6 Marc Dequènes (Duck)
304a503d Marc Dequènes (Duck)
self.short_desc = "show skeleton items tree"
self.usages_params = [
38e5c5db Marc Dequènes (Duck)
"[<location-item-full-handle>]"
304a503d Marc Dequènes (Duck)
]
f4aab4ba Marc Dequènes (Duck)
self.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-a', '--all', 'Display all objects in the tree.' ) { $program_options[:tree_all_objects] = true }
end
304a503d Marc Dequènes (Duck)
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

304a503d Marc Dequènes (Duck)
puts "Tree:"

f4aab4ba Marc Dequènes (Duck)
search_base = nil
unless args.empty?
38e5c5db Marc Dequènes (Duck)
loc_obj_klass, loc_item = params_shift_item_handle_full_handle(args, true, "location full-handle")
f4aab4ba Marc Dequènes (Duck)
search_base = loc_item.dn
end
0d75a0b6 Marc Dequènes (Duck)
4ca7cd95 Marc Dequènes (Duck)
tree = @shadow.tree(search_base, $program_options[:debug], $program_options[:tree_all_objects])
f4aab4ba Marc Dequènes (Duck)
Display.display_hash_tree(tree, 0)
0d75a0b6 Marc Dequènes (Duck)
end
end
304a503d Marc Dequènes (Duck)
cmdparser.add_command(TreeCommand.new)
b0304d30 Marc Dequènes (Duck)
38e5c5db Marc Dequènes (Duck)
class ModifyCommand < Command
304a503d Marc Dequènes (Duck)
def initialize
38e5c5db Marc Dequènes (Duck)
super('modify', false)
b0304d30 Marc Dequènes (Duck)
304a503d Marc Dequènes (Duck)
self.short_desc = "Modify attributes of an item"
self.usages_params = [
53db4aef Marc Dequènes (Duck)
"<item-full-handle> [:parent=<value>] (<field>|<relation>|:aspects)(=|+=|-=)<value> [(<field>|<relation>|:aspects)(=|+=|-=)<value>] ..."
304a503d Marc Dequènes (Duck)
]
b0304d30 Marc Dequènes (Duck)
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

38e5c5db Marc Dequènes (Duck)
obj_klass, item = params_shift_item_handle_full_handle(args)
43bd8057 Marc Dequènes (Duck)
2bd92292 Marc Dequènes (Duck)
modification_done = LdapShadows::Manipulation.item_modify_from_strings(item, args)
304a503d Marc Dequènes (Duck)
if modification_done
5aa80ef6 Marc Dequènes (Duck)
item.save!
304a503d Marc Dequènes (Duck)
puts "Modification done."
0814bfc3 Marc Dequènes (Duck)
else
304a503d Marc Dequènes (Duck)
puts "Nothing to do."
0814bfc3 Marc Dequènes (Duck)
end
b0304d30 Marc Dequènes (Duck)
end
304a503d Marc Dequènes (Duck)
end
38e5c5db Marc Dequènes (Duck)
cmdparser.add_command(ModifyCommand.new)
0814bfc3 Marc Dequènes (Duck)
38e5c5db Marc Dequènes (Duck)
class DeleteCommand < Command
304a503d Marc Dequènes (Duck)
def initialize
38e5c5db Marc Dequènes (Duck)
super('delete', false)
304a503d Marc Dequènes (Duck)
self.short_desc = "Delete an item"
self.usages_params = [
38e5c5db Marc Dequènes (Duck)
"<item-full-handle>"
304a503d Marc Dequènes (Duck)
]
deb322cb Marc Dequènes (Duck)
self.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-r', '--recursive', 'Delete object and all children recursively.' ) { $program_options[:recursive_delete] = true }
end
304a503d Marc Dequènes (Duck)
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

38e5c5db Marc Dequènes (Duck)
obj_klass, item = params_shift_item_handle_full_handle(args)
b0304d30 Marc Dequènes (Duck)
deb322cb Marc Dequènes (Duck)
if $program_options[:recursive_delete]
d5e43f08 Marc Dequènes (Duck)
item.delete_recursive
deb322cb Marc Dequènes (Duck)
else
item.delete
end
304a503d Marc Dequènes (Duck)
puts "Deletion done."
34cb4054 Marc Dequènes (Duck)
end
b0304d30 Marc Dequènes (Duck)
end
38e5c5db Marc Dequènes (Duck)
cmdparser.add_command(DeleteCommand.new)
304a503d Marc Dequènes (Duck)
5d28ad6e Marc Dequènes (Duck)
class CreateCommand < Command
def initialize
super('create', false)

self.short_desc = "Create an item"
self.usages_params = [
194e4c78 Marc Dequènes (Duck)
"<new-item-full-handle> [:parent=<value>] [(<field>|<relation>|:aspects)(=|+=|-=)<value>] ..."
5d28ad6e Marc Dequènes (Duck)
]
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

e84e08b6 Marc Dequènes (Duck)
obj_klass, item_hdl = params_shift_item_handle_full_handle(args, false)
5d28ad6e Marc Dequènes (Duck)
item = obj_klass.new(item_hdl)

2bd92292 Marc Dequènes (Duck)
modification_done = LdapShadows::Manipulation.item_modify_from_strings(item, args)
5d28ad6e Marc Dequènes (Duck)
7414a658 Marc Dequènes (Duck)
item.save!
3d5fa735 Marc Dequènes (Duck)
puts "Creation done."
5d28ad6e Marc Dequènes (Duck)
end
end
cmdparser.add_command(CreateCommand.new)

bd714351 Marc Dequènes (Duck)
class SearchCommand < Command
def initialize
super('search', false)

self.short_desc = "Search items"
self.usages_params = [
35b342c1 Marc Dequènes (Duck)
"[:objects=<object>[,<object>]...] [:aspects=<aspect>[,<aspect>]...] [<field>=<value>] [:parents|:siblings|:children|:ancestors|:successors=<item-full-handle>[,<item-full-handle>]] ..."
bd714351 Marc Dequènes (Duck)
]
35b342c1 Marc Dequènes (Duck)
self.usage_extra_info = "Criterias are AND-ed, but lists of values for a criteria are OR-ed."
bd714351 Marc Dequènes (Duck)
end

0df6346a Marc Dequènes (Duck)
def execute(args)
super

cd48b9c9 Marc Dequènes (Duck)
raise LdapShadows::SyntaxError, _("no search arguments") if args.empty?
bd714351 Marc Dequènes (Duck)
4ca7cd95 Marc Dequènes (Duck)
res = LdapShadows::Manipulation.items_find_from_strings(@shadow, args)
a4aeb5ee Marc Dequènes (Duck)
unless res.empty?
display_lines = []
c30d4613 Marc Dequènes (Duck)
res.each do |raw_item|
4ca7cd95 Marc Dequènes (Duck)
display_lines << LdapShadows::Manipulation.raw_item_info(@shadow, raw_item)[:name]
a4aeb5ee Marc Dequènes (Duck)
end
puts display_lines.join("\n")
end
bd714351 Marc Dequènes (Duck)
end
end
cmdparser.add_command(SearchCommand.new)

b0304d30 Marc Dequènes (Duck)
127edd07 Marc Dequènes (Duck)
begin
cmdparser.parse
cd48b9c9 Marc Dequènes (Duck)
rescue LdapShadows::SyntaxError => e
bf23ac8c Marc Dequènes (Duck)
STDERR.puts _("Syntax error: %s") % e.to_s
exit 1
rescue PreProcessingError => e
STDERR.puts _("Preprocessing error: %s") % e.to_s
exit 2
127edd07 Marc Dequènes (Duck)
rescue ActiveLdap::EntryInvalid => e
bf23ac8c Marc Dequènes (Duck)
# work around activeldap#26758 (for english output only :-/)
11c9a6c9 Marc Dequènes (Duck)
STDERR.puts e.to_s.sub(/invalid format: .*: required syntax:/, "invalid format. required syntax:")
127edd07 Marc Dequènes (Duck)
exit 3
rescue ActiveLdap::OperationNotPermitted => e
bf23ac8c Marc Dequènes (Duck)
STDERR.puts _("You don't have enough rights for this operation")
127edd07 Marc Dequènes (Duck)
exit 3
rescue ActiveLdap::Error => e
bf23ac8c Marc Dequènes (Duck)
STDERR.puts _("LDAP error: %s") % e.to_s
127edd07 Marc Dequènes (Duck)
exit 3
f706cc1d Marc Dequènes (Duck)
rescue ProcessingError => e
STDERR.puts _("Processing error: %s") % e.to_s
exit 3
cd48b9c9 Marc Dequènes (Duck)
rescue WeirdError => e
bf23ac8c Marc Dequènes (Duck)
STDERR.puts _("Weird error: %s") % e.to_s
exit 4
127edd07 Marc Dequènes (Duck)
end