-
Notifications
You must be signed in to change notification settings - Fork 61
/
Copy pathrestore.py
113 lines (95 loc) · 4.09 KB
/
restore.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
#!/usr/bin/env python
import sys, os, dropbox, time
from datetime import datetime
APP_KEY = 'hacwza866qep9o6' # INSERT APP_KEY HERE
APP_SECRET = 'kgipko61g58n6uc' # INSERT APP_SECRET HERE
DELAY = 0.2 # delay between each file (try to stay under API rate limits)
HELP_MESSAGE = \
"""Note: You must specify the path starting with "/", where "/" is the root
of your dropbox folder. So if your dropbox directory is at "/home/user/dropbox"
and you want to restore "/home/user/dropbox/folder", the ROOTPATH is "/folder".
"""
HISTORY_WARNING = \
"""Dropbox only keeps historical file versions for 30 days (unless you have
enabled extended version history). Please specify a cutoff date within the past
30 days, or if you have extended version history, you may remove this check
from the source code."""
def authorize():
flow = dropbox.client.DropboxOAuth2FlowNoRedirect(APP_KEY, APP_SECRET)
authorize_url = flow.start()
print('1. Go to: ' + authorize_url)
print('2. Click "Allow" (you might have to log in first)')
print('3. Copy the authorization code.')
try:
input = raw_input
except NameError:
pass
code = input("Enter the authorization code here: ").strip()
access_token, user_id = flow.finish(code)
return access_token
def login(token_save_path):
if os.path.exists(token_save_path):
with open(token_save_path) as token_file:
access_token = token_file.read()
else:
access_token = authorize()
with open(token_save_path, 'w') as token_file:
token_file.write(access_token)
return dropbox.client.DropboxClient(access_token)
def parse_date(s):
a = s.split('+')[0].strip()
return datetime.strptime(a, '%a, %d %b %Y %H:%M:%S')
def restore_file(client, path, cutoff_datetime, is_deleted, verbose=False):
revisions = client.revisions(path.encode('utf8'))
revision_dict = dict((parse_date(r['modified']), r) for r in revisions)
# skip if current revision is the same as it was at the cutoff
if max(revision_dict.keys()) < cutoff_datetime:
if verbose:
print(path.encode('utf8') + ' SKIP')
return
# look for the most recent revision before the cutoff
pre_cutoff_modtimes = [d for d in revision_dict.keys()
if d < cutoff_datetime]
if len(pre_cutoff_modtimes) > 0:
modtime = max(pre_cutoff_modtimes)
rev = revision_dict[modtime]['rev']
if verbose:
print(path.encode('utf8') + ' ' + str(modtime))
client.restore(path.encode('utf8'), rev)
else: # there were no revisions before the cutoff, so delete
if verbose:
print(path.encode('utf8') + ' ' + ('SKIP' if is_deleted else 'DELETE'))
if not is_deleted:
client.file_delete(path.encode('utf8'))
def restore_folder(client, path, cutoff_datetime, verbose=False):
if verbose:
print('Restoring folder: ' + path.encode('utf8'))
try:
folder = client.metadata(path.encode('utf8'), list=True,
include_deleted=True)
except dropbox.rest.ErrorResponse as e:
print(str(e))
print(HELP_MESSAGE)
return
for item in folder.get('contents', []):
if item.get('is_dir', False):
restore_folder(client, item['path'], cutoff_datetime, verbose)
else:
restore_file(client, item['path'], cutoff_datetime,
item.get('is_deleted', False), verbose)
time.sleep(DELAY)
def main():
if len(sys.argv) != 3:
usage = 'usage: {0} ROOTPATH YYYY-MM-DD\n{1}'
sys.exit(usage.format(sys.argv[0], HELP_MESSAGE))
root_path_encoded, cutoff = sys.argv[1:]
root_path = root_path_encoded.decode(sys.stdin.encoding)
cutoff_datetime = datetime(*map(int, cutoff.split('-')))
if (datetime.utcnow() - cutoff_datetime).days >= 30:
sys.exit(HISTORY_WARNING)
if cutoff_datetime > datetime.utcnow():
sys.exit('Cutoff date must be in the past')
client = login('token.dat')
restore_folder(client, root_path, cutoff_datetime, verbose=True)
if __name__ == '__main__':
main()