1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import imaplib
24 import re
25 import os
26 import time
27 import socket
28 import StringIO
29 import rfc822
30 import getpass
31 import email
32
33 import duplicity.backend
34 from duplicity import globals
35 from duplicity import log
36 from duplicity.errors import *
37
38
67
69 parsed_url = self._url
70 try:
71 imap_server = os.environ['IMAP_SERVER']
72 except KeyError:
73 imap_server = parsed_url.hostname
74
75
76 try:
77 self._conn.close()
78 except Exception:
79 pass
80
81 if (parsed_url.scheme == "imap"):
82 cl = imaplib.IMAP4
83 self._conn = cl(imap_server, 143)
84 elif (parsed_url.scheme == "imaps"):
85 cl = imaplib.IMAP4_SSL
86 self._conn = cl(imap_server, 993)
87
88 log.Debug("Type of imap class: %s" % (cl.__name__))
89 self.remote_dir = re.sub(r'^/', r'', parsed_url.path, 1)
90
91
92 if (not(globals.imap_full_address)):
93 self._conn.login(self._username, self._password)
94 self._conn.select(globals.imap_mailbox)
95 log.Info("IMAP connected")
96 else:
97 self._conn.login(self._username + "@" + parsed_url.hostname, self._password)
98 self._conn.select(globals.imap_mailbox)
99 log.Info("IMAP connected")
100
101
102 - def _prepareBody(self,f,rname):
103 mp = email.MIMEMultipart.MIMEMultipart()
104
105
106
107
108 mp["From"]=self.remote_dir
109 mp["Subject"]=rname
110
111 a = email.MIMEBase.MIMEBase("application","binary")
112 a.set_payload(f.read())
113
114 email.Encoders.encode_base64(a)
115
116 mp.attach(a)
117
118 return mp.as_string()
119
120 - def put(self, source_path, remote_filename = None):
121 if not remote_filename:
122 remote_filename = source_path.get_filename()
123 f=source_path.open("rb")
124 allowedTimeout = globals.timeout
125 if (allowedTimeout == 0):
126
127 allowedTimeout = 2880
128 while allowedTimeout > 0:
129 try:
130 self._conn.select(remote_filename)
131 body=self._prepareBody(f,remote_filename)
132
133
134 self._conn.select(globals.imap_mailbox)
135 self._conn.append(globals.imap_mailbox, None, None, body)
136 break
137 except (imaplib.IMAP4.abort, socket.error, socket.sslerror):
138 allowedTimeout -= 1
139 log.Info("Error saving '%s', retrying in 30s " % remote_filename)
140 time.sleep(30)
141 while allowedTimeout > 0:
142 try:
143 self._resetConnection()
144 break
145 except (imaplib.IMAP4.abort, socket.error, socket.sslerror):
146 allowedTimeout -= 1
147 log.Info("Error reconnecting, retrying in 30s ")
148 time.sleep(30)
149
150 log.Info("IMAP mail with '%s' subject stored" % remote_filename)
151
152 - def get(self, remote_filename, local_path):
153 allowedTimeout = globals.timeout
154 if (allowedTimeout == 0):
155
156 allowedTimeout = 2880
157 while allowedTimeout > 0:
158 try:
159 self._conn.select(globals.imap_mailbox)
160 (result,list) = self._conn.search(None, 'Subject', remote_filename)
161 if result != "OK":
162 raise Exception(list[0])
163
164
165 if list[0] == '':
166 raise Exception("no mail with subject %s")
167
168 (result,list) = self._conn.fetch(list[0],"(RFC822)")
169
170 if result != "OK":
171 raise Exception(list[0])
172 rawbody=list[0][1]
173
174 p = email.Parser.Parser()
175
176 m = p.parsestr(rawbody)
177
178 mp = m.get_payload(0)
179
180 body = mp.get_payload(decode=True)
181 break
182 except (imaplib.IMAP4.abort, socket.error, socket.sslerror):
183 allowedTimeout -= 1
184 log.Info("Error loading '%s', retrying in 30s " % remote_filename)
185 time.sleep(30)
186 while allowedTimeout > 0:
187 try:
188 self._resetConnection()
189 break
190 except (imaplib.IMAP4.abort, socket.error, socket.sslerror):
191 allowedTimeout -= 1
192 log.Info("Error reconnecting, retrying in 30s ")
193 time.sleep(30)
194
195 tfile = local_path.open("wb")
196 tfile.write(body)
197 local_path.setdata()
198 log.Info("IMAP mail with '%s' subject fetched" % remote_filename)
199
201 ret = []
202 (result,list) = self._conn.select(globals.imap_mailbox)
203 if result != "OK":
204 raise BackendException(list[0])
205
206
207
208
209
210 (result,list) = self._conn.search(None, 'FROM', self.remote_dir)
211 if result!="OK":
212 raise Exception(list[0])
213 if list[0]=='':
214 return ret
215 nums=list[0].split(" ")
216 set="%s:%s"%(nums[0],nums[-1])
217 (result,list) = self._conn.fetch(set,"(BODY[HEADER])")
218 if result!="OK":
219 raise Exception(list[0])
220
221 for msg in list:
222 if (len(msg)==1):continue
223 io = StringIO.StringIO(msg[1])
224 m = rfc822.Message(io)
225 subj = m.getheader("subject")
226 header_from = m.getheader("from")
227
228
229 if (not (header_from == None)):
230 if (re.compile("^" + self.remote_dir + "$").match(header_from)):
231 ret.append(subj)
232 log.Info("IMAP LIST: %s %s" % (subj,header_from))
233 return ret
234
236 (ret,list)=fun(*args)
237 if ret != "OK":
238 raise Exception(list[0])
239 return list
240
242 self._imapf(self._conn.store,i,"+FLAGS",'\\DELETED')
243
246
247 - def delete(self, filename_list):
257
262
263 duplicity.backend.register_backend("imap", ImapBackend);
264 duplicity.backend.register_backend("imaps", ImapBackend);
265