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

Proposed by Roman Yepishev
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 Approve
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.
Revision history for this message
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
1180. By Roman Yepishev

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

Revision history for this message
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.

Revision history for this message
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
1=== modified file 'bin/duplicity.1'
2--- bin/duplicity.1 2016-02-05 09:58:57 +0000
3+++ bin/duplicity.1 2016-02-10 17:21:56 +0000
4@@ -1205,6 +1205,16 @@
5 .B "A NOTE ON MULTI BACKEND"
6 below.
7 .RE
8+.PP
9+.BR "MediaFire"
10+.PP
11+.RS
12+mf://user[:password]@mediafire.com/some_dir
13+.PP
14+See also
15+.B "A NOTE ON MEDIAFIRE BACKEND"
16+below.
17+.RE
18
19 .SH TIME FORMATS
20 duplicity uses time strings in two places. Firstly, many of the files
21@@ -1871,6 +1881,22 @@
22 .B SWIFT_AUTHVERSION
23 is unspecified, it will default to version 1.
24
25+.SH A NOTE ON MEDIAFIRE BACKEND
26+This backend requires
27+.B mediafire
28+python library to be installed on the system. See
29+.BR REQUIREMENTS .
30+
31+Use URL escaping for username (and password, if provided via command line):
32+
33+.PP
34+.RS
35+mf://duplicity%40example.com@mediafire.com/some_folder
36+.PP
37+.RE
38+
39+The destination folder will be created for you if it does not exist.
40+
41 .SH A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING
42 Signing and symmetrically encrypt at the same time with the gpg binary on the
43 command line, as used within duplicity, is a specifically challenging issue.
44@@ -2075,6 +2101,10 @@
45 .B Python kerberos module
46 for kerberos authentication
47 - https://github.com/02strich/pykerberos
48+.TP
49+.BR "MediaFire backend"
50+.B MediaFire Python Open SDK
51+- https://pypi.python.org/pypi/mediafire/
52
53 .SH AUTHOR
54 .TP
55
56=== added file 'duplicity/backends/mediafirebackend.py'
57--- duplicity/backends/mediafirebackend.py 1970-01-01 00:00:00 +0000
58+++ duplicity/backends/mediafirebackend.py 2016-02-10 17:21:56 +0000
59@@ -0,0 +1,138 @@
60+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
61+#
62+# Copyright 2016 Roman Yepishev <rye@keypressure.com>
63+#
64+# This file is part of duplicity.
65+#
66+# Duplicity is free software; you can redistribute it and/or modify it
67+# under the terms of the GNU General Public License as published by the
68+# Free Software Foundation; either version 2 of the License, or (at your
69+# option) any later version.
70+#
71+# Duplicity is distributed in the hope that it will be useful, but
72+# WITHOUT ANY WARRANTY; without even the implied warranty of
73+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74+# General Public License for more details.
75+#
76+# You should have received a copy of the GNU General Public License
77+# along with duplicity; if not, write to the Free Software Foundation,
78+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
79+
80+"""MediaFire Duplicity Backend"""
81+
82+import os
83+
84+import duplicity.backend
85+
86+from duplicity.errors import BackendException
87+
88+DUPLICITY_APP_ID = '45593'
89+
90+
91+class MediafireBackend(duplicity.backend.Backend):
92+ """Use this backend when saving to MediaFire
93+
94+ URLs look like mf:/root/folder.
95+ """
96+ def __init__(self, parsed_url):
97+ try:
98+ import mediafire.client
99+ except ImportError:
100+ raise BackendException("This backend requires "
101+ "the mediafire library")
102+
103+ duplicity.backend.Backend.__init__(self, parsed_url)
104+
105+ mediafire_email = parsed_url.username
106+ mediafire_password = self.get_password()
107+
108+ self._file_res = mediafire.client.File
109+ self._folder_res = mediafire.client.Folder
110+ self._downloaderror_exc = mediafire.client.DownloadError
111+ self._notfound_exc = mediafire.client.ResourceNotFoundError
112+
113+ self.client = mediafire.client.MediaFireClient()
114+ self.client.login(app_id=DUPLICITY_APP_ID,
115+ email=mediafire_email,
116+ password=mediafire_password)
117+
118+ # //username:password@host/path/to/folder -> path/to/folder
119+ uri = 'mf:///' + parsed_url.path.split('/', 3)[3]
120+
121+ # Create folder if it does not exist and make sure it is private
122+ # See MediaFire Account Settings /Security and Privacy / Share Link
123+ # to set "Inherit from parent folder"
124+ try:
125+ folder = self.client.get_resource_by_uri(uri)
126+ if not isinstance(folder, self._folder_res):
127+ raise BackendException("target_url already exists "
128+ "and is not a folder")
129+ except mediafire.client.ResourceNotFoundError:
130+ # force folder to be private
131+ folder = self.client.create_folder(uri, recursive=True)
132+ self.client.update_folder_metadata(uri, privacy='private')
133+
134+ self.folder = folder
135+
136+ def _put(self, source_path, remote_filename=None):
137+ """Upload file"""
138+ # Use source file name if remote one is not defined
139+ if remote_filename is None:
140+ remote_filename = os.path.basename(source_path.name)
141+
142+ uri = self._build_uri(remote_filename)
143+
144+ with self.client.upload_session():
145+ self.client.upload_file(source_path.open('rb'), uri)
146+
147+ def _get(self, filename, local_path):
148+ """Download file"""
149+ uri = self._build_uri(filename)
150+ try:
151+ self.client.download_file(uri, local_path.open('wb'))
152+ except self._downloaderror_exc as ex:
153+ raise BackendException(ex)
154+
155+ def _list(self):
156+ """List files in backup directory"""
157+ uri = self._build_uri()
158+ filenames = []
159+ for item in self.client.get_folder_contents_iter(uri):
160+ if not isinstance(item, self._file_res):
161+ continue
162+
163+ filenames.append(item['filename'].encode('utf-8'))
164+
165+ return filenames
166+
167+ def _delete(self, filename):
168+ """Delete single file"""
169+ uri = self._build_uri(filename)
170+ self.client.delete_file(uri)
171+
172+ def _delete_list(self, filename_list):
173+ """Delete list of files"""
174+ for filename in filename_list:
175+ self._delete(filename)
176+
177+ def _query(self, filename):
178+ """Stat the remote file"""
179+ uri = self._build_uri(filename)
180+
181+ try:
182+ resource = self.client.get_resource_by_uri(uri)
183+ size = int(resource['size'])
184+ except self._notfound_exc:
185+ size = -1
186+
187+ return {'size': size}
188+
189+ def _build_uri(self, filename=None):
190+ """Build relative URI"""
191+ return (
192+ 'mf:' + self.folder["folderkey"] +
193+ ('/' + filename if filename else '')
194+ )
195+
196+
197+duplicity.backend.register_backend("mf", MediafireBackend)
198
199=== modified file 'duplicity/commandline.py'
200--- duplicity/commandline.py 2016-02-02 12:20:14 +0000
201+++ duplicity/commandline.py 2016-02-10 17:21:56 +0000
202@@ -918,6 +918,7 @@
203 onedrive://%(some_dir)s
204 azure://%(container_name)s
205 b2://%(account_id)s[:%(application_key)s]@%(bucket_name)s/[%(some_dir)s/]
206+ mf://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s
207
208 """ % dict
209

Subscribers

People subscribed via source and target branches