~duplicity-team/duplicity/0.7-series

« back to all changes in this revision

Viewing changes to duplicity/backends/webdavbackend.py

  • Committer: Kenneth Loafman
  • Date: 2014-12-12 14:39:54 UTC
  • Revision ID: kenneth@loafman.com-20141212143954-wyln65yd1ynzsrlx
* Source formatted, using PyDev, all source files to fix some easily fixed
  PEP8 issues. Use ignore space when comparing against previous versions.

Show diffs side-by-side

added added

removed removed

Lines of Context:
74
74
                raise FatalBackendException("""For certificate verification a cacert database file is needed in one of these locations: %s
75
75
Hints:
76
76
  Consult the man page, chapter 'SSL Certificate Verification'.
77
 
  Consider using the options --ssl-cacert-file, --ssl-no-check-certificate .""" % ", ".join(cacert_candidates) )
 
77
  Consider using the options --ssl-cacert-file, --ssl-no-check-certificate .""" % ", ".join(cacert_candidates))
78
78
            # check if file is accessible (libssl errors are not very detailed)
79
79
            if not os.access(self.cacert_file, os.R_OK):
80
80
                raise FatalBackendException("Cacert database file '%s' is not readable." % self.cacert_file)
98
98
                return httplib.HTTPSConnection.request(self, *args, **kwargs)
99
99
            except ssl.SSLError as e:
100
100
                # encapsulate ssl errors
101
 
                raise BackendException("SSL failed: %s" % util.uexc(e),log.ErrorCode.backend_error)
 
101
                raise BackendException("SSL failed: %s" % util.uexc(e), log.ErrorCode.backend_error)
102
102
 
103
103
 
104
104
class WebDAVBackend(duplicity.backend.Backend):
114
114
       request body MUST be treated as if it were an 'allprop' request.  "
115
115
    it was retired because e.g. box.net didn't support <D:allprop/>
116
116
    """
117
 
    listbody =""
 
117
    listbody = ""
118
118
 
119
119
    """Connect to remote store using WebDAV Protocol"""
120
120
    def __init__(self, parsed_url):
134
134
 
135
135
        self.conn = None
136
136
 
137
 
    def sanitize_path(self,path):
 
137
    def sanitize_path(self, path):
138
138
        if path:
139
139
            foldpath = re.compile('/+')
140
 
            return foldpath.sub('/', path + '/' )
 
140
            return foldpath.sub('/', path + '/')
141
141
        else:
142
142
            return '/'
143
143
 
144
 
    def getText(self,nodelist):
 
144
    def getText(self, nodelist):
145
145
        rc = ""
146
146
        for node in nodelist:
147
147
            if node.nodeType == node.TEXT_NODE:
163
163
        log.Info("WebDAV create connection on '%s'" % (self.parsed_url.hostname))
164
164
        self._close()
165
165
        # http schemes needed for redirect urls from servers
166
 
        if self.parsed_url.scheme in ['webdav','http']:
 
166
        if self.parsed_url.scheme in ['webdav', 'http']:
167
167
            self.conn = httplib.HTTPConnection(self.parsed_url.hostname, self.parsed_url.port)
168
 
        elif self.parsed_url.scheme in ['webdavs','https']:
 
168
        elif self.parsed_url.scheme in ['webdavs', 'https']:
169
169
            if globals.ssl_no_check_certificate:
170
170
                self.conn = httplib.HTTPSConnection(self.parsed_url.hostname, self.parsed_url.port)
171
171
            else:
182
182
        Wraps the connection.request method to retry once if authentication is
183
183
        required
184
184
        """
185
 
        self._close() # or we get previous request's data or exception
 
185
        self._close()  # or we get previous request's data or exception
186
186
        self.connect()
187
187
 
188
 
        quoted_path = urllib.quote(path,"/:~")
 
188
        quoted_path = urllib.quote(path, "/:~")
189
189
 
190
190
        if self.digest_challenge is not None:
191
191
            self.headers['Authorization'] = self.get_digest_authorization(path)
192
192
 
193
 
        log.Info("WebDAV %s %s request with headers: %s " % (method,quoted_path,self.headers))
194
 
        log.Info("WebDAV data length: %s " % len(str(data)) )
 
193
        log.Info("WebDAV %s %s request with headers: %s " % (method, quoted_path, self.headers))
 
194
        log.Info("WebDAV data length: %s " % len(str(data)))
195
195
        self.conn.request(method, quoted_path, data, self.headers)
196
196
        response = self.conn.getresponse()
197
 
        log.Info("WebDAV response status %s with reason '%s'." % (response.status,response.reason))
 
197
        log.Info("WebDAV response status %s with reason '%s'." % (response.status, response.reason))
198
198
        # resolve redirects and reset url on listing requests (they usually come before everything else)
199
 
        if response.status in [301,302] and method == 'PROPFIND':
200
 
            redirect_url = response.getheader('location',None)
 
199
        if response.status in [301, 302] and method == 'PROPFIND':
 
200
            redirect_url = response.getheader('location', None)
201
201
            response.close()
202
202
            if redirect_url:
203
 
                log.Notice("WebDAV redirect to: %s " % urllib.unquote(redirect_url) )
 
203
                log.Notice("WebDAV redirect to: %s " % urllib.unquote(redirect_url))
204
204
                if redirected > 10:
205
205
                    raise FatalBackendException("WebDAV redirected 10 times. Giving up.")
206
206
                self.parsed_url = duplicity.backend.ParsedUrl(redirect_url)
207
207
                self.directory = self.sanitize_path(self.parsed_url.path)
208
 
                return self.request(method,self.directory,data,redirected+1)
 
208
                return self.request(method, self.directory, data, redirected + 1)
209
209
            else:
210
210
                raise FatalBackendException("WebDAV missing location header in redirect response.")
211
211
        elif response.status == 401:
213
213
            response.close()
214
214
            self.headers['Authorization'] = self.get_authorization(response, quoted_path)
215
215
            log.Info("WebDAV retry request with authentification headers.")
216
 
            log.Info("WebDAV %s %s request2 with headers: %s " % (method,quoted_path,self.headers))
217
 
            log.Info("WebDAV data length: %s " % len(str(data)) )
 
216
            log.Info("WebDAV %s %s request2 with headers: %s " % (method, quoted_path, self.headers))
 
217
            log.Info("WebDAV data length: %s " % len(str(data)))
218
218
            self.conn.request(method, quoted_path, data, self.headers)
219
219
            response = self.conn.getresponse()
220
 
            log.Info("WebDAV response2 status %s with reason '%s'." % (response.status,response.reason))
 
220
            log.Info("WebDAV response2 status %s with reason '%s'." % (response.status, response.reason))
221
221
 
222
222
        return response
223
223
 
275
275
            del self.headers['Depth']
276
276
            # if the target collection does not exist, create it.
277
277
            if response.status == 404:
278
 
                response.close() # otherwise next request fails with ResponseNotReady
 
278
                response.close()  # otherwise next request fails with ResponseNotReady
279
279
                self.makedir()
280
280
                # just created an empty folder, so return empty
281
281
                return []
286
286
                status = response.status
287
287
                reason = response.reason
288
288
                response.close()
289
 
                raise BackendException("Bad status code %s reason %s." % (status,reason))
 
289
                raise BackendException("Bad status code %s reason %s." % (status, reason))
290
290
 
291
291
            log.Debug("%s" % (document,))
292
292
            dom = xml.dom.minidom.parseString(document)
307
307
        # url causes directory to start with /, but it might be given
308
308
        # with or without trailing / (which is required)
309
309
        if dirs[-1] == '':
310
 
            dirs=dirs[0:-1]
311
 
        for i in range(1,len(dirs)):
312
 
            d="/".join(dirs[0:i+1])+"/"
 
310
            dirs = dirs[0:-1]
 
311
        for i in range(1, len(dirs)):
 
312
            d = "/".join(dirs[0:i + 1]) + "/"
313
313
 
314
314
            self.headers['Depth'] = "1"
315
315
            response = self.request("PROPFIND", d)
322
322
 
323
323
                res = self.request("MKCOL", d)
324
324
                if res.status != 201:
325
 
                    raise BackendException("WebDAV MKCOL %s failed: %s %s" % (d,res.status,res.reason))
 
325
                    raise BackendException("WebDAV MKCOL %s failed: %s %s" % (d, res.status, res.reason))
326
326
 
327
327
    def taste_href(self, href):
328
328
        """
356
356
            raise BackendException(m)
357
357
 
358
358
        if filename.startswith(self.directory):
359
 
            filename = filename.replace(self.directory,'',1)
 
359
            filename = filename.replace(self.directory, '', 1)
360
360
            return filename
361
361
        else:
362
362
            return None
378
378
                status = response.status
379
379
                reason = response.reason
380
380
                response.close()
381
 
                raise BackendException("Bad status code %s reason %s." % (status,reason))
 
381
                raise BackendException("Bad status code %s reason %s." % (status, reason))
382
382
        except Exception as e:
383
383
            raise e
384
384
        finally:
398
398
                status = response.status
399
399
                reason = response.reason
400
400
                response.close()
401
 
                raise BackendException("Bad status code %s reason %s." % (status,reason))
 
401
                raise BackendException("Bad status code %s reason %s." % (status, reason))
402
402
        except Exception as e:
403
403
            raise e
404
404
        finally:
416
416
                status = response.status
417
417
                reason = response.reason
418
418
                response.close()
419
 
                raise BackendException("Bad status code %s reason %s." % (status,reason))
 
419
                raise BackendException("Bad status code %s reason %s." % (status, reason))
420
420
        except Exception as e:
421
421
            raise e
422
422
        finally: