diff options
author | aunsane <aunsane@gmail.com> | 2017-04-16 01:32:19 +0300 |
---|---|---|
committer | aunsane <aunsane@gmail.com> | 2017-04-16 01:32:58 +0300 |
commit | 0b9fa1d90f8d0aff7118837ceb1211b578a5a9c8 (patch) | |
tree | 3b8be7b839a98a3a52a38d713c2d708ada015510 /plugins/CloudFile/src/Services | |
parent | 008fb731e3e3b587f596afba1cfe7446de7f0cac (diff) |
CloudFile: initial commit
- Dropbox (worked)
- Yandex.Disk (worked)
- GDrive (not worked)
Diffstat (limited to 'plugins/CloudFile/src/Services')
-rw-r--r-- | plugins/CloudFile/src/Services/dropbox_api.h | 200 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/dropbox_service.cpp | 277 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/dropbox_service.h | 33 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/google_api.h | 101 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/google_service.cpp | 216 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/google_service.h | 31 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/yandex_api.h | 84 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/yandex_service.cpp | 219 | ||||
-rw-r--r-- | plugins/CloudFile/src/Services/yandex_service.h | 31 |
9 files changed, 1192 insertions, 0 deletions
diff --git a/plugins/CloudFile/src/Services/dropbox_api.h b/plugins/CloudFile/src/Services/dropbox_api.h new file mode 100644 index 0000000000..3db69250e8 --- /dev/null +++ b/plugins/CloudFile/src/Services/dropbox_api.h @@ -0,0 +1,200 @@ +#ifndef _DROPBOXSERVICE_API_H_ +#define _DROPBOXSERVICE_API_H_ + +namespace DropboxAPI +{ +#define DROPBOX_API_VER "/2" +#define DROPBOX_WWW_URL "https://www.dropbox.com" +#define DROPBOX_API "https://api.dropboxapi.com" +#define DROPBOX_API_RPC DROPBOX_API DROPBOX_API_VER +#define DROPBOX_CONTENT "https://content.dropboxapi.com" +#define DROPBOX_API_CU DROPBOX_CONTENT DROPBOX_API_VER + +#define DROPBOX_APP_KEY "fa8du7gkf2q8xzg" +#include "../../../miranda-private-keys/Dropbox/secret_key.h" + + class GetAccessTokenRequest : public HttpRequest + { + public: + GetAccessTokenRequest(const char *requestToken) : + HttpRequest(REQUEST_POST, DROPBOX_API "/oauth2/token") + { + AddHeader("Content-Type", "application/x-www-form-urlencoded"); + + CMStringA data(CMStringDataFormat::FORMAT, + "client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s", + DROPBOX_APP_KEY, DROPBOX_API_SECRET, requestToken); + SetData(data.GetBuffer(), data.GetLength()); + } + }; + + class RevokeAccessTokenRequest : public HttpRequest + { + public: + RevokeAccessTokenRequest(const char *token) : + HttpRequest(REQUEST_POST, DROPBOX_API "/oauth2/token/revoke") + { + AddBearerAuthHeader(token); + } + }; + + class UploadFileRequest : public HttpRequest + { + public: + UploadFileRequest(const char *token, const char *path, const char *data, size_t size) : + HttpRequest(REQUEST_POST, DROPBOX_API_CU "/files/upload") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/octet-stream"); + + JSONNode params(JSON_NODE); + params + << JSONNode("path", path) + << JSONNode("mode", "overwrite"); + + AddHeader("Dropbox-API-Arg", params.write().c_str()); + + SetData(data, size); + } + }; + + class StartUploadSessionRequest : public HttpRequest + { + public: + StartUploadSessionRequest(const char *token, const char *data, size_t size) : + HttpRequest(REQUEST_POST, DROPBOX_API_CU "/files/upload_session/start") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/octet-stream"); + + SetData(data, size); + } + }; + + class AppendToUploadSessionRequest : public HttpRequest + { + public: + AppendToUploadSessionRequest(const char *token, const char *sessionId, size_t offset, const char *data, size_t size) : + HttpRequest(REQUEST_POST, DROPBOX_API_CU "/files/upload_session/append_v2") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/octet-stream"); + + + JSONNode cursor; + cursor.set_name("cursor"); + cursor + << JSONNode("session_id", sessionId) + << JSONNode("offset", (unsigned long)offset); + + JSONNode param; + param << cursor; + + AddHeader("Dropbox-API-Arg", param.write().c_str()); + + SetData(data, size); + } + }; + + class FinishUploadSessionRequest : public HttpRequest + { + public: + FinishUploadSessionRequest(const char *token, const char *sessionId, size_t offset, const char *path, const char *data, size_t size) : + HttpRequest(REQUEST_POST, DROPBOX_API_CU "/files/upload_session/finish") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/octet-stream"); + + JSONNode cursor(JSON_NODE); + cursor.set_name("cursor"); + cursor + << JSONNode("session_id", sessionId) + << JSONNode("offset", (unsigned long)offset); + + JSONNode commit(JSON_NODE); + commit.set_name("commit"); + commit + << JSONNode("path", path) + << JSONNode("mode", "overwrite"); + + JSONNode params(JSON_NODE); + params + << cursor + << commit; + + AddHeader("Dropbox-API-Arg", params.write().c_str()); + + SetData(data, size); + } + }; + + class CreateFolderRequest : public HttpRequest + { + public: + CreateFolderRequest(const char *token, const char *path) : + HttpRequest(REQUEST_POST, DROPBOX_API_RPC "/files/create_folder") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + + JSONNode root(JSON_NODE); + root << JSONNode("path", path); + + json_string data = root.write(); + SetData(data.c_str(), data.length()); + } + }; + + class GetTemporaryLinkRequest : public HttpRequest + { + public: + GetTemporaryLinkRequest(const char *token, const char *path) : + HttpRequest(REQUEST_POST, DROPBOX_API_RPC "/files/get_temporary_link") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + + JSONNode root(JSON_NODE); + root << JSONNode("path", path); + + json_string data = root.write(); + SetData(data.c_str(), data.length()); + } + }; + + class CreateSharedLinkRequest : public HttpRequest + { + public: + CreateSharedLinkRequest(const char *token, const char *path) : + HttpRequest(REQUEST_POST, DROPBOX_API_RPC "/sharing/create_shared_link_with_settings") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + + JSONNode root(JSON_NODE); + root << JSONNode("path", path); + + json_string data = root.write(); + SetData(data.c_str(), data.length()); + } + }; + + class GetSharedLinkRequest : public HttpRequest + { + public: + GetSharedLinkRequest(const char *token, const char *path) : + HttpRequest(REQUEST_POST, DROPBOX_API_RPC "/sharing/list_shared_links") + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + + JSONNode root(JSON_NODE); + root << JSONNode("path", path); + + json_string data = root.write(); + SetData(data.c_str(), data.length()); + } + }; +}; + +#endif //_DROPBOXSERVICE_API_H_ diff --git a/plugins/CloudFile/src/Services/dropbox_service.cpp b/plugins/CloudFile/src/Services/dropbox_service.cpp new file mode 100644 index 0000000000..26c0ff6af3 --- /dev/null +++ b/plugins/CloudFile/src/Services/dropbox_service.cpp @@ -0,0 +1,277 @@ +#include "..\stdafx.h" +#include "dropbox_api.h" + +CDropboxService::CDropboxService(HNETLIBUSER hConnection) + : CCloudService(hConnection) +{ +} + +const char* CDropboxService::GetModule() const +{ + return "Dropbox"; +} + +const wchar_t* CDropboxService::GetText() const +{ + return L"Dropbox"; +} + +HANDLE CDropboxService::GetIcon() const +{ + return GetIconHandle(IDI_DROPBOX); +} + +bool CDropboxService::IsLoggedIn() +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + return token != NULL; +} + +void CDropboxService::Login() +{ + COAuthDlg(this, DROPBOX_WWW_URL "/oauth2/authorize?response_type=code&client_id=" DROPBOX_APP_KEY, RequestAccessTokenThread).DoModal(); +} + +void CDropboxService::Logout() +{ + mir_forkthreadex(RevokeAccessTokenThread, this); +} + +unsigned CDropboxService::RequestAccessTokenThread(void *owner, void *param) +{ + HWND hwndDlg = (HWND)param; + CDropboxService *service = (CDropboxService*)owner; + + if (service->IsLoggedIn()) + service->Logout(); + + char requestToken[128]; + GetDlgItemTextA(hwndDlg, IDC_OAUTH_CODE, requestToken, _countof(requestToken)); + + DropboxAPI::GetAccessTokenRequest request(requestToken); + NLHR_PTR response(request.Send(service->hConnection)); + + if (response == NULL || response->resultCode != HTTP_CODE_OK) { + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError()); + //ShowNotification(TranslateT("server does not respond"), MB_ICONERROR); + return 0; + } + + JSONNode root = JSONNode::parse(response->pData); + if (root.empty()) { + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError(response->resultCode)); + //ShowNotification((wchar_t*)error_description, MB_ICONERROR); + return 0; + } + + JSONNode node = root.at("error_description"); + if (!node.isnull()) { + ptrW error_description(mir_a2u_cp(node.as_string().c_str(), CP_UTF8)); + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError(response->resultCode)); + //ShowNotification((wchar_t*)error_description, MB_ICONERROR); + return 0; + } + + node = root.at("access_token"); + db_set_s(NULL, service->GetModule(), "TokenSecret", node.as_string().c_str()); + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_OFFLINE, (WPARAM)ID_STATUS_ONLINE); + + SetDlgItemTextA(hwndDlg, IDC_OAUTH_CODE, ""); + + EndDialog(hwndDlg, 1); + + return 0; +} + +unsigned CDropboxService::RevokeAccessTokenThread(void *param) +{ + CDropboxService *service = (CDropboxService*)param; + + ptrA token(db_get_sa(NULL, service->GetModule(), "TokenSecret")); + DropboxAPI::RevokeAccessTokenRequest request(token); + NLHR_PTR response(request.Send(service->hConnection)); + + return 0; +} + +void CDropboxService::HandleJsonError(JSONNode &node) +{ + JSONNode error = node.at("error"); + if (!error.isnull()) { + json_string tag = error.at(".tag").as_string(); + throw Exception(tag.c_str()); + } +} + +char* CDropboxService::UploadFile(const char *data, size_t size, char *path) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + ptrA encodedPath(mir_utf8encode(path)); + DropboxAPI::UploadFileRequest request(token, encodedPath, data, size); + NLHR_PTR response(request.Send(hConnection)); + + JSONNode root = GetJsonResponse(response); + JSONNode node = root.at("path_lower"); + mir_strcpy(path, node.as_string().c_str()); + + return path; +} + +void CDropboxService::StartUploadSession(const char *data, size_t size, char *sessionId) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + DropboxAPI::StartUploadSessionRequest request(token, data, size); + NLHR_PTR response(request.Send(hConnection)); + + JSONNode root = GetJsonResponse(response); + JSONNode node = root.at("session_id"); + mir_strcpy(sessionId, node.as_string().c_str()); +} + +void CDropboxService::AppendToUploadSession(const char *data, size_t size, const char *sessionId, size_t offset) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + DropboxAPI::AppendToUploadSessionRequest request(token, sessionId, offset, data, size); + NLHR_PTR response(request.Send(hConnection)); + + GetJsonResponse(response); +} + +char* CDropboxService::FinishUploadSession(const char *data, size_t size, const char *sessionId, size_t offset, char *path) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + DropboxAPI::FinishUploadSessionRequest request(token, sessionId, offset, path, data, size); + NLHR_PTR response(request.Send(hConnection)); + + JSONNode root = GetJsonResponse(response); + JSONNode node = root.at("path_lower"); + mir_strcpy(path, node.as_string().c_str()); + + return path; +} + +void CDropboxService::CreateFolder(const char *path) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + DropboxAPI::CreateFolderRequest request(token, path); + NLHR_PTR response(request.Send(hConnection)); + + HandleHttpError(response); + + // forder exists on server + if (response->resultCode == HTTP_CODE_FORBIDDEN) + return; + + GetJsonResponse(response); +} + +void CDropboxService::CreateSharedLink(const char *path, char *url) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + DropboxAPI::CreateSharedLinkRequest shareRequest(token, path); + NLHR_PTR response(shareRequest.Send(hConnection)); + + HandleHttpError(response); + + JSONNode root = JSONNode::parse(response->pData); + if (root.isnull()) + throw Exception(HttpStatusToError()); + + JSONNode error = root.at("error"); + if (error.isnull()) { + JSONNode link = root.at("link"); + mir_strcpy(url, link.as_string().c_str()); + return; + } + + json_string tag = error.at(".tag").as_string(); + if (tag != "shared_link_already_exists") + throw Exception(tag.c_str()); + + DropboxAPI::GetSharedLinkRequest getRequest(token, path); + response = getRequest.Send(hConnection); + + root = GetJsonResponse(response); + + JSONNode links = root.at("links").as_array(); + JSONNode link = links[0u].at("url"); + mir_strcpy(url, link.as_string().c_str()); +} + +UINT CDropboxService::Upload(FileTransferParam *ftp) +{ + if (!IsLoggedIn()) + Login(); + + try { + const wchar_t *folderName = ftp->GetFolderName(); + if (folderName) { + char path[MAX_PATH], url[MAX_PATH]; + PreparePath(folderName, path); + CreateFolder(path); + CreateSharedLink(path, url); + ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(url))); + } + + ftp->FirstFile(); + do + { + const wchar_t *fileName = ftp->GetCurrentRelativeFilePath(); + uint64_t fileSize = ftp->GetCurrentFileSize(); + + int chunkSize = ftp->GetCurrentFileChunkSize(); + mir_ptr<char>data((char*)mir_calloc(chunkSize)); + size_t size = ftp->ReadCurrentFile(data, chunkSize); + + size_t offset = 0; + char sessionId[64]; + StartUploadSession(data, size, sessionId); + + offset += size; + ftp->Progress(size); + + for (size_t chunk = 0; chunk < (fileSize / chunkSize) - 1; chunk++) + { + ftp->CheckCurrentFile(); + + size = ftp->ReadCurrentFile(data, chunkSize); + AppendToUploadSession(data, size, sessionId, offset); + + offset += size; + ftp->Progress(size); + } + + if (offset < fileSize) + size = ftp->ReadCurrentFile(data, fileSize - offset); + else + size = 0; + + 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); + FinishUploadSession(data, size, sessionId, offset, path); + + ftp->Progress(size); + + if (!wcschr(fileName, L'\\')) { + char url[MAX_PATH]; + CreateSharedLink(path, url); + ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(url))); + } + } while (ftp->NextFile()); + } + catch (Exception &ex) { + Netlib_Logf(hConnection, "%s: %s", MODULE, ex.what()); + ftp->SetStatus(ACKRESULT_FAILED); + return ACKRESULT_FAILED; + } + + ftp->SetStatus(ACKRESULT_SUCCESS); + return ACKRESULT_SUCCESS; +} diff --git a/plugins/CloudFile/src/Services/dropbox_service.h b/plugins/CloudFile/src/Services/dropbox_service.h new file mode 100644 index 0000000000..93b2fb53d3 --- /dev/null +++ b/plugins/CloudFile/src/Services/dropbox_service.h @@ -0,0 +1,33 @@ +#ifndef _CLOUDSERVICE_DROPBOX_H_ +#define _CLOUDSERVICE_DROPBOX_H_ + +class CDropboxService : public CCloudService +{ +private: + static unsigned RequestAccessTokenThread(void *owner, void *param); + static unsigned __stdcall RevokeAccessTokenThread(void *param); + + void HandleJsonError(JSONNode &node); + + char* UploadFile(const char *data, size_t size, char *path); + void StartUploadSession(const char *data, size_t size, char *sessionId); + void AppendToUploadSession(const char *data, size_t size, const char *sessionId, size_t offset); + char* FinishUploadSession(const char *data, size_t size, const char *sessionId, size_t offset, char *path); + void CreateFolder(const char *path); + void CreateSharedLink(const char *path, char *url); + +public: + CDropboxService(HNETLIBUSER hConnection); + + const char* GetModule() const; + const wchar_t* GetText() const; + HANDLE GetIcon() const; + + bool IsLoggedIn(); + void Login(); + void Logout(); + + UINT Upload(FileTransferParam *ftp); +}; + +#endif //_CLOUDSERVICE_DROPBOX_H_
\ No newline at end of file diff --git a/plugins/CloudFile/src/Services/google_api.h b/plugins/CloudFile/src/Services/google_api.h new file mode 100644 index 0000000000..7df5446065 --- /dev/null +++ b/plugins/CloudFile/src/Services/google_api.h @@ -0,0 +1,101 @@ +#ifndef _GDRIVESERVICE_API_H_ +#define _GDRIVESERVICE_API_H_ + +namespace GDriveAPI +{ +#define GOOGLE_OAUTH "https://accounts.google.com/o/oauth2/v2" +#define GDRIVE_API "https://www.googleapis.com/drive/v2/files" + +#define GOOGLE_APP_ID "271668553802-3sd3tubkf165ibgrqnrhe3id8mcgnaf7.apps.googleusercontent.com" +#include "../../../miranda-private-keys/Google/client_secret.h" + + class GetAccessTokenRequest : public HttpRequest + { + public: + GetAccessTokenRequest(const char *code) : + HttpRequest(REQUEST_POST, "https://www.googleapis.com/oauth2/v4/token") + { + AddHeader("Content-Type", "application/x-www-form-urlencoded"); + + CMStringA data(CMStringDataFormat::FORMAT, + "redirect_uri=urn:ietf:wg:oauth:2.0:oob&client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s", + GOOGLE_APP_ID, GOOGLE_CLIENT_SECRET, code); + SetData(data.GetBuffer(), data.GetLength()); + } + }; + + class RevokeAccessTokenRequest : public HttpRequest + { + public: + RevokeAccessTokenRequest(const char *token) : + HttpRequest(REQUEST_POST, GOOGLE_OAUTH "/revoke") + { + AddUrlParameter("token=%s", token); + } + }; + + class StartUploadFileRequest : public HttpRequest + { + public: + StartUploadFileRequest(const char *token) : + HttpRequest(REQUEST_POST, GDRIVE_API) + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + AddUrlParameter("uploadType=resumable"); + } + }; + + class UploadFileRequest : public HttpRequest + { + public: + UploadFileRequest(const char *token, const char *data, size_t size) : + HttpRequest(REQUEST_POST, GDRIVE_API) + { + AddBearerAuthHeader(token); + AddUrlParameter("uploadType=resumable"); + + SetData(data, size); + } + }; + + class CreateFolderRequest : public HttpRequest + { + public: + CreateFolderRequest(const char *token, const char *path) : + HttpRequest(REQUEST_PUT, GDRIVE_API) + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + + JSONNode params(JSON_NODE); + params + << JSONNode("name", path) + << JSONNode("mimeType", "pplication/vnd.google-apps.folder"); + + json_string data = params.write(); + SetData(data.c_str(), data.length()); + } + }; + + class ShareRequest : public HttpRequest + { + public: + ShareRequest(const char *token, const char *fileId) : + HttpRequest(REQUEST_PUT, FORMAT, GDRIVE_API "/%s/permissions", fileId) + { + AddBearerAuthHeader(token); + AddHeader("Content-Type", "application/json"); + + JSONNode params(JSON_NODE); + params + << JSONNode("role", "reader") + << JSONNode("type", "anyone"); + + json_string data = params.write(); + SetData(data.c_str(), data.length()); + } + }; +}; + +#endif //_GDRIVESERVICE_API_H_ diff --git a/plugins/CloudFile/src/Services/google_service.cpp b/plugins/CloudFile/src/Services/google_service.cpp new file mode 100644 index 0000000000..f0185bfbb8 --- /dev/null +++ b/plugins/CloudFile/src/Services/google_service.cpp @@ -0,0 +1,216 @@ +#include "..\stdafx.h" +#include "google_api.h" + +CGDriveService::CGDriveService(HNETLIBUSER hConnection) + : CCloudService(hConnection) +{ +} + +const char* CGDriveService::GetModule() const +{ + return "Google"; +} + +const wchar_t* CGDriveService::GetText() const +{ + return L"GDrive"; +} + +HANDLE CGDriveService::GetIcon() const +{ + return GetIconHandle(IDI_GDRIVE); +} + +bool CGDriveService::IsLoggedIn() +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + return token != NULL; +} + +void CGDriveService::Login() +{ + COAuthDlg(this, GOOGLE_OAUTH "/auth?response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file&redirect_uri=urn:ietf:wg:oauth:2.0:oob&client_id=" GOOGLE_APP_ID, RequestAccessTokenThread).DoModal(); +} + +void CGDriveService::Logout() +{ + mir_forkthreadex(RevokeAccessTokenThread, this); +} + +unsigned CGDriveService::RequestAccessTokenThread(void *owner, void *param) +{ + HWND hwndDlg = (HWND)param; + CGDriveService *service = (CGDriveService*)owner; + + if (service->IsLoggedIn()) + service->Logout(); + + char requestToken[128]; + GetDlgItemTextA(hwndDlg, IDC_OAUTH_CODE, requestToken, _countof(requestToken)); + + GDriveAPI::GetAccessTokenRequest request(requestToken); + NLHR_PTR response(request.Send(service->hConnection)); + + if (response == NULL || response->resultCode != HTTP_CODE_OK) { + const char *error = response->dataLength + ? response->pData + : service->HttpStatusToError(response->resultCode); + + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), error); + ShowNotification(TranslateT("server does not respond"), MB_ICONERROR); + return 0; + } + + JSONNode root = JSONNode::parse(response->pData); + if (root.empty()) { + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError(response->resultCode)); + ShowNotification(TranslateT("server does not respond"), MB_ICONERROR); + return 0; + } + + JSONNode node = root.at("error_description"); + if (!node.isnull()) { + ptrW error_description(mir_a2u_cp(node.as_string().c_str(), CP_UTF8)); + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError(response->resultCode)); + ShowNotification((wchar_t*)error_description, MB_ICONERROR); + return 0; + } + + node = root.at("access_token"); + db_set_s(NULL, service->GetModule(), "TokenSecret", node.as_string().c_str()); + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_OFFLINE, (WPARAM)ID_STATUS_ONLINE); + + SetDlgItemTextA(hwndDlg, IDC_OAUTH_CODE, ""); + + EndDialog(hwndDlg, 1); + + return 0; +} + +unsigned CGDriveService::RevokeAccessTokenThread(void *param) +{ + CGDriveService *service = (CGDriveService*)param; + + ptrA token(db_get_sa(NULL, service->GetModule(), "TokenSecret")); + GDriveAPI::RevokeAccessTokenRequest request(token); + NLHR_PTR response(request.Send(service->hConnection)); + + return 0; +} + +void CGDriveService::HandleJsonError(JSONNode &node) +{ + JSONNode error = node.at("error"); + if (!error.isnull()) { + json_string tag = error.at(".tag").as_string(); + throw Exception(tag.c_str()); + } +} + +void CGDriveService::StartUploadFile() +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + GDriveAPI::StartUploadFileRequest request(token); + NLHR_PTR response(request.Send(hConnection)); + + GetJsonResponse(response); +} + +void CGDriveService::UploadFile(const char *url, const char *data, size_t size) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + GDriveAPI::UploadFileRequest request(token, data, size); + NLHR_PTR response(request.Send(hConnection)); + + if (response == NULL) + throw Exception(HttpStatusToError()); + + if (response->resultCode >= HTTP_CODE_OK && + response->resultCode <= HTTP_CODE_MULTIPLE_CHOICES) { + return; + } + + if (response->dataLength) + throw Exception(response->pData); + throw Exception(HttpStatusToError(response->resultCode)); +} + +void CGDriveService::CreateFolder(const char *path) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + GDriveAPI::CreateFolderRequest request(token, path); + NLHR_PTR response(request.Send(hConnection)); + + GetJsonResponse(response); +} + +void CGDriveService::CreateSharedLink(const char *path, char *url) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + GDriveAPI::ShareRequest request(token, path); + NLHR_PTR response(request.Send(hConnection)); + + JSONNode root = GetJsonResponse(response); + JSONNode link = root.at("href"); + mir_strcpy(url, link.as_string().c_str()); +} + +UINT CGDriveService::Upload(FileTransferParam *ftp) +{ + if (!IsLoggedIn()) + Login(); + + if (!IsLoggedIn()) { + ftp->SetStatus(ACKRESULT_FAILED); + return ACKRESULT_FAILED; + } + + try { + const wchar_t *folderName = ftp->GetFolderName(); + if (folderName) { + char path[MAX_PATH], link[MAX_PATH]; + PreparePath(folderName, path); + CreateFolder(path); + CreateSharedLink(path, link); + ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(link))); + } + + ftp->FirstFile(); + do + { + 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(); + + mir_ptr<char>data((char*)mir_calloc(fileSize)); + size_t size = ftp->ReadCurrentFile(data, fileSize); + UploadFile("", data, size); + + ftp->Progress(size); + + if (!wcschr(fileName, L'\\')) { + char url[MAX_PATH]; + CreateSharedLink(path, url); + ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(url))); + } + } while (ftp->NextFile()); + } + catch (Exception &ex) { + Netlib_Logf(hConnection, "%s: %s", MODULE, ex.what()); + ftp->SetStatus(ACKRESULT_FAILED); + return ACKRESULT_FAILED; + } + + ftp->SetStatus(ACKRESULT_SUCCESS); + return ACKRESULT_SUCCESS; +} diff --git a/plugins/CloudFile/src/Services/google_service.h b/plugins/CloudFile/src/Services/google_service.h new file mode 100644 index 0000000000..db72ef9dee --- /dev/null +++ b/plugins/CloudFile/src/Services/google_service.h @@ -0,0 +1,31 @@ +#ifndef _CLOUDFILE_GDRIVE_H_ +#define _CLOUDFILE_GDRIVE_H_ + +class CGDriveService : public CCloudService +{ +private: + static unsigned RequestAccessTokenThread(void *owner, void *param); + static unsigned __stdcall RevokeAccessTokenThread(void *param); + + void HandleJsonError(JSONNode &node); + + void StartUploadFile(); + void UploadFile(const char *url, const char *data, size_t size); + void CreateFolder(const char *path); + void CreateSharedLink(const char *path, char *url); + +public: + CGDriveService(HNETLIBUSER hConnection); + + const char* GetModule() const; + const wchar_t* GetText() const; + HANDLE GetIcon() const; + + bool IsLoggedIn(); + void Login(); + void Logout(); + + UINT Upload(FileTransferParam *ftp); +}; + +#endif //_CLOUDFILE_GDRIVE_H_
\ No newline at end of file diff --git a/plugins/CloudFile/src/Services/yandex_api.h b/plugins/CloudFile/src/Services/yandex_api.h new file mode 100644 index 0000000000..7548f6bd14 --- /dev/null +++ b/plugins/CloudFile/src/Services/yandex_api.h @@ -0,0 +1,84 @@ +#ifndef _YANDEXSERVICE_API_H_ +#define _YANDEXSERVICE_API_H_ + +namespace YandexAPI +{ +#define YANDEX_OAUTH "https://oauth.yandex.ru" +#define YADISK_API "https://cloud-api.yandex.net/v1/disk/resources" + +#define YANDEX_APP_ID "c311a5967cae4efa88d1af97d01ea0e8" +#include "../../../miranda-private-keys/Yandex/client_secret.h" + + class GetAccessTokenRequest : public HttpRequest + { + public: + GetAccessTokenRequest(const char *code) : + HttpRequest(REQUEST_POST, YANDEX_OAUTH "/token") + { + AddHeader("Content-Type", "application/x-www-form-urlencoded"); + + CMStringA data(CMStringDataFormat::FORMAT, + "client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s", + YANDEX_APP_ID, YADISK_CLIENT_SECRET, code); + SetData(data.GetBuffer(), data.GetLength()); + } + }; + + class RevokeAccessTokenRequest : public HttpRequest + { + public: + RevokeAccessTokenRequest(const char *token) : + HttpRequest(REQUEST_POST, YANDEX_OAUTH "/token/revoke") + { + AddOAuthHeader(token); + } + }; + + class GetUploadUrlRequest : public HttpRequest + { + public: + GetUploadUrlRequest(const char *token, const char *path) : + HttpRequest(REQUEST_GET, YADISK_API "/upload") + { + AddOAuthHeader(token); + AddUrlParameter("path=%s", ptrA(mir_urlEncode(path))); + AddUrlParameter("overwrite=true"); + } + }; + + class UploadFileRequest : public HttpRequest + { + public: + UploadFileRequest(const char *token, const char *url, const char *data, size_t size) : + HttpRequest(REQUEST_PUT, url) + { + AddOAuthHeader(token); + + SetData(data, size); + } + }; + + class CreateFolderRequest : public HttpRequest + { + public: + CreateFolderRequest(const char *token, const char *path) : + HttpRequest(REQUEST_PUT, YADISK_API) + { + AddOAuthHeader(token); + AddUrlParameter("path=%s", ptrA(mir_urlEncode(path))); + } + }; + + class PublishRequest : public HttpRequest + { + public: + PublishRequest(const char *token, const char *path) : + HttpRequest(REQUEST_PUT, YADISK_API "/publish") + { + AddOAuthHeader(token); + AddUrlParameter("path=%s", ptrA(mir_urlEncode(path))); + } + }; +}; + +#endif //_YANDEXSERVICE_API_H_ diff --git a/plugins/CloudFile/src/Services/yandex_service.cpp b/plugins/CloudFile/src/Services/yandex_service.cpp new file mode 100644 index 0000000000..03f34a432e --- /dev/null +++ b/plugins/CloudFile/src/Services/yandex_service.cpp @@ -0,0 +1,219 @@ +#include "..\stdafx.h" +#include "yandex_api.h" + +CYandexService::CYandexService(HNETLIBUSER hConnection) + : CCloudService(hConnection) +{ +} + +const char* CYandexService::GetModule() const +{ + return "Yandex"; +} + +const wchar_t* CYandexService::GetText() const +{ + return L"Яндекс.Диск"; +} + +HANDLE CYandexService::GetIcon() const +{ + return NULL; +} + +bool CYandexService::IsLoggedIn() +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + return token != NULL; +} + +void CYandexService::Login() +{ + COAuthDlg(this, YANDEX_OAUTH "/authorize?response_type=code&client_id=" YANDEX_APP_ID, RequestAccessTokenThread).DoModal(); +} + +void CYandexService::Logout() +{ + mir_forkthreadex(RevokeAccessTokenThread, this); +} + +unsigned CYandexService::RequestAccessTokenThread(void *owner, void *param) +{ + HWND hwndDlg = (HWND)param; + CYandexService *service = (CYandexService*)owner; + + if (service->IsLoggedIn()) + service->Logout(); + + char requestToken[128]; + GetDlgItemTextA(hwndDlg, IDC_OAUTH_CODE, requestToken, _countof(requestToken)); + + YandexAPI::GetAccessTokenRequest request(requestToken); + NLHR_PTR response(request.Send(service->hConnection)); + + if (response == NULL || response->resultCode != HTTP_CODE_OK) { + const char *error = response->dataLength + ? response->pData + : service->HttpStatusToError(response->resultCode); + + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), error); + ShowNotification(TranslateT("server does not respond"), MB_ICONERROR); + return 0; + } + + JSONNode root = JSONNode::parse(response->pData); + if (root.empty()) { + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError(response->resultCode)); + ShowNotification(TranslateT("server does not respond"), MB_ICONERROR); + return 0; + } + + JSONNode node = root.at("error_description"); + if (!node.isnull()) { + ptrW error_description(mir_a2u_cp(node.as_string().c_str(), CP_UTF8)); + Netlib_Logf(service->hConnection, "%s: %s", service->GetModule(), service->HttpStatusToError(response->resultCode)); + ShowNotification((wchar_t*)error_description, MB_ICONERROR); + return 0; + } + + node = root.at("access_token"); + db_set_s(NULL, service->GetModule(), "TokenSecret", node.as_string().c_str()); + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_OFFLINE, (WPARAM)ID_STATUS_ONLINE); + + SetDlgItemTextA(hwndDlg, IDC_OAUTH_CODE, ""); + + EndDialog(hwndDlg, 1); + + return 0; +} + +unsigned CYandexService::RevokeAccessTokenThread(void *param) +{ + CYandexService *service = (CYandexService*)param; + + ptrA token(db_get_sa(NULL, service->GetModule(), "TokenSecret")); + YandexAPI::RevokeAccessTokenRequest request(token); + NLHR_PTR response(request.Send(service->hConnection)); + + return 0; +} + +void CYandexService::HandleJsonError(JSONNode &node) +{ + JSONNode error = node.at("error"); + if (!error.isnull()) { + json_string tag = error.at(".tag").as_string(); + throw Exception(tag.c_str()); + } +} + +void CYandexService::GetUploadUrl(char *path, char *url) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + YandexAPI::GetUploadUrlRequest request(token, path); + NLHR_PTR response(request.Send(hConnection)); + + JSONNode root = GetJsonResponse(response); + JSONNode node = root.at("href"); + mir_strcpy(url, node.as_string().c_str()); +} + +void CYandexService::UploadFile(const char *url, const char *data, size_t size) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + YandexAPI::UploadFileRequest request(token, url, data, size); + NLHR_PTR response(request.Send(hConnection)); + + if (response == NULL) + throw Exception(HttpStatusToError()); + + if (response->resultCode >= HTTP_CODE_OK && + response->resultCode <= HTTP_CODE_MULTIPLE_CHOICES) { + return; + } + + if (response->dataLength) + throw Exception(response->pData); + throw Exception(HttpStatusToError(response->resultCode)); +} + +void CYandexService::CreateFolder(const char *path) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + YandexAPI::CreateFolderRequest request(token, path); + NLHR_PTR response(request.Send(hConnection)); + + GetJsonResponse(response); +} + +void CYandexService::CreateSharedLink(const char *path, char *url) +{ + ptrA token(db_get_sa(NULL, GetModule(), "TokenSecret")); + YandexAPI::PublishRequest request(token, path); + NLHR_PTR response(request.Send(hConnection)); + + JSONNode root = GetJsonResponse(response); + JSONNode link = root.at("href"); + mir_strcpy(url, link.as_string().c_str()); +} + +UINT CYandexService::Upload(FileTransferParam *ftp) +{ + if (!IsLoggedIn()) + Login(); + + if (!IsLoggedIn()) { + ftp->SetStatus(ACKRESULT_FAILED); + return ACKRESULT_FAILED; + } + + try { + const wchar_t *folderName = ftp->GetFolderName(); + if (folderName) { + char path[MAX_PATH], link[MAX_PATH]; + PreparePath(folderName, path); + CreateFolder(path); + CreateSharedLink(path, link); + ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(link))); + } + + ftp->FirstFile(); + do + { + 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); + char url[MAX_PATH]; + GetUploadUrl(path, url); + + mir_ptr<char>data((char*)mir_calloc(fileSize)); + size_t size = ftp->ReadCurrentFile(data, fileSize); + UploadFile(url, data, size); + + ftp->Progress(size); + + if (!wcschr(fileName, L'\\')) { + char url[MAX_PATH]; + CreateSharedLink(path, url); + ftp->AppendFormatData(L"%s\r\n", ptrW(mir_utf8decodeW(url))); + } + } while (ftp->NextFile()); + } + catch (Exception &ex) { + Netlib_Logf(hConnection, "%s: %s", MODULE, ex.what()); + ftp->SetStatus(ACKRESULT_FAILED); + return ACKRESULT_FAILED; + } + + ftp->SetStatus(ACKRESULT_SUCCESS); + return ACKRESULT_SUCCESS; +} diff --git a/plugins/CloudFile/src/Services/yandex_service.h b/plugins/CloudFile/src/Services/yandex_service.h new file mode 100644 index 0000000000..a3a84735c4 --- /dev/null +++ b/plugins/CloudFile/src/Services/yandex_service.h @@ -0,0 +1,31 @@ +#ifndef _CLOUDFILE_YANDEX_H_ +#define _CLOUDFILE_YANDEX_H_ + +class CYandexService : public CCloudService +{ +private: + static unsigned RequestAccessTokenThread(void *owner, void *param); + static unsigned __stdcall RevokeAccessTokenThread(void *param); + + void HandleJsonError(JSONNode &node); + + void GetUploadUrl(char *path, char *url); + void UploadFile(const char *url, const char *data, size_t size); + void CreateFolder(const char *path); + void CreateSharedLink(const char *path, char *url); + +public: + CYandexService(HNETLIBUSER hConnection); + + const char* GetModule() const; + const wchar_t* GetText() const; + HANDLE GetIcon() const; + + bool IsLoggedIn(); + void Login(); + void Logout(); + + UINT Upload(FileTransferParam *ftp); +}; + +#endif //_CLOUDFILE_YANDEX_H_
\ No newline at end of file |