diff --git a/example/src/main/java/com/dracoon/sdk/example/Main.java b/example/src/main/java/com/dracoon/sdk/example/Main.java index 82aae0e..5ef5ce5 100644 --- a/example/src/main/java/com/dracoon/sdk/example/Main.java +++ b/example/src/main/java/com/dracoon/sdk/example/Main.java @@ -75,7 +75,10 @@ public static void main(String[] args) throws Exception { //searchNodesPaged(client); //createDownloadShare(client); - createDownloadShareEncrypted(client); + //createDownloadShareEncrypted(client); + + //generateMissingFileKeys(client); + generateMissingFileKeysForOneNode(client); } private static void getServerData(DracoonClient client) throws DracoonException { @@ -388,4 +391,15 @@ private static void createDownloadShareEncrypted(DracoonClient client) throws Dr dlShare.getAccessKey())); } + private static void generateMissingFileKeys(DracoonClient client) throws DracoonException { + client.nodes().generateMissingFileKeys(10); + } + + private static void generateMissingFileKeysForOneNode(DracoonClient client) + throws DracoonException { + long nodeId = 1L; + + client.nodes().generateMissingFileKeys(nodeId, 10); + } + } diff --git a/src/main/java/com/dracoon/sdk/DracoonClient.java b/src/main/java/com/dracoon/sdk/DracoonClient.java index b451a53..6c6ed64 100644 --- a/src/main/java/com/dracoon/sdk/DracoonClient.java +++ b/src/main/java/com/dracoon/sdk/DracoonClient.java @@ -314,7 +314,7 @@ void deleteNodes(DeleteNodesRequest request) throws DracoonNetIOException, * @return the new node * * @throws DracoonFileIOException If a file error occurred. - * @throws DracoonCryptoException If a the encryption failed. + * @throws DracoonCryptoException If the encryption failed. * @throws DracoonNetIOException If a network error occurred. * @throws DracoonApiException If the API responded with an error. */ @@ -332,7 +332,7 @@ Node uploadFile(String id, FileUploadRequest request, File file, * so on. (Null, if not needed.) * * @throws DracoonFileIOException If a file error occurred. - * @throws DracoonCryptoException If a the encryption failed. + * @throws DracoonCryptoException If the encryption failed. * @throws DracoonNetIOException If a network error occurred. * @throws DracoonApiException If the API responded with an error. */ @@ -358,7 +358,7 @@ void startUploadFileAsync(String id, FileUploadRequest request, File file, * * @throws DracoonNetIOException If a network error occurred. * @throws DracoonApiException If the API responded with an error. - * @throws DracoonCryptoException If a the decryption failed. + * @throws DracoonCryptoException If the decryption failed. * @throws DracoonFileIOException If a file error occurred. */ void downloadFile(String id, long nodeId, File file, @@ -376,12 +376,12 @@ void downloadFile(String id, long nodeId, File file, * * @throws DracoonNetIOException If a network error occurred. * @throws DracoonApiException If the API responded with an error. + * @throws DracoonCryptoException If the decryption failed. * @throws DracoonFileIOException If a file error occurred. - * @throws DracoonCryptoException If a the decryption failed. */ void startDownloadFileAsync(String id, long nodeId, File file, FileDownloadCallback callback) throws DracoonNetIOException, DracoonApiException, - DracoonFileIOException, DracoonCryptoException; + DracoonCryptoException, DracoonFileIOException; /** * Cancels an asynchronous file download. @@ -425,6 +425,55 @@ NodeList searchNodes(long parentNodeId, String searchString) NodeList searchNodes(long parentNodeId, String searchString, long offset, long limit) throws DracoonNetIOException, DracoonApiException; + /** + * Generates file keys for files with missing file keys. + * + * @throws DracoonNetIOException If a network error occurred. + * @throws DracoonApiException If the API responded with an error. + * @throws DracoonCryptoException If a encryption/decryption failed. + */ + void generateMissingFileKeys() throws DracoonNetIOException, DracoonApiException, + DracoonCryptoException; + + /** + * Generates file keys for files with missing file keys. The argument {@code limit} + * restricts the generation to a certain number. + * + * @param limit The number limit. (Number of records; must be positive.) + * + * @throws DracoonNetIOException If a network error occurred. + * @throws DracoonApiException If the API responded with an error. + * @throws DracoonCryptoException If a encryption/decryption failed. + */ + void generateMissingFileKeys(int limit) throws DracoonNetIOException, DracoonApiException, + DracoonCryptoException; + + /** + * Generates file keys for a file with missing file keys. + * + * @param nodeId The node ID of the file. + * + * @throws DracoonNetIOException If a network error occurred. + * @throws DracoonApiException If the API responded with an error. + * @throws DracoonCryptoException If a encryption/decryption failed. + */ + void generateMissingFileKeys(long nodeId) throws DracoonNetIOException, DracoonApiException, + DracoonCryptoException; + + /** + * Generates file keys for files with missing file keys. The argument {@code limit} + * restricts the generation to a certain number. + * + * @param nodeId The node ID of the file. + * @param limit The number limit. (Number of records; must be positive.) + * + * @throws DracoonNetIOException If a network error occurred. + * @throws DracoonApiException If the API responded with an error. + * @throws DracoonCryptoException If a encryption/decryption failed. + */ + void generateMissingFileKeys(long nodeId, int limit) throws DracoonNetIOException, + DracoonApiException, DracoonCryptoException; + } /** diff --git a/src/main/java/com/dracoon/sdk/error/DracoonApiCode.java b/src/main/java/com/dracoon/sdk/error/DracoonApiCode.java index a2d2ad8..eafde20 100644 --- a/src/main/java/com/dracoon/sdk/error/DracoonApiCode.java +++ b/src/main/java/com/dracoon/sdk/error/DracoonApiCode.java @@ -29,25 +29,27 @@ public enum DracoonApiCode { VALIDATION_BAD_FILE_NAME(-135, "Bad file name."), VALIDATION_EXPIRATION_DATE_IN_PAST(-136, "Expiration date is in past."), VALIDATION_EXPIRATION_DATE_TOO_LATE(-137, "Expiration date is too late."), - VALIDATION_FILE_KEY_MISSING(-138, "File key has to be provided."), - VALIDATION_FOLDER_FILE_ALREADY_EXISTS(-139, "A folder/file with the same name already exits."), - VALIDATION_ROOM_ALREADY_EXISTS(-140, "A room with the same name already exits."), - VALIDATION_FOLDER_ALREADY_EXISTS(-141, "A folder with the same name already exits."), - VALIDATION_FILE_ALREADY_EXISTS(-142, "A file with the same name already exits."), - VALIDATION_NODES_NOT_IN_SAME_PARENT(-143, "Folders/files must be in same parent."), - VALIDATION_CAN_NOT_MOVE_NODE_TO_OWN_PLACE(-144, "A node can\'t be moved to its own place."), - VALIDATION_ROOM_FOLDER_CAN_NOT_BE_OVERWRITTEN(-145, "A room or folder can\'t be overwritten."), - VALIDATION_FOLDER_CAN_NOT_BE_OVERWRITTEN(-146, "A folder can\'t be overwritten."), - VALIDATION_CAN_NOT_COPY_ROOM(-147, "A rooms can\'t be copied."), - VALIDATION_CAN_NOT_MOVE_ROOM(-148, "A rooms can\'t be moved."), - VALIDATION_PATH_TOO_LONG(-149, "Path is too long."), + VALIDATION_FOLDER_FILE_ALREADY_EXISTS(-138, "A folder/file with the same name already exits."), + VALIDATION_ROOM_ALREADY_EXISTS(-139, "A room with the same name already exits."), + VALIDATION_FOLDER_ALREADY_EXISTS(-140, "A folder with the same name already exits."), + VALIDATION_FILE_ALREADY_EXISTS(-141, "A file with the same name already exits."), + VALIDATION_NODES_NOT_IN_SAME_PARENT(-142, "Folders/files must be in same parent."), + VALIDATION_CAN_NOT_MOVE_NODE_TO_OWN_PLACE(-143, "A node can\'t be moved to its own place."), + VALIDATION_ROOM_FOLDER_CAN_NOT_BE_OVERWRITTEN(-144, "A room or folder can\'t be overwritten."), + VALIDATION_FOLDER_CAN_NOT_BE_OVERWRITTEN(-145, "A folder can\'t be overwritten."), + VALIDATION_CAN_NOT_COPY_ROOM(-146, "A rooms can\'t be copied."), + VALIDATION_CAN_NOT_MOVE_ROOM(-147, "A rooms can\'t be moved."), + VALIDATION_PATH_TOO_LONG(-148, "Path is too long."), + VALIDATION_USER_HAS_NO_KEY_PAIR(-149, "User has no encryption key pair."), VALIDATION_INVALID_USER_KEY_PAIR(-150, "Invalid encryption key pair."), - VALIDATION_ROOM_NOT_ENCRYPTED(-151, "Room not encrypted."), - VALIDATION_SOURCE_ROOM_NOT_ENCRYPTED(-152, "Encrypted files can\'t be copied or moved to an not encrypted room."), - VALIDATION_TARGET_ROOM_NOT_ENCRYPTED(-153, "Not encrypted files can\'t be copied or moved to an encrypted room."), - VALIDATION_CAN_NOT_CREATE_DL_SHARE_ON_ENCRYPTED_ROOM_FOLDER(-154, "A download share can\'t be created on a encrypted room or folder."), - VALIDATION_PASSWORD_NOT_SECURE(-155, "Password is not secure."), - VALIDATION_EMAIL_ADDRESS_INVALID(-156, "Invalid email address."), + VALIDATION_USER_HAS_NO_FILE_KEY(-151, "User has no encryption file key."), + VALIDATION_USER_FILE_KEY_MISSING(-152, "Encryption file key has to be provided."), + VALIDATION_ROOM_NOT_ENCRYPTED(-153, "Room not encrypted."), + VALIDATION_SOURCE_ROOM_NOT_ENCRYPTED(-154, "Encrypted files can\'t be copied or moved to an not encrypted room."), + VALIDATION_TARGET_ROOM_NOT_ENCRYPTED(-155, "Not encrypted files can\'t be copied or moved to an encrypted room."), + VALIDATION_CAN_NOT_CREATE_DL_SHARE_ON_ENCRYPTED_ROOM_FOLDER(-156, "A download share can\'t be created on a encrypted room or folder."), + VALIDATION_PASSWORD_NOT_SECURE(-157, "Password is not secure."), + VALIDATION_EMAIL_ADDRESS_INVALID(-158, "Invalid email address."), PERMISSION_ERROR(-160, "User has no permissions to execute the action in this room."), PERMISSION_MANAGE_ERROR(-161, "User has no permission to manage this room."), @@ -69,22 +71,21 @@ public enum DracoonApiCode { SERVER_TARGET_NODE_NOT_FOUND(-206, "Target room or folder was not found."), SERVER_TARGET_ROOM_NOT_FOUND(-207, "Target room was not found."), SERVER_TARGET_FOLDER_NOT_FOUND(-208, "Target folder was not found."), - SERVER_FILE_KEY_NOT_FOUND(-209, "File key could not be found."), - SERVER_UPLOAD_SEGMENT_INVALID(-210, "Provided file segment is invalid."), - SERVER_DOWNLOAD_SEGMENT_INVALID(-211, "Requested file segment is invalid."), - SERVER_INSUFFICIENT_STORAGE(-212, "Not enough free storage on the server."), - SERVER_USER_NOT_FOUND(-213, "User could not be found."), - SERVER_USER_ALREADY_APPROVED_EULA(-214, "User already approved EULA."), - SERVER_USER_CAN_NOT_CHANGE_NAME(-215, "User is not allowed to change his user name."), - SERVER_USER_ALREADY_EXISTS(-216, "A user with this user name already exists."), - SERVER_USER_CAN_NOT_CHANGE_PASSWORD(-217, "User is not allowed to change his password."), - SERVER_USER_CAN_NOT_RESET_PASSWORD(-218, "Password can\'t be reset for this user."), - SERVER_USER_RESET_PASSWORD_EVERY_5_MINUTES(-219, "Password can only be reset every 5 minutes."), + SERVER_UPLOAD_SEGMENT_INVALID(-209, "Provided file segment is invalid."), + SERVER_DOWNLOAD_SEGMENT_INVALID(-210, "Requested file segment is invalid."), + SERVER_INSUFFICIENT_STORAGE(-211, "Not enough free storage on the server."), + SERVER_USER_NOT_FOUND(-212, "User could not be found."), + SERVER_USER_ALREADY_APPROVED_EULA(-213, "User already approved EULA."), + SERVER_USER_CAN_NOT_CHANGE_NAME(-214, "User is not allowed to change his user name."), + SERVER_USER_ALREADY_EXISTS(-215, "A user with this user name already exists."), + SERVER_USER_CAN_NOT_CHANGE_PASSWORD(-216, "User is not allowed to change his password."), + SERVER_USER_CAN_NOT_RESET_PASSWORD(-217, "Password can\'t be reset for this user."), + SERVER_USER_RESET_PASSWORD_EVERY_5_MINUTES(-218, "Password can only be reset every 5 minutes."), + SERVER_USER_KEY_PAIR_NOT_FOUND(-219, "Encryption key pair was not found."), SERVER_USER_KEY_PAIR_ALREADY_SET(-220, "Encryption key pair was already set."), - SERVER_USER_KEY_PAIR_NOT_FOUND(-221, "Encryption key pair was not found."), - SERVER_USER_HAS_NO_FILE_KEY(-222, "User has no file key for the file."), - SERVER_SMS_IS_DISABLED(-223, "SMS sending is disabled."), - SERVER_SMS_COULD_NOT_BE_SEND(-224, "SMS could not be send."); + SERVER_USER_FILE_KEY_NOT_FOUND(-221, "Encryption file key was not found."), + SERVER_SMS_IS_DISABLED(-222, "SMS sending is disabled."), + SERVER_SMS_COULD_NOT_BE_SEND(-223, "SMS could not be send."); private final int mNumber; private final String mText; diff --git a/src/main/java/com/dracoon/sdk/internal/DracoonErrorParser.java b/src/main/java/com/dracoon/sdk/internal/DracoonErrorParser.java index a83730c..8d8dcab 100644 --- a/src/main/java/com/dracoon/sdk/internal/DracoonErrorParser.java +++ b/src/main/java/com/dracoon/sdk/internal/DracoonErrorParser.java @@ -304,7 +304,7 @@ else if (errorCode == -41053) return DracoonApiCode.PERMISSION_CREATE_ERROR; case NOT_FOUND: if (errorCode == -40014) - return DracoonApiCode.SERVER_USER_HAS_NO_FILE_KEY; + return DracoonApiCode.VALIDATION_USER_HAS_NO_FILE_KEY; else if (errorCode == -41050) return DracoonApiCode.SERVER_FOLDER_FILE_NOT_FOUND; else if (errorCode == -41051) @@ -348,7 +348,7 @@ else if (errorCode == -41302) return DracoonApiCode.PERMISSION_ERROR; case NOT_FOUND: if (errorCode == -40014) - return DracoonApiCode.SERVER_USER_HAS_NO_FILE_KEY; + return DracoonApiCode.VALIDATION_USER_HAS_NO_FILE_KEY; else if (errorCode == -41050) return DracoonApiCode.SERVER_FOLDER_FILE_NOT_FOUND; else if (errorCode == -41051) @@ -365,7 +365,7 @@ else if (errorCode == -41051) } } - public DracoonApiCode parseCreateFileUploadError(Response response) { + public DracoonApiCode parseFileUploadCreateError(Response response) { ApiErrorResponse errorResponse = getErrorResponse(response); if (errorResponse == null) { return DracoonApiCode.SERVER_UNKNOWN_ERROR; @@ -417,7 +417,7 @@ public DracoonApiCode parseFileUploadError(Response response) { } } - public DracoonApiCode parseCompleteFileUploadError(Response response) { + public DracoonApiCode parseFileUploadCompleteError(Response response) { ApiErrorResponse errorResponse = getErrorResponse(response); if (errorResponse == null) { return DracoonApiCode.SERVER_UNKNOWN_ERROR; @@ -429,7 +429,7 @@ public DracoonApiCode parseCompleteFileUploadError(Response response) { switch (HttpStatus.valueOf(statusCode)) { case BAD_REQUEST: if (errorCode == -40763) - return DracoonApiCode.VALIDATION_FILE_KEY_MISSING; + return DracoonApiCode.VALIDATION_USER_FILE_KEY_MISSING; else return DracoonApiCode.VALIDATION_UNKNOWN_ERROR; case CONFLICT: @@ -442,7 +442,7 @@ public DracoonApiCode parseCompleteFileUploadError(Response response) { } } - public DracoonApiCode parseDownloadTokenError(Response response) { + public DracoonApiCode parseDownloadTokenGetError(Response response) { ApiErrorResponse errorResponse = getErrorResponse(response); if (errorResponse == null) { return DracoonApiCode.SERVER_UNKNOWN_ERROR; @@ -459,6 +459,48 @@ public DracoonApiCode parseDownloadTokenError(Response response) { } } + public DracoonApiCode parseDownloadShareCreateError(Response response) { + ApiErrorResponse errorResponse = getErrorResponse(response); + if (errorResponse == null) { + return DracoonApiCode.SERVER_UNKNOWN_ERROR; + } + + int statusCode = response.code(); + int errorCode = (errorResponse.errorCode != null) ? errorResponse.errorCode : 0; + + switch (HttpStatus.valueOf(statusCode)) { + case BAD_REQUEST: + if (errorCode == -10002) + return DracoonApiCode.VALIDATION_PASSWORD_NOT_SECURE; + else if (errorCode == -50004) + return DracoonApiCode.VALIDATION_CAN_NOT_CREATE_DL_SHARE_ON_ENCRYPTED_ROOM_FOLDER; + else if (errorCode == -80006) + return DracoonApiCode.VALIDATION_EXPIRATION_DATE_IN_PAST; + else if (errorCode == -80008) + return DracoonApiCode.VALIDATION_EXPIRATION_DATE_TOO_LATE; + else if (errorCode == -80009) + return DracoonApiCode.VALIDATION_EMAIL_ADDRESS_INVALID; + else if (errorCode == -80030) + return DracoonApiCode.SERVER_SMS_IS_DISABLED; + else + return DracoonApiCode.VALIDATION_UNKNOWN_ERROR; + case FORBIDDEN: + return DracoonApiCode.PERMISSION_READ_ERROR; + case NOT_FOUND: + if (errorCode == -40000) + return DracoonApiCode.SERVER_FOLDER_FILE_NOT_FOUND; + else + return DracoonApiCode.SERVER_UNKNOWN_ERROR; + case BAD_GATEWAY: + if (errorCode == -90090) + return DracoonApiCode.SERVER_SMS_COULD_NOT_BE_SEND; + else + return DracoonApiCode.SERVER_UNKNOWN_ERROR; + default: + return parseStandardError(statusCode, errorCode); + } + } + public DracoonApiCode parseFileKeyQueryError(Response response) { ApiErrorResponse errorResponse = getErrorResponse(response); if (errorResponse == null) { @@ -473,7 +515,7 @@ public DracoonApiCode parseFileKeyQueryError(Response response) { if (errorCode == -40751) return DracoonApiCode.SERVER_FILE_NOT_FOUND; else if (errorCode == -40761) - return DracoonApiCode.SERVER_FILE_KEY_NOT_FOUND; + return DracoonApiCode.SERVER_USER_FILE_KEY_NOT_FOUND; else return DracoonApiCode.SERVER_UNKNOWN_ERROR; default: @@ -481,7 +523,7 @@ else if (errorCode == -40761) } } - public DracoonApiCode parseDownloadShareCreateError(Response response) { + public DracoonApiCode parseMissingFileKeysQueryError(Response response) { ApiErrorResponse errorResponse = getErrorResponse(response); if (errorResponse == null) { return DracoonApiCode.SERVER_UNKNOWN_ERROR; @@ -492,30 +534,51 @@ public DracoonApiCode parseDownloadShareCreateError(Response response) { switch (HttpStatus.valueOf(statusCode)) { case BAD_REQUEST: - if (errorCode == -10002) - return DracoonApiCode.VALIDATION_PASSWORD_NOT_SECURE; - else if (errorCode == -50004) - return DracoonApiCode.VALIDATION_CAN_NOT_CREATE_DL_SHARE_ON_ENCRYPTED_ROOM_FOLDER; - else if (errorCode == -80006) - return DracoonApiCode.VALIDATION_EXPIRATION_DATE_IN_PAST; - else if (errorCode == -80008) - return DracoonApiCode.VALIDATION_EXPIRATION_DATE_TOO_LATE; - else if (errorCode == -80009) - return DracoonApiCode.VALIDATION_EMAIL_ADDRESS_INVALID; - else if (errorCode == -80030) - return DracoonApiCode.SERVER_SMS_IS_DISABLED; + if (errorCode == -40001) + return DracoonApiCode.VALIDATION_ROOM_NOT_ENCRYPTED; else return DracoonApiCode.VALIDATION_UNKNOWN_ERROR; - case FORBIDDEN: - return DracoonApiCode.PERMISSION_READ_ERROR; case NOT_FOUND: if (errorCode == -40000) - return DracoonApiCode.SERVER_FOLDER_FILE_NOT_FOUND; + return DracoonApiCode.SERVER_ROOM_NOT_FOUND; + else if (errorCode == -40751) + return DracoonApiCode.SERVER_FILE_NOT_FOUND; + else if (errorCode == -70501) + return DracoonApiCode.SERVER_USER_NOT_FOUND; else return DracoonApiCode.SERVER_UNKNOWN_ERROR; - case BAD_GATEWAY: - if (errorCode == -90090) - return DracoonApiCode.SERVER_SMS_COULD_NOT_BE_SEND; + default: + return parseStandardError(statusCode, errorCode); + } + } + + public DracoonApiCode parseFileKeysSetError(Response response) { + ApiErrorResponse errorResponse = getErrorResponse(response); + if (errorResponse == null) { + return DracoonApiCode.SERVER_UNKNOWN_ERROR; + } + + int statusCode = response.code(); + int errorCode = (errorResponse.errorCode != null) ? errorResponse.errorCode : 0; + + switch (HttpStatus.valueOf(statusCode)) { + case BAD_REQUEST: + if (errorCode == -40001) + return DracoonApiCode.VALIDATION_ROOM_NOT_ENCRYPTED; + else + return DracoonApiCode.VALIDATION_UNKNOWN_ERROR; + case FORBIDDEN: + if (errorCode == -40761) + return DracoonApiCode.VALIDATION_USER_HAS_NO_FILE_KEY; + else if (errorCode == -70020) + return DracoonApiCode.VALIDATION_USER_HAS_NO_KEY_PAIR; + else + return DracoonApiCode.SERVER_UNKNOWN_ERROR; + case NOT_FOUND: + if (errorCode == -40751) + return DracoonApiCode.SERVER_FILE_NOT_FOUND; + else if (errorCode == -70501) + return DracoonApiCode.SERVER_USER_NOT_FOUND; else return DracoonApiCode.SERVER_UNKNOWN_ERROR; default: diff --git a/src/main/java/com/dracoon/sdk/internal/DracoonNodesImpl.java b/src/main/java/com/dracoon/sdk/internal/DracoonNodesImpl.java index 3874ddb..b10cba3 100644 --- a/src/main/java/com/dracoon/sdk/internal/DracoonNodesImpl.java +++ b/src/main/java/com/dracoon/sdk/internal/DracoonNodesImpl.java @@ -20,17 +20,24 @@ import com.dracoon.sdk.internal.mapper.FolderMapper; import com.dracoon.sdk.internal.mapper.NodeMapper; import com.dracoon.sdk.internal.mapper.RoomMapper; +import com.dracoon.sdk.internal.mapper.UserMapper; import com.dracoon.sdk.internal.model.ApiCopyNodesRequest; import com.dracoon.sdk.internal.model.ApiCreateFolderRequest; import com.dracoon.sdk.internal.model.ApiCreateRoomRequest; import com.dracoon.sdk.internal.model.ApiDeleteNodesRequest; +import com.dracoon.sdk.internal.model.ApiFileIdFileKey; import com.dracoon.sdk.internal.model.ApiFileKey; +import com.dracoon.sdk.internal.model.ApiMissingFileKeys; import com.dracoon.sdk.internal.model.ApiMoveNodesRequest; import com.dracoon.sdk.internal.model.ApiNode; import com.dracoon.sdk.internal.model.ApiNodeList; +import com.dracoon.sdk.internal.model.ApiSetFileKeysRequest; import com.dracoon.sdk.internal.model.ApiUpdateFileRequest; import com.dracoon.sdk.internal.model.ApiUpdateFolderRequest; import com.dracoon.sdk.internal.model.ApiUpdateRoomRequest; +import com.dracoon.sdk.internal.model.ApiUserIdFileId; +import com.dracoon.sdk.internal.model.ApiUserIdFileIdFileKey; +import com.dracoon.sdk.internal.model.ApiUserIdUserPublicKey; import com.dracoon.sdk.internal.validator.FileValidator; import com.dracoon.sdk.internal.validator.FolderValidator; import com.dracoon.sdk.internal.validator.NodeValidator; @@ -57,7 +64,9 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -440,7 +449,7 @@ public void startUploadFileAsync(String id, FileUploadRequest request, File file userPublicKey = userKeyPair.getUserPublicKey(); } - FileUploadCallback stoppedCallback = new FileUploadCallback() { + FileUploadCallback internalCallback = new FileUploadCallback() { @Override public void onStarted(String id) { @@ -475,7 +484,7 @@ public void onFailed(String id, DracoonException e) { } upload.addCallback(callback); - upload.addCallback(stoppedCallback); + upload.addCallback(internalCallback); mUploads.put(id, upload); @@ -632,6 +641,159 @@ private NodeList searchNodesInternally(long parentNodeId, String searchString, L return NodeMapper.fromApiNodeList(data); } + // --- File key generation methods --- + + @Override + public void generateMissingFileKeys() throws DracoonNetIOException, DracoonApiException, + DracoonCryptoException { + generateMissingFileKeysInternally(null, null); + } + + @Override + public void generateMissingFileKeys(int limit) throws DracoonNetIOException, DracoonApiException, + DracoonCryptoException { + generateMissingFileKeysInternally(null, limit); + } + + @Override + public void generateMissingFileKeys(long nodeId) throws DracoonNetIOException, DracoonApiException, + DracoonCryptoException { + generateMissingFileKeysInternally(nodeId, null); + } + + @Override + public void generateMissingFileKeys(long nodeId, int limit) throws DracoonNetIOException, + DracoonApiException, DracoonCryptoException { + generateMissingFileKeysInternally(nodeId, limit); + } + + private void generateMissingFileKeysInternally(Long nodeId, Integer limit) + throws DracoonNetIOException, DracoonApiException, DracoonCryptoException { + assertServerApiVersion(); + + Long batchOffset = 0L; + Long batchLimit = 10L; + + UserKeyPair userKeyPair = mClient.getAccountImpl().getAndCheckUserKeyPair(); + String userPrivateKeyPassword = mClient.getEncryptionPassword(); + + boolean isFinished = false; + while (!isFinished) { + isFinished = generateMissingFileKeysBatch(nodeId, batchOffset, batchLimit, + userKeyPair.getUserPrivateKey(), userPrivateKeyPassword); + batchOffset = batchOffset + batchLimit; + if (limit != null && batchOffset > limit) { + break; + } + } + } + + private boolean generateMissingFileKeysBatch(Long nodeId, Long offset, Long limit, + UserPrivateKey userPrivateKey, String userPrivateKeyPassword) + throws DracoonNetIOException, DracoonApiException, DracoonCryptoException { + ApiMissingFileKeys apiMissingFileKeys = getMissingFileKeysBatch(nodeId, offset, limit); + if (apiMissingFileKeys.items.isEmpty()) { + return true; + } + + List apiUserIdFileIds = apiMissingFileKeys.items; + Map userPublicKeys = convertUserPublicKeys(apiMissingFileKeys.users); + Map encryptedFileKeys = convertFileKeys(apiMissingFileKeys.files); + Map plainFileKeys = decryptFileKeys(encryptedFileKeys, userPrivateKey, + userPrivateKeyPassword); + + List apiUserIdFileIdFileKeys = new ArrayList<>(); + + for (ApiUserIdFileId apiUserIdFileId : apiUserIdFileIds) { + UserPublicKey userPublicKey = userPublicKeys.get(apiUserIdFileId.userId); + PlainFileKey plainFileKey = plainFileKeys.get(apiUserIdFileId.fileId); + + EncryptedFileKey encryptedFileKey = encryptFileKey(apiUserIdFileId.fileId, plainFileKey, + userPublicKey); + + ApiFileKey apiFileKey = FileMapper.toApiFileKey(encryptedFileKey); + + ApiUserIdFileIdFileKey apiUserIdFileIdFileKey = new ApiUserIdFileIdFileKey(); + apiUserIdFileIdFileKey.userId = apiUserIdFileId.userId; + apiUserIdFileIdFileKey.fileId = apiUserIdFileId.fileId; + apiUserIdFileIdFileKey.fileKey = apiFileKey; + + apiUserIdFileIdFileKeys.add(apiUserIdFileIdFileKey); + } + + setFileKeysBatch(apiUserIdFileIdFileKeys); + + return false; + } + + private ApiMissingFileKeys getMissingFileKeysBatch(Long nodeId, Long offset, Long limit) + throws DracoonNetIOException, DracoonApiException { + String accessToken = mClient.getAccessToken(); + Call call = mService.getMissingFileKeys(accessToken, nodeId, offset, + limit); + Response response = mHttpHelper.executeRequest(call); + + if (!response.isSuccessful()) { + DracoonApiCode errorCode = mErrorParser.parseMissingFileKeysQueryError(response); + String errorText = String.format("Query of missing file keys failed with '%s'!", + errorCode.name()); + mLog.d(LOG_TAG, errorText); + throw new DracoonApiException(errorCode); + } + + return response.body(); + } + + private Map convertUserPublicKeys( + List apiUserIdUserPublicKeys) { + Map userPublicKeys = new HashMap<>(); + for (ApiUserIdUserPublicKey apiUserIdUserPublicKey : apiUserIdUserPublicKeys) { + UserPublicKey userPublicKey = UserMapper.fromApiUserPublicKey( + apiUserIdUserPublicKey.publicKeyContainer); + userPublicKeys.put(apiUserIdUserPublicKey.id, userPublicKey); + } + return userPublicKeys; + } + + private Map convertFileKeys(List apiFileIdFileKeys) { + Map encryptedFileKeys = new HashMap<>(); + for (ApiFileIdFileKey apiFileIdFileKey : apiFileIdFileKeys) { + EncryptedFileKey encryptedFileKey = FileMapper.fromApiFileKey( + apiFileIdFileKey.fileKeyContainer); + encryptedFileKeys.put(apiFileIdFileKey.id, encryptedFileKey); + } + return encryptedFileKeys; + } + + private Map decryptFileKeys(Map encryptedFileKeys, + UserPrivateKey userPrivateKey, String userPrivateKeyPassword) + throws DracoonCryptoException { + Map plainFileKeys = new HashMap<>(); + for (Map.Entry encryptedFileKey : encryptedFileKeys.entrySet()) { + PlainFileKey plainFileKey = decryptFileKey(null, encryptedFileKey.getValue(), + userPrivateKey, userPrivateKeyPassword); + plainFileKeys.put(encryptedFileKey.getKey(), plainFileKey); + } + return plainFileKeys; + } + + private void setFileKeysBatch(List apiUserIdFileIdFileKeys) + throws DracoonNetIOException, DracoonApiException { + String accessToken = mClient.getAccessToken(); + ApiSetFileKeysRequest request = new ApiSetFileKeysRequest(); + request.items = apiUserIdFileIdFileKeys; + Call call = mService.setFileKeys(accessToken, request); + Response response = mHttpHelper.executeRequest(call); + + if (!response.isSuccessful()) { + DracoonApiCode errorCode = mErrorParser.parseFileKeysSetError(response); + String errorText = String.format("Setting missing file keys failed with '%s'!", + errorCode.name()); + mLog.d(LOG_TAG, errorText); + throw new DracoonApiException(errorCode); + } + } + // --- File key methods --- public PlainFileKey createFileKey(String version) throws DracoonCryptoException { diff --git a/src/main/java/com/dracoon/sdk/internal/DracoonService.java b/src/main/java/com/dracoon/sdk/internal/DracoonService.java index 91ca9a4..4d9d8cb 100644 --- a/src/main/java/com/dracoon/sdk/internal/DracoonService.java +++ b/src/main/java/com/dracoon/sdk/internal/DracoonService.java @@ -12,11 +12,13 @@ import com.dracoon.sdk.internal.model.ApiDownloadToken; import com.dracoon.sdk.internal.model.ApiFileKey; import com.dracoon.sdk.internal.model.ApiFileUpload; +import com.dracoon.sdk.internal.model.ApiMissingFileKeys; import com.dracoon.sdk.internal.model.ApiMoveNodesRequest; import com.dracoon.sdk.internal.model.ApiNode; import com.dracoon.sdk.internal.model.ApiNodeList; import com.dracoon.sdk.internal.model.ApiServerTime; import com.dracoon.sdk.internal.model.ApiServerVersion; +import com.dracoon.sdk.internal.model.ApiSetFileKeysRequest; import com.dracoon.sdk.internal.model.ApiUpdateFileRequest; import com.dracoon.sdk.internal.model.ApiUpdateFolderRequest; import com.dracoon.sdk.internal.model.ApiUpdateRoomRequest; @@ -149,6 +151,16 @@ Call getDownloadToken(@Header(AUTHORIZATION_HEADER) String tok Call getFileKey(@Header(AUTHORIZATION_HEADER) String token, @Path("file_id") Long fileId); + @GET(API_PATH + "/nodes/missingFileKeys") + Call getMissingFileKeys(@Header(AUTHORIZATION_HEADER) String token, + @Query("file_id") Long fileId, + @Query("offset") Long offset, + @Query("limit") Long limit); + + @POST(API_PATH + "/nodes/files/keys") + Call setFileKeys(@Header(AUTHORIZATION_HEADER) String token, + @Body ApiSetFileKeysRequest request); + @POST(API_PATH + "/shares/downloads") Call createDownloadShare(@Header(AUTHORIZATION_HEADER) String token, @Body ApiCreateDownloadShareRequest request); diff --git a/src/main/java/com/dracoon/sdk/internal/DracoonSharesImpl.java b/src/main/java/com/dracoon/sdk/internal/DracoonSharesImpl.java index 28f700e..3b0b39a 100644 --- a/src/main/java/com/dracoon/sdk/internal/DracoonSharesImpl.java +++ b/src/main/java/com/dracoon/sdk/internal/DracoonSharesImpl.java @@ -39,8 +39,8 @@ public DownloadShare createDownloadShare(CreateDownloadShareRequest request) if (isEncrypted) { long nodeId = request.getNodeId(); - String creatorEncPw = mClient.getEncryptionPassword(); UserKeyPair creatorKeyPair = mClient.getAccountImpl().getAndCheckUserKeyPair(); + String creatorEncPw = mClient.getEncryptionPassword(); EncryptedFileKey creatorEncFileKey = mClient.getNodesImpl().getFileKey(nodeId); PlainFileKey plainFileKey = mClient.getNodesImpl().decryptFileKey(nodeId, diff --git a/src/main/java/com/dracoon/sdk/internal/EncFileUpload.java b/src/main/java/com/dracoon/sdk/internal/EncFileUpload.java index 8605272..55e2dea 100644 --- a/src/main/java/com/dracoon/sdk/internal/EncFileUpload.java +++ b/src/main/java/com/dracoon/sdk/internal/EncFileUpload.java @@ -141,7 +141,7 @@ private ApiNode completeUpload(String uploadId, String fileName, Response response = mHttpHelper.executeRequest(call, this); if (!response.isSuccessful()) { - DracoonApiCode errorCode = mErrorParser.parseCompleteFileUploadError(response); + DracoonApiCode errorCode = mErrorParser.parseFileUploadCompleteError(response); String errorText = String.format("Completion of upload '%s' failed with '%s'!", mId, errorCode.name()); mLog.d(LOG_TAG, errorText); diff --git a/src/main/java/com/dracoon/sdk/internal/FileDownload.java b/src/main/java/com/dracoon/sdk/internal/FileDownload.java index 9839509..a95e14b 100644 --- a/src/main/java/com/dracoon/sdk/internal/FileDownload.java +++ b/src/main/java/com/dracoon/sdk/internal/FileDownload.java @@ -114,7 +114,7 @@ protected String getDownloadUrl(long nodeId) throws DracoonNetIOException, Draco Response response = mHttpHelper.executeRequest(call, this); if (!response.isSuccessful()) { - DracoonApiCode errorCode = mErrorParser.parseDownloadTokenError(response); + DracoonApiCode errorCode = mErrorParser.parseDownloadTokenGetError(response); String errorText = String.format("Creation of file download '%s' for file '%d' " + "failed with '%s'!", mId, nodeId, errorCode.name()); mLog.d(LOG_TAG, errorText); diff --git a/src/main/java/com/dracoon/sdk/internal/FileUpload.java b/src/main/java/com/dracoon/sdk/internal/FileUpload.java index 7719af9..7467c05 100644 --- a/src/main/java/com/dracoon/sdk/internal/FileUpload.java +++ b/src/main/java/com/dracoon/sdk/internal/FileUpload.java @@ -195,7 +195,7 @@ protected String createUpload(long parentNodeId, String name, int classification Response response = mHttpHelper.executeRequest(call, this); if (!response.isSuccessful()) { - DracoonApiCode errorCode = mErrorParser.parseCreateFileUploadError(response); + DracoonApiCode errorCode = mErrorParser.parseFileUploadCreateError(response); String errorText = String.format("Creation of upload '%s' failed with '%s'!", mId, errorCode.name()); mLog.d(LOG_TAG, errorText); @@ -273,7 +273,7 @@ private ApiNode completeUpload(String uploadId, String fileName, Response response = mHttpHelper.executeRequest(call, this); if (!response.isSuccessful()) { - DracoonApiCode errorCode = mErrorParser.parseCompleteFileUploadError(response); + DracoonApiCode errorCode = mErrorParser.parseFileUploadCompleteError(response); String errorText = String.format("Completion of upload '%s' failed with '%s'!", mId, errorCode.name()); mLog.d(LOG_TAG, errorText); diff --git a/src/main/java/com/dracoon/sdk/internal/mapper/FileMapper.java b/src/main/java/com/dracoon/sdk/internal/mapper/FileMapper.java index be77021..a718c5f 100644 --- a/src/main/java/com/dracoon/sdk/internal/mapper/FileMapper.java +++ b/src/main/java/com/dracoon/sdk/internal/mapper/FileMapper.java @@ -38,12 +38,12 @@ public static ApiFileKey toApiFileKey(EncryptedFileKey encryptedFileKey) { return apiFileKey; } - public static EncryptedFileKey fromApiFileKey(ApiFileKey apiEncryptedFileKey) { + public static EncryptedFileKey fromApiFileKey(ApiFileKey apiFileKey) { EncryptedFileKey encryptedFileKey = new EncryptedFileKey(); - encryptedFileKey.setKey(apiEncryptedFileKey.key); - encryptedFileKey.setIv(apiEncryptedFileKey.iv); - encryptedFileKey.setTag(apiEncryptedFileKey.tag); - encryptedFileKey.setVersion(apiEncryptedFileKey.version); + encryptedFileKey.setKey(apiFileKey.key); + encryptedFileKey.setIv(apiFileKey.iv); + encryptedFileKey.setTag(apiFileKey.tag); + encryptedFileKey.setVersion(apiFileKey.version); return encryptedFileKey; } diff --git a/src/main/java/com/dracoon/sdk/internal/mapper/UserMapper.java b/src/main/java/com/dracoon/sdk/internal/mapper/UserMapper.java index d5eb510..efa6755 100644 --- a/src/main/java/com/dracoon/sdk/internal/mapper/UserMapper.java +++ b/src/main/java/com/dracoon/sdk/internal/mapper/UserMapper.java @@ -120,7 +120,7 @@ private static UserPrivateKey fromApiUserPrivateKey(ApiUserPrivateKey apiUserPri return userPrivateKey; } - private static UserPublicKey fromApiUserPublicKey(ApiUserPublicKey apiUserPublicKey) { + public static UserPublicKey fromApiUserPublicKey(ApiUserPublicKey apiUserPublicKey) { if (apiUserPublicKey == null) { return null; } diff --git a/src/main/java/com/dracoon/sdk/internal/model/ApiFileIdFileKey.java b/src/main/java/com/dracoon/sdk/internal/model/ApiFileIdFileKey.java new file mode 100644 index 0000000..b8eeb58 --- /dev/null +++ b/src/main/java/com/dracoon/sdk/internal/model/ApiFileIdFileKey.java @@ -0,0 +1,6 @@ +package com.dracoon.sdk.internal.model; + +public class ApiFileIdFileKey { + public Long id; + public ApiFileKey fileKeyContainer; +} diff --git a/src/main/java/com/dracoon/sdk/internal/model/ApiMissingFileKeys.java b/src/main/java/com/dracoon/sdk/internal/model/ApiMissingFileKeys.java new file mode 100644 index 0000000..b9dcc61 --- /dev/null +++ b/src/main/java/com/dracoon/sdk/internal/model/ApiMissingFileKeys.java @@ -0,0 +1,10 @@ +package com.dracoon.sdk.internal.model; + +import java.util.List; + +public class ApiMissingFileKeys { + public ApiRange range; + public List items; + public List users; + public List files; +} diff --git a/src/main/java/com/dracoon/sdk/internal/model/ApiSetFileKeysRequest.java b/src/main/java/com/dracoon/sdk/internal/model/ApiSetFileKeysRequest.java new file mode 100644 index 0000000..9b94002 --- /dev/null +++ b/src/main/java/com/dracoon/sdk/internal/model/ApiSetFileKeysRequest.java @@ -0,0 +1,7 @@ +package com.dracoon.sdk.internal.model; + +import java.util.List; + +public class ApiSetFileKeysRequest { + public List items; +} diff --git a/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdFileId.java b/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdFileId.java new file mode 100644 index 0000000..ae48980 --- /dev/null +++ b/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdFileId.java @@ -0,0 +1,6 @@ +package com.dracoon.sdk.internal.model; + +public class ApiUserIdFileId { + public Long userId; + public Long fileId; +} diff --git a/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdFileIdFileKey.java b/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdFileIdFileKey.java new file mode 100644 index 0000000..db64f9c --- /dev/null +++ b/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdFileIdFileKey.java @@ -0,0 +1,7 @@ +package com.dracoon.sdk.internal.model; + +public class ApiUserIdFileIdFileKey { + public Long userId; + public Long fileId; + public ApiFileKey fileKey; +} diff --git a/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdUserPublicKey.java b/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdUserPublicKey.java new file mode 100644 index 0000000..3993afc --- /dev/null +++ b/src/main/java/com/dracoon/sdk/internal/model/ApiUserIdUserPublicKey.java @@ -0,0 +1,6 @@ +package com.dracoon.sdk.internal.model; + +public class ApiUserIdUserPublicKey { + public Long id; + public ApiUserPublicKey publicKeyContainer; +} diff --git a/src/main/java/com/dracoon/sdk/model/DeleteNodesRequest.java b/src/main/java/com/dracoon/sdk/model/DeleteNodesRequest.java index 5274198..d8a215c 100644 --- a/src/main/java/com/dracoon/sdk/model/DeleteNodesRequest.java +++ b/src/main/java/com/dracoon/sdk/model/DeleteNodesRequest.java @@ -17,7 +17,7 @@ private DeleteNodesRequest() { } /** - * Returns IDs of nodes which should be deleted. + * Returns node IDs of nodes which should be deleted. * * @return The node IDs. */ @@ -38,7 +38,7 @@ public static class Builder { /** * Constructs a new builder. * - * @param ids IDs of the nodes which should be deleted. + * @param ids The node IDs of the nodes which should be deleted. */ public Builder(List ids) { mRequest = new DeleteNodesRequest(); diff --git a/src/main/java/com/dracoon/sdk/model/UpdateFileRequest.java b/src/main/java/com/dracoon/sdk/model/UpdateFileRequest.java index 41dcb8d..cac1f68 100644 --- a/src/main/java/com/dracoon/sdk/model/UpdateFileRequest.java +++ b/src/main/java/com/dracoon/sdk/model/UpdateFileRequest.java @@ -21,9 +21,9 @@ private UpdateFileRequest() { } /** - * Returns the ID of the file which should be updated. + * Returns the node ID of the file which should be updated. * - * @return the ID + * @return the node ID */ public Long getId() { return mId; diff --git a/src/main/java/com/dracoon/sdk/model/UpdateFolderRequest.java b/src/main/java/com/dracoon/sdk/model/UpdateFolderRequest.java index 23ce1ab..0957f0a 100644 --- a/src/main/java/com/dracoon/sdk/model/UpdateFolderRequest.java +++ b/src/main/java/com/dracoon/sdk/model/UpdateFolderRequest.java @@ -17,9 +17,9 @@ private UpdateFolderRequest() { } /** - * Returns the ID of the folder which should be updated. + * Returns the node ID of the folder which should be updated. * - * @return the ID + * @return the node ID */ public Long getId() { return mId; diff --git a/src/main/java/com/dracoon/sdk/model/UpdateRoomRequest.java b/src/main/java/com/dracoon/sdk/model/UpdateRoomRequest.java index fefbec5..1862034 100644 --- a/src/main/java/com/dracoon/sdk/model/UpdateRoomRequest.java +++ b/src/main/java/com/dracoon/sdk/model/UpdateRoomRequest.java @@ -18,9 +18,9 @@ private UpdateRoomRequest() { } /** - * Returns the ID of the room which should be updated. + * Returns the node ID of the room which should be updated. * - * @return the ID + * @return the node ID */ public Long getId() { return mId;