duplicity-team team mailing list archive
-
duplicity-team team
-
Mailing list archive
-
Message #03394
[Merge] lp:~rye/duplicity/mediafire into lp:duplicity
Roman Yepishev has proposed merging lp:~rye/duplicity/mediafire into lp:duplicity.
Requested reviews:
duplicity-team (duplicity-team)
For more details, see:
https://code.launchpad.net/~rye/duplicity/mediafire/+merge/285547
Backend for https://www.mediafire.com
Requires https://pypi.python.org/pypi/mediafire/ installed.
Requires MEDIAFIRE_EMAIL, MEDIAFIRE_PASSWORD environment variables as described in the man page.
To test:
1. Create an account at https://www.mediafire.com
2. Run:
pip install mediafire
export MEDIAFIRE_EMAIL=your-email@xxxxxxxxxxx
export MEDIAFIRE_PASSWORD=your-mediafire-password
duplicity /path/to/dir mf:///backup-directory
--
Your team duplicity-team is requested to review the proposed merge of lp:~rye/duplicity/mediafire into lp:duplicity.
=== modified file 'bin/duplicity.1'
--- bin/duplicity.1 2016-02-05 09:58:57 +0000
+++ bin/duplicity.1 2016-02-10 00:40:35 +0000
@@ -1205,6 +1205,16 @@
.B "A NOTE ON MULTI BACKEND"
below.
.RE
+.PP
+.BR "MediaFire"
+.PP
+.RS
+mf:///some_dir
+.PP
+See also
+.B "A NOTE ON MEDIAFIRE BACKEND"
+below.
+.RE
.SH TIME FORMATS
duplicity uses time strings in two places. Firstly, many of the files
@@ -1871,6 +1881,20 @@
.B SWIFT_AUTHVERSION
is unspecified, it will default to version 1.
+.SH A NOTE ON MEDIAFIRE BACKEND
+This backend requires
+.B mediafire
+python library to be installed on the system. See
+.BR REQUIREMENTS .
+
+It uses the following environment variables for authentication:
+.B MEDIAFIRE_EMAIL
+- account email,
+.B MEDIAFIRE_PASSWORD
+- account password.
+
+The destination folder will be created for you if it does not exist.
+
.SH A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING
Signing and symmetrically encrypt at the same time with the gpg binary on the
command line, as used within duplicity, is a specifically challenging issue.
@@ -2075,6 +2099,10 @@
.B Python kerberos module
for kerberos authentication
- https://github.com/02strich/pykerberos
+.TP
+.BR "MediaFire backend"
+.B MediaFire Python Open SDK
+- https://pypi.python.org/pypi/mediafire/
.SH AUTHOR
.TP
=== added file 'duplicity/backends/mediafirebackend.py'
--- duplicity/backends/mediafirebackend.py 1970-01-01 00:00:00 +0000
+++ duplicity/backends/mediafirebackend.py 2016-02-10 00:40:35 +0000
@@ -0,0 +1,144 @@
+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
+#
+# Copyright 2016 Roman Yepishev <rye@xxxxxxxxxxxxxxx>
+#
+# This file is part of duplicity.
+#
+# Duplicity is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# Duplicity is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with duplicity; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""MediaFire Duplicity Backend"""
+
+import os
+
+import duplicity.backend
+
+from duplicity.errors import BackendException
+
+DUPLICITY_APP_ID = '45593'
+
+
+class MediafireBackend(duplicity.backend.Backend):
+ """Use this backend when saving to MediaFire
+
+ URLs look like mf:/root/folder.
+ """
+ def __init__(self, parsed_url):
+ try:
+ import mediafire.client
+ except ImportError:
+ raise BackendException("This backend requires "
+ "the mediafire library")
+
+ if "MEDIAFIRE_EMAIL" not in os.environ:
+ raise BackendException("MEDIAFIRE_EMAIL environment variable "
+ "is not set")
+ if "MEDIAFIRE_PASSWORD" not in os.environ:
+ raise BackendException("MEDIAFIRE_PASSWORD environment variable "
+ "is not set")
+
+ self._file_res = mediafire.client.File
+ self._folder_res = mediafire.client.Folder
+ self._downloaderror_exc = mediafire.client.DownloadError
+ self._notfound_exc = mediafire.client.ResourceNotFoundError
+
+ mediafire_email = os.environ["MEDIAFIRE_EMAIL"]
+ mediafire_password = os.environ["MEDIAFIRE_PASSWORD"]
+
+ duplicity.backend.Backend.__init__(self, parsed_url)
+
+ self.client = mediafire.client.MediaFireClient()
+ self.client.login(app_id=DUPLICITY_APP_ID,
+ email=mediafire_email,
+ password=mediafire_password)
+
+ uri = 'mf:' + parsed_url.path
+
+ # Create folder if it does not exist and make sure it is private
+ # See MediaFire Account Settings /Security and Privacy / Share Link
+ # to set "Inherit from parent folder"
+ try:
+ folder = self.client.get_resource_by_uri(uri)
+ if not isinstance(folder, self._folder_res):
+ raise BackendException("target_url already exists "
+ "and is not a folder")
+ except mediafire.client.ResourceNotFoundError:
+ # force folder to be private
+ folder = self.client.create_folder(uri, recursive=True)
+ self.client.update_folder_metadata(uri, privacy='private')
+
+ self.folder = folder
+
+ def _put(self, source_path, remote_filename=None):
+ """Upload file"""
+ # Use source file name if remote one is not defined
+ if remote_filename is None:
+ remote_filename = os.path.basename(source_path.name)
+
+ uri = self._build_uri(remote_filename)
+
+ with self.client.upload_session():
+ self.client.upload_file(source_path.open('rb'), uri)
+
+ def _get(self, filename, local_path):
+ """Download file"""
+ uri = self._build_uri(filename)
+ try:
+ self.client.download_file(uri, local_path.open('wb'))
+ except self._downloaderror_exc as ex:
+ raise BackendException(ex)
+
+ def _list(self):
+ """List files in backup directory"""
+ uri = self._build_uri()
+ filenames = []
+ for item in self.client.get_folder_contents_iter(uri):
+ if not isinstance(item, self._file_res):
+ continue
+
+ filenames.append(item['filename'].encode('utf-8'))
+
+ return filenames
+
+ def _delete(self, filename):
+ """Delete single file"""
+ uri = self._build_uri(filename)
+ self.client.delete_file(uri)
+
+ def _delete_list(self, filename_list):
+ """Delete list of files"""
+ for filename in filename_list:
+ self._delete(filename)
+
+ def _query(self, filename):
+ """Stat the remote file"""
+ uri = self._build_uri(filename)
+
+ try:
+ resource = self.client.get_resource_by_uri(uri)
+ size = int(resource['size'])
+ except self._notfound_exc:
+ size = -1
+
+ return {'size': size}
+
+ def _build_uri(self, filename=None):
+ """Build relative URI"""
+ return (
+ 'mf:' + self.folder["folderkey"] +
+ ('/' + filename if filename else '')
+ )
+
+
+duplicity.backend.register_backend("mf", MediafireBackend)
=== modified file 'duplicity/commandline.py'
--- duplicity/commandline.py 2016-02-02 12:20:14 +0000
+++ duplicity/commandline.py 2016-02-10 00:40:35 +0000
@@ -918,6 +918,7 @@
onedrive://%(some_dir)s
azure://%(container_name)s
b2://%(account_id)s[:%(application_key)s]@%(bucket_name)s/[%(some_dir)s/]
+ mf:///%(some_dir)s
""" % dict