commit 7cae0102718dcd9e194e04ea551c5759700b331d
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sat Mar 7 21:58:06 2009 +0100

    [evol] added gettext support using user prefered langage

diff --git a/.gitignore b/.gitignore
index a37de5b..e1344ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
 config
 log
 var
+locale
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..5139f50
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,28 @@
+Dir.chdir(File.dirname(File.expand_path(__FILE__)))
+$: << "./lib"
+
+ENV['LC_ALL'] = "C"
+
+require 'rake'
+require 'gettext/utils'
+require 'lib/cyborghood/base.rb'
+
+namespace :i18n do
+desc "Create mo-files for l10n"
+  task :makemo do
+    GetText.create_mofiles(true, "po", "locale")
+  end
+
+  desc "Update pot/po files to match new version."
+  task :updatepo do
+    GetText.update_pofiles(CyborgHood::PRODUCT,
+      Dir.glob("lib/**/*.{rb,rhtml}"),
+      CyborgHood::PRODUCT + " " + CyborgHood::VERSION)
+    # temporary component listing
+    ["Postman"].each do |component|
+      GetText.update_pofiles(CyborgHood::PRODUCT + "_" + component,
+        component.downcase,
+        CyborgHood::PRODUCT + " " + CyborgHood::VERSION)
+    end
+  end
+end
diff --git a/TODO b/TODO
index 0c29326..6e8325c 100644
--- a/TODO
+++ b/TODO
@@ -1,2 +1,3 @@
+- split libs into common and per component and adapt translations accordingly
 - ban keys from unknow users flooding -> counter, reseted when key added in DB
 - protect against intercepted mail with falsified headers (From/Reply-To/... could be tampered to get replies, reply tampered too, and then resent to avoid being detected)
diff --git a/lib/cyborghood/base.rb b/lib/cyborghood/base.rb
index 4c4481f..a3175c7 100644
--- a/lib/cyborghood/base.rb
+++ b/lib/cyborghood/base.rb
@@ -22,12 +22,18 @@ require 'singleton'
 require 'yaml'
 require 'log4r'
 require "cyborghood/lang_additions"
+require 'gettext'
 
 module CyborgHood
+  include GetText
 
   PRODUCT = "CyborgHood"
   VERSION = "0.2.0~dev"
 
+  bindtextdomain(PRODUCT)
+  bindtextdomain(PRODUCT + "_" + self.human_name)
+  textdomain(PRODUCT + "_" + self.human_name)
+
   # severities: # :dangerous :unrecoverable :ignorable
   # categories:
   #  - db
diff --git a/lib/cyborghood/cyborg.rb b/lib/cyborghood/cyborg.rb
old mode 100755
new mode 100644
diff --git a/lib/cyborghood/mail.rb b/lib/cyborghood/mail.rb
index bff5595..f453067 100644
--- a/lib/cyborghood/mail.rb
+++ b/lib/cyborghood/mail.rb
@@ -174,7 +174,7 @@ module CyborgHood
         refs = []
       end
       unless command_txt
-        order = Order.new(false, "Mail does not contain a proper text part for commands.")
+        order = Order.new(false, N_("Mail does not contain a proper text part for commands."))
         order.user = self.user
         return order
       end
@@ -202,15 +202,15 @@ module CyborgHood
 
     def parse_signed
       sigs_check = verify_pgp_signature()
-      return Order.new(false, "mail not formatted correctly (signed part)") if sigs_check.nil? or sigs_check.size != 1
+      return Order.new(false, N_("Mail not formatted correctly (signed part).")) if sigs_check.nil? or sigs_check.size != 1
 
       sig_check = sigs_check.first
-      return Order.new(false, "Mail content tampered or badly signed: " + sig_check.to_s) unless sig_check.status == 0
+      return Order.new(false, N_("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, "Mail is from an unknown person.")
+        order = Order.new(false, N_("Mail is from an unknown person."))
         order.warn_sender = true
         return order
       end
@@ -222,14 +222,12 @@ module CyborgHood
       logger.debug "Signature drift time: #{drift}"
       unless drift.abs < MAX_DRIFT_TIME
         if drift > 0
-          order = Order.new(false, "The signature was made too long ago (check your system clock)." +
-                            " Rejected message to avoid replay attacks.")
+          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
         else
           # mark message to prevent later replay of the message
           mark_processed(sig_check.timestamp)
-          order = Order.new(false, "The signature was made in the future (check your system clock)." +
-                            " Rejected message to avoid replay attacks.")
+          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
         end
         return order
@@ -271,7 +269,7 @@ module CyborgHood
         end
       end
 
-      Order.new(false, "Mail not formatted correctly (encrypted part).")
+      Order.new(false, N_("Mail not formatted correctly (encrypted part)."))
     end
 
     def mark_dir
diff --git a/po/CyborgHood.pot b/po/CyborgHood.pot
new file mode 100644
index 0000000..b9cb6f4
--- /dev/null
+++ b/po/CyborgHood.pot
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: CyborgHood 0.2.0~dev\n"
+"POT-Creation-Date: 2009-03-07 21:55+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"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: lib/cyborghood/mail.rb:177
+msgid "Mail does not contain a proper text part for commands."
+msgstr ""
+
+#: lib/cyborghood/mail.rb:205
+msgid "Mail not formatted correctly (signed part)."
+msgstr ""
+
+#: lib/cyborghood/mail.rb:208
+msgid "Mail content tampered or badly signed: "
+msgstr ""
+
+#: lib/cyborghood/mail.rb:213
+msgid "Mail is from an unknown person."
+msgstr ""
+
+#: lib/cyborghood/mail.rb:225
+msgid ""
+"The signature was made too long ago (check your system clock). Rejected "
+"message to avoid replay attacks."
+msgstr ""
+
+#: lib/cyborghood/mail.rb:230
+msgid ""
+"The signature was made in the future (check your system clock). Rejected "
+"message to avoid replay attacks."
+msgstr ""
+
+#: lib/cyborghood/mail.rb:272
+msgid "Mail not formatted correctly (encrypted part)."
+msgstr ""
diff --git a/po/CyborgHood_Postman.pot b/po/CyborgHood_Postman.pot
new file mode 100644
index 0000000..6307052
--- /dev/null
+++ b/po/CyborgHood_Postman.pot
@@ -0,0 +1,69 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: CyborgHood 0.2.0~dev\n"
+"POT-Creation-Date: 2009-03-07 21:55+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"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: postman:49
+msgid "Command not recognized."
+msgstr ""
+
+#: postman:63 postman:181
+msgid "Internal error. Administrator is warned."
+msgstr ""
+
+#: postman:93
+msgid "You are manager of the following zones: %s."
+msgstr ""
+
+#: postman:107 postman:129
+msgid "You are not allowed to manage this zone."
+msgstr ""
+
+#: postman:113
+msgid "Requested zone content attached."
+msgstr ""
+
+#: postman:125
+msgid "This zone is not hosted here."
+msgstr ""
+
+#: postman:139
+msgid "Attachment number not found."
+msgstr ""
+
+#: postman:144
+msgid "Attachment has wrong content-type."
+msgstr ""
+
+#: postman:159
+msgid "Invalid zone data."
+msgstr ""
+
+#: postman:166
+msgid "Zone serial is not superior to current serial."
+msgstr ""
+
+#: postman:177
+msgid "Zone updated."
+msgstr ""
+
+#: postman:306
+msgid "Hello %s,"
+msgstr ""
+
+#: postman:308
+msgid "Follows the transcript of your commands:"
+msgstr ""
diff --git a/po/fr/CyborgHood.po b/po/fr/CyborgHood.po
new file mode 100644
index 0000000..7c14cc4
--- /dev/null
+++ b/po/fr/CyborgHood.po
@@ -0,0 +1,53 @@
+# CyborgHood
+# Copyright (C) 2009 DuckCorp
+# This file is distributed under the same license as the CyborgHood package.
+# Marc Dequènes (Duck) <Duck@DuckCorp.org>, 2009.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: CyborgHood 0.2.0~dev\n"
+"POT-Creation-Date: 2009-03-07 21:55+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"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=(n != 1);\n"
+
+#: lib/cyborghood/mail.rb:177
+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:205
+msgid "Mail not formatted correctly (signed part)."
+msgstr "Le mail n'est pas formatté correctement (partie signée)."
+
+#: lib/cyborghood/mail.rb:208
+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:213
+msgid "Mail is from an unknown person."
+msgstr "Le mail provient d'une personne inconnue."
+
+#: lib/cyborghood/mail.rb:225
+msgid ""
+"The signature was made too long ago (check your system clock). Rejected "
+"message to avoid replay attacks."
+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:230
+msgid ""
+"The signature was made in the future (check your system clock). Rejected "
+"message to avoid replay attacks."
+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:272
+msgid "Mail not formatted correctly (encrypted part)."
+msgstr "Le mail n'est pas formatté correctement (partie chiffrée)."
diff --git a/po/fr/CyborgHood_Postman.po b/po/fr/CyborgHood_Postman.po
new file mode 100644
index 0000000..024f4fd
--- /dev/null
+++ b/po/fr/CyborgHood_Postman.po
@@ -0,0 +1,68 @@
+# CyborgHood
+# Copyright (C) 2009 DuckCorp
+# This file is distributed under the same license as the CyborgHood package.
+# Marc Dequènes (Duck) <Duck@DuckCorp.org>, 2009.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: CyborgHood 0.2.0~dev\n"
+"POT-Creation-Date: 2009-03-07 21:55+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"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=(n != 1);\n"
+
+#: postman:49
+msgid "Command not recognized."
+msgstr "Commande non reconnue"
+
+#: postman:63 postman:181
+msgid "Internal error. Administrator is warned."
+msgstr "Erreur interne. L'administrateur a été prévenu."
+
+#: postman:93
+msgid "You are manager of the following zones: %s."
+msgstr "Vous gérez les zones suivantes : %s."
+
+#: postman:107 postman:129
+msgid "You are not allowed to manage this zone."
+msgstr "Vous n'êtes pas autorisé à gérer cette zone."
+
+#: postman:113
+msgid "Requested zone content attached."
+msgstr "Le contenu de la zone demandée est en pièce jointe."
+
+#: postman:125
+msgid "This zone is not hosted here."
+msgstr "Cette zone n'est pas hébergée ici."
+
+#: postman:139
+msgid "Attachment number not found."
+msgstr "Numéro de pièce jointe non trouvé."
+
+#: postman:144
+msgid "Attachment has wrong content-type."
+msgstr "La pièce jointe a un mauvais type de contenu (content-type)."
+
+#: postman:159
+msgid "Invalid zone data."
+msgstr "Données pour la zone invalides."
+
+#: postman:166
+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."
+
+#: postman:177
+msgid "Zone updated."
+msgstr "Zone mise à jour."
+
+#: postman:306
+msgid "Hello %s,"
+msgstr "Bonjour %s,"
+
+#: postman:308
+msgid "Follows the transcript of your commands:"
+msgstr "La retranscription des commandes suit :"
diff --git a/postman b/postman
index a0ba4a7..46745bf 100755
--- a/postman
+++ b/postman
@@ -46,7 +46,7 @@ module CyborgHood
             result = OpenStruct.new
             result.cmd = cmdstr
             result.ok = false
-            result.message = "Command not recognized."
+            result.message = _("Command not recognized.")
             result.refs = nil
           end
         rescue CyberError => e
@@ -60,7 +60,7 @@ module CyborgHood
           result = OpenStruct.new
           result.cmd = cmdstr
           result.ok = false
-          result.message = "Internal error. Administrator is warned."
+          result.message = _("Internal error. Administrator is warned.")
           result.refs = nil
         end
 
@@ -90,7 +90,7 @@ module CyborgHood
           list = CyborgHood::DnsDomain.find_by_manager(user)
           txt_list = list.collect{|z| z.cn }.sort.join(", ")
           result.ok = true
-          result.message = "You are manager of the following zones: #{txt_list}."
+          result.message = _("You are manager of the following zones: %s.") + txt_list
         when "GET"
           return if cmdline.empty?
           case cmdline.shift.upcase
@@ -100,17 +100,17 @@ module CyborgHood
 
             dom = CyborgHood::DnsDomain.new(zone)
             unless dom.hosted?
-              result.message = "This zone is not hosted here."
+              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."
+              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."
+            result.message = _("Requested zone content attached.")
             zone_ref = {:content => srv_dns.read_zone, :filename => "dnszone_#{zone}.txt"}.to_ostruct
             result.refs = [zone_ref]
           end
@@ -122,11 +122,11 @@ module CyborgHood
             zone = cmdline.shift.downcase
             dom = CyborgHood::DnsDomain.new(zone)
             unless dom.hosted?
-              result.message = "This zone is not hosted here."
+              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."
+              result.message = _("You are not allowed to manage this zone.")
               return result
             end
             srv_dns = CyborgHood::Services::DNS.new(zone)
@@ -136,12 +136,12 @@ module CyborgHood
             return unless content_ref =~ /^@(\d+)$/
             part_ref = $1.to_i
             unless (1..refs.size).include? part_ref
-              result.message = "Attachment number not found."
+              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."
+              result.message = _("Attachment has wrong content-type.")
               return result
             end
 
@@ -156,14 +156,14 @@ module CyborgHood
 
             dns_result = srv_dns.check_zone_file(f.path)
             unless dns_result.ok
-              result.message = "Invalid zone data."
+              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."
+              result.message = _("Zone serial is not superior to current serial.")
               f.close!
               return result
             end
@@ -174,11 +174,11 @@ module CyborgHood
               if srv_dns.reload_zone
                 logger.debug "zone reloaded"
                 result.ok = true
-                result.message = "Zone updated."
+                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."
+                result.message = _("Internal error. Administrator is warned.")
               end
             rescue
               logger.warn "Writing zone file failed"
@@ -280,11 +280,18 @@ module CyborgHood
         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 and order.user.preferredLanguage
+          locale = order.user.preferredLanguage
+        else
+          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}")
+                                                         " was rejected for the following reason:\n  #{_(order.message)}")
             mail_reply.deliver
           end
           msg.delete
@@ -296,9 +303,9 @@ module CyborgHood
 
         # create transcript
         logger.debug "Preparing reply"
-        reply_txt = "Hello #{order.user.cn},\n\n"
-        reply_txt += order.message + "\n\n" if order.message
-        reply_txt += "Follows the transcript of your commands:\n"
+        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"
