commit dfc483086769ea56e5da789007c7d31c5d3c5a76
Author: Marc Dequenes <duck@Orfeo.duckcorp.org>
Date:   Tue Feb 24 02:17:19 2009 +0100

    [evol] reorganized CommandParser, and improved messages and error handling

diff --git a/postman b/postman
index da3055b..4c9a647 100755
--- a/postman
+++ b/postman
@@ -34,173 +34,171 @@ require 'fileutils'
 
 #Socket.gethostname
 
-class CommandParser
-  def self.run(order)
-    result_list = []
-    order.commands.each do |cmd|
-      logger.info "Executing command: #{cmd}"
-      begin
-        result_list << execute_cmd(order.user, cmd, order.refs)
-      rescue
-        logger.info "Command failed: " + $!
+module CyborgHood
+  class CommandParser
+    def self.run(order)
+      result_list = []
+      order.commands.each do |cmdstr|
+        logger.info "Executing command: #{cmdstr}"
+        begin
+          result = execute_cmd(order.user, cmdstr, order.refs)
+          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 = 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
       end
+      result_list
     end
-    result_list
-  end
 
-  private
+    private
 
-  def self.execute_cmd(user, cmdstr, refs)
-    cmdline = Shellwords.shellwords(cmdstr)
-    subsys = cmdline.shift
+    def self.execute_cmd(user, cmdstr, refs)
+      cmdline = Shellwords.shellwords(cmdstr)
+      subsys = cmdline.shift
 
-    result = OpenStruct.new
-    result.cmd = cmdstr
-    ok = true
-    case subsys.upcase
-    when "DNS"
-      case cmdline.shift.upcase
-      when "INFO"
-        if cmdline.empty?
+      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(", ")
-          logger.info "User is manager of the following zones: " + txt_list
-          result.message = "You are manager of the following zones: " + txt_list
-        else
-          ok = false
-        end
-      when "GET"
-        case cmdline.shift.upcase
-        when "ZONE"
-          zone = cmdline.shift.downcase
-          dom = CyborgHood::DnsDomain.new(zone)
-          logger.info "User requesting zone content for '#{zone}'"
-          if dom.hosted?
-            if dom.managed_by? user
-              logger.info "User is manager of the zone"
-              srv_dns = CyborgHood::Services::DNS.new(zone)
-              result.message = "Requested zone content attached."
-              zone_ref = {:content => srv_dns.read_zone, :filename => "dnszone_#{zone}.txt"}.to_ostruct
-              result.refs = [zone_ref]
-            else
-              logger.info "User is not allowed to manage the zone"
+          result.ok = true
+          result.message = "You are manager of the following zones: #{txt_list}."
+        when "GET"
+          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
-          else
-            logger.info "Zone not hosted"
-            result.message "This zone is not hosted here."
+
+            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
-        else
-          ok = false
-        end
-      when "SET"
-        case cmdline.shift.upcase
-        when "ZONE"
-          zone = cmdline.shift.downcase
-          dom = CyborgHood::DnsDomain.new(zone)
-          logger.info "User requesting zone content for '#{zone}'"
-          if dom.hosted?
-            if dom.managed_by? user
-              logger.info "User is manager of the zone"
-              srv_dns = CyborgHood::Services::DNS.new(zone)
-
-              content_ref = cmdline.shift.downcase
-              if content_ref =~ /^@(\d+)$/
-                part_ref = $1.to_i
-                if (1..refs.size).include? part_ref
-                  part = refs[part_ref]
-                  if part.content_type == "text/plain"
-                    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}"
-
-                    result = srv_dns.check_zone_file(f.path)
-                    if result.ok
-                      logger.debug "New serial: #{result.serial}"
-                      # allow new serial or missing serial (to allow creating a new zone or replacing a broken zone)
-                      if current_serial.nil? or result.serial > current_serial
-                        begin
-                          srv_dns.write_zone_from_file(f.path)
-                          f.close!
-                          logger.info "zone changed"
-                          if srv_dns.reload_zone
-                            logger.info "zone reloaded"
-                          else
-                            logger.info "zone reload failed, replacing old content"
-                            srv_dns.replace_zone_with_backup
-                            result.message = "Internal error."
-                            return result
-                          end
-                        rescue
-                          logger.debug "Writing zone file failed"
-                          raise
-                        ensure
-                          f.close!
-                        end
-                      else
-                        logger.info "zone serial is not superior to current serial"
-                        result.message = "Zone serial is not superior to current serial."
-                        f.close!
-                        return result
-                      end
-                    else
-                      logger.info "new zone file is invalid"
-                      result.message = "invalid zone data"
-                      f.close!
-                      return result
-                    end
-
-                    f.close!
-                  else
-                    logger.info "attachment for zone is not plain text"
-                    result.message = "Attachment has wrong type."
-                    return result
-                  end
-                else
-                  logger.info "attachement for zone not found"
-                  result.message = "Attachment number not found."
-                  return result
-                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}'"
+
+            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
+
+            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
-                ok = false
+                logger.warn "zone reload failed, replacing old content"
+                srv_dns.replace_zone_with_backup
+                result.message = "Internal error. Administrator is warned."
               end
-            else
-              logger.info "User is not allowed to manage the zone"
-              result.message = "You are not allowed to manage this zone."
+            rescue
+              logger.warn "Writing zone file failed"
+              raise
+            ensure
+              f.close!
             end
-          else
-            logger.info "Zone not hosted"
-            result.message "This zone is not hosted here."
           end
-        else
-          ok = false
         end
-      else
-        ok = false
       end
-    else
-      ok = false
-    end
 
-    if not ok
-      result.message = "Command not recognized"
-      result.refs = nil
-      logger.info "Command not recognized: #{cmdstr}"
+      if result.message.nil?
+        # here fall lost souls
+        nil
+      else
+        result
+      end
     end
-
-    result
   end
-end
 
-# imap.store(message_id, "+FLAGS", [:Deleted])
-# imap.expunge()
-
-module CyborgHood
   # not yet ready to be a real Cyborg
   class Postman #< Cyborg
     def initialize
