vutran vutran - 2 months ago 16
Python Question

Restore deleted files using Dropbox API

I was searching for a way to use Dropbox API to restore my files and Google brought me here.

First of all, a bit about my situation:

My computer got a virus that rename all of my files (idk if its recognized as rename or delete on Dropbox) and they are all synced to Dropbox. I want to download all my original files using Dropbox API.

As i can see on web interface, I can download them individually but I got like thousand files so I couldn't do it.

My problem:

I used Python API wrapper to work with Dropbox API. I first fetched all of my files and tried to get all of their revisions but the original files are not included in revision list.

Then I tried to list all of my files including deleted files and I can see my original files listed. I tried to download them using download endpoint but it returned

File not found
error. Has anyone bumped into something similar? How can I solve this issue?

My code snippet:

dbx = dropbox.Dropbox('token is intentionally hidden')
print dbx.users_get_current_account()
for entry in dbx.files_list_folder('', recursive = False, include_deleted = True).entries:
if (isinstance(entry, dropbox.files.FileMetadata) or isinstance(entry, dropbox.files.DeletedMetadata)):
if not entry.name.endswith('cerber2'):
print "name: ", entry.name, "| path: ", entry.path_lower
print repr(entry)
try:
meta, resp = dbx.files_download(entry.path_lower)
except dropbox.exceptions.ApiError as e:
print e.user_message_text
print "-" * 80

Answer

I've been scratching my head over this exact problem this afternoon, to try and restore the state of a friend's Dropbox which got hit by the same ransomware. I don't know whether you're still in need of a solution, but in a nutshell, here's the state of play.

In Dropbox API V2, every file has a unique ID. This is persisted across deletes, renames, moves, etc. This will be key to fixing this problem, as Dropbox tracks file history by file path, so as soon as the ransomware renamed your files, the option of simply rolling back your files programmatically was lost. To make things even more difficult, fetch a directory listing with include_deleted set to True and you'll notice that Dropbox don't include file IDs in the metadata for deletions. If they did, this would be a breeze.

So, here's what to do instead:

  1. Fetch a list of files, as normal.
  2. Split that list of files into two lists, existing files and those that have been deleted:

    deleted = list(filter(lambda file: isinstance(file, dropbox.files.DeletedMetadata), files.entries))

(where files is an instance of dropbox.files.ListFolderResult)

  1. Here's where things get a bit hairy when it comes to API calls. For each of the deleted files, you need to fetch a list of revisions, using dropbox.Dropbox.files_list_revisions. Take the first revision, which will be the newest, and store its ID alongside the file path. This is how we get an ID for a deleted file.
  2. Now that we (hopefully) have an ID for each deleted file in your folder/Dropbox, all that's left to do is to match up those IDs with the IDs of the existing encrypted .cerber2 files. Once you've done that, you'll have a mapping between the encrypted .cerber2 file and the original, decrypted file stored in your Dropbox history. Simply restore the old file and delete the files created by the virus.

I've purposefully left the actual implementation in code up to you, but I hope this helps.