forked from westerngateguard/duplicity-pydrive-backend
-
Notifications
You must be signed in to change notification settings - Fork 1
/
pydrivebackend.py
141 lines (114 loc) · 5.98 KB
/
pydrivebackend.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2015 Yigal Asnis
#
# This file 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.
#
# It 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
import string
import os
import duplicity.backend
from duplicity.errors import BackendException
class PyDriveBackend(duplicity.backend.Backend):
"""Connect to remote store using PyDrive API"""
def __init__(self, parsed_url):
duplicity.backend.Backend.__init__(self, parsed_url)
try:
global pydrive
import httplib2
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
except ImportError:
raise BackendException('PyDrive backend requires PyDrive installation'
'Please read the manpage to fix.')
if 'GOOGLE_AUTH_MODE' not in os.environ:
raise BackendException('GOOGLE_AUTH_MODE environment variable not set. Please read the manpage to fix.')
auth_mode = os.environ['GOOGLE_AUTH_MODE']
if auth_mode != 'managed' and auth_mode != 'personal':
raise BackendException('GOOGLE_AUTH_MODE environment variable not set to either "managed" or "personal". Please read the manpage to fix.')
if auth_mode == 'managed':
if 'GOOGLE_DRIVE_ACCOUNT_KEY' not in os.environ:
raise BackendException('GOOGLE_DRIVE_ACCOUNT_KEY environment variable not set. Please read the manpage to fix.')
account_key = os.environ['GOOGLE_DRIVE_ACCOUNT_KEY']
credentials = SignedJwtAssertionCredentials(parsed_url.username + '@' + parsed_url.hostname, account_key, scope='https://www.googleapis.com/auth/drive')
credentials.authorize(httplib2.Http())
gauth = GoogleAuth()
gauth.credentials = credentials
else:
if 'GOOGLE_SECRETS_FILE' not in os.environ:
raise BackendException('GOOGLE_SECRETS_FILE environment variable not set. Please read the manpage to fix.')
secrets_file = os.environ['GOOGLE_SECRETS_FILE']
if 'GOOGLE_CREDENTIALS_FILE' not in os.environ:
raise BackendException('GOOGLE_CREDENTIALS_FILE environment variable not set. Please read the manpage to fix.')
credentials_file = os.environ['GOOGLE_CREDENTIALS_FILE']
gauth = GoogleAuth()
gauth.LoadClientConfigFile(secrets_file)
gauth.LoadCredentialsFile(credentials_file)
if gauth.credentials is None:
gauth.CommandLineAuth()
if gauth.access_token_expired:
gauth.Refresh()
else:
gauth.Authorize()
gauth.SaveCredentialsFile(credentials_file)
self.drive = GoogleDrive(gauth)
# Dirty way to find root folder id
file_list = self.drive.ListFile({'q': "'Root' in parents"}).GetList()
if file_list:
parent_folder_id = file_list[0]['parents'][0]['id']
else:
file_in_root = self.drive.CreateFile({'title': 'i_am_in_root'})
file_in_root.Upload()
parent_folder_id = file_in_root['parents'][0]['id']
# Fetch destination folder entry and create hierarchy if required.
folder_names = string.split(parsed_url.path, '/')
for folder_name in folder_names:
if not folder_name:
continue
file_list = self.drive.ListFile({'q': "'" + parent_folder_id + "' in parents"}).GetList()
folder = next((item for item in file_list if item['title'] == folder_name and item['mimeType'] == 'application/vnd.google-apps.folder'), None)
if folder is None:
folder = self.drive.CreateFile({'title': folder_name, 'mimeType': "application/vnd.google-apps.folder", 'parents': [{'id': parent_folder_id}]})
folder.Upload()
parent_folder_id = folder['id']
self.folder = parent_folder_id
def FilesList(self):
return self.drive.ListFile({'q': "'" + self.folder + "' in parents"}).GetList()
def id_by_name(self, filename):
try:
return next(item for item in self.FilesList() if item['title'] == filename)['id']
except:
return ''
def _put(self, source_path, remote_filename):
drive_file = self.drive.CreateFile({'title': remote_filename, 'parents': [{"kind": "drive#fileLink", "id": self.folder}]})
drive_file.SetContentFile(source_path.name)
drive_file.Upload()
def _get(self, remote_filename, local_path):
drive_file = self.drive.CreateFile({'id': self.id_by_name(remote_filename)})
drive_file.GetContentFile(local_path.name)
def _list(self):
return [item['title'] for item in self.FilesList()]
def _delete(self, filename):
file_id = self.id_by_name(filename)
drive_file = self.drive.CreateFile({'id': file_id})
drive_file.auth.service.files().delete(fileId=drive_file['id']).execute()
def _query(self, filename):
try:
size = int((item for item in self.FilesList() if item['title'] == filename).next()['fileSize'])
except:
size = -1
return {'size': size}
duplicity.backend.register_backend('pydrive', PyDriveBackend)
duplicity.backend.uses_netloc.extend(['pydrive'])