diff options
author | watcherhd <watcherhd@e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb> | 2011-04-21 14:14:52 +0000 |
---|---|---|
committer | watcherhd <watcherhd@e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb> | 2011-04-21 14:14:52 +0000 |
commit | cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c (patch) | |
tree | 30df260fdc5a1b5a7049c2f8cac8b7ef17513d6d /icqj_s7_sss_mod/oscar_filetransfer.c | |
parent | 19b6f534d2e784a1e120bf52c4aa07004798f473 (diff) |
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
Diffstat (limited to 'icqj_s7_sss_mod/oscar_filetransfer.c')
-rw-r--r-- | icqj_s7_sss_mod/oscar_filetransfer.c | 2519 |
1 files changed, 2519 insertions, 0 deletions
diff --git a/icqj_s7_sss_mod/oscar_filetransfer.c b/icqj_s7_sss_mod/oscar_filetransfer.c new file mode 100644 index 0000000..e7849f9 --- /dev/null +++ b/icqj_s7_sss_mod/oscar_filetransfer.c @@ -0,0 +1,2519 @@ +// ---------------------------------------------------------------------------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,2007 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: https://icqjplusmod.googlecode.com/svn/trunk/oscar_filetransfer.c $
+// Revision : $Revision: 50 $
+// Last change on : $Date: 2007-08-28 02:57:00 +0300 (Вт, 28 авг 2007) $
+// Last change by : $Author: sss123next $
+//
+// DESCRIPTION:
+//
+// Describe me here please...
+//
+// -----------------------------------------------------------------------------
+
+#include "icqoscar.h"
+
+
+typedef struct {
+ int type;
+ int incoming;
+ HANDLE hContact;
+ HANDLE hConnection;
+ DWORD dwRemoteIP;
+ oscar_filetransfer *ft;
+ oscar_listener *listener;
+} oscarthreadstartinfo;
+
+
+// small utility function
+extern void NormalizeBackslash(char* path);
+
+static void oft_newConnectionReceived(HANDLE hNewConnection, DWORD dwRemoteIP, void *pExtra);
+
+static DWORD __stdcall oft_connectionThread(oscarthreadstartinfo *otsi);
+static void sendOscarPacket(oscar_connection *oc, icq_packet *packet);
+static int oft_handlePackets(oscar_connection *oc, unsigned char *buf, int len);
+static int oft_handleFileData(oscar_connection *oc, unsigned char *buf, int len);
+static int oft_handleProxyData(oscar_connection *oc, unsigned char *buf, int len);
+static void handleOFT2FramePacket(oscar_connection *oc, WORD datatype, BYTE *pBuffer, WORD wLen);
+static void sendOFT2FramePacket(oscar_connection *oc, WORD datatype);
+
+static void oft_sendFileData(oscar_connection *oc);
+static void oft_sendPeerInit(oscar_connection *oc);
+
+static void proxy_sendInitTunnel(oscar_connection *oc);
+static void proxy_sendJoinTunnel(oscar_connection *oc, WORD wPort);
+
+
+static CRITICAL_SECTION oftMutex;
+static int fileTransferCount = 0;
+static basic_filetransfer** fileTransferList = NULL;
+
+static oscar_filetransfer* CreateOscarTransfer();
+static void ReleaseFileTransfer(void *ft);
+static oscar_filetransfer* FindOscarTransfer(HANDLE hContact, DWORD dwID1, DWORD dwID2);
+
+
+//
+// Common functions
+/////////////////////////////
+
+char* FindFilePathContainer(const char** files, int iFile, char* szContainer)
+{
+ int i;
+ const char* szThisFile = files[iFile];
+ char* szFileName = ExtractFileName(szThisFile);
+
+ szContainer[0] = '\0';
+
+ if (szThisFile != szFileName)
+ { // find an earlier subdirectory to be used as a container
+ for (i = iFile - 1; i >= 0; i--)
+ {
+ int len = strlennull(files[i]);
+
+ if (!_strnicmp(files[i], szThisFile, len) && (szThisFile[len] == '\\' || szThisFile[len] == '/'))
+ {
+ char* pszLastBackslash;
+
+ if (((pszLastBackslash = strrchr(files[i], '\\')) == NULL) &&
+ ((pszLastBackslash = strrchr(files[i], '/')) == NULL))
+ {
+ strcpy(szContainer, files[i]);
+ }
+ else
+ {
+ len = pszLastBackslash - files[i] + 1;
+ strncpy(szContainer, szThisFile + len, szFileName - szThisFile - len);
+ szContainer[szFileName - szThisFile - len] = '\0';
+ }
+ }
+ }
+ }
+ return szFileName;
+}
+
+
+//
+// Utility functions
+/////////////////////////////
+
+
+static oscar_filetransfer* CreateOscarTransfer()
+{
+ oscar_filetransfer* ft = (oscar_filetransfer*)SAFE_MALLOC(sizeof(oscar_filetransfer));
+
+ ft->ft_magic = FT_MAGIC_OSCAR; // Setup signature
+ // Init members
+ ft->fileId = -1;
+
+ EnterCriticalSection(&oftMutex);
+
+ fileTransferList = (basic_filetransfer**)SAFE_REALLOC(fileTransferList, sizeof(basic_filetransfer*)*(fileTransferCount + 1));
+ fileTransferList[fileTransferCount++] = (basic_filetransfer*)ft;
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: FT struct 0x%x created", ft);
+#endif
+ LeaveCriticalSection(&oftMutex);
+
+ return ft;
+}
+
+
+
+filetransfer *CreateIcqFileTransfer()
+{
+ filetransfer *ft = (filetransfer*)SAFE_MALLOC(sizeof(filetransfer));
+
+ ft->ft_magic = FT_MAGIC_ICQ;
+
+ EnterCriticalSection(&oftMutex);
+
+ fileTransferList = (basic_filetransfer**)SAFE_REALLOC(fileTransferList, sizeof(basic_filetransfer*)*(fileTransferCount + 1));
+ fileTransferList[fileTransferCount++] = (basic_filetransfer*)ft;
+
+#ifdef _DEBUG
+ NetLog_Direct("FT struct 0x%x created", ft);
+#endif
+ LeaveCriticalSection(&oftMutex);
+
+ return ft;
+}
+
+
+
+static int getFileTransferIndex(void *ft)
+{
+ int i;
+
+ for (i = 0; i < fileTransferCount; i++)
+ {
+ if (fileTransferList[i] == ft)
+ return i;
+ }
+ return -1;
+}
+
+
+
+static void ReleaseFileTransfer(void *ft)
+{
+ int i = getFileTransferIndex(ft);
+
+ if (i != -1)
+ {
+ fileTransferCount--;
+ fileTransferList[i] = fileTransferList[fileTransferCount];
+ fileTransferList = (basic_filetransfer**)SAFE_REALLOC(fileTransferList, sizeof(basic_filetransfer*)*fileTransferCount);
+ }
+}
+
+
+
+int IsValidFileTransfer(void *ft)
+{
+ int res = 0;
+
+ EnterCriticalSection(&oftMutex);
+
+ if (getFileTransferIndex(ft) != -1) res = 1;
+
+ LeaveCriticalSection(&oftMutex);
+
+ return res;
+}
+
+
+
+int IsValidOscarTransfer(void *ft)
+{
+ int res = 0;
+
+ EnterCriticalSection(&oftMutex);
+
+ if (getFileTransferIndex(ft) != -1 && ((basic_filetransfer*)ft)->ft_magic == FT_MAGIC_OSCAR)
+ res = 1;
+
+ LeaveCriticalSection(&oftMutex);
+
+ return res;
+}
+
+
+
+static oscar_filetransfer* FindOscarTransfer(HANDLE hContact, DWORD dwID1, DWORD dwID2)
+{
+ int i;
+
+ EnterCriticalSection(&oftMutex);
+
+ for (i = 0; i < fileTransferCount; i++)
+ {
+ if (((basic_filetransfer*)fileTransferList[i])->ft_magic == FT_MAGIC_OSCAR)
+ {
+ oscar_filetransfer *oft = (oscar_filetransfer*)fileTransferList[i];
+
+ if (oft->hContact == hContact && oft->pMessage.dwMsgID1 == dwID1 && oft->pMessage.dwMsgID2 == dwID2)
+ {
+ LeaveCriticalSection(&oftMutex);
+
+ return oft;
+ }
+ }
+ }
+
+ LeaveCriticalSection(&oftMutex);
+
+ return NULL;
+}
+
+
+
+// Release file transfer structure
+void SafeReleaseFileTransfer(void **ft)
+{
+ basic_filetransfer **bft = (basic_filetransfer**)ft;
+
+ EnterCriticalSection(&oftMutex);
+
+ // Check for filetransfer validity
+ if (getFileTransferIndex(*ft) == -1)
+ {
+ LeaveCriticalSection(&oftMutex);
+ return;
+ }
+
+ if (*bft)
+ {
+ if ((*bft)->ft_magic == FT_MAGIC_ICQ)
+ { // release ICQ filetransfer structure and its contents
+ filetransfer *ift = (filetransfer*)(*bft);
+
+ SAFE_FREE(&ift->szFilename);
+ SAFE_FREE(&ift->szDescription);
+ SAFE_FREE(&ift->szSavePath);
+ SAFE_FREE(&ift->szThisFile);
+ SAFE_FREE(&ift->szThisSubdir);
+ if (ift->files)
+ {
+ int i;
+
+ for (i = 0; i < (int)ift->dwFileCount; i++)
+ SAFE_FREE(&ift->files[i]);
+ SAFE_FREE((char**)&ift->files);
+ }
+ // Invalidate transfer
+ ReleaseFileTransfer(ift);
+#ifdef _DEBUG
+ NetLog_Direct("FT struct 0x%x released", ft);
+#endif
+ // Release memory
+ SAFE_FREE(ft);
+ }
+ else if ((*bft)->ft_magic == FT_MAGIC_OSCAR)
+ { // release oscar filetransfer structure and its contents
+ oscar_filetransfer *oft = (oscar_filetransfer*)(*bft);
+ // If connected, close connection
+ if (oft->connection)
+ CloseOscarConnection(oft->connection);
+ // Release oscar listener
+ if (oft->listener)
+ ReleaseOscarListener((oscar_listener**)&oft->listener);
+ // Release cookie
+ if (oft->dwCookie)
+ FreeCookie(oft->dwCookie);
+ // Release all dynamic members
+ SAFE_FREE(&oft->rawFileName);
+ SAFE_FREE(&oft->szSavePath);
+ SAFE_FREE(&oft->szThisFile);
+ SAFE_FREE(&oft->szThisPath);
+ if (oft->files)
+ {
+ int i;
+
+ for (i = 0; i < oft->wFilesCount; i++)
+ SAFE_FREE(&oft->files[i].szFile);
+ SAFE_FREE((void**)&oft->files);
+ }
+ if (oft->files_ansi)
+ {
+ int i;
+
+ for (i = 0; i < oft->wFilesCount; i++)
+ SAFE_FREE(&oft->files_ansi[i]);
+ SAFE_FREE((void**)&oft->files_ansi);
+ }
+ if (oft->file_containers)
+ {
+ int i;
+
+ for (i = 0; i < oft->containerCount; i++)
+ SAFE_FREE(&oft->file_containers[i]);
+ SAFE_FREE((void**)&oft->file_containers);
+ }
+ if (oft->fileId != -1)
+ {
+#ifdef _DEBUG
+ NetLog_Direct("OFT: _close(%u)", oft->fileId);
+#endif
+ _close(oft->fileId);
+ }
+ // Invalidate transfer
+ ReleaseFileTransfer(oft);
+#ifdef _DEBUG
+ NetLog_Direct("OFT: FT struct 0x%x released", ft);
+#endif
+ // Release memory
+ SAFE_FREE(ft);
+ }
+ }
+ LeaveCriticalSection(&oftMutex);
+}
+
+
+
+
+// Calculate oft checksum of buffer
+// --------------------------------
+// Information was gathered from Gaim's sources, thanks
+//
+DWORD oft_calc_checksum(int offset, const BYTE *buffer, int len, DWORD dwChecksum)
+{
+ DWORD checksum;
+ int i;
+
+ checksum = (dwChecksum >> 16) & 0xffff;
+ for (i = 0; i < len; i++)
+ {
+ WORD val = buffer[i];
+ DWORD oldchecksum = checksum;
+
+ if (((i + offset) & 1) == 0)
+ val = val << 8;
+
+ if (checksum < val)
+ checksum -= val + 1;
+ else // simulate carry
+ checksum -= val;
+ }
+ checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
+ checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
+ return checksum << 16;
+}
+
+
+
+DWORD oft_calc_file_checksum(int hFile, __int64 maxSize)
+{
+ BYTE buf[OFT_BUFFER_SIZE];
+ int bytesRead;
+ __int64 offset = 0;
+ DWORD dwCheck = 0xFFFF0000;
+
+ _lseek(hFile, 0, SEEK_SET);
+ bytesRead = _read(hFile, buf, sizeof(buf));
+ if (bytesRead == -1)
+ return dwCheck;
+
+ while(bytesRead)
+ {
+ dwCheck = oft_calc_checksum((int)offset, buf, bytesRead, dwCheck);
+ offset += bytesRead;
+ bytesRead = _read(hFile, buf, sizeof(buf));
+ if (bytesRead + offset > maxSize) bytesRead = (int)(maxSize - offset);
+ }
+ _lseek(hFile, 0, SEEK_SET); // back to beginning
+
+ return dwCheck;
+}
+
+
+
+oscar_listener* CreateOscarListener(oscar_filetransfer *ft, NETLIBNEWCONNECTIONPROC_V2 handler)
+{
+ oscar_listener *listener = (oscar_listener*)SAFE_MALLOC(sizeof(oscar_listener));
+
+ listener->ft = ft;
+ if (listener->hBoundPort = NetLib_BindPort(handler, listener, &listener->wPort, NULL))
+ return listener; // Success
+
+ SAFE_FREE(&listener);
+
+ return NULL; // Failure
+}
+
+
+
+void ReleaseOscarListener(oscar_listener **pListener)
+{
+ oscar_listener *listener = *pListener;
+
+ if (listener)
+ { // Close listening port
+ if (listener->hBoundPort)
+ NetLib_SafeCloseHandle(&listener->hBoundPort);
+
+ NetLog_Direct("Oscar listener on port %d released.", listener->wPort);
+ }
+ SAFE_FREE(pListener);
+}
+
+
+//
+// Miranda FT interface handlers & services
+/////////////////////////////
+
+void InitOscarFileTransfer()
+{
+ InitializeCriticalSection(&oftMutex);
+}
+
+
+
+void UninitOscarFileTransfer()
+{
+ DeleteCriticalSection(&oftMutex);
+}
+
+
+
+void handleRecvServMsgOFT(unsigned char *buf, WORD wLen, DWORD dwUin, char *szUID, DWORD dwID1, DWORD dwID2, WORD wCommand)
+{
+ HANDLE hContact = HContactFromUID(dwUin, szUID, NULL);
+
+ if (wCommand == 0)
+ { // this is OFT request
+ oscar_tlv_chain* chain = readIntoTLVChain(&buf, wLen, 0);
+
+ if (chain)
+ {
+ WORD wAckType = getWordFromChain(chain, 0x0A, 1);
+
+ if (wAckType == 1)
+ { // This is first request in this OFT
+ oscar_filetransfer *ft = CreateOscarTransfer();
+ char* pszFileName = NULL;
+ char* pszDescription = NULL;
+ WORD wFilenameLength;
+
+ NetLog_Server("This is a file request");
+
+ // This TLV chain may contain the following TLVs:
+ // TLV(A): Acktype 0x0001 - file request / abort request
+ // 0x0002 - file ack
+ // TLV(F): Unknown
+ // TLV(E): Language ?
+ // TLV(2): Proxy IP
+ // TLV(16): Proxy IP Check
+ // TLV(3): External IP
+ // TLV(4): Internal IP
+ // TLV(5): Port
+ // TLV(17): Port Check
+ // TLV(10): Proxy Flag
+ // TLV(D): Charset of User Message
+ // TLV(C): User Message (ICQ_COOL_FT)
+ // TLV(2711): FT info
+ // TLV(2712): Charset of file name
+
+ // init filetransfer structure
+ ft->pMessage.dwMsgID1 = dwID1;
+ ft->pMessage.dwMsgID2 = dwID2;
+ ft->bUseProxy = getTLV(chain, 0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = getDWordFromChain(chain, 0x02, 1);
+ ft->dwRemoteInternalIP = getDWordFromChain(chain, 0x03, 1);
+ ft->dwRemoteExternalIP = getDWordFromChain(chain, 0x04, 1);
+ ft->wRemotePort = getWordFromChain(chain, 0x05, 1);
+ ft->wReqNum = wAckType;
+
+ { // User Message
+ oscar_tlv* tlv = getTLV(chain, 0x0C, 1);
+
+ if (tlv)
+ { // parse User Message
+ BYTE* tBuf = tlv->pData;
+
+ pszDescription = (char*)_alloca(tlv->wLen + 2);
+ unpackString(&tBuf, pszDescription, tlv->wLen);
+ pszDescription[tlv->wLen] = '\0';
+ pszDescription[tlv->wLen+1] = '\0';
+ { // apply User Message encoding
+ oscar_tlv *charset = getTLV(chain, 0x0D, 1);
+ char *str = pszDescription;
+ char *bTag,*eTag;
+
+ if (charset)
+ { // decode charset
+ char *szEnc = (char*)_alloca(charset->wLen + 1);
+
+ strncpy(szEnc, charset->pData, charset->wLen);
+ szEnc[charset->wLen] = '\0';
+ str = ApplyEncoding(pszDescription, szEnc);
+ }
+ else
+ str = null_strdup(str);
+ // eliminate HTML tags
+ pszDescription = EliminateHtml(str, strlennull(str));
+
+ bTag = strstr(pszDescription, "<DESC>");
+ if (bTag)
+ { // take special Description - ICQJ's extension
+ eTag = strstr(bTag, "</DESC>");
+ if (eTag)
+ {
+ *eTag = '\0';
+ str = null_strdup(bTag + 6);
+ SAFE_FREE(&pszDescription);
+ pszDescription = str;
+ }
+ }
+ else
+ {
+ bTag = strstr(pszDescription, "<FS>");
+ if (bTag)
+ { // take only <FS> - Description tag if present
+ eTag = strstr(bTag, "</FS>");
+ if (eTag)
+ {
+ *eTag = '\0';
+ str = null_strdup(bTag + 4);
+ SAFE_FREE(&pszDescription);
+ pszDescription = str;
+ }
+ }
+ }
+ }
+ }
+ if (!strlennull(pszDescription)) pszDescription = ICQTranslateUtf("No description given");
+ }
+ { // parse File Transfer Info block
+ oscar_tlv* tlv = getTLV(chain, 0x2711, 1);
+ BYTE* tBuf = tlv->pData;
+ WORD tLen = tlv->wLen;
+ WORD wFlag;
+
+ unpackWord(&tBuf, &wFlag); // FT flag
+ unpackWord(&tBuf, &ft->wFilesCount);
+ unpackDWord(&tBuf, (DWORD*)&ft->qwTotalSize);
+ tLen -= 8;
+ // Filename / Directory Name
+ wFilenameLength = tLen - 1;
+ pszFileName = (char*)_alloca(tLen);
+ unpackString(&tBuf, pszFileName, wFilenameLength);
+ pszFileName[wFilenameLength] = '\0';
+ { // apply Filename / Directory Name encoding
+ oscar_tlv* charset = getTLV(chain, 0x2712, 1);
+
+ if (charset)
+ {
+ char* szEnc = (char*)_alloca(charset->wLen + 1);
+
+ strncpy(szEnc, charset->pData, charset->wLen);
+ szEnc[charset->wLen] = '\0';
+ pszFileName = ApplyEncoding(pszFileName, szEnc);
+ }
+ else
+ pszFileName = ansi_to_utf8(pszFileName);
+ }
+ if (ft->wFilesCount == 1)
+ { // Filename - use for DB event (convert to Ansi - File DB events does not support Unicode)
+ char* szAnsi = (char*)_alloca(strlennull(pszFileName) + 2);
+ utf8_decode_static(pszFileName, szAnsi, strlennull(pszFileName) + 1);
+ SAFE_FREE(&pszFileName);
+ pszFileName = szAnsi;
+ }
+ else
+ { // Save Directory name for future use
+ ft->szThisPath = pszFileName;
+ // for multi-file transfer we do not display "folder" name, but create only a simple notice
+ pszFileName = (char*)_alloca(64);
+
+ null_snprintf(pszFileName, 64, ICQTranslate("%d Files"), ft->wFilesCount);
+ }
+ }
+ { // Total Size TLV (ICQ 6 and AIM 6)
+ oscar_tlv *tlv = getTLV(chain, 0x2713, 1);
+
+ if (tlv && tlv->wLen >= 8)
+ {
+ BYTE *tBuf = tlv->pData;
+
+ unpackQWord(&tBuf, &ft->qwTotalSize);
+ }
+ }
+ {
+ CCSDATA ccs;
+ PROTORECVEVENT pre;
+ char* szBlob;
+ int bAdded;
+ HANDLE hContact = HContactFromUID(dwUin, szUID, &bAdded);
+ char* szAnsi;
+
+ ft->hContact = hContact;
+ ft->szDescription = pszDescription;
+ ft->fileId = -1;
+
+ szAnsi = (char*)_alloca(strlennull(pszDescription)+2);
+ utf8_decode_static(pszDescription, szAnsi, strlennull(pszDescription)+1);
+
+ // Send chain event
+ szBlob = (char*)_alloca(sizeof(DWORD) + strlennull(pszFileName) + strlennull(szAnsi) + 2);
+ *(PDWORD)szBlob = (DWORD)ft;
+ strcpy(szBlob + sizeof(DWORD), pszFileName);
+ strcpy(szBlob + sizeof(DWORD) + strlennull(pszFileName) + 1, szAnsi); // DB event is ansi only!
+ ccs.szProtoService = PSR_FILE;
+ ccs.hContact = hContact;
+ ccs.wParam = 0;
+ ccs.lParam = (LPARAM)⪯
+ pre.flags = 0;
+ pre.timestamp = time(NULL);
+ pre.szMessage = szBlob;
+ pre.lParam = 0;
+
+ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs);
+ }
+ }
+ else if (wAckType == 2)
+ { // First attempt failed, reverse requested
+ oscar_filetransfer *ft = FindOscarTransfer(hContact, dwID1, dwID2);
+
+ if (ft)
+ {
+ NetLog_Direct("OFT: Redirect received (%d)", wAckType);
+
+ ft->wReqNum = wAckType;
+
+ if (ft->sending)
+ {
+ ReleaseOscarListener((oscar_listener**)&ft->listener);
+
+ ft->bUseProxy = getTLV(chain, 0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = getDWordFromChain(chain, 0x02, 1);
+ ft->dwRemoteInternalIP = getDWordFromChain(chain, 0x03, 1);
+ ft->dwRemoteExternalIP = getDWordFromChain(chain, 0x04, 1);
+ ft->wRemotePort = getWordFromChain(chain, 0x05, 1);
+
+ OpenOscarConnection(hContact, ft, ft->bUseProxy ? OCT_PROXY_RECV: OCT_REVERSE);
+ }
+ else
+ { // Just sanity
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&ft);
+ }
+ }
+ else
+ NetLog_Server("Error: Invalid request, no such transfer");
+ }
+ else if (wAckType == 3)
+ { // Transfering thru proxy, join tunnel
+ oscar_filetransfer *ft = FindOscarTransfer(hContact, dwID1, dwID2);
+
+ if (ft)
+ { // release possible previous listener
+ NetLog_Direct("OFT: Redirect received (%d)", wAckType);
+
+ ft->wReqNum = wAckType;
+
+ ReleaseOscarListener((oscar_listener**)&ft->listener);
+
+ ft->bUseProxy = getTLV(chain, 0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = getDWordFromChain(chain, 0x02, 1);
+ ft->wRemotePort = getWordFromChain(chain, 0x05, 1);
+
+ if (ft->bUseProxy && ft->dwProxyIP)
+ { // Init proxy connection
+ OpenOscarConnection(hContact, ft, OCT_PROXY_RECV);
+ }
+ else
+ { // try Stage 4
+ OpenOscarConnection(hContact, ft, OCT_PROXY);
+ }
+ }
+ else
+ NetLog_Server("Error: Invalid request, no such transfer");
+ }
+ else if (wAckType == 4)
+ {
+ oscar_filetransfer *ft = FindOscarTransfer(hContact, dwID1, dwID2);
+
+ if (ft)
+ {
+ NetLog_Direct("OFT: Redirect received (%d)", wAckType);
+
+ ft->wReqNum = wAckType;
+ ft->bUseProxy = getTLV(chain, 0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = getDWordFromChain(chain, 0x02, 1);
+ ft->wRemotePort = getWordFromChain(chain, 0x05, 1);
+
+ if (ft->bUseProxy && ft->dwProxyIP)
+ { // Init proxy connection
+ OpenOscarConnection(hContact, ft, OCT_PROXY_RECV);
+ }
+ else
+ NetLog_Server("Error: Invalid request, IP missing.");
+ }
+ else
+ NetLog_Server("Error: Invalid request, no such transfer");
+ }
+ else
+ NetLog_Server("Error: Uknown Stage %d request", wAckType);
+
+ disposeChain(&chain);
+ }
+ else
+ NetLog_Server("Error: Missing TLV chain in OFT request");
+ }
+ else if (wCommand == 1)
+ { // transfer cancelled/aborted
+ oscar_filetransfer *ft = FindOscarTransfer(hContact, dwID1, dwID2);
+
+ if (ft)
+ {
+ NetLog_Server("OFT: File transfer cancelled by %s", strUID(dwUin, szUID));
+
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Notify user, that the FT was cancelled // TODO: new ACKRESULT_?
+ icq_LogMessage(LOG_ERROR, "The file transfer was aborted by the other user.");
+ // Release transfer
+ SafeReleaseFileTransfer(&ft);
+ }
+ else
+ NetLog_Server("Error: Invalid request, no such transfer");
+ }
+ else if (wCommand == 2)
+ { // transfer accepted - connection established
+ oscar_filetransfer *ft = FindOscarTransfer(hContact, dwID1, dwID2);
+
+ if (ft)
+ {
+ NetLog_Direct("OFT: Session established.");
+ // Init connection
+ if (ft->sending)
+ {
+ if (ft->connection && ((oscar_connection*)(ft->connection))->status == OCS_CONNECTED)
+ oft_sendPeerInit((oscar_connection*)ft->connection);
+ else
+ ft->initialized = 1; // accept was received
+ }
+ else
+ NetLog_Server("Warning: Received invalid rendezvous accept");
+ }
+ else
+ NetLog_Server("Error: Invalid request, no such transfer");
+ }
+ else
+ {
+ NetLog_Server("Error: Unknown wCommand=0x%x in OFT request", wCommand);
+ }
+}
+
+
+
+void handleRecvServResponseOFT(unsigned char *buf, WORD wLen, DWORD dwUin, char *szUID, void* ft)
+{
+ WORD wDataLen;
+
+ if (wLen < 2) return;
+
+ unpackWord(&buf, &wDataLen);
+
+ if (wDataLen == 2)
+ {
+ oscar_filetransfer *oft = (oscar_filetransfer*)ft;
+ WORD wStatus;
+
+ unpackWord(&buf, &wStatus);
+
+ switch (wStatus)
+ {
+ case 1:
+ { // FT denied (icq5)
+ NetLog_Server("OFT: File transfer denied by %s", strUID(dwUin, szUID));
+
+ ICQBroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oft);
+ }
+ break;
+
+ case 4: // Proxy error
+ {
+ icq_LogMessage(LOG_ERROR, "The file transfer failed: Proxy error");
+
+ ICQBroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oft);
+ }
+ break;
+
+ case 5: // Invalid request
+ {
+ icq_LogMessage(LOG_ERROR, "The file transfer failed: Invalid request");
+
+ ICQBroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oft);
+ }
+ break;
+
+ case 6: // Proxy Failed (IP = 0)
+ {
+ icq_LogMessage(LOG_ERROR, "The file transfer failed: Proxy unavailable");
+
+ ICQBroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oft);
+ }
+ break;
+
+ default:
+ {
+ NetLog_Server("OFT: Uknown request response code 0x%x", wStatus);
+
+ ICQBroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oft);
+ }
+ }
+ }
+}
+
+
+
+static char* oftGetFileContainer(oscar_filetransfer* oft, const char** files, int iFile)
+{
+ char szPath[MAX_PATH];
+ char* szFileName = FindFilePathContainer(files, iFile, szPath);
+ char* szPathUtf = ansi_to_utf8(szPath);
+ int i;
+
+ // try to find existing container
+ for (i = 0; i < oft->containerCount; i++)
+ if (!strcmp(szPathUtf, oft->file_containers[i]))
+ {
+ SAFE_FREE(&szPathUtf);
+
+ return oft->file_containers[i];
+ }
+
+ // create new container
+ i = oft->containerCount++;
+ oft->file_containers = (char**)SAFE_REALLOC(oft->file_containers, (sizeof(char*) * oft->containerCount));
+ oft->file_containers[i] = szPathUtf;
+
+ return oft->file_containers[i];
+}
+
+
+
+int oftInitTransfer(HANDLE hContact, DWORD dwUin, char* szUid, char** files, char* pszDesc)
+{
+ oscar_filetransfer *ft;
+ int i, filesCount;
+ struct _stati64 statbuf;
+
+ // Initialize filetransfer struct
+ NetLog_Server("Init file send");
+
+ ft = CreateOscarTransfer();
+ ft->hContact = hContact;
+ ft->pMessage.bMessageType = MTYPE_FILEREQ;
+ InitMessageCookie(&ft->pMessage);
+
+ for (filesCount = 0; files[filesCount]; filesCount++);
+ ft->files = (oft_file_record *)SAFE_MALLOC(sizeof(oft_file_record) * filesCount);
+ ft->files_ansi = (char **)SAFE_MALLOC(sizeof(char *) * filesCount);
+ ft->qwTotalSize = 0;
+ // Prepare files arrays
+ for (i = 0; i < filesCount; i++)
+ {
+ if (_stati64(files[i], &statbuf))
+ NetLog_Server("IcqSendFile() was passed invalid filename \"%s\"", files[i]);
+ else
+ {
+ if (!(statbuf.st_mode&_S_IFDIR))
+ { // take only files
+ ft->files[ft->wFilesCount].szFile = FileNameToUtf(files[i]);
+ ft->files[ft->wFilesCount].szContainer = oftGetFileContainer(ft, files, i);
+ ft->files_ansi[ft->wFilesCount] = null_strdup(files[i]);
+
+ ft->wFilesCount++;
+ ft->qwTotalSize += statbuf.st_size;
+ }
+ }
+ }
+ if (!ft->wFilesCount)
+ { // found no valid files to send
+ icq_LogMessage(LOG_ERROR, LPGEN("Failed to Initialize File Transfer. No valid files were specified."));
+ // Notify UI
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&ft);
+
+ return 0; // Failure
+ }
+ if (ft->qwTotalSize >= 0x100000000 && ft->wFilesCount > 1)
+ { // file larger than 4GB can be send only as single
+ icq_LogMessage(LOG_ERROR, "The files are too big to be sent at once. Files bigger than 4GB can be sent only separately.");
+ // Notify UI
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&ft);
+
+ return 0; // Failure
+ }
+
+ NetLog_Server("OFT: Found %d files.", ft->wFilesCount);
+
+ ft->szDescription = ansi_to_utf8(pszDesc);
+ ft->sending = 1;
+ ft->fileId = -1;
+ ft->iCurrentFile = 0;
+ ft->dwCookie = AllocateCookie(CKT_FILE, ICQ_MSG_SRV_SEND, hContact, ft);
+
+ // Init oscar fields
+ {
+ ft->wEncrypt = 0;
+ ft->wCompress = 0;
+ ft->wPartsCount = 1;
+ ft->wPartsLeft = 1;
+ strcpy(ft->rawIDString, "Cool FileXfer");
+ ft->bHeaderFlags = 0x20;
+ ft->bNameOff = 0x1C;
+ ft->bSizeOff = 0x11;
+ ft->dwRecvForkCheck = 0xFFFF0000;
+ ft->dwThisForkCheck = 0xFFFF0000;
+ ft->dwRecvFileCheck = 0xFFFF0000;
+ }
+
+ // Send file transfer request
+ {
+ char* pszFiles;
+
+ if (ft->wFilesCount == 1)
+ { // transfering single file, give filename
+ pszFiles = ExtractFileName(ft->files[0].szFile);
+ }
+ else
+ { // check if transfering one directory
+ char *szFirstDiv, *szFirstDir = ft->file_containers[0];
+ int nFirstDirLen;
+
+ // default is no root dir
+ pszFiles = "";
+
+ if ((szFirstDiv = strstr(szFirstDir, "\\")) || (szFirstDiv = strstr(szFirstDir, "/")))
+ nFirstDirLen = szFirstDiv - szFirstDir;
+ else
+ nFirstDirLen = strlennull(szFirstDir);
+
+ if (nFirstDirLen)
+ { // got root dir from first container, check if others are only sub-dirs
+ for (i = 0; i < ft->containerCount; i++)
+ {
+ if (strnicmp(ft->file_containers[i], szFirstDir, nFirstDirLen))
+ {
+ szFirstDir = NULL;
+ break;
+ }
+ }
+ if (szFirstDir)
+ { // fine, we are sending only one directory
+ pszFiles = szFirstDir;
+ if (szFirstDiv) szFirstDiv[0] = '\0';
+ nFirstDirLen++; // include backslash
+ // cut all files container by root dir - it is transferred as root separately
+ for (i = 0; i < ft->wFilesCount; i++)
+ ft->files[i].szContainer += nFirstDirLen;
+ }
+ }
+ }
+
+ // Create listener
+ ft->listener = CreateOscarListener(ft, oft_newConnectionReceived);
+
+ // Send packet
+ if (ft->listener)
+ {
+ oft_sendFileRequest(dwUin, szUid, ft, pszFiles, ICQGetContactSettingDword(NULL, "RealIP", 0));
+ }
+ else
+ { // try stage 1 proxy
+ ft->szThisFile = null_strdup(pszFiles);
+ OpenOscarConnection(hContact, ft, OCT_PROXY_INIT);
+ }
+ }
+
+ return (int)(HANDLE)ft; // Success
+}
+
+
+
+DWORD oftFileAllow(HANDLE hContact, WPARAM wParam, LPARAM lParam)
+{
+ oscar_filetransfer* ft = (oscar_filetransfer*)wParam;
+ DWORD dwUin;
+ uid_str szUid;
+
+ if (ICQGetContactSettingUID(hContact, &dwUin, &szUid))
+ return 0; // Invalid contact
+
+ ft->szSavePath = ansi_to_utf8((char *)lParam);
+ if (ft->szThisPath)
+ { // Append Directory name to the save path, when transfering a directory
+ ft->szSavePath = (char*)SAFE_REALLOC(ft->szSavePath, strlennull(ft->szSavePath) + strlennull(ft->szThisPath) + 4);
+ NormalizeBackslash(ft->szSavePath);
+ strcat(ft->szSavePath, ft->szThisPath);
+ NormalizeBackslash(ft->szSavePath);
+ }
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Request accepted, saving to '%s'.", ft->szSavePath);
+#endif
+
+ // Create cookie
+ ft->dwCookie = AllocateCookie(CKT_FILE, ICQ_MSG_SRV_SEND, hContact, ft);
+
+ OpenOscarConnection(hContact, ft, ft->bUseProxy ? OCT_PROXY_RECV: OCT_NORMAL);
+
+ return wParam; // Success
+}
+
+
+
+DWORD oftFileDeny(HANDLE hContact, WPARAM wParam, LPARAM lParam)
+{
+ oscar_filetransfer* ft = (oscar_filetransfer*)wParam;
+ DWORD dwUin;
+ uid_str szUid;
+
+ if (IsValidOscarTransfer(ft))
+ {
+ if (ICQGetContactSettingUID(hContact, &dwUin, &szUid))
+ return 1; // Invalid contact
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Request denied.");
+#endif
+
+ oft_sendFileDeny(dwUin, szUid, ft);
+
+ // Release structure
+ SafeReleaseFileTransfer(&ft);
+
+ return 0; // Success
+ }
+ return 1; // Invalid transfer
+}
+
+
+
+DWORD oftFileCancel(HANDLE hContact, WPARAM wParam, LPARAM lParam)
+{
+ oscar_filetransfer* ft = (oscar_filetransfer*)wParam;
+ DWORD dwUin;
+ uid_str szUid;
+
+ if (IsValidOscarTransfer(ft))
+ {
+ if (ft->hContact != hContact)
+ return 1; // Bad contact or hTransfer
+
+ if (ICQGetContactSettingUID(hContact, &dwUin, &szUid))
+ return 1; // Invalid contact
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Transfer cancelled.");
+#endif
+
+ oft_sendFileCancel(dwUin, szUid, ft);
+
+ ICQBroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+
+ // Release structure
+ SafeReleaseFileTransfer(&ft);
+
+ return 0; // Success
+ }
+ return 1; // Invalid transfer
+}
+
+
+
+void oftFileResume(oscar_filetransfer *ft, int action, const char *szFilename)
+{
+ oscar_connection *oc;
+ int openFlags;
+
+ if (ft->connection == NULL)
+ return;
+
+ oc = (oscar_connection*)ft->connection;
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Resume Transfer, Action: %d, FileName: '%s'", action, szFilename);
+#endif
+
+ switch (action)
+ {
+ case FILERESUME_RESUME:
+ openFlags = _O_BINARY | _O_RDWR;
+ break;
+
+ case FILERESUME_OVERWRITE:
+ openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY;
+ ft->qwFileBytesDone = 0;
+ break;
+
+ case FILERESUME_SKIP:
+ openFlags = _O_BINARY | _O_WRONLY;
+ ft->qwFileBytesDone = ft->qwThisFileSize;
+ break;
+
+ case FILERESUME_RENAME:
+ openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY;
+ SAFE_FREE(&ft->szThisFile);
+ ft->szThisFile = ansi_to_utf8(szFilename);
+ ft->qwFileBytesDone = 0;
+ break;
+
+ default: // workaround for bug in Miranda Core
+ if (ft->resumeAction == FILERESUME_RESUME)
+ openFlags = _O_BINARY | _O_RDWR;
+ else
+ { // default to overwrite
+ openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY;
+ ft->qwFileBytesDone = 0;
+ }
+ }
+ ft->resumeAction = action;
+
+ ft->fileId = OpenFileUtf(ft->szThisFile, openFlags, _S_IREAD | _S_IWRITE);
+#ifdef _DEBUG
+ NetLog_Direct("OFT: OpenFileUtf(%s, %u) returned %u", ft->szThisFile, openFlags, ft->fileId);
+#endif
+ if (ft->fileId == -1)
+ {
+#ifdef _DEBUG
+ NetLog_Direct("OFT: errno=%d", errno);
+#endif
+ 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.");
+
+ ICQBroadcastAck(oc->ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc->ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oc->ft);
+ return;
+ }
+
+ if (action == FILERESUME_RESUME)
+ ft->qwFileBytesDone = _lseeki64(ft->fileId, 0, SEEK_END);
+ else
+ _lseeki64(ft->fileId, ft->qwFileBytesDone, SEEK_SET);
+
+ ft->qwBytesDone += ft->qwFileBytesDone;
+
+ if (action == FILERESUME_RESUME)
+ { // use smart-resume
+ oc->status = OCS_RESUME;
+ ft->dwRecvFileCheck = oft_calc_file_checksum(ft->fileId, ft->qwFileBytesDone);
+ _lseek(ft->fileId, 0, SEEK_END);
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Starting Smart-Resume");
+#endif
+
+ sendOFT2FramePacket(oc, OFT_TYPE_RESUMEREQUEST);
+
+ return;
+ }
+ else if (action == FILERESUME_SKIP)
+ { // we are skiping the file, send "we are done"
+ oc->status = OCS_NEGOTIATION;
+ }
+ else
+ { // Send "we are ready"
+ oc->status = OCS_DATA;
+
+ sendOFT2FramePacket(oc, OFT_TYPE_READY);
+ }
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0);
+
+ if (!ft->qwThisFileSize || action == FILERESUME_SKIP)
+ { // if the file is empty we will not receive any data
+ BYTE buf;
+ oft_handleFileData(oc, &buf, 0);
+ }
+}
+
+
+
+static void oft_buildProtoFileTransferStatus(oscar_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_ansi;
+ else
+ pfts->files = NULL; /* FIXME */
+ pfts->totalFiles = ft->wFilesCount;
+ pfts->currentFileNumber = ft->iCurrentFile;
+ pfts->totalBytes = (DWORD)ft->qwTotalSize; // FIXME
+ pfts->totalProgress = (DWORD)ft->qwBytesDone; // FIXME
+// utf8_decode(ft->szPath, &pfts->workingDir); // not used by the UI anyway
+ utf8_decode(ft->szThisFile, &pfts->currentFile);
+ pfts->currentFileSize = (DWORD)ft->qwThisFileSize; // FIXME
+ pfts->currentFileTime = ft->dwThisFileDate;
+ pfts->currentFileProgress = (DWORD)ft->qwFileBytesDone; // FIXME
+}
+
+
+
+void CloseOscarConnection(oscar_connection *oc)
+{
+ EnterCriticalSection(&oftMutex);
+
+ if (oc)
+ {
+ oc->type = OCT_CLOSING;
+
+ if (oc->hConnection)
+ { // we need this for Netlib handle consistency
+ NetLib_CloseConnection(&oc->hConnection, FALSE);
+ }
+ }
+ LeaveCriticalSection(&oftMutex);
+}
+
+
+
+void OpenOscarConnection(HANDLE hContact, oscar_filetransfer *ft, int type)
+{
+ oscarthreadstartinfo *otsi = (oscarthreadstartinfo*)SAFE_MALLOC(sizeof(oscarthreadstartinfo));
+
+ otsi->hContact = hContact;
+ otsi->type = type;
+ otsi->ft = ft;
+
+ ICQCreateThread(oft_connectionThread, otsi);
+}
+
+
+
+static int CreateOscarProxyConnection(oscar_connection *oc)
+{
+ NETLIBOPENCONNECTION nloc = {0};
+
+ // inform UI
+ ICQBroadcastAck(oc->ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTPROXY, oc->ft, 0);
+
+ nloc.szHost = OSCAR_PROXY_HOST;
+ nloc.wPort = ICQGetContactSettingWord(NULL, "OscarPort", DEFAULT_SERVER_PORT);
+ if (nloc.wPort == 0)
+ nloc.wPort = RandRange(1024, 65535);
+
+ oc->hConnection = NetLib_OpenConnection(ghServerNetlibUser, "Proxy ", &nloc);
+ if (!oc->hConnection)
+ { // proxy connection failed
+ return 0;
+ }
+ oc->type = OCT_PROXY;
+ oc->status = OCS_PROXY;
+ oc->ft->connection = oc;
+ // init proxy
+ proxy_sendInitTunnel(oc);
+
+ return 1; // Success
+}
+
+
+
+// This function is called from the Netlib when someone is connecting to our oscar_listener
+static void oft_newConnectionReceived(HANDLE hNewConnection, DWORD dwRemoteIP, void *pExtra)
+{
+ oscarthreadstartinfo *otsi = (oscarthreadstartinfo*)SAFE_MALLOC(sizeof(oscarthreadstartinfo));
+ oscar_listener* listener = (oscar_listener*)pExtra;
+
+ otsi->type = listener->ft->sending ? OCT_NORMAL : OCT_REVERSE;
+ otsi->incoming = 1;
+ otsi->hConnection = hNewConnection;
+ otsi->dwRemoteIP = dwRemoteIP;
+ otsi->listener = listener;
+
+ // Start a new thread for the incomming connection
+ ICQCreateThread(oft_connectionThread, otsi);
+}
+
+
+
+static DWORD __stdcall oft_connectionThread(oscarthreadstartinfo *otsi)
+{
+ oscar_connection oc = {0};
+ oscar_listener *source;
+ NETLIBPACKETRECVER packetRecv={0};
+ HANDLE hPacketRecver;
+
+ oc.hContact = otsi->hContact;
+ oc.hConnection = otsi->hConnection;
+ oc.type = otsi->type;
+ oc.incoming = otsi->incoming;
+ oc.ft = otsi->ft;
+ source = otsi->listener;
+ if (oc.incoming)
+ {
+ if (IsValidOscarTransfer(source->ft))
+ {
+ oc.ft = source->ft;
+ oc.ft->dwRemoteExternalIP = otsi->dwRemoteIP;
+ oc.hContact = oc.ft->hContact;
+ oc.ft->connection = &oc;
+ oc.status = OCS_CONNECTED;
+
+ ReleaseOscarListener((oscar_listener**)&oc.ft->listener);
+ }
+ else
+ { // FT is already over, kill listener
+ NetLog_Direct("Received unexpected connection, closing.");
+
+ CloseOscarConnection(&oc);
+ ReleaseOscarListener(&source);
+
+ SAFE_FREE(&otsi);
+
+ return 0;
+ }
+ }
+ SAFE_FREE(&otsi);
+
+ if (oc.hContact)
+ { // Load contact information
+ ICQGetContactSettingUID(oc.hContact, &oc.dwUin, &oc.szUid);
+ }
+
+ // Load local IP information
+ oc.dwLocalExternalIP = ICQGetContactSettingDword(NULL, "IP", 0);
+ oc.dwLocalInternalIP = ICQGetContactSettingDword(NULL, "RealIP", 0);
+
+ if (!oc.incoming)
+ { // create outgoing connection
+ if (oc.type == OCT_NORMAL || oc.type == OCT_REVERSE)
+ { // create outgoing connection to peer
+ NETLIBOPENCONNECTION nloc = {0};
+ IN_ADDR addr = {0}, addr2 = {0};
+
+ if (oc.ft->dwRemoteExternalIP == oc.dwLocalExternalIP && oc.ft->dwRemoteInternalIP)
+ addr.S_un.S_addr = htonl(oc.ft->dwRemoteInternalIP);
+ else
+ {
+ addr.S_un.S_addr = htonl(oc.ft->dwRemoteExternalIP);
+ // for different internal, try it also (for LANs with multiple external IP, VPNs, etc.)
+ if (oc.ft->dwRemoteInternalIP != oc.ft->dwRemoteExternalIP)
+ addr2.S_un.S_addr = htonl(oc.ft->dwRemoteInternalIP);
+ }
+
+ // Inform UI that we will attempt to connect
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTING, oc.ft, 0);
+
+ if (!addr.S_un.S_addr && oc.type == OCT_NORMAL)
+ { // IP to connect to is empty, request reverse
+ oscar_listener* listener = CreateOscarListener(oc.ft, oft_newConnectionReceived);
+
+ if (listener)
+ { // we got listening port, fine send request
+ oc.ft->listener = listener;
+ // notify UI
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_LISTENING, oc.ft, 0);
+
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, oc.dwLocalInternalIP, listener->wPort, FALSE);
+
+ return 0;
+ }
+ if (!CreateOscarProxyConnection(&oc))
+ { // normal connection failed, notify peer, wait for error or stage 3 proxy
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, 0, 0, FALSE);
+ // stage 3 can follow
+ return 0;
+ }
+ }
+ else if (addr.S_un.S_addr && oc.ft->wRemotePort)
+ {
+ nloc.szHost = inet_ntoa(addr);
+ nloc.wPort = oc.ft->wRemotePort;
+ nloc.timeout = 8; // 8 secs to connect
+ oc.hConnection = NetLib_OpenConnection(ghDirectNetlibUser, oc.type==OCT_REVERSE?"Reverse ":NULL, &nloc);
+ if (!oc.hConnection && addr2.S_un.S_addr)
+ { // first address failed, try second one if available
+ nloc.szHost = inet_ntoa(addr2);
+ oc.hConnection = NetLib_OpenConnection(ghDirectNetlibUser, oc.type==OCT_REVERSE?"Reverse ":NULL, &nloc);
+ }
+ if (!oc.hConnection)
+ {
+ if (oc.type == OCT_NORMAL)
+ { // connection failed, try reverse
+ oscar_listener* listener = CreateOscarListener(oc.ft, oft_newConnectionReceived);
+
+ if (listener)
+ { // we got listening port, fine send request
+ oc.ft->listener = listener;
+ // notify UI that we await connection
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_LISTENING, oc.ft, 0);
+
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, oc.dwLocalInternalIP, listener->wPort, FALSE);
+
+ return 0;
+ }
+ }
+ if (!CreateOscarProxyConnection(&oc))
+ { // proxy connection failed, notify peer, wait for error or stage 4 proxy
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, 0, 0, FALSE);
+ // stage 3 or stage 4 can follow
+ return 0;
+ }
+ }
+ else
+ {
+ oc.status = OCS_CONNECTED;
+ // ack normal connection
+ oc.ft->connection = &oc;
+ // acknowledge OFT - connection is ready
+ oft_sendFileAccept(oc.dwUin, oc.szUid, oc.ft);
+ // signal UI
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTED, oc.ft, 0);
+ }
+ }
+ else
+ { // try proxy, stage 3 (sending)
+ if (!CreateOscarProxyConnection(&oc))
+ { // proxy connection failed, notify peer, wait for error or stage 4 proxy
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, 0, 0, FALSE);
+ // stage 4 can follow
+ return 0;
+ }
+ }
+ }
+ else if (oc.type == OCT_PROXY_RECV)
+ { // stage 2 & stage 4
+ if (oc.ft->dwProxyIP && oc.ft->wRemotePort)
+ { // create proxy connection, join tunnel
+ NETLIBOPENCONNECTION nloc = {0};
+ IN_ADDR addr = {0};
+
+ // inform UI that we will connect to file proxy
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTPROXY, oc.ft, 0);
+
+ addr.S_un.S_addr = htonl(oc.ft->dwProxyIP);
+ nloc.szHost = inet_ntoa(addr);
+ nloc.wPort = ICQGetContactSettingWord(NULL, "OscarPort", DEFAULT_SERVER_PORT);
+ if (nloc.wPort == 0)
+ nloc.wPort = RandRange(1024, 65535);
+ oc.hConnection = NetLib_OpenConnection(ghServerNetlibUser, "Proxy ", &nloc);
+ if (!oc.hConnection)
+ { // proxy connection failed, we are out of possibilities
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+ // notify the other side, that we failed
+ oft_sendFileResponse(oc.dwUin, oc.szUid, oc.ft, 0x04);
+ // Release structure
+ SafeReleaseFileTransfer(&oc.ft);
+
+ return 0;
+ }
+ oc.status = OCS_PROXY;
+ oc.ft->connection = &oc;
+ // Join proxy tunnel
+ proxy_sendJoinTunnel(&oc, oc.ft->wRemotePort);
+ }
+ else // stage 2 failed (empty IP)
+ { // try stage 3, or send response error 0x06
+ if (!CreateOscarProxyConnection(&oc))
+ {
+ oft_sendFileResponse(oc.dwUin, oc.szUid, oc.ft, 0x06);
+ // notify UI
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+ // Release structure
+ SafeReleaseFileTransfer(&oc.ft);
+
+ return 0;
+ }
+ }
+ }
+ else if (oc.type == OCT_PROXY)
+ { // stage 4
+ if (!CreateOscarProxyConnection(&oc))
+ { // proxy connection failed, we are out of possibilities
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+ // notify the other side, that we failed
+ oft_sendFileResponse(oc.dwUin, oc.szUid, oc.ft, 0x06);
+ // Release structure
+ SafeReleaseFileTransfer(&oc.ft);
+
+ return 0;
+ }
+ }
+ else if (oc.type == OCT_PROXY_INIT)
+ { // stage 1
+ if (!CreateOscarProxyConnection(&oc))
+ { // We failed to init transfer, notify UI
+ icq_LogMessage(LOG_ERROR, "Failed to Initialize File Transfer. Unable to bind local port and File proxy unavailable.");
+ // Release transfer
+ SafeReleaseFileTransfer(&oc.ft);
+
+ return 0;
+ }
+ else
+ oc.type = OCT_PROXY_INIT;
+ }
+ }
+ if (!oc.hConnection)
+ { // one more sanity check
+ NetLog_Direct("Error: No OFT connection.");
+ return 0;
+ }
+ if (oc.status != OCS_PROXY)
+ { // Connected, notify FT UI
+ ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, oc.ft, 0);
+
+ // send init OFT frame - just for different order of packets (just like Trillian)
+ if (oc.status == OCS_CONNECTED && oc.ft->sending && (oc.ft->initialized || oc.type == OCT_REVERSE))
+ oft_sendPeerInit((oscar_connection*)oc.ft->connection);
+ }
+ hPacketRecver = (HANDLE)CallService(MS_NETLIB_CREATEPACKETRECVER, (WPARAM)oc.hConnection, 8192);
+ packetRecv.cbSize = sizeof(packetRecv);
+
+ // Packet receiving loop
+
+ while (oc.hConnection)
+ {
+ int recvResult;
+
+ packetRecv.dwTimeout = oc.wantIdleTime ? 0 : 120000;
+
+ recvResult = CallService(MS_NETLIB_GETMOREPACKETS, (WPARAM)hPacketRecver, (LPARAM)&packetRecv);
+ if (!recvResult)
+ {
+ NetLog_Direct("Clean closure of oscar socket (%p)", oc.hConnection);
+ break;
+ }
+
+ if (recvResult == SOCKET_ERROR)
+ {
+ if (GetLastError() == ERROR_TIMEOUT)
+ { // TODO: this will not work on some systems
+ if (oc.wantIdleTime)
+ { // here we want to send file data packets
+ oft_sendFileData(&oc);
+ }
+ else if (oc.status != OCS_WAITING)
+ {
+ NetLog_Direct("Connection timeouted, closing.");
+ break;
+ }
+ }
+ else if (oc.type != OCT_CLOSING || GetLastError() != 87)
+ { // log only significant errors, not "connection killed by us"
+ NetLog_Direct("Abortive closure of oscar socket (%p) (%d)", oc.hConnection, GetLastError());
+ break;
+ }
+ }
+
+ if (oc.type == OCT_CLOSING)
+ packetRecv.bytesUsed = packetRecv.bytesAvailable;
+ else
+ packetRecv.bytesUsed = oft_handlePackets(&oc, packetRecv.buffer, packetRecv.bytesAvailable);
+ }
+
+ // End of packet receiving loop
+
+ NetLib_SafeCloseHandle(&hPacketRecver);
+
+ CloseOscarConnection(&oc);
+
+ // Clean up
+ EnterCriticalSection(&oftMutex);
+ if (getFileTransferIndex(oc.ft) != -1)
+ {
+ oc.ft->connection = NULL; // release link
+ }
+ LeaveCriticalSection(&oftMutex);
+ // Give server some time for abort/cancel to arrive
+ SleepEx(1000, TRUE);
+ // Error handling
+ if (IsValidOscarTransfer(oc.ft))
+ {
+ if (oc.status == OCS_DATA)
+ {
+ ICQBroadcastAck(oc.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+
+ icq_LogMessage(LOG_ERROR, "Connection lost during file transfer.");
+ // Release structure
+ SafeReleaseFileTransfer(&oc.ft);
+ }
+ else if (oc.status == OCS_NEGOTIATION)
+ {
+ ICQBroadcastAck(oc.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+
+ icq_LogMessage(LOG_ERROR, "File transfer negotiation failed for unknown reason.");
+ // Release structure
+ SafeReleaseFileTransfer(&oc.ft);
+ }
+ }
+
+ return 0;
+}
+
+
+
+static void sendOscarPacket(oscar_connection *oc, icq_packet *packet)
+{
+ if (oc->hConnection)
+ {
+ int nResult;
+
+ nResult = Netlib_Send(oc->hConnection, (const char*)packet->pData, packet->wLen, 0);
+
+ if (nResult == SOCKET_ERROR)
+ {
+ NetLog_Direct("Oscar %p socket error: %d, closing", oc->hConnection, GetLastError());
+ CloseOscarConnection(oc);
+ }
+ }
+
+ SAFE_FREE(&packet->pData);
+}
+
+
+
+static int oft_handlePackets(oscar_connection *oc, unsigned char *buf, int len)
+{
+ BYTE *pBuf;
+ DWORD dwHead;
+ WORD datalen;
+ WORD datatype;
+ int bytesUsed = 0;
+
+ while (len > 0)
+ {
+ if (oc->status == OCS_DATA && !oc->ft->sending)
+ {
+ return oft_handleFileData(oc, buf, len);
+ }
+ else if (oc->status == OCS_PROXY)
+ {
+ return oft_handleProxyData(oc, buf, len);
+ }
+ if (len < 6)
+ break;
+
+ pBuf = buf;
+ unpackDWord(&pBuf, &dwHead);
+ if (dwHead != 0x4F465432)
+ { // bad packet
+ NetLog_Direct("OFT: Received invalid packet (dwHead = 0x%x).", dwHead);
+
+ CloseOscarConnection(oc);
+ break;
+ }
+
+ unpackWord(&pBuf, &datalen);
+
+ if (len < datalen) // wait for whole packet
+ break;
+
+ unpackWord(&pBuf, &datatype);
+#ifdef _DEBUG
+ NetLog_Direct("OFT2: Type %u, Length %u bytes", datatype, datalen);
+#endif
+ handleOFT2FramePacket(oc, datatype, pBuf, (WORD)(datalen - 8));
+
+ /* Increase pointers so we can check for more data */
+ buf += datalen;
+ len -= datalen;
+ bytesUsed += datalen;
+ }
+
+ return bytesUsed;
+}
+
+
+
+static int oft_handleProxyData(oscar_connection *oc, unsigned char *buf, int len)
+{
+ oscar_filetransfer *ft = oc->ft;
+ BYTE *pBuf;
+ WORD datalen;
+ WORD wCommand;
+ int bytesUsed = 0;
+
+
+ while (len > 2)
+ {
+ pBuf = buf;
+
+ unpackWord(&pBuf, &datalen);
+ datalen += 2;
+
+ if (len < datalen)
+ break; // packet is not complete
+
+ if (datalen < 12)
+ { // malformed packet
+ CloseOscarConnection(oc);
+ break;
+ }
+ pBuf += 2; // packet version
+ unpackWord(&pBuf, &wCommand);
+ pBuf += 6;
+ // handle packet
+ switch (wCommand)
+ {
+ case 0x01: // Error
+ {
+ WORD wError;
+ char* szError;
+
+ unpackWord(&pBuf, &wError);
+ switch(wError)
+ {
+ case 0x0D:
+ szError = "Bad request";
+ break;
+ case 0x0E:
+ szError = "Malformed packet";
+ break;
+ case 0x10:
+ szError = "Initial request timeout";
+ break;
+ case 0x1A:
+ szError = "Accept period timeout";
+ break;
+ case 0x1C:
+ szError = "Invalid data";
+ break;
+
+ default:
+ szError = "Unknown";
+ }
+ // Notify peer
+ oft_sendFileResponse(oc->dwUin, oc->szUid, oc->ft, 0x06);
+
+ NetLog_Server("Proxy Error: %s (0x%x)", szError, wError);
+ // Notify UI
+ ICQBroadcastAck(oc->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc->ft, 0);
+ // Release structure
+ SafeReleaseFileTransfer(&oc->ft);
+ }
+ break;
+
+ case 0x03: // Tunnel created
+ {
+ WORD wCode;
+ DWORD dwIP;
+
+ unpackWord(&pBuf, &wCode);
+ unpackDWord(&pBuf, &dwIP);
+
+ if (oc->type == OCT_PROXY_INIT)
+ { // Proxy ready, send Stage 1 Request
+ ft->bUseProxy = 1;
+ ft->wRemotePort = wCode;
+ ft->dwProxyIP = dwIP;
+ oft_sendFileRequest(oc->dwUin, oc->szUid, ft, ft->szThisFile, 0);
+ SAFE_FREE(&ft->szThisFile);
+ // Notify UI
+ ICQBroadcastAck(oc->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, oc->ft, 0);
+ }
+ else
+ {
+ NetLog_Server("Proxy Tunnel ready, notify peer.");
+ oft_sendFileRedirect(oc->dwUin, oc->szUid, ft, dwIP, wCode, TRUE);
+ }
+ }
+ break;
+
+ case 0x05: // Connection ready
+ oc->status = OCS_CONNECTED; // connection ready to send packets
+ // Notify UI
+ ICQBroadcastAck(oc->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTED, oc->ft, 0);
+ // signal we are ready
+ if (oc->type == OCT_PROXY_RECV)
+ {
+ oft_sendFileAccept(oc->dwUin, oc->szUid, ft);
+ if (ft->sending) // accept processed (sending only)
+ ft->initialized = 1;
+ }
+
+ NetLog_Server("Proxy Tunnel established");
+
+ if (ft->initialized && ft->sending)
+ oft_sendPeerInit((oscar_connection*)ft->connection);
+ break;
+
+ default:
+ NetLog_Server("Unknown proxy command 0x%x", wCommand);
+ }
+
+ buf += datalen;
+ len -= datalen;
+ bytesUsed += datalen;
+ }
+
+ return bytesUsed;
+}
+
+
+
+static int oft_handleFileData(oscar_connection *oc, unsigned char *buf, int len)
+{
+ oscar_filetransfer *ft = oc->ft;
+ DWORD dwLen = len;
+ int bytesUsed = 0;
+
+ // do not accept more data than expected
+ if (ft->qwThisFileSize - ft->qwFileBytesDone < dwLen)
+ dwLen = (int)(ft->qwThisFileSize - ft->qwFileBytesDone);
+
+ if (ft->fileId == -1)
+ { // something went terribly bad
+#ifdef _DEBUG
+ NetLog_Direct("Error: handleFileData(%u bytes) without fileId!", len);
+#endif
+ CloseOscarConnection(oc);
+ return 0;
+ }
+ _write(ft->fileId, buf, dwLen);
+ // update checksum
+ ft->dwRecvFileCheck = oft_calc_checksum((int)ft->qwFileBytesDone, buf, dwLen, ft->dwRecvFileCheck);
+ bytesUsed += dwLen;
+ ft->qwBytesDone += dwLen;
+ ft->qwFileBytesDone += dwLen;
+
+ if (GetTickCount() > ft->dwLastNotify + 700 || ft->qwFileBytesDone == ft->qwThisFileSize)
+ { // notify FT UI of our progress, at most every 700ms - do not be faster than Miranda
+ PROTOFILETRANSFERSTATUS pfts;
+
+ oft_buildProtoFileTransferStatus(ft, &pfts);
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&pfts);
+ SAFE_FREE(&pfts.currentFile);
+ SAFE_FREE(&pfts.workingDir);
+ oc->ft->dwLastNotify = GetTickCount();
+ }
+ if (ft->qwFileBytesDone == ft->qwThisFileSize)
+ {
+ /* EOF */
+#ifdef _DEBUG
+ NetLog_Direct("OFT: _close(%u)", ft->fileId);
+#endif
+ _close(ft->fileId);
+ ft->fileId = -1;
+
+ if (ft->resumeAction != FILERESUME_SKIP && ft->dwRecvFileCheck != ft->dwThisFileCheck)
+ {
+ NetLog_Direct("Error: File checksums does not match!");
+ { // Notify UI
+ char *pszMsg = ICQTranslateUtf("The checksum of file \"%s\" does not match, the file is probably damaged.");
+ char szBuf[MAX_PATH];
+
+ null_snprintf(szBuf, MAX_PATH, pszMsg, ExtractFileName(ft->szThisFile));
+ icq_LogMessage(LOG_ERROR, szBuf);
+
+ SAFE_FREE(&pszMsg);
+ }
+ } // keep transfer going (icq6 ignores checksums completely)
+ else if (ft->resumeAction == FILERESUME_SKIP)
+ NetLog_Direct("OFT: File receive skipped.");
+ else
+ NetLog_Direct("OFT: File received successfully.");
+
+ if ((DWORD)(ft->iCurrentFile + 1) == ft->wFilesCount)
+ {
+ ft->bHeaderFlags = 0x01; // the whole process is over
+ // ack received file
+ sendOFT2FramePacket(oc, OFT_TYPE_DONE);
+ oc->type = OCT_CLOSING;
+ NetLog_Direct("File Transfer completed successfully.");
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&ft);
+ }
+ else
+ { // ack received file
+ sendOFT2FramePacket(oc, OFT_TYPE_DONE);
+ oc->status = OCS_NEGOTIATION;
+ }
+
+ }
+ return bytesUsed;
+}
+
+
+
+static void handleOFT2FramePacket(oscar_connection *oc, WORD datatype, BYTE *pBuffer, WORD wLen)
+{
+ DWORD dwID1;
+ DWORD dwID2;
+
+ if (wLen < 232)
+ { // allow shorter packets, but at least with filename
+ NetLog_Direct("Error: Malformed OFT2 Frame, ignoring.");
+ return;
+ }
+
+ unpackLEDWord(&pBuffer, &dwID1);
+ wLen -= 4;
+ unpackLEDWord(&pBuffer, &dwID2);
+ wLen -= 4;
+
+ if (datatype == OFT_TYPE_REQUEST && !oc->ft->initialized)
+ { // first request does not contain MsgIDs we need to send them in ready packet
+ dwID1 = oc->ft->pMessage.dwMsgID1;
+ dwID2 = oc->ft->pMessage.dwMsgID2;
+ }
+
+ if (oc->ft->pMessage.dwMsgID1 != dwID1 || oc->ft->pMessage.dwMsgID2 != dwID2)
+ { // this is not the right packet - bad Message IDs
+ NetLog_Direct("Error: Invalid Packet Cookie, closing.");
+ CloseOscarConnection(oc);
+
+ return;
+ }
+
+ switch (datatype)
+ {
+ case OFT_TYPE_REQUEST:
+ { // Sender ready
+ oscar_filetransfer *ft = oc->ft;
+ char *szFullPath;
+
+ if (ft->sending)
+ { // just sanity check - this is only for receiving client
+ NetLog_Direct("Error: Invalid Packet, closing.");
+ CloseOscarConnection(oc);
+
+ return;
+ }
+
+ // Read Frame data
+ if (!ft->initialized)
+ {
+ unpackWord(&pBuffer, &ft->wEncrypt);
+ unpackWord(&pBuffer, &ft->wCompress);
+ unpackWord(&pBuffer, &ft->wFilesCount);
+ }
+ else
+ pBuffer += 6;
+ unpackWord(&pBuffer, &ft->wFilesLeft);
+ ft->iCurrentFile = ft->wFilesCount - oc->ft->wFilesLeft;
+ if (!ft->initialized)
+ unpackWord(&pBuffer, &ft->wPartsCount);
+ else
+ pBuffer += 2;
+ unpackWord(&pBuffer, &ft->wPartsLeft);
+ if (!ft->initialized)
+ { // just check it
+ DWORD dwSize;
+
+ unpackDWord(&pBuffer, &dwSize);
+ if (dwSize != (DWORD)ft->qwTotalSize)
+ { // the 32bits does not match, use them as full size
+ ft->qwTotalSize = dwSize;
+
+ NetLog_Server("Warning: Invalid total size.");
+ }
+ }
+ else
+ pBuffer += 4;
+ { // this allows us to receive single >4GB file correctly
+ DWORD dwSize;
+
+ unpackDWord(&pBuffer, &dwSize);
+ if (dwSize == (DWORD)ft->qwTotalSize && ft->wFilesCount == 1)
+ ft->qwThisFileSize = ft->qwTotalSize;
+ else
+ ft->qwThisFileSize = dwSize;
+ }
+ unpackDWord(&pBuffer, &ft->dwThisFileDate);
+ unpackDWord(&pBuffer, &ft->dwThisFileCheck);
+ unpackDWord(&pBuffer, &ft->dwRecvForkCheck);
+ unpackDWord(&pBuffer, &ft->dwThisForkSize);
+ unpackDWord(&pBuffer, &ft->dwThisFileCreation);
+ unpackDWord(&pBuffer, &ft->dwThisForkCheck);
+ pBuffer += 4; // File Bytes Done
+ unpackDWord(&pBuffer, &ft->dwRecvFileCheck);
+ if (!ft->initialized)
+ unpackString(&pBuffer, ft->rawIDString, 32);
+ else
+ pBuffer += 32;
+ unpackByte(&pBuffer, &ft->bHeaderFlags);
+ unpackByte(&pBuffer, &ft->bNameOff);
+ unpackByte(&pBuffer, &ft->bSizeOff);
+ if (!ft->initialized)
+ {
+ unpackString(&pBuffer, ft->rawDummy, 69);
+ unpackString(&pBuffer, ft->rawMacInfo, 16);
+ }
+ else
+ pBuffer += 85;
+ unpackWord(&pBuffer, &ft->wEncoding);
+ unpackWord(&pBuffer, &ft->wSubEncoding);
+ ft->cbRawFileName = wLen - 176;
+ SAFE_FREE(&ft->rawFileName); // release previous buffers
+ SAFE_FREE(&ft->szThisFile);
+ ft->rawFileName = (char*)SAFE_MALLOC(ft->cbRawFileName + 2);
+ unpackString(&pBuffer, ft->rawFileName, ft->cbRawFileName);
+ // Prepare file
+ if (ft->wEncoding == 2)
+ { // UCS-2 encoding
+ ft->szThisFile = ApplyEncoding(ft->rawFileName, "unicode-2-0");
+ }
+ else
+ {
+ ft->szThisFile = ansi_to_utf8(ft->rawFileName);
+ }
+
+ { // convert dir markings to normal backslashes
+ DWORD i;
+
+ for (i = 0; i < strlennull(ft->szThisFile); i++)
+ {
+ if (ft->szThisFile[i] == 0x01) ft->szThisFile[i] = '\\';
+ }
+ }
+
+ ft->initialized = 1; // First Frame Processed
+
+ NetLog_Direct("File '%s', %I64u Bytes", ft->szThisFile, ft->qwThisFileSize);
+
+ { // Prepare Path Information
+ char* szFile = strrchr(ft->szThisFile, '\\');
+
+ SAFE_FREE(&ft->szThisPath); // release previous path
+ if (szFile)
+ {
+ ft->szThisPath = ft->szThisFile;
+ szFile[0] = '\0'; // split that strings
+ ft->szThisFile = null_strdup(szFile + 1);
+ // no cheating with paths
+ if (!IsValidRelativePath(ft->szThisPath))
+ {
+ NetLog_Direct("Invalid path information");
+ break;
+ }
+ }
+ else
+ ft->szThisPath = null_strdup("");
+ }
+
+ /* no cheating with paths */
+ if (!IsValidRelativePath(ft->szThisFile))
+ {
+ NetLog_Direct("Invalid path information");
+ break;
+ }
+ szFullPath = (char*)SAFE_MALLOC(strlennull(ft->szSavePath)+strlennull(ft->szThisPath)+strlennull(ft->szThisFile)+3);
+ strcpy(szFullPath, ft->szSavePath);
+ NormalizeBackslash(szFullPath);
+ strcat(szFullPath, ft->szThisPath);
+ NormalizeBackslash(szFullPath);
+ // make sure the dest dir exists
+ if (MakeDirUtf(szFullPath))
+ NetLog_Direct("Failed to create destination directory!");
+
+ strcat(szFullPath, ft->szThisFile);
+ // we joined the full path to dest file
+ SAFE_FREE(&ft->szThisFile);
+ ft->szThisFile = szFullPath;
+
+ ft->qwFileBytesDone = 0;
+
+ {
+ /* file resume */
+ PROTOFILETRANSFERSTATUS pfts;
+
+ oft_buildProtoFileTransferStatus(ft, &pfts);
+ if (ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, ft, (LPARAM)&pfts))
+ {
+ oc->status = OCS_WAITING;
+
+ SAFE_FREE(&pfts.currentFile);
+ SAFE_FREE(&pfts.workingDir);
+ break; /* UI supports resume: it will call PS_FILERESUME */
+ }
+ SAFE_FREE(&pfts.currentFile);
+ SAFE_FREE(&pfts.workingDir);
+
+ ft->fileId = OpenFileUtf(ft->szThisFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE);
+#ifdef _DEBUG
+ NetLog_Direct("OFT: OpenFileUtf(%s, %u) returned %u", ft->szThisFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, ft->fileId);
+#endif
+ if (ft->fileId == -1)
+ {
+#ifdef _DEBUG
+ NetLog_Direct("OFT: errno=%d", errno);
+#endif
+ 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.");
+
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oc->ft);
+ return;
+ }
+ }
+ // Send "we are ready"
+ oc->status = OCS_DATA;
+
+ sendOFT2FramePacket(oc, OFT_TYPE_READY);
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0);
+ if (!ft->qwThisFileSize)
+ { // if the file is empty we will not receive any data
+ BYTE buf;
+
+ oft_handleFileData(oc, &buf, 0);
+ }
+ return;
+ }
+
+ case OFT_TYPE_READY:
+ case OFT_TYPE_RESUMEACK:
+ { // Receiver is ready
+ oscar_filetransfer *ft = oc->ft;
+
+ oc->status = OCS_DATA;
+ oc->wantIdleTime = 1;
+
+ NetLog_Direct("OFT: Receiver ready.");
+ }
+ break;
+
+ case OFT_TYPE_RESUMEREQUEST:
+ { // Receiver wants to resume file transfer from point
+ oscar_filetransfer *ft = oc->ft;
+ DWORD dwResumeCheck, dwResumeOffset, dwFileCheck;
+
+ if (!ft->sending)
+ { // just sanity check - this is only for sending client
+ NetLog_Direct("Error: Invalid Packet, closing.");
+ CloseOscarConnection(oc);
+
+ return;
+ }
+ // Read Resume Frame data
+ pBuffer += 44;
+ unpackDWord(&pBuffer, &dwResumeOffset);
+ unpackDWord(&pBuffer, &dwResumeCheck);
+
+ dwFileCheck = oft_calc_file_checksum(ft->fileId, dwResumeOffset);
+ if (dwFileCheck == dwResumeCheck && dwResumeOffset <= ft->qwThisFileSize)
+ { // resume seems ok
+ ft->qwFileBytesDone = dwResumeOffset;
+ ft->qwBytesDone += dwResumeOffset;
+ lseek(ft->fileId, dwResumeOffset, SEEK_SET);
+
+ NetLog_Direct("OFT: Resume request, ready.");
+ }
+ else
+ NetLog_Direct("OFT: Resume request, restarting.");
+
+ // Ready for resume
+ sendOFT2FramePacket(oc, OFT_TYPE_RESUMEREADY);
+ }
+ break;
+
+ case OFT_TYPE_RESUMEREADY:
+ { // Process Smart-resume reply
+ oscar_filetransfer *ft = oc->ft;
+ DWORD dwResumeOffset, dwResumeCheck;
+
+ if (ft->sending)
+ { // just sanity check - this is only for receiving client
+ NetLog_Direct("Error: Invalid Packet, closing.");
+ CloseOscarConnection(oc);
+
+ return;
+ }
+ // Read Resume Reply data
+ pBuffer += 44;
+ unpackDWord(&pBuffer, &dwResumeOffset);
+ unpackDWord(&pBuffer, &dwResumeCheck);
+
+ if (ft->qwFileBytesDone != dwResumeOffset)
+ {
+ ft->qwBytesDone -= (ft->qwFileBytesDone - dwResumeOffset);
+ ft->qwFileBytesDone = dwResumeOffset;
+ ft->dwRecvFileCheck = dwResumeCheck;
+ }
+ lseek(ft->fileId, dwResumeOffset, SEEK_SET);
+
+ if (ft->qwThisFileSize != ft->qwFileBytesDone)
+ NetLog_Direct("OFT: Resuming from offset %u.", dwResumeOffset);
+
+ // Prepare to receive data
+ oc->status = OCS_DATA;
+
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0);
+
+ // Ready for receive
+ sendOFT2FramePacket(oc, OFT_TYPE_RESUMEACK);
+
+ if (ft->qwThisFileSize == ft->qwFileBytesDone)
+ { // all data already processed
+ BYTE buf;
+
+ oft_handleFileData(oc, &buf, 0);
+ }
+ }
+ break;
+
+ case OFT_TYPE_DONE:
+ { // File done
+ oscar_filetransfer *ft = oc->ft;
+
+ oc->status = OCS_NEGOTIATION;
+ oc->wantIdleTime = 0;
+
+ NetLog_Direct("OFT: File sent successfully.");
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: _close(%u)", ft->fileId);
+#endif
+ _close(ft->fileId); // FIXME: this needs fix for "skip file" feature
+ ft->fileId = -1;
+ ft->iCurrentFile++;
+ // continue with next file
+ oft_sendPeerInit(oc);
+ }
+ break;
+
+ default:
+ NetLog_Direct("Error: Uknown OFT frame type 0x%x", datatype);
+ }
+}
+
+
+//
+// Proxy packets
+/////////////////////////////
+
+static void proxy_sendInitTunnel(oscar_connection *oc)
+{
+ icq_packet packet;
+ WORD wLen = 39 + getUINLen(dwLocalUIN);
+
+ packet.wLen = wLen;
+ init_generic_packet(&packet, 2);
+
+ packWord(&packet, wLen);
+ packWord(&packet, OSCAR_PROXY_VERSION);
+ packWord(&packet, 0x02); // wCommand
+ packDWord(&packet, 0); // Unknown
+ packWord(&packet, 0); // Flags?
+ packUIN(&packet, dwLocalUIN);
+ packLEDWord(&packet, oc->ft->pMessage.dwMsgID1);
+ packLEDWord(&packet, oc->ft->pMessage.dwMsgID2);
+ packDWord(&packet, 0x00010010); // TLV(1)
+ packGUID(&packet, MCAP_FILE_TRANSFER);
+
+ sendOscarPacket(oc, &packet);
+}
+
+
+
+static void proxy_sendJoinTunnel(oscar_connection *oc, WORD wPort)
+{
+ icq_packet packet;
+ WORD wLen = 41 + getUINLen(dwLocalUIN);
+
+ packet.wLen = wLen;
+ init_generic_packet(&packet, 2);
+
+ packWord(&packet, wLen);
+ packWord(&packet, OSCAR_PROXY_VERSION);
+ packWord(&packet, 0x04); // wCommand
+ packDWord(&packet, 0); // Unknown
+ packWord(&packet, 0); // Flags?
+ packUIN(&packet, dwLocalUIN);
+ packWord(&packet, wPort);
+ packLEDWord(&packet, oc->ft->pMessage.dwMsgID1);
+ packLEDWord(&packet, oc->ft->pMessage.dwMsgID2);
+ packDWord(&packet, 0x00010010); // TLV(1)
+ packGUID(&packet, MCAP_FILE_TRANSFER);
+
+ sendOscarPacket(oc, &packet);
+}
+
+
+//
+// Direct packets
+/////////////////////////////
+
+static void oft_sendFileData(oscar_connection *oc)
+{
+ oscar_filetransfer *ft = oc->ft;
+ BYTE buf[OFT_BUFFER_SIZE];
+ int bytesRead = 0;
+ icq_packet packet;
+
+ if (ft->fileId == -1)
+ return;
+ bytesRead = _read(ft->fileId, buf, sizeof(buf));
+ if (bytesRead == -1)
+ return;
+
+ if (!bytesRead)
+ { //
+ oc->wantIdleTime = 0;
+ return;
+ }
+
+ if ((DWORD)bytesRead > (ft->qwThisFileSize - ft->qwFileBytesDone))
+ { // do not send more than expected, limit to known size
+ bytesRead = (DWORD)(ft->qwThisFileSize - ft->qwFileBytesDone);
+ oc->wantIdleTime = 0;
+ }
+ packet.wLen = bytesRead;
+ init_generic_packet(&packet, 0);
+ packBuffer(&packet, buf, (WORD)bytesRead); // we are sending raw data
+ sendOscarPacket(oc, &packet);
+
+ ft->qwBytesDone += bytesRead;
+ ft->qwFileBytesDone += bytesRead;
+
+ if (GetTickCount() > ft->dwLastNotify + 700 || oc->wantIdleTime == 0 || ft->qwFileBytesDone == ft->qwThisFileSize)
+ { // notify only once a while or after last data packet sent
+ PROTOFILETRANSFERSTATUS pfts;
+
+ oft_buildProtoFileTransferStatus(ft, &pfts);
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&pfts);
+ SAFE_FREE(&pfts.currentFile);
+ SAFE_FREE(&pfts.workingDir);
+ ft->dwLastNotify = GetTickCount();
+ }
+}
+
+
+
+static void oft_sendPeerInit(oscar_connection *oc)
+{
+ oscar_filetransfer *ft = oc->ft;
+ struct _stati64 statbuf;
+ char *pszThisFileName;
+ wchar_t *pwsThisFile;
+
+ // prepare init frame
+ if (ft->iCurrentFile >= (int)ft->wFilesCount)
+ { // All files done, great!
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oc->ft);
+ return;
+ }
+
+ SAFE_FREE(&ft->szThisFile);
+ ft->szThisFile = null_strdup(ft->files[ft->iCurrentFile].szFile);
+ if (FileStatUtf(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.");
+
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oc->ft);
+ return;
+ }
+
+ { // create full relative filename
+ char* szThisContainer = ft->files[ft->iCurrentFile].szContainer;
+
+ pszThisFileName = (char*)SAFE_MALLOC(strlennull(ft->szThisFile) + strlennull(szThisContainer) + 4);
+ strcpy(pszThisFileName, szThisContainer);
+ NormalizeBackslash(pszThisFileName);
+ strcat(pszThisFileName, ExtractFileName(ft->szThisFile));
+ }
+ { // convert backslashes to dir markings
+ DWORD i;
+
+ for (i = 0; i < strlennull(pszThisFileName); i++)
+ {
+ if (pszThisFileName[i] == '\\' || pszThisFileName[i] == '/') pszThisFileName[i] = 0x01;
+ }
+ }
+
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0);
+
+ ft->fileId = OpenFileUtf(ft->szThisFile, _O_BINARY | _O_RDONLY, 0);
+#ifdef _DEBUG
+ NetLog_Direct("OFT: OpenFileUtf(%s, %u) returned %u", ft->szThisFile, _O_BINARY | _O_RDONLY, ft->fileId);
+#endif
+ if (ft->fileId == -1)
+ {
+#ifdef _DEBUG
+ NetLog_Direct("OFT: errno=%d", errno);
+#endif
+ SAFE_FREE(&pszThisFileName);
+ 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.");
+ //
+ ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer(&oc->ft);
+ return;
+ }
+
+ ft->qwThisFileSize = statbuf.st_size;
+ ft->dwThisFileDate = statbuf.st_mtime;
+ ft->dwThisFileCreation = statbuf.st_ctime;
+ ft->dwThisFileCheck = oft_calc_file_checksum(ft->fileId, ft->qwThisFileSize);
+ ft->qwFileBytesDone = 0;
+ ft->dwRecvFileCheck = 0xFFFF0000;
+ SAFE_FREE(&ft->rawFileName);
+
+ if (IsUSASCII(pszThisFileName, strlennull(pszThisFileName)))
+ {
+ ft->wEncoding = 0; // ascii
+ ft->cbRawFileName = strlennull(pszThisFileName) + 1;
+ if (ft->cbRawFileName < 64) ft->cbRawFileName = 64;
+ ft->rawFileName = (char*)SAFE_MALLOC(ft->cbRawFileName);
+ strcpy(ft->rawFileName, pszThisFileName);
+ SAFE_FREE(&pszThisFileName);
+ }
+ else
+ {
+ ft->wEncoding = 2; // ucs-2
+ pwsThisFile = make_unicode_string(pszThisFileName);
+ SAFE_FREE(&pszThisFileName);
+ ft->cbRawFileName = wcslen(pwsThisFile) * sizeof(wchar_t) + 2;
+ if (ft->cbRawFileName < 64) ft->cbRawFileName = 64;
+ ft->rawFileName = (char*)SAFE_MALLOC(ft->cbRawFileName);
+ // convert to LE ordered string
+ unpackWideString((char**)&pwsThisFile, (wchar_t*)ft->rawFileName, (WORD)(wcslen(pwsThisFile) * sizeof(wchar_t)));
+ SAFE_FREE(&pwsThisFile);
+ }
+ ft->wFilesLeft = (WORD)(ft->wFilesCount - ft->iCurrentFile);
+
+ sendOFT2FramePacket(oc, OFT_TYPE_REQUEST);
+}
+
+
+
+static void sendOFT2FramePacket(oscar_connection *oc, WORD datatype)
+{
+ oscar_filetransfer *ft = oc->ft;
+ icq_packet packet;
+
+ packet.wLen = 192 + ft->cbRawFileName;
+ init_generic_packet(&packet, 0);
+ // Basic Oscar Frame
+ packDWord(&packet, 0x4F465432); // Magic
+ packWord(&packet, packet.wLen);
+ packWord(&packet, datatype);
+ // Cookie
+ packLEDWord(&packet, ft->pMessage.dwMsgID1);
+ packLEDWord(&packet, ft->pMessage.dwMsgID2);
+ packWord(&packet, ft->wEncrypt);
+ packWord(&packet, ft->wCompress);
+ packWord(&packet, ft->wFilesCount);
+ packWord(&packet, ft->wFilesLeft);
+ packWord(&packet, ft->wPartsCount);
+ packWord(&packet, ft->wPartsLeft);
+ packDWord(&packet, (DWORD)ft->qwTotalSize);
+ packDWord(&packet, (DWORD)ft->qwThisFileSize);
+ packDWord(&packet, ft->dwThisFileDate);
+ packDWord(&packet, ft->dwThisFileCheck);
+ packDWord(&packet, ft->dwRecvForkCheck);
+ packDWord(&packet, ft->dwThisForkSize);
+ packDWord(&packet, ft->dwThisFileCreation);
+ packDWord(&packet, ft->dwThisForkCheck);
+ packDWord(&packet, (DWORD)ft->qwFileBytesDone);
+ packDWord(&packet, ft->dwRecvFileCheck);
+ packBuffer(&packet, ft->rawIDString, 32);
+ packByte(&packet, ft->bHeaderFlags);
+ packByte(&packet, ft->bNameOff);
+ packByte(&packet, ft->bSizeOff);
+ packBuffer(&packet, ft->rawDummy, 69);
+ packBuffer(&packet, ft->rawMacInfo, 16);
+ packWord(&packet, ft->wEncoding);
+ packWord(&packet, ft->wSubEncoding);
+ packBuffer(&packet, ft->rawFileName, ft->cbRawFileName);
+
+ sendOscarPacket(oc, &packet);
+}
|