From cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c Mon Sep 17 00:00:00 2001 From: watcherhd Date: Thu, 21 Apr 2011 14:14:52 +0000 Subject: svn.miranda.im is moving to a new home! git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@7 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- icqj_mod/icq_filetransfer.c | 576 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100644 icqj_mod/icq_filetransfer.c (limited to 'icqj_mod/icq_filetransfer.c') diff --git a/icqj_mod/icq_filetransfer.c b/icqj_mod/icq_filetransfer.c new file mode 100644 index 0000000..576d65e --- /dev/null +++ b/icqj_mod/icq_filetransfer.c @@ -0,0 +1,576 @@ +// ---------------------------------------------------------------------------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,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater +// Copyright © 2004,2005,2006 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. +// +// ----------------------------------------------------------------------------- +// +// File name : $Source: /cvsroot/miranda/miranda/protocols/IcqOscarJ/icq_filetransfer.c,v $ +// Revision : $Revision: 2874 $ +// Last change on : $Date: 2006-05-16 23:38:00 +0200 (Tue, 16 May 2006) $ +// Last change by : $Author: ghazan $ +// +// 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->sending = ft->sending; + if (ft->sending) + pfts->files = ft->files; + else + pfts->files = NULL; /* FIXME */ + pfts->totalFiles = ft->dwFileCount; + pfts->currentFileNumber = ft->iCurrentFile; + pfts->totalBytes = ft->dwTotalSize; + pfts->totalProgress = ft->dwBytesDone; + pfts->workingDir = ft->szSavePath; + pfts->currentFile = ft->szThisFile; + pfts->currentFileSize = ft->dwThisFileSize; + pfts->currentFileTime = ft->dwThisFileDate; + pfts->currentFileProgress = ft->dwFileBytesDone; +} + + + +static void file_sendTransferSpeed(directconnect* dc) +{ + icq_packet packet; + + directPacketInit(&packet, 5); + packByte(&packet, PEER_FILE_SPEED); /* Ident */ + packLEDWord(&packet, dc->ft->dwTransferSpeed); + sendDirectPacket(dc, &packet); +} + + + +static void file_sendNick(directconnect* dc) +{ + icq_packet packet; + char* szNick; + WORD wNickLen; + DBVARIANT dbv; + + + dbv.type = DBVT_DELETED; + if (ICQGetContactSetting(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, szNick, (WORD)(wNickLen + 1)); + sendDirectPacket(dc, &packet); + ICQFreeVariant(&dbv); +} + + + +static void file_sendNextFile(directconnect* dc) +{ + icq_packet packet; + struct _stat statbuf; + char *pszThisFileName; + char szThisSubDir[MAX_PATH]; + WORD wThisFileNameLen, wThisSubDirLen; + + + if (dc->ft->iCurrentFile >= (int)dc->ft->dwFileCount) + { + ICQBroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dc->ft, 0); + CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + return; + } + + dc->ft->szThisFile = null_strdup(dc->ft->files[dc->ft->iCurrentFile]); + if (_stat(dc->ft->szThisFile, &statbuf)) + { + icq_LogMessage(LOG_ERROR, "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."); + CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + return; + } + + szThisSubDir[0] = '\0'; + + if (((pszThisFileName = strrchr(dc->ft->szThisFile, '\\')) == NULL) && + ((pszThisFileName = strrchr(dc->ft->szThisFile, '/')) == NULL)) + { + pszThisFileName = dc->ft->szThisFile; + } + else + { + int i; + int len; + + + /* find an earlier subdirectory to be used as a container */ + for (i = dc->ft->iCurrentFile - 1; i >= 0; i--) + { + len = strlennull(dc->ft->files[i]); + if (!_strnicmp(dc->ft->files[i], dc->ft->szThisFile, len) && + (dc->ft->szThisFile[len] == '\\' || dc->ft->szThisFile[len] == '/')) + { + char* pszLastBackslash; + + if (((pszLastBackslash = strrchr(dc->ft->files[i], '\\')) == NULL) && + ((pszLastBackslash = strrchr(dc->ft->files[i], '/')) == NULL)) + { + strcpy(szThisSubDir, dc->ft->files[i]); + } + else + { + len = pszLastBackslash - dc->ft->files[i] + 1; + strncpy(szThisSubDir, dc->ft->szThisFile + len, + pszThisFileName - dc->ft->szThisFile - len); + szThisSubDir[pszThisFileName - dc->ft->szThisFile - len] = '\0'; + } + } + } + pszThisFileName++; // skip backslash + } + + if (statbuf.st_mode&_S_IFDIR) + { + dc->ft->currentIsDir = 1; + } + else + { + dc->ft->currentIsDir = 0; + dc->ft->fileId = _open(dc->ft->szThisFile, _O_BINARY | _O_RDONLY); + if (dc->ft->fileId == -1) + { + icq_LogMessage(LOG_ERROR, "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."); + CloseDirectConnection(dc); + dc->ft->hConnection = NULL; + return; + } + + } + dc->ft->dwThisFileSize = statbuf.st_size; + dc->ft->dwThisFileDate = statbuf.st_mtime; + dc->ft->dwFileBytesDone = 0; + + wThisFileNameLen = strlennull(pszThisFileName); + wThisSubDirLen = strlennull(szThisSubDir); + + 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, pszThisFileName, (WORD)(wThisFileNameLen + 1)); + packLEWord(&packet, (WORD)(wThisSubDirLen + 1)); + packBuffer(&packet, szThisSubDir, (WORD)(wThisSubDirLen + 1)); + packLEDWord(&packet, dc->ft->dwThisFileSize); + packLEDWord(&packet, statbuf.st_mtime); + packLEDWord(&packet, dc->ft->dwTransferSpeed); + + sendDirectPacket(dc, &packet); + + ICQBroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, dc->ft, 0); +} + + + +static void file_sendResume(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 */ + sendDirectPacket(dc, &packet); +} + + + +static void file_sendData(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); + 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); + ICQBroadcastAck(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(dc); /* this will close the socket if no more files */ + } +} + + + +void handleFileTransferIdle(directconnect* dc) +{ + file_sendData(dc); +} + + + +void icq_sendFileResume(filetransfer* ft, int action, const char* szFilename) +{ + int openFlags; + directconnect *dc; + + + if (ft->hConnection == NULL) + return; + + dc = FindFileTransferDC(ft); + if (!dc) return; // something is broken... + + 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 = _open(ft->szThisFile, openFlags, _S_IREAD | _S_IWRITE); + if (ft->fileId == -1) + { + icq_LogMessage(LOG_ERROR, "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_CloseHandle(ft->hConnection); + ft->hConnection = NULL; + 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(dc); + + ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0); +} + + + +// small utility function +static 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 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(dc); + file_sendNick(dc); + } + ICQBroadcastAck(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(dc); + break; + + case PEER_FILE_NEXTFILE: + if (wLen < 20) + return; + buf++; /* id */ + { + WORD wThisFilenameLen, wSubdirLen; + BYTE isDirectory; + char *szFullPath; + + unpackByte(&buf, &isDirectory); + unpackLEWord(&buf, &wThisFilenameLen); + if (wLen < 19 + wThisFilenameLen) + return; + SAFE_FREE(&dc->ft->szThisFile); + dc->ft->szThisFile = (char *)SAFE_MALLOC(wThisFilenameLen + 1); + memcpy(dc->ft->szThisFile, buf, wThisFilenameLen); + dc->ft->szThisFile[wThisFilenameLen] = '\0'; + buf += wThisFilenameLen; + + unpackLEWord(&buf, &wSubdirLen); + if (wLen < 18 + wThisFilenameLen + wSubdirLen) + return; + SAFE_FREE(&dc->ft->szThisSubdir); + dc->ft->szThisSubdir = (char *)SAFE_MALLOC(wSubdirLen + 1); + memcpy(dc->ft->szThisSubdir, buf, wSubdirLen); + dc->ft->szThisSubdir[wSubdirLen] = '\0'; + buf += wSubdirLen; + + unpackLEDWord(&buf, &dc->ft->dwThisFileSize); + unpackLEDWord(&buf, &dc->ft->dwThisFileDate); + unpackLEDWord(&buf, &dc->ft->dwTransferSpeed); + + /* no cheating with paths */ + if (strstr(dc->ft->szThisFile, "..\\") || strstr(dc->ft->szThisFile, "../") || + strstr(dc->ft->szThisFile, ":\\") || strstr(dc->ft->szThisFile, ":/") || + dc->ft->szThisFile[0] == '\\' || dc->ft->szThisFile[0] == '/') + { + NetLog_Direct("Invalid path information"); + break; + } + if (strstr(dc->ft->szThisSubdir, "..\\") || strstr(dc->ft->szThisSubdir, "../") || + strstr(dc->ft->szThisSubdir, ":\\") || strstr(dc->ft->szThisSubdir, ":/") || + dc->ft->szThisSubdir[0] == '\\' || dc->ft->szThisSubdir[0] == '/') + { + NetLog_Direct("Invalid path information"); + break; + } + + 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) + { + _mkdir(dc->ft->szThisFile); + dc->ft->fileId = -1; + } + else + { + /* file resume */ + PROTOFILETRANSFERSTATUS pfts = {0}; + + file_buildProtoFileTransferStatus(dc->ft, &pfts); + if (ICQBroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, dc->ft, (LPARAM)&pfts)) + break; /* UI supports resume: it will call PS_FILERESUME */ + + dc->ft->fileId = _open(dc->ft->szThisFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); + if (dc->ft->fileId == -1) + { + icq_LogMessage(LOG_ERROR, "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(dc); + ICQBroadcastAck(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); + ICQBroadcastAck(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 */ + ICQBroadcastAck(dc->ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dc->ft, 0); + } + } + break; + + default: + NetLog_Direct("Unknown file transfer packet ignored."); + break; + } +} -- cgit v1.2.3