#include "common.h" int CDropbox::SendFile(const char *fileName, const char *data, int length) { char *utf8_fileName = mir_utf8encode(fileName); CMStringA url; url.AppendFormat(DROPBOX_APICONTENT_URL "/files_put/%s/%s", DROPBOX_API_ROOT, utf8_fileName); url.Replace('\\', '/'); mir_free(utf8_fileName); HttpRequest *request = new HttpRequest(hNetlibUser, REQUEST_PUT, DROPBOX_APICONTENT_URL "/files_put"); request->AddBearerAuthHeader(db_get_sa(NULL, MODULE, "TokenSecret")); request->pData = (char*)mir_alloc(sizeof(char)* length); memcpy(request->pData, data, length); request->dataLength = length; mir_ptr response(request->Send()); delete request; return HandleHttpResponseError(hNetlibUser, response); } int CDropbox::SendFileChunkedFirst(const char *data, int length, char *uploadId, int &offset) { HttpRequest *request = new HttpRequest(hNetlibUser, REQUEST_PUT, DROPBOX_APICONTENT_URL "/chunked_upload"); request->AddBearerAuthHeader(db_get_sa(NULL, MODULE, "TokenSecret")); request->AddHeader("Content-Type", "application/octet-stream"); request->pData = (char*)mir_alloc(sizeof(char)* length); memcpy(request->pData, data, length); request->dataLength = length; mir_ptr response(request->Send()); delete request; if (response && response->resultCode == HTTP_STATUS_OK) { JSONNODE *root = json_parse(response->pData); if (root) { JSONNODE *node = json_get(root, "upload_id"); strcpy(uploadId, mir_u2a(json_as_string(node))); node = json_get(root, "offset"); offset = json_as_int(node); } } return HandleHttpResponseError(hNetlibUser, response); } int CDropbox::SendFileChunkedNext(const char *data, int length, const char *uploadId, int &offset) { CMStringA url = DROPBOX_APICONTENT_URL "/chunked_upload"; url.AppendFormat("?upload_id=%s&offset=%i", uploadId, offset); HttpRequest *request = new HttpRequest(hNetlibUser, REQUEST_PUT, url); request->AddBearerAuthHeader(db_get_sa(NULL, MODULE, "TokenSecret")); request->AddHeader("Content-Type", "application/octet-stream"); request->pData = (char*)mir_alloc(sizeof(char)* length); memcpy(request->pData, data, length); request->dataLength = length; mir_ptr response(request->Send()); delete request; if (response && response->resultCode == HTTP_STATUS_OK) { JSONNODE *root = json_parse(response->pData); if (root) { JSONNODE *node = json_get(root, "offset"); offset = json_as_int(node); } } return HandleHttpResponseError(hNetlibUser, response); } int CDropbox::SendFileChunkedLast(const char *fileName, const char *uploadId) { CMStringA url; url.AppendFormat( "%s/commit_chunked_upload/%s/%s", DROPBOX_APICONTENT_URL, DROPBOX_API_ROOT, fileName); url.Replace('\\', '/'); CMStringA param = CMStringA("upload_id=") + uploadId; HttpRequest *request = new HttpRequest(hNetlibUser, REQUEST_POST, url); request->AddBearerAuthHeader(db_get_sa(NULL, MODULE, "TokenSecret")); request->AddHeader("Content-Type", "application/x-www-form-urlencoded"); request->pData = mir_strdup(param); request->dataLength = param.GetLength(); mir_ptr response(request->Send()); delete request; return HandleHttpResponseError(hNetlibUser, response); } int CDropbox::CreateFolder(const char *folderName) { CMStringA folder = folderName; folder.Replace('\\', '/'); CMStringA param; param.AppendFormat("root=%s&path=%s", DROPBOX_API_ROOT, folder); HttpRequest *request = new HttpRequest(hNetlibUser, REQUEST_POST, DROPBOX_API_URL "/fileops/create_folder"); request->AddBearerAuthHeader(db_get_sa(NULL, MODULE, "TokenSecret")); request->AddHeader("Content-Type", "application/x-www-form-urlencoded"); request->pData = mir_strdup(param); request->dataLength = param.GetLength(); mir_ptr response(request->Send()); delete request; // forder exists on server if (response->resultCode == HTTP_STATUS_FORBIDDEN) return 0; return HandleHttpResponseError(hNetlibUser, response); } int CDropbox::CreateDownloadUrl(const char *path, wchar_t *url) { CMStringA api_url = DROPBOX_API_URL; api_url.AppendFormat("/shares/%s/%s", DROPBOX_API_ROOT, path); if (!db_get_b(NULL, MODULE, "UseSortLinks", 1)) api_url += "?short_url=false"; HttpRequest *request = new HttpRequest(hNetlibUser, REQUEST_POST, api_url); request->AddBearerAuthHeader(db_get_sa(NULL, MODULE, "TokenSecret")); mir_ptr response(request->Send()); delete request; if (response && response->resultCode == HTTP_STATUS_OK) { JSONNODE *root = json_parse(response->pData); if (root != NULL) { JSONNODE *node = json_get(root, "url"); wcscpy(url, json_as_string(node)); } } return HandleHttpResponseError(hNetlibUser, response); } UINT CDropbox::SendFilesAsync(void *owner, void *arg) { bool error = false; CDropbox *instance = (CDropbox*)owner; FileTransferParam *ftp = (FileTransferParam*)arg; ProtoBroadcastAck(MODULE, ftp->pfts.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, ftp->hProcess, 0); if (ftp->pwszFolders) { for (int i = 0; ftp->pwszFolders[i]; i++) { ptrA utf8_folderName(mir_utf8encodeW(ftp->pwszFolders[i])); if (!instance->CreateFolder(utf8_folderName)) { if (!strchr(utf8_folderName, '\\')) { wchar_t url[MAX_PATH]; if (!instance->CreateDownloadUrl(utf8_folderName, url)) ftp->AddUrl(url); else { error = true; break; } } } else { error = true; break; } } } if (!error) { for (int i = 0; ftp->pfts.pwszFiles[i]; i++) { FILE *file = _wfopen(ftp->pfts.pwszFiles[i], L"rb"); if (file) { const wchar_t *fileName = NULL; if (!ftp->relativePathStart) fileName = wcsrchr(ftp->pfts.pwszFiles[i], L'\\') + 1; else fileName = &ftp->pfts.pwszFiles[i][ftp->relativePathStart]; fseek(file, 0, SEEK_END); size_t fileSize = ftell(file); fseek(file, 0, SEEK_SET); ftp->pfts.currentFileNumber = i; ftp->pfts.currentFileSize = fileSize; ftp->pfts.currentFileProgress = 0; ftp->pfts.wszCurrentFile = wcsrchr(ftp->pfts.pwszFiles[i], '\\') + 1; ProtoBroadcastAck(MODULE, ftp->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ftp->hProcess, (LPARAM)&ftp->pfts); // int offset = 0; char *uploadId = new char[32]; int chunkSize = DROPBOX_FILE_CHUNK_SIZE / 4; if (fileSize < 1024 * 1024) chunkSize = DROPBOX_FILE_CHUNK_SIZE / 20; else if (fileSize > 20 * 1024 * 1024) chunkSize = DROPBOX_FILE_CHUNK_SIZE; while (!feof(file) && fileSize != offset) { if (ferror(file)) { error = true; break; } char *data = new char[chunkSize + 1]; int count = (int)fread(data, sizeof(char), chunkSize, file); if (!offset) { if (instance->SendFileChunkedFirst(data, count, uploadId, offset)) { error = true; break; } } else { if (instance->SendFileChunkedNext(data, count, uploadId, offset)) { error = true; break; } } ftp->pfts.currentFileProgress += count; ftp->pfts.totalProgress += count; ProtoBroadcastAck(MODULE, ftp->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ftp->hProcess, (LPARAM)&ftp->pfts); } fclose(file); if (!error) { ptrA utf8_fileName(mir_utf8encodeW(fileName)); if (instance->SendFileChunkedLast(utf8_fileName, uploadId)) { error = true; break; } else { if (!wcschr(fileName, L'\\')) { wchar_t url[MAX_PATH]; if (!instance->CreateDownloadUrl(utf8_fileName, url)) ftp->AddUrl(url); else { error = true; break; } } ftp->pfts.currentFileProgress = ftp->pfts.currentFileSize; if (i < ftp->pfts.totalFiles - 1) ProtoBroadcastAck(MODULE, ftp->pfts.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ftp->hProcess, 0); } } } else { error = true; break; } } } if (error) { ProtoBroadcastAck(MODULE, ftp->pfts.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ftp->hProcess, 0); return 1; } ProtoBroadcastAck(MODULE, ftp->pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ftp->hProcess, 0); return 0; } UINT CDropbox::SendFilesAndReportAsync(void *owner, void *arg) { CDropbox *instance = (CDropbox*)owner; FileTransferParam *ftp = (FileTransferParam*)arg; int res = SendFilesAsync(owner, arg); if (!res) { CMString urls; for (int i = 0; ftp->pwszUrls[i]; i++) urls.AppendFormat(L"%s\r\n", ftp->pwszUrls[i]); wchar_t *data = urls.GetBuffer(); if (db_get_b(NULL, MODULE, "UrlAutoSend", 1)) { char *message = mir_utf8encodeW(data); if (ftp->hContact != instance->GetDefaultContact()) { if (CallContactService(ftp->hContact, PSS_MESSAGE, PREF_UTF, (LPARAM)message) != ACKRESULT_FAILED) { DBEVENTINFO dbei = { sizeof(dbei) }; dbei.flags = DBEF_UTF | DBEF_SENT/* | DBEF_READ*/; dbei.szModule = MODULE; dbei.timestamp = time(NULL); dbei.eventType = EVENTTYPE_MESSAGE; dbei.cbBlob = wcslen(data); dbei.pBlob = (PBYTE)message; db_event_add(ftp->hContact, &dbei); } else CallServiceSync(MS_MSG_SENDMESSAGEW, (WPARAM)ftp->hContact, (LPARAM)data); } else { DBEVENTINFO dbei = { sizeof(dbei) }; dbei.flags = DBEF_UTF; dbei.szModule = MODULE; dbei.timestamp = time(NULL); dbei.eventType = EVENTTYPE_MESSAGE; dbei.cbBlob = wcslen(data); dbei.pBlob = (PBYTE)message; db_event_add(ftp->hContact, &dbei); } } if (db_get_b(NULL, MODULE, "UrlPasteToMessageInputArea", 0)) CallServiceSync(MS_MSG_SENDMESSAGEW, (WPARAM)ftp->hContact, (LPARAM)data); if (db_get_b(NULL, MODULE, "UrlCopyToClipboard", 0)) { if (OpenClipboard(NULL)) { EmptyClipboard(); size_t size = sizeof(wchar_t) * (urls.GetLength() + 1); HGLOBAL hClipboardData = GlobalAlloc(NULL, size); if (hClipboardData) { wchar_t *pchData = (wchar_t*)GlobalLock(hClipboardData); if (pchData) { memcpy(pchData, (wchar_t*)data, size); GlobalUnlock(hClipboardData); SetClipboardData(CF_UNICODETEXT, hClipboardData); } } CloseClipboard(); } } } delete ftp; return res; } UINT CDropbox::SendFilesAndEventAsync(void *owner, void *arg) { CDropbox *instance = (CDropbox*)owner; FileTransferParam *ftp = (FileTransferParam*)arg; int res = SendFilesAsync(owner, arg); TRANSFERINFO ti = { 0 }; ti.hProcess = ftp->hProcess; ti.status = res; ti.data = ftp->pwszUrls; NotifyEventHooks(instance->hFileSentEventHook, ftp->hContact, (LPARAM)&ti); delete ftp; return res; }