imap to imap или переезд почты
Прелюдия:
Эту статью я написал еще в январе месяце, но как то забил на её публикацию. Сейчас нашел, чуть поправил и решил опубликовать.
Если кто то помнит, то около года назад я рассказывал о том как круто дружить с google apps, а точнее иметь гугло почту на своем домене.
Все это безусловно хорошо, но давно меня уже терзала та мысль, что надо все таки переезжать на обычный gmail вариант. Вызвано это несколькими причинами:
- До google apps все новинки докатываются с опозданием.
- Удобнее держать один аккаунт на все сервисы.
План:
Составив в голове небольшой план переезда, я начал по пунктам разбирать варианты решения проблем.
1.Контакты.
С переносом контактов проблем не возникло вовсе, т.к. гугл умеет сам их экспортировать в opml файл и импортировать его обратно.
2.Письма.
Основной задачей являлся перенос писем, причем простое выгребание по pop3 тут не как не подходит, т.к. мне нужно что бы у писем осталась та же дата, которая была и в оригинале.

Программа imapsync.
Сразу хочу рассказать о подводных камнях, на которые я наткнулся. Данная утилита работает весьма не хитрым способом. Указываете ей «source» и «destination» сервера, после чего она проверяет есть ли данное письмо на «destination» сервере, если нету то скачивает его на ваш компьютер и заливает на сервер, если есть то приступает к следующему письму. Когда письма в папке заканчиваются, то переходим к следующей папке соответственно. Скорость работы не очень большая, у меня на одно письмо, без вложений, тратилось примерно секунда, так что я очень настоятельно рекомендую перед запуском удалить все письма из корзины, из спама и прочую архивную макулатуру, которая вам не нужна.
Второй подводный камень, это то, что при простом запуске из командной строки, письма начали переносить не совсем корректно, а точнее не совсем правильно начали присваиваться Лэйблы, возможно это не косяк, а просто я не дождался пока программа отработает свой цикл полностью, но как бы там не было, я нажал ctrl+c и запустил другой вариант.
Скрипт на Ruby.
#!/usr/bin/env ruby
require 'net/imap'
# Source server connection info.
SOURCE_NAME = 'username@example.com'
SOURCE_HOST = 'mail.example.com'
SOURCE_PORT = 993
SOURCE_SSL = true
SOURCE_USER = 'username'
SOURCE_PASS = 'password'
# Destination server connection info.
DEST_NAME = 'username@gmail.com'
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'
}
# Maximum number of messages to select at once.
UID_BLOCK_SIZE = 1024
# Utility methods.
def dd(message)
puts "[#{DEST_NAME}] #{message}"
end
def ds(message)
puts "[#{SOURCE_NAME}] #{message}"
end
def uid_fetch_block(server, uids, *args)
pos = 0
while pos < uids.size server.uid_fetch(uids[pos, UID_BLOCK_SIZE], *args).each {|data| yield data } pos += UID_BLOCK_SIZE end end @failures = 0 @existing = 0 @synced = 0 # 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...'
uids = dest.uid_search(['ALL'])
if uids.length > 0
uid_fetch_block(dest, uids, ['ENVELOPE']) do |data|
dest_info[data.attr['ENVELOPE'].message_id] = true
end
end
dd "Found #{uids.length} messages"
# Loop through all messages in the source folder.
uids = source.uid_search(['ALL'])
ds "Found #{uids.length} messages"
if uids.length > 0
uid_fetch_block(source, uids, ['ENVELOPE']) do |data|
mid = data.attr['ENVELOPE'].message_id
# If this message is already in the destination folder, skip it.
if dest_info[mid]
@existing += 1
next
end
# 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}..."
tries = 0
begin
tries += 1
dest.append(dest_folder, msg.attr['RFC822'], msg.attr['FLAGS'],
msg.attr['INTERNALDATE'])
@synced += 1
rescue Net::IMAP::NoResponseError => ex
if tries < 10
dd "Error: #{ex.message}. Retrying..."
sleep 1 * tries
retry
else
@failures += 1
dd "Error: #{ex.message}. Tried and failed #{tries} times; giving up on this message."
end
end
end
end
source.close
dest.close
end
puts "Finished. Message counts: #{@existing} untouched, #{@synced} transferred, #{@failures} failures."Скорость работы примерно такая же, но мне он показался более удобным, его можно оставить в screen'e на сервере и ложиться спать.
В общем, удачных переездов.



Понравилась запись? Подпишись на RSS!