From cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c Mon Sep 17 00:00:00 2001 From: watcherhd Date: Thu, 21 Apr 2011 14:14:52 +0000 Subject: svn.miranda.im is moving to a new home! git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@7 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- .../protocols/IcqOscarJ/oscar_filetransfer.c | 1211 ++++++++++++++++++++ 1 file changed, 1211 insertions(+) create mode 100644 miranda-wine/protocols/IcqOscarJ/oscar_filetransfer.c (limited to 'miranda-wine/protocols/IcqOscarJ/oscar_filetransfer.c') diff --git a/miranda-wine/protocols/IcqOscarJ/oscar_filetransfer.c b/miranda-wine/protocols/IcqOscarJ/oscar_filetransfer.c new file mode 100644 index 0000000..ce7cbdc --- /dev/null +++ b/miranda-wine/protocols/IcqOscarJ/oscar_filetransfer.c @@ -0,0 +1,1211 @@ +// ---------------------------------------------------------------------------80 +// ICQ plugin for Miranda Instant Messenger +// ________________________________________ +// +// Copyright © 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede +// Copyright © 2001,2002 Jon Keating, Richard Hughes +// Copyright © 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater +// Copyright © 2004,2005,2006 Joe Kucera +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// ----------------------------------------------------------------------------- +// +// File name : $Source: /cvsroot/miranda/miranda/protocols/IcqOscarJ/oscar_filetransfer.c,v $ +// Revision : $Revision: 3720 $ +// Last change on : $Date: 2006-09-07 00:56:27 +0400 (Чтв, 07 Сен 2006) $ +// Last change by : $Author: jokusoftware $ +// +// 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 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 void handleOFT2FramePacket(oscar_connection *oc, WORD datatype, BYTE *pBuffer, WORD wLen); +static void sendOFT2FramePacket(oscar_connection *oc, WORD datatype); + +void oft_sendPeerInit(oscar_connection *oc); + +static CRITICAL_SECTION oftMutex; +static int oftTransferCount = 0; +static oscar_filetransfer** oftTransferList = NULL; + +static oscar_filetransfer* CreateOscarTransfer(); +static void SafeReleaseOscarTransfer(oscar_filetransfer *ft); +static int IsValidOscarTransfer(void *ft); +static oscar_filetransfer* FindOscarTransfer(HANDLE hContact, DWORD dwID1, DWORD dwID2); + + +// +// Utility functions +///////////////////////////// + + +static oscar_filetransfer* CreateOscarTransfer() +{ + oscar_filetransfer* ft = (oscar_filetransfer*)SAFE_MALLOC(sizeof(oscar_filetransfer)); + + EnterCriticalSection(&oftMutex); + + oftTransferList = (oscar_filetransfer**)realloc(oftTransferList, sizeof(oscar_filetransfer*)*(oftTransferCount + 1)); + oftTransferList[oftTransferCount++] = ft; + + LeaveCriticalSection(&oftMutex); + + return ft; +} + + + +static void SafeReleaseOscarTransfer(oscar_filetransfer *ft) +{ + int i; + + EnterCriticalSection(&oftMutex); + + for (i = 0; i < oftTransferCount; i++) + { + if (oftTransferList[i] == ft) + { + oftTransferCount--; + SafeReleaseFileTransfer(&oftTransferList[i]); + oftTransferList[i] = oftTransferList[oftTransferCount]; + oftTransferList = (oscar_filetransfer**)realloc(oftTransferList, sizeof(oscar_filetransfer*)*oftTransferCount); + break; + } + } + + LeaveCriticalSection(&oftMutex); +} + + + +static int IsValidOscarTransfer(void *ft) +{ + int i; + + EnterCriticalSection(&oftMutex); + + for (i = 0; i < oftTransferCount; i++) + { + if (oftTransferList[i] == ft) + { + LeaveCriticalSection(&oftMutex); + + return 1; + } + } + + LeaveCriticalSection(&oftMutex); + + return 0; +} + + + +static oscar_filetransfer* FindOscarTransfer(HANDLE hContact, DWORD dwID1, DWORD dwID2) +{ + int i; + + EnterCriticalSection(&oftMutex); + + for (i = 0; i < oftTransferCount; i++) + { + if (oftTransferList[i]->hContact == hContact && oftTransferList[i]->pMessage.dwMsgID1 == dwID1 && oftTransferList[i]->pMessage.dwMsgID2 == dwID2) + { + LeaveCriticalSection(&oftMutex); + + return oftTransferList[i]; + } + } + + LeaveCriticalSection(&oftMutex); + + return NULL; +} + + + +// Release file transfer structure +void SafeReleaseFileTransfer(void **ft) +{ + basic_filetransfer **bft = (basic_filetransfer**)ft; + + 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); + } + SAFE_FREE(ft); + } + else + { // release oscar filetransfer structure and its contents + oscar_filetransfer *oft = (oscar_filetransfer*)(*bft); + // FIX ME: release all dynamic members + if (oft->listener) + ReleaseOscarListener((oscar_listener**)&oft->listener); + SAFE_FREE(&oft->rawFileName); + SAFE_FREE(&oft->szSavePath); + SAFE_FREE(&oft->szThisFile); + if (oft->files) + { + int i; + + for (i = 0; i < oft->wFilesCount; i++) + SAFE_FREE(&oft->files[i]); + SAFE_FREE((char**)&oft->files); + } + SAFE_FREE(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; + 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; +} + + + +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, FALSE); + } + 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) +{ + if (wCommand == 0) + { // this is OFT request + oscar_tlv_chain* chain = readIntoTLVChain(&buf, wLen, 0); + + if (chain) + { + HANDLE hContact = HContactFromUID(dwUin, szUID, NULL); + 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 + + ft->ft_magic = FT_MAGIC_OSCAR; + // 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->dwRemoteExternalIP = getDWordFromChain(chain, 0x03, 1); + ft->dwRemoteInternalIP = getDWordFromChain(chain, 0x04, 1); + ft->dwRemotePort = getWordFromChain(chain, 0x05, 1); + + { // User Message + oscar_tlv* tlv = getTLV(chain, 0x0C, 1); + + if (tlv) + { // parse User Message + BYTE* tBuf = tlv->pData; + + pszDescription = (char*)_alloca(tlv->wLen + 1); + unpackString(&tBuf, pszDescription, tlv->wLen); + pszDescription[tlv->wLen] = '\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); + } + // decode XML tags + str = DemangleXml(str, strlennull(str)); + if (charset) SAFE_FREE(&pszDescription); + pszDescription = str; + + bTag = strstr(str, ""); + if (bTag) + { // take only - Description tag if present + eTag = strstr(bTag, ""); + if (eTag) + { + *eTag = '\0'; + pszDescription = null_strdup(bTag + 4); + SAFE_FREE(&str); + } + } + } + + } + if (!strlennull(pszDescription)) pszDescription = ICQTranslate("No description given"); + } + { // parse File Transfer Info block + oscar_tlv* tlv = getTLV(chain, 0x2711, 1); + BYTE* tBuf = tlv->pData; + WORD tLen = tlv->wLen; + + tBuf += 2; // FT flag + unpackWord(&tBuf, &ft->wFilesCount); + unpackDWord(&tBuf, &ft->dwTotalSize); + tLen -= 8; + // Filename + wFilenameLength = tLen - 1; + pszFileName = (char*)_alloca(tLen); + unpackString(&tBuf, pszFileName, wFilenameLength); + pszFileName[wFilenameLength] = '\0'; + { // apply Filename 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 // needs to be alloced + pszFileName = null_strdup(pszFileName); + } + } + { + CCSDATA ccs; + PROTORECVEVENT pre; + char* szBlob; + int bAdded; + HANDLE hContact = HContactFromUID(dwUin, szUID, &bAdded); + + ft->hContact = hContact; + ft->szFilename = pszFileName; + ft->szDescription = pszDescription; + ft->fileId = -1; + + // Send chain event + szBlob = (char*)_alloca(sizeof(DWORD) + strlennull(pszFileName) + strlennull(pszDescription) + 2); + *(PDWORD)szBlob = (DWORD)ft; + strcpy(szBlob + sizeof(DWORD), pszFileName); + strcpy(szBlob + sizeof(DWORD) + strlennull(pszFileName) + 1, pszDescription); + 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 + } + else if (wAckType == 3) + { // Reverse attempt failed, trying proxy + } + + disposeChain(&chain); + } + else + NetLog_Server("Error: Missing TLV chain in OFT request"); + } + else + { // TODO: handle other commands / cancels + } +} + + + +DWORD oftFileAllow(HANDLE hContact, WPARAM wParam, LPARAM lParam) +{ + oscar_filetransfer* ft = (oscar_filetransfer*)wParam; + + ft->szSavePath = null_strdup((char *)lParam); + + OpenOscarConnection(hContact, ft, ft->bUseProxy ? OCT_PROXY: 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 (ICQGetContactSettingUID(hContact, &dwUin, &szUid)) + return 1; // Invalid contact + + oft_sendFileDeny(dwUin, szUid, ft); + + SafeReleaseOscarTransfer(ft); + + return 0; // Success +} + + + +DWORD oftFileCancel(HANDLE hContact, WPARAM wParam, LPARAM lParam) +{ + oscar_filetransfer* ft = (oscar_filetransfer*)wParam; + DWORD dwUin; + uid_str szUid; + + if (ft->hContact != hContact) + return 1; // Bad contact or hTransfer + + if (ICQGetContactSettingUID(hContact, &dwUin, &szUid)) + return 1; // Invalid contact + + oft_sendFileDeny(dwUin, szUid, ft); + + ICQBroadcastAck(hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft, 0); + + if (ft->connection) + { + CloseOscarConnection((oscar_connection*)ft->connection); + } + // Release structure + SafeReleaseOscarTransfer(ft); + + return 0; // Success +} + + + +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; + + switch (action) + { + case FILERESUME_RESUME: // FIX ME: this needs to request resume!!! + openFlags = _O_BINARY | _O_WRONLY; + break; + + case FILERESUME_OVERWRITE: + openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY; + ft->dwFileBytesDone = 0; + break; + + case FILERESUME_SKIP: + openFlags = _O_BINARY | _O_WRONLY; + ft->dwFileBytesDone = ft->dwThisFileSize; + break; + + case FILERESUME_RENAME: + openFlags = _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY; + SAFE_FREE(&ft->szThisFile); + ft->szThisFile = null_strdup(szFilename); + ft->dwFileBytesDone = 0; + break; + } + + // TODO: unicode support + ft->fileId = _open(ft->szThisFile, openFlags, _S_IREAD | _S_IWRITE); + if (ft->fileId == -1) + { + icq_LogMessage(LOG_ERROR, "Your file receive has been aborted because Miranda could not open the destination file in order to write to it. You may be trying to save to a read-only folder."); + + CloseOscarConnection(oc); + return; + } + + if (action == FILERESUME_RESUME) + ft->dwFileBytesDone = _lseek(ft->fileId, 0, SEEK_END); + else + _lseek(ft->fileId, ft->dwFileBytesDone, SEEK_SET); + + ft->dwBytesDone += ft->dwFileBytesDone; + + // Send "we are ready" + oc->status = OCS_DATA; + + sendOFT2FramePacket(oc, OFT_TYPE_READY); + ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0); + + if (!ft->dwThisFileSize) + { // 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; + else + pfts->files = NULL; /* FIXME */ + pfts->totalFiles = ft->wFilesCount; + pfts->currentFileNumber = ft->iCurrentFile; + pfts->totalBytes = ft->dwTotalSize; + pfts->totalProgress = ft->dwBytesDone; + pfts->workingDir = ft->szSavePath; + pfts->currentFile = ft->szThisFile; + pfts->currentFileSize = ft->dwThisFileSize; + pfts->currentFileTime = ft->dwThisFileDate; + pfts->currentFileProgress = ft->dwFileBytesDone; +} + + + +void CloseOscarConnection(oscar_connection *oc) +{ + EnterCriticalSection(&oftMutex); + + if (oc) + { + oc->type = OCT_CLOSING; + + if (oc->hConnection) + { // we need this for Netlib handle consistency + NetLib_SafeCloseHandle(&oc->hConnection, FALSE); + } + } + LeaveCriticalSection(&oftMutex); +} + + + +void OpenOscarConnection(HANDLE hContact, oscar_filetransfer *ft, int type) +{ + pthread_t tid; + oscarthreadstartinfo *otsi = (oscarthreadstartinfo*)SAFE_MALLOC(sizeof(oscarthreadstartinfo)); + + otsi->hContact = hContact; + otsi->type = type; + otsi->ft = ft; + + tid.hThread = (HANDLE)forkthreadex(NULL, 0, oft_connectionThread, otsi, 0, &tid.dwThreadId); + CloseHandle(tid.hThread); +} + + + +// This function is called from the Netlib when someone is connecting to our oscar_listener +void oft_newConnectionReceived(HANDLE hNewConnection, DWORD dwRemoteIP, void *pExtra) +{ + pthread_t tid; + 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 + tid.hThread = (HANDLE)forkthreadex(NULL, 0, oft_connectionThread, otsi, 0, &tid.dwThreadId); + CloseHandle(tid.hThread); +} + + + +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; + } + else + { // FT is already over, kill listener + NetLog_Direct("Received unexpected connection, closing."); + + CloseOscarConnection(&oc); + ReleaseOscarListener(&source); + + 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) + { // FIX ME: this needs more work for proxy & sending + if (oc.type == OCT_NORMAL) + { // create outgoing connection to peer + NETLIBOPENCONNECTION nloc = {0}; + IN_ADDR addr = {0}; + + nloc.cbSize = sizeof(nloc); + + // FIX ME: Just for testing reverse + 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); + + // 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) + { // 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; + oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, oc.dwLocalInternalIP, listener->wPort, FALSE); +// FIX ME: FT UI should support this +// ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_LISTENING, oc.ft, 0); + } + // FIX ME: try proxy + return 0; + } + nloc.szHost = inet_ntoa(addr); + nloc.wPort = (WORD)oc.ft->dwRemotePort; + NetLog_Direct("%sConnecting to %s:%u", oc.type==OCT_REVERSE?"Reverse ":"", nloc.szHost, nloc.wPort); + + oc.hConnection = NetLib_OpenConnection(ghDirectNetlibUser, &nloc); + if (!oc.hConnection) + { // 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; + oft_sendFileRedirect(oc.dwUin, oc.szUid, oc.ft, oc.dwLocalInternalIP, listener->wPort, FALSE); +// FIX ME: FT UI should support this +// ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_LISTENING, oc.ft, 0); + } + // FIX ME: try proxy + return 0; + } + oc.ft->connection = &oc; + // acknowledge OFT - connection is ready + oft_sendFileAccept(oc.dwUin, oc.szUid, oc.ft); + } + } + if (!oc.hConnection) + { // one more sanity chech + NetLog_Direct("Error: No OFT connection."); + return 0; + } + // Connected, notify FT UI + ICQBroadcastAck(oc.ft->hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, oc.ft, 0); + // Init connection + if (oc.ft->sending) + oft_sendPeerInit(&oc); // only if sending file... + + 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 : 60000; + + 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 should want to send file data packets + } + else + { + NetLog_Direct("Connection timeouted, closing."); + break; + } + } + else + { + 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, FALSE); + + CloseOscarConnection(&oc); + + // FIX ME: Clean up, error handling + if (IsValidOscarTransfer(oc.ft)) + { + oc.ft->connection = NULL; // release link + } + + 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) + { + return oft_handleFileData(oc, buf, len); + } + if (len < 6) + break; + + pBuf = buf; + unpackDWord(&pBuf, &dwHead); + if (dwHead != 0x4F465432) + { // bad packet + 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_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->dwThisFileSize - ft->dwFileBytesDone < dwLen) + dwLen = ft->dwThisFileSize - ft->dwFileBytesDone; + + if (ft->fileId == -1) + { // something went terribly bad + CloseOscarConnection(oc); + return 0; + } + _write(ft->fileId, buf, dwLen); + // update checksum + ft->dwRecvFileCheck = oft_calc_checksum(ft->dwFileBytesDone, buf, dwLen, ft->dwRecvFileCheck); + bytesUsed += dwLen; + ft->dwBytesDone += dwLen; + ft->dwFileBytesDone += dwLen; + + if (GetTickCount() > ft->dwLastNotify + 700 || ft->dwFileBytesDone == ft->dwThisFileSize) + { // 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); + oc->ft->dwLastNotify = GetTickCount(); + } + if (ft->dwFileBytesDone == ft->dwThisFileSize) + { + /* EOF */ + _close(ft->fileId); + ft->fileId = -1; + + if (ft->dwRecvFileCheck != ft->dwThisFileCheck) + { + NetLog_Direct("Error: File checksums does not match!"); + // FIX ME: here we should inform user and end transfer + } + + 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; + CloseOscarConnection(oc); // close connection + NetLog_Direct("File Transfer completed successfully."); + ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft, 0); + // Release structure + SafeReleaseOscarTransfer(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 < 248) + { + 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->initalized) + { // 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; + + // Read Frame data + if (!ft->initalized) + { + 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->initalized) + unpackWord(&pBuffer, &ft->wPartsCount); + else + pBuffer += 2; + unpackWord(&pBuffer, &ft->wPartsLeft); + if (!ft->initalized) + unpackDWord(&pBuffer, &ft->dwTotalSize); + else + pBuffer += 4; + unpackDWord(&pBuffer, &ft->dwThisFileSize); + unpackDWord(&pBuffer, &ft->dwThisFileDate); + unpackDWord(&pBuffer, &ft->dwThisFileCheck); + unpackDWord(&pBuffer, &ft->dwRecvForkCheck); + unpackDWord(&pBuffer, &ft->dwThisForkSize); + unpackDWord(&pBuffer, &ft->dwThisFileCreation); + unpackDWord(&pBuffer, &ft->dwThisForkCheck); + unpackDWord(&pBuffer, &ft->dwBytesDone); + unpackDWord(&pBuffer, &ft->dwRecvFileCheck); + if (!ft->initalized) + unpackString(&pBuffer, ft->rawIDString, 32); + else + pBuffer += 32; + unpackByte(&pBuffer, &ft->bHeaderFlags); + unpackByte(&pBuffer, &ft->bNameOff); + unpackByte(&pBuffer, &ft->bSizeOff); + if (!ft->initalized) + { + unpackString(&pBuffer, ft->rawDummy, 69); + unpackString(&pBuffer, ft->rawMacInfo, 16); + } + else + pBuffer += 85; + unpackWord(&pBuffer, &ft->wEncoding); + unpackWord(&pBuffer, &ft->wSubEncoding); + ft->cbRawFileName = wLen - 184; + SAFE_FREE(&ft->rawFileName); // release previous buffers + SAFE_FREE(&ft->szThisFile); + ft->rawFileName = (char*)SAFE_MALLOC(ft->cbRawFileName); + unpackString(&pBuffer, ft->rawFileName, ft->cbRawFileName); + // Prepare file + if (ft->wEncoding == 2) + { // FIX ME: this is bad + ft->szThisFile = ApplyEncoding(ft->rawFileName, "unicode-2-0"); + // FIX ME: need to convert dir markings + } + else + { + DWORD i; + + ft->szThisFile = null_strdup(ft->rawFileName); + + for (i = 0; i < strlennull(ft->szThisFile); i++) + { // convert dir markings to normal backslashes + if (ft->szThisFile[i] == 0x01) ft->szThisFile[i] = '\\'; + } + } + + ft->initalized = 1; // First Frame Processed + + NetLog_Direct("File '%s', %d Bytes", ft->szThisFile, ft->dwThisFileSize); + + { // Prepare Path Information + char* szFile = strrchr(ft->szThisFile, '\\'); + + SAFE_FREE(&ft->szThisSubdir); // release previous path + if (szFile) + { + char *szNewDir; + + ft->szThisSubdir = ft->szThisFile; + szFile[0] = '\0'; // split that strings + ft->szThisFile = null_strdup(szFile + 1); + // no cheating with paths + if (strstr(ft->szThisSubdir, "..\\") || strstr(ft->szThisSubdir, "../") || + strstr(ft->szThisSubdir, ":\\") || strstr(ft->szThisSubdir, ":/") || + ft->szThisSubdir[0] == '\\' || ft->szThisSubdir[0] == '/') + { + NetLog_Direct("Invalid path information"); + break; + } + szNewDir = (char*)_alloca(strlennull(ft->szSavePath)+strlennull(ft->szThisSubdir)+2); + strcpy(szNewDir, ft->szSavePath); + NormalizeBackslash(szNewDir); + strcat(szNewDir, ft->szThisSubdir); + _mkdir(szNewDir); // FIX ME: this will fail for multi sub-sub-sub-dirs at once + } + else + ft->szThisSubdir = null_strdup(""); + } + + /* no cheating with paths */ + if (strstr(ft->szThisFile, "..\\") || strstr(ft->szThisFile, "../") || + strstr(ft->szThisFile, ":\\") || strstr(ft->szThisFile, ":/") || + ft->szThisFile[0] == '\\' || ft->szThisFile[0] == '/') + { + NetLog_Direct("Invalid path information"); + break; + } + szFullPath = (char*)SAFE_MALLOC(strlennull(ft->szSavePath)+strlennull(ft->szThisSubdir)+strlennull(ft->szThisFile)+3); + strcpy(szFullPath, ft->szSavePath); + NormalizeBackslash(szFullPath); + strcat(szFullPath, ft->szThisSubdir); + NormalizeBackslash(szFullPath); + _chdir(szFullPath); // set current dir - not very useful + strcat(szFullPath, ft->szThisFile); + // we joined the full path to dest file + SAFE_FREE(&ft->szThisFile); + ft->szThisFile = szFullPath; + + ft->dwFileBytesDone = 0; + + { + /* file resume */ + PROTOFILETRANSFERSTATUS pfts = {0}; + + oft_buildProtoFileTransferStatus(ft, &pfts); + if (ICQBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_FILERESUME, ft, (LPARAM)&pfts)) + break; /* UI supports resume: it will call PS_FILERESUME */ + + // TODO: Unicode support + ft->fileId = _open(ft->szThisFile, _O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY, _S_IREAD | _S_IWRITE); + if (ft->fileId == -1) + { // FIX ME: this needs some UI interaction + 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."); + CloseOscarConnection(oc); + 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->dwThisFileSize) + { // if the file is empty we will not receive any data + BYTE buf; + + oft_handleFileData(oc, &buf, 0); + } + return; + } + } +} + + +// +// Direct packets +///////////////////////////// + +static void oft_sendPeerInit(oscar_connection *oc) +{ + // prepare init frame + + // 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, ft->dwTotalSize); + packDWord(&packet, ft->dwThisFileSize); + 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, ft->dwBytesDone); + 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); +} \ No newline at end of file -- cgit v1.2.3