diff options
Diffstat (limited to 'protocols/IcqOscarJ/oscar_filetransfer.cpp')
-rw-r--r-- | protocols/IcqOscarJ/oscar_filetransfer.cpp | 2452 |
1 files changed, 2452 insertions, 0 deletions
diff --git a/protocols/IcqOscarJ/oscar_filetransfer.cpp b/protocols/IcqOscarJ/oscar_filetransfer.cpp new file mode 100644 index 0000000000..b6dd52cf5f --- /dev/null +++ b/protocols/IcqOscarJ/oscar_filetransfer.cpp @@ -0,0 +1,2452 @@ +// ---------------------------------------------------------------------------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-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// -----------------------------------------------------------------------------
+//
+// File name : $URL: http://miranda.googlecode.com/svn/trunk/miranda/protocols/IcqOscarJ/oscar_filetransfer.cpp $
+// Revision : $Revision: 14229 $
+// Last change on : $Date: 2012-05-14 03:26:21 +0300 (Пн, 14 май 2012) $
+// Last change by : $Author: borkra $
+//
+// DESCRIPTION:
+//
+// OSCAR File-Transfers implementation
+//
+// -----------------------------------------------------------------------------
+
+#include "icqoscar.h"
+
+struct oscarthreadstartinfo
+{
+ int type;
+ int incoming;
+ HANDLE hContact;
+ HANDLE hConnection;
+ DWORD dwRemoteIP;
+ oscar_filetransfer *ft;
+ oscar_listener *listener;
+};
+
+
+// small utility function
+extern void NormalizeBackslash(char* path);
+
+//
+// Common functions
+/////////////////////////////
+
+char *FindFilePathContainer(const char **files, int iFile, char *szContainer)
+{
+ const char *szThisFile = files[iFile];
+ char *szFileName = (char*)ExtractFileName(szThisFile);
+
+ szContainer[0] = '\0';
+
+ if (szThisFile != szFileName)
+ { // find an earlier subdirectory to be used as a container
+ for (int i = iFile - 1; i >= 0; i--)
+ {
+ int len = strlennull(files[i]);
+
+ if (!_strnicmp(files[i], szThisFile, len) && (szThisFile[len] == '\\' || szThisFile[len] == '/'))
+ {
+ const char *pszLastBackslash;
+
+ if (((pszLastBackslash = strrchr(files[i], '\\')) == NULL) &&
+ ((pszLastBackslash = strrchr(files[i], '/')) == NULL))
+ {
+ strcpy(szContainer, files[i]);
+ }
+ else
+ {
+ len = pszLastBackslash - files[i] + 1;
+ null_strcpy(szContainer, szThisFile + len, szFileName - szThisFile - len);
+ }
+ }
+ }
+ }
+ return szFileName;
+}
+
+
+//
+// Utility functions
+/////////////////////////////
+
+oscar_filetransfer* CIcqProto::CreateOscarTransfer()
+{
+ oscar_filetransfer* ft = (oscar_filetransfer*)SAFE_MALLOC(sizeof(oscar_filetransfer));
+
+ ft->ft_magic = FT_MAGIC_OSCAR; // Setup signature
+ // Init members
+ ft->fileId = -1;
+
+ icq_lock l(oftMutex);
+
+ fileTransferList = (basic_filetransfer**)SAFE_REALLOC(fileTransferList, sizeof(basic_filetransfer*)*(fileTransferCount + 1));
+ fileTransferList[fileTransferCount++] = ft;
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: FT struct 0x%x created", ft);
+#endif
+
+ return ft;
+}
+
+
+filetransfer *CIcqProto::CreateIcqFileTransfer()
+{
+ filetransfer *ft = (filetransfer*)SAFE_MALLOC(sizeof(filetransfer));
+
+ ft->ft_magic = FT_MAGIC_ICQ;
+
+ icq_lock l(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
+
+ return ft;
+}
+
+
+int CIcqProto::getFileTransferIndex(void *ft)
+{
+ for (int i = 0; i < fileTransferCount; i++)
+ {
+ if (fileTransferList[i] == ft)
+ return i;
+ }
+ return -1;
+}
+
+
+void CIcqProto::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 CIcqProto::IsValidFileTransfer(void *ft)
+{
+ icq_lock l(oftMutex);
+
+ if (getFileTransferIndex(ft) != -1) return 1;
+
+ return 0;
+}
+
+
+int CIcqProto::IsValidOscarTransfer(void *ft)
+{
+ icq_lock l(oftMutex);
+
+ if (getFileTransferIndex(ft) != -1 && ((basic_filetransfer*)ft)->ft_magic == FT_MAGIC_OSCAR)
+ return 1;
+
+ return 0;
+}
+
+
+oscar_filetransfer* CIcqProto::FindOscarTransfer(HANDLE hContact, DWORD dwID1, DWORD dwID2)
+{
+ icq_lock l(oftMutex);
+
+ for (int i = 0; i < fileTransferCount; i++)
+ {
+ if (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)
+ return oft;
+ }
+ }
+
+ return NULL;
+}
+
+
+// Release file transfer structure
+void CIcqProto::SafeReleaseFileTransfer(void **ft)
+{
+ basic_filetransfer **bft = (basic_filetransfer**)ft;
+
+ icq_lock l(oftMutex);
+
+ // Check for filetransfer validity
+ if (getFileTransferIndex(*ft) == -1)
+ 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->pszFiles)
+ {
+ for (int i = 0; i < (int)ift->dwFileCount; i++)
+ SAFE_FREE(&ift->pszFiles[i]);
+ SAFE_FREE((void**)&ift->pszFiles);
+ }
+ // Invalidate transfer
+ ReleaseFileTransfer(ift);
+#ifdef _DEBUG
+ NetLog_Direct("FT struct 0x%x released", ft);
+#endif
+ // Release memory
+ SAFE_FREE((void**)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);
+ SAFE_FREE(&oft->szDescription);
+ if (oft->files)
+ {
+ for (int i = 0; i < oft->wFilesCount; i++)
+ SAFE_FREE(&oft->files[i].szFile);
+ SAFE_FREE((void**)&oft->files);
+ }
+ if (oft->files_list)
+ {
+/* for (int i = 0; i < oft->wFilesCount; i++)
+ SAFE_FREE(&oft->files_list[i]);*/
+ SAFE_FREE((void**)&oft->files_list);
+ }
+ if (oft->file_containers)
+ {
+ for (int 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((void**)ft);
+ }
+ }
+}
+
+
+// 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 = (dwChecksum >> 16) & 0xffff;
+
+ for (int 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, (maxSize < sizeof(buf)) ? maxSize : (unsigned)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* CIcqProto::CreateOscarListener(oscar_filetransfer *ft, NETLIBNEWCONNECTIONPROC_V2 handler)
+{
+ oscar_listener *listener = (oscar_listener*)SAFE_MALLOC(sizeof(oscar_listener));
+
+ if (listener)
+ {
+ listener->ppro = this;
+ listener->ft = ft;
+ if (listener->hBoundPort = NetLib_BindPort(handler, listener, &listener->wPort, NULL))
+ return listener; // Success
+
+ SAFE_FREE((void**)&listener);
+ }
+
+ return NULL; // Failure
+}
+
+
+void CIcqProto::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((void**)pListener);
+}
+
+
+//
+// Miranda FT interface handlers & services
+/////////////////////////////
+
+void CIcqProto::handleRecvServMsgOFT(BYTE *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 = chain->getWord(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 = chain->getTLV(0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = chain->getDWord(0x02, 1);
+ ft->dwRemoteInternalIP = chain->getDWord(0x03, 1);
+ ft->dwRemoteExternalIP = chain->getDWord(0x04, 1);
+ ft->wRemotePort = chain->getWord(0x05, 1);
+ ft->wReqNum = wAckType;
+
+ { // User Message
+ oscar_tlv* tlv = chain->getTLV(0x0C, 1);
+
+ if (tlv)
+ { // parse User Message
+ BYTE* tBuf = tlv->pData;
+
+ pszDescription = (char*)_alloca(tlv->wLen + 2);
+ unpackString(&tBuf, (char*)pszDescription, tlv->wLen);
+ pszDescription[tlv->wLen] = '\0';
+ pszDescription[tlv->wLen+1] = '\0';
+ { // apply User Message encoding
+ oscar_tlv *charset = chain->getTLV(0x0D, 1);
+ char *str = pszDescription;
+ char *bTag,*eTag;
+
+ if (charset)
+ { // decode charset
+ char *szEnc = (char*)_alloca(charset->wLen + 1);
+
+ null_strcpy(szEnc, (char*)charset->pData, charset->wLen);
+ str = ApplyEncoding((char*)pszDescription, szEnc);
+ }
+ else
+ str = null_strdup(str);
+ // eliminate HTML tags
+ pszDescription = EliminateHtml(str, strlennull(str));
+
+ bTag = strstrnull(pszDescription, "<DESC>");
+ if (bTag)
+ { // take special Description - ICQJ's extension
+ eTag = strstrnull(bTag, "</DESC>");
+ if (eTag)
+ {
+ *eTag = '\0';
+ str = null_strdup(bTag + 6);
+ SAFE_FREE(&pszDescription);
+ pszDescription = str;
+ }
+ }
+ else
+ {
+ bTag = strstrnull(pszDescription, "<FS>");
+ if (bTag)
+ { // take only <FS> - Description tag if present
+ eTag = strstrnull(bTag, "</FS>");
+ if (eTag)
+ {
+ *eTag = '\0';
+ str = null_strdup(bTag + 4);
+ SAFE_FREE(&pszDescription);
+ pszDescription = str;
+ }
+ }
+ }
+ }
+ }
+ if (!strlennull(pszDescription))
+ {
+ SAFE_FREE(&pszDescription);
+ pszDescription = ICQTranslateUtf(LPGEN("No description given"));
+ }
+ }
+ { // parse File Transfer Info block
+ oscar_tlv* tlv = chain->getTLV(0x2711, 1);
+
+ // sanity check
+ if (!tlv || tlv->wLen < 8)
+ {
+ NetLog_Server("Error: Malformed file request");
+ // release structures
+ SafeReleaseFileTransfer((void**)&ft);
+ SAFE_FREE(&pszDescription);
+ return;
+ }
+
+ 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
+ if (tLen)
+ { // some filename specified, unpack
+ wFilenameLength = tLen - 1;
+ pszFileName = (char*)_alloca(tLen);
+ unpackString(&tBuf, (char*)pszFileName, wFilenameLength);
+ pszFileName[wFilenameLength] = '\0';
+ }
+ else if (ft->wFilesCount == 1) // give some generic file name
+ pszFileName = "unnamed_file";
+ else // or empty directory name
+ pszFileName = "";
+
+ { // apply Filename / Directory Name encoding
+ oscar_tlv* charset = chain->getTLV(0x2712, 1);
+
+ if (charset)
+ {
+ char* szEnc = (char*)_alloca(charset->wLen + 1);
+
+ null_strcpy(szEnc, (char*)charset->pData, charset->wLen);
+ pszFileName = ApplyEncoding(pszFileName, szEnc);
+ }
+ else
+ pszFileName = ansi_to_utf8(pszFileName);
+ }
+ if (ft->wFilesCount == 1)
+ { // Filename - use for DB event
+ char *szFileName = (char*)_alloca(strlennull(pszFileName) + 1);
+
+ strcpy(szFileName, pszFileName);
+ SAFE_FREE(&pszFileName);
+ pszFileName = szFileName;
+ }
+ 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);
+
+ char tmp[64];
+ null_snprintf(pszFileName, 64, ICQTranslateUtfStatic(LPGEN("%d Files"), tmp, SIZEOF(tmp)), ft->wFilesCount);
+ }
+ }
+ // Total Size TLV (ICQ 6 and AIM 6)
+ {
+ oscar_tlv *tlv = chain->getTLV(0x2713, 1);
+
+ if (tlv && tlv->wLen >= 8)
+ {
+ BYTE *tBuf = tlv->pData;
+
+ unpackQWord(&tBuf, &ft->qwTotalSize);
+ }
+ }
+ int bAdded;
+ HANDLE hContact = HContactFromUID(dwUin, szUID, &bAdded);
+
+ ft->hContact = hContact;
+ ft->szDescription = pszDescription;
+ ft->fileId = -1;
+
+ // Send chain event
+ char *szBlob = (char*)_alloca(sizeof(DWORD) + strlennull(pszFileName) + strlennull(pszDescription) + 2);
+ *(PDWORD)szBlob = 0;
+ strcpy(szBlob + sizeof(DWORD), pszFileName);
+ strcpy(szBlob + sizeof(DWORD) + strlennull(pszFileName) + 1, pszDescription);
+
+ PROTORECVEVENT pre;
+ pre.flags = PREF_UTF;
+ pre.timestamp = time(NULL);
+ pre.szMessage = szBlob;
+ pre.lParam = (LPARAM)ft;
+
+ CCSDATA ccs;
+ ccs.szProtoService = PSR_FILE;
+ ccs.hContact = hContact;
+ ccs.wParam = 0;
+ ccs.lParam = (LPARAM)⪯
+ 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->flags & OFTF_SENDING)
+ {
+ ReleaseOscarListener((oscar_listener**)&ft->listener);
+
+ ft->bUseProxy = chain->getTLV(0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = chain->getDWord(0x02, 1);
+ ft->dwRemoteInternalIP = chain->getDWord(0x03, 1);
+ ft->dwRemoteExternalIP = chain->getDWord(0x04, 1);
+ ft->wRemotePort = chain->getWord(0x05, 1);
+
+ OpenOscarConnection(hContact, ft, ft->bUseProxy ? OCT_PROXY_RECV: OCT_REVERSE);
+ }
+ else
+ { // Just sanity
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&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 = chain->getTLV(0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = chain->getDWord(0x02, 1);
+ ft->wRemotePort = chain->getWord(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 = chain->getTLV(0x10, 1) ? 1 : 0;
+ ft->dwProxyIP = chain->getDWord(0x02, 1);
+ ft->wRemotePort = chain->getWord(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));
+
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Notify user, that the FT was cancelled // TODO: new ACKRESULT_?
+ icq_LogMessage(LOG_ERROR, LPGEN("The file transfer was aborted by the other user."));
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&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->flags & OFTF_SENDING)
+ {
+ if (ft->connection && ft->connection->status == OCS_CONNECTED)
+ {
+ if (!(ft->flags & OFTF_FILE_REQUEST_SENT))
+ {
+ ft->flags |= OFTF_FILE_REQUEST_SENT;
+ // proceed with first file
+ oft_sendPeerInit(ft->connection);
+ }
+ }
+ ft->flags |= OFTF_INITIALIZED; // 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 CIcqProto::handleRecvServResponseOFT(BYTE *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));
+
+ BroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oft);
+ }
+ break;
+
+ case 4: // Proxy error
+ {
+ icq_LogMessage(LOG_ERROR, LPGEN("The file transfer failed: Proxy error"));
+
+ BroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oft);
+ }
+ break;
+
+ case 5: // Invalid request
+ {
+ icq_LogMessage(LOG_ERROR, LPGEN("The file transfer failed: Invalid request"));
+
+ BroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oft);
+ }
+ break;
+
+ case 6: // Proxy Failed (IP = 0)
+ {
+ icq_LogMessage(LOG_ERROR, LPGEN("The file transfer failed: Proxy unavailable"));
+
+ BroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oft);
+ }
+ break;
+
+ default:
+ {
+ NetLog_Server("OFT: Uknown request response code 0x%x", wStatus);
+
+ BroadcastAck(oft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)oft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oft);
+ }
+ }
+ }
+}
+
+
+// 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->flags & OFTF_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
+ listener->ppro->ForkThread(( IcqThreadFunc )&CIcqProto::oft_connectionThread, otsi );
+}
+
+
+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 (!strcmpnull(szPathUtf, oft->file_containers[i]))
+ {
+ SAFE_FREE((void**)&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];
+}
+
+
+HANDLE CIcqProto::oftInitTransfer(HANDLE hContact, DWORD dwUin, char* szUid, const TCHAR** files, const TCHAR* pszDesc)
+{
+ oscar_filetransfer *ft;
+ int i, filesCount;
+ struct _stati64 statbuf;
+ char ** filesUtf;
+
+ // 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_list = (char**)SAFE_MALLOC(sizeof(TCHAR *) * filesCount);
+ ft->qwTotalSize = 0;
+
+ filesUtf = (char**)SAFE_MALLOC(sizeof(char *) * filesCount);
+ for(i = 0; i < filesCount; i++) filesUtf[i] = FileNameToUtf(files[i]);
+
+ // Prepare files arrays
+ for (i = 0; i < filesCount; i++)
+ {
+ if (_tstati64(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 = ft->files_list[ft->wFilesCount] = null_strdup(filesUtf[i]);
+ ft->files[ft->wFilesCount].szContainer = oftGetFileContainer(ft, (LPCSTR*) filesUtf, i);
+
+ ft->wFilesCount++;
+ ft->qwTotalSize += statbuf.st_size;
+ }
+ }
+ }
+
+ for (int i = 0; i < filesCount; i++)
+ SAFE_FREE(&filesUtf[i]);
+ SAFE_FREE((void**)&filesUtf);
+
+ 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
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&ft);
+
+ return 0; // Failure
+ }
+#ifdef __GNUC__
+#define OSCAR_MAX_SIZE 0x100000000ULL
+#else
+#define OSCAR_MAX_SIZE 0x100000000
+#endif
+ if (ft->qwTotalSize >= OSCAR_MAX_SIZE && ft->wFilesCount > 1)
+ { // file larger than 4GB can be send only as single
+ icq_LogMessage(LOG_ERROR, LPGEN("The files are too big to be sent at once. Files bigger than 4GB can be sent only separately."));
+ // Notify UI
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, (HANDLE)ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&ft);
+
+ return 0; // Failure
+ }
+
+ NetLog_Server("OFT: Found %d files.", ft->wFilesCount);
+
+ ft->szDescription = tchar_to_utf8(pszDesc);
+ ft->flags = OFTF_SENDING;
+ 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 = (char*)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 = strstrnull(szFirstDir, "\\")) || (szFirstDiv = strstrnull(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((char*)ft->file_containers[i], (char*)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, getSettingDword(NULL, "RealIP", 0));
+ }
+ else
+ { // try stage 1 proxy
+ ft->szThisFile = null_strdup(pszFiles);
+ OpenOscarConnection(hContact, ft, OCT_PROXY_INIT);
+ }
+ }
+
+ return ft; // Success
+}
+
+
+HANDLE CIcqProto::oftFileAllow(HANDLE hContact, HANDLE hTransfer, const TCHAR *szPath)
+{
+ oscar_filetransfer *ft = (oscar_filetransfer*)hTransfer;
+ DWORD dwUin;
+ uid_str szUid;
+
+ if (getContactUid(hContact, &dwUin, &szUid))
+ return 0; // Invalid contact
+
+ if (!IsValidOscarTransfer(ft))
+ return 0; // Invalid transfer
+
+ ft->szSavePath = tchar_to_utf8(szPath);
+
+ 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 hTransfer; // Success
+}
+
+
+DWORD CIcqProto::oftFileDeny(HANDLE hContact, HANDLE hTransfer, const TCHAR *szReason)
+{
+ oscar_filetransfer *ft = (oscar_filetransfer*)hTransfer;
+ DWORD dwUin;
+ uid_str szUid;
+
+ if (getContactUid(hContact, &dwUin, &szUid))
+ return 1; // Invalid contact
+
+ if (IsValidOscarTransfer(ft))
+ {
+ if (ft->hContact != hContact)
+ return 1; // Bad contact or hTransfer
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Request denied.");
+#endif
+
+ oft_sendFileDeny(dwUin, szUid, ft);
+
+ // Release structure
+ SafeReleaseFileTransfer((void**)&ft);
+
+ return 0; // Success
+ }
+ return 1; // Invalid transfer
+}
+
+
+DWORD CIcqProto::oftFileCancel(HANDLE hContact, HANDLE hTransfer)
+{
+ oscar_filetransfer* ft = (oscar_filetransfer*)hTransfer;
+ DWORD dwUin;
+ uid_str szUid;
+
+ if (getContactUid(hContact, &dwUin, &szUid))
+ return 1; // Invalid contact
+
+ if (IsValidOscarTransfer(ft))
+ {
+ if (ft->hContact != hContact)
+ return 1; // Bad contact or hTransfer
+
+#ifdef _DEBUG
+ NetLog_Direct("OFT: Transfer cancelled.");
+#endif
+
+ oft_sendFileCancel(dwUin, szUid, ft);
+
+ BroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+
+ // Release structure
+ SafeReleaseFileTransfer((void**)&ft);
+
+ return 0; // Success
+ }
+ return 1; // Invalid transfer
+}
+
+
+void CIcqProto::oftFileResume(oscar_filetransfer *ft, int action, const TCHAR *szFilename)
+{
+ int openFlags;
+
+ if (ft->connection == NULL)
+ return;
+
+ oscar_connection *oc = 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 = tchar_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, 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."));
+
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&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 skipping the file, send "we are done"
+ oc->status = OCS_NEGOTIATION;
+ }
+ else
+ { // Send "we are ready"
+ oc->status = OCS_DATA;
+ ft->flags |= OFTF_FILE_RECEIVING;
+
+ sendOFT2FramePacket(oc, OFT_TYPE_READY);
+ }
+ BroadcastAck(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->flags = PFTS_UTF + ((ft->flags & OFTF_SENDING) ? PFTS_SENDING : PFTS_RECEIVING);
+ if (ft->flags & OFTF_SENDING)
+ pfts->pszFiles = ft->files_list;
+ else
+ pfts->pszFiles = NULL; /* FIXME */
+ pfts->totalFiles = ft->wFilesCount;
+ pfts->currentFileNumber = ft->iCurrentFile;
+ pfts->totalBytes = ft->qwTotalSize;
+ pfts->totalProgress = ft->qwBytesDone;
+ pfts->szWorkingDir = ft->szThisPath;
+ pfts->szCurrentFile = ft->szThisFile;
+ pfts->currentFileSize = ft->qwThisFileSize;
+ pfts->currentFileTime = ft->dwThisFileDate;
+ pfts->currentFileProgress = ft->qwFileBytesDone;
+}
+
+
+void CIcqProto::CloseOscarConnection(oscar_connection *oc)
+{
+ icq_lock l(oftMutex);
+
+ if (oc)
+ {
+ oc->type = OCT_CLOSING;
+
+ if (oc->hConnection)
+ { // we need this for Netlib handle consistency
+ NetLib_CloseConnection(&oc->hConnection, FALSE);
+ }
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CIcqProto::OpenOscarConnection(HANDLE hContact, oscar_filetransfer *ft, int type)
+{
+ oscarthreadstartinfo *otsi = (oscarthreadstartinfo*)SAFE_MALLOC(sizeof(oscarthreadstartinfo));
+
+ otsi->hContact = hContact;
+ otsi->type = type;
+ otsi->ft = ft;
+
+ ForkThread(( IcqThreadFunc )&CIcqProto::oft_connectionThread, otsi );
+}
+
+
+int CIcqProto::CreateOscarProxyConnection(oscar_connection *oc)
+{
+ NETLIBOPENCONNECTION nloc = {0};
+
+ // inform UI
+ BroadcastAck(oc->ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTPROXY, oc->ft, 0);
+
+ nloc.szHost = OSCAR_PROXY_HOST;
+ nloc.wPort = getSettingWord(NULL, "OscarPort", m_bSecureConnection ? DEFAULT_SERVER_PORT_SSL : DEFAULT_SERVER_PORT);
+ if (nloc.wPort == 0)
+ nloc.wPort = RandRange(1024, 65535);
+ if (m_bGatewayMode)
+ nloc.flags |= NLOCF_HTTPGATEWAY;
+
+ oc->hConnection = NetLib_OpenConnection(m_hServerNetlibUser, "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
+}
+
+
+void __cdecl CIcqProto::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;
+ }
+ else
+ { // FT is already over, kill listener
+ NetLog_Direct("Received unexpected connection, closing.");
+
+ CloseOscarConnection(&oc);
+ ReleaseOscarListener(&source);
+
+ SAFE_FREE((void**)&otsi);
+ return;
+ }
+ }
+ SAFE_FREE((void**)&otsi);
+
+ if (oc.hContact)
+ { // Load contact information
+ getContactUid(oc.hContact, &oc.dwUin, &oc.szUid);
+ }
+
+ // Load local IP information
+ oc.dwLocalExternalIP = getSettingDword(NULL, "IP", 0);
+ oc.dwLocalInternalIP = getSettingDword(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 if (oc.ft->dwRemoteExternalIP)
+ {
+ 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);
+ }
+ else // try LAN
+ addr.S_un.S_addr = htonl(oc.ft->dwRemoteInternalIP);
+
+ // Inform UI that we will attempt to connect
+ BroadcastAck(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
+ BroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_LISTENING, oc.ft, 0);
+
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, oc.dwLocalInternalIP, listener->wPort, FALSE);
+ return;
+ }
+ 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;
+ }
+ }
+ 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(m_hDirectNetlibUser, 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(m_hDirectNetlibUser, 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
+ BroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_LISTENING, oc.ft, 0);
+
+ oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, oc.dwLocalInternalIP, listener->wPort, FALSE);
+ return;
+ }
+ }
+ 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;
+ }
+ }
+ 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
+ BroadcastAck(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;
+ }
+ }
+ }
+ 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
+ BroadcastAck(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 = getSettingWord(NULL, "OscarPort", m_bSecureConnection ? DEFAULT_SERVER_PORT_SSL : DEFAULT_SERVER_PORT);
+ if (nloc.wPort == 0)
+ nloc.wPort = RandRange(1024, 65535);
+ if (m_bGatewayMode)
+ nloc.flags |= NLOCF_HTTPGATEWAY;
+ oc.hConnection = NetLib_OpenConnection(m_hServerNetlibUser, "Proxy ", &nloc);
+ if (!oc.hConnection)
+ { // proxy connection failed, we are out of possibilities
+ BroadcastAck(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((void**)&oc.ft);
+ return;
+ }
+ 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
+ BroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+ // Release structure
+ SafeReleaseFileTransfer((void**)&oc.ft);
+ return;
+ }
+ }
+ }
+ else if (oc.type == OCT_PROXY)
+ { // stage 4
+ if (!CreateOscarProxyConnection(&oc))
+ { // proxy connection failed, we are out of possibilities
+ BroadcastAck(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((void**)&oc.ft);
+ return;
+ }
+ }
+ else if (oc.type == OCT_PROXY_INIT)
+ { // stage 1
+ if (!CreateOscarProxyConnection(&oc))
+ { // We failed to init transfer, notify UI
+ icq_LogMessage(LOG_ERROR, LPGEN("Failed to Initialize File Transfer. Unable to bind local port and File proxy unavailable."));
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oc.ft);
+ return;
+ }
+ else
+ oc.type = OCT_PROXY_INIT;
+ }
+ }
+ if (!oc.hConnection)
+ { // one more sanity check
+ NetLog_Direct("Error: No OFT connection.");
+ return;
+ }
+ if (oc.status != OCS_PROXY)
+ { // Connected, notify FT UI
+ BroadcastAck(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->flags & OFTF_SENDING) && ((oc.ft->flags & OFTF_INITIALIZED) || oc.type == OCT_REVERSE) && !(oc.ft->flags & OFTF_FILE_REQUEST_SENT))
+ {
+ oc.ft->flags |= OFTF_FILE_REQUEST_SENT;
+ // proceed with first file
+ oft_sendPeerInit(&oc);
+ }
+ }
+ 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
+ icq_lock l(oftMutex);
+
+ if (getFileTransferIndex(oc.ft) != -1)
+ oc.ft->connection = NULL; // release link
+ }
+ // Give server some time for abort/cancel to arrive
+ SleepEx(1000, TRUE);
+ // Error handling
+ if (IsValidOscarTransfer(oc.ft))
+ {
+ if (oc.status == OCS_DATA)
+ {
+ BroadcastAck(oc.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+
+ icq_LogMessage(LOG_ERROR, LPGEN("Connection lost during file transfer."));
+ // Release structure
+ SafeReleaseFileTransfer((void**)&oc.ft);
+ }
+ else if (oc.status == OCS_NEGOTIATION)
+ {
+ BroadcastAck(oc.hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc.ft, 0);
+
+ icq_LogMessage(LOG_ERROR, LPGEN("File transfer negotiation failed for unknown reason."));
+ // Release structure
+ SafeReleaseFileTransfer((void**)&oc.ft);
+ }
+ }
+}
+
+
+void CIcqProto::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((void**)&packet->pData);
+}
+
+
+int CIcqProto::oft_handlePackets(oscar_connection *oc, BYTE *buf, int len)
+{
+ int bytesUsed = 0;
+
+ while (len > 0)
+ {
+ if (oc->status == OCS_DATA && (oc->ft->flags & OFTF_FILE_RECEIVING))
+ {
+ return oft_handleFileData(oc, buf, len);
+ }
+ else if (oc->status == OCS_PROXY)
+ {
+ return oft_handleProxyData(oc, buf, len);
+ }
+ if (len < 6)
+ break;
+
+ BYTE *pBuf = buf;
+ DWORD dwHead;
+ unpackDWord(&pBuf, &dwHead);
+ if (dwHead != 0x4F465432)
+ { // bad packet
+ NetLog_Direct("OFT: Received invalid packet (dwHead = 0x%x).", dwHead);
+
+ CloseOscarConnection(oc);
+ break;
+ }
+
+ WORD datalen;
+ unpackWord(&pBuf, &datalen);
+
+ if (len < datalen) // wait for whole packet
+ break;
+
+ WORD datatype;
+ 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;
+}
+
+
+int CIcqProto::oft_handleProxyData(oscar_connection *oc, BYTE *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
+ BroadcastAck(oc->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, oc->ft, 0);
+ // Release structure
+ SafeReleaseFileTransfer((void**)&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
+ BroadcastAck(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
+ BroadcastAck(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->flags & OFTF_SENDING) // connection is ready for transfer (sending only)
+ ft->flags |= OFTF_INITIALIZED;
+ }
+
+ NetLog_Server("Proxy Tunnel established");
+
+ if ((ft->flags & OFTF_INITIALIZED) && (ft->flags & OFTF_SENDING) && !(ft->flags & OFTF_FILE_REQUEST_SENT))
+ {
+ ft->flags |= OFTF_FILE_REQUEST_SENT;
+ // proceed with first file
+ oft_sendPeerInit(ft->connection);
+ }
+ break;
+
+ default:
+ NetLog_Server("Unknown proxy command 0x%x", wCommand);
+ }
+
+ buf += datalen;
+ len -= datalen;
+ bytesUsed += datalen;
+ }
+
+ return bytesUsed;
+}
+
+
+int CIcqProto::oft_handleFileData(oscar_connection *oc, BYTE *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);
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&pfts);
+ ft->dwLastNotify = GetTickCount();
+ }
+ if (ft->qwFileBytesDone == ft->qwThisFileSize)
+ {
+ /* EOF */
+ ft->flags &= ~OFTF_FILE_RECEIVING;
+
+#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(LPGEN("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.");
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&ft);
+ }
+ else
+ { // ack received file
+ sendOFT2FramePacket(oc, OFT_TYPE_DONE);
+ oc->status = OCS_NEGOTIATION;
+ }
+
+ }
+ return bytesUsed;
+}
+
+
+void CIcqProto::handleOFT2FramePacket(oscar_connection *oc, WORD datatype, BYTE *pBuffer, WORD wLen)
+{
+ oscar_filetransfer *ft = oc->ft;
+ 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 && !(ft->flags & OFTF_FILE_REQUEST_RECEIVED))
+ { // first request does not contain MsgIDs we need to send them in ready packet
+ dwID1 = ft->pMessage.dwMsgID1;
+ dwID2 = ft->pMessage.dwMsgID2;
+ }
+
+ if (ft->pMessage.dwMsgID1 != dwID1 || 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
+ if (ft->flags & OFTF_SENDING)
+ { // just sanity check - this is only for receiving client
+ NetLog_Direct("Error: Invalid Packet, closing.");
+ CloseOscarConnection(oc);
+ return;
+ }
+
+ // Read Frame data
+ if (!(ft->flags & OFTF_FILE_REQUEST_RECEIVED))
+ {
+ unpackWord(&pBuffer, &ft->wEncrypt);
+ unpackWord(&pBuffer, &ft->wCompress);
+ unpackWord(&pBuffer, &ft->wFilesCount);
+ }
+ else
+ pBuffer += 6;
+ unpackWord(&pBuffer, &ft->wFilesLeft);
+ ft->iCurrentFile = ft->wFilesCount - ft->wFilesLeft;
+ if (!(ft->flags & OFTF_FILE_REQUEST_RECEIVED))
+ unpackWord(&pBuffer, &ft->wPartsCount);
+ else
+ pBuffer += 2;
+ unpackWord(&pBuffer, &ft->wPartsLeft);
+ if (!(ft->flags & OFTF_FILE_REQUEST_RECEIVED))
+ { // 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->flags & OFTF_FILE_REQUEST_RECEIVED))
+ unpackString(&pBuffer, ft->rawIDString, 32);
+ else
+ pBuffer += 32;
+ unpackByte(&pBuffer, &ft->bHeaderFlags);
+ unpackByte(&pBuffer, &ft->bNameOff);
+ unpackByte(&pBuffer, &ft->bSizeOff);
+ if (!(ft->flags & OFTF_FILE_REQUEST_RECEIVED))
+ {
+ unpackString(&pBuffer, (char*)ft->rawDummy, 69);
+ unpackString(&pBuffer, (char*)ft->rawMacInfo, 16);
+ }
+ else
+ pBuffer += 85;
+ unpackWord(&pBuffer, &ft->wEncoding);
+ unpackWord(&pBuffer, &ft->wSubEncoding);
+ ft->cbRawFileName = wLen - 176;
+ SAFE_FREE((void**)&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
+ int i;
+
+ for (i = 0; i < strlennull(ft->szThisFile); i++)
+ {
+ if (ft->szThisFile[i] == 0x01) ft->szThisFile[i] = '\\';
+ }
+ }
+
+ ft->flags |= OFTF_FILE_REQUEST_RECEIVED; // 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;
+ }
+ char *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 (BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, ft, (LPARAM)&pfts))
+ {
+ oc->status = OCS_WAITING;
+ break; /* UI supports resume: it will call PS_FILERESUME */
+ }
+
+ 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, 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."));
+
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&oc->ft);
+ return;
+ }
+ }
+ // Send "we are ready"
+ oc->status = OCS_DATA;
+ ft->flags |= OFTF_FILE_RECEIVING;
+
+ sendOFT2FramePacket(oc, OFT_TYPE_READY);
+ BroadcastAck(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
+ oc->status = OCS_DATA;
+ oc->wantIdleTime = 1;
+ ft->flags |= OFTF_FILE_SENDING;
+
+ NetLog_Direct("OFT: Receiver ready.");
+ }
+ break;
+
+ case OFT_TYPE_RESUMEREQUEST:
+ { // Receiver wants to resume file transfer from point
+ DWORD dwResumeCheck, dwResumeOffset, dwFileCheck;
+
+ if (!(ft->flags & OFTF_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
+ DWORD dwResumeOffset, dwResumeCheck;
+
+ if (ft->flags & OFTF_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;
+ if (dwResumeOffset)
+ ft->dwRecvFileCheck = dwResumeCheck;
+ else // Restarted resume (data mismatch)
+ ft->dwRecvFileCheck = 0xFFFF0000;
+ }
+ _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;
+
+ BroadcastAck(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
+ oc->status = OCS_NEGOTIATION;
+ oc->wantIdleTime = 0;
+
+ ft->flags &= ~OFTF_FILE_SENDING;
+
+ 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
+/////////////////////////////
+
+void CIcqProto::proxy_sendInitTunnel(oscar_connection *oc)
+{
+ icq_packet packet;
+ WORD wLen = 39 + getUINLen(m_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, m_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);
+}
+
+void CIcqProto::proxy_sendJoinTunnel(oscar_connection *oc, WORD wPort)
+{
+ icq_packet packet;
+ WORD wLen = 41 + getUINLen(m_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, m_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
+/////////////////////////////
+
+void CIcqProto::oft_sendFileData(oscar_connection *oc)
+{
+ oscar_filetransfer *ft = oc->ft;
+ BYTE buf[OFT_BUFFER_SIZE];
+
+ if (ft->fileId == -1)
+ return;
+
+ int 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;
+ }
+
+ if (bytesRead)
+ {
+ icq_packet packet;
+
+ 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);
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&pfts);
+ ft->dwLastNotify = GetTickCount();
+ }
+}
+
+void CIcqProto::oft_sendPeerInit(oscar_connection *oc)
+{
+ icq_lock l(oftMutex);
+
+ oscar_filetransfer *ft = oc->ft;
+ struct _stati64 statbuf;
+ char *pszThisFileName;
+
+ // prepare init frame
+ if (ft->iCurrentFile >= (int)ft->wFilesCount)
+ { // All files done, great!
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&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, 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."));
+
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&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
+ int i;
+ for (i = 0; i < strlennull(pszThisFileName); i++)
+ if (pszThisFileName[i] == '\\' || pszThisFileName[i] == '/')
+ pszThisFileName[i] = 0x01;
+ }
+
+ BroadcastAck(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((void**)&pszThisFileName);
+ 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."));
+ //
+ BroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0);
+ // Release transfer
+ SafeReleaseFileTransfer((void**)&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((void**)&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, (char*)pszThisFileName);
+ SAFE_FREE((void**)&pszThisFileName);
+ }
+ else
+ {
+ ft->wEncoding = 2; // ucs-2
+ WCHAR *pwsThisFile = make_unicode_string(pszThisFileName);
+ SAFE_FREE((void**)&pszThisFileName);
+ ft->cbRawFileName = strlennull(pwsThisFile) * (int)sizeof(WCHAR) + 2;
+ if (ft->cbRawFileName < 64) ft->cbRawFileName = 64;
+ ft->rawFileName = (char*)SAFE_MALLOC(ft->cbRawFileName);
+ // convert to LE ordered string
+ BYTE *pwsThisFileBuf = (BYTE*)pwsThisFile; // need this - unpackWideString moves the address!
+ unpackWideString(&pwsThisFileBuf, (WCHAR*)ft->rawFileName, (WORD)(strlennull(pwsThisFile) * sizeof(WCHAR)));
+ SAFE_FREE((void**)&pwsThisFile);
+ }
+ ft->wFilesLeft = (WORD)(ft->wFilesCount - ft->iCurrentFile);
+
+ sendOFT2FramePacket(oc, OFT_TYPE_REQUEST);
+}
+
+void CIcqProto::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, (LPBYTE)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, (LPBYTE)ft->rawFileName, ft->cbRawFileName);
+
+ sendOscarPacket(oc, &packet);
+}
|