Merge lp:~rye/duplicity/mediafire into lp:~duplicity-team/duplicity/0.7-series

Proposed by Roman Yepishev on 2016-02-10
Status: Merged
Merged at revision: 1194
Proposed branch: lp:~rye/duplicity/mediafire
Merge into: lp:~duplicity-team/duplicity/0.7-series
Diff against target: 208 lines (+169/-0)
3 files modified
bin/duplicity.1 (+30/-0)
duplicity/backends/mediafirebackend.py (+138/-0)
duplicity/commandline.py (+1/-0)
To merge this branch: bzr merge lp:~rye/duplicity/mediafire
Reviewer Review Type Date Requested Status
edso 2016-02-10 Approve on 2016-02-18
Review via email: mp+285547@code.launchpad.net

Description of the change

Backend for https://www.mediafire.com

Requires https://pypi.python.org/pypi/mediafire/ installed.

To test:

1. Create an account at https://www.mediafire.com
2. Run:

pip install mediafire

export FTP_PASSWORD=your-mediafire-password

duplicity /path/to/dir mf://your-email%<email address hidden>/backup-directory

To post a comment you must log in.
edso (ed.so) wrote :

hey Roman,

looks good! any specific reason why you didn't use the existing user/passphrase API in from backend.py ?

i really see no need to add an additional set of backend specific env vars.

..ede/duply.net

review: Needs Information
lp:~rye/duplicity/mediafire updated on 2016-02-10
1180. By Roman Yepishev on 2016-02-10

Use built-in username/password support in backends.py.

Backend is no longer reading environment variables.

MEDIAFIRE_EMAIL ->
   mf://duplicity%<email address hidden>/some_path

MEDIAFIRE_PASSWORD ->
   provided via command line via get_password() or
   FTP_PASSWORD env var or
   mf://duplicity%40example.com:some%<email address hidden>/some_path

Roman Yepishev (rye) wrote :

Nope, there was no reason not to use the built-in support for the username/password retrieval.

Updated code to remove backend-specific env vars.

edso (ed.so) wrote :

thanks Roman!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/duplicity.1'
--- bin/duplicity.1 2016-02-05 09:58:57 +0000
+++ bin/duplicity.1 2016-02-10 17:21:56 +0000
@@ -1205,6 +1205,16 @@
1205.B "A NOTE ON MULTI BACKEND"1205.B "A NOTE ON MULTI BACKEND"
1206below.1206below.
1207.RE1207.RE
1208.PP
1209.BR "MediaFire"
1210.PP
1211.RS
1212mf://user[:password]@mediafire.com/some_dir
1213.PP
1214See also
1215.B "A NOTE ON MEDIAFIRE BACKEND"
1216below.
1217.RE
12081218
1209.SH TIME FORMATS1219.SH TIME FORMATS
1210duplicity uses time strings in two places. Firstly, many of the files1220duplicity uses time strings in two places. Firstly, many of the files
@@ -1871,6 +1881,22 @@
1871.B SWIFT_AUTHVERSION1881.B SWIFT_AUTHVERSION
1872is unspecified, it will default to version 1.1882is unspecified, it will default to version 1.
18731883
1884.SH A NOTE ON MEDIAFIRE BACKEND
1885This backend requires
1886.B mediafire
1887python library to be installed on the system. See
1888.BR REQUIREMENTS .
1889
1890Use URL escaping for username (and password, if provided via command line):
1891
1892.PP
1893.RS
1894mf://duplicity%40example.com@mediafire.com/some_folder
1895.PP
1896.RE
1897
1898The destination folder will be created for you if it does not exist.
1899
1874.SH A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING1900.SH A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING
1875Signing and symmetrically encrypt at the same time with the gpg binary on the1901Signing and symmetrically encrypt at the same time with the gpg binary on the
1876command line, as used within duplicity, is a specifically challenging issue.1902command line, as used within duplicity, is a specifically challenging issue.
@@ -2075,6 +2101,10 @@
2075.B Python kerberos module2101.B Python kerberos module
2076for kerberos authentication2102for kerberos authentication
2077- https://github.com/02strich/pykerberos2103- https://github.com/02strich/pykerberos
2104.TP
2105.BR "MediaFire backend"
2106.B MediaFire Python Open SDK
2107- https://pypi.python.org/pypi/mediafire/
20782108
2079.SH AUTHOR2109.SH AUTHOR
2080.TP2110.TP
20812111
=== added file 'duplicity/backends/mediafirebackend.py'
--- duplicity/backends/mediafirebackend.py 1970-01-01 00:00:00 +0000
+++ duplicity/backends/mediafirebackend.py 2016-02-10 17:21:56 +0000
@@ -0,0 +1,138 @@
1# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
2#
3# Copyright 2016 Roman Yepishev <rye@keypressure.com>
4#
5# This file is part of duplicity.
6#
7# Duplicity is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License as published by the
9# Free Software Foundation; either version 2 of the License, or (at your
10# option) any later version.
11#
12# Duplicity is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with duplicity; if not, write to the Free Software Foundation,
19# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21"""MediaFire Duplicity Backend"""
22
23import os
24
25import duplicity.backend
26
27from duplicity.errors import BackendException
28
29DUPLICITY_APP_ID = '45593'
30
31
32class MediafireBackend(duplicity.backend.Backend):
33 """Use this backend when saving to MediaFire
34
35 URLs look like mf:/root/folder.
36 """
37 def __init__(self, parsed_url):
38 try:
39 import mediafire.client
40 except ImportError:
41 raise BackendException("This backend requires "
42 "the mediafire library")
43
44 duplicity.backend.Backend.__init__(self, parsed_url)
45
46 mediafire_email = parsed_url.username
47 mediafire_password = self.get_password()
48
49 self._file_res = mediafire.client.File
50 self._folder_res = mediafire.client.Folder
51 self._downloaderror_exc = mediafire.client.DownloadError
52 self._notfound_exc = mediafire.client.ResourceNotFoundError
53
54 self.client = mediafire.client.MediaFireClient()
55 self.client.login(app_id=DUPLICITY_APP_ID,
56 email=mediafire_email,
57 password=mediafire_password)
58
59 # //username:password@host/path/to/folder -> path/to/folder
60 uri = 'mf:///' + parsed_url.path.split('/', 3)[3]
61
62 # Create folder if it does not exist and make sure it is private
63 # See MediaFire Account Settings /Security and Privacy / Share Link
64 # to set "Inherit from parent folder"
65 try:
66 folder = self.client.get_resource_by_uri(uri)
67 if not isinstance(folder, self._folder_res):
68 raise BackendException("target_url already exists "
69 "and is not a folder")
70 except mediafire.client.ResourceNotFoundError:
71 # force folder to be private
72 folder = self.client.create_folder(uri, recursive=True)
73 self.client.update_folder_metadata(uri, privacy='private')
74
75 self.folder = folder
76
77 def _put(self, source_path, remote_filename=None):
78 """Upload file"""
79 # Use source file name if remote one is not defined
80 if remote_filename is None:
81 remote_filename = os.path.basename(source_path.name)
82
83 uri = self._build_uri(remote_filename)
84
85 with self.client.upload_session():
86 self.client.upload_file(source_path.open('rb'), uri)
87
88 def _get(self, filename, local_path):
89 """Download file"""
90 uri = self._build_uri(filename)
91 try:
92 self.client.download_file(uri, local_path.open('wb'))
93 except self._downloaderror_exc as ex:
94 raise BackendException(ex)
95
96 def _list(self):
97 """List files in backup directory"""
98 uri = self._build_uri()
99 filenames = []
100 for item in self.client.get_folder_contents_iter(uri):
101 if not isinstance(item, self._file_res):
102 continue
103
104 filenames.append(item['filename'].encode('utf-8'))
105
106 return filenames
107
108 def _delete(self, filename):
109 """Delete single file"""
110 uri = self._build_uri(filename)
111 self.client.delete_file(uri)
112
113 def _delete_list(self, filename_list):
114 """Delete list of files"""
115 for filename in filename_list:
116 self._delete(filename)
117
118 def _query(self, filename):
119 """Stat the remote file"""
120 uri = self._build_uri(filename)
121
122 try:
123 resource = self.client.get_resource_by_uri(uri)
124 size = int(resource['size'])
125 except self._notfound_exc:
126 size = -1
127
128 return {'size': size}
129
130 def _build_uri(self, filename=None):
131 """Build relative URI"""
132 return (
133 'mf:' + self.folder["folderkey"] +
134 ('/' + filename if filename else '')
135 )
136
137
138duplicity.backend.register_backend("mf", MediafireBackend)
0139
=== modified file 'duplicity/commandline.py'
--- duplicity/commandline.py 2016-02-02 12:20:14 +0000
+++ duplicity/commandline.py 2016-02-10 17:21:56 +0000
@@ -918,6 +918,7 @@
918 onedrive://%(some_dir)s918 onedrive://%(some_dir)s
919 azure://%(container_name)s919 azure://%(container_name)s
920 b2://%(account_id)s[:%(application_key)s]@%(bucket_name)s/[%(some_dir)s/]920 b2://%(account_id)s[:%(application_key)s]@%(bucket_name)s/[%(some_dir)s/]
921 mf://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
921922
922""" % dict923""" % dict
923924

Subscribers

People subscribed via source and target branches