// ---------------------------------------------------------------------------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
// Copyright � 2012-2017 Miranda NG Team
// 
// 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.
// -----------------------------------------------------------------------------
//  DESCRIPTION:
//
//  Handles packets from Location family
// -----------------------------------------------------------------------------

#include "stdafx.h"

extern const char* cliSpamBot;

void CIcqProto::handleLocationFam(BYTE *pBuffer, size_t wBufferLength, snac_header *pSnacHeader)
{
	switch (pSnacHeader->wSubtype) {
	case ICQ_LOCATION_RIGHTS_REPLY: // Reply to CLI_REQLOCATION
		debugLogA("Server sent SNAC(x02,x03) - SRV_LOCATION_RIGHTS_REPLY");
		break;

	case ICQ_LOCATION_USR_INFO_REPLY: // AIM user info reply
		handleLocationUserInfoReply(pBuffer, wBufferLength, pSnacHeader->dwRef);
		break;

	case ICQ_ERROR:
		{ 
			WORD wError;
			if (wBufferLength >= 2)
				unpackWord(&pBuffer, &wError);
			else 
				wError = 0;

			if (wError == 4) {
				MCONTACT hCookieContact;
				cookie_fam15_data *pCookieData;
				if (FindCookie(pSnacHeader->dwRef, &hCookieContact, (void**)&pCookieData) && !getContactUin(hCookieContact) && pCookieData->bRequestType == REQUESTTYPE_PROFILE) {
					ProtoBroadcastAck(hCookieContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1, 0);
					ReleaseCookie(pSnacHeader->dwRef);
				}
			}

			LogFamilyError(ICQ_LOCATION_FAMILY, wError);
			break;
		}

	default:
		debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LOCATION_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef);
		break;
	}
}

static char* AimApplyEncoding(char* pszStr, const char* pszEncoding)
{
	// decode encoding to ANSI only
	if (pszStr && pszEncoding) {
		const char *szEnc = strstrnull(pszEncoding, "charset=");
		if (szEnc) { // decode custom encoding to Utf-8
			char *szStr = ApplyEncoding(pszStr, szEnc + 9);
			// decode utf-8 to ansi
			char *szRes = nullptr;

			SAFE_FREE((void**)&pszStr);
			if (!utf8_decode(szStr, &szRes))
				szRes = _strdup(szStr);		// Legacy fix
			SAFE_FREE((void**)&szStr);

			return szRes;
		}
	}
	return pszStr;
}

void CIcqProto::handleLocationUserInfoReply(BYTE* buf, size_t wLen, DWORD dwCookie)
{
	DWORD dwUIN;
	uid_str szUID;
	WORD wTLVCount;
	WORD wWarningLevel;
	WORD status;
	cookie_message_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
	MCONTACT hContact = HContactFromUID(dwUIN, szUID, nullptr);

	// Ignore away status if the user is not already on our list
	if (hContact == INVALID_CONTACT_ID) {
		debugLogA("Ignoring away reply (%s)", strUID(dwUIN, szUID));
		return;
	}

	MCONTACT hCookieContact;
	if (!FindCookie(dwCookie, &hCookieContact, (void**)&pCookieData)) {
		debugLogA("Error: Received unexpected away reply from %s", strUID(dwUIN, szUID));
		return;
	}

	if (hContact != hCookieContact) {
		debugLogA("Error: Away reply Contact does not match Cookie Contact(0x%x != 0x%x)", hContact, hCookieContact);

		ReleaseCookie(dwCookie); // This could be a bad idea, but I think it is safe
		return;
	}

	switch (GetCookieType(dwCookie)) {
	case CKT_FAMILYSPECIAL:
		ReleaseCookie(dwCookie);
		{
			// Syntax check
			if (wLen < 4)
				return;

			char *szMsg = nullptr;
			BYTE *tmp = buf;

			// Get general chain
			oscar_tlv_chain* pChain;
			if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount)))
				return;

			disposeChain(&pChain);

			wLen -= (buf - tmp);

			// Get extra chain
			if (pChain = readIntoTLVChain(&buf, wLen, 2)) {
				char *szEncoding = nullptr;

				// Get Profile encoding TLV
				oscar_tlv *pTLV = pChain->getTLV(0x05, 1);
				if (pTLV && pTLV->wLen > 0) // store client capabilities
					db_set_blob(hContact, m_szModuleName, "CapBuf", pTLV->pData, pTLV->wLen);
				else
					delSetting(hContact, "CapBuf");

				pTLV = pChain->getTLV(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 = pChain->getTLV(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);
			}

			setString(hContact, "About", szMsg);
			ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1 ,0);

			SAFE_FREE((void**)&szMsg);
		}
		break;

	default: // away message
		status = AwayMsgTypeToStatus(pCookieData->nAckType);
		if (status == ID_STATUS_OFFLINE) {
			debugLogA("SNAC(2.6) Ignoring unknown status message from %s", strUID(dwUIN, szUID));

			ReleaseCookie(dwCookie);
			return;
		}

		ReleaseCookie(dwCookie);

		// Syntax check
		if (wLen < 4)
			return;

		BYTE *tmp = buf;

		// Get general chain
		oscar_tlv_chain* pChain;
		if (!(pChain = readIntoTLVChain(&buf, wLen, wTLVCount)))
			return;

		disposeChain(&pChain);

		wLen -= (buf - tmp);

		// Get extra chain
		char *szMsg = nullptr;
		if (pChain = readIntoTLVChain(&buf, wLen, 2)) {
			char *szEncoding = nullptr;

			// Get Away encoding TLV
			oscar_tlv *pTLV = pChain->getTLV(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 = pChain->getTLV(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);
		}

		PROTORECVEVENT pre = { 0 };
		pre.szMessage = szMsg ? szMsg : (char *)"";
		pre.timestamp = time(nullptr);
		pre.lParam = dwCookie;
		ProtoChainRecv(hContact, PSR_AWAYMSG, status, (LPARAM)&pre);

		SAFE_FREE((void**)&szMsg);
	}
}