// ---------------------------------------------------------------------------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/fam_02location.c,v $ // Revision : $Revision: 2874 $ // Last change on : $Date: 2006-05-16 23:38:00 +0200 (Tue, 16 May 2006) $ // Last change by : $Author: ghazan $ // // DESCRIPTION: // // Describe me here please... // // ----------------------------------------------------------------------------- #include "icqoscar.h" static void handleLocationAwayReply(BYTE* buf, WORD wLen, DWORD dwCookie); void handleLocationFam(unsigned char *pBuffer, WORD wBufferLength, snac_header* pSnacHeader) { switch (pSnacHeader->wSubtype) { case ICQ_LOCATION_RIGHTS_REPLY: // Reply to CLI_REQLOCATION NetLog_Server("Server sent SNAC(x02,x03) - SRV_LOCATION_RIGHTS_REPLY"); break; case ICQ_LOCATION_USR_INFO_REPLY: // AIM user info reply handleLocationAwayReply(pBuffer, wBufferLength, pSnacHeader->dwRef); break; case ICQ_ERROR: { WORD wError; DWORD dwCookieUin; fam15_cookie_data *pCookieData; if (wBufferLength >= 2) { unpackWord(&pBuffer, &wError); LogFamilyError(ICQ_LOCATION_FAMILY, wError); if (pSnacHeader->dwRef >= 2 && pSnacHeader->dwRef <= 0xFFFF) icq_SetUserStatus(0, (WORD)(pSnacHeader->dwRef), -5); } else wError = 0; if (wError == 4) { if (FindCookie(pSnacHeader->dwRef, &dwCookieUin, &pCookieData) && !dwCookieUin && pCookieData->bRequestType == REQUESTTYPE_PROFILE) { ICQBroadcastAck(pCookieData->hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0); ReleaseCookie(pSnacHeader->dwRef); } } LogFamilyError(ICQ_LOCATION_FAMILY, wError); break; } default: NetLog_Server("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LOCATION_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef); break; } } static void handleLocationUserOnlineInfo(HANDLE hContact, BYTE *buf, WORD wLen, DWORD dwCookie) { oscar_tlv_chain *pChain; oscar_tlv* pTLV; DWORD dwPort; DWORD dwRealIP; DWORD dwIP; DWORD dwDirectConnCookie; WORD wVersion; WORD wStatusFlags; WORD wStatus; BYTE nTCPFlag; DWORD dwOnlineSince = 0; WORD wIdleTimer = 0; DWORD dwFP1 = 0; DWORD dwFP2 = 0; DWORD dwFP3 = 0; DWORD dwBuild = 0; WORD wOldStatus, wNewStatus; BOOL Hidden = 0; #ifdef _DEBUG Netlib_Logf(ghServerNetlibUser, "Server sent SNAC(x02,x06) - SRV_USER_ONLINE_INFO"); #endif // Get chain if (!(pChain = readIntoTLVChain(&buf, wLen, 0))) return; // Get DC info TLV pTLV = getTLV(pChain, 0x0C, 1); if (pTLV && (pTLV->wLen >= 15)) { unsigned char* pBuffer; pBuffer = pTLV->pData; unpackDWord(&pBuffer, &dwRealIP); unpackDWord(&pBuffer, &dwPort); unpackByte(&pBuffer, &nTCPFlag); unpackWord(&pBuffer, &wVersion); unpackDWord(&pBuffer, &dwDirectConnCookie); pBuffer += 4; // Web front port pBuffer += 4; // Client features unpackDWord(&pBuffer, &dwFP1); unpackDWord(&pBuffer, &dwFP2); unpackDWord(&pBuffer, &dwFP3); } else { // This client doesnt want DCs dwRealIP = 0; dwPort = 0; nTCPFlag = 0; wVersion = 0; dwDirectConnCookie = 0; } // Get Status info TLV pTLV = getTLV(pChain, 0x06, 1); if (pTLV && (pTLV->wLen >= 4)) { unsigned char* pBuffer; pBuffer = pTLV->pData; unpackWord(&pBuffer, &wStatusFlags); unpackWord(&pBuffer, &wStatus); } else { // Huh? No status TLV? Lets guess then... wStatusFlags = 0; wStatus = ICQ_STATUS_ONLINE; } #ifdef _DEBUG Netlib_Logf(ghServerNetlibUser, "Flags are %x", wStatusFlags); Netlib_Logf(ghServerNetlibUser, "Status is %x", wStatus); #endif // Get IP TLV dwIP = getDWordFromChain(pChain, 0x0a, 1); // Get Online Since TLV { int len; int i = 1; while(len = getLenFromChain(pChain, 0x03, i)) { if (len == 4) { dwOnlineSince = getDWordFromChain(pChain, 0x03, i); break; } else i++; } } // Get Idle timer TLV wIdleTimer = getWordFromChain(pChain, 0x04, 1); #ifdef _DEBUG if (wIdleTimer) Netlib_Logf(ghServerNetlibUser, "Idle timer is %u.", wIdleTimer); // Netlib_Logf(ghServerNetlibUser, "Online since %s", asctime(localtime(&dwOnlineSince))); #endif wOldStatus = DBGetContactSettingWord(hContact, gpszICQProtoName, "Status", ID_STATUS_OFFLINE); wNewStatus = IcqStatusToMiranda(wStatus); if (wOldStatus != ID_STATUS_OFFLINE && CheckContactCapabilities(hContact, WAS_FOUND)) Hidden = 1; if (dwIP) DBWriteContactSettingDword(hContact, gpszICQProtoName, "IP", dwIP); if (dwRealIP) DBWriteContactSettingDword(hContact, gpszICQProtoName, "RealIP", dwRealIP); if (dwPort) DBWriteContactSettingWord(hContact, gpszICQProtoName, "UserPort", (WORD)(dwPort & 0xffff)); DBWriteContactSettingDword(hContact, gpszICQProtoName, "LogonTS", dwOnlineSince); DBWriteContactSettingDword(hContact, gpszICQProtoName, "DirectCookie", dwDirectConnCookie); DBWriteContactSettingWord(hContact, gpszICQProtoName, "Version", wVersion); if (!wIdleTimer) { DWORD dw = DBGetContactSettingDword(hContact, gpszICQProtoName, "IdleTS", 0); DBWriteContactSettingDword(hContact, gpszICQProtoName, "OldIdleTS", dw); } DBWriteContactSettingDword(hContact, gpszICQProtoName, "IdleTS", (wIdleTimer)?((DWORD)time(NULL) - wIdleTimer * 60):0); // Free TLV chain disposeChain(&pChain); // make some checks if (dwCookie >= 2 && dwCookie <= 0xFFFF) icq_SetUserStatus(0, (WORD)dwCookie, 5); // hack: restore hidden stuff here if (Hidden) { ClearContactCapabilities(hContact, CAPF_SRV_RELAY); // for compability SetContactCapabilities(hContact, WAS_FOUND); // dim icon if (!DBGetContactSettingDword(hContact, gpszICQProtoName, "IdleTS", 0)) DBWriteContactSettingDword(hContact, gpszICQProtoName, "IdleTS", (DWORD)time(NULL)); } // write new status after all other info if (wOldStatus != wNewStatus) DBWriteContactSettingWord(hContact, gpszICQProtoName, "Status", wNewStatus); } static char* AimApplyEncoding(char* pszStr, const char* pszEncoding) { // decode encoding to ANSI only if (pszStr && pszEncoding) { char *szEnc = strstr(pszEncoding, "charset="); if (szEnc) { szEnc = szEnc + 9; // get charset string if (!strnicmp(szEnc, "utf-8", 5)) { // it is utf-8 encoded char *szRes = NULL; utf8_decode(pszStr, &szRes); SAFE_FREE(&pszStr); return szRes; } if (!strnicmp(szEnc, "unicode-2-0", 11)) { // it is UCS-2 encoded int wLen = wcslen((wchar_t*)pszStr) + 1; wchar_t *szStr = (wchar_t*)_alloca(wLen*2); char *szRes = (char*)SAFE_MALLOC(wLen); char *tmp = pszStr; unpackWideString(&tmp, szStr, (WORD)(wLen*2)); WideCharToMultiByte(CP_ACP, 0, szStr, -1, szRes, wLen, NULL, NULL); SAFE_FREE(&pszStr); return szRes; } } } return pszStr; } void handleLocationAwayReply(BYTE* buf, WORD wLen, DWORD dwCookie) { HANDLE hContact; DWORD dwUIN; uid_str szUID; WORD wTLVCount; WORD wWarningLevel; DWORD dwCookieUin; WORD status; message_cookie_data *pCookieData; // Unpack the sender's user ID if (!unpackUID(&buf, &wLen, &dwUIN, &szUID)) return; // Syntax check if (wLen < 4) return; // Warning level? unpackWord(&buf, &wWarningLevel); wLen -= 2; // TLV count unpackWord(&buf, &wTLVCount); wLen -= 2; // Determine contact hContact = HContactFromUID(dwUIN, szUID, NULL); // Ignore away status if the user is not already on our list if (hContact == INVALID_HANDLE_VALUE) { #ifdef _DEBUG NetLog_Server("Ignoring away reply (%s)", strUID(dwUIN, szUID)); #endif return; } if (!FindCookie(dwCookie, &dwCookieUin, &pCookieData)) { NetLog_Server("Error: Received unexpected away reply from %s", strUID(dwUIN, szUID)); //handleLocationUserOnlineInfo(hContact, buf, wLen, dwCookie); return; } if (dwUIN != dwCookieUin) { NetLog_Server("Error: Away reply UIN does not match Cookie UIN(%u != %u)", dwUIN, dwCookieUin); ReleaseCookie(dwCookie); // This could be a bad idea, but I think it is safe return; } if (GetCookieType(dwCookie) == CKT_FAMILYSPECIAL) { ReleaseCookie(dwCookie); // Read user info TLVs { oscar_tlv_chain* pChain; oscar_tlv* pTLV; BYTE *tmp; char *szMsg = NULL; // Syntax check if (wLen < 4) return; tmp = buf; // Get general chain if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount))) return; disposeChain(&pChain); wLen -= (buf - tmp); // Get extra chain if (pChain = readIntoTLVChain(&buf, wLen, 2)) { char* szEncoding = NULL; // Get Profile encoding TLV pTLV = getTLV(pChain, 0x01, 1); if (pTLV && (pTLV->wLen >= 1)) { szEncoding = (char*)_alloca(pTLV->wLen + 1); memcpy(szEncoding, pTLV->pData, pTLV->wLen); szEncoding[pTLV->wLen] = '\0'; } // Get Profile info TLV pTLV = getTLV(pChain, 0x02, 1); if (pTLV && (pTLV->wLen >= 1)) { szMsg = (char*)SAFE_MALLOC(pTLV->wLen + 2); memcpy(szMsg, pTLV->pData, pTLV->wLen); szMsg[pTLV->wLen] = '\0'; szMsg[pTLV->wLen + 1] = '\0'; szMsg = AimApplyEncoding(szMsg, szEncoding); szMsg = EliminateHtml(szMsg, pTLV->wLen); } // Free TLV chain disposeChain(&pChain); } ICQWriteContactSettingString(hContact, "About", szMsg); ICQBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1 ,0); SAFE_FREE(&szMsg); } } else { status = AwayMsgTypeToStatus(pCookieData->nAckType); if (status == ID_STATUS_OFFLINE) { NetLog_Server("SNAC(2.6) Ignoring unknown status message from %s", strUID(dwUIN, szUID)); ReleaseCookie(dwCookie); return; } ReleaseCookie(dwCookie); // Read user info TLVs { oscar_tlv_chain* pChain; oscar_tlv* pTLV; BYTE *tmp; char *szMsg = NULL; CCSDATA ccs; PROTORECVEVENT pre; // Syntax check if (wLen < 4) return; tmp = buf; // Get general chain if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount))) return; disposeChain(&pChain); wLen -= (buf - tmp); // Get extra chain if (pChain = readIntoTLVChain(&buf, wLen, 2)) { char* szEncoding = NULL; // Get Away encoding TLV pTLV = getTLV(pChain, 0x03, 1); if (pTLV && (pTLV->wLen >= 1)) { szEncoding = (char*)_alloca(pTLV->wLen + 1); memcpy(szEncoding, pTLV->pData, pTLV->wLen); szEncoding[pTLV->wLen] = '\0'; } // Get Away info TLV pTLV = getTLV(pChain, 0x04, 1); if (pTLV && (pTLV->wLen >= 1)) { szMsg = (char*)SAFE_MALLOC(pTLV->wLen + 2); memcpy(szMsg, pTLV->pData, pTLV->wLen); szMsg[pTLV->wLen] = '\0'; szMsg[pTLV->wLen + 1] = '\0'; szMsg = AimApplyEncoding(szMsg, szEncoding); szMsg = EliminateHtml(szMsg, pTLV->wLen); } // Free TLV chain disposeChain(&pChain); } ccs.szProtoService = PSR_AWAYMSG; ccs.hContact = hContact; ccs.wParam = status; ccs.lParam = (LPARAM)⪯ pre.flags = 0; pre.szMessage = szMsg?szMsg:""; pre.timestamp = time(NULL); pre.lParam = dwCookie; CallService(MS_PROTO_CHAINRECV,0,(LPARAM)&ccs); SAFE_FREE(&szMsg); } } }