commit 6bc51624438375e0d63f00ffa642a6c5720b7312
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sat Jun 19 00:18:31 2010 +0200

    [evol] reorganized imap processing

diff --git a/bin/postman b/bin/postman
index 280c06e..c513f7b 100755
--- a/bin/postman
+++ b/bin/postman
@@ -64,157 +64,145 @@ module CyborgHood
       end
 
       def run
-        imap = IMAP.new(@config.imap)
-        until @stop_asap
-          t = Time.now.to_i
-          logger.debug "Starting mail check"
-          check_mails(imap)
-          logger.debug "Mail check finished"
-          t2 = Time.now.to_i
-          sleep_time = @config.imap.min_check_interval - (t2 - t)
-          if sleep_time > 0
-            logger.debug "Having a break before new check..."
-            begin
-              sleep(sleep_time)
-            rescue
-            end
-          end
+        logger.info "Bot starting"
+        imap = IMAP.new(@config.imap, @config.imap.min_check_interval)
+        imap.check_mails do |msg|
+          process_message(msg)
         end
-        logger.info "Bot was asked to stop..." if @stop_asap
         logger.info "Bot terminating"
       end
 
       def ask_to_stop
         @stop_asap = true
-	# wakeup sleeps
-	Process.kill("ALRM", $$)
+        # wakeup sleeps
+        Process.kill("ALRM", $$)
       end
 
       private
 
-      def check_mails(imap)
-        imap.check_mail do |msg|
-          if @stop_asap
-            logger.info "Bot was asked to stop..."
-            break
-          end
+      def process_message(msg)
+        if @stop_asap
+          logger.info "Bot was asked to stop..."
+          return false
+        end
 
-          mail = Mail.new(msg.content)
-          logger.info "Received mail with ID '#{mail.message_id}': #{mail.from_pretty} -> #{mail.to_pretty} (#{mail.subject_pretty})"
+        mail = Mail.new(msg.content)
+        logger.info "Received mail with ID '#{mail.message_id}': #{mail.from_pretty} -> #{mail.to_pretty} (#{mail.subject_pretty})"
 
-          # 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
-            next
-          end
+        # 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
 
-          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})"
-              next
-            when :processable
-              logger.error "Untreated processing problem, skipping mail (#{e.message})"
-              next
-            when :ignorable
-              logger.warn "Internal processing warning, continuing (#{e.message})"
-            end
+        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})"
           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:",
-                           :id => mail.message_id).to_s + "\n  " + report.error.to_s + "\n" + mail_signature())
-              mail_reply.deliver
-            end
-            msg.delete
-            next
+        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:",
+                                                           :id => mail.message_id).to_s + "\n  " + report.error.to_s + "\n" + mail_signature())
+            mail_reply.deliver
           end
+          msg.delete
+          return true
+        end
 
-          order = MailOrder.parse(report.user, report.message)
-          result_tag = order.valid? ? "SUCCESS" : "FAILURE"
-          result_msg = "Processing result: #{result_tag}"
-	  result_msg += " (#{order.error.untranslated})" unless order.valid?
-          logger.info result_msg
+        order = MailOrder.parse(report.user, report.message)
+        result_tag = order.valid? ? "SUCCESS" : "FAILURE"
+        result_msg = "Processing result: #{result_tag}"
+        result_msg += " (#{order.error.untranslated})" unless order.valid?
+        logger.info result_msg
 
-          reply_intro = _("Hello %{cn},", :cn => order.user.cn)
+        reply_intro = _("Hello %{cn},", :cn => order.user.cn)
 
-          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:",
-                         :id => mail.message_id).to_s + "\n  " + order.error.to_s + "\n" + mail_signature())
-            mail_reply.sign_and_crypt(order.user.keyFingerPrint)
-            mail_reply.deliver
-            msg.delete
-            next
-          end
+        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:",
+                                                         :id => mail.message_id).to_s + "\n  " + order.error.to_s + "\n" + mail_signature())
+          mail_reply.sign_and_crypt(order.user.keyFingerPrint)
+          mail_reply.deliver
+          msg.delete
+          return true
+        end
 
-          logger.debug "Message accepted, processing orders..."
-          result_list = CommandRunner.run(order)
-
-          # create transcript
-          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" + mail_signature()
+        logger.debug "Message accepted, processing orders..."
+        result_list = CommandRunner.run(order)
+
+        # create transcript
+        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" + mail_signature()
+
+        # create mail
+        logger.debug "Preparing mail"
+        mail_reply = mail.create_reply
+        if reply_attachments.empty?
+          transcript_part = mail_reply
+        else
+          mail_reply.to_multipart!
 
-          # create mail
-          logger.debug "Preparing mail"
-          mail_reply = mail.create_reply
-          if reply_attachments.empty?
-            transcript_part = mail_reply
-          else
-            mail_reply.to_multipart!
+          p = CyborgHood::Mail.blank
+          transcript_part = p
+          mail_reply.parts << p
 
+          reply_attachments.each do |attachment|
             p = CyborgHood::Mail.blank
-            transcript_part = p
+            p.set_content_type("text", "plain", {'charset' => "utf-8"})
+            p.set_disposition("attachment", {'filename' => attachment.filename})
+            p.quoted_printable_body = attachment.content
             mail_reply.parts << p
-
-            reply_attachments.each do |attachment|
-              p = CyborgHood::Mail.blank
-              p.set_content_type("text", "plain", {'charset' => "utf-8"})
-              p.set_disposition("attachment", {'filename' => attachment.filename})
-              p.quoted_printable_body = attachment.content
-              mail_reply.parts << p
-            end
           end
-          # insert transcript
-          transcript_part.set_content_type("text", "plain", {'charset' => 'utf-8', 'format' => 'flowed'})
-          transcript_part.set_disposition("inline")
-          transcript_part.quoted_printable_body = reply_txt
+        end
+        # insert transcript
+        transcript_part.set_content_type("text", "plain", {'charset' => 'utf-8', 'format' => 'flowed'})
+        transcript_part.set_disposition("inline")
+        transcript_part.quoted_printable_body = reply_txt
 
-          # send reply
-          logger.debug "Sending mail"
-          mail_reply.sign_and_crypt(order.user.keyFingerPrint)
-          mail_reply.deliver
+        # send reply
+        logger.debug "Sending mail"
+        mail_reply.sign_and_crypt(order.user.keyFingerPrint)
+        mail_reply.deliver
 
-          logger.info "Message processed completely, deleting"
-          msg.delete
-        end
+        logger.info "Message processed completely, deleting"
+        msg.delete
+
+        true
       end
 
       def mail_signature
diff --git a/lib/cyborghood/imap.rb b/lib/cyborghood/imap.rb
index 7470318..941193a 100644
--- a/lib/cyborghood/imap.rb
+++ b/lib/cyborghood/imap.rb
@@ -20,10 +20,11 @@ require 'net/imap'
 
 module CyborgHood
   class IMAP
-    def initialize(params)
+    def initialize(params, min_check_interval)
       @params = params
       @imap = nil
       @is_loggued = false
+      @min_check_interval = min_check_interval
     end
 
     class IMAPMessage
@@ -33,6 +34,7 @@ module CyborgHood
 
         @config = Config.instance
         @content = nil
+        @stop_mail_check = false
       end
 
       def content
@@ -44,7 +46,7 @@ module CyborgHood
 
       def delete
 	if @config.debug.nil? or @config.debug.flags.nil? or not @config.debug.flags.include?(:debug_nomaildeletion)
-          @imap.store(@message_id, "+FLAGS", [:Deleted])
+      @imap.store(@message_id, "+FLAGS", [:Deleted])
 	end
       end
     end
@@ -61,10 +63,13 @@ module CyborgHood
         @imap = Net::IMAP.new(@params.host, port, ssl, @params.ca_cert, check_ca)
       rescue SocketError
         logger.warn "Could not connect to the IMAP server"
-	return false
+        return false
       end
       logger.debug "Connected (IMAP Capabilities: " + @imap.capability.join(", ") + ")"
       #p @imap.getquotaroot("INBOX")
+
+      @stop_mail_check = false
+
       true
     end
 
@@ -76,33 +81,59 @@ module CyborgHood
         @imap.authenticate('LOGIN', @params.login, @params.passwd)
       rescue Net::IMAP::NoResponseError
         logger.warn "Could not authenticate to the IMAP server"
-	disconnect
-	return false
+        disconnect
+        return false
       end
       logger.debug "Authenticated as '#{@params.login}'"
       @is_loggued = true
       true
     end
 
-    def check_mail(&message_handler)
+    def check_mails_once(&message_handler)
       connect &&
-      authenticate &&
-      check_inbox(&message_handler) &&
-      disconnect
+          authenticate &&
+          check_inbox(&message_handler)
     rescue SocketError, Net::IMAP::NoResponseError => e
       logger.warn "IMAP or Network error: " + e.message
     rescue Net::IMAP::Error => e
       raise CyberError.new(:unrecoverable, "protocol/imap", e.message)
+    ensure
+      begin
+        disconnect
+      rescue
+      end
+    end
+
+    def check_mails(&message_handler)
+      until @stop_mail_check
+        t = Time.now.to_i
+        r = check_mails_once(&message_handler)
+        t2 = Time.now.to_i
+
+        sleep_time = @min_check_interval - (t2 - t)
+        if sleep_time > 0
+          logger.debug "Having a break before new check..."
+          begin
+            sleep(sleep_time)
+          rescue
+          end
+        end
+      end
     end
 
     def check_inbox
       logger.debug "Examining INBOX"
       @imap.select('INBOX')
 
+      logger.debug "Starting mail check"
       @imap.search(["ALL"], "UTF-8").each do |message_id|
         logger.debug "*** Fetched mail ##{message_id}"
-        yield IMAPMessage.new(@imap, message_id)
+        unless yield IMAPMessage.new(@imap, message_id)
+          @stop_mail_check = true
+          break
+        end
       end
+      logger.debug "Mail check finished"
 
       @imap.expunge
     end
