From 04cf33a1d0a76891c9c7691251360b5c7cd0dfeb Mon Sep 17 00:00:00 2001 From: David Bailey Date: Tue, 22 Aug 2017 20:05:12 +0100 Subject: [PATCH] Update with requested changes --- AnkiConnect.py | 59 +++++++++++++++++++------------------------------- README.md | 29 +++++++++++-------------- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/AnkiConnect.py b/AnkiConnect.py index 40f29b3..b5f2774 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -27,6 +27,7 @@ import select import socket import sys from time import time +from unicodedata import normalize # @@ -336,43 +337,27 @@ class AnkiNoteParams: # class AnkiBridge: - def getFilePath(self, filename): - mediaFolder = self.collection().media.dir() - filePath = os.path.normpath(os.path.join(mediaFolder, filename)) - # catch attempts to write outside the media folder - if os.path.commonprefix([mediaFolder, filePath]) != mediaFolder: - return False - - return filePath + def storeMediaFile(self, filename, data): + self.deleteMediaFile(filename) + self.media().writeData(filename, base64.b64decode(data)) - def storeFile(self, filename, data): - filePath = self.getFilePath(filename) - if filePath: - with open(filePath, 'wb') as file: - file.write(base64.b64decode(data)) - return True + def retrieveMediaFile(self, filename): + # based on writeData from anki/media.py + filename = os.path.basename(filename) + filename = normalize("NFC", filename) + filename = self.media().stripIllegal(filename) + + path = os.path.join(self.media().dir(), filename) + if os.path.exists(path): + with open(path, 'rb') as file: + return base64.b64encode(file.read()).decode('ascii') return False - def retrieveFile(self, filename): - filePath = self.getFilePath(filename) - if filePath and os.path.isfile(filePath): - with open(filePath, 'rb') as file: - data = base64.b64encode(file.read()) - return data.decode('ascii') - - return False - - - def deleteFile(self, filename): - filePath = self.getFilePath(filename) - if filePath and os.path.isfile(filePath): - os.remove(filePath) - return True - - return False + def deleteMediaFile(self, filename): + self.media().syncDelete(filename) def addNote(self, params): @@ -922,18 +907,18 @@ class AnkiConnect: @webApi - def storeFile(self, filename, data): - return self.anki.storeFile(filename, data) + def storeMediaFile(self, filename, data): + return self.anki.storeMediaFile(filename, data) @webApi - def retrieveFile(self, filename): - return self.anki.retrieveFile(filename) + def retrieveMediaFile(self, filename): + return self.anki.retrieveMediaFile(filename) @webApi - def deleteFile(self, filename): - return self.anki.deleteFile(filename) + def deleteMediaFile(self, filename): + return self.anki.deleteMediaFile(filename) @webApi diff --git a/README.md b/README.md index 754c32a..219a110 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Categories: * [Card Suspension](#card-suspension) * [Card Intervals](#card-intervals) * [Finding Notes and Cards](#finding-notes-and-cards) -* [File Storage](#file-storage) +* [Media File Storage](#media-file-storage) * [Graphical](#graphical) ### Miscellaneous ### @@ -853,12 +853,11 @@ Categories: ] ``` -### File Storage ### +### Media File Storage ### -* **storeFile** +* **storeMediaFile** - Stores a file with the specified base64-encoded contents inside the media folder. Returns `true` upon success or - `false` if attempting to write a file outside the media folder. + Stores a file with the specified base64-encoded contents inside the media folder. Note: to prevent Anki from removing files not used by any cards (e.g. for configuration files), prefix the filename with an underscore. These files are still synchronized to AnkiWeb. @@ -866,7 +865,7 @@ Categories: *Sample request*: ``` { - "action": "storeFile", + "action": "storeMediaFile", "params": { "filename": "_hello.txt", "data": "SGVsbG8sIHdvcmxkIQ==" @@ -876,7 +875,7 @@ Categories: *Sample response*: ``` - true + null ``` *Content of `_hello.txt`*: @@ -884,15 +883,14 @@ Categories: Hello world! ``` -* **retrieveFile** +* **retrieveMediaFile** - Retrieves the base64-encoded contents of the specified file, returning `false` if the file does not exist or if - attempting to read a file outside the media folder. + Retrieves the base64-encoded contents of the specified file, returning `false` if the file does not exist. *Sample request*: ``` { - "action": "retrieveFile", + "action": "retrieveMediaFile", "params": { "filename": "_hello.txt" } @@ -904,15 +902,14 @@ Categories: "SGVsbG8sIHdvcmxkIQ==" ``` -* **deleteFile** +* **deleteMediaFile** - Deletes the specified file inside the media folder, returning `true` if successful, or `false` if the file does not - exist or if attempting to delete a file outside the media folder. + Deletes the specified file inside the media folder. *Sample request*: ``` { - "action": "deleteFile", + "action": "deleteMediaFile", "params": { "filename": "_hello.txt" } @@ -921,7 +918,7 @@ Categories: *Sample response*: ``` - true + null ``` ### Graphical ###