Example #1
0
void CIcqProto::handleLookupFam(BYTE *pBuffer, size_t wBufferLength, snac_header* pSnacHeader)
{
    switch (pSnacHeader->wSubtype) {

    case ICQ_LOOKUP_EMAIL_REPLY: // AIM search reply
        handleLookupEmailReply(pBuffer, wBufferLength, pSnacHeader->dwRef);
        break;

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

        cookie_search *pCookie;
        if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&pCookie)) {
            if (wError == 0x14)
                debugLogA("Lookup: No results");

            ReleaseLookupCookie(pSnacHeader->dwRef, pCookie);

            if (wError == 0x14) return;
        }

        LogFamilyError(ICQ_LOOKUP_FAMILY, wError);
    }
    break;

    default:
        debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LOOKUP_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef);
        break;
    }
}
Example #2
0
Cookie *CookieManager::CreateCookie(const char *name, const char *description, CookieAccess access)
{
	Cookie *pCookie = FindCookie(name);

	/* Check if cookie already exists */
	if (pCookie != NULL)
	{
		/* Update data fields to the provided values */
		UTIL_strncpy(pCookie->description, description, MAX_DESC_LENGTH);
		pCookie->access = access;

		return pCookie;
	}

	/* First time cookie - Create from scratch */
	pCookie = new Cookie(name, description, access);
	
	/* Attempt to insert cookie into the db and get its ID num */
	TQueryOp *op = new TQueryOp(Query_InsertCookie, pCookie);
	op->m_params.cookie = pCookie;
	
	cookieFinder.insert(name, pCookie);
	cookieList.append(pCookie);

	g_ClientPrefs.AddQueryToQueue(op);

	return pCookie;
}
Example #3
0
void CookieManager::ClientConnectCallback(int serial, IQuery *data)
{
	int client;

	/* Check validity of client */
	if ((client = playerhelpers->GetClientFromSerial(serial)) == 0)
	{
		return;
	}
	statsPending[client] = false;
	
	IResultSet *results;
	/* Check validity of results */
	if (data == NULL || (results = data->GetResultSet()) == NULL)
	{
		return;
	}

	CookieData *pData;
	IResultRow *row;
	unsigned int timestamp;
	CookieAccess access;
	
	while (results->MoreRows() && ((row = results->FetchRow()) != NULL))
	{
		const char *name = "";
		row->GetString(0, &name, NULL);
		
		const char *value = "";
		row->GetString(1, &value, NULL);

		pData = new CookieData(value);
		pData->changed = false;

		pData->timestamp = (row->GetInt(4, (int *)&timestamp) == DBVal_Data) ? timestamp : 0;

		Cookie *parent = FindCookie(name);

		if (parent == NULL)
		{
			const char *desc = "";
			row->GetString(2, &desc, NULL);

			access = CookieAccess_Public;
			row->GetInt(3, (int *)&access);

			parent = CreateCookie(name, desc, access);
		}

		pData->parent = parent;
		parent->data[client] = pData;
		clientData[client].append(pData);
	}

	statsLoaded[client] = true;

	cookieDataLoadedForward->PushCell(client);
	cookieDataLoadedForward->Execute(NULL);
}
Example #4
0
void CIcqProto::handleLocationFam(BYTE *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
		handleLocationUserInfoReply(pBuffer, wBufferLength, pSnacHeader->dwRef);
		break;

	case ICQ_ERROR:
		{ 
			WORD wError;
			HANDLE hCookieContact;
			cookie_fam15_data *pCookieData;


			if (wBufferLength >= 2)
				unpackWord(&pBuffer, &wError);
			else 
				wError = 0;

			if (wError == 4)
			{
				if (FindCookie(pSnacHeader->dwRef, &hCookieContact, (void**)&pCookieData) && !getContactUin(hCookieContact) && pCookieData->bRequestType == REQUESTTYPE_PROFILE)
				{
					BroadcastAck(hCookieContact, 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;
	}
}
Example #5
0
void CIcqProto::handleLookupEmailReply(BYTE* buf, WORD wLen, DWORD dwCookie)
{
	ICQSEARCHRESULT sr = {0};
	oscar_tlv_chain *pChain;
	cookie_search *pCookie;

	if (!FindCookie(dwCookie, NULL, (void**)&pCookie))
	{
		NetLog_Server("Error: Received unexpected lookup reply");
		return;
	}
	
	NetLog_Server("SNAC(0x0A,0x3): Lookup reply");

	sr.hdr.cbSize = sizeof(sr);
  sr.hdr.flags = PSR_TCHAR;
	sr.hdr.email = ansi_to_tchar(pCookie->szObject);

	// Syntax check, read chain
	if (wLen >= 4 && (pChain = readIntoTLVChain(&buf, wLen, 0)))
	{
		for (WORD i = 1; TRUE; i++)
		{ // collect the results
			char *szUid = pChain->getString(0x01, i);
			if (!szUid) break;
      sr.hdr.id = ansi_to_tchar(szUid);
			sr.hdr.nick = sr.hdr.id;
			// broadcast the result
			if (pCookie->dwMainId)
				BroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)pCookie->dwMainId, (LPARAM)&sr);
			else
				BroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)dwCookie, (LPARAM)&sr);
			SAFE_FREE(&sr.hdr.id);
      SAFE_FREE(&szUid);
		}
		disposeChain(&pChain);
	}
  SAFE_FREE(&sr.hdr.email);

	ReleaseLookupCookie(dwCookie, pCookie);
}
Example #6
0
void CIcqProto::handleDirectMessage(directconnect* dc, PBYTE buf, WORD wLen)
{
	WORD wCommand;
	WORD wCookie;
	BYTE bMsgType,bMsgFlags;
	WORD wStatus;
	WORD wFlags;
	WORD wTextLen;
	char* pszText = NULL;


	// The first part of the packet should always be at least 31 bytes
	if (wLen < 31)
	{
		NetLog_Direct("Error during parsing of DC packet 2 PEER_MSG (too short)");
		return;
	}

	// Skip packet checksum
	buf += 4;
	wLen -= 4;

	// Command:
	//   0x07d0 = 2000 - cancel given message.
	//   0x07da = 2010 - acknowledge message.
	//   0x07ee = 2030 - normal message/request.
	unpackLEWord(&buf, &wCommand);
	wLen -= 2;

	// Unknown, always 0xe (14)
	buf += 2;
	wLen -= 2;

	// Sequence number
	unpackLEWord(&buf, &wCookie);
	wLen -=2;

	// Unknown, always zeroes
	buf += 12;
	wLen -= 12;

	// Peer message type
	unpackByte(&buf, &bMsgType);
	// Peer message flags
	unpackByte(&buf, &bMsgFlags);
	wLen -= 2;

	// The current status of the user, or whether the message was accepted or not.
	//   0x00 - user is online, or message was receipt, or file transfer accepted
	//   0x01 - refused
	//   0x04 - auto-refused, because of away
	//   0x09 - auto-refused, because of occupied
	//   0x0a - auto-refused, because of dnd
	//   0x0e - auto-refused, because of na
	unpackLEWord(&buf, &wStatus);
	wLen -= 2;

	// Flags, or priority
	// Seen: 1 - Chat request
	//       0 - File auto accept (type 3)
	//       33 - priority ?
	unpackLEWord(&buf, &wFlags);
	wLen -= 2;

	// Messagetext. This is either the status message or the actual message
	// when this is a PEER_MSG_MSG packet
	unpackLEWord(&buf, &wTextLen);
	if (wTextLen > 0)
	{
		pszText = (char*)_alloca(wTextLen+1);
		unpackString(&buf, pszText, wTextLen);
		pszText[wTextLen] = '\0';
	}
	wLen = (wLen - 2) - wTextLen;

#ifdef _DEBUG
	NetLog_Direct("Handling PEER_MSG '%s', command %u, cookie %u, messagetype %u, messageflags %u, status %u, flags %u", pszText, wCommand, wCookie, bMsgType, bMsgFlags, wStatus, wFlags);
#else
	NetLog_Direct("Message through direct - UID: %u", dc->dwRemoteUin);
#endif

	// The remaining actual message is handled either as a status message request,
	// a greeting message, a acknowledge or a normal (text, url, file) message
	if (wCommand == DIRECT_MESSAGE)
		switch (bMsgType)
	{
		case MTYPE_FILEREQ: // File inits
			handleFileRequest(buf, wLen, dc->dwRemoteUin, wCookie, 0, 0, pszText, 7, TRUE);
			break;

		case MTYPE_AUTOAWAY:
		case MTYPE_AUTOBUSY:
		case MTYPE_AUTONA:
		case MTYPE_AUTODND:
		case MTYPE_AUTOFFC:
			{
				char **szMsg = MirandaStatusToAwayMsg(AwayMsgTypeToStatus(bMsgType));
				if (szMsg)
					icq_sendAwayMsgReplyDirect(dc, wCookie, bMsgType, ( const char** )szMsg);
			}
			break;

		case MTYPE_PLUGIN: // Greeting
			handleDirectGreetingMessage(dc, buf, wLen, wCommand, wCookie, bMsgType, bMsgFlags, wStatus, wFlags, pszText);
			break;

		default: 
			{
				message_ack_params pMsgAck = {0};
        uid_str szUID;

				buf -= wTextLen;
				wLen += wTextLen;

				pMsgAck.bType = MAT_DIRECT;
				pMsgAck.pDC = dc;
				pMsgAck.wCookie = wCookie;
				pMsgAck.msgType = bMsgType;
				pMsgAck.bFlags = bMsgFlags;
				handleMessageTypes(dc->dwRemoteUin, szUID, time(NULL), 0, 0, wCookie, dc->wVersion, (int)bMsgType, (int)bMsgFlags, 0, (DWORD)wLen, wTextLen, (char*)buf, MTF_DIRECT, &pMsgAck);
				break;
			}
	}
	else if (wCommand == DIRECT_ACK)
	{
		if (bMsgFlags == 3)
		{ // this is status reply
      uid_str szUID;

			buf -= wTextLen;
			wLen += wTextLen;

			handleMessageTypes(dc->dwRemoteUin, szUID, time(NULL), 0, 0, wCookie, dc->wVersion, (int)bMsgType, (int)bMsgFlags, 2, (DWORD)wLen, wTextLen, (char*)buf, MTF_DIRECT, NULL);
		}
		else
		{
			MCONTACT hCookieContact;
			cookie_message_data *pCookieData = NULL;

			if (!FindCookie(wCookie, &hCookieContact, (void**)&pCookieData))
			{
				NetLog_Direct("Received an unexpected direct ack");
			}
			else if (hCookieContact != dc->hContact)
			{
				NetLog_Direct("Direct Contact does not match Cookie Contact(0x%x != 0x%x)", dc->hContact, hCookieContact);
				ReleaseCookie(wCookie); // This could be a bad idea, but I think it is safe
			}
			else
			{ // the ack is correct
				int ackType = -1;

				switch (bMsgType)
				{ 
				case MTYPE_PLAIN:
					ackType = ACKTYPE_MESSAGE;
					break;
				case MTYPE_URL:
					ackType = ACKTYPE_URL;
					break;
				case MTYPE_CONTACTS:
					ackType = ACKTYPE_CONTACTS;
					break;

				case MTYPE_FILEREQ: // File acks
					handleFileAck(buf, wLen, dc->dwRemoteUin, wCookie, wStatus, pszText);
					break;

				case MTYPE_PLUGIN: // Greeting
					handleDirectGreetingMessage(dc, buf, wLen, wCommand, wCookie, bMsgType, bMsgFlags, wStatus, wFlags, pszText);
					break;

				default: 
					NetLog_Direct("Skipped packet from direct connection");
					break;
				}
				if (ackType != -1)
				{ // was a good ack to broadcast ?
					ProtoBroadcastAck(dc->hContact, ackType, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0);
					ReleaseCookie(wCookie);
				}
			}
		}
	}
	else if (wCommand == DIRECT_CANCEL)
	{
		NetLog_Direct("Cannot handle abort messages yet... :(");
	}
	else
		NetLog_Direct("Unknown wCommand, packet skipped");
}
Example #7
0
void CIcqProto::handleDirectGreetingMessage(directconnect* dc, PBYTE buf, WORD wLen, WORD wCommand, WORD wCookie, BYTE bMsgType, BYTE bMsgFlags, WORD wStatus, WORD wFlags, char* pszText)
{
	DWORD dwLengthToEnd;
	DWORD dwDataLength;
	char* pszFileName = NULL;
	int typeId;
	WORD qt;

#ifdef _DEBUG
	NetLog_Direct("Handling PEER_MSG_GREETING, command %u, cookie %u, messagetype %u, messageflags %u, status %u, flags %u", wCommand, wCookie, bMsgType, bMsgFlags, wStatus, wFlags);
#endif

	NetLog_Direct("Parsing Greeting message through direct");

	if (!unpackPluginTypeId(&buf, &wLen, &typeId, &qt, TRUE)) return;

	// Length of remaining data
	unpackLEDWord(&buf, &dwLengthToEnd);
	if (dwLengthToEnd < 4 || dwLengthToEnd > wLen)
	{
		NetLog_Direct("Error: Sanity checking failed (%d) in handleDirectGreetingMessage, datalen %u wLen %u", 2, dwLengthToEnd, wLen);
		return;
	}

	// Length of message/reason
	unpackLEDWord(&buf, &dwDataLength);
	wLen -= 4;
	if (dwDataLength > wLen)
	{
		NetLog_Direct("Error: Sanity checking failed (%d) in handleDirectGreetingMessage, datalen %u wLen %u", 3, dwDataLength, wLen);
		return;
	}

	if (typeId == MTYPE_FILEREQ && wCommand == DIRECT_MESSAGE)
	{
		char* szMsg;

		NetLog_Direct("This is file request");
		szMsg = (char*)_alloca(dwDataLength+1);
		unpackString(&buf, szMsg, (WORD)dwDataLength);
		szMsg[dwDataLength] = '\0';
		wLen = wLen - (WORD)dwDataLength;

		handleFileRequest(buf, wLen, dc->dwRemoteUin, wCookie, 0, 0, szMsg, 8, TRUE);
	}
	else if (typeId == MTYPE_FILEREQ && wCommand == DIRECT_ACK)
	{
		char* szMsg;

		NetLog_Direct("This is file ack");
		szMsg = (char*)_alloca(dwDataLength+1);
		unpackString(&buf, szMsg, (WORD)dwDataLength);
		szMsg[dwDataLength] = '\0';
		wLen = wLen - (WORD)dwDataLength;

		// 50 - file request granted/refused
		handleFileAck(buf, wLen, dc->dwRemoteUin, wCookie, wStatus, szMsg);
	}
	else if (typeId && wCommand == DIRECT_MESSAGE)
	{
    uid_str szUID;
		message_ack_params pMsgAck = {0};

		pMsgAck.bType = MAT_DIRECT;
		pMsgAck.pDC = dc;
		pMsgAck.wCookie = wCookie;
		pMsgAck.msgType = typeId;
		handleMessageTypes(dc->dwRemoteUin, szUID, time(NULL), 0, 0, wCookie, dc->wVersion, typeId, 0, 0, dwLengthToEnd, (WORD)dwDataLength, (char*)buf, MTF_PLUGIN | MTF_DIRECT, &pMsgAck);
	}
	else if (typeId == MTYPE_STATUSMSGEXT && wCommand == DIRECT_ACK)
	{ // especially for icq2003b
		NetLog_Direct("This is extended status reply");

		char *szMsg = (char*)_alloca(dwDataLength+1);
    uid_str szUID;
		unpackString(&buf, szMsg, (WORD)dwDataLength);
		szMsg[dwDataLength] = '\0';

		handleMessageTypes(dc->dwRemoteUin, szUID, time(NULL), 0, 0, wCookie, dc->wVersion, (int)(qt + 0xE7), 3, 2, (DWORD)wLen, (WORD)dwDataLength, szMsg, MTF_PLUGIN | MTF_DIRECT, NULL);
	}
	else if (typeId && wCommand == DIRECT_ACK)
	{
		MCONTACT hCookieContact;
		cookie_message_data *pCookieData = NULL;

		if (!FindCookie(wCookie, &hCookieContact, (void**)&pCookieData))
		{
			NetLog_Direct("Received an unexpected direct ack");
		}
		else if (hCookieContact != dc->hContact)
		{
			NetLog_Direct("Direct Contact does not match Cookie Contact(0x%x != 0x%x)", dc->hContact, hCookieContact);
			ReleaseCookie(wCookie); // This could be a bad idea, but I think it is safe
		}
		else
		{
			int ackType = -1;

			switch (typeId)
			{
			case MTYPE_MESSAGE:
				ackType = ACKTYPE_MESSAGE;
				break;
			case MTYPE_URL:
				ackType = ACKTYPE_URL;
				break;
			case MTYPE_CONTACTS:
				ackType = ACKTYPE_CONTACTS;
				break;
			case MTYPE_SCRIPT_NOTIFY:
				{
					char *szMsg;

					szMsg = (char*)_alloca(dwDataLength + 1);
					if (dwDataLength > 0)
						memcpy(szMsg, buf, dwDataLength);
					szMsg[dwDataLength] = '\0';

					handleXtrazNotifyResponse(dc->dwRemoteUin, dc->hContact, wCookie, szMsg, dwDataLength);
				}
				break;

			default:
				NetLog_Direct("Skipped packet from direct connection");
				break;
			}

			if (ackType != -1)
			{ // was a good ack to broadcast ?
				ProtoBroadcastAck(dc->hContact, ackType, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0);
			}
			// Release cookie
			ReleaseCookie(wCookie);
		}
	}
	else
		NetLog_Direct("Unsupported plugin message type %s", typeId);
}
void CIcqProto::handleFileAck(PBYTE buf, WORD wLen, DWORD dwUin, DWORD dwCookie, WORD wStatus, char* pszText)
{
	char* pszFileName = NULL;
	DWORD dwFileSize;
	HANDLE hCookieContact;
	WORD wPort;
	WORD wFilenameLength;
	filetransfer* ft;


	// Find the filetransfer that belongs to this response
	if (!FindCookie(dwCookie, &hCookieContact, (void**)&ft))
	{
		NetLog_Direct("Error: Received unexpected file transfer request response");
		return;
	}

	FreeCookie(dwCookie);

	if (hCookieContact != HContactFromUIN(dwUin, NULL))
	{
		NetLog_Direct("Error: UINs do not match in file transfer request response");
		return;
	}

	// If status != 0, a request has been denied
	if (wStatus != 0)
	{
		NetLog_Direct("File transfer denied by %u.", dwUin);
		ProtoBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)ft, 0);

		FreeCookie(dwCookie);

		return;
	}

	if (wLen < 6) 
	{ // sanity check
		NetLog_Direct("Ignoring malformed file transfer request response");
		return;
	}

	// Port to connect to
	unpackWord(&buf, &wPort);
	ft->dwRemotePort = wPort;
	wLen -= 2;

	// Unknown
	buf += 2;
	wLen -= 2;

	// Filename
	unpackLEWord(&buf, &wFilenameLength);
	if (wFilenameLength > 0)
	{
		if (wFilenameLength > wLen - 2)
			wFilenameLength = wLen - 2;
		pszFileName = (char*)_alloca(wFilenameLength+1);
		unpackString(&buf, pszFileName, wFilenameLength);
		pszFileName[wFilenameLength] = '\0';
	}
	wLen = wLen - 2 - wFilenameLength;

	if (wLen >= 4)
	{ // Total filesize
		unpackLEDWord(&buf, &dwFileSize);
		wLen -= 4;
	}
	else
		dwFileSize = 0;

	NetLog_Direct("File transfer ack from %u, port %u, name %s, size %u", dwUin, ft->dwRemotePort, pszFileName, dwFileSize);

	ProtoBroadcastAck(ft->hContact, ACKTYPE_FILE, ACKRESULT_CONNECTING, (HANDLE)ft, 0);

	OpenDirectConnection(ft->hContact, DIRECTCONN_FILE, ft);
}
Example #9
0
void CIcqProto::handleDirectoryUpdateResponse(BYTE *databuf, WORD wPacketLen, WORD wCookie, WORD wReplySubtype)
{
	WORD wBytesRemaining = 0;
	snac_header requestSnac = {0};
	BYTE requestResult;

#ifdef _DEBUG
	debugLogA("Received directory update response");
#endif
	if (wPacketLen >= 2)
		unpackLEWord(&databuf, &wBytesRemaining);
	wPacketLen -= 2;
	_ASSERTE(wPacketLen == wBytesRemaining);

	if (!unpackSnacHeader(&requestSnac, &databuf, &wPacketLen) || !requestSnac.bValid)
	{
		debugLogA("Error: Failed to parse directory response");
		return;
	}

	cookie_directory_data *pCookieData;
	MCONTACT hContact;
	// check request cookie
	if (!FindCookie(wCookie, &hContact, (void**)&pCookieData) || !pCookieData)
	{
		debugLogA("Warning: Ignoring unrequested directory reply type (x%x, x%x)", requestSnac.wFamily, requestSnac.wSubtype);
		return;
	}
	/// FIXME: we should really check the snac contents according to cookie data here ?? 

	if (wPacketLen >= 3)
		unpackByte(&databuf, &requestResult);
	else
	{
		debugLogA("Error: Malformed directory response");
		ReleaseCookie(wCookie);
		return;
	}
	if (requestResult != 1 && requestResult != 4)
	{
		debugLogA("Error: Directory request failed, status %u", requestResult);

		if (pCookieData->bRequestType == DIRECTORYREQUEST_UPDATEOWNER)
			ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_FAILED, (HANDLE)wCookie, 0);

		ReleaseCookie(wCookie);
		return;
	}
	WORD wLen;

	unpackWord(&databuf, &wLen);
	wPacketLen -= 3;
	if (wLen)
		debugLogA("Warning: Data in error message present!");

	if (pCookieData->bRequestType == DIRECTORYREQUEST_UPDATEOWNER)
		ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0);
	if (wPacketLen == 0x18)
	{
		DWORD64 qwMetaTime;
		BYTE pbEmptyMetaToken[0x10] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

		unpackQWord(&databuf, &qwMetaTime);
		setSettingBlob(NULL, DBSETTING_METAINFO_TIME, (BYTE*)&qwMetaTime, 8);

		if (memcmp(databuf, pbEmptyMetaToken, 0x10))
			setSettingBlob(NULL, DBSETTING_METAINFO_TOKEN, databuf, 0x10);
	}
	ReleaseCookie(wCookie);
}
Example #10
0
void CIcqProto::handleServiceFam(BYTE *pBuffer, size_t wBufferLength, snac_header *pSnacHeader, serverthread_info *info)
{
	icq_packet packet;

	switch (pSnacHeader->wSubtype) {

	case ICQ_SERVER_READY:
		debugLogA("Server is ready and is requesting my Family versions");
		debugLogA("Sending my Families");

		// This packet is a response to SRV_FAMILIES SNAC(1,3).
		// This tells the server which SNAC families and their corresponding
		// versions which the client understands. This also seems to identify
		// the client as an ICQ vice AIM client to the server.
		// Miranda mimics the behaviour of ICQ 6
		serverPacketInit(&packet, 54);
		packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_FAMILIES);
		packDWord(&packet, 0x00220001);
		packDWord(&packet, 0x00010004);
		packDWord(&packet, 0x00130004);
		packDWord(&packet, 0x00020001);
		packDWord(&packet, 0x00030001);
		packDWord(&packet, 0x00150001);
		packDWord(&packet, 0x00040001);
		packDWord(&packet, 0x00060001);
		packDWord(&packet, 0x00090001);
		packDWord(&packet, 0x000a0001);
		packDWord(&packet, 0x000b0001);
		sendServPacket(&packet);
		break;

	case ICQ_SERVER_FAMILIES2:
		/* This is a reply to CLI_FAMILIES and it tells the client which families and their versions that this server understands.
		* We send a rate request packet */
		debugLogA("Server told me his Family versions");
		debugLogA("Requesting Rate Information");

		serverPacketInit(&packet, 10);
		packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_REQ_RATE_INFO);
		sendServPacket(&packet);
		break;

	case ICQ_SERVER_RATE_INFO:
		debugLogA("Server sent Rate Info");

		/* init rates management */
		m_rates = new rates(this, pBuffer, wBufferLength);

		/* ack rate levels */
		debugLogA("Sending Rate Info Ack");

		m_rates->initAckPacket(&packet);
		sendServPacket(&packet);

		/* CLI_REQINFO - This command requests from the server certain information about the client that is stored on the server. */
		debugLogA("Sending CLI_REQINFO");

		serverPacketInit(&packet, 10);
		packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_REQINFO);
		sendServPacket(&packet);

		if (m_bSsiEnabled) {
			cookie_servlist_action* ack;
			DWORD dwCookie;

			DWORD dwLastUpdate = getDword("SrvLastUpdate", 0);
			WORD wRecordCount = getWord("SrvRecordCount", 0);

			// CLI_REQLISTS - we want to use SSI
			debugLogA("Requesting roster rights");

			serverPacketInit(&packet, 16);
			packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_REQLISTS);
			packTLVWord(&packet, 0x0B, 0x000F); // mimic ICQ 6
			sendServPacket(&packet);

			if (!wRecordCount) { // CLI_REQROSTER
				// we do not have any data - request full list
				debugLogA("Requesting full roster");

				serverPacketInit(&packet, 10);
				ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
				if (ack) { // we try to use standalone cookie if available
					ack->dwAction = SSA_CHECK_ROSTER; // loading list
					dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_CLI_REQUEST, 0, ack);
				}
				else // if not use that old fake
					dwCookie = ICQ_LISTS_CLI_REQUEST << 0x10;

				packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_REQUEST, 0, dwCookie);
				sendServPacket(&packet);
			}
			else { // CLI_CHECKROSTER
				debugLogA("Requesting roster check");

				serverPacketInit(&packet, 16);
				ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
				if (ack)  { // TODO: rewrite - use get list service for empty list
					// we try to use standalone cookie if available
					ack->dwAction = SSA_CHECK_ROSTER; // loading list
					dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_CLI_CHECK, 0, ack);
				}
				else // if not use that old fake
					dwCookie = ICQ_LISTS_CLI_CHECK << 0x10;

				packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_CHECK, 0, dwCookie);
				// check if it was not changed elsewhere (force reload, set that setting to zero)
				if (IsServerGroupsDefined()) {
					packDWord(&packet, dwLastUpdate);  // last saved time
					packWord(&packet, wRecordCount);   // number of records saved
				}
				else { // we need to get groups info into DB, force receive list
					packDWord(&packet, 0);  // last saved time
					packWord(&packet, 0);   // number of records saved
				}
				sendServPacket(&packet);
			}
		}

		// CLI_REQLOCATION
		debugLogA("Requesting Location rights");

		serverPacketInit(&packet, 10);
		packFNACHeader(&packet, ICQ_LOCATION_FAMILY, ICQ_LOCATION_CLI_REQ_RIGHTS);
		sendServPacket(&packet);

		// CLI_REQBUDDY
		debugLogA("Requesting Client-side contactlist rights");

		serverPacketInit(&packet, 16);
		packFNACHeader(&packet, ICQ_BUDDY_FAMILY, ICQ_USER_CLI_REQBUDDY);
		// Query flags: 1 = Enable Avatars
		//              2 = Enable offline status message notification
		//              4 = Enable Avatars for offline contacts
		//              8 = Use reject for not authorized contacts
		packTLVWord(&packet, 0x05, 0x0007);
		sendServPacket(&packet);

		// CLI_REQICBM
		debugLogA("Sending CLI_REQICBM");

		serverPacketInit(&packet, 10);
		packFNACHeader(&packet, ICQ_MSG_FAMILY, ICQ_MSG_CLI_REQICBM);
		sendServPacket(&packet);

		// CLI_REQBOS
		debugLogA("Sending CLI_REQBOS");

		serverPacketInit(&packet, 10);
		packFNACHeader(&packet, ICQ_BOS_FAMILY, ICQ_PRIVACY_REQ_RIGHTS);
		sendServPacket(&packet);
		break;

	case ICQ_SERVER_PAUSE:
		debugLogA("Server is going down in a few seconds... (Flags: %u)", pSnacHeader->wFlags);
		// This is the list of groups that we want to have on the next server
		serverPacketInit(&packet, 30);
		packFNACHeader(&packet, ICQ_SERVICE_FAMILY, ICQ_CLIENT_PAUSE_ACK);
		packWord(&packet, ICQ_SERVICE_FAMILY);
		packWord(&packet, ICQ_LISTS_FAMILY);
		packWord(&packet, ICQ_LOCATION_FAMILY);
		packWord(&packet, ICQ_BUDDY_FAMILY);
		packWord(&packet, ICQ_EXTENSIONS_FAMILY);
		packWord(&packet, ICQ_MSG_FAMILY);
		packWord(&packet, 0x06);
		packWord(&packet, ICQ_BOS_FAMILY);
		packWord(&packet, ICQ_LOOKUP_FAMILY);
		packWord(&packet, ICQ_STATS_FAMILY);
		sendServPacket(&packet);

		debugLogA("Sent server pause ack");
		break;

	case ICQ_SERVER_MIGRATIONREQ:
		debugLogA("Server migration requested (Flags: %u)", pSnacHeader->wFlags);

		pBuffer += 2; // Unknown, seen: 0
		wBufferLength -= 2;
		{
			oscar_tlv_chain *chain = readIntoTLVChain(&pBuffer, wBufferLength, 0);

			if (info->cookieDataLen > 0)
				SAFE_FREE((void**)&info->cookieData);

			info->newServer = chain->getString(0x05, 1);
			info->newServerSSL = chain->getNumber(0x8E, 1);
			info->cookieData = (BYTE*)chain->getString(0x06, 1);
			info->cookieDataLen = chain->getLength(0x06, 1);

			disposeChain(&chain);

			if (!info->newServer || !info->cookieData) {
				icq_LogMessage(LOG_FATAL, LPGEN("A server migration has failed because the server returned invalid data. You must reconnect manually."));
				SAFE_FREE(&info->newServer);
				SAFE_FREE((void**)&info->cookieData);
				info->cookieDataLen = 0;
				info->isNewServerReady = false;
				return;
			}

			debugLogA("Migration has started. New server will be %s", info->newServer);

			m_iDesiredStatus = m_iStatus;
			SetCurrentStatus(ID_STATUS_CONNECTING); // revert to connecting state

			info->isNewServerReady = info->isMigrating = true;
		}
		break;

	case ICQ_SERVER_NAME_INFO: // This is the reply to CLI_REQINFO
		debugLogA("Received self info");
		{
			BYTE bUinLen;
			unpackByte(&pBuffer, &bUinLen);
			pBuffer += bUinLen;
			pBuffer += 4;      /* warning level & user class */
			wBufferLength -= 5 + bUinLen;

			// This is during the login sequence
			if (pSnacHeader->dwRef == ICQ_CLIENT_REQINFO << 0x10) {
				// TLV(x01) User type?
				// TLV(x0C) Empty CLI2CLI Direct connection info
				// TLV(x0A) External IP
				// TLV(x0F) Number of seconds that user has been online
				// TLV(x03) The online since time.
				// TLV(x0A) External IP again
				// TLV(x22) Unknown
				// TLV(x1E) Unknown: empty.
				// TLV(x05) Member of ICQ since.
				// TLV(x14) Unknown
				oscar_tlv_chain *chain = readIntoTLVChain(&pBuffer, wBufferLength, 0);

				// Save external IP
				DWORD dwValue = chain->getDWord(0x0A, 1);
				setDword("IP", dwValue);

				// Save member since timestamp
				dwValue = chain->getDWord(0x05, 1);
				if (dwValue) setDword("MemberTS", dwValue);

				dwValue = chain->getDWord(0x03, 1);
				setDword("LogonTS", dwValue ? dwValue : time(NULL));

				disposeChain(&chain);

				// If we are in SSI mode, this is sent after the list is acked instead
				// to make sure that we don't set status before seing the visibility code
				if (!m_bSsiEnabled || info->isMigrating)
					handleServUINSettings(wListenPort, info);
			}
		}
		break;

	case ICQ_SERVER_RATE_CHANGE:
		if (wBufferLength >= 2) {
			WORD wStatus, wClass;
			DWORD dwLevel;
			// We now have global rate management, although controlled are only some
			// areas. This should not arrive in most cases. If it does, update our
			// local rate levels & issue broadcast.
			unpackWord(&pBuffer, &wStatus);
			unpackWord(&pBuffer, &wClass);
			pBuffer += 20;
			unpackDWord(&pBuffer, &dwLevel);
			{
				mir_cslock l(m_ratesMutex);
				m_rates->updateLevel(wClass, dwLevel);
			}

			if (wStatus == 2 || wStatus == 3) {
				// this is only the simplest solution, needs rate management to every section
				ProtoBroadcastAck(NULL, ICQACKTYPE_RATEWARNING, ACKRESULT_STATUS, (HANDLE)wClass, wStatus);
				if (wStatus == 2)
					debugLogA("Rates #%u: Alert", wClass);
				else
					debugLogA("Rates #%u: Limit", wClass);
			}
			else if (wStatus == 4) {
				ProtoBroadcastAck(NULL, ICQACKTYPE_RATEWARNING, ACKRESULT_STATUS, (HANDLE)wClass, wStatus);
				debugLogA("Rates #%u: Clear", wClass);
			}
		}

		break;

	case ICQ_SERVER_REDIRECT_SERVICE: // reply to family request, got new connection point
	{
		oscar_tlv_chain *pChain = NULL;
		cookie_family_request *pCookieData;

		if (!(pChain = readIntoTLVChain(&pBuffer, wBufferLength, 0))) {
			debugLogA("Received Broken Redirect Service SNAC(1,5).");
			break;
		}

		// pick request data
		WORD wFamily = pChain->getWord(0x0D, 1);
		if ((!FindCookie(pSnacHeader->dwRef, NULL, (void**)&pCookieData)) || (pCookieData->wFamily != wFamily)) {
			disposeChain(&pChain);
			debugLogA("Received unexpected SNAC(1,5), skipping.");
			break;
		}

		FreeCookie(pSnacHeader->dwRef);

		// new family entry point received
		char *pServer = pChain->getString(0x05, 1);
		BYTE bServerSSL = pChain->getNumber(0x8E, 1);
		char *pCookie = pChain->getString(0x06, 1);
		WORD wCookieLen = pChain->getLength(0x06, 1);

		if (!pServer || !pCookie) {
			debugLogA("Server returned invalid data, family unavailable.");

			SAFE_FREE(&pServer);
			SAFE_FREE(&pCookie);
			SAFE_FREE((void**)&pCookieData);
			disposeChain(&pChain);
			break;
		}

		// Get new family server ip and port
		WORD wPort = info->wServerPort; // get default port
		parseServerAddress(pServer, &wPort);

		// establish connection
		NETLIBOPENCONNECTION nloc = { 0 };
		if (m_bGatewayMode)
			nloc.flags |= NLOCF_HTTPGATEWAY;
		nloc.szHost = pServer;
		nloc.wPort = wPort;

		HANDLE hConnection = NetLib_OpenConnection(m_hNetlibUser, wFamily == ICQ_AVATAR_FAMILY ? "Avatar " : NULL, &nloc);

		if (hConnection == NULL)
			debugLogA("Unable to connect to ICQ new family server.");
		// we want the handler to be called even if the connecting failed
		else if (bServerSSL) { /* Start SSL session if requested */
			debugLogA("(%p) Starting SSL negotiation", CallService(MS_NETLIB_GETSOCKET, (WPARAM)hConnection, 0));

			if (!CallService(MS_NETLIB_STARTSSL, (WPARAM)hConnection, 0)) {
				debugLogA("Unable to connect to ICQ new family server, SSL could not be negotiated");
				NetLib_CloseConnection(&hConnection, FALSE);
			}
		}

		(this->*pCookieData->familyHandler)(hConnection, pCookie, wCookieLen);

		// Free allocated memory
		// NOTE: "cookie" will get freed when we have connected to the avatar server.
		disposeChain(&pChain);
		SAFE_FREE(&pServer);
		SAFE_FREE((void**)&pCookieData);
		break;
	}

	case ICQ_SERVER_EXTSTATUS: // our session data
		debugLogA("Received owner session data.");

		while (wBufferLength > 4) { // loop thru all items
			WORD itemType = pBuffer[0] * 0x10 | pBuffer[1];
			BYTE itemFlags = pBuffer[2];
			size_t itemLen = pBuffer[3];

			if (itemType == AVATAR_HASH_PHOTO) { /// TODO: handle photo item
				// skip photo item
				debugLogA("Photo item recognized");
			}
			else if ((itemType == AVATAR_HASH_STATIC || itemType == AVATAR_HASH_FLASH) && (itemLen >= 0x10)) {
				debugLogA("Avatar item recognized");

				if (m_bAvatarsEnabled && !info->bMyAvatarInited) { // signal the server after login
					// this refreshes avatar state - it used to work automatically, but now it does not
					if (getByte("ForceOurAvatar", 0)) { // keep our avatar
						TCHAR *file = GetOwnAvatarFileName();
						SetMyAvatar(0, (LPARAM)file);
						SAFE_FREE(&file);
					}
					else { // only change avatar hash to the same one
						BYTE hash[0x14];
						memcpy(hash, pBuffer, 0x14);
						hash[2] = 1; // update image status
						updateServAvatarHash(hash, 0x14);
					}
					info->bMyAvatarInited = true;
					break;
				}
				// process owner avatar hash changed notification
				handleAvatarOwnerHash(itemFlags, pBuffer, itemLen + 4);
			}
			else if (itemType == 0x02) {
				debugLogA("Status message item recognized");
			}
			else if (itemType == 0x0E) {
				debugLogA("Status mood item recognized");
			}

			// move to next item
			if (wBufferLength >= itemLen + 4) {
				wBufferLength -= itemLen + 4;
				pBuffer += itemLen + 4;
			}
			else {
				pBuffer += wBufferLength;
				wBufferLength = 0;
			}
		}
		break;

	case ICQ_ERROR: // Something went wrong, probably the request for avatar family failed
	{
		WORD wError;
		if (wBufferLength >= 2)
			unpackWord(&pBuffer, &wError);
		else
			wError = 0;

		LogFamilyError(ICQ_SERVICE_FAMILY, wError);
	}
		break;

		// Stuff we don't care about
	case ICQ_SERVER_MOTD:
		debugLogA("Server message of the day");
		break;

	default:
		debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_SERVICE_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef);
		break;
	}
}
Example #11
0
void __cdecl CIcqProto::InfoUpdateThread( void* )
{
	int i;
	DWORD dwWait = WAIT_OBJECT_0;

	debugLogA("%s thread starting.", "Info-Update");

	bInfoUpdateRunning = TRUE;

	while (bInfoUpdateRunning)
	{
		if (!nInfoUserCount && bInfoPendingUsers) // whole queue processed, check if more users needs updating
			icq_RescanInfoUpdate();

		if (!nInfoUserCount || !bInfoUpdateEnabled || !icqOnline())
		{
			dwWait = WAIT_TIMEOUT;
			while (bInfoUpdateRunning && dwWait == WAIT_TIMEOUT)
			{ // wait for new work or until we should end
				dwWait = ICQWaitForSingleObject(hInfoQueueEvent, 10000);
			}
		}
		if (!bInfoUpdateRunning) break;

		switch (dwWait) {
		case WAIT_IO_COMPLETION:
			// Possible shutdown in progress
			break;

		case WAIT_OBJECT_0:
		case WAIT_TIMEOUT:
			// Time to check for new users
			if (!bInfoUpdateEnabled) continue; // we can't send requests now      

			if (nInfoUserCount && icqOnline())
			{
				time_t now = time(NULL);
				BOOL bNotReady = FALSE, bTimeOuted = FALSE;

				// Check the list, take only users that were there for at least 5sec
				// wait if any user is there shorter than 5sec and not a single user is there longer than 20sec
				infoUpdateMutex->Enter();
				for (i = 0; i<LISTSIZE; i++)
				{
					if (m_infoUpdateList[i].hContact)
					{
						if (m_infoUpdateList[i].queued + 20 < now)
						{
							bTimeOuted = TRUE;
							break;
						}
						else if (m_infoUpdateList[i].queued + 5 >= now)
							bNotReady = TRUE;
					}
				}
				infoUpdateMutex->Leave();

				if (!bTimeOuted && bNotReady)
				{
					SleepEx(1000, TRUE);
					if (!bInfoUpdateRunning)
					{ // need to end as fast as possible
						debugLogA("%s thread ended.", "Info-Update");
						goto LBL_Exit;
					}
					continue;
				}

				if (FindCookie(dwInfoActiveRequest, NULL, NULL))
				{ // only send another request, when the previous is completed
#ifdef _DEBUG
					debugLogA("Info-Update: Request 0x%x still in progress.", dwInfoActiveRequest);
#endif
					SleepEx(1000, TRUE);
					if (!bInfoUpdateRunning)
					{ // need to end as fast as possible
						debugLogA("%s thread ended.", "Info-Update");
						goto LBL_Exit;
					}
					continue;
				}

#ifdef _DEBUG
				debugLogA("Info-Update: Users %u in queue.", nInfoUserCount);
#endif
				// Either some user is waiting long enough, or all users are ready (waited at least the minimum time)
				m_ratesMutex->Enter();
				if (!m_rates)
				{ // we cannot send info request - icq is offline
					m_ratesMutex->Leave();
					break;
				}
				WORD wGroup = m_rates->getGroupFromSNAC(ICQ_EXTENSIONS_FAMILY, ICQ_META_CLI_REQUEST);
				while (m_rates->getNextRateLevel(wGroup) < m_rates->getLimitLevel(wGroup, RML_IDLE_30))
				{ // we are over rate, need to wait before sending
					int nDelay = m_rates->getDelayToLimitLevel(wGroup, RML_IDLE_50);

					m_ratesMutex->Leave();
#ifdef _DEBUG
					debugLogA("Rates: InfoUpdate delayed %dms", nDelay);
#endif
					SleepEx(nDelay, TRUE); // do not keep things locked during sleep
					if (!bInfoUpdateRunning)
					{ // need to end as fast as possible
						debugLogA("%s thread ended.", "Info-Update");
						goto LBL_Exit;
					}
					m_ratesMutex->Enter();
					if (!m_rates) // we lost connection when we slept, go away
						break;
				}
				if (!m_rates)
				{ // we cannot send info request - icq is offline
					m_ratesMutex->Leave();
					break;
				}
				m_ratesMutex->Leave();

				userinfo *hContactList[LISTSIZE];
				int nListIndex = 0;
				BYTE *pRequestData = NULL;
				int nRequestSize = 0;

				infoUpdateMutex->Enter();
				for (i = 0; i<LISTSIZE; i++)
				{
					if (m_infoUpdateList[i].hContact)
					{
						// check TS again, maybe it has been updated while we slept
						if (IsMetaInfoChanged(m_infoUpdateList[i].hContact))
						{
							if (m_infoUpdateList[i].queued + 5 < now)
							{
								BYTE *pItem = NULL;
								int nItemSize = 0;
								DBVARIANT dbv = {DBVT_DELETED};

								if (!getSetting(m_infoUpdateList[i].hContact, DBSETTING_METAINFO_TOKEN, &dbv))
								{ // retrieve user details using privacy token
									ppackTLV(&pItem, &nItemSize, 0x96, dbv.cpbVal, dbv.pbVal);
									db_free(&dbv);
								}
								// last updated time
								ppackTLVDouble(&pItem, &nItemSize, 0x64, getSettingDouble(m_infoUpdateList[i].hContact, DBSETTING_METAINFO_TIME, 0));

								ppackTLVUID(&pItem, &nItemSize, 0x32, m_infoUpdateList[i].dwUin, NULL);
								ppackWord(&pRequestData, &nRequestSize, (WORD)nItemSize);
								ppackBuffer(&pRequestData, &nRequestSize, nItemSize, pItem);
								// take a reference
								SAFE_FREE((void**)&pItem);
								hContactList[nListIndex++] = &m_infoUpdateList[i];
							}
						}
						else
						{
#ifdef _DEBUG
							debugLogA("Dequeued absolete user %u", m_infoUpdateList[i].dwUin);
#endif
							// Dequeue user and find another one
							m_infoUpdateList[i].dwUin = 0;
							m_infoUpdateList[i].hContact = NULL;
							nInfoUserCount--;
							// continue for loop
						}
					}
				}

#ifdef _DEBUG
				debugLogA("Request info for %u user(s).", nListIndex);
#endif
				if (!nListIndex)
				{ // no users to request info for
					infoUpdateMutex->Leave();
					break;
				}
				if (!(dwInfoActiveRequest = sendUserInfoMultiRequest(pRequestData, nRequestSize, nListIndex)))
				{ // sending data packet failed
					SAFE_FREE((void**)&pRequestData);
					infoUpdateMutex->Leave();
					break;
				}
				SAFE_FREE((void**)&pRequestData);

				for (i = 0; i<nListIndex; i++)
				{ // Dequeue users and go back to sleep
					hContactList[i]->dwUin = 0;
					hContactList[i]->hContact = NULL;
					hContactList[i]->queued = 0;
					nInfoUserCount--;
				}
				infoUpdateMutex->Leave();
			}
			break;

		default:
			// Something strange happened. Exit
			bInfoUpdateRunning = FALSE;
			break;
		}
	}

	debugLogA("%s thread ended.", "Info-Update");

LBL_Exit:
	SAFE_DELETE(&infoUpdateMutex);
	CloseHandle(hInfoQueueEvent);
}
Example #12
0
void CIcqProto::handleDirectoryQueryResponse(BYTE *databuf, WORD wPacketLen, WORD wCookie, WORD wReplySubtype, WORD wFlags)
{
	WORD wBytesRemaining = 0;
	snac_header requestSnac = {0};
	BYTE requestResult;

#ifdef _DEBUG
	debugLogA("Received directory query response");
#endif
	if (wPacketLen >= 2)
		unpackLEWord(&databuf, &wBytesRemaining);
	wPacketLen -= 2;
	_ASSERTE(wPacketLen == wBytesRemaining);

	if (!unpackSnacHeader(&requestSnac, &databuf, &wPacketLen) || !requestSnac.bValid)
	{
		debugLogA("Error: Failed to parse directory response");
		return;
	}

	cookie_directory_data *pCookieData;
	MCONTACT hContact;
	// check request cookie
	if (!FindCookie(wCookie, &hContact, (void**)&pCookieData) || !pCookieData)
	{
		debugLogA("Warning: Ignoring unrequested directory reply type (x%x, x%x)", requestSnac.wFamily, requestSnac.wSubtype);
		return;
	}
	/// FIXME: we should really check the snac contents according to cookie data here ?? 

	// Check if this is the last packet for this request
	BOOL bMoreDataFollows = wFlags&0x0001 && requestSnac.wFlags&0x0001;

	if (wPacketLen >= 3) 
		unpackByte(&databuf, &requestResult);
	else
	{
		debugLogA("Error: Malformed directory response");
		if (!bMoreDataFollows)
			ReleaseCookie(wCookie);
		return;
	}
	if (requestResult != 1 && requestResult != 4)
	{
		debugLogA("Error: Directory request failed, status %u", requestResult);

		if (!bMoreDataFollows)
		{
			if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER)
				ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0);
			else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH)
				ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that
			ReleaseCookie(wCookie);
		}
		return;
	}
	WORD wLen;

	unpackWord(&databuf, &wLen);
	wPacketLen -= 3;
	if (wLen)
		debugLogA("Warning: Data in error message present!");

	if (wPacketLen <= 0x16)
	{ // sanity check
		debugLogA("Error: Malformed directory response");

		if (!bMoreDataFollows)
		{
			if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER)
				ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0);
			else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH)
				ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that
			ReleaseCookie(wCookie);
		}
		return;
	}
	databuf += 0x10; // unknown stuff
	wPacketLen -= 0x10;

	DWORD dwItemCount;
	WORD wPageCount;

	/// FIXME: check itemcount, pagecount against the cookie data ???

	unpackDWord(&databuf, &dwItemCount);
	unpackWord(&databuf, &wPageCount);
	wPacketLen -= 6;

	if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH && !bMoreDataFollows)
		debugLogA("Directory Search: %d contacts found (%u pages)", dwItemCount, wPageCount);

	if (wPacketLen <= 2)
	{ // sanity check, block expected
		debugLogA("Error: Malformed directory response");

		if (!bMoreDataFollows)
		{
			if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER)
				ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0);
			else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH)
				ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that
			ReleaseCookie(wCookie);
		}
		return;
	}
	WORD wData;

	unpackWord(&databuf, &wData); // This probably the count of items following (a block)
	wPacketLen -= 2;
	if (wPacketLen >= 2 && wData >= 1)
	{
		unpackWord(&databuf, &wLen);  // This is the size of the first item
		wPacketLen -= 2;
	}

	if (wData == 0 && pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH)
	{
		debugLogA("Directory Search: No contacts found");
		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0);
		ReleaseCookie(wCookie);
		return;
	}

	_ASSERTE(wData == 1 && wPacketLen == wLen);
	if (wData != 1 || wPacketLen != wLen)
	{
		debugLogA("Error: Malformed directory response (missing data)");

		if (!bMoreDataFollows)
		{
			if (pCookieData->bRequestType == DIRECTORYREQUEST_INFOUSER)
				ProtoBroadcastAck(hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0);
			else if (pCookieData->bRequestType == DIRECTORYREQUEST_SEARCH)
				ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); // should report error here, but Find/Add module does not support that
			ReleaseCookie(wCookie);
		}
		return;
	}
	oscar_tlv_chain *pDirectoryData = readIntoTLVChain(&databuf, wLen, -1);
	if (pDirectoryData)
	{
		switch (pCookieData->bRequestType)
		{
		case DIRECTORYREQUEST_INFOOWNER:
			parseDirectoryUserDetailsData(NULL, pDirectoryData, wCookie, pCookieData, wReplySubtype);
			break;

		case DIRECTORYREQUEST_INFOUSER:
      {
  		  DWORD dwUin = 0;
	  	  char *szUid = pDirectoryData->getString(0x32, 1);
		    if (!szUid) 
		    {
			    debugLogA("Error: Received unrecognized data from the directory");
			    break;
		    }

  		  if (IsStringUIN(szUid))
	  		  dwUin = atoi(szUid);

        if (hContact != HContactFromUID(dwUin, szUid, NULL))
        {
          debugLogA("Error: Received data does not match cookie contact, ignoring.");
          SAFE_FREE(&szUid);
          break;
        }
        else
          SAFE_FREE(&szUid);
      }
      
		case DIRECTORYREQUEST_INFOMULTI:
			parseDirectoryUserDetailsData(hContact, pDirectoryData, wCookie, pCookieData, wReplySubtype);
			break;

		case DIRECTORYREQUEST_SEARCH:
			parseDirectorySearchData(pDirectoryData, wCookie, pCookieData, wReplySubtype);
			break;

		default:
			debugLogA("Error: Unknown cookie type %x for directory response!", pCookieData->bRequestType);
		}
		disposeChain(&pDirectoryData);
	}
	else
		debugLogA("Error: Failed parsing directory response");

	// Release Memory
	if (!bMoreDataFollows)
		ReleaseCookie(wCookie);
}
Example #13
0
void CIcqProto::handleExtensionError(BYTE *buf, WORD wPackLen)
{
	WORD wErrorCode;

	if (wPackLen < 2)
		wErrorCode = 0;

	if (wPackLen >= 2 && wPackLen <= 6)
		unpackWord(&buf, &wErrorCode);
	else
	{ // TODO: cookies need to be handled and freed here on error
		oscar_tlv_chain *chain = NULL;

		unpackWord(&buf, &wErrorCode);
		wPackLen -= 2;
		chain = readIntoTLVChain(&buf, wPackLen, 0);
		if (chain)
		{
			oscar_tlv* pTLV;

			pTLV = chain->getTLV(0x21, 1); // get meta error data
			if (pTLV && pTLV->wLen >= 8)
			{
				BYTE *pBuffer = pTLV->pData;
				WORD wData;
				pBuffer += 6;
				unpackLEWord(&pBuffer, &wData); // get request type
				switch (wData)
				{
				case CLI_META_INFO_REQ:
					if (pTLV->wLen >= 12)
					{
						WORD wSubType;
						WORD wCookie;

						unpackWord(&pBuffer, &wCookie);
						unpackLEWord(&pBuffer, &wSubType);
						// more sofisticated detection, send ack
						if (wSubType == META_REQUEST_FULL_INFO)
						{
							MCONTACT hContact;
							cookie_fam15_data *pCookieData = NULL;

							int foundCookie = FindCookie(wCookie, &hContact, (void**)&pCookieData);
							if (foundCookie && pCookieData)
							{
								ProtoBroadcastAck(hContact,  ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE)1 ,0);

								ReleaseCookie(wCookie);  // we do not leak cookie and memory
							}

							debugLogA("Full info request error 0x%02x received", wErrorCode);
						}
						else if (wSubType == META_SET_PASSWORD_REQ)
						{
							// failed to change user password, report to UI
							ProtoBroadcastAck(NULL, ACKTYPE_SETINFO, ACKRESULT_FAILED, (HANDLE)wCookie, 0);

							debugLogA("Meta change password request failed, error 0x%02x", wErrorCode);
						}
						else
							debugLogA("Meta request error 0x%02x received", wErrorCode);
					}
					else 
						debugLogA("Meta request error 0x%02x received", wErrorCode);

					break;

				default:
					debugLogA("Unknown request 0x%02x error 0x%02x received", wData, wErrorCode);
				}
				disposeChain(&chain);
				return;
			}
			disposeChain(&chain);
		}
	}
	LogFamilyError(ICQ_EXTENSIONS_FAMILY, wErrorCode);
}
Example #14
0
void CIcqProto::parseSearchReplies(unsigned char *databuf, WORD wPacketLen, WORD wCookie, WORD wReplySubtype, BYTE bResultCode)
{
	BYTE bParsingOK = FALSE; // For debugging purposes only
	BOOL bLastUser = FALSE;
	cookie_search *pCookie;

	if (!FindCookie(wCookie, NULL, (void**)&pCookie))
	{
		debugLogA("Warning: Received unexpected search reply");
		pCookie = NULL;
	}

	switch (wReplySubtype)
	{

	case SRV_LAST_USER_FOUND: // Search: last user found reply
		bLastUser = TRUE;

	case SRV_USER_FOUND:      // Search: user found reply
		if (bLastUser)
			debugLogA("SNAC(0x15,0x3): Last search reply");
		else
			debugLogA("SNAC(0x15,0x3): Search reply");

		if (bResultCode == 0xA)
		{
			ICQSEARCHRESULT sr = {0};
			DWORD dwUin;
			char szUin[UINMAXLEN];
			WORD wLen;

			sr.hdr.cbSize = sizeof(sr);

			// Remaining bytes
			if (wPacketLen < 2)
				break;
			unpackLEWord(&databuf, &wLen);
			wPacketLen -= 2;

			_ASSERTE(wLen <= wPacketLen);
			if (wLen > wPacketLen)
				break;

			// Uin
			if (wPacketLen < 4)
				break;
			unpackLEDWord(&databuf, &dwUin); // Uin
			wPacketLen -= 4;
			sr.uin = dwUin;
			_itoa(dwUin, szUin, 10);
			sr.hdr.id = (FNAMECHAR*)szUin;

			// Nick
			if (wPacketLen < 2)
				break;
			unpackLEWord(&databuf, &wLen);
			wPacketLen -= 2;
			if (wLen > 0)
			{
				if (wPacketLen < wLen || (databuf[wLen-1] != 0))
					break;
				sr.hdr.nick = (FNAMECHAR*)databuf;
				databuf += wLen;
			}
			else
			{
				sr.hdr.nick = NULL;
			}

			// First name
			if (wPacketLen < 2)
				break;
			unpackLEWord(&databuf, &wLen);
			wPacketLen -= 2;
			if (wLen > 0)
			{
				if (wPacketLen < wLen || (databuf[wLen-1] != 0))
					break;
				sr.hdr.firstName = (FNAMECHAR*)databuf;
				databuf += wLen;
			}
			else
			{
				sr.hdr.firstName = NULL;
			}

			// Last name
			if (wPacketLen < 2)
				break;
			unpackLEWord(&databuf, &wLen);
			wPacketLen -= 2;
			if (wLen > 0)
			{
				if (wPacketLen < wLen || (databuf[wLen-1] != 0))
					break;
				sr.hdr.lastName = (FNAMECHAR*)databuf;
				databuf += wLen;
			}
			else
			{
				sr.hdr.lastName = NULL;
			}

			// E-mail name
			if (wPacketLen < 2)
				break;
			unpackLEWord(&databuf, &wLen);
			wPacketLen -= 2;
			if (wLen > 0)
			{
				if (wPacketLen < wLen || (databuf[wLen-1] != 0))
					break;
				sr.hdr.email = (FNAMECHAR*)databuf;
				databuf += wLen;
			}
			else
			{
				sr.hdr.email = NULL;
			}

			// Authentication needed flag
			if (wPacketLen < 1)
				break;
			unpackByte(&databuf, &sr.auth);

			// Finally, broadcast the result
			ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)wCookie, (LPARAM)&sr);

			// Broadcast "Last result" ack if this was the last user found
			if (wReplySubtype == SRV_LAST_USER_FOUND)
			{
				if (wPacketLen>=10)
				{
					DWORD dwLeft;

					databuf += 5;
					unpackLEDWord(&databuf, &dwLeft);
					if (dwLeft)
						debugLogA("Warning: %d search results omitted", dwLeft);
				}
				ReleaseSearchCookie(wCookie, pCookie);
			}
			bParsingOK = TRUE;
		}
		else 
		{
			// Failed search
			debugLogA("SNAC(0x15,0x3): Search error %u", bResultCode);

			ReleaseSearchCookie(wCookie, pCookie);

			bParsingOK = TRUE;
		}
		break;

	case SRV_RANDOM_FOUND: // Random search server reply
	default:
		if (pCookie)
			ReleaseCookie(wCookie);
		break;
	}

	// For debugging purposes only
	if (!bParsingOK)
	{
		debugLogA("Warning: Parsing error in 15/03 search reply type x%x", wReplySubtype);
		_ASSERTE(!bParsingOK);
	}
}
Example #15
0
void CIcqProto::handleLocationUserInfoReply(BYTE* buf, WORD wLen, DWORD dwCookie)
{
	HANDLE hContact;
	DWORD dwUIN;
	uid_str szUID;
	WORD wTLVCount;
	WORD wWarningLevel;
	HANDLE hCookieContact;
	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
	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, &hCookieContact, (void**)&pCookieData))
	{
		NetLog_Server("Error: Received unexpected away reply from %s", strUID(dwUIN, szUID));
		return;
	}

	if (hContact != hCookieContact)
	{
		NetLog_Server("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);

			// Read user info TLVs
			{
				oscar_tlv_chain* pChain;
				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))
				{
					oscar_tlv *pTLV;
					char *szEncoding = NULL;

					// Get Profile encoding TLV
					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);
				}

				setSettingString(hContact, "About", szMsg);
				BroadcastAck(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)
			{
				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 = 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);
				}

				ccs.szProtoService = PSR_AWAYMSG;
				ccs.hContact = hContact;
				ccs.wParam = status;
				ccs.lParam = (LPARAM)&pre;
				pre.flags = 0;
				pre.szMessage = szMsg?szMsg:(char *)"";
				pre.timestamp = time(NULL);
				pre.lParam = dwCookie;

				CallService(MS_PROTO_CHAINRECV,0,(LPARAM)&ccs);

				SAFE_FREE((void**)&szMsg);
			}
			break;
		}
	}
}
Example #16
0
/****************************************************************************
PARAMETERS:
URL - New URL for the page to load

RETURNS:
True if page loaded successfully, false if not

REMARKS:
Remove an applet from the manager. Called during applet destruction
****************************************************************************/
bool wxHtmlAppletWindow::LoadPage(
    const wxString& link)
{
    wxString href(link);

    if (link.GetChar(0) == '?'){
        wxString cmd = link.BeforeFirst('=');
        wxString cmdValue = link.AfterFirst('=');

        // Launches the default Internet browser for the system.
        if(!(cmd.CmpNoCase("?EXTERNAL"))) {
            return wxSpawnBrowser(this, cmdValue.c_str());
            }

        // Launches an external program on the system.
        if (!(cmd.CmpNoCase("?EXECUTE"))) {
            int waitflag = P_NOWAIT;
            bool ret;
            wxString currentdir;
            wxString filename, path, ext;

            // Parse the params sent to the execute command. For now the only
            // parm is "wait". wait will cause spawn wait, default is nowait.
            // Since we only need one param for now I am not going to make this
            // any smater then it needs to be. If we need more params later i'll
            // fix it.
            int i = cmdValue.Find('(');
            if (i != -1) {
                wxString param = cmdValue.AfterFirst('(');
                cmdValue.Truncate(i);
                if (!param.CmpNoCase("wait)"))
                    waitflag = P_WAIT;
                }

            currentdir = wxGetCwd();
            //we don't want to change the path of the virtual file system so we have to use these
            //functions rather than the filesystem
            wxSplitPath(cmdValue, &path, &filename, &ext);
            if (path.CmpNoCase("") != 0) wxSetWorkingDirectory(path);

            ret = !spawnl( waitflag, cmdValue , NULL );
            //HACK should use wxExecute
            //ret = wxExecute(filename, bool sync = FALSE, wxProcess *callback = NULL)
            wxSetWorkingDirectory(currentdir);

            return ret;
            }

        // Looks for a href in a variable stored as a cookie. The href can be
        // changed on the fly.
        if (!(cmd.CmpNoCase("?VIRTUAL"))){
            wxObject *obj = FindCookie(cmdValue);
            VirtualData *virtData = wxDynamicCast(obj,VirtualData);
            if (virtData) {
                // recurse and loadpage, just in case the link is like another
                // ? link
                return LoadPage(virtData->GetHref());
                }
            else {
#ifdef CHECKED
                wxLogError(_T("VIRTUAL LINK ERROR: '%s' does not exist."), cmdValue.c_str());
#endif
                return true;
                }
            }

        // This launches a qlet - It is like an applet but is more generic in that it
        // can be of any wxWin type so it then has the freedom to do more stuff.
        if (!(cmd.CmpNoCase("?WXPLUGIN"))){
            if (!cmdValue.IsNull()) {
                // TODO: We are going to need to add code to parse the command line
                //       parameters string in here in the future...
                wxString cmdLine = link.AfterFirst('(');
                cmdLine = cmdLine.BeforeLast(')');
                if (!CreatePlugIn(cmdValue,cmdLine)) {
#ifdef CHECKED
                    wxLogError(_T("Launch PlugIn ERROR: '%s' does not exist."), cmdValue.c_str());
#endif
                    }
                }
             return true;
            }

        // This used in a link or href will take you back in the history.
        if (!(cmd.CmpNoCase("?BACK"))){
            HistoryBack();
            return true;
            }

        // This used in a link or href will take you forward in the history
        if (!(cmd.CmpNoCase("?FORWARD"))){
            HistoryForward();
            return true;
            }
        }

    // Inform all the applets that the new page is being loaded
    for (wxAppletList::Node *node = m_AppletList.GetFirst(); node; node = node->GetNext())
        (node->GetData())->OnLinkClicked(wxHtmlLinkInfo(href));
    Show(false);

    m_openedlast = href;
    bool stat = wxHtmlWindow::LoadPage(href);
    Show(true);

    // Enable/Dis the navbar tools
    if (m_NavBarEnabled) {
        m_NavBar->EnableTool(m_NavForwardId,HistoryCanForward());
        m_NavBar->EnableTool(m_NavBackId,HistoryCanBack());
        }
    return stat;
}
Example #17
0
void CookieManager::ClientConnectCallback(int serial, IQuery *data)
{
    int client;
    IResultSet *results;

    /* Check validity of client */
    if ((client = playerhelpers->GetClientFromSerial(serial)) == 0)
    {
        return;
    }

    /* Check validity of results */
    if (data == NULL || (results = data->GetResultSet()) == NULL)
    {
        return;
    }

    IResultRow *row;
    do
    {
        if ((row = results->FetchRow()) == NULL)
        {
            break;
        }

        const char *name;
        row->GetString(0, &name, NULL);

        const char *value;
        row->GetString(1, &value, NULL);

        CookieData *pData = new CookieData(value);
        pData->changed = false;

        Cookie *parent = FindCookie(name);

        if (parent == NULL)
        {
            const char *desc;
            row->GetString(2, &desc, NULL);

            CookieAccess access = CookieAccess_Public;
            row->GetInt(3, (int *)&access);

            parent = CreateCookie(name, desc, access);
            cookieTrie.insert(name, parent);
            cookieList.push_back(parent);
        }

        pData->parent = parent;
        parent->data[client] = pData;

        clientData[client].push_back(pData);

    } while (results->MoreRows());

    statsLoaded[client] = true;

    cookieDataLoadedForward->PushCell(client);
    cookieDataLoadedForward->Execute(NULL);
}