summaryrefslogtreecommitdiff
path: root/protocols/IcqOscarJ/icq_filetransfer.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
commit48540940b6c28bb4378abfeb500ec45a625b37b6 (patch)
tree2ef294c0763e802f91d868bdef4229b6868527de /protocols/IcqOscarJ/icq_filetransfer.cpp
parent5c350913f011e119127baeb32a6aedeb4f0d33bc (diff)
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/IcqOscarJ/icq_filetransfer.cpp')
-rw-r--r--protocols/IcqOscarJ/icq_filetransfer.cpp522
1 files changed, 522 insertions, 0 deletions
diff --git a/protocols/IcqOscarJ/icq_filetransfer.cpp b/protocols/IcqOscarJ/icq_filetransfer.cpp
new file mode 100644
index 0000000000..ed81ad4c5a
--- /dev/null
+++ b/protocols/IcqOscarJ/icq_filetransfer.cpp
@@ -0,0 +1,522 @@
+// ---------------------------------------------------------------------------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.
+//
+// -----------------------------------------------------------------------------
+//
+// File name : $URL: http://miranda.googlecode.com/svn/trunk/miranda/protocols/IcqOscarJ/icq_filetransfer.cpp $
+// Revision : $Revision: 12331 $
+// Last change on : $Date: 2010-08-18 16:59:50 +0300 (Ср, 18 авг 2010) $
+// Last change by : $Author: george.hazan $
+//
+// 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;
+ }
+}