Project

General

Profile

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

d32ee48a Marc Dequenes
#--
# CyborgHood, a distributed system management software.
4888ca24 Marc Dequènes (Duck)
# Copyright (c) 2009-2011 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
a445b722 Marc Dequènes (Duck)
require 'cyborghood'
55a68712 Marc Dequenes
5f013d0a Marc Dequènes (Duck)
dfc48308 Marc Dequenes
module CyborgHood
34bef607 Marc Dequènes (Duck)
module PostmanLand
class Postman < Cyborg
# load config before modules are included
Config.load(self.human_name)
5f013d0a Marc Dequènes (Duck)
c2f9dd87 Marc Dequènes (Duck)
inject_deferred_translations

34bef607 Marc Dequènes (Duck)
include BotNet
ccab26de Marc Dequènes (Duck)
34bef607 Marc Dequènes (Duck)
def setup
super
ccab26de Marc Dequènes (Duck)
34bef607 Marc Dequènes (Duck)
define_interface "0.1~"
5f013d0a Marc Dequènes (Duck)
end

34bef607 Marc Dequènes (Duck)
def start_work
self.services.imap.check_mails do |msg|
177a5b4f Marc Dequènes (Duck)
begin
34bef607 Marc Dequènes (Duck)
# Postman is not yet ready to process messages using the botnet
#process_message(msg)
177a5b4f Marc Dequènes (Duck)
rescue CyberError => e
raise
rescue
a1df93fe Marc Dequènes (Duck)
logger.error "Mail processing crashed unexpectedly: " + $!
logger.error "Crash class: " + $!.class.to_s
177a5b4f Marc Dequènes (Duck)
logger.error "Crash trace: " + $!.backtrace.join("\n")
true
end
5f013d0a Marc Dequènes (Duck)
end
a1df93fe Marc Dequènes (Duck)
rescue
logger.error "IMAP processing crashed unexpectedly: " + $!
logger.error "Crash class: " + $!.class.to_s
logger.error "Crash trace: " + $!.backtrace.join("\n")
ccab26de Marc Dequènes (Duck)
end
5f013d0a Marc Dequènes (Duck)
a089227c Marc Dequènes (Duck)
def stop(condition)
34bef607 Marc Dequènes (Duck)
super do
self.services.imap.stop_mail_check
end
end

a089227c Marc Dequènes (Duck)
protected

34bef607 Marc Dequènes (Duck)
def ready_to_stop?
a089227c Marc Dequènes (Duck)
# TODO: also wait for messages being processed to be finished (or try to interrupt cleanly if possible in :quickly condition)
34bef607 Marc Dequènes (Duck)
super and not self.services.imap.connected?
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

34bef607 Marc Dequènes (Duck)
reg = Needle::Registry.new
reg.define do |b|
b.require 'cyborghood', CyborgHood
b.require 'cyborghood-postman/land', CyborgHood::PostmanLand

b.bot { CyborgHood::PostmanLand::Postman.new(b.postman_land) }
end

bot = reg.bot
55a68712 Marc Dequenes
trap('INT') do
a089227c Marc Dequènes (Duck)
bot.stop(:quickly)
55a68712 Marc Dequenes
end
trap('TERM') do
a089227c Marc Dequènes (Duck)
bot.stop(:quickly)
55a68712 Marc Dequenes
end

bot.run