summaryrefslogtreecommitdiff
path: root/plugins/CloudFile/src/cloud_file.cpp
blob: 2c019cbd96e77b2b8a1cd6741d508df719df37c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include "stdafx.h"

CCloudService::CCloudService(const char *protoName, const wchar_t *userName)
	: PROTO<CCloudService>(protoName, userName)
{
	MUUID muidLast = MIID_LAST;
	m_hLangpack = GetPluginLangId(muidLast, 0);

	NETLIBUSER nlu = {};
	nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE;
	nlu.szSettingsModule = (char*)protoName;
	nlu.szDescriptiveName.w = (wchar_t*)userName;
	m_hConnection = Netlib_RegisterUser(&nlu);

	CreateProtoService(PS_CREATEACCMGRUI, &CCloudService::OnAccountManagerInit);
}

CCloudService::~CCloudService()
{
	Netlib_CloseHandle(m_hConnection);
	m_hConnection = nullptr;
}

void CCloudService::OnErase()
{
	KillModuleMenus(m_hLangpack);
}

int CCloudService::GetId() const
{
	return m_hLangpack;
}

const char* CCloudService::GetAccountName() const
{
	return m_szModuleName;
}

const wchar_t* CCloudService::GetUserName() const
{
	return m_tszUserName;
}

INT_PTR CCloudService::GetCaps(int type, MCONTACT)
{
	switch (type) {
	case PFLAGNUM_1:
		return PF1_FILESEND;
	case PFLAGNUM_2:
	case PFLAGNUM_5:
		return PF2_NONE;
	default:
		return 0;
	}
}

int CCloudService::FileCancel(MCONTACT, HANDLE hTransfer)
{
	FileTransferParam *ftp = Transfers.find((FileTransferParam*)&hTransfer);
	if (ftp)
		ftp->Terminate();

	return 0;
}

HANDLE CCloudService::SendFile(MCONTACT hContact, const wchar_t *description, wchar_t **paths)
{
	FileTransferParam *ftp = new FileTransferParam(hContact);
	ftp->SetDescription(description);
	ftp->SetWorkingDirectory(paths[0]);
	for (int i = 0; paths[i]; i++) {
		if (PathIsDirectory(paths[i]))
			continue;
		ftp->AddFile(paths[i]);
	}
	Transfers.insert(ftp);
	mir_forkthreadowner(UploadAndReportProgressThread, this, ftp);
	return (HANDLE)ftp->GetId();
}

void CCloudService::OpenUploadDialog(MCONTACT hContact)
{
	char *proto = GetContactProto(hContact);
	if (!mir_strcmpi(proto, META_PROTO))
		hContact = CallService(MS_MC_GETMOSTONLINECONTACT, hContact);

	auto it = InterceptedContacts.find(hContact);
	if (it == InterceptedContacts.end()) {
		HWND hwnd = (HWND)CallService(MS_FILE_SENDFILE, hContact, 0);
		InterceptedContacts[hContact] = hwnd;
	}
	else
		SetActiveWindow(it->second);
}

INT_PTR CCloudService::OnAccountManagerInit(WPARAM, LPARAM lParam)
{
	CAccountManagerDlg *page = new CAccountManagerDlg(this);
	page->SetParent((HWND)lParam);
	page->Show();
	return (INT_PTR)page->GetHwnd();
}

std::string CCloudService::PreparePath(const std::string &path) const
{
	std::string newPath = path;
	if (newPath[0] != '/')
		newPath.insert(0, "/");
	std::replace(newPath.begin(), newPath.end(), '\\', '/');
	size_t pos = newPath.find("//");
	while (pos != std::string::npos) {
		newPath.replace(pos, 2, "/");
		pos = newPath.find("//", pos + 1);
	}
	return newPath;
}

char* CCloudService::HttpStatusToError(int status)
{
	switch (status) {
	case HTTP_CODE_OK:
		return "OK";
	case HTTP_CODE_BAD_REQUEST:
		return "Bad input parameter. Error message should indicate which one and why";
	case HTTP_CODE_UNAUTHORIZED:
		return "Bad or expired token. This can happen if the user or Dropbox revoked or expired an access token. To fix, you should re-authenticate the user";
	case HTTP_CODE_FORBIDDEN:
		return "Bad OAuth request (wrong consumer key, bad nonce, expired timestamp...). Unfortunately, re-authenticating the user won't help here";
	case HTTP_CODE_NOT_FOUND:
		return "File or folder not found at the specified path";
	case HTTP_CODE_METHOD_NOT_ALLOWED:
		return "Request method not expected (generally should be GET or POST)";
	case HTTP_CODE_TOO_MANY_REQUESTS:
		return "Your app is making too many requests and is being rate limited. 429s can trigger on a per-app or per-user basis";
	case HTTP_CODE_SERVICE_UNAVAILABLE:
		return "If the response includes the Retry-After header, this means your OAuth 1.0 app is being rate limited. Otherwise, this indicates a transient server error, and your app should retry its request.";
	}

	return "Unknown error";
}

void CCloudService::HttpResponseToError(NETLIBHTTPREQUEST *response)
{
	if (response == nullptr)
		throw Exception(HttpStatusToError());
	if (response->dataLength)
		throw Exception(response->pData);
	throw Exception(HttpStatusToError(response->resultCode));
}

void CCloudService::HandleHttpError(NETLIBHTTPREQUEST *response)
{
	if (response == nullptr)
		throw Exception(HttpStatusToError());

	if (HTTP_CODE_SUCCESS(response->resultCode))
		return;

	if (response->resultCode == HTTP_CODE_UNAUTHORIZED)
		delSetting("TokenSecret");

	HttpResponseToError(response);
}

JSONNode CCloudService::GetJsonResponse(NETLIBHTTPREQUEST *response)
{
	HandleHttpError(response);

	JSONNode root = JSONNode::parse(response->pData);
	if (root.isnull())
		throw Exception(HttpStatusToError());

	HandleJsonError(root);

	return root;
}

UINT CCloudService::Upload(CCloudService *service, FileTransferParam *ftp)
{
	try {
		if (!service->IsLoggedIn())
			service->Login();

		if (!service->IsLoggedIn()) {
			ftp->SetStatus(ACKRESULT_FAILED);
			return ACKRESULT_FAILED;
		}

		service->Upload(ftp);
	}
	catch (Exception &ex) {
		service->debugLogA("%s: %s", service->GetModuleName(), ex.what());
		ftp->SetStatus(ACKRESULT_FAILED);
		return ACKRESULT_FAILED;
	}

	ftp->SetStatus(ACKRESULT_SUCCESS);
	return ACKRESULT_SUCCESS;
}