Project

General

Profile

« Previous | Next » 

Revision ccab26de

Added by Marc Dequènes about 15 years ago

  • ID ccab26de9450c362f61c0c391e47f9861d5d35d9

[fix] gettext domains are binded correctly now (using a new PostmanHome submodule to separate domains easily)

View differences:

postman
module CyborgHood
# not yet ready to be a real Cyborg
class Postman #< Cyborg
module PostmanHome
include GetText
bindtextdomain(PRODUCT + "_" + self.human_name, {:path => File.join(APP_ROOT, "locale"), :charset => "UTF-8"})
textdomain(PRODUCT + "_" + self.human_name)
def initialize
# load config
Config.load(self.human_name.downcase)
@config = Config.instance
ldap_config = @config.ldap
ldap_config.logger = logger
ActiveLdap::Base.establish_connection(ldap_config.marshal_dump)
# setup logs
unless @config.log.nil?
logger.output_level(@config.log.console_level) unless @config.log.console_level.nil?
logger.log_to_file(@config.log.file) unless @config.log.file.nil?
end
bindtextdomain("cyborghood_postman", {:path => File.join(APP_ROOT, "locale"), :charset => "UTF-8"})
@current_thread = Thread.current
@stop_asap = false
@waiting = false
# not yet ready to be a real Cyborg
class Postman #< Cyborg
include GetText
logger.info "Bot '#{self.human_name}' loaded"
end
def initialize
# load config
Config.load(self.human_name.downcase)
@config = Config.instance
def run
imap = IMAP.new(@config.imap)
until @stop_asap
t = Time.now.to_i
logger.debug "Starting mail check"
check_mails(imap)
logger.debug "Mail check finished"
t2 = Time.now.to_i
sleep_time = @config.imap.min_check_interval - (t2 - t)
if sleep_time > 0
logger.debug "Having a break before new check..."
@waiting = true
begin
sleep(sleep_time)
rescue
end
@waiting = false
ldap_config = @config.ldap
ldap_config.logger = logger
ActiveLdap::Base.establish_connection(ldap_config.marshal_dump)
# setup logs
unless @config.log.nil?
logger.output_level(@config.log.console_level) unless @config.log.console_level.nil?
logger.log_to_file(@config.log.file) unless @config.log.file.nil?
end
@current_thread = Thread.current
@stop_asap = false
@waiting = false
logger.info "Bot '#{self.human_name}' loaded"
end
logger.info "Bot was asked to stop..." if @stop_asap
logger.info "Bot terminating"
end
def check_mails(imap)
imap.check_mail do |msg|
if @stop_asap
logger.info "Bot was asked to stop..."
break
def run
imap = IMAP.new(@config.imap)
until @stop_asap
t = Time.now.to_i
logger.debug "Starting mail check"
check_mails(imap)
logger.debug "Mail check finished"
t2 = Time.now.to_i
sleep_time = @config.imap.min_check_interval - (t2 - t)
if sleep_time > 0
logger.debug "Having a break before new check..."
@waiting = true
begin
sleep(sleep_time)
rescue
end
@waiting = false
end
end
logger.info "Bot was asked to stop..." if @stop_asap
logger.info "Bot terminating"
end
mail = Mail.new(msg.content)
logger.info "Received mail with ID '#{mail.message_id}': #{mail.from_addrs} -> #{mail.to_addrs} (#{mail.subject})"
def check_mails(imap)
imap.check_mail do |msg|
if @stop_asap
logger.info "Bot was asked to stop..."
break
end
# ignore mails not signed or encrypted
unless mail.is_pgp_signed? or mail.is_pgp_encrypted?
logger.info "Mail not signed/encrypted or not RFC3156 compliant, ignoring..."
msg.delete
next
end
mail = Mail.new(msg.content)
logger.info "Received mail with ID '#{mail.message_id}': #{mail.from_addrs} -> #{mail.to_addrs} (#{mail.subject})"
logger.debug "RFC3156 content detected"
begin
order = mail.parse
rescue CyberError => e
case e.severity
when :dangerous
logger.fatal " (#{e.message})"
exit 2
when :unrecoverable
logger.error "Internal processing error, skipping mail (#{e.message})"
# ignore mails not signed or encrypted
unless mail.is_pgp_signed? or mail.is_pgp_encrypted?
logger.info "Mail not signed/encrypted or not RFC3156 compliant, ignoring..."
msg.delete
next
when :ignorable
end
end
result_tag = order.ok ? "SUCCESS" : "FAILURE"
logger.info "Processing result: #{result_tag} (#{order.message})"
logger.info "Extra processing information: " + order.system_message if order.system_message
if order.user
if order.user.preferredLanguage
logger.debug "User preference for langage: " + order.user.preferredLanguage
set_locale(order.user.preferredLanguage)
logger.debug "RFC3156 content detected"
begin
order = mail.parse
rescue CyberError => e
case e.severity
when :dangerous
logger.fatal " (#{e.message})"
exit 2
when :unrecoverable
logger.error "Internal processing error, skipping mail (#{e.message})"
next
when :ignorable
end
end
result_tag = order.ok ? "SUCCESS" : "FAILURE"
logger.info "Processing result: #{result_tag} (#{order.message})"
logger.info "Extra processing information: " + order.system_message if order.system_message
if order.user
if order.user.preferredLanguage
logger.debug "User preference for langage: " + order.user.preferredLanguage
set_locale(order.user.preferredLanguage)
else
logger.debug "No user preference for langage, using english"
set_locale("en")
end
else
logger.debug "No user preference for langage, using english"
set_locale("en")
end
else
set_locale("en")
end
unless order.ok
if order.warn_sender
logger.info "Sending reply for rejected message"
mail_reply = mail.create_simple_reject_reply("A message (ID: #{mail.message_id}), apparently from you," +
" was rejected for the following reason:\n #{_(order.message)}")
mail_reply.deliver
unless order.ok
if order.warn_sender
logger.info "Sending reply for rejected message"
mail_reply = mail.create_simple_reject_reply(sprintf(
_("A message (ID: %s), apparently from you, was rejected for the following reason:"),
mail.message_id) + "\n " + _(order.message))
mail_reply.deliver
end
msg.delete
next
end
msg.delete
next
end
logger.debug "Message accepted, processing orders..."
result_list = CommandParser.run(order)
# create transcript
logger.debug "Preparing reply"
reply_txt = sprintf(_("Hello %s,"), order.user.cn) + "\n\n"
reply_txt += _(order.message) + "\n\n" if order.message
reply_txt += _("Follows the transcript of your commands:") + "\n"
reply_attachments = []
result_list.each do |result|
reply_txt << "> #{result.cmd}\n"
reply_txt << "#{result.message}\n"
reply_attachments += result.refs unless result.refs.nil?
end
# create mail
logger.debug "Preparing mail"
mail_reply = mail.create_reply
if reply_attachments.empty?
transcript_part = mail_reply
else
mail_reply.set_content_type("multipart", "mixed", {'boundary' => TMail.new_boundary})
parts = []
logger.debug "Message accepted, processing orders..."
result_list = CommandParser.run(order)
# create transcript
logger.debug "Preparing reply"
reply_txt = sprintf(_("Hello %s,"), order.user.cn) + "\n\n"
reply_txt += _(order.message) + "\n\n" if order.message
reply_txt += _("Follows the transcript of your commands:") + "\n"
reply_attachments = []
result_list.each do |result|
reply_txt << "> #{result.cmd}\n"
reply_txt << "#{result.message}\n"
reply_attachments += result.refs unless result.refs.nil?
end
p = CyborgHood::Mail.new
transcript_part = p
mail_reply.parts << p
# create mail
logger.debug "Preparing mail"
mail_reply = mail.create_reply
if reply_attachments.empty?
transcript_part = mail_reply
else
mail_reply.set_content_type("multipart", "mixed", {'boundary' => TMail.new_boundary})
parts = []
reply_attachments.each do |attachment|
p = CyborgHood::Mail.new
p.set_content_type("text", "plain", {'charset' => "utf-8"})
p.set_disposition("attachment", {'filename' => attachment.filename})
p.quoted_printable_body = attachment.content
transcript_part = p
mail_reply.parts << p
reply_attachments.each do |attachment|
p = CyborgHood::Mail.new
p.set_content_type("text", "plain", {'charset' => "utf-8"})
p.set_disposition("attachment", {'filename' => attachment.filename})
p.quoted_printable_body = attachment.content
mail_reply.parts << p
end
end
# insert transcript
transcript_part.set_content_type("text", "plain", {'charset' => 'utf-8', 'format' => 'flowed'})
transcript_part.set_disposition("inline")
transcript_part.quoted_printable_body = reply_txt
# send reply
logger.debug "Sending mail"
mail_reply.sign_and_crypt(order.user.keyFingerPrint)
mail_reply.deliver
logger.info "Message processed completely, deleting"
msg.delete
end
# insert transcript
transcript_part.set_content_type("text", "plain", {'charset' => 'utf-8', 'format' => 'flowed'})
transcript_part.set_disposition("inline")
transcript_part.quoted_printable_body = reply_txt
# send reply
logger.debug "Sending mail"
mail_reply.sign_and_crypt(order.user.keyFingerPrint)
mail_reply.deliver
logger.info "Message processed completely, deleting"
msg.delete
end
end
def ask_to_stop
@stop_asap = true
Thread.critical = true
@current_thread.raise if @waiting
Thread.critical = false
def ask_to_stop
@stop_asap = true
Thread.critical = true
@current_thread.raise if @waiting
Thread.critical = false
end
end
end
class CommandParser
include GetText
textdomain(PRODUCT + "_Postman")
def self.run(order)
result_list = []
order.commands.each do |cmdstr|
logger.info "Executing command: #{cmdstr}"
begin
result = execute_cmd(order.user, cmdstr, order.references)
if result.nil?
class CommandParser
include GetText
def self.run(order)
result_list = []
order.commands.each do |cmdstr|
logger.info "Executing command: #{cmdstr}"
begin
result = execute_cmd(order.user, cmdstr, order.references)
if result.nil?
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
result.message = _("Command not recognized.")
result.refs = nil
end
rescue CyberError => e
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
result.message = _("Command not recognized.")
result.message = e.message.capitalize + "."
result.refs = nil
rescue
logger.warn "Command crashed: " + $!
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
result.message = _("Internal error. Administrator is warned.")
result.refs = nil
end
rescue CyberError => e
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
result.message = e.message.capitalize + "."
result.refs = nil
rescue
logger.warn "Command crashed: " + $!
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
result.message = _("Internal error. Administrator is warned.")
result.refs = nil
end
tag = result.ok ? "SUCCESS" :"FAILURE"
logger.debug "Command result: [#{tag}] #{result.message}"
result_list << result
tag = result.ok ? "SUCCESS" :"FAILURE"
logger.debug "Command result: [#{tag}] #{result.message}"
result_list << result
end
result_list
end
result_list
end
private
def self.execute_cmd(user, cmdstr, refs)
cmdline = Shellwords.shellwords(cmdstr)
subsys = cmdline.shift
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
ok = true
case subsys.upcase
when "DNS"
return if cmdline.empty?
case cmdline.shift.upcase
when "INFO"
return unless cmdline.empty?
list = CyborgHood::DnsDomain.find_by_manager(user)
txt_list = list.collect{|z| z.cn }.sort.join(", ")
result.ok = true
result.message = sprintf(_("You are manager of the following zones: %s."), txt_list)
when "GET"
return if cmdline.empty?
case cmdline.shift.upcase
when "ZONE"
return if cmdline.empty?
zone = cmdline.shift.downcase
private
dom = CyborgHood::DnsDomain.new(zone)
unless dom.hosted?
result.message = ("This zone is not hosted here.")
return result
end
unless dom.managed_by? user
result.message = _("You are not allowed to manage this zone.")
return result
end
def self.execute_cmd(user, cmdstr, refs)
cmdline = Shellwords.shellwords(cmdstr)
subsys = cmdline.shift
srv_dns = CyborgHood::Services::DNS.new(zone)
result.ok = true
result.message = _("Requested zone content attached.")
zone_ref = {:content => srv_dns.read_zone, :filename => "dnszone_#{zone}.txt"}.to_ostruct
result.refs = [zone_ref]
end
when "SET"
result = OpenStruct.new
result.cmd = cmdstr
result.ok = false
ok = true
case subsys.upcase
when "DNS"
return if cmdline.empty?
case cmdline.shift.upcase
when "ZONE"
return if cmdline.empty?
zone = cmdline.shift.downcase
dom = CyborgHood::DnsDomain.new(zone)
unless dom.hosted?
result.message = _("This zone is not hosted here.")
return result
end
unless dom.managed_by? user
result.message = _("You are not allowed to manage this zone.")
return result
end
srv_dns = CyborgHood::Services::DNS.new(zone)
when "INFO"
return unless cmdline.empty?
list = CyborgHood::DnsDomain.find_by_manager(user)
txt_list = list.collect{|z| z.cn }.sort.join(", ")
result.ok = true
result.message = sprintf(_("You are manager of the following zones: %s."), txt_list)
when "GET"
return if cmdline.empty?
content_ref = cmdline.shift.downcase
return unless content_ref =~ /^@(\d+)$/
part_ref = $1.to_i
unless (1..refs.size).include? part_ref
result.message = _("Attachment number not found.")
return result
end
part = refs[part_ref]
unless part.content_type == "text/plain"
result.message = _("Attachment has wrong content-type.")
return result
case cmdline.shift.upcase
when "ZONE"
return if cmdline.empty?
zone = cmdline.shift.downcase
dom = CyborgHood::DnsDomain.new(zone)
unless dom.hosted?
result.message = ("This zone is not hosted here.")
return result
end
unless dom.managed_by? user
result.message = _("You are not allowed to manage this zone.")
return result
end
srv_dns = CyborgHood::Services::DNS.new(zone)
result.ok = true
result.message = _("Requested zone content attached.")
zone_ref = {:content => srv_dns.read_zone, :filename => "dnszone_#{zone}.txt"}.to_ostruct
result.refs = [zone_ref]
end
when "SET"
return if cmdline.empty?
case cmdline.shift.upcase
when "ZONE"
return if cmdline.empty?
zone = cmdline.shift.downcase
dom = CyborgHood::DnsDomain.new(zone)
unless dom.hosted?
result.message = _("This zone is not hosted here.")
return result
end
unless dom.managed_by? user
result.message = _("You are not allowed to manage this zone.")
return result
end
srv_dns = CyborgHood::Services::DNS.new(zone)
return if cmdline.empty?
content_ref = cmdline.shift.downcase
return unless content_ref =~ /^@(\d+)$/
part_ref = $1.to_i
unless (1..refs.size).include? part_ref
result.message = _("Attachment number not found.")
return result
end
part = refs[part_ref]
unless part.content_type == "text/plain"
result.message = _("Attachment has wrong content-type.")
return result
end
f = Tempfile.new(zone)
f.write(part.body)
f.close
logger.debug "Created temporary zone file '#{f.path}'"
f = Tempfile.new(zone)
f.write(part.body)
f.close
logger.debug "Created temporary zone file '#{f.path}'"
srv_dns = CyborgHood::Services::DNS.new(zone)
current_serial = srv_dns.serial
logger.debug "Current serial: #{current_serial}"
srv_dns = CyborgHood::Services::DNS.new(zone)
current_serial = srv_dns.serial
logger.debug "Current serial: #{current_serial}"
dns_result = srv_dns.check_zone_file(f.path)
unless dns_result.ok
result.message = _("Invalid zone data.")
f.close!
return result
end
logger.debug "New serial: #{dns_result.serial}"
# allow new serial or missing serial (to allow creating a new zone or replacing a broken zone)
unless current_serial.nil? or dns_result.serial > current_serial
result.message = _("Zone serial is not superior to current serial.")
f.close!
return result
end
dns_result = srv_dns.check_zone_file(f.path)
unless dns_result.ok
result.message = _("Invalid zone data.")
f.close!
return result
end
logger.debug "New serial: #{dns_result.serial}"
# allow new serial or missing serial (to allow creating a new zone or replacing a broken zone)
unless current_serial.nil? or dns_result.serial > current_serial
result.message = _("Zone serial is not superior to current serial.")
f.close!
return result
end
begin
srv_dns.write_zone_from_file(f.path)
logger.debug "zone changed"
if srv_dns.reload_zone
logger.debug "zone reloaded"
result.ok = true
result.message = _("Zone updated.")
else
logger.warn "zone reload failed, replacing old content"
srv_dns.replace_zone_with_backup
result.message = _("Internal error. Administrator is warned.")
begin
srv_dns.write_zone_from_file(f.path)
logger.debug "zone changed"
if srv_dns.reload_zone
logger.debug "zone reloaded"
result.ok = true
result.message = _("Zone updated.")
else
logger.warn "zone reload failed, replacing old content"
srv_dns.replace_zone_with_backup
result.message = _("Internal error. Administrator is warned.")
end
rescue
logger.warn "Writing zone file failed"
raise
ensure
f.close!
end
rescue
logger.warn "Writing zone file failed"
raise
ensure
f.close!
end
end
end
end
if result.message.nil?
# here fall lost souls
nil
else
result
if result.message.nil?
# here fall lost souls
nil
else
result
end
end
end
end
end
bot = CyborgHood::Postman.new
bot = CyborgHood::PostmanHome::Postman.new
trap('INT') do
bot.ask_to_stop

Also available in: Unified diff