/* ICQ Corporate protocol plugin for Miranda IM. Copyright (C) 2003-2005 Eugene Tarasenko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "corp.h" std::vector icqTransfers; /////////////////////////////////////////////////////////////////////////////// void WINAPI transferTimerProc(HWND, UINT, UINT_PTR hTimer, DWORD) { KillTimer(nullptr, hTimer); for (size_t i = 0; i < icqTransfers.size(); i++) if (hTimer == icqTransfers[i]->hTimer) icqTransfers[i]->process(); } /////////////////////////////////////////////////////////////////////////////// ICQTransfer::ICQTransfer(ICQUser *u, unsigned int theSequence) : socket(WM_NETEVENT_TRANSFER) { uin = u->uin; hContact = u->hContact; sequence = theSequence; files = nullptr; description = nullptr; path = nullptr; sending = 0; speed = 100; count = 0; current = -1; fileName = nullptr; fileSize = 0; fileProgress = 0; totalSize = 0; totalProgress = 0; lastNotify = 0; hTimer = NULL; hFile = INVALID_HANDLE_VALUE; } /////////////////////////////////////////////////////////////////////////////// ICQTransfer::~ICQTransfer() { closeFile(); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::processTcpPacket(Packet &packet) { unsigned int /*i,*/ status, junkLong; unsigned char cmd/*, junkChar*/; char *name = nullptr, *directoryName = nullptr; packet >> cmd; switch (cmd) { case 0x00: T("[tcp] recieve initialising file transfer\n"); packet >> junkLong >> count >> totalSize >> speed >> name; files = new char*[count + 1]; ZeroMemory(files, (count + 1)*sizeof(char*)); ack(ACKRESULT_INITIALISING); sendPacket0x01(); break; case 0x01: T("[tcp] ack initialising\n"); packet >> speed >> name; ack(ACKRESULT_INITIALISING); sendPacket0x02(); break; case 0x02: T("[tcp] recieve next file\n"); packet >> directory >> files[++current] >> directoryName >> fileSize >> fileDate >> speed; if (directoryName[0]) { char *fullName = new char[mir_strlen(directoryName) + mir_strlen(files[current]) + 2]; sprintf(fullName, "%s\\%s", directoryName, files[current]); delete[] files[current]; files[current] = fullName; } if (directory) createDirectory(); else openFile(); ack(ACKRESULT_NEXTFILE); if (fileProgress) ack(ACKRESULT_FILERESUME); else sendPacket0x03(); break; case 0x03: T("[tcp] ack next file\n"); packet >> fileProgress >> status >> speed; totalProgress += fileProgress; setFilePosition(); ack(ACKRESULT_NEXTFILE); if (status != 0) { totalProgress += fileSize - fileProgress; fileProgress = fileSize; closeFile(); ack(ACKRESULT_DATA); } process(); break; case 0x04: T("[tcp] recieve stop file\n"); packet >> junkLong; totalProgress += fileSize - fileProgress; fileProgress = fileSize; closeFile(); ack(ACKRESULT_DATA); break; case 0x05: T("[tcp] recieve new speed\n"); packet >> speed; break; case 0x06: unsigned long result; WriteFile(hFile, packet.data(), packet.dataSize(), &result, nullptr); fileProgress += result; totalProgress += result; if (fileProgress >= fileSize) closeFile(); ack(ACKRESULT_DATA); break; default: T("[tcp] unknown packet:\n%s", packet.print()); packet.reset(); } delete[] directoryName; delete[] name; } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x00() { char nick[1] = { 0 }; sending = true; Packet packet; packet << (unsigned char)0x00 << (unsigned int)0x00 << count << totalSize << speed << nick; T("[tcp] send packet 0x00\n"); socket.sendPacket(packet); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x01() { char nick[1] = { 0 }; Packet packet; packet << (unsigned char)0x01 << speed << nick; T("[tcp] send packet 0x01\n"); socket.sendPacket(packet); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x02() { current++; openFile(); char *directoryName = _strdup(fileName); char *p = strrchr(directoryName, '\\'); p[0] = 0; p[1] = 0; Packet packet; packet << (unsigned char)0x02 << directory << (strrchr(fileName, '\\') + 1) << (directoryName + mir_strlen(path) + 1) << fileSize << fileDate << speed; T("[tcp] send packet 0x02\n"); socket.sendPacket(packet); ack(ACKRESULT_NEXTFILE); free(directoryName); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x03() { Packet packet; packet << (unsigned char)0x03 << fileProgress << (unsigned int)0x00 << speed; setFilePosition(); T("[tcp] send packet 0x03\n"); socket.sendPacket(packet); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x04() { T("[tcp] send packet 0x04\n"); // icq_PacketAppend8(p, 0x04); // icq_PacketAppend32(p, filenum); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x05() { T("[tcp] send packet 0x05\n"); // icq_PacketAppend8(p, 0x05); // icq_PacketAppend32(p, speed); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::sendPacket0x06() { Packet packet; packet << (unsigned char)0x06; unsigned long result; ReadFile(hFile, packet.data(), 2048, &result, nullptr); if (result == 0) return; packet.add(result); socket.sendPacket(packet); fileProgress += result; totalProgress += result; if (fileProgress >= fileSize) closeFile(); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::ack(unsigned int result) { if (result == ACKRESULT_DATA && GetTickCount() < lastNotify + 250 && fileProgress < fileSize) return; PROTOFILETRANSFERSTATUS fts; fts.cbSize = sizeof(fts); fts.hContact = hContact; fts.pszFiles = files; fts.totalFiles = count; fts.currentFileNumber = current; fts.totalBytes = totalSize; fts.totalProgress = totalProgress; fts.szWorkingDir = path; fts.szCurrentFile = fileName; fts.currentFileSize = fileSize; fts.currentFileProgress = fileProgress; fts.currentFileTime = TimeZone_ToLocal(fileDate); ProtoBroadcastAck(protoName, hContact, ACKTYPE_FILE, result, this, (LPARAM)&fts); lastNotify = GetTickCount(); if (result == ACKRESULT_DATA && current >= count - 1 && fileProgress >= fileSize) { ProtoBroadcastAck(protoName, hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, this, 0); socket.closeConnection(); unsigned int i; for (i = 0; i < icqTransfers.size(); i++) { if (icqTransfers[i] == this) { delete icqTransfers[i]; icqTransfers[i] = icqTransfers[icqTransfers.size() - 1]; icqTransfers.pop_back(); break; } } } } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::process() { unsigned int /*i, */startTime; hTimer = NULL; if (current >= count) return; startTime = GetTickCount(); while (fileProgress < fileSize && GetTickCount() < startTime + 100) sendPacket0x06(); ack(ACKRESULT_DATA); if (fileProgress < fileSize) hTimer = SetTimer(nullptr, 0, 1, transferTimerProc); else if (current < count - 1) sendPacket0x02(); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::resume(int action, const char *newName) { switch (action) { case FILERESUME_OVERWRITE: T("[ ] overwrite existing file\n"); fileProgress = 0; break; case FILERESUME_RESUME: T("[ ] file resume\n"); break; case FILERESUME_RENAME: T("[ ] rename file\n"); delete[] fileName; fileName = new char[mir_strlen(newName) + 1]; mir_strcpy(fileName, newName); files[current] = fileName; openFile(); fileProgress = 0; break; case FILERESUME_SKIP: T("[ ] skip file\n"); fileProgress = fileSize; break; } totalProgress += fileProgress; sendPacket0x03(); ack(ACKRESULT_NEXTFILE); if (fileProgress) ack(ACKRESULT_DATA); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::openFile() { if (hFile != INVALID_HANDLE_VALUE) closeFile(); if (path) SetCurrentDirectory(path); fileName = files[current]; WIN32_FIND_DATA findData; HANDLE hFind = FindFirstFile(fileName, &findData); if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { T("open directory %s\n", fileName); directory = 1; fileProgress = 0; fileSize = 0; fileDate = *(__int64*)(&findData.ftLastWriteTime) / 10000000 - 11644473600i64; return; } } directory = 0; hFile = CreateFile(fileName, sending ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, 0, nullptr); if (hFile == INVALID_HANDLE_VALUE) { char msg[2048]; T("can't open file %s\n", fileName); sprintf(msg, "%s\n%s", sending ? Translate("Your file transfer has been aborted because one of the files that you selected to send is no longer readable from the disk. You may have deleted or moved it.") : Translate("Your file receive has been aborted because Miranda could not open the destination file in order to write to it. You may be trying to save to a read-only folder."), fileName); MessageBox(nullptr, msg, Translate(protoName), MB_ICONWARNING | MB_OK); return; } __int64 fileTime; if (sending) { fileProgress = 0; fileSize = GetFileSize(hFile, nullptr); GetFileTime(hFile, nullptr, nullptr, (LPFILETIME)&fileTime); fileDate = fileTime / 10000000 - 11644473600i64; } else { fileProgress = GetFileSize(hFile, nullptr); fileTime = (11644473600i64 + (__int64)fileDate) * 10000000; SetFileTime(hFile, nullptr, nullptr, (LPFILETIME)&fileTime); } } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::closeFile() { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::setFilePosition() { if (hFile != INVALID_HANDLE_VALUE) SetFilePointer(hFile, fileProgress, nullptr, FILE_BEGIN); } /////////////////////////////////////////////////////////////////////////////// void ICQTransfer::createDirectory() { if (path) SetCurrentDirectory(path); fileName = files[current]; CreateDirectory(fileName, nullptr); fileProgress = 0; }