Googlemail-Backup mit IMAP

Winnie | 5.10.2008

Wie schon erwähnt, bin ich auf der Suche nach der perfekten Lösung, meine Googlemail-E-Mails zu sichern. Und ich denke ich habe sie gefunden: ein Ruby-Script, das die Synchronisation zwischen meinem Googlemail-Account und einem zweiten IMAP-Account übernimmt. Synchronisation ist dabei vielleicht das falsche Wort, es ist eher ein Backup, was den zweiten IMAP-Account auf dem gleichen Stand wie den Googlemail-Account hält.
Die Basis für das Script kommt von Ryan Grove. Ich habe das Löschen von Nachrichten und die Ordner-Auflistung hinzugefügt.

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env ruby
require 'net/imap'
 
# Source server connection info.
SOURCE_HOST = 'imap.googlemail.com'
SOURCE_PORT = 993
SOURCE_SSL  = true
SOURCE_USER = 'EMAIL@googlemail.com'
SOURCE_PASS = 'PASSWORT'
 
# Destination server connection info.
DEST_HOST = 'imap.SERVER.com'
DEST_PORT = 143
DEST_SSL  = false
DEST_USER = 'USER'
DEST_PASS = 'PASSWORT'
 
# List of all Folder that should not be synced
FOLDERS_EXCLUDE = [
  '[Google Mail]',
  '[Google Mail]/Sent Mail', 
  '[Google Mail]/Spam',
  '[Google Mail]/Trash'
]
 
# 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)
 
# Getting the folders to sync
folders = Array.new
source.list("", "%").each do |mailbox|
    if FOLDERS_EXCLUDE.include?(mailbox.name) == false
        folders << mailbox.name
    end
 
    if mailbox.attr.include?(Net::IMAP::NOSELECT)
        source.list("", "#{mailbox.name}/%").each do |mailboxChild|
            if FOLDERS_EXCLUDE.include?(mailboxChild.name) == false
                folders << mailboxChild.name
            end
        end 
    end 
end
#puts folders
 
 
# Loop through folders and copy messages.
folders.each do |foldername|
  # Open source folder in read-only mode.
  begin
    ds "selecting folder '#{foldername}'..."
    source.examine(foldername)
  rescue => e
    ds "error: select failed: #{e}"
    next
  end
 
  # Open (or create) destination folder in read-write mode.
  begin
    dd "selecting folder '#{foldername}'..."
    dest.select(foldername)
  rescue => e
    begin
      dd "folder not found; creating..."
      dest.create(foldername)
      dest.select(foldername)
    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
    dest.uid_fetch(uids, ['ENVELOPE']).each do |data|
      dest_info[data.attr['ENVELOPE'].message_id] = true      
    end
  end
 
  # Loop through all messages in the source folder.
  uids = source.uid_search(['ALL'])
  if uids.length > 0
    source.uid_fetch(uids, ['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(foldername, msg.attr['RFC822'], msg.attr['FLAGS'],
          msg.attr['INTERNALDATE'])
    end
  end
 
 
 
  # Build a lookup hash of all message ids present in the source folder.
  source_info = {}
 
  dd 'analyzing source messages...'
  uids = source.uid_search(['ALL'])
  if uids.length > 0
    source.uid_fetch(uids, ['ENVELOPE']).each do |data|
      source_info[data.attr['ENVELOPE'].message_id] = true      
    end
  end
 
  # Loop through all messages in the source folder.
  uids = dest.uid_search(['ALL'])
  if uids.length > 0
     dest.uid_fetch(uids, ['ENVELOPE']).each do |data|
      mid = data.attr['ENVELOPE'].message_id
 
      # If this message is already in the destination folder, skip it.
      next if source_info[mid]
 
      # Setting flag for deletion
      ds "deleting message #{mid}..."
     dest.store(data.seqno, "+FLAGS", [:Deleted])
    end
  end
 
 
  dest.expunge
  source.close
  dest.close
end
 
 
puts 'done'

Kategorien: Blog |

2 Kommentare zu “Googlemail-Backup mit IMAP”

  1. Bits und so #116 (Glücksschrumpfen) | Bits und so schreibt:
    16.10.2008 um 11:54

    [...] winnie: 31 Days of iPhone Apps, Googlemail Backup mit IMAP [...]

  2. Marius schreibt:
    11.12.2008 um 14:59

    Hi Winnie!

    Ich habs im Cast schon kurz gehört und grad nach der heutigen BitsundSo Folge nochmal hier gelsen.
    Du kannst den selben Effekt auch wunderbar mit dem Imapfilter erreichen (http://imapfilter.hellug.gr/) der auch wunderbar auf BSD läuft.
    Bei mir filtert er auf dem FreeBSD Mailserver die Mails in Unterordner. Er unterstützt aber auch mehrere Mailserver. Damit ist das Backup 1 Zeile auf der Shell.

    LG Marius

Kommentare