Project

General

Profile

Download (7.57 KB) Statistics
| Branch: | Tag: | Revision:
55a68712 Marc Dequenes
#!/usr/bin/ruby -Ku

d32ee48a Marc Dequenes
#--
# CyborgHood, a distributed system management software.
e7315259 Marc Dequènes (Duck)
# Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
d32ee48a Marc Dequenes
#
# 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/>.
#++

d4b5798a Marc Dequènes (Duck)
# to allow in-place run for test
$: << File.join(File.dirname(__FILE__), "..", "lib")
55a68712 Marc Dequenes
#require 'socket'
157c68c9 Marc Dequenes
require 'tempfile'
55a68712 Marc Dequenes
require 'shellwords'
44648f83 Marc Dequènes (Duck)
require 'cyborghood'
bc4894ce Marc Dequenes
require 'cyborghood/imap'
c427bfc7 Marc Dequenes
require 'cyborghood/mail'
cb0d68fd Marc Dequènes (Duck)
require 'cyborghood/mail_order'
7d9d434c Marc Dequènes (Duck)
require 'cyborghood/command_runner'
3f7a1eee Marc Dequenes
55a68712 Marc Dequenes
#Socket.gethostname

5f013d0a Marc Dequènes (Duck)
dfc48308 Marc Dequenes
module CyborgHood
2f1c6b5b Marc Dequènes (Duck)
# example for specific validation rules of the bot-specific config file
#class Config
# protected
# class PostmanValidator < CyborgHoodValidator
# def validate_hook_in(value, rule, path, msg_list)
# msg_list << "Youhou !!!"
# end
# end
#end

ccab26de Marc Dequènes (Duck)
module PostmanHome
ed09e1e5 Marc Dequènes (Duck)
include I18nTranslation
d4b5798a Marc Dequènes (Duck)
bindtextdomain("cyborghood_postman", {:path => Config::L10N_DIR, :charset => "UTF-8"})
5f013d0a Marc Dequènes (Duck)
ccab26de Marc Dequènes (Duck)
# not yet ready to be a real Cyborg
class Postman #< Cyborg
ed09e1e5 Marc Dequènes (Duck)
include I18nTranslation
5f013d0a Marc Dequènes (Duck)
ccab26de Marc Dequènes (Duck)
def initialize
# load config
Config.load(self.human_name.downcase)
@config = Config.instance
5f013d0a Marc Dequènes (Duck)
ccab26de Marc Dequènes (Duck)
ldap_config = @config.ldap
ldap_config.logger = logger
c8996a8d Marc Dequènes (Duck)
ActiveLdap::Base.setup_connection(ldap_config.marshal_dump)
ccab26de Marc Dequènes (Duck)
# setup logs
unless @config.log.nil?
logger.output_level(@config.log.console_level) unless @config.log.console_level.nil?
a3732b39 Marc Dequènes (Duck)
unless @config.log.path.nil?
if File.directory? @config.log.path
logger.log_to_file(File.join(@config.log.path, "ch_#{self.class.human_name}.log"))
else
logger.fatal "Log path does not exist or is not a directory, exiting"
exit 1
end
end
5f013d0a Marc Dequènes (Duck)
end
ccab26de Marc Dequènes (Duck)
2fd36b40 Marc Dequènes (Duck)
@imap = IMAP.new(@config.imap, @config.imap.min_check_interval)
ccab26de Marc Dequènes (Duck)
logger.info "Bot '#{self.human_name}' loaded"
5f013d0a Marc Dequènes (Duck)
end

ccab26de Marc Dequènes (Duck)
def run
6bc51624 Marc Dequènes (Duck)
logger.info "Bot starting"
2fd36b40 Marc Dequènes (Duck)
@imap.check_mails do |msg|
6bc51624 Marc Dequènes (Duck)
process_message(msg)
5f013d0a Marc Dequènes (Duck)
end
ccab26de Marc Dequènes (Duck)
logger.info "Bot terminating"
end
5f013d0a Marc Dequènes (Duck)
c75f6985 Marc Dequènes (Duck)
def ask_to_stop
2fd36b40 Marc Dequènes (Duck)
logger.info "Bot was asked to stop..."
@imap.stop_mail_check
c75f6985 Marc Dequènes (Duck)
end

private

6bc51624 Marc Dequènes (Duck)
def process_message(msg)
mail = Mail.new(msg.content)
logger.info "Received mail with ID '#{mail.message_id}': #{mail.from_pretty} -> #{mail.to_pretty} (#{mail.subject_pretty})"
5f013d0a Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
# 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
return true
end
ccab26de Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
logger.debug "RFC3156 content detected"
begin
report = mail.process
rescue CyberError => e
case e.severity
when :grave
logger.fatal "Fatal processing error, exiting (#{e.message})"
exit 2
when :unrecoverable
logger.error "Internal processing error, skipping mail (#{e.message})"
return true
when :processable
logger.error "Untreated processing problem, skipping mail (#{e.message})"
return true
when :ignorable
logger.warn "Internal processing warning, continuing (#{e.message})"
ccab26de Marc Dequènes (Duck)
end
6bc51624 Marc Dequènes (Duck)
end
result_tag = report.ok? ? "SUCCESS" : "FAILURE"
result_msg = "Processing result: #{result_tag}"
result_msg += " (#{report.error.untranslated})" unless report.ok?
logger.info result_msg

i18n = I18nController.instance
i18n.set_language_for_user(report.user)

unless report.ok?
if report.warn_sender
logger.info "Sending reply for rejected message"
reply_intro = report.user ? _("Hello %{cn},", :cn =>report.user.cn) : _("Hello,")
mail_reply = mail.create_simple_reject_reply(reply_intro.to_s + "\n\n" +
_("A message (ID: %{id}), apparently from you, was rejected for the following reason:",
6ed10f49 Marc Dequènes (Duck)
:id => mail.message_id).to_s + "\n " + report.error.to_s + "\n")
6bc51624 Marc Dequènes (Duck)
mail_reply.deliver
5f013d0a Marc Dequènes (Duck)
end
6bc51624 Marc Dequènes (Duck)
msg.delete
return true
end
5f013d0a Marc Dequènes (Duck)
3d7bbe55 Marc Dequènes (Duck)
mail_parser = MailOrderParser.new(report.user)
order = mail_parser.parse(report.message)
6bc51624 Marc Dequènes (Duck)
result_tag = order.valid? ? "SUCCESS" : "FAILURE"
result_msg = "Processing result: #{result_tag}"
result_msg += " (#{order.error.untranslated})" unless order.valid?
logger.info result_msg
eb6e0359 Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
reply_intro = _("Hello %{cn},", :cn => order.user.cn)
cb0d68fd Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
unless order.valid?
logger.info "Sending reply for rejected order"
mail_reply = mail.create_simple_reject_reply(reply_intro.to_s + "\n\n" +
_("An order, in a message (ID: %{id}) from you, was rejected for the following reason:",
6ed10f49 Marc Dequènes (Duck)
:id => mail.message_id).to_s + "\n " + order.error.to_s + "\n")
6bc51624 Marc Dequènes (Duck)
mail_reply.deliver
msg.delete
return true
end
cb0d68fd Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
logger.debug "Message accepted, processing orders..."
result_list = CommandRunner.run(order)

45cd6107 Marc Dequènes (Duck)
# create transcript and attachments
logger.debug "Preparing reply"
reply_txt = reply_intro.to_s + "\n\n"
reply_txt += _("Follows the transcript of your commands:").to_s + "\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
reply_txt += "\n"

6bc51624 Marc Dequènes (Duck)
# create mail
logger.debug "Preparing mail"
mail_reply = mail.create_reply
if reply_attachments.empty?
transcript_part = mail_reply
else
mail_reply.to_multipart!
5f013d0a Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
p = CyborgHood::Mail.blank
transcript_part = p
mail_reply.parts << p
5f013d0a Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
reply_attachments.each do |attachment|
8ddd43db Marc Dequènes (Duck)
p = CyborgHood::Mail.blank
6bc51624 Marc Dequènes (Duck)
p.set_content_type("text", "plain", {'charset' => "utf-8"})
p.set_disposition("attachment", {'filename' => attachment.filename})
p.quoted_printable_body = attachment.content
5f013d0a Marc Dequènes (Duck)
mail_reply.parts << p
end
6bc51624 Marc Dequènes (Duck)
end
6ed10f49 Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
# insert transcript
transcript_part.set_content_type("text", "plain", {'charset' => 'utf-8', 'format' => 'flowed'})
transcript_part.set_disposition("inline")
45cd6107 Marc Dequènes (Duck)
transcript_part.quoted_printable_body = reply_txt + mail_reply.default_body_signature
ccab26de Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
# send reply
logger.debug "Sending mail"
958e72a0 Marc Dequènes (Duck)
mail_reply.sign_and_crypt
6bc51624 Marc Dequènes (Duck)
mail_reply.deliver
ccab26de Marc Dequènes (Duck)
6bc51624 Marc Dequènes (Duck)
logger.info "Message processed completely, deleting"
msg.delete

true
5f013d0a Marc Dequènes (Duck)
end
end
55a68712 Marc Dequenes
end
end

ccab26de Marc Dequènes (Duck)
bot = CyborgHood::PostmanHome::Postman.new
55a68712 Marc Dequenes
trap('INT') do
bot.ask_to_stop
end
trap('TERM') do
bot.ask_to_stop
end

bot.run