Прелюдия:
Эту статью я написал еще в январе месяце, но как то забил на её публикацию. Сейчас нашел, чуть поправил и решил опубликовать.
Если кто то помнит, то около года назад я рассказывал о том как круто дружить с 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 на сервере и ложиться спать.
В общем, удачных переездов.



[...] The Way You Lie ft. RihannaСкачать клип Love the way you lieadobe air в контактеперенос почты с одного аккаунта gmail habr [...]