summaryrefslogtreecommitdiff
path: root/plugins/CloudFile/src/Services
diff options
context:
space:
mode:
authoraunsane <aunsane@gmail.com>2017-04-16 01:32:19 +0300
committeraunsane <aunsane@gmail.com>2017-04-16 01:32:58 +0300
commit0b9fa1d90f8d0aff7118837ceb1211b578a5a9c8 (patch)
tree3b8be7b839a98a3a52a38d713c2d708ada015510 /plugins/CloudFile/src/Services
parent008fb731e3e3b587f596afba1cfe7446de7f0cac (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.h200
-rw-r--r--plugins/CloudFile/src/Services/dropbox_service.cpp277
-rw-r--r--plugins/CloudFile/src/Services/dropbox_service.h33
-rw-r--r--plugins/CloudFile/src/Services/google_api.h101
-rw-r--r--plugins/CloudFile/src/Services/google_service.cpp216
-rw-r--r--plugins/CloudFile/src/Services/google_service.h31
-rw-r--r--plugins/CloudFile/src/Services/yandex_api.h84
-rw-r--r--plugins/CloudFile/src/Services/yandex_service.cpp219
-rw-r--r--plugins/CloudFile/src/Services/yandex_service.h31
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