diff options
author | aunsane <aunsane@gmail.com> | 2017-04-23 13:50:22 +0300 |
---|---|---|
committer | aunsane <aunsane@gmail.com> | 2017-04-23 13:50:22 +0300 |
commit | 3aac528057ce49c8497211a4348df5363af17e1e (patch) | |
tree | 2a4c1b8cad10d59bd5fd1682a1d62a6925001abf | |
parent | 5048672e81f3ee9aa864ef9d736a3d74da051754 (diff) |
CloudFile: implement GDrive upload
-rw-r--r-- | include/m_http.h | 2 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/google_api.h | 44 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/google_service.cpp | 93 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/google_service.h | 6 | ||||
-rw-r--r-- | plugins/CloudFile/src/cloud_service.cpp | 2 | ||||
-rw-r--r-- | plugins/CloudFile/src/file_transfer.h | 4 |
6 files changed, 100 insertions, 51 deletions
diff --git a/include/m_http.h b/include/m_http.h index 85feebdbb3..297f4e4466 100644 --- a/include/m_http.h +++ b/include/m_http.h @@ -42,6 +42,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define HTTP_CODE_PARTIAL_CONTENT 206 #define HTTP_CODE_MULTI_STATUS 207 +#define HTTP_CODE_SUCCESS(code) ((code) <= (HTTP_CODE_MULTI_STATUS) && (code) >= (HTTP_CODE_OK)) + // 3xx Redirection #define HTTP_CODE_MULTIPLE_CHOICES 300 #define HTTP_CODE_MOVED_PERMANENTLY 301 diff --git a/plugins/CloudFile/src/Services/google_api.h b/plugins/CloudFile/src/Services/google_api.h index 7df5446065..a48e9e878b 100644 --- a/plugins/CloudFile/src/Services/google_api.h +++ b/plugins/CloudFile/src/Services/google_api.h @@ -4,9 +4,11 @@ namespace GDriveAPI { #define GOOGLE_OAUTH "https://accounts.google.com/o/oauth2/v2" -#define GDRIVE_API "https://www.googleapis.com/drive/v2/files" +#define GDRIVE_API "https://www.googleapis.com/drive/v3/files" +#define GDRIVE_UPLOAD "https://www.googleapis.com/upload/drive/v3/files" +#define GDRIVE_SHARE "https://drive.google.com/open?id=%s" -#define GOOGLE_APP_ID "271668553802-3sd3tubkf165ibgrqnrhe3id8mcgnaf7.apps.googleusercontent.com" +#define GOOGLE_APP_ID "528761318515-9hp30q3pcsk7c3qhbajs5ntvi7aiqp0b.apps.googleusercontent.com" #include "../../../miranda-private-keys/Google/client_secret.h" class GetAccessTokenRequest : public HttpRequest @@ -37,25 +39,34 @@ namespace GDriveAPI class StartUploadFileRequest : public HttpRequest { public: - StartUploadFileRequest(const char *token) : - HttpRequest(REQUEST_POST, GDRIVE_API) + StartUploadFileRequest(const char *token, const char *name) : + HttpRequest(REQUEST_POST, GDRIVE_UPLOAD) { - AddBearerAuthHeader(token); - AddHeader("Content-Type", "application/json"); + AddUrlParameter("access_token=%s", token); AddUrlParameter("uploadType=resumable"); + + AddHeader("Content-Type", "application/json"); + + JSONNode params(JSON_NODE); + params << JSONNode("name", name); + + json_string data = params.write(); + SetData(data.c_str(), data.length()); } }; class UploadFileRequest : public HttpRequest { public: - UploadFileRequest(const char *token, const char *data, size_t size) : - HttpRequest(REQUEST_POST, GDRIVE_API) + UploadFileRequest(const char *uploadUri, const char *chunk, size_t chunkSize, uint64_t offset, uint64_t fileSize) : + HttpRequest(REQUEST_PUT, uploadUri) { - AddBearerAuthHeader(token); - AddUrlParameter("uploadType=resumable"); + uint64_t rangeMin = offset; + uint64_t rangeMax = offset + chunkSize - 1; + CMStringA range(CMStringDataFormat::FORMAT, "bytes %I64u-%I64u/%I64u", rangeMin, rangeMax, fileSize); + AddHeader("Content-Range", range); - SetData(data, size); + SetData(chunk, chunkSize); } }; @@ -65,7 +76,7 @@ namespace GDriveAPI CreateFolderRequest(const char *token, const char *path) : HttpRequest(REQUEST_PUT, GDRIVE_API) { - AddBearerAuthHeader(token); + AddUrlParameter("access_token=%s", token); AddHeader("Content-Type", "application/json"); JSONNode params(JSON_NODE); @@ -78,13 +89,14 @@ namespace GDriveAPI } }; - class ShareRequest : public HttpRequest + class GrantPermissionsRequest : public HttpRequest { public: - ShareRequest(const char *token, const char *fileId) : - HttpRequest(REQUEST_PUT, FORMAT, GDRIVE_API "/%s/permissions", fileId) + GrantPermissionsRequest(const char *token, const char *fileId) : + HttpRequest(REQUEST_POST, FORMAT, GDRIVE_API "/%s/permissions", fileId) { - AddBearerAuthHeader(token); + AddUrlParameter("access_token=%s", token); + AddHeader("Content-Type", "application/json"); JSONNode params(JSON_NODE); diff --git a/plugins/CloudFile/src/Services/google_service.cpp b/plugins/CloudFile/src/Services/google_service.cpp index f0185bfbb8..03b4880bd0 100644 --- a/plugins/CloudFile/src/Services/google_service.cpp +++ b/plugins/CloudFile/src/Services/google_service.cpp @@ -107,26 +107,46 @@ void CGDriveService::HandleJsonError(JSONNode &node) } } -void CGDriveService::StartUploadFile() +void CGDriveService::StartUploadFile(char *uploadUri, const char *name) { ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); - GDriveAPI::StartUploadFileRequest request(token); - NLHR_PTR response(request.Send(hConnection)); + GDriveAPI::StartUploadFileRequest request(token, name); + NETLIBHTTPREQUEST* response = request.Send(hConnection); - GetJsonResponse(response); + if (response == NULL) + throw Exception(HttpStatusToError()); + + if (HTTP_CODE_SUCCESS(response->resultCode)) { + for (int i = 0; i < response->headersCount; i++) + { + if (mir_strcmpi(response->headers[i].szName, "Location")) + continue; + + mir_strcpy(uploadUri, response->headers[i].szValue); + return; + } + } + + if (response->dataLength) + throw Exception(response->pData); + throw Exception(HttpStatusToError(response->resultCode)); } -void CGDriveService::UploadFile(const char *url, const char *data, size_t size) +void CGDriveService::UploadFile(const char *uploadUri, const char *chunk, size_t chunkSize, uint64_t offset, uint64_t fileSize, char *fileId) { - ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); - GDriveAPI::UploadFileRequest request(token, data, size); + GDriveAPI::UploadFileRequest request(uploadUri, chunk, chunkSize, offset, fileSize); NLHR_PTR response(request.Send(hConnection)); if (response == NULL) throw Exception(HttpStatusToError()); - if (response->resultCode >= HTTP_CODE_OK && - response->resultCode <= HTTP_CODE_MULTIPLE_CHOICES) { + if (response->resultCode == HTTP_CODE_PERMANENT_REDIRECT) + return; + + if (HTTP_CODE_SUCCESS(response->resultCode)) { + JSONNode root = GetJsonResponse(response); + JSONNode id = root.at("id"); + mir_strcpy(fileId, id.as_string().c_str()); return; } @@ -144,15 +164,24 @@ void CGDriveService::CreateFolder(const char *path) GetJsonResponse(response); } -void CGDriveService::CreateSharedLink(const char *path, char *url) +void CGDriveService::CreateSharedLink(const char *fileId, char *url) { ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); - GDriveAPI::ShareRequest request(token, path); + GDriveAPI::GrantPermissionsRequest request(token, fileId); NLHR_PTR response(request.Send(hConnection)); - JSONNode root = GetJsonResponse(response); - JSONNode link = root.at("href"); - mir_strcpy(url, link.as_string().c_str()); + if (response == NULL) + throw Exception(HttpStatusToError()); + + if (HTTP_CODE_SUCCESS(response->resultCode)) { + CMStringA sharedUrl(CMStringDataFormat::FORMAT, GDRIVE_SHARE, fileId); + mir_strcpy(url, sharedUrl); + return; + } + + if (response->dataLength) + throw Exception(response->pData); + throw Exception(HttpStatusToError(response->resultCode)); } UINT CGDriveService::Upload(FileTransferParam *ftp) @@ -181,26 +210,32 @@ UINT CGDriveService::Upload(FileTransferParam *ftp) const wchar_t *fileName = ftp->GetCurrentRelativeFilePath(); uint64_t fileSize = ftp->GetCurrentFileSize(); - char path[MAX_PATH]; - const wchar_t *serverFolder = ftp->GetServerFolder(); - if (serverFolder) { - wchar_t serverPath[MAX_PATH] = { 0 }; - mir_snwprintf(serverPath, L"%s\\%s", serverFolder, fileName); - PreparePath(serverPath, path); - } - else - PreparePath(fileName, path); - StartUploadFile(); + uint64_t offset = 0; + char fileId[32]; + char uploadUri[1024]; + StartUploadFile(uploadUri, T2Utf(fileName)); + + size_t chunkSize = ftp->GetCurrentFileChunkSize(); + mir_ptr<char>chunk((char*)mir_calloc(chunkSize)); - mir_ptr<char>data((char*)mir_calloc(fileSize)); - size_t size = ftp->ReadCurrentFile(data, fileSize); - UploadFile("", data, size); + size_t size = 0; + for (size_t i = 0; i < (fileSize / chunkSize); i++) + { + ftp->CheckCurrentFile(); - ftp->Progress(size); + size = ftp->ReadCurrentFile(chunk, chunkSize); + if (size == 0) + break; + + UploadFile(uploadUri, chunk, size, offset, fileSize, fileId); + + offset += size; + ftp->Progress(size); + } if (!wcschr(fileName, L'\\')) { char url[MAX_PATH]; - CreateSharedLink(path, url); + CreateSharedLink(fileId, url); ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(url))); } } while (ftp->NextFile()); diff --git a/plugins/CloudFile/src/Services/google_service.h b/plugins/CloudFile/src/Services/google_service.h index db72ef9dee..d9c99009c6 100644 --- a/plugins/CloudFile/src/Services/google_service.h +++ b/plugins/CloudFile/src/Services/google_service.h @@ -9,10 +9,10 @@ private: void HandleJsonError(JSONNode &node); - void StartUploadFile(); - void UploadFile(const char *url, const char *data, size_t size); + void StartUploadFile(char *uploadUri, const char *name); + void UploadFile(const char *uploadUri, const char *chunk, size_t chunkSize, uint64_t offset, uint64_t fileSize, char *fileId); void CreateFolder(const char *path); - void CreateSharedLink(const char *path, char *url); + void CreateSharedLink(const char *fileId, char *url); public: CGDriveService(HNETLIBUSER hConnection); diff --git a/plugins/CloudFile/src/cloud_service.cpp b/plugins/CloudFile/src/cloud_service.cpp index 9e57dc14aa..a14af9b821 100644 --- a/plugins/CloudFile/src/cloud_service.cpp +++ b/plugins/CloudFile/src/cloud_service.cpp @@ -10,7 +10,7 @@ LIST<CCloudService> Services(10, CompareServices); void InitServices() { Services.insert(new CDropboxService(hNetlibConnection)); - //Services.insert(new CGDriveService(hNetlibConnection)); + Services.insert(new CGDriveService(hNetlibConnection)); Services.insert(new CYandexService(hNetlibConnection)); PROTOCOLDESCRIPTOR pd = { sizeof(pd) }; diff --git a/plugins/CloudFile/src/file_transfer.h b/plugins/CloudFile/src/file_transfer.h index 16f515f856..700988eaf4 100644 --- a/plugins/CloudFile/src/file_transfer.h +++ b/plugins/CloudFile/src/file_transfer.h @@ -189,9 +189,9 @@ public: return pfts.currentFileSize; } - const uint64_t GetCurrentFileChunkSize() const + const size_t GetCurrentFileChunkSize() const { - int chunkSize = 1024 * 1024; + size_t chunkSize = 1024 * 1024; if (pfts.currentFileSize < chunkSize) chunkSize = min(pfts.currentFileSize, chunkSize / 4); else if (pfts.currentFileSize > 20 * chunkSize) |