From 3b55a62fdcb1f8222de3c2c8fbed530792c419a0 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Fri, 12 Oct 2012 14:53:57 +0000 Subject: GTalkExt, ICQ, IRC, Jabber: folders restructurization git-svn-id: http://svn.miranda-ng.org/main/trunk@1890 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/IcqOscarJ/src/icq_filetransfer.cpp | 515 +++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 protocols/IcqOscarJ/src/icq_filetransfer.cpp (limited to 'protocols/IcqOscarJ/src/icq_filetransfer.cpp') diff --git a/protocols/IcqOscarJ/src/icq_filetransfer.cpp b/protocols/IcqOscarJ/src/icq_filetransfer.cpp new file mode 100644 index 0000000000..61484c3987 --- /dev/null +++ b/protocols/IcqOscarJ/src/icq_filetransfer.cpp @@ -0,0 +1,515 @@ +// ---------------------------------------------------------------------------80 +// ICQ plugin for Miranda Instant Messenger +// ________________________________________ +// +// Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede +// Copyright © 2001-2002 Jon Keating, Richard Hughes +// Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater +// Copyright © 2004-2009 Joe Kucera +// +// 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. +// +// ----------------------------------------------------------------------------- +// DESCRIPTION: +// +// Describe me here please... +// +// ----------------------------------------------------------------------------- +#include "icqoscar.h" + + +static void file_buildProtoFileTransferStatus(filetransfer* ft, PROTOFILETRANSFERSTATUS* pfts) +{ + ZeroMemory(pfts, sizeof(PROTOFILETRANSFERSTATUS)); + pfts->cbSize = sizeof(PROTOFILETRANSFERSTATUS); + pfts->hContact = ft->hContact; + pfts->flags = PFTS_UTF | (ft->sending ? PFTS_SENDING : PFTS_RECEIVING); /* Standard FT is Ansi only */ + if (ft->sending) + pfts->pszFiles = ft->pszFiles; + else + pfts->pszFiles = NULL; /* FIXME */ + pfts->totalFiles = ft->dwFileCount; + pfts->currentFileNumber = ft->iCurrentFile; + pfts->totalBytes = ft->dwTotalSize; + pfts->totalProgress = ft->dwBytesDone; + pfts->szWorkingDir = ft->szSavePath; + pfts->szCurrentFile = ft->szThisFile; + pfts->currentFileSize = ft->dwThisFileSize; + pfts->currentFileTime = ft->dwThisFileDate; + pfts->currentFileProgress = ft->dwFileBytesDone; +} + + +static void file_sendTransferSpeed(CIcqProto* ppro, directconnect* dc) +{ + icq_packet packet; + + directPacketInit(&packet, 5); + packByte(&packet, PEER_FILE_SPEED); /* Ident */ + packLEDWord(&packet, dc->ft->dwTransferSpeed); + ppro->sendDirectPacket(dc, &packet); +} + + +static void file_sendNick(CIcqProto* ppro, directconnect* dc) +{ + icq_packet packet; + char* szNick; + WORD wNickLen; + DBVARIANT dbv = {DBVT_DELETED}; + + if (ppro->getSettingString(NULL, "Nick", &dbv)) + szNick = ""; + else + szNick = dbv.pszVal; + + wNickLen = strlennull(szNick); + + directPacketInit(&packet, (WORD)(8 + wNickLen)); + packByte(&packet, PEER_FILE_INIT_ACK); /* Ident */ + packLEDWord(&packet, dc->ft->dwTransferSpeed); + packLEWord(&packet, (WORD)(wNickLen + 1)); + packBuffer(&packet, (LPBYTE)szNick, (WORD)(wNickLen + 1)); + ppro->sendDirectPacket(dc, &packet); + ICQFreeVariant(&dbv); +} + + +static void file_sendNextFile(CIcqProto* ppro, directconnect* dc) +{ + icq_packet packet; + struct _stati64 statbuf; + char szThisSubDir[MAX_PATH]; + + if (dc->ft->iCurrentFile >= (int)dc->ft->dwFileCount) + { + ppro->BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dc->ft, 0); + ppro->CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + return; + } + + dc->ft->szThisFile = dc->ft->pszFiles[dc->ft->iCurrentFile]; + if (FileStatUtf(dc->ft->szThisFile, &statbuf)) + { + ppro->icq_LogMessage(LOG_ERROR, LPGEN("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.")); + ppro->CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + return; + } + + char *pszThisFileName = FindFilePathContainer((LPCSTR*)dc->ft->pszFiles, dc->ft->iCurrentFile, szThisSubDir); + + if (statbuf.st_mode&_S_IFDIR) + { + dc->ft->currentIsDir = 1; + } + else + { + dc->ft->currentIsDir = 0; + dc->ft->fileId = OpenFileUtf(dc->ft->szThisFile, _O_BINARY | _O_RDONLY, _S_IREAD); + if (dc->ft->fileId == -1) + { + ppro->icq_LogMessage(LOG_ERROR, LPGEN("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.")); + ppro->CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + return; + } + + } + dc->ft->dwThisFileSize = statbuf.st_size; + dc->ft->dwThisFileDate = statbuf.st_mtime; + dc->ft->dwFileBytesDone = 0; + + char *szThisFileNameAnsi = NULL, *szThisSubDirAnsi = NULL; + if (!utf8_decode(pszThisFileName, &szThisFileNameAnsi)) + szThisFileNameAnsi = NULL; + if (!utf8_decode(szThisSubDir, &szThisSubDirAnsi)) + szThisSubDirAnsi = NULL; + WORD wThisFileNameLen = strlennull(szThisFileNameAnsi); + WORD wThisSubDirLen = strlennull(szThisSubDirAnsi); + + directPacketInit(&packet, (WORD)(20 + wThisFileNameLen + wThisSubDirLen)); + packByte(&packet, PEER_FILE_NEXTFILE); /* Ident */ + packByte(&packet, (BYTE)((statbuf.st_mode & _S_IFDIR) != 0)); // Is subdir + packLEWord(&packet, (WORD)(wThisFileNameLen + 1)); + packBuffer(&packet, (LPBYTE)szThisFileNameAnsi, (WORD)(wThisFileNameLen + 1)); + packLEWord(&packet, (WORD)(wThisSubDirLen + 1)); + packBuffer(&packet, (LPBYTE)szThisSubDirAnsi, (WORD)(wThisSubDirLen + 1)); + packLEDWord(&packet, dc->ft->dwThisFileSize); + packLEDWord(&packet, statbuf.st_mtime); + packLEDWord(&packet, dc->ft->dwTransferSpeed); + SAFE_FREE(&szThisFileNameAnsi); + SAFE_FREE(&szThisSubDirAnsi); + ppro->sendDirectPacket(dc, &packet); + + ppro->BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, dc->ft, 0); +} + + +static void file_sendResume(CIcqProto* ppro, directconnect* dc) +{ + icq_packet packet; + + directPacketInit(&packet, 17); + packByte(&packet, PEER_FILE_RESUME); /* Ident */ + packLEDWord(&packet, dc->ft->dwFileBytesDone); /* file resume */ + packLEDWord(&packet, 0); /* unknown */ + packLEDWord(&packet, dc->ft->dwTransferSpeed); + packLEDWord(&packet, dc->ft->iCurrentFile + 1); /* file number */ + ppro->sendDirectPacket(dc, &packet); +} + + +static void file_sendData(CIcqProto* ppro, directconnect* dc) +{ + BYTE buf[2048]; + int bytesRead = 0; + + if (!dc->ft->currentIsDir) + { + icq_packet packet; + + if (dc->ft->fileId == -1) + return; + bytesRead = _read(dc->ft->fileId, buf, sizeof(buf)); + if (bytesRead == -1) + return; + + directPacketInit(&packet, (WORD)(1 + bytesRead)); + packByte(&packet, PEER_FILE_DATA); /* Ident */ + packBuffer(&packet, buf, (WORD)bytesRead); + ppro->sendDirectPacket(dc, &packet); + } + + dc->ft->dwBytesDone += bytesRead; + dc->ft->dwFileBytesDone += bytesRead; + + if (GetTickCount() > dc->ft->dwLastNotify + 500 || bytesRead == 0) + { + PROTOFILETRANSFERSTATUS pfts; + + file_buildProtoFileTransferStatus(dc->ft, &pfts); + ppro->BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, dc->ft, (LPARAM)&pfts); + + dc->ft->dwLastNotify = GetTickCount(); + } + + if (bytesRead == 0) + { + if (!dc->ft->currentIsDir) _close(dc->ft->fileId); + dc->ft->fileId = -1; + dc->wantIdleTime = 0; + dc->ft->iCurrentFile++; + file_sendNextFile(ppro, dc); /* this will close the socket if no more files */ + } +} + + +void CIcqProto::handleFileTransferIdle(directconnect* dc) +{ + file_sendData(this, dc); +} + + +void CIcqProto::icq_sendFileResume(filetransfer *ft, int action, const char *szFilename) +{ + if (ft->hConnection == NULL) + return; + + directconnect *dc = FindFileTransferDC(ft); + if (!dc) return; // something is broken... + + int openFlags; + + switch (action) + { + case FILERESUME_RESUME: + openFlags = _O_BINARY | _O_WRONLY; + break; + + case FILERESUME_OVERWRITE: + openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY; + ft->dwFileBytesDone = 0; + break; + + case FILERESUME_SKIP: + openFlags = _O_BINARY | _O_WRONLY; + ft->dwFileBytesDone = ft->dwThisFileSize; + break; + + case FILERESUME_RENAME: + openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY; + SAFE_FREE(&ft->szThisFile); + ft->szThisFile = null_strdup(szFilename); + ft->dwFileBytesDone = 0; + break; + } + + ft->fileId = OpenFileUtf(ft->szThisFile, openFlags, _S_IREAD | _S_IWRITE); + if (ft->fileId == -1) + { + icq_LogMessage(LOG_ERROR, LPGEN("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.")); + NetLib_CloseConnection(&ft->hConnection, FALSE); + return; + } + + if (action == FILERESUME_RESUME) + ft->dwFileBytesDone = _lseek(ft->fileId, 0, SEEK_END); + else + _lseek(ft->fileId, ft->dwFileBytesDone, SEEK_SET); + + ft->dwBytesDone += ft->dwFileBytesDone; + + file_sendResume(this, dc); + + BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0); +} + + +// small utility function +void NormalizeBackslash(char* path) +{ + int len = strlennull(path); + + if (len && path[len-1] != '\\') strcat(path, "\\"); +} + +/* a file transfer looks like this: +S: 0 +R: 5 +R: 1 +S: 2 +R: 3 +S: 6 * many +(for more files, send 2, 3, 6*many) +*/ + +void CIcqProto::handleFileTransferPacket(directconnect* dc, PBYTE buf, WORD wLen) +{ + if (wLen < 1) + return; + + NetLog_Direct("Handling file packet"); + + switch (buf[0]) + { + case PEER_FILE_INIT: /* first packet of a file transfer */ + if (dc->initialised) + return; + if (wLen < 19) + return; + buf += 5; /* id, and unknown 0 */ + dc->type = DIRECTCONN_FILE; + { + DWORD dwFileCount; + DWORD dwTotalSize; + DWORD dwTransferSpeed; + WORD wNickLength; + int bAdded; + + unpackLEDWord(&buf, &dwFileCount); + unpackLEDWord(&buf, &dwTotalSize); + unpackLEDWord(&buf, &dwTransferSpeed); + unpackLEWord(&buf, &wNickLength); + + dc->ft = FindExpectedFileRecv(dc->dwRemoteUin, dwTotalSize); + if (dc->ft == NULL) + { + NetLog_Direct("Unexpected file receive"); + CloseDirectConnection(dc); + return; + } + dc->ft->dwFileCount = dwFileCount; + dc->ft->dwTransferSpeed = dwTransferSpeed; + dc->ft->hContact = HContactFromUIN(dc->ft->dwUin, &bAdded); + dc->ft->dwBytesDone = 0; + dc->ft->iCurrentFile = -1; + dc->ft->fileId = -1; + dc->ft->hConnection = dc->hConnection; + dc->ft->dwLastNotify = GetTickCount(); + + dc->initialised = 1; + + file_sendTransferSpeed(this, dc); + file_sendNick(this, dc); + } + BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, dc->ft, 0); + break; + + case PEER_FILE_INIT_ACK: + if (wLen < 8) + return; + buf++; + unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); + /* followed by nick */ + file_sendNextFile(this, dc); + break; + + case PEER_FILE_NEXTFILE: + if (wLen < 20) + return; + buf++; /* id */ + { + char *szAnsi; + WORD wThisFilenameLen, wSubdirLen; + BYTE isDirectory; + + unpackByte(&buf, &isDirectory); + unpackLEWord(&buf, &wThisFilenameLen); + if (wLen < 19 + wThisFilenameLen) + return; + SAFE_FREE(&dc->ft->szThisFile); + szAnsi = (char *)_alloca(wThisFilenameLen + 1); + memcpy(szAnsi, buf, wThisFilenameLen); + szAnsi[wThisFilenameLen] = '\0'; + dc->ft->szThisFile = ansi_to_utf8(szAnsi); + buf += wThisFilenameLen; + + unpackLEWord(&buf, &wSubdirLen); + if (wLen < 18 + wThisFilenameLen + wSubdirLen) + return; + SAFE_FREE(&dc->ft->szThisSubdir); + szAnsi = (char *)_alloca(wSubdirLen + 1); + memcpy(szAnsi, buf, wSubdirLen); + szAnsi[wSubdirLen] = '\0'; + dc->ft->szThisSubdir = ansi_to_utf8(szAnsi); + buf += wSubdirLen; + + unpackLEDWord(&buf, &dc->ft->dwThisFileSize); + unpackLEDWord(&buf, &dc->ft->dwThisFileDate); + unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); + + /* no cheating with paths */ + if (!IsValidRelativePath(dc->ft->szThisFile) || !IsValidRelativePath(dc->ft->szThisSubdir)) + { + NetLog_Direct("Invalid path information"); + break; + } + + char *szFullPath = (char*)SAFE_MALLOC(strlennull(dc->ft->szSavePath)+strlennull(dc->ft->szThisSubdir)+strlennull(dc->ft->szThisFile)+3); + strcpy(szFullPath, dc->ft->szSavePath); + NormalizeBackslash(szFullPath); + strcat(szFullPath, dc->ft->szThisSubdir); + NormalizeBackslash(szFullPath); +// _chdir(szFullPath); // set current dir - not very useful + strcat(szFullPath, dc->ft->szThisFile); + // we joined the full path to dest file + SAFE_FREE(&dc->ft->szThisFile); + dc->ft->szThisFile = szFullPath; + + dc->ft->dwFileBytesDone = 0; + dc->ft->iCurrentFile++; + + if (isDirectory) + { + MakeDirUtf(dc->ft->szThisFile); + dc->ft->fileId = -1; + } + else + { + /* file resume */ + PROTOFILETRANSFERSTATUS pfts = {0}; + + file_buildProtoFileTransferStatus(dc->ft, &pfts); + if (BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, dc->ft, (LPARAM)&pfts)) + break; /* UI supports resume: it will call PS_FILERESUME */ + + dc->ft->fileId = OpenFileUtf(dc->ft->szThisFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); + if (dc->ft->fileId == -1) + { + icq_LogMessage(LOG_ERROR, LPGEN("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.")); + CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + break; + } + } + } + file_sendResume(this, dc); + BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, dc->ft, 0); + break; + + case PEER_FILE_RESUME: + if (dc->ft->fileId == -1 && !dc->ft->currentIsDir) + return; + if (wLen < 13) + return; + if (wLen < 17) + NetLog_Direct("Warning: Received short PEER_FILE_RESUME"); + buf++; + { + DWORD dwRestartFrom; + + unpackLEDWord(&buf, &dwRestartFrom); + if (dwRestartFrom > dc->ft->dwThisFileSize) + return; + buf += 4; /* unknown. 0 */ + unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); + buf += 4; /* unknown. 1 */ + if (!dc->ft->currentIsDir) + _lseek(dc->ft->fileId, dwRestartFrom, 0); + dc->wantIdleTime = 1; + dc->ft->dwBytesDone += dwRestartFrom; + dc->ft->dwFileBytesDone += dwRestartFrom; + } + break; + + case PEER_FILE_SPEED: + if (wLen < 5) + return; + buf++; + unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); + dc->ft->dwLastNotify = GetTickCount(); + break; + + case PEER_FILE_DATA: + if (!dc->ft->currentIsDir) + { + if (dc->ft->fileId == -1) + break; + buf++; wLen--; + _write(dc->ft->fileId, buf, wLen); + } + else + wLen = 0; + dc->ft->dwBytesDone += wLen; + dc->ft->dwFileBytesDone += wLen; + if (GetTickCount() > dc->ft->dwLastNotify + 500 || wLen < 2048) + { + PROTOFILETRANSFERSTATUS pfts; + + file_buildProtoFileTransferStatus(dc->ft, &pfts); + BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, dc->ft, (LPARAM)&pfts); + dc->ft->dwLastNotify = GetTickCount(); + } + if (wLen < 2048) + { + /* EOF */ + if (!dc->ft->currentIsDir) + _close(dc->ft->fileId); + dc->ft->fileId = -1; + if ((DWORD)dc->ft->iCurrentFile == dc->ft->dwFileCount - 1) + { + dc->type = DIRECTCONN_CLOSING; /* this guarantees that we won't accept any more data but that the sender is still free to closesocket() neatly */ + BroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dc->ft, 0); + } + } + break; + + default: + NetLog_Direct("Unknown file transfer packet ignored."); + break; + } +} -- cgit v1.2.3