diff options
author | George Hazan <ghazan@miranda.im> | 2017-02-03 21:48:25 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2017-02-03 21:48:25 +0300 |
commit | 9c6dda528c8d53a3dd89f7afd57aece8d4951453 (patch) | |
tree | e376eb150f1b8d874c52b2272686f1d4ec216b7e | |
parent | 86c4ef4b99f87698b38c278a501d258f9245da84 (diff) |
first attempt to implement file transfers
-rw-r--r-- | protocols/Discord/src/http.cpp | 2 | ||||
-rw-r--r-- | protocols/Discord/src/proto.cpp | 109 | ||||
-rw-r--r-- | protocols/Discord/src/proto.h | 4 |
3 files changed, 113 insertions, 2 deletions
diff --git a/protocols/Discord/src/http.cpp b/protocols/Discord/src/http.cpp index a9136c5552..e9dc031e62 100644 --- a/protocols/Discord/src/http.cpp +++ b/protocols/Discord/src/http.cpp @@ -59,11 +59,11 @@ AsyncHttpRequest::AsyncHttpRequest(CDiscordProto *ppro, int iRequestType, LPCSTR } else flags |= NLHRF_NODUMPSEND; - AddHeader("Content-Type", "application/json"); if (pRoot != NULL) { ptrW text(json_write(pRoot)); pData = mir_utf8encodeW(text); dataLength = (int)mir_strlen(pData); + AddHeader("Content-Type", "application/json"); } requestType = iRequestType; 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; +} ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h index 91cfe59ab7..1ee7625f9d 100644 --- a/protocols/Discord/src/proto.h +++ b/protocols/Discord/src/proto.h @@ -112,6 +112,7 @@ class CDiscordProto : public PROTO<CDiscordProto> ////////////////////////////////////////////////////////////////////////////////////// // threads + void __cdecl SendFileThread(void*); void __cdecl ServerThread(void*); void __cdecl SearchThread(void *param); void __cdecl SendMessageAckThread(void* param); @@ -215,6 +216,8 @@ public: virtual int __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT *evt) override; virtual int __cdecl SendMsg(MCONTACT hContact, int flags, const char* pszSrc) override; + virtual HANDLE __cdecl SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override; + virtual int __cdecl SetStatus(int iNewStatus) override; virtual int __cdecl OnEvent(PROTOEVENTTYPE, WPARAM, LPARAM) override; @@ -258,6 +261,7 @@ public: void OnReceiveCreateChannel(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveAuth(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveChannels(NETLIBHTTPREQUEST*, AsyncHttpRequest*); + void OnReceiveFile(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveFriends(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveGuilds(NETLIBHTTPREQUEST*, AsyncHttpRequest*); |