commit cb0d68fd2c857faea5a05f374b2bb4464a1191fd
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Tue Mar 10 01:29:09 2009 +0100

    [evol] commands management rework #2: moved command parsing out of the Mail class and created new classes to handle it

diff --git a/bin/postman b/bin/postman
index 563345a..74ca813 100755
--- a/bin/postman
+++ b/bin/postman
@@ -26,9 +26,9 @@ require 'tempfile'
 require 'shellwords'
 require 'cyborghood/imap'
 require 'cyborghood/mail'
+require 'cyborghood/mail_order'
 require 'cyborghood/objects'
 require 'cyborghood/services/dns'
-require 'fileutils'
 
 #Socket.gethostname
 
@@ -115,11 +115,11 @@ module CyborgHood
 
           logger.debug "RFC3156 content detected"
           begin
-            order = mail.parse
+            report = mail.process
           rescue CyberError => e
             case e.severity
             when :dangerous
-              logger.fatal " (#{e.message})"
+              logger.fatal "Fatal processing error, exiting (#{e.message})"
               exit 2
             when :unrecoverable
               logger.error "Internal processing error, skipping mail (#{e.message})"
@@ -127,14 +127,13 @@ module CyborgHood
             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)
+          result_tag = report.ok? ? "SUCCESS" : "FAILURE"
+          logger.info "Processing result: #{result_tag} (#{report.error})"
+
+          if report.user
+            if report.user.preferredLanguage
+              logger.debug "User preference for langage: " + report.user.preferredLanguage
+              set_locale(report.user.preferredLanguage)
             else
               logger.debug "No user preference for langage, using english"
               set_locale("en")
@@ -143,25 +142,39 @@ module CyborgHood
             set_locale("en")
           end
 
-          unless order.ok
-            if order.warn_sender
+          unless report.ok?
+            if report.warn_sender
               logger.info "Sending reply for rejected message"
-              mail_reply = mail.create_simple_reject_reply(_("Hello,") + "\n\n" + sprintf(
+              intro = report.user ? sprintf(_("Hello %s,"), report.user.cn) : _("Hello,")
+              mail_reply = mail.create_simple_reject_reply(intro + "\n\n" + sprintf(
                            _("A message (ID: %s), apparently from you, was rejected for the following reason:"),
-                           mail.message_id) + "\n  " + _(order.message) + "\n" + mail_signature())
+                           mail.message_id) + "\n  " + _(report.error) + "\n" + mail_signature())
               mail_reply.deliver
             end
             msg.delete
             next
           end
 
+          order = MailOrder.parse(report.user, report.message)
+          result_tag = order.valid? ? "SUCCESS" : "FAILURE"
+          logger.info "Processing result: #{result_tag} (#{order.error})"
+
+          unless order.valid?
+            logger.info "Sending reply for rejected order"
+            mail_reply = mail.create_simple_reject_reply(sprintf(_("Hello %s,"), order.user.cn) + "\n\n" +
+                         sprintf(_("An order, in a message (ID: %s) from you, was rejected for the following reason:"),
+                         mail.message_id) + "\n  " + _(order.error) + "\n" + mail_signature())
+            mail_reply.deliver
+            msg.delete
+            next
+          end
+
           logger.debug "Message accepted, processing orders..."
           result_list = CommandRunner.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|
@@ -223,33 +236,42 @@ module CyborgHood
       def self.run(order)
         result_list = []
         order.commands.each do |cmd|
-          logger.info "Executing command: #{cmd.cmdline}"
-          begin
-            result = execute_cmd(order.user, cmd.cmdsplit, order.shared_params)
-            if result.nil?
+          if cmd.valid?
+            logger.info "Executing command: #{cmd.cmdline}"
+            begin
+              result = execute_cmd(order.user, cmd.cmdsplit, order.shared_parameters)
+              if result.nil?
+                result = OpenStruct.new
+                result.cmd = cmd.cmdline
+                result.ok = false
+                result.message = _("Command not recognized.")
+                result.refs = nil
+              end
+            rescue CyberError => e
+              result = OpenStruct.new
+              result.cmd = cmd.cmdline
+              result.ok = false
+              result.message = e.message.capitalize + "."
+              result.refs = nil
+            rescue
+              logger.warn "Command crashed: " + $!
               result = OpenStruct.new
               result.cmd = cmd.cmdline
               result.ok = false
-              result.message = _("Command not recognized.")
+              result.message = _("Internal error. Administrator is warned.")
               result.refs = nil
             end
-          rescue CyberError => e
-            result = OpenStruct.new
-            result.cmd = cmd.cmdline
-            result.ok = false
-            result.message = e.message.capitalize + "."
-            result.refs = nil
-          rescue
-            logger.warn "Command crashed: " + $!
+
+            tag = result.ok ? "SUCCESS" :"FAILURE"
+            logger.debug "Command result: [#{tag}] #{result.message}"
+          else
+            logger.info "Detected invalid command: #{cmd.cmdline}"
             result = OpenStruct.new
             result.cmd = cmd.cmdline
             result.ok = false
-            result.message = _("Internal error. Administrator is warned.")
+            result.message = cmd.parsing_errors.collect{|err| _(err) }.join("\n")
             result.refs = nil
           end
-
-          tag = result.ok ? "SUCCESS" :"FAILURE"
-          logger.debug "Command result: [#{tag}] #{result.message}"
           result_list << result
         end
         result_list
@@ -257,7 +279,7 @@ module CyborgHood
 
       private
 
-      def self.execute_cmd(user, cmdline, shared_params)
+      def self.execute_cmd(user, cmdline, shared_parameters)
         subsys = cmdline.shift
 
         result = OpenStruct.new
@@ -316,11 +338,7 @@ module CyborgHood
 
               return if cmdline.empty?
               content_ref = cmdline.shift
-              if content_ref.nil?
-                result.message = _("Attachment number not found.")
-                return result
-              end
-              part = shared_params[content_ref]
+              part = shared_parameters[content_ref]
               unless part.type == "text/plain"
                 result.message = _("Attachment has wrong content-type.")
                 return result
diff --git a/lib/cyborghood/base.rb b/lib/cyborghood/base.rb
index a516239..fef38b8 100644
--- a/lib/cyborghood/base.rb
+++ b/lib/cyborghood/base.rb
@@ -30,13 +30,14 @@ module CyborgHood
   include GetText
   bindtextdomain("cyborghood", {:path => Config::L10N_DIR, :charset => "UTF-8"})
 
-  # severities: # :dangerous :unrecoverable :ignorable
+  # severities: :dangerous :unrecoverable :processable :ignorable
   # categories:
-  #  - db
+  #  - db (not a protocol?)
   #    + ldap
   #    + sql
   #  - protocol
   #    + imap
+  #    + mail
   #  - service
   #    + dns
   class CyberError < StandardError
diff --git a/lib/cyborghood/mail.rb b/lib/cyborghood/mail.rb
index e548c07..f7a21a8 100644
--- a/lib/cyborghood/mail.rb
+++ b/lib/cyborghood/mail.rb
@@ -35,43 +35,23 @@ require 'fileutils'
 require 'digest/md5'
 
 
-# This class handles RFC3156 signed messages, validates them, and extract orders properly.
-# Encrypted content are not implemented yet.
+# This class handles RFC3156 signed/encrypted messages, validates them, and extract content properly.
+# It also implements a protection against replay attacks.
 module CyborgHood
-  class Command
-    attr_reader :cmdline, :cmdsplit
+  class MailReport
+    attr_reader :error, :warn_sender, :user, :message
 
-    def initialize(cmdline, cmdsplit)
-      @cmdline = cmdline
-      @cmdsplit = cmdsplit
-    end
-  end
-
-  class SharedParameter
-    attr_reader :type, :content
+    def initialize(params = {})
+      @error = params[:error]
+      @warn_sender = params[:warn_sender]
+      @user = params[:user]
+      @message = params[:message]
 
-    def initialize(content, type = nil)
-      @content = content
-      @type = type
+      @warn_sender = false
     end
-  end
-
-  class ParameterReference
-    attr_reader :reference
-
-    def initialize(reference)
-      @reference = reference
-    end
-  end
-
-  class Order
-    attr_accessor :ok, :message, :system_message, :user, :commands, :shared_parameters
-    attr_writer = :warn_sender
 
-    def initialize(ok, message = nil, system_message = nil)
-      @ok = ok
-      @message = message
-      @system_message = system_message
+    def ok?
+      @error.nil? and @user and @message
     end
 
     def warn_sender
@@ -108,17 +88,15 @@ module CyborgHood
       @mail
     end
 
-    def parse
+    def process
       if is_marked?
-        return Order.new(false, "Replay detected.")
+        return MailReport.new(:error => "Replay detected.")
       end
 
-      return parse_signed() if is_pgp_signed?
-      return parse_encrypted() if is_pgp_encrypted?
-      # don't parse commands if user is not identified
-      return parse_plain if self.user
+      return process_signed() if is_pgp_signed?
+      return process_encrypted() if is_pgp_encrypted?
 
-      Order.new(false, "Mail not RFC3156 compliant.")
+      MailReport.new(:error => "Mail not RFC3156 compliant.")
     end
 
     def create_reply
@@ -191,94 +169,16 @@ module CyborgHood
 
     private
 
-    def parse_plain
-      command_txt = nil
-      shared_params = nil
-      if multipart?
-        if parts[0].content_type == "text/plain"
-          command_txt = self.parts[0].body
-          shared_params = {}
-          i = 1
-          self.parts.each do |p|
-            shared_params[i] = SharedParameter.new(p.body, p.content_type)
-            filename = p.header['content-type'].params('filename')
-            shared_params[filename] = ParameterReference.new(i) if filename
-            i += 1
-          end
-        end
-      else
-        command_txt = self.body if self.content_type == "text/plain"
-        shared_params = {}
-      end
-      unless command_txt
-        order = Order.new(false, N_("Mail does not contain a proper text part for commands."))
-        order.user = self.user
-        return order
-      end
-
-      commands = []
-      command_txt.each_line do |line|
-        line.chomp!
-        sline = line.strip
-        # skip empty lines and comments
-        next if sline == "" or sline[0, 1] == "#"
-        # stop processing when detecting message signature
-        break if line == "-- "
-
-        used_refs = []
-        cmd_parts = sline.shellsplit.collect do |word|
-          if =~ /^@([a-zA-Z0-9._-]+)$/
-            ref = $1
-            d_ref, d_param = dereference_param(shared_params, param)
-            # TODO: should add error message for attachment not found in the Command
-            used_refs << d_ref
-            d_param
-          else
-            word
-          end
-        end
-
-        commands << Command.new(sline, cmd_parts)
-      end
-
-      shared_params.delete_if{|ref, param| not used_refs.include?(ref) }
-
-      logger.debug "Mail OK"
-      mark_processed(self.signature_timestamp)
-
-      order = Order.new(true)
-      order.user = self.user
-      order.commands = commands
-      order.references = shared_params
-      order
-    end
-
-    def dereference_param(shared_params, param)
-      if param.is_a? SharedParameter
-        [ref, ParameterReference(ref)]
-      elsif param.is_a? ParameterReference
-        d_ref = param.reference
-        d_param = shared_params[d_ref]
-        return dereference_param(shared_params, d_param)
-      else
-        nil
-      end
-    end
-
-    def parse_signed
+    def process_signed
       sigs_check = verify_pgp_signature()
-      return Order.new(false, N_("Mail not formatted correctly (signed part).")) if sigs_check.nil? or sigs_check.size != 1
+      return MailReport.new(:error => "Mail not formatted correctly (signed part).") if sigs_check.nil? or sigs_check.size != 1
 
       sig_check = sigs_check.first
-      return Order.new(false, N_("Mail content tampered or badly signed: ") + sig_check.to_s) unless sig_check.status == 0
+      return MailReport.new(:error => "Mail content tampered or badly signed: " + sig_check.to_s) unless sig_check.status == 0
 
       logger.info "Mail content was properly signed by key #{sig_check.fingerprint}"
       user = Person.find_by_fingerprint(sig_check.fingerprint)
-      if user.nil?
-        order = Order.new(false, N_("Mail is from an unknown person."))
-        order.warn_sender = true
-        return order
-      end
+      return MailReport.new(:error => "Mail is from an unknown person.", :warn_sender => true) if user.nil?
 
       logger.info "Mail is from user #{user.uid} (#{user.cn})"
       self.user = user
@@ -287,17 +187,17 @@ module CyborgHood
       logger.debug "Signature drift time: #{drift}"
       unless drift.abs < MAX_DRIFT_TIME
         if drift > 0
-          order = Order.new(false, N_("The signature was made too long ago (check your system clock). Rejected message to avoid replay attacks."))
-          order.user = self.user
+          return MailReport.new(:error => N_("The signature was made too long ago (check your system clock). Rejected message to avoid replay attacks."), :user => user)
         else
           # mark message to prevent later replay of the message
           mark_processed(sig_check.timestamp)
-          order = Order.new(false, N_("The signature was made in the future (check your system clock). Rejected message to avoid replay attacks."))
-          order.user = self.user
+          return MailReport.new(:error => N_("The signature was made in the future (check your system clock). Rejected message to avoid replay attacks."), :user => user)
         end
-        return order
       end
 
+      logger.debug "Mail OK"
+      mark_processed(self.signature_timestamp)
+
       signed_content = pgp_signed_part()
 
       # create a fake mail and chain parsing operations
@@ -306,10 +206,11 @@ module CyborgHood
       plain_mail.signature_timestamp = sig_check.timestamp
       # propagate message_id to be able to mark messages (replay protection)
       plain_mail.message_id = @mail.message_id
-      return plain_mail.parse
+
+      MailReport.new(:user => user, :message => plain_mail)
     end
 
-    def parse_encrypted
+    def process_encrypted
       catch :notforme do
         begin
           # block is not passed to delegate (limitation ?)
@@ -329,13 +230,13 @@ module CyborgHood
           clear_mail.user = self.user
           # propagate message_id to be able to mark messages (replay protection)
           clear_mail.message_id = @mail.message_id
-          return clear_mail.parse
+          return clear_mail.process
         rescue GPGME::Error, NotImplementedError => e
           raise CyberError.new(:unrecoverable, "protocol/mail", e.message)
         end
       end
 
-      Order.new(false, N_("Mail not formatted correctly (encrypted part)."))
+      MailReport.new(:error => "Mail not formatted correctly (encrypted part).")
     end
 
     def mark_dir
diff --git a/lib/cyborghood/mail_order.rb b/lib/cyborghood/mail_order.rb
new file mode 100644
index 0000000..4190f5e
--- /dev/null
+++ b/lib/cyborghood/mail_order.rb
@@ -0,0 +1,50 @@
+require 'cyborghood/order'
+
+module CyborgHood
+  class MailOrder < Order
+    def self.parse(user, message)
+      logger.debug "Parsing Mail"
+      order_txt = nil
+      shared_parameters = nil
+      if message.multipart?
+        if message.parts[0].content_type == "text/plain"
+          order_txt = message.parts[0].body
+          shared_parameters = {}
+          i = 1
+          message.parts.each do |p|
+            shared_parameters[i] = SharedParameter.new(p.body, p.content_type)
+            filename = p.header['content-type'].params('filename')
+            shared_parameters[filename] = ParameterReference.new(i) if filename
+            i += 1
+          end
+        end
+      else
+        order_txt = message.body if message.content_type == "text/plain"
+        shared_parameters = {}
+      end
+      return new(:error => N_("Mail does not contain a proper text part for commands."), :user => user) if order_txt.nil?
+
+      command_lines = order_txt.split("\n")
+
+      # remove message signature
+      signature_pos = command_lines.index("-- ")
+      command_lines.slice!(signature_pos..-1) if signature_pos
+
+      super(user, command_lines, shared_parameters) do
+        if word =~ /^@([a-zA-Z0-9._-]+)$/
+          ref = $1
+          d_ref, d_param = dereference_param(shared_parameters, param)
+          if d_ref.nil?
+            # TODO: a message class is needed to handle passing translatable strings with parameters (doing late sprintf for example)
+            errors << N_("Attachment '#{ref}' not found.")
+          else
+            used_refs << d_ref
+          end
+          d_param
+        else
+          word
+        end
+      end
+    end
+  end
+end
diff --git a/lib/cyborghood/order.rb b/lib/cyborghood/order.rb
new file mode 100644
index 0000000..971ac08
--- /dev/null
+++ b/lib/cyborghood/order.rb
@@ -0,0 +1,83 @@
+module CyborgHood
+  class Command
+    attr_reader :cmdline, :cmdsplit, :parsing_errors
+
+    def initialize(cmdline, cmdsplit, parsing_errors = [])
+      @cmdline = cmdline
+      @cmdsplit = cmdsplit
+      @parsing_errors = parsing_errors
+    end
+
+    def valid?
+      @parsing_errors.empty?
+    end
+  end
+
+  class SharedParameter
+    attr_reader :type, :content
+
+    def initialize(content, type = nil)
+      @content = content
+      @type = type
+    end
+  end
+
+  class ParameterReference
+    attr_reader :reference
+
+    def initialize(reference)
+      @reference = reference
+    end
+  end
+
+  class Order
+    attr_reader :error, :user, :commands, :shared_parameters
+
+    def initialize(params = {})
+      @error = params[:error]
+      @user = params[:user]
+      @commands = params[:commands]
+      @shared_parameters = params[:shared_parameters]
+    end
+
+    def self.parse(user, command_lines, shared_parameters)
+      logger.debug "Parsing Order"
+      used_refs = []
+      commands = []
+      command_lines.each do |line|
+        line.strip!
+        # skip empty lines and comments
+        next if line == "" or line[0, 1] == "#"
+
+        errors = []
+        cmd_parts = line.shellsplit.collect do |word, errors, used_refs|
+          yield word
+        end
+
+        commands << Command.new(line, cmd_parts, errors)
+      end
+
+      # remove references to unused parameters
+      shared_parameters.delete_if{|ref, param| not used_refs.include?(ref) }
+
+      logger.debug "Order is OK"
+      new(:user => @user, :commands => commands, :shared_parameters => shared_parameters)
+    end
+
+    def dereference_param(shared_parameters, param)
+      if param.is_a? SharedParameter
+        [ref, ParameterReference(ref)]
+      elsif param.is_a? ParameterReference
+        d_ref = param.reference
+        d_param = shared_parameters[d_ref]
+        return dereference_param(shared_parameters, d_param)
+      else
+        nil
+      end
+    end
+
+    def valid?
+       @error.nil? and @user and not @commands.empty?
+    end
+  end
+end
diff --git a/po/cyborghood.pot b/po/cyborghood.pot
index 23f98c7..4ab89be 100644
--- a/po/cyborghood.pot
+++ b/po/cyborghood.pot
@@ -7,7 +7,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: CyborgHood 0.2.0~dev\n"
-"POT-Creation-Date: 2009-03-08 19:14+0100\n"
+"POT-Creation-Date: 2009-03-10 01:23+0100\n"
 "PO-Revision-Date: 2009-03-07 21:38+0100\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,34 +16,18 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
-#: lib/cyborghood/mail.rb:181
-msgid "Mail does not contain a proper text part for commands."
-msgstr ""
-
-#: lib/cyborghood/mail.rb:210
-msgid "Mail not formatted correctly (signed part)."
-msgstr ""
-
-#: lib/cyborghood/mail.rb:213
-msgid "Mail content tampered or badly signed: "
-msgstr ""
-
-#: lib/cyborghood/mail.rb:218
-msgid "Mail is from an unknown person."
-msgstr ""
-
-#: lib/cyborghood/mail.rb:230
+#: lib/cyborghood/mail.rb:190
 msgid ""
 "The signature was made too long ago (check your system clock). Rejected "
 "message to avoid replay attacks."
 msgstr ""
 
-#: lib/cyborghood/mail.rb:235
+#: lib/cyborghood/mail.rb:194
 msgid ""
 "The signature was made in the future (check your system clock). Rejected "
 "message to avoid replay attacks."
 msgstr ""
 
-#: lib/cyborghood/mail.rb:278
-msgid "Mail not formatted correctly (encrypted part)."
+#: lib/cyborghood/mail_order.rb:25
+msgid "Mail does not contain a proper text part for commands."
 msgstr ""
diff --git a/po/cyborghood_postman.pot b/po/cyborghood_postman.pot
index 1dea500..04cff39 100644
--- a/po/cyborghood_postman.pot
+++ b/po/cyborghood_postman.pot
@@ -7,7 +7,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: CyborgHood 0.2.0~dev\n"
-"POT-Creation-Date: 2009-03-08 19:14+0100\n"
+"POT-Creation-Date: 2009-03-10 01:23+0100\n"
 "PO-Revision-Date: 2009-03-07 21:38+0100\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,7 +16,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
-#: /opt/cyborghood-dev/bin/postman:149
+#: /opt/cyborghood-dev/bin/postman:148 /opt/cyborghood-dev/bin/postman:164
+#: /opt/cyborghood-dev/bin/postman:177
+msgid "Hello %s,"
+msgstr ""
+
+#: /opt/cyborghood-dev/bin/postman:148
 msgid "Hello,"
 msgstr ""
 
@@ -26,62 +31,60 @@ msgid ""
 "reason:"
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:163
-msgid "Hello %s,"
+#: /opt/cyborghood-dev/bin/postman:165
+msgid ""
+"An order, in a message (ID: %s) from you, was rejected for the following "
+"reason:"
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:165
+#: /opt/cyborghood-dev/bin/postman:178
 msgid "Follows the transcript of your commands:"
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:214
+#: /opt/cyborghood-dev/bin/postman:227
 msgid "Contact eMail:"
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:215
+#: /opt/cyborghood-dev/bin/postman:228
 msgid "Contact URL:"
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:233
+#: /opt/cyborghood-dev/bin/postman:247
 msgid "Command not recognized."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:247 /opt/cyborghood-dev/bin/postman:365
+#: /opt/cyborghood-dev/bin/postman:261 /opt/cyborghood-dev/bin/postman:380
 msgid "Internal error. Administrator is warned."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:277
+#: /opt/cyborghood-dev/bin/postman:298
 msgid "You are manager of the following zones: %s."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:291 /opt/cyborghood-dev/bin/postman:313
+#: /opt/cyborghood-dev/bin/postman:312 /opt/cyborghood-dev/bin/postman:334
 msgid "You are not allowed to manage this zone."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:297
+#: /opt/cyborghood-dev/bin/postman:318
 msgid "Requested zone content attached."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:309
+#: /opt/cyborghood-dev/bin/postman:330
 msgid "This zone is not hosted here."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:323
-msgid "Attachment number not found."
-msgstr ""
-
-#: /opt/cyborghood-dev/bin/postman:328
+#: /opt/cyborghood-dev/bin/postman:343
 msgid "Attachment has wrong content-type."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:343
+#: /opt/cyborghood-dev/bin/postman:358
 msgid "Invalid zone data."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:350
+#: /opt/cyborghood-dev/bin/postman:365
 msgid "Zone serial is not superior to current serial."
 msgstr ""
 
-#: /opt/cyborghood-dev/bin/postman:361
+#: /opt/cyborghood-dev/bin/postman:376
 msgid "Zone updated."
 msgstr ""
diff --git a/po/fr/cyborghood.po b/po/fr/cyborghood.po
index 4359831..aa6e0d2 100644
--- a/po/fr/cyborghood.po
+++ b/po/fr/cyborghood.po
@@ -7,7 +7,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: CyborgHood 0.2.0~dev\n"
-"POT-Creation-Date: 2009-03-08 19:14+0100\n"
+"POT-Creation-Date: 2009-03-10 01:23+0100\n"
 "PO-Revision-Date: 2009-03-07 20:59+0100\n"
 "Last-Translator: Marc Dequènes (Duck) <Duck@DuckCorp.org>\n"
 "MIME-Version: 1.0\n"
@@ -15,24 +15,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=(n != 1);\n"
 
-#: lib/cyborghood/mail.rb:181
-msgid "Mail does not contain a proper text part for commands."
-msgstr ""
-"Le mail ne contient pas de partie textuelle correcte pour les commandes."
-
-#: lib/cyborghood/mail.rb:210
-msgid "Mail not formatted correctly (signed part)."
-msgstr "Le mail n'est pas formatté correctement (partie signée)."
-
-#: lib/cyborghood/mail.rb:213
-msgid "Mail content tampered or badly signed: "
-msgstr "Le contenu du mail a été altéré ou n'a pas été signé correctement."
-
-#: lib/cyborghood/mail.rb:218
-msgid "Mail is from an unknown person."
-msgstr "Le mail provient d'une personne inconnue."
-
-#: lib/cyborghood/mail.rb:230
+#: lib/cyborghood/mail.rb:190
 msgid ""
 "The signature was made too long ago (check your system clock). Rejected "
 "message to avoid replay attacks."
@@ -40,7 +23,7 @@ msgstr ""
 "La signature a été faite depuis trop longtemps (vérifiez votre horloge "
 "système. Message rejeté pour éviter les attaques par rejeux."
 
-#: lib/cyborghood/mail.rb:235
+#: lib/cyborghood/mail.rb:194
 msgid ""
 "The signature was made in the future (check your system clock). Rejected "
 "message to avoid replay attacks."
@@ -48,6 +31,7 @@ msgstr ""
 "La signature a été faite dans le futur (vérifiez votre horloge système. "
 "Message rejeté pour éviter les attaques par rejeux."
 
-#: lib/cyborghood/mail.rb:278
-msgid "Mail not formatted correctly (encrypted part)."
-msgstr "Le mail n'est pas formatté correctement (partie chiffrée)."
+#: lib/cyborghood/mail_order.rb:25
+msgid "Mail does not contain a proper text part for commands."
+msgstr ""
+"Le mail ne contient pas de partie textuelle correcte pour les commandes."
diff --git a/po/fr/cyborghood_postman.po b/po/fr/cyborghood_postman.po
index daa8314..390fde3 100644
--- a/po/fr/cyborghood_postman.po
+++ b/po/fr/cyborghood_postman.po
@@ -7,7 +7,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: CyborgHood 0.2.0~dev\n"
-"POT-Creation-Date: 2009-03-08 19:14+0100\n"
+"POT-Creation-Date: 2009-03-10 01:23+0100\n"
 "PO-Revision-Date: 2009-03-07 21:27+0100\n"
 "Last-Translator: Marc Dequènes (Duck) <Duck@DuckCorp.org>\n"
 "MIME-Version: 1.0\n"
@@ -15,7 +15,12 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=(n != 1);\n"
 
-#: /opt/cyborghood-dev/bin/postman:149
+#: /opt/cyborghood-dev/bin/postman:148 /opt/cyborghood-dev/bin/postman:164
+#: /opt/cyborghood-dev/bin/postman:177
+msgid "Hello %s,"
+msgstr "Bonjour %s,"
+
+#: /opt/cyborghood-dev/bin/postman:148
 msgid "Hello,"
 msgstr "Bonjour,"
 
@@ -27,62 +32,62 @@ msgstr ""
 "Un message (ID: %s), provenant apparemment de vous, a été rejeté pour la "
 "raison suivante :"
 
-#: /opt/cyborghood-dev/bin/postman:163
-msgid "Hello %s,"
-msgstr "Bonjour %s,"
-
 #: /opt/cyborghood-dev/bin/postman:165
+msgid ""
+"An order, in a message (ID: %s) from you, was rejected for the following "
+"reason:"
+msgstr ""
+"Un order, dans in message (ID: %s) venant de vous, a été rejeté pour la "
+"raison suivante :"
+
+#: /opt/cyborghood-dev/bin/postman:178
 msgid "Follows the transcript of your commands:"
 msgstr "La retranscription des commandes suit :"
 
-#: /opt/cyborghood-dev/bin/postman:214
+#: /opt/cyborghood-dev/bin/postman:227
 msgid "Contact eMail:"
 msgstr "eMail de Contact :"
 
-#: /opt/cyborghood-dev/bin/postman:215
+#: /opt/cyborghood-dev/bin/postman:228
 msgid "Contact URL:"
 msgstr "URL de Contact :"
 
-#: /opt/cyborghood-dev/bin/postman:233
+#: /opt/cyborghood-dev/bin/postman:247
 msgid "Command not recognized."
 msgstr "Commande non reconnue"
 
-#: /opt/cyborghood-dev/bin/postman:247 /opt/cyborghood-dev/bin/postman:365
+#: /opt/cyborghood-dev/bin/postman:261 /opt/cyborghood-dev/bin/postman:380
 msgid "Internal error. Administrator is warned."
 msgstr "Erreur interne. L'administrateur a été prévenu."
 
-#: /opt/cyborghood-dev/bin/postman:277
+#: /opt/cyborghood-dev/bin/postman:298
 msgid "You are manager of the following zones: %s."
 msgstr "Vous gérez les zones suivantes : %s."
 
-#: /opt/cyborghood-dev/bin/postman:291 /opt/cyborghood-dev/bin/postman:313
+#: /opt/cyborghood-dev/bin/postman:312 /opt/cyborghood-dev/bin/postman:334
 msgid "You are not allowed to manage this zone."
 msgstr "Vous n'êtes pas autorisé à gérer cette zone."
 
-#: /opt/cyborghood-dev/bin/postman:297
+#: /opt/cyborghood-dev/bin/postman:318
 msgid "Requested zone content attached."
 msgstr "Le contenu de la zone demandée est en pièce jointe."
 
-#: /opt/cyborghood-dev/bin/postman:309
+#: /opt/cyborghood-dev/bin/postman:330
 msgid "This zone is not hosted here."
 msgstr "Cette zone n'est pas hébergée ici."
 
-#: /opt/cyborghood-dev/bin/postman:323
-msgid "Attachment number not found."
-msgstr "Numéro de pièce jointe non trouvé."
-
-#: /opt/cyborghood-dev/bin/postman:328
+#: /opt/cyborghood-dev/bin/postman:343
 msgid "Attachment has wrong content-type."
 msgstr "La pièce jointe a un mauvais type de contenu (content-type)."
 
-#: /opt/cyborghood-dev/bin/postman:343
+#: /opt/cyborghood-dev/bin/postman:358
 msgid "Invalid zone data."
 msgstr "Données pour la zone invalides."
 
-#: /opt/cyborghood-dev/bin/postman:350
+#: /opt/cyborghood-dev/bin/postman:365
 msgid "Zone serial is not superior to current serial."
 msgstr "Le numéro de série de la zone n'est pas supèrieur à celui actuel."
 
-#: /opt/cyborghood-dev/bin/postman:361
+#: /opt/cyborghood-dev/bin/postman:376
 msgid "Zone updated."
 msgstr "Zone mise à jour."
