Skip to content

Instantly share code, notes, and snippets.

@hoanga
Created August 12, 2009 06:09
Show Gist options
  • Save hoanga/166350 to your computer and use it in GitHub Desktop.
Save hoanga/166350 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'net/imap'
require 'rubygems'
require 'tmail'
# Introduction:
#
# This small library is to archive emails from an imap server
# in an incremental fashion. None of the other tools (fetchmail,
# imapfilter, getmail) were able to do what I wanted which was:
#
# 1. Fetch a limited # of email messages from an imap inbox
# 2. Download and store into a Maildir backend
#
# Example usage that connects to Gmail and archives 100 messages into
# the Maildir directory '/home/myuser/maildir/new':
#
# ia = ImapArchive.new('myuser', 'mypassword')
# ia.archive_path = '/home/myuser/maildir/new'
# ia.process!
class ImapArchive
attr_accessor :username
attr_accessor :password
DEFAULT_OPTS = {
:server => 'imap.gmail.com',
:port => '993',
:mailbox => 'INBOX',
:max_count => 100,
:archive_type => 'Maildir',
:archive_path => '/tmp',
}
# Define accessor methods for all of the default_opts keys
DEFAULT_OPTS.keys.each do |opt|
define_method("#{opt}") { @config[opt] }
define_method("#{opt}=") { |param| @config[opt] = param }
end
#
# Returns an ImapArchive object, requiring a +username+ and +password+.
# Also can take an optional parameters hash to direct access to
# another server, port, inbox, or path to archive
#
# +opts+ arguments:
#
# :server => String (default : 'imap.gmail.com')
# :port => String (default : '993')
# :mailbox => String (default : 'INBOX')
# :max_count => Fixnum (default : 100)
# :archive_type => String (default : 'Maildir')
# :archive_path => String (default : '/tmp')
#
def initialize(username, password, opts = {})
@username = username
@password = password
@config = DEFAULT_OPTS.merge(opts)
end
# Cleans up any messages that need to be deleted or processed via
# IMAP expunge command
def cleanup
@imap.expunge()
end
# Establish a connection to the imap server and sets the mailbox
def connect
@imap = Net::IMAP.new(@config[:server], @config[:port], true)
@imap.login(@username, @password)
@imap.select(@config[:mailbox])
end
# Disconnect from the imap server
def disconnect
@imap.logout()
@imap.disconnect()
end
# Generates a unique-ish filename for storage into a Maildir directory
def get_uniq_filename
t = Time.now.to_i.to_s
h = `hostname`.chomp
pid = Process.pid.to_s
"#{t}.P#{pid}.#{h}"
end
# Does the heavy lifting of searching the mailbox for messages to
# process. Pulls the message down locally and stores into the
# +archive_path+ up to the +max_count+ configured
def pull
i = 0
@imap.search(["NOT", "DELETED"]).each do |message_id|
mail = fetch_and_parse(message_id)
archive(mail)
i += 1
@imap.store(message_id, "+FLAGS", [:Deleted]) # Delete
# Exit if we reach the max # of messages we want to process
break if i > @config[:max_count]
end
end
# A convenience method to connect, pull any outstanding messages,
# delete processed messages and disconnect from the server
def process!
connect
pull
cleanup
disconnect
end
private
def fetch_and_parse(message_id)
msg = @imap.fetch(message_id, 'RFC822')
TMail::Mail.parse(msg.first.attr['RFC822'])
end
def archive(mail)
# Archive it
fpath = @config[:archive_path]
fname = get_uniq_filename
File.open("#{fpath}/#{fname}", 'w') do |f|
f.puts(mail)
end
end
end
if __FILE__ == $0
require 'yaml'
config = YAML.load_file('gmail.yml')
username = config[:username]
password = config[:password]
imap = ImapArchive.new(username, password, config)
imap.process!
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment