siannopollo / mail_fetcher

A plugin for accessing POP3/IMAP accounts in rails

This URL has Read+Write access

mail_fetcher / lib / mail_fetcher.rb
100644 92 lines (80 sloc) 4.105 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
require File.dirname(__FILE__) + "/net/pop"
require File.dirname(__FILE__) + "/net/imap"
require "yaml"
 
class MailFetcher
  class << self
    attr_accessor :mailer_class, :config, :access
    
    def mailer_class
      @mailer_class.to_s.classify.constantize unless @mailer_class.nil?
    end
    
    # Configuration options:
    # * <tt>mailer_methods</tt> - An array of symbols of the methods you wish the mailer_class to invoke on each
    # fetched email. Defaults to :receive.
    # * <tt>keep</tt> - Tells the fetcher whether to keep the emails. :keep => true keeps the fetcher from
    # deleting emails. :keep is implied to be true if :delete_if is used. Fetcher will delete all mail by default.
    # * <tt>delete_if</tt> - A block to pass in that is run against each email to determine whether it should be
    # deleted from the server. If the block evaluates to true, the email is deleted. All other emails are left intact.
    # * <tt>env</tt> - Tells the fetcher which configuration in mailer.yml to use. Defaults to ENV["RAILS_ENV"].
    # * <tt>finish</tt> - So, the Net::POP docs say to close the connection with pop#finish, but for some odd
    # reason calling the below function when connected to GMail resets POP3 access and won't allow you to reconnect.
    # This plugin was tested with a GMail account, hence the need for this option.
    # * <tt>use_imap</tt> - Used in conjunction with IMAP, in that it passes the IMAP object to the
    # mailer_class to do whatever it wishes with the connection (since IMAP allows for more features than POP3).
    # Just pass in the name of the method(s) to pass the IMAP object to: :use_imap => :process_with_imap.
    def fetch(options={})
      options = prepare_options(options)
      check_mailer!(options)
      self.config ||= YAML.load_file("#{RAILS_ROOT}/config/mail_fetcher.yml")[options[:env] || ENV["RAILS_ENV"]]
      send :"fetch_#{access || "imap"}", options
    end
    
    def fetch_pop(options={})
      Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
      pop = Net::POP3.new(config[:server]).start(config[:username], config[:password])
      
      pop.mails.each do |mail|
        email = TMail::Mail.parse(mail.pop)
        [options[:mailer_methods]].flatten.each {|method| mailer_class.send(method, email)}
        mail.delete if (options[:delete_if] && options[:delete_if].call(email))
        mail.delete unless options[:keep]
      end
      pop.finish if options[:finish]
    end
    
    def fetch_imap(options={})
      imap = Net::IMAP.new(config[:server], config[:port], true)
      imap.login(config[:username], config[:password])
      
      if options[:use_imap]
        [options[:use_imap]].flatten.each {|method| mailer_class.send(method, imap)}
      else
        imap.examine('INBOX')
        imap.search(['ALL']).each do |message_id|
          email = TMail::Mail.parse(imap.fetch(message_id,'RFC822')[0].attr['RFC822'])
          [options[:mailer_methods]].flatten.each {|method| mailer_class.send(method, email)}
          imap.store(message_id, "+FLAGS", [:Deleted]) if (options[:delete_if] && options[:delete_if].call(email))
          imap.store(message_id, "+FLAGS", [:Deleted]) unless options[:keep]
        end
        imap.close
      end
    end
    
    protected
    def check_mailer!(options={})
      raise NoMailerError if mailer_class.nil?
      raise MailerClassInterfaceError if ([options[:mailer_methods]].flatten.size == 1 && !mailer_class.respond_to?(:receive))
    end
    
    def prepare_options(options)
      returning options do |opts|
        opts[:mailer_methods] ||= :receive
        opts[:keep] = true if opts[:delete_if]
        opts[:finish] ||= false
      end
    end
  end
  
  class NoMailerError < StandardError
    def message
      "A mailer_class must be defined before you can fetch mail"
    end
  end
  
  class MailerClassInterfaceError < StandardError
    def message
      "The mailer_class should at least respond to #receive.\nAlternately, try passing in :mailer_methods => [:method1, :method2] to MailFetcher.fetch"
    end
  end
end