#!/usr/bin/env ruby require 'net/imap' # # http://wonko.com/article/554 # # Gilles LAMIRAL: Your Ruby code is nice. Is it GPL? Can I make a reference # to it in the imapsync distribution? # # Wonko : Please consider this code public domain (and unsupported). # You're more than welcome to refer to it if you'd like. # # # Source server connection info. SOURCE_HOST = 'mail.example.com' SOURCE_PORT = 143 SOURCE_SSL = false SOURCE_USER = 'username' SOURCE_PASS = 'password' # Destination server connection info. DEST_HOST = 'imap.gmail.com' DEST_PORT = 993 DEST_SSL = true DEST_USER = 'username@gmail.com' DEST_PASS = 'password' # Mapping of source folders to destination folders. The key is the name of the # folder on the source server, the value is the name on the destination server. # Any folder not specified here will be ignored. If a destination folder does # not exist, it will be created. FOLDERS = { 'INBOX' => 'INBOX', 'sourcefolder' => 'gmailfolder' } # Utility methods. def dd(message) puts "[#{DEST_HOST}] #{message}" end def ds(message) puts "[#{SOURCE_HOST}] #{message}" end # Connect and log into both servers. ds 'connecting...' source = Net::IMAP.new(SOURCE_HOST, SOURCE_PORT, SOURCE_SSL) ds 'logging in...' source.login(SOURCE_USER, SOURCE_PASS) dd 'connecting...' dest = Net::IMAP.new(DEST_HOST, DEST_PORT, DEST_SSL) dd 'logging in...' dest.login(DEST_USER, DEST_PASS) # Loop through folders and copy messages. FOLDERS.each do |source_folder, dest_folder| # Open source folder in read-only mode. begin ds "selecting folder '#{source_folder}'..." source.examine(source_folder) rescue => e ds "error: select failed: #{e}" next end # Open (or create) destination folder in read-write mode. begin dd "selecting folder '#{dest_folder}'..." dest.select(dest_folder) rescue => e begin dd "folder not found; creating..." dest.create(dest_folder) dest.select(dest_folder) rescue => ee dd "error: could not create folder: #{e}" next end end # Build a lookup hash of all message ids present in the destination folder. dest_info = {} dd 'analyzing existing messages...' dest.uid_fetch(dest.uid_search(['ALL']), ['ENVELOPE']).each do |data| dest_info[data.attr['ENVELOPE'].message_id] = true end # Loop through all messages in the source folder. source.uid_fetch(source.uid_search(['ALL']), ['ENVELOPE']).each do |data| mid = data.attr['ENVELOPE'].message_id # If this message is already in the destination folder, skip it. next if dest_info[mid] # Download the full message body from the source folder. ds "downloading message #{mid}..." msg = source.uid_fetch(data.attr['UID'], ['RFC822', 'FLAGS', 'INTERNALDATE']).first # Append the message to the destination folder, preserving flags and # internal timestamp. dd "storing message #{mid}..." dest.append(dest_folder, msg.attr['RFC822'], msg.attr['FLAGS'], msg.attr['INTERNALDATE']) end source.close dest.close end puts 'done'