summaryrefslogtreecommitdiff
path: root/protocols/Discord/src/proto.cpp
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2017-02-03 21:48:25 +0300
committerGeorge Hazan <ghazan@miranda.im>2017-02-03 21:48:25 +0300
commit9c6dda528c8d53a3dd89f7afd57aece8d4951453 (patch)
treee376eb150f1b8d874c52b2272686f1d4ec216b7e /protocols/Discord/src/proto.cpp
parent86c4ef4b99f87698b38c278a501d258f9245da84 (diff)
first attempt to implement file transfers
Diffstat (limited to 'protocols/Discord/src/proto.cpp')
-rw-r--r--protocols/Discord/src/proto.cpp109
1 files changed, 108 insertions, 1 deletions
diff --git a/protocols/Discord/src/proto.cpp b/protocols/Discord/src/proto.cpp
index b0d47a941e..4e00497dca 100644
--- a/protocols/Discord/src/proto.cpp
+++ b/protocols/Discord/src/proto.cpp
@@ -104,7 +104,7 @@ DWORD_PTR CDiscordProto::GetCaps(int type, MCONTACT)
{
switch (type) {
case PFLAGNUM_1:
- return PF1_IM | PF1_MODEMSGRECV | PF1_SERVERCLIST | PF1_BASICSEARCH | PF1_EXTSEARCH | PF1_ADDSEARCHRES;
+ return PF1_IM | PF1_MODEMSGRECV | PF1_SERVERCLIST | PF1_BASICSEARCH | PF1_EXTSEARCH | PF1_ADDSEARCHRES | PF1_FILESEND;
case PFLAGNUM_2:
case PFLAGNUM_3:
return PF2_ONLINE | PF2_LONGAWAY | PF2_HEAVYDND | PF2_INVISIBLE;
@@ -443,6 +443,113 @@ int CDiscordProto::OnDeleteContact(MCONTACT hContact)
return 0;
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct SendFileThreadParam
+{
+ MCONTACT hContact;
+ CMStringW wszDescr, wszFileName;
+
+ SendFileThreadParam(MCONTACT _p1, LPCWSTR _p2, LPCWSTR _p3) :
+ hContact(_p1),
+ wszFileName(_p2),
+ wszDescr(_p3)
+ {}
+};
+
+void CDiscordProto::SendFileThread(void *param)
+{
+ SendFileThreadParam *p = (SendFileThreadParam*)param;
+
+ FILE *in = _wfopen(p->wszFileName, L"rb");
+ if (in == NULL) {
+ LBL_Error:
+ ProtoBroadcastAck(p->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, param);
+ delete p;
+ return;
+ }
+
+ ProtoBroadcastAck(p->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, param);
+
+ char szRandom[16], szRandomText[33];
+ Utils_GetRandom(szRandom, _countof(szRandom));
+ bin2hex(szRandom, _countof(szRandom), szRandomText);
+ CMStringA szBoundary(FORMAT, "------Boundary%s", szRandomText);
+
+ if (p->wszDescr.IsEmpty())
+ p->wszDescr = L"blabla";
+
+ CMStringA szBody;
+ szBody.Append(szBoundary + "\r\n");
+ szBody.Append("Content-Disposition: form-data; name=\"content\"\r\n\r\n");
+ szBody.Append(ptrA(mir_utf8encodeW(p->wszDescr)));
+ szBody.Append("\r\n");
+
+ szBody.Append(szBoundary + "\r\n");
+ szBody.Append("Content-Disposition: form-data; name=\"tts\"\r\n\r\nfalse\r\n");
+
+ wchar_t *pFileName = wcsrchr(p->wszFileName.GetBuffer(), '\\');
+ if (pFileName != nullptr)
+ pFileName++;
+ else
+ pFileName = p->wszFileName.GetBuffer();
+
+ szBody.Append(szBoundary + "\r\n");
+ szBody.AppendFormat("Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n", ptrA(mir_utf8encodeW(pFileName)));
+ szBody.AppendFormat("Content-Type: %S\r\n", ProtoGetAvatarMimeType(ProtoGetAvatarFileFormat(p->wszFileName)));
+ szBody.Append("\r\n");
+
+ size_t cbBytes = filelength(fileno(in));
+
+ CMStringA szUrl(FORMAT, "/channels/%lld/messages", getId(p->hContact, DB_KEY_CHANNELID));
+ AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_POST, szUrl, &CDiscordProto::OnReceiveFile);
+
+ pReq->AddHeader("Content-Type", CMStringA("multipart/form-data; boundary=" + szBoundary));
+ pReq->AddHeader("Accept", "*/*");
+
+ szBoundary.Insert(0, "\r\n");
+ szBoundary.Append("--\r\n");
+ pReq->dataLength = int(szBody.GetLength() + szBoundary.GetLength() + cbBytes);
+ pReq->pData = (char*)mir_alloc(pReq->dataLength+1);
+ memcpy(pReq->pData, szBody.c_str(), szBody.GetLength());
+ size_t cbRead = fread(pReq->pData + szBody.GetLength(), 1, cbBytes, in);
+ fclose(in);
+ if (cbBytes != cbRead)
+ goto LBL_Error;
+
+ memcpy(pReq->pData + szBody.GetLength() + cbBytes, szBoundary, szBoundary.GetLength());
+ pReq->pUserInfo = p;
+ Push(pReq);
+
+ ProtoBroadcastAck(p->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTED, param);
+}
+
+void CDiscordProto::OnReceiveFile(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
+{
+ SendFileThreadParam *p = (SendFileThreadParam*)pReq->pUserInfo;
+ if (pReply->resultCode != 200) {
+ ProtoBroadcastAck(p->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, p);
+ debugLogA("CDiscordProto::SendFile failed: %d", pReply->resultCode);
+ }
+ else {
+ ProtoBroadcastAck(p->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, p);
+ debugLogA("CDiscordProto::SendFile succeeded");
+ }
+
+ delete p;
+}
+
+HANDLE CDiscordProto::SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles)
+{
+ SnowFlake id = getId(hContact, DB_KEY_CHANNELID);
+ if (id == 0)
+ return nullptr;
+
+ // we don't wanna block the main thread, right?
+ SendFileThreadParam *param = new SendFileThreadParam(hContact, ppszFiles[0], szDescription);
+ ForkThread(&CDiscordProto::SendFileThread, param);
+ return param;
+}
/////////////////////////////////////////////////////////////////////////////////////////