summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRozhuk Ivan <rozhuk.im@gmail.com>2014-11-25 23:34:49 +0000
committerRozhuk Ivan <rozhuk.im@gmail.com>2014-11-25 23:34:49 +0000
commited4483115a924f177b786e11bc436243512eb8da (patch)
tree01ba33ddeba47db569a4ac8e9e56f4945eb26a68
parent74f4ab340ebf9a3399f82ad38b53ad6fcdf8885d (diff)
jabber code clean up
req code review git-svn-id: http://svn.miranda-ng.org/main/trunk@11074 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
-rw-r--r--protocols/IcqOscarJ/src/fam_13servclist.cpp3510
-rw-r--r--protocols/IcqOscarJ/src/icq_avatar.cpp8
-rw-r--r--protocols/IcqOscarJ/src/icqosc_svcs.cpp4
-rw-r--r--protocols/JabberG/src/jabber_search.cpp1532
-rw-r--r--protocols/JabberG/src/jabber_userinfo.cpp1697
5 files changed, 3380 insertions, 3371 deletions
diff --git a/protocols/IcqOscarJ/src/fam_13servclist.cpp b/protocols/IcqOscarJ/src/fam_13servclist.cpp
index 050aea947a..13d3958ec8 100644
--- a/protocols/IcqOscarJ/src/fam_13servclist.cpp
+++ b/protocols/IcqOscarJ/src/fam_13servclist.cpp
@@ -1,1755 +1,1755 @@
-// ---------------------------------------------------------------------------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-2014 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.
-// -----------------------------------------------------------------------------
-
-#include "icqoscar.h"
-
-static int unpackServerListItem(BYTE **pbuf, WORD *pwLen, char *pszRecordName, WORD *pwGroupId, WORD *pwItemId, WORD *pwItemType, WORD *pwTlvLength);
-
-
-void CIcqProto::handleServCListFam(BYTE *pBuffer, WORD wBufferLength, snac_header* pSnacHeader, serverthread_info *info)
-{
- switch (pSnacHeader->wSubtype) {
- case ICQ_LISTS_ACK: // UPDATE_ACK
- if (wBufferLength >= 2) {
- WORD wError;
- cookie_servlist_action* sc;
-
- unpackWord(&pBuffer, &wError);
-
- if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // look for action cookie
- debugLogA("Received expected server list ack, action: %d, result: %d", sc->dwAction, wError);
- FreeCookie(pSnacHeader->dwRef); // release cookie
-
- if (sc->dwAction == SSA_ACTION_GROUP) { // group cookie, handle sub-items
- int i;
-
- debugLogA("Server-List: Grouped action contains %d actions.", sc->dwGroupCount);
-
- pBuffer -= 2; // revoke unpack
- if (wBufferLength != 2 * sc->dwGroupCount)
- debugLogA("Error: Server list ack does not contain expected amount of result codes (%u != %u)", wBufferLength / 2, sc->dwGroupCount);
-
- for (i = 0; i < sc->dwGroupCount; i++) {
- if (wBufferLength >= 2) { // get proper result code
- unpackWord(&pBuffer, &wError);
- wBufferLength -= 2;
- }
- else // missing result code, give some special
- wError = -1;
-
- debugLogA("Action: %d, ack result: %d", sc->pGroupItems[i]->dwAction, wError);
-
- // call normal ack handler
- handleServerCListAck(sc->pGroupItems[i], wError);
- }
- // Release cookie
- SAFE_FREE((void**)&sc->pGroupItems);
- SAFE_FREE((void**)&sc);
- }
- else // single ack
- handleServerCListAck(sc, wError);
- }
- else debugLogA("Received unexpected server list ack %u", wError);
- }
- break;
-
- case ICQ_LISTS_SRV_REPLYLISTS:
- /* received server-list rights */
- handleServerCListRightsReply(pBuffer, wBufferLength);
- debugLogA("Server sent SNAC(x13,x03) - SRV_REPLYLISTS");
- break;
-
- case ICQ_LISTS_LIST: // SRV_REPLYROSTER
- {
- cookie_servlist_action* sc;
- BOOL blWork = bIsSyncingCL;
- bIsSyncingCL = TRUE; // this is not used if cookie takes place
-
- if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // we do it by reliable cookie
- if (!sc->lParam) { // is this first packet ?
- ResetSettingsOnListReload();
- sc->lParam = 1;
- }
- handleServerCListReply(pBuffer, wBufferLength, pSnacHeader->wFlags, info);
- if (!(pSnacHeader->wFlags & 0x0001)) // was that last packet ?
- ReleaseCookie(pSnacHeader->dwRef); // yes, release cookie
- }
- else { // use old fake
- if (!blWork) // this can fail on some crazy situations
- ResetSettingsOnListReload();
-
- handleServerCListReply(pBuffer, wBufferLength, pSnacHeader->wFlags, info);
- }
- }
- break;
-
- case ICQ_LISTS_UPTODATE: // SRV_REPLYROSTEROK
- bIsSyncingCL = FALSE;
- {
- cookie_servlist_action* sc;
- if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // we requested servlist check
- debugLogA("Server stated roster is ok.");
- ReleaseCookie(pSnacHeader->dwRef);
- LoadServerIDs();
- }
- else debugLogA("Server sent unexpected SNAC(x13,x0F) - SRV_REPLYROSTEROK");
-
- // This will activate the server side list
- sendRosterAck(); // this must be here, cause of failures during cookie alloc
- handleServUINSettings(wListenPort, info);
- }
- break;
-
- case ICQ_LISTS_CLI_MODIFYSTART:
- debugLogA("Server sent SNAC(x13,x%02x) - %s", ICQ_LISTS_CLI_MODIFYSTART, "Server is modifying contact list");
- break;
-
- case ICQ_LISTS_CLI_MODIFYEND:
- debugLogA("Server sent SNAC(x13,x%02x) - %s", ICQ_LISTS_CLI_MODIFYEND, "End of server modification");
- break;
-
- case ICQ_LISTS_ADDTOLIST:
- case ICQ_LISTS_UPDATEGROUP:
- case ICQ_LISTS_REMOVEFROMLIST:
- {
- int nItems = 0;
-
- while (wBufferLength >= 10) {
- WORD wGroupId, wItemId, wItemType, wTlvLen;
- uid_str szRecordName;
-
- if (unpackServerListItem(&pBuffer, &wBufferLength, szRecordName, &wGroupId, &wItemId, &wItemType, &wTlvLen)) {
- BYTE *buf = pBuffer;
- oscar_tlv_chain *pChain = NULL;
-
- nItems++;
-
- // parse possible item's data
- if (wBufferLength >= wTlvLen && wTlvLen > 0) {
- pChain = readIntoTLVChain(&buf, wTlvLen, 0);
- pBuffer += wTlvLen;
- wBufferLength -= wTlvLen;
- }
- else if (wTlvLen > 0)
- wBufferLength = 0;
-
- // process item change
- if (pSnacHeader->wSubtype == ICQ_LISTS_ADDTOLIST)
- handleServerCListItemAdd(szRecordName, wGroupId, wItemId, wItemType, pChain);
- else if (pSnacHeader->wSubtype == ICQ_LISTS_UPDATEGROUP)
- handleServerCListItemUpdate(szRecordName, wGroupId, wItemId, wItemType, pChain);
- else if (pSnacHeader->wSubtype == ICQ_LISTS_REMOVEFROMLIST)
- handleServerCListItemDelete(szRecordName, wGroupId, wItemId, wItemType, pChain);
-
- // release memory
- disposeChain(&pChain);
- }
- }
- {
- // log packet basics
- char *szChange;
-
- if (pSnacHeader->wSubtype == ICQ_LISTS_ADDTOLIST)
- szChange = "Server added %u item(s) to list";
- else if (pSnacHeader->wSubtype == ICQ_LISTS_UPDATEGROUP)
- szChange = "Server updated %u item(s) on list";
- else if (pSnacHeader->wSubtype == ICQ_LISTS_REMOVEFROMLIST)
- szChange = "Server removed %u item(s) from list";
-
- char szLogText[MAX_PATH];
- mir_snprintf(szLogText, MAX_PATH, szChange, nItems);
- debugLogA("Server sent SNAC(x13,x%02x) - %s", pSnacHeader->wSubtype, szLogText);
- }
- }
- break;
-
- case ICQ_LISTS_AUTHREQUEST:
- handleRecvAuthRequest(pBuffer, wBufferLength);
- break;
-
- case ICQ_LISTS_SRV_AUTHRESPONSE:
- handleRecvAuthResponse(pBuffer, wBufferLength);
- break;
-
- case ICQ_LISTS_AUTHGRANTED:
- debugLogA("Server sent SNAC(x13,x%02x) - %s", ICQ_LISTS_AUTHGRANTED, "User granted us future authorization");
- break;
-
- case ICQ_LISTS_YOUWEREADDED:
- handleRecvAdded(pBuffer, wBufferLength);
- break;
-
- case ICQ_LISTS_ERROR:
- if (wBufferLength >= 2) {
- WORD wError;
- cookie_servlist_action* sc;
-
- unpackWord(&pBuffer, &wError);
-
- if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // look for action cookie
- debugLogA("Received server list error, action: %d, result: %d", sc->dwAction, wError);
- FreeCookie(pSnacHeader->dwRef); // release cookie
-
- if (sc->dwAction == SSA_CHECK_ROSTER) { // the serv-list is unavailable turn it off
- icq_LogMessage(LOG_ERROR, LPGEN("Server contact list is unavailable, Miranda will use local contact list."));
- m_bSsiEnabled = 0;
- handleServUINSettings(wListenPort, info);
- }
- /// FIXME: properly release pending operations & cookie memory
- SAFE_FREE((void**)&sc);
- }
- else LogFamilyError(ICQ_LISTS_FAMILY, wError);
- }
- break;
-
- default:
- debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LISTS_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef);
- break;
- }
-}
-
-
-static int unpackServerListItem(BYTE **pbuf, WORD *pwLen, char *pszRecordName, WORD *pwGroupId, WORD *pwItemId, WORD *pwItemType, WORD *pwTlvLength)
-{
- WORD wRecordNameLen;
-
- // The name of the entry. If this is a group header, then this
- // is the name of the group. If it is a plain contact list entry,
- // then it's the UIN of the contact.
- unpackWord(pbuf, &wRecordNameLen);
- if (*pwLen < 10 + wRecordNameLen || wRecordNameLen >= MAX_PATH)
- return 0; // Failure
-
- unpackString(pbuf, pszRecordName, wRecordNameLen);
- if (pszRecordName)
- pszRecordName[wRecordNameLen] = '\0';
-
- // The group identifier this entry belongs to. If 0, this is meta information or
- // a contact without a group
- unpackWord(pbuf, pwGroupId);
-
- // The ID of this entry. Group headers have ID 0. Otherwise, this
- // is a random number generated when the user is added to the
- // contact list, or when the user is ignored. See CLI_ADDBUDDY.
- unpackWord(pbuf, pwItemId);
-
- // This field indicates what type of entry this is
- unpackWord(pbuf, pwItemType);
-
- // The length in bytes of the following TLV chain
- unpackWord(pbuf, pwTlvLength);
-
- *pwLen -= wRecordNameLen + 10;
-
- return 1; // Success
-}
-
-
-void CIcqProto::handleServerCListRightsReply(BYTE *buf, WORD wLen)
-{
- /* received list rights, store the item limits for future use */
- oscar_tlv_chain* chain;
-
- memset(m_wServerListLimits, -1, sizeof(m_wServerListLimits));
- m_wServerListGroupMaxContacts = 0;
- m_wServerListRecordNameMaxLength = 0xFFFF;
-
- if (chain = readIntoTLVChain(&buf, wLen, 0)) {
- // determine max number of contacts in a group
- m_wServerListGroupMaxContacts = chain->getWord(0x0C, 1);
- // determine length limit for server-list item's name
- m_wServerListRecordNameMaxLength = chain->getWord(0x06, 1);
-
- if (oscar_tlv *pTLV = chain->getTLV(0x04, 1)) { // limits for item types
- WORD *pLimits = (WORD*)pTLV->pData;
- for (int i = 0; i < pTLV->wLen / 2; i++) {
- m_wServerListLimits[i] = (pLimits[i] & 0xFF) << 8 | (pLimits[i] >> 8);
-
- if (i + 1 >= SIZEOF(m_wServerListLimits))
- break;
- }
-
- debugLogA("SSI: Max %d contacts (%d per group), %d groups, %d permit, %d deny, %d ignore items.", m_wServerListLimits[SSI_ITEM_BUDDY], m_wServerListGroupMaxContacts, m_wServerListLimits[SSI_ITEM_GROUP], m_wServerListLimits[SSI_ITEM_PERMIT], m_wServerListLimits[SSI_ITEM_DENY], m_wServerListLimits[SSI_ITEM_IGNORE]);
- }
-
- disposeChain(&chain);
- }
-}
-
-DWORD CIcqProto::updateServerGroupData(WORD wGroupId, void *groupData, int groupSize, DWORD dwOperationFlags)
-{
- cookie_servlist_action* ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
- if (!ack) {
- debugLogA("Updating of group on server list failed (malloc error)");
- return 0;
- }
- ack->dwAction = SSA_GROUP_UPDATE;
- ack->szGroupName = getServListGroupName(wGroupId);
- ack->wGroupId = wGroupId;
-
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack);
- return icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, ack->wGroupId, ack->szGroupName, groupData, groupSize, dwOperationFlags);
-}
-
-void CIcqProto::handleServerCListAck(cookie_servlist_action* sc, WORD wError)
-{
- switch (sc->dwAction) {
- case SSA_VISIBILITY:
- if (wError)
- debugLogA("Server visibility update failed, error %d", wError);
- break;
-
- case SSA_CONTACT_UPDATE:
- servlistPendingRemoveContact(sc->hContact, sc->wContactId, sc->wGroupId, wError ? PENDING_RESULT_FAILED : PENDING_RESULT_SUCCESS);
- if (wError) {
- debugLogA("Updating of server contact failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Updating of server contact failed."));
- }
- break;
-
- case SSA_PRIVACY_ADD:
- if (wError) {
- debugLogA("Adding of privacy item to server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Adding of privacy item to server list failed."));
- }
- break;
-
- case SSA_PRIVACY_REMOVE:
- if (wError) {
- debugLogA("Removing of privacy item from server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Removing of privacy item from server list failed."));
- }
- FreeServerID(sc->wContactId, SSIT_ITEM); // release server id
- break;
-
- case SSA_CONTACT_ADD:
- if (wError) {
- if (wError == 0xE) { // server refused to add contact w/o auth, add with
- debugLogA("Contact could not be added without authorization, add with await auth flag.");
-
- setByte(sc->hContact, "Auth", 1); // we need auth
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_ADDTOLIST, sc->hContact, sc);
- icq_sendServerContact(sc->hContact, dwCookie, ICQ_LISTS_ADDTOLIST, sc->wGroupId, sc->wContactId, SSOP_ITEM_ACTION | SSOF_CONTACT, 500, NULL);
-
- sc = NULL; // we do not want it to be freed now
- break;
- }
- FreeServerID(sc->wContactId, SSIT_ITEM);
-
- debugLogA("Adding of contact to server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Adding of contact to server list failed."));
-
- servlistPendingRemoveContact(sc->hContact, 0, sc->wGroupId, PENDING_RESULT_FAILED);
-
- servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
- }
- else {
- void* groupData;
- int groupSize;
-
- setWord(sc->hContact, DBSETTING_SERVLIST_ID, sc->wContactId);
- setWord(sc->hContact, DBSETTING_SERVLIST_GROUP, sc->wGroupId);
-
- servlistPendingRemoveContact(sc->hContact, sc->wContactId, sc->wGroupId, PENDING_RESULT_SUCCESS);
-
- if (groupData = collectBuddyGroup(sc->wGroupId, &groupSize)) { // the group is not empty, just update it
- updateServerGroupData(sc->wGroupId, groupData, groupSize, SSOF_END_OPERATION);
- SAFE_FREE((void**)&groupData);
- }
- else { // this should never happen
- debugLogA("Group update failed.");
- servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
- }
- }
- break;
-
- case SSA_GROUP_ADD:
- if (wError) {
- FreeServerID(sc->wGroupId, SSIT_GROUP);
- debugLogA("Adding of group to server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Adding of group to server list failed."));
-
- servlistPendingRemoveGroup(sc->szGroup, 0, PENDING_RESULT_FAILED);
- }
- else { // group added, we need to update master group
- void* groupData;
- int groupSize;
-
- setServListGroupName(sc->wGroupId, sc->szGroupName); // add group to namelist
- { // add group to known
- char *szCListGroup = getServListGroupCListPath(sc->wGroupId);
-
- // create link to the original CList group
- setServListGroupLinkID(sc->szGroup, sc->wGroupId);
-
- servlistPendingRemoveGroup(sc->szGroup, sc->wGroupId, PENDING_RESULT_SUCCESS);
- SAFE_FREE((void**)&szCListGroup);
- }
-
- groupData = collectGroups(&groupSize);
- groupData = SAFE_REALLOC(groupData, groupSize + 2);
- *(((WORD*)groupData) + (groupSize >> 1)) = sc->wGroupId; // add this new group id
- groupSize += 2;
-
- cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
- if (ack) {
- ack->dwAction = SSA_GROUP_UPDATE;
-
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack);
- icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, 0, ack->szGroupName, groupData, groupSize, SSOF_END_OPERATION);
- }
- else // end server modifications here
- servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100);
-
- SAFE_FREE((void**)&groupData);
- }
- if (sc->szGroup != sc->szGroupName)
- SAFE_FREE((void**)&sc->szGroup);
-
- SAFE_FREE((void**)&sc->szGroupName);
- break;
-
- case SSA_CONTACT_REMOVE:
- if (!wError) {
- void* groupData;
- int groupSize;
-
- setWord(sc->hContact, DBSETTING_SERVLIST_ID, 0); // clear the values
- setWord(sc->hContact, DBSETTING_SERVLIST_GROUP, 0);
-
- FreeServerID(sc->wContactId, SSIT_ITEM);
-
- servlistPendingRemoveContact(sc->hContact, 0, sc->wGroupId, PENDING_RESULT_SUCCESS);
-
- if (groupData = collectBuddyGroup(sc->wGroupId, &groupSize)) { // the group is still not empty, just update it
- updateServerGroupData(sc->wGroupId, groupData, groupSize, SSOF_END_OPERATION);
- }
- else // the group is empty, delete it
- {
- char *szGroup = getServListGroupCListPath(sc->wGroupId);
-
- servlistRemoveGroup(szGroup, sc->wGroupId);
- SAFE_FREE((void**)&szGroup);
- }
- SAFE_FREE((void**)&groupData); // free the memory
- }
- else {
- debugLogA("Removing of contact from server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Removing of contact from server list failed."));
-
- servlistPendingRemoveContact(sc->hContact, sc->wContactId, sc->wGroupId, PENDING_RESULT_FAILED);
-
- servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
- }
- break;
-
- case SSA_GROUP_UPDATE:
- if (wError) {
- debugLogA("Updating of group on server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Updating of group on server list failed."));
- }
- SAFE_FREE((void**)&sc->szGroupName);
- break;
-
- case SSA_GROUP_REMOVE:
- SAFE_FREE((void**)&sc->szGroupName);
- if (wError) {
- debugLogA("Removing of group from server list failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Removing of group from server list failed."));
-
- servlistPendingRemoveGroup(sc->szGroup, 0, PENDING_RESULT_FAILED);
-
- servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
- SAFE_FREE((void**)&sc->szGroup);
- }
- else { // group removed, we need to update master group
- void* groupData;
- int groupSize;
-
- setServListGroupName(sc->wGroupId, NULL); // clear group from namelist
- FreeServerID(sc->wGroupId, SSIT_GROUP);
- removeGroupPathLinks(sc->wGroupId);
-
- servlistPendingRemoveGroup(sc->szGroup, 0, PENDING_RESULT_SUCCESS);
- SAFE_FREE((void**)&sc->szGroup);
-
- groupData = collectGroups(&groupSize);
- sc->wGroupId = 0;
- sc->dwAction = SSA_GROUP_UPDATE;
- sc->szGroupName = NULL;
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, sc);
-
- icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, 0, sc->szGroupName, groupData, groupSize, SSOF_END_OPERATION);
- // end server modifications here
-
- sc = NULL; // we do not want to be freed here
-
- SAFE_FREE((void**)&groupData);
- }
- break;
-
- case SSA_CONTACT_SET_GROUP:
- // we moved contact to another group
- if (sc->lParam == -1) // the first was an error
- break;
-
- if (wError) {
- if (wError == 0x0E && sc->lParam == 1) { // second ack - adding failed with error 0x0E, try to add with AVAIT_AUTH flag
- if (!getByte(sc->hContact, "Auth", 0)) { // we tried without AWAIT_AUTH, try again with it
- debugLogA("Contact could not be added without authorization, add with await auth flag.");
- setByte(sc->hContact, "Auth", 1); // we need auth
- }
- else { // we tried with AWAIT_AUTH, try again without
- debugLogA("Contact count not be added awaiting authorization, try authorized.");
- setByte(sc->hContact, "Auth", 0);
- }
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_ADDTOLIST, sc->hContact, sc);
- icq_sendServerContact(sc->hContact, dwCookie, ICQ_LISTS_ADDTOLIST, sc->wNewGroupId, sc->wNewContactId, SSOP_ITEM_ACTION | SSOF_CONTACT, 400, NULL);
-
- sc->lParam = 2; // do not cycle
- sc = NULL; // we do not want to be freed here
- break;
- }
- FreeServerID(sc->wNewContactId, SSIT_ITEM);
- debugLogA("Moving of user to another group on server list failed, error %d", wError);
- icq_LogMessage(LOG_ERROR, LPGEN("Moving of user to another group on server list failed."));
-
- servlistPendingRemoveContact(sc->hContact, 0, (WORD)(sc->lParam ? sc->wGroupId : sc->wNewGroupId), PENDING_RESULT_FAILED);
-
- servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
-
- if (!sc->lParam) { // is this first ack ?
- sc->lParam = -1;
- sc = NULL; // this can't be freed here
- }
- break;
- }
-
- if (sc->lParam) { // is this the second ack ?
- void* groupData;
- int groupSize;
- int bEnd = 1; // shall we end the sever modifications
-
- setWord(sc->hContact, DBSETTING_SERVLIST_ID, sc->wNewContactId);
- setWord(sc->hContact, DBSETTING_SERVLIST_GROUP, sc->wNewGroupId);
-
- servlistPendingRemoveContact(sc->hContact, sc->wNewContactId, sc->wNewGroupId, PENDING_RESULT_SUCCESS);
-
- if (groupData = collectBuddyGroup(sc->wGroupId, &groupSize)) // update the group we moved from
- { // the group is still not empty, just update it
- updateServerGroupData(sc->wGroupId, groupData, groupSize, 0);
- SAFE_FREE((void**)&groupData); // free the memory
- }
- else { // the group is empty, delete it
- char* szGroup = getServListGroupCListPath(sc->wGroupId);
-
- servlistRemoveGroup(szGroup, sc->wGroupId);
- SAFE_FREE((void**)&szGroup);
- bEnd = 0; // here the modifications go on
- }
-
- groupData = collectBuddyGroup(sc->wNewGroupId, &groupSize); // update the group we moved to
- updateServerGroupData(sc->wNewGroupId, groupData, groupSize, bEnd ? SSOF_END_OPERATION : 0);
- // end server modifications here
- SAFE_FREE((void**)&groupData);
-
- }
- else // contact was deleted from server-list
- {
- delSetting(sc->hContact, DBSETTING_SERVLIST_ID);
- delSetting(sc->hContact, DBSETTING_SERVLIST_GROUP);
- FreeServerID(sc->wContactId, SSIT_ITEM); // release old contact id
- sc->lParam = 1;
- sc = NULL; // wait for second ack
- }
- break;
-
- case SSA_CONTACT_FIX_AUTH:
- if (wError) { // FIXME: something failed, we should handle it properly
- }
- break;
-
- case SSA_GROUP_RENAME:
- if (wError) {
- debugLogA("Renaming of server group failed, error %d", wError);
- icq_LogMessage(LOG_WARNING, LPGEN("Renaming of server group failed."));
-
- servlistPendingRemoveGroup(sc->szGroup, sc->wGroupId, PENDING_RESULT_FAILED);
- }
- else {
- setServListGroupName(sc->wGroupId, sc->szGroupName);
- removeGroupPathLinks(sc->wGroupId);
- { // add group to known
- char *szCListGroup = getServListGroupCListPath(sc->wGroupId);
-
- /// FIXME: need to create link to the new group name before unique item name correction as well
- setServListGroupLinkID(szCListGroup, sc->wGroupId);
- SAFE_FREE((void**)&szCListGroup);
- }
- servlistPendingRemoveGroup(sc->szGroup, sc->wGroupId, PENDING_RESULT_SUCCESS);
- }
- SAFE_FREE((void**)&sc->szGroupName);
- SAFE_FREE((void**)&sc->szGroup);
- break;
-
- case SSA_SETAVATAR:
- if (wError) {
- debugLogA("Uploading of avatar hash failed.");
- if (sc->wGroupId) { // is avatar added or updated?
- FreeServerID(sc->wContactId, SSIT_ITEM);
- delSetting(DBSETTING_SERVLIST_AVATAR); // to fix old versions
- }
- }
- else setWord(DBSETTING_SERVLIST_AVATAR, sc->wContactId);
- break;
-
- case SSA_REMOVEAVATAR:
- if (wError)
- debugLogA("Removing of avatar hash failed.");
- else {
- FreeServerID(sc->wContactId, SSIT_ITEM);
- delSetting(DBSETTING_SERVLIST_AVATAR);
- }
- break;
-
- case SSA_SERVLIST_ACK:
- ProtoBroadcastAck(sc->hContact, ICQACKTYPE_SERVERCLIST, wError ? ACKRESULT_FAILED : ACKRESULT_SUCCESS, (HANDLE)sc->lParam, wError);
- break;
-
- case SSA_IMPORT:
- if (wError)
- debugLogA("Re-starting import sequence failed, error %d", wError);
- else {
- setWord("SrvImportID", 0);
- delSetting("ImportTS");
- }
- break;
-
- default:
- debugLogA("Server ack cookie type (%d) not recognized.", sc->dwAction);
- }
-
- SAFE_FREE((void**)&sc); // free the memory
- return;
-}
-
-MCONTACT CIcqProto::HContactFromRecordName(const char* szRecordName, int *bAdded)
-{
- MCONTACT hContact = INVALID_CONTACT_ID;
-
- if (!IsStringUIN(szRecordName)) // probably AIM contact
- hContact = HContactFromUID(0, szRecordName, bAdded);
- else { // this should be ICQ number
- DWORD dwUin = atoi(szRecordName);
- hContact = HContactFromUIN(dwUin, bAdded);
- }
- return hContact;
-}
-
-int CIcqProto::getServerDataFromItemTLV(oscar_tlv_chain* pChain, unsigned char *buf) /// FIXME: need to keep original order
-{
- // get server-list item's TLV data
- oscar_tlv_chain* list = pChain;
- int datalen = 0;
- icq_packet pBuf;
-
- // Initialize our handy data buffer
- pBuf.wPlace = 0;
- pBuf.pData = buf;
-
- while (list) { // collect non-standard TLVs and save them to DB
- if (list->tlv.wType != SSI_TLV_AWAITING_AUTH &&
- list->tlv.wType != SSI_TLV_NAME &&
- list->tlv.wType != SSI_TLV_COMMENT &&
- list->tlv.wType != SSI_TLV_METAINFO_TOKEN &&
- list->tlv.wType != SSI_TLV_METAINFO_TIME) { // only TLVs which we do not handle on our own
- packTLV(&pBuf, list->tlv.wType, list->tlv.wLen, list->tlv.pData);
-
- datalen += list->tlv.wLen + 4;
- }
- list = list->next;
- }
- return datalen;
-}
-
-void CIcqProto::handleServerCListReply(BYTE *buf, WORD wLen, WORD wFlags, serverthread_info *info)
-{
- BYTE bySSIVersion;
- WORD wRecordCount;
- WORD wRecord;
- WORD wGroupId;
- WORD wItemId;
- WORD wTlvType;
- WORD wTlvLength;
- BOOL bIsLastPacket;
- uid_str szRecordName;
- oscar_tlv_chain* pChain = NULL;
- oscar_tlv* pTLV = NULL;
- char *szActiveSrvGroup = NULL;
- WORD wActiveSrvGroupId = -1;
-
-
- // If flag bit 1 is set, this is not the last
- // packet. If it is 0, this is the last packet
- // and there will be a timestamp at the end.
- if (wFlags & 0x0001)
- bIsLastPacket = FALSE;
- else
- bIsLastPacket = TRUE;
-
- if (wLen < 3)
- return;
-
- // Version number of SSI protocol?
- unpackByte(&buf, &bySSIVersion);
- wLen -= 1;
-
- // Total count of following entries. This is the size of the server
- // side contact list and should be saved and sent with CLI_CHECKROSTER.
- // NOTE: When the entries are split up in several packets, each packet
- // has it's own count and they must be added to get the total size of
- // server list.
- unpackWord(&buf, &wRecordCount);
- wLen -= 2;
- debugLogA("SSI: number of entries is %u, version is %u", wRecordCount, bySSIVersion);
-
- // Loop over all items in the packet
- for (wRecord = 0; wRecord < wRecordCount; wRecord++) {
- debugLogA("SSI: parsing record %u", wRecord + 1);
-
- if (wLen < 10) { // minimum: name length (zero), group ID, item ID, empty TLV
- debugLogA("Warning: SSI parsing error (%d)", 0);
- break;
- }
-
- if (!unpackServerListItem(&buf, &wLen, szRecordName, &wGroupId, &wItemId, &wTlvType, &wTlvLength)) { // unpack basic structure
- debugLogA("Warning: SSI parsing error (%d)", 1);
- break;
- }
-
- debugLogA("Name: '%s', GroupID: %u, EntryID: %u, EntryType: %u, TLVlength: %u",
- szRecordName, wGroupId, wItemId, wTlvType, wTlvLength);
-
- if (wLen < wTlvLength) {
- debugLogA("Warning: SSI parsing error (%d)", 2);
- break;
- }
-
- // Initialize the tlv chain
- if (wTlvLength > 0) {
- pChain = readIntoTLVChain(&buf, wTlvLength, 0);
- wLen -= wTlvLength;
- }
- else pChain = NULL;
-
- switch (wTlvType) {
- case SSI_ITEM_BUDDY:
- {
- /* this is a contact */
- int bAdded;
- MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
-
- if (hContact != INVALID_CONTACT_ID) {
- int bRegroup = 0;
- int bNicked = 0;
-
- if (bAdded) { // Not already on list: added
- debugLogA("SSI added new %s contact '%s'", "ICQ", szRecordName);
-
- AddJustAddedContact(hContact);
- }
- else { // we should add new contacts and this contact was just added, show it
- if (IsContactJustAdded(hContact)) {
- setContactHidden(hContact, 0);
- bAdded = 1; // we want details for new contacts
- }
- else debugLogA("SSI ignoring existing contact '%s'", szRecordName);
-
- // Contact on server is always on list
- db_set_b(hContact, "CList", "NotOnList", 0);
- }
-
- // Save group and item ID
- setWord(hContact, DBSETTING_SERVLIST_ID, wItemId);
- setWord(hContact, DBSETTING_SERVLIST_GROUP, wGroupId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
-
- if (!bAdded && getByte("LoadServerDetails", DEFAULT_SS_LOAD)) { // check if the contact has been moved on the server
- if (wActiveSrvGroupId != wGroupId || !szActiveSrvGroup) {
- SAFE_FREE(&szActiveSrvGroup);
- szActiveSrvGroup = getServListGroupCListPath(wGroupId);
- wActiveSrvGroupId = wGroupId;
- }
- char *szLocalGroup = getContactCListGroup(hContact);
-
- if (!strlennull(szLocalGroup)) { // no CListGroup
- SAFE_FREE(&szLocalGroup);
-
- szLocalGroup = null_strdup(DEFAULT_SS_GROUP);
- }
-
- if (strcmpnull(szActiveSrvGroup, szLocalGroup) &&
- (strlennull(szActiveSrvGroup) >= strlennull(szLocalGroup) || (szActiveSrvGroup && _strnicmp(szActiveSrvGroup, szLocalGroup, strlennull(szLocalGroup))))) { // contact moved to new group or sub-group or not to master group
- bRegroup = 1;
- }
- if (bRegroup && !stricmpnull(DEFAULT_SS_GROUP, szActiveSrvGroup)) /// TODO: invent something more clever for "root" group
- { // is it the default "General" group ?
- bRegroup = 0; // if yes, do not move to it - cause it would hide the contact
- }
- SAFE_FREE(&szLocalGroup);
- }
-
- if (bRegroup || bAdded) { // if we should load server details or contact was just added, update its group
- if (wActiveSrvGroupId != wGroupId || !szActiveSrvGroup) {
- SAFE_FREE(&szActiveSrvGroup);
- szActiveSrvGroup = getServListGroupCListPath(wGroupId);
- wActiveSrvGroupId = wGroupId;
- }
-
- if (szActiveSrvGroup) { // try to get Miranda Group path from groupid, if succeeded save to db
- moveContactToCListGroup(hContact, szActiveSrvGroup);
- }
- }
-
- if (pChain) { // Look for nickname TLV and copy it to the db if necessary
- if (pTLV = pChain->getTLV(SSI_TLV_NAME, 1)) {
- if (pTLV->pData && (pTLV->wLen > 0)) {
- char *pszNick;
- WORD wNickLength;
-
- wNickLength = pTLV->wLen;
-
- pszNick = (char*)SAFE_MALLOC(wNickLength + 1);
- // Copy buffer to utf-8 buffer
- memcpy(pszNick, pTLV->pData, wNickLength);
- pszNick[wNickLength] = 0; // Terminate string
-
- debugLogA("Nickname is '%s'", pszNick);
-
- bNicked = 1;
-
- // Write nickname to database
- if (getByte("LoadServerDetails", DEFAULT_SS_LOAD) || bAdded) { // if just added contact, save details always - does no harm
- char *szOldNick;
-
- if (szOldNick = getSettingStringUtf(hContact, "CList", "MyHandle", NULL)) {
- if ((strcmpnull(szOldNick, pszNick)) && (strlennull(pszNick) > 0)) { // check if the truncated nick changed, i.e. do not overwrite locally stored longer nick
- if (strlennull(szOldNick) <= strlennull(pszNick) || strncmp(szOldNick, pszNick, null_strcut(szOldNick, MAX_SSI_TLV_NAME_SIZE))) {
- // Yes, we really do need to delete it first. Otherwise the CLUI nick
- // cache isn't updated (I'll look into it)
- db_unset(hContact, "CList", "MyHandle");
- db_set_utf(hContact, "CList", "MyHandle", pszNick);
- }
- }
- SAFE_FREE(&szOldNick);
- }
- else if (strlennull(pszNick) > 0) {
- db_unset(hContact, "CList", "MyHandle");
- db_set_utf(hContact, "CList", "MyHandle", pszNick);
- }
- }
- SAFE_FREE(&pszNick);
- }
- else debugLogA("Invalid nickname");
- }
- if (bAdded && !bNicked)
- icq_QueueUser(hContact); // queue user without nick for fast auto info update
-
- // Look for comment TLV and copy it to the db if necessary
- if (pTLV = pChain->getTLV(SSI_TLV_COMMENT, 1)) {
- if (pTLV->pData && (pTLV->wLen > 0)) {
- char *pszComment;
- WORD wCommentLength;
-
-
- wCommentLength = pTLV->wLen;
-
- pszComment = (char*)SAFE_MALLOC(wCommentLength + 1);
- // Copy buffer to utf-8 buffer
- memcpy(pszComment, pTLV->pData, wCommentLength);
- pszComment[wCommentLength] = 0; // Terminate string
-
- debugLogA("Comment is '%s'", pszComment);
-
- // Write comment to database
- if (getByte("LoadServerDetails", DEFAULT_SS_LOAD) || bAdded) { // if just added contact, save details always - does no harm
- char *szOldComment;
-
- if (szOldComment = getSettingStringUtf(hContact, "UserInfo", "MyNotes", NULL)) {
- if ((strcmpnull(szOldComment, pszComment)) && (strlennull(pszComment) > 0)) // check if the truncated comment changed, i.e. do not overwrite locally stored longer comment
- if (strlennull(szOldComment) <= strlennull(pszComment) || strncmp((char*)szOldComment, (char*)pszComment, null_strcut(szOldComment, MAX_SSI_TLV_COMMENT_SIZE)))
- db_set_utf(hContact, "UserInfo", "MyNotes", pszComment);
-
- SAFE_FREE((void**)&szOldComment);
- }
- else if (strlennull(pszComment) > 0)
- db_set_utf(hContact, "UserInfo", "MyNotes", pszComment);
- }
- SAFE_FREE((void**)&pszComment);
- }
- else debugLogA("Invalid comment");
- }
-
- // Look for need-authorization TLV
- if (pChain->getTLV(SSI_TLV_AWAITING_AUTH, 1)) {
- setByte(hContact, "Auth", 1);
- debugLogA("SSI contact need authorization");
- }
- else setByte(hContact, "Auth", 0);
-
- if (pTLV = pChain->getTLV(SSI_TLV_METAINFO_TOKEN, 1)) {
- setSettingBlob(hContact, DBSETTING_METAINFO_TOKEN, pTLV->pData, pTLV->wLen);
- if (pChain->getTLV(SSI_TLV_METAINFO_TIME, 1))
- setSettingDouble(hContact, DBSETTING_METAINFO_TIME, pChain->getDouble(SSI_TLV_METAINFO_TIME, 1));
- debugLogA("SSI contact has meta info token");
- }
- else {
- delSetting(hContact, DBSETTING_METAINFO_TOKEN);
- delSetting(hContact, DBSETTING_METAINFO_TIME);
- }
-
- { // store server-list item's TLV data
- BYTE* data = (BYTE*)SAFE_MALLOC(wTlvLength);
- int datalen = getServerDataFromItemTLV(pChain, data);
-
- if (datalen > 0)
- setSettingBlob(hContact, DBSETTING_SERVLIST_DATA, data, datalen);
- else
- delSetting(hContact, DBSETTING_SERVLIST_DATA);
-
- SAFE_FREE((void**)&data);
- }
- }
- }
- else // failed to add or other error
- debugLogA("SSI failed to handle %s Item '%s'", "Buddy", szRecordName);
- }
- break;
-
- case SSI_ITEM_GROUP:
- if ((wGroupId == 0) && (wItemId == 0)) {
- /* list of groups. wTlvType=1, data is TLV(C8) containing list of WORDs which */
- /* is the group ids
- /* we don't need to use this. Our processing is on-the-fly */
- /* this record is always sent first in the first packet only, */
- }
- else if (wGroupId != 0) {
- /* wGroupId != 0: a group record */
- if (wItemId == 0) { /* no item ID: this is a group */
- /* pszRecordName is the name of the group */
- ReserveServerID(wGroupId, SSIT_GROUP, 0);
-
- setServListGroupName(wGroupId, szRecordName);
-
- debugLogA("Group %s added to known groups.", szRecordName);
-
- /* demangle full grouppath, set it to known */
- SAFE_FREE(&szActiveSrvGroup);
- szActiveSrvGroup = getServListGroupCListPath(wGroupId);
- wActiveSrvGroupId = wGroupId;
-
- /* TLV contains a TLV(C8) with a list of WORDs of contained contact IDs */
- /* our processing is good enough that we don't need this duplication */
- }
- else debugLogA("Unhandled type 0x01, wItemID != 0");
- }
- else debugLogA("Unhandled type 0x01");
- break;
-
- case SSI_ITEM_PERMIT:
- {
- /* item on visible list */
- /* wItemId not related to contact ID */
- /* pszRecordName is the UIN */
- int bAdded;
- MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
-
- if (hContact != INVALID_CONTACT_ID) {
- if (bAdded) {
- debugLogA("SSI added new %s contact '%s'", "Permit", szRecordName);
- // It wasn't previously in the list, we hide it so it only appears in the visible list
- setContactHidden(hContact, 1);
- // Add it to the list, so it can be added properly if proper contact
- AddJustAddedContact(hContact);
- }
- else debugLogA("SSI %s contact already exists '%s'", "Permit", szRecordName);
-
- // Save permit ID
- setWord(hContact, DBSETTING_SERVLIST_PERMIT, wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
- // Set apparent mode
- setWord(hContact, "ApparentMode", ID_STATUS_ONLINE);
- debugLogA("Visible-contact (%s)", szRecordName);
- }
- else { // failed to add or other error
- debugLogA("SSI failed to handle %s Item '%s'", "Permit", szRecordName);
- ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- }
- }
- break;
-
- case SSI_ITEM_DENY:
- {
- /* Item on invisible list */
- /* wItemId not related to contact ID */
- /* pszRecordName is the UIN */
- int bAdded;
- MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
-
- if (hContact != INVALID_CONTACT_ID) {
- if (bAdded) {
- /* not already on list: added */
- debugLogA("SSI added new %s contact '%s'", "Deny", szRecordName);
- // It wasn't previously in the list, we hide it so it only appears in the visible list
- setContactHidden(hContact, 1);
- // Add it to the list, so it can be added properly if proper contact
- AddJustAddedContact(hContact);
- }
- else debugLogA("SSI %s contact already exists '%s'", "Deny", szRecordName);
-
- // Save Deny ID
- setWord(hContact, DBSETTING_SERVLIST_DENY, wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
-
- // Set apparent mode
- setWord(hContact, "ApparentMode", ID_STATUS_OFFLINE);
- debugLogA("Invisible-contact (%s)", szRecordName);
- }
- else { // failed to add or other error
- debugLogA("SSI failed to handle %s Item '%s'", "Deny", szRecordName);
- ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- }
- }
- break;
-
- case SSI_ITEM_VISIBILITY: /* My visibility settings */
- // Look for visibility TLV
- if (BYTE bVisibility = pChain->getByte(SSI_TLV_VISIBILITY, 1)) { // found it, store the id, we do not need current visibility - we do not rely on it
- setWord(DBSETTING_SERVLIST_PRIVACY, wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
-
- debugLogA("Visibility is %u", bVisibility);
- }
- else ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- break;
-
- case SSI_ITEM_IGNORE:
- {
- /* item on ignore list */
- /* wItemId not related to contact ID */
- /* pszRecordName is the UIN */
- int bAdded;
- MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
-
- if (hContact != INVALID_CONTACT_ID) {
- if (bAdded) {
- /* not already on list: add */
- debugLogA("SSI added new %s contact '%s'", "Ignore", szRecordName);
- // It wasn't previously in the list, we hide it
- setContactHidden(hContact, 1);
- // Add it to the list, so it can be added properly if proper contact
- AddJustAddedContact(hContact);
- }
- else debugLogA("SSI %s contact already exists '%s'", "Ignore", szRecordName);
-
- // Save Ignore ID
- setWord(hContact, DBSETTING_SERVLIST_IGNORE, wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
-
- // Set apparent mode & ignore
- setWord(hContact, "ApparentMode", ID_STATUS_OFFLINE);
- // set ignore all events
- CallService(MS_IGNORE_IGNORE, hContact, IGNOREEVENT_ALL);
- debugLogA("Ignore-contact (%s)", szRecordName);
- }
- else { // failed to add or other error
- debugLogA("SSI failed to handle %s Item '%s'", "Ignore", szRecordName);
- ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- }
- }
- break;
-
- case SSI_ITEM_UNKNOWN2:
- debugLogA("SSI unknown type 0x11");
-
- ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- break;
-
- case SSI_ITEM_IMPORTTIME:
- if (wGroupId == 0) {
- /* time our list was first imported */
- /* pszRecordName is "Import Time" */
- /* data is TLV(13) {TLV(D4) {time_t importTime}} */
- setDword("ImportTS", pChain->getDWord(SSI_TLV_TIMESTAMP, 1));
- setWord("SrvImportID", wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
- debugLogA("SSI %s item recognized", "first import");
- }
- break;
-
- case SSI_ITEM_BUDDYICON:
- if (wGroupId == 0) {
- /* our avatar MD5-hash */
- /* pszRecordName is "1" */
- /* data is TLV(D5) hash */
- /* we ignore this, just save the id */
- /* cause we get the hash again after login */
- if (!strcmpnull(szRecordName, "12")) { // need to handle Photo Item separately
- setWord(DBSETTING_SERVLIST_PHOTO, wItemId);
- debugLogA("SSI %s item recognized", "Photo");
- }
- else {
- setWord(DBSETTING_SERVLIST_AVATAR, wItemId);
- debugLogA("SSI %s item recognized", "Avatar");
- }
- ReserveServerID(wItemId, SSIT_ITEM, 0);
- }
- break;
-
- case SSI_ITEM_METAINFO:
- if (wGroupId == 0) {
- /* our meta info token & last update time */
- /* pszRecordName is "ICQ-MDIR" */
- /* data is TLV(15C) and TLV(15D) */
- oscar_tlv* pToken = pChain->getTLV(SSI_TLV_METAINFO_TOKEN, 1);
- oscar_tlv* pTime = pChain->getTLV(SSI_TLV_METAINFO_TIME, 1);
- if (pToken)
- setSettingBlob(NULL, DBSETTING_METAINFO_TOKEN, pToken->pData, pToken->wLen);
- if (pTime)
- setSettingDouble(NULL, DBSETTING_METAINFO_TIME, pChain->getDouble(SSI_TLV_METAINFO_TIME, 1));
-
- setWord(DBSETTING_SERVLIST_METAINFO, wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
-
- debugLogA("SSI %s item recognized", "Meta info");
- }
- break;
-
- case SSI_ITEM_CLIENTDATA:
- if (wGroupId == 0) {
- /* ICQ2k ShortcutBar Items */
- /* data is TLV(CD) text */
- if (wItemId)
- ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- }
-
- case SSI_ITEM_SAVED:
- case SSI_ITEM_PREAUTH:
- break;
-
- default:
- debugLogA("SSI unhandled item %2x", wTlvType);
-
- if (wItemId)
- ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
- break;
- }
-
- disposeChain(&pChain);
- } // end for
-
- // Release Memory
- SAFE_FREE(&szActiveSrvGroup);
-
- debugLogA("Bytes left: %u", wLen);
-
- setWord("SrvRecordCount", (WORD)(wRecord + getWord("SrvRecordCount", 0)));
-
- if (bIsLastPacket) {
- // No contacts left to sync
- bIsSyncingCL = FALSE;
-
- StoreServerIDs();
-
- icq_RescanInfoUpdate();
-
- if (wLen >= 4) {
- DWORD dwLastUpdateTime;
-
- /* finally we get a time_t of the last update time */
- unpackDWord(&buf, &dwLastUpdateTime);
- setDword("SrvLastUpdate", dwLastUpdateTime);
- debugLogA("Last update of server list was (%u) %s", dwLastUpdateTime, time2text(dwLastUpdateTime));
-
- sendRosterAck();
- handleServUINSettings(wListenPort, info);
-
- servlistProcessLogin();
- }
- else debugLogA("Last packet missed update time...");
-
- if (getWord("SrvRecordCount", 0) == 0) { // we got empty serv-list, create master group
- cookie_servlist_action* ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
- if (ack) {
- DWORD dwCookie;
-
- ack->dwAction = SSA_GROUP_UPDATE;
- ack->szGroupName = null_strdup("");
- dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_ADDTOLIST, 0, ack);
- icq_sendServerGroup(dwCookie, ICQ_LISTS_ADDTOLIST, 0, ack->szGroupName, NULL, 0, 0);
- }
- }
- // serv-list sync finished, clear just added contacts
- FlushJustAddedContacts();
- }
- else debugLogA("Waiting for more packets");
-}
-
-void CIcqProto::handleServerCListItemAdd(const char *szRecordName, WORD wGroupId, WORD wItemId, WORD wItemType, oscar_tlv_chain *pItemData)
-{
- if (wItemType == SSI_ITEM_IMPORTTIME) {
- if (pItemData) {
- setDword("ImportTS", pItemData->getDWord(SSI_TLV_TIMESTAMP, 1));
- setWord("SrvImportID", wItemId);
- ReserveServerID(wItemId, SSIT_ITEM, 0);
-
- debugLogA("Server added Import timestamp to list");
- return;
- }
- }
- // Reserve server-list ID
- ReserveServerID(wItemId, wItemType == SSI_ITEM_GROUP ? SSIT_GROUP : SSIT_ITEM, SSIF_UNHANDLED);
-}
-
-void CIcqProto::handleServerCListItemUpdate(const char *szRecordName, WORD wGroupId, WORD wItemId, WORD wItemType, oscar_tlv_chain *pItemData)
-{
- MCONTACT hContact = (wItemType == SSI_ITEM_BUDDY || wItemType == SSI_ITEM_DENY || wItemType == SSI_ITEM_PERMIT || wItemType == SSI_ITEM_IGNORE) ? HContactFromRecordName(szRecordName, NULL) : NULL;
-
- if (hContact != INVALID_CONTACT_ID && wItemType == SSI_ITEM_BUDDY) { // a contact was updated on server
- if (pItemData) {
- oscar_tlv *pAuth = pItemData->getTLV(SSI_TLV_AWAITING_AUTH, 1);
- BYTE bAuth = getByte(hContact, "Auth", 0);
-
- if (bAuth && !pAuth) { // server authorized our contact
- char str[MAX_PATH];
- char msg[MAX_PATH];
- char *nick = NickFromHandleUtf(hContact);
-
- setByte(hContact, "Auth", 0);
- mir_snprintf(str, MAX_PATH, ICQTranslateUtfStatic(LPGEN("Contact \"%s\" was authorized in the server list."), msg, MAX_PATH), nick);
- icq_LogMessage(LOG_WARNING, str);
- SAFE_FREE(&nick);
- }
- else if (!bAuth && pAuth) { // server took away authorization of our contact
- char str[MAX_PATH];
- char msg[MAX_PATH];
- char *nick = NickFromHandleUtf(hContact);
-
- setByte(hContact, "Auth", 1);
- mir_snprintf(str, MAX_PATH, ICQTranslateUtfStatic(LPGEN("Contact \"%s\" lost its authorization in the server list."), msg, MAX_PATH), nick);
- icq_LogMessage(LOG_WARNING, str);
- SAFE_FREE(&nick);
- }
- {
- // update metainfo data
- DBVARIANT dbv = { 0 };
- oscar_tlv *pToken = pItemData->getTLV(SSI_TLV_METAINFO_TOKEN, 1);
- oscar_tlv *pTime = pItemData->getTLV(SSI_TLV_METAINFO_TIME, 1);
-
- if (!getSetting(hContact, DBSETTING_METAINFO_TOKEN, &dbv)) {
- if (!pToken || dbv.cpbVal != pToken->wLen || memcmp(dbv.pbVal, pToken->pData, dbv.cpbVal)) {
- if (!pToken)
- debugLogA("Contact %s, meta info token removed", szRecordName);
- else
- debugLogA("Contact %s, meta info token changed", szRecordName);
-
- // user info was changed, refresh
- if (IsMetaInfoChanged(hContact))
- icq_QueueUser(hContact);
- }
-
- db_free(&dbv);
- }
- else if (pToken) {
- debugLogA("Contact %s, meta info token added", szRecordName);
-
- // user info was changed, refresh
- if (IsMetaInfoChanged(hContact))
- icq_QueueUser(hContact);
- }
-
- if (pToken)
- setSettingBlob(hContact, DBSETTING_METAINFO_TOKEN, pToken->pData, pToken->wLen);
- if (pTime)
- setSettingDouble(hContact, DBSETTING_METAINFO_TIME, pItemData->getDouble(SSI_TLV_METAINFO_TIME, 1));
- }
- {
- // update server's data - otherwise consequent operations can fail with 0x0E
- BYTE *data = (BYTE*)_alloca(pItemData->getChainLength());
- int datalen = getServerDataFromItemTLV(pItemData, data);
-
- if (datalen > 0)
- setSettingBlob(hContact, DBSETTING_SERVLIST_DATA, data, datalen);
- else
- delSetting(hContact, DBSETTING_SERVLIST_DATA);
- }
- }
- }
- else if (wItemType == SSI_ITEM_METAINFO) { // owner MetaInfo data updated
- if (pItemData) {
- DBVARIANT dbv = { 0 };
- oscar_tlv *pToken = pItemData->getTLV(SSI_TLV_METAINFO_TOKEN, 1);
- oscar_tlv *pTime = pItemData->getTLV(SSI_TLV_METAINFO_TIME, 1);
-
- if (!getSetting(hContact, DBSETTING_METAINFO_TOKEN, &dbv)) {
- if (!pToken || dbv.cpbVal != pToken->wLen || memcmp(dbv.pbVal, pToken->pData, dbv.cpbVal)) {
- if (!pToken)
- debugLogA("Owner meta info token removed");
- else
- debugLogA("Owner meta info token changed");
- }
-
- db_free(&dbv);
- }
-
- if (pToken)
- setSettingBlob(hContact, DBSETTING_METAINFO_TOKEN, pToken->pData, pToken->wLen);
- if (pTime)
- setSettingDouble(hContact, DBSETTING_METAINFO_TIME, pItemData->getDouble(SSI_TLV_METAINFO_TIME, 1));
- }
- }
- else if (wItemType == SSI_ITEM_GROUP) // group updated
- debugLogA("Server updated our group \"%s\" on list", szRecordName);
-}
-
-void CIcqProto::handleServerCListItemDelete(const char *szRecordName, WORD wGroupId, WORD wItemId, WORD wItemType, oscar_tlv_chain *pItemData)
-{
- MCONTACT hContact = (wItemType == SSI_ITEM_BUDDY || wItemType == SSI_ITEM_DENY || wItemType == SSI_ITEM_PERMIT || wItemType == SSI_ITEM_IGNORE) ? HContactFromRecordName(szRecordName, NULL) : NULL;
-
- if (hContact != INVALID_CONTACT_ID && wItemType == SSI_ITEM_BUDDY) { // a contact was removed from our list
- if (getWord(hContact, DBSETTING_SERVLIST_ID, 0) == wItemId) {
- delSetting(hContact, DBSETTING_SERVLIST_ID);
- delSetting(hContact, DBSETTING_SERVLIST_GROUP);
- delSetting(hContact, "Auth");
-
- char str[MAX_PATH];
- char msg[MAX_PATH];
- char *nick = NickFromHandleUtf(hContact);
-
- mir_snprintf(str, MAX_PATH, ICQTranslateUtfStatic(LPGEN("User \"%s\" was removed from server list."), msg, MAX_PATH), nick);
- icq_LogMessage(LOG_WARNING, str);
- SAFE_FREE(&nick);
- }
- }
- // Release server-list ID
- FreeServerID(wItemId, wItemType == SSI_ITEM_GROUP ? SSIT_GROUP : SSIT_ITEM);
-}
-
-void CIcqProto::handleRecvAuthRequest(unsigned char *buf, WORD wLen)
-{
- DWORD dwUin;
- uid_str szUid;
- if (!unpackUID(&buf, &wLen, &dwUin, &szUid))
- return;
-
- if (dwUin && IsOnSpammerList(dwUin)) {
- debugLogA("Ignored Message from known Spammer");
- return;
- }
-
- WORD wReasonLen;
- unpackWord(&buf, &wReasonLen);
- wLen -= 2;
- if (wReasonLen > wLen)
- return;
-
- int bAdded;
- MCONTACT hContact = HContactFromUID(dwUin, szUid, &bAdded);
-
- PROTORECVEVENT pre = { 0 };
- pre.timestamp = time(NULL);
- pre.lParam = sizeof(DWORD) * 2 + 5;
- // Prepare reason
- char *szReason = (char*)SAFE_MALLOC(wReasonLen + 1);
- int nReasonLen = 0;
- if (szReason) {
- memcpy(szReason, buf, wReasonLen);
- szReason[wReasonLen] = '\0';
- nReasonLen = strlennull(szReason);
-
- char *temp = (char*)_alloca(nReasonLen + 2);
- if (!IsUSASCII(szReason, nReasonLen) && UTF8_IsValid(szReason) && utf8_decode_static(szReason, temp, nReasonLen + 1))
- pre.flags |= PREF_UTF;
- }
-
- // Read nick name from DB
- char *szNick = NULL;
- if (dwUin) {
- DBVARIANT dbv = { 0 };
- if (pre.flags & PREF_UTF)
- szNick = getSettingStringUtf(hContact, "Nick", NULL);
- else if (!getString(hContact, "Nick", &dbv)) {
- szNick = null_strdup(dbv.pszVal);
- db_free(&dbv);
- }
- }
- else szNick = null_strdup(szUid);
-
- int nNickLen = strlennull(szNick);
-
- pre.lParam += nNickLen + nReasonLen;
-
- setByte(hContact, "Grant", 1);
-
- /*blob is: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)*/
- char *szBlob = (char *)_alloca(pre.lParam);
- char *pCurBlob = szBlob;
- *(DWORD*)pCurBlob = dwUin; pCurBlob += sizeof(DWORD);
- *(DWORD*)pCurBlob = DWORD(hContact); pCurBlob += sizeof(DWORD);
-
- if (nNickLen) { // if we have nick we add it, otherwise keep trailing zero
- memcpy(pCurBlob, szNick, nNickLen);
- pCurBlob += nNickLen;
- }
- *pCurBlob = 0; pCurBlob++; // Nick
- *pCurBlob = 0; pCurBlob++; // FirstName
- *pCurBlob = 0; pCurBlob++; // LastName
- *pCurBlob = 0; pCurBlob++; // email
- if (nReasonLen) {
- memcpy(pCurBlob, szReason, nReasonLen);
- pCurBlob += nReasonLen;
- }
- *pCurBlob = 0; // Reason
- pre.szMessage = szBlob;
-
- // TODO: Change for new auth system, include all known informations
- ProtoChainRecv(hContact, PSR_AUTH, 0, (LPARAM)&pre);
-
- SAFE_FREE(&szNick);
- SAFE_FREE(&szReason);
-}
-
-void CIcqProto::handleRecvAdded(unsigned char *buf, WORD wLen)
-{
- DWORD dwUin;
- uid_str szUid;
- DWORD cbBlob;
- PBYTE pBlob, pCurBlob;
- int bAdded;
- char* szNick;
- int nNickLen;
- DBVARIANT dbv = { 0 };
-
- if (!unpackUID(&buf, &wLen, &dwUin, &szUid)) return;
-
- if (dwUin && IsOnSpammerList(dwUin)) {
- debugLogA("Ignored Message from known Spammer");
- return;
- }
-
- MCONTACT hContact = HContactFromUID(dwUin, szUid, &bAdded);
-
- cbBlob = sizeof(DWORD) * 2 + 4;
-
- if (dwUin) {
- if (getString(hContact, "Nick", &dbv))
- nNickLen = 0;
- else {
- szNick = dbv.pszVal;
- nNickLen = strlennull(szNick);
- }
- }
- else nNickLen = strlennull(szUid);
-
- cbBlob += nNickLen;
-
- pCurBlob = pBlob = (PBYTE)_alloca(cbBlob);
- /*blob is: uin(DWORD), hContact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) */
- *(DWORD*)pCurBlob = dwUin; pCurBlob += sizeof(DWORD);
- *(DWORD*)pCurBlob = DWORD(hContact); pCurBlob += sizeof(DWORD);
- if (nNickLen && dwUin) { // if we have nick we add it, otherwise keep trailing zero
- memcpy(pCurBlob, szNick, nNickLen);
- pCurBlob += nNickLen;
- }
- else {
- memcpy(pCurBlob, szUid, nNickLen);
- pCurBlob += nNickLen;
- }
- *(char *)pCurBlob = 0; pCurBlob++;
- *(char *)pCurBlob = 0; pCurBlob++;
- *(char *)pCurBlob = 0; pCurBlob++;
- *(char *)pCurBlob = 0;
- // TODO: Change for new auth system
-
- AddEvent(NULL, EVENTTYPE_ADDED, time(NULL), 0, cbBlob, pBlob);
-}
-
-void CIcqProto::handleRecvAuthResponse(unsigned char *buf, WORD wLen)
-{
- DWORD dwUin;
- uid_str szUid;
- char* szNick = NULL;
- WORD nReasonLen;
- char* szReason;
- int bAdded;
-
- BYTE bResponse = 0xFF;
-
- if (!unpackUID(&buf, &wLen, &dwUin, &szUid)) return;
-
- if (dwUin && IsOnSpammerList(dwUin)) {
- debugLogA("Ignored Message from known Spammer");
- return;
- }
-
- MCONTACT hContact = HContactFromUID(dwUin, szUid, &bAdded);
- if (hContact != INVALID_CONTACT_ID)
- szNick = NickFromHandle(hContact);
-
- if (wLen > 0) {
- unpackByte(&buf, &bResponse);
- wLen -= 1;
- }
- if (wLen >= 2) {
- unpackWord(&buf, &nReasonLen);
- wLen -= 2;
- if (wLen >= nReasonLen) {
- szReason = (char*)_alloca(nReasonLen + 1);
- unpackString(&buf, szReason, nReasonLen);
- szReason[nReasonLen] = '\0';
- }
- }
-
- switch (bResponse) {
- case 0:
- debugLogA("Authorization request %s by %s", "denied", strUID(dwUin, szUid));
- // TODO: Add to system history as soon as new auth system is ready
- break;
-
- case 1:
- setByte(hContact, "Auth", 0);
- debugLogA("Authorization request %s by %s", "granted", strUID(dwUin, szUid));
- // TODO: Add to system history as soon as new auth system is ready
- break;
-
- default:
- debugLogA("Unknown Authorization request response (%u) from %s", bResponse, strUID(dwUin, szUid));
- break;
-
- }
- SAFE_FREE(&szNick);
-}
-
-// Updates the visibility code used while in SSI mode. If a server ID is
-// not stored in the local DB, a new ID will be added to the server list.
-//
-// Possible values are:
-// 01 - Allow all users to see you
-// 02 - Block all users from seeing you
-// 03 - Allow only users in the permit list to see you
-// 04 - Block only users in the invisible list from seeing you
-// 05 - Allow only users in the buddy list to see you
-//
-void CIcqProto::updateServVisibilityCode(BYTE bCode)
-{
- icq_packet packet;
- WORD wVisibilityID;
- WORD wCommand;
-
- if ((bCode > 0) && (bCode < 6)) {
- cookie_servlist_action* ack;
- DWORD dwCookie;
- BYTE bVisibility = getByte("SrvVisibility", 0);
-
- if (bVisibility == bCode) // if no change was made, not necescary to update that
- return;
- setByte("SrvVisibility", bCode);
-
- // Do we have a known server visibility ID? We should, unless we just subscribed to the serv-list for the first time
- if ((wVisibilityID = getWord(DBSETTING_SERVLIST_PRIVACY, 0)) == 0) {
- // No, create a new random ID
- wVisibilityID = GenerateServerID(SSIT_ITEM, 0);
- setWord(DBSETTING_SERVLIST_PRIVACY, wVisibilityID);
- wCommand = ICQ_LISTS_ADDTOLIST;
-
- debugLogA("Made new srvVisibilityID, id is %u, code is %u", wVisibilityID, bCode);
- }
- else {
- debugLogA("Reused srvVisibilityID, id is %u, code is %u", wVisibilityID, bCode);
- wCommand = ICQ_LISTS_UPDATEGROUP;
- }
-
- ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
- if (!ack) {
- debugLogA("Cookie alloc failure.");
- return; // out of memory, go away
- }
-
- ack->dwAction = SSA_VISIBILITY; // update visibility
- dwCookie = AllocateCookie(CKT_SERVERLIST, wCommand, 0, ack); // take cookie
-
- // Build and send packet
- serverPacketInit(&packet, 25);
- packFNACHeader(&packet, ICQ_LISTS_FAMILY, wCommand, 0, dwCookie);
- packWord(&packet, 0); // Name (null)
- packWord(&packet, 0); // GroupID (0 if not relevant)
- packWord(&packet, wVisibilityID); // EntryID
- packWord(&packet, SSI_ITEM_VISIBILITY); // EntryType
- packWord(&packet, 5); // Length in bytes of following TLV
- packTLV(&packet, SSI_TLV_VISIBILITY, 1, &bCode); // TLV (Visibility)
- sendServPacket(&packet);
- // There is no need to send ICQ_LISTS_CLI_MODIFYSTART or
- // ICQ_LISTS_CLI_MODIFYEND when modifying the visibility code
- }
-}
-
-// Updates the avatar hash used while in SSI mode. If a server ID is
-// not stored in the local DB, a new ID will be added to the server list.
-void CIcqProto::updateServAvatarHash(BYTE *pHash, int size)
-{
- void** pDoubleObject = NULL;
- void* doubleObject = NULL;
- DWORD dwOperationFlags = 0;
- WORD wAvatarID;
- WORD wCommand;
- char szItemName[2] = { 0, 0 };
-
- int bResetHash = 0;
- DBVARIANT dbvHash;
- if (!getSetting(NULL, "AvatarHash", &dbvHash)) {
- szItemName[0] = 0x30 + dbvHash.pbVal[1];
-
- if (memcmp(pHash, dbvHash.pbVal, 2) != 0) // add code to remove old hash from server
- bResetHash = 1;
-
- db_free(&dbvHash);
- }
-
- if (bResetHash) { // start update session
- // pair the packets (need to be send in the correct order
- dwOperationFlags |= SSOF_BEGIN_OPERATION | SSOF_END_OPERATION;
- pDoubleObject = &doubleObject;
- }
-
- if (bResetHash || !pHash) {
- // Do we have a known server avatar ID?
- if (wAvatarID = getWord(DBSETTING_SERVLIST_AVATAR, 0)) {
- cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
- if (!ack) {
- debugLogA("Cookie alloc failure.");
- return; // out of memory, go away
- }
- ack->dwAction = SSA_REMOVEAVATAR; // update avatar hash
- ack->wContactId = wAvatarID;
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_REMOVEFROMLIST, 0, ack); // take cookie
- icq_sendServerItem(dwCookie, ICQ_LISTS_REMOVEFROMLIST, 0, wAvatarID, szItemName, NULL, 0, SSI_ITEM_BUDDYICON, SSOP_ITEM_ACTION | dwOperationFlags, 400, pDoubleObject);
- }
- }
-
- if (!pHash)
- return;
-
- WORD hashsize = size - 2;
-
- // Do we have a known server avatar ID? We should, unless we just subscribed to the serv-list for the first time
- if (bResetHash || (wAvatarID = getWord(DBSETTING_SERVLIST_AVATAR, 0)) == 0) {
- // No, create a new random ID
- wAvatarID = GenerateServerID(SSIT_ITEM, 0);
- wCommand = ICQ_LISTS_ADDTOLIST;
- debugLogA("Made new srvAvatarID, id is %u", wAvatarID);
- }
- else {
- debugLogA("Reused srvAvatarID, id is %u", wAvatarID);
- wCommand = ICQ_LISTS_UPDATEGROUP;
- }
-
- cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
- if (!ack) {
- debugLogA("Cookie alloc failure.");
- return; // out of memory, go away
- }
- ack->dwAction = SSA_SETAVATAR; // update avatar hash
- ack->wContactId = wAvatarID;
- DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, wCommand, 0, ack); // take cookie
-
- szItemName[0] = 0x30 + pHash[1];
-
- // Build the packet
- WORD wTLVlen = 8 + hashsize;
-
- // Initialize our handy data buffer
- icq_packet pBuffer;
- pBuffer.wPlace = 0;
- pBuffer.pData = (BYTE *)_alloca(wTLVlen);
- pBuffer.wLen = wTLVlen;
-
- packTLV(&pBuffer, SSI_TLV_NAME, 0, NULL); // TLV (Name)
- packTLV(&pBuffer, SSI_TLV_AVATARHASH, hashsize, pHash + 2); // TLV (Hash)
-
- icq_sendServerItem(dwCookie, wCommand, 0, wAvatarID, szItemName, pBuffer.pData, wTLVlen, SSI_ITEM_BUDDYICON, SSOP_ITEM_ACTION | dwOperationFlags, 400, pDoubleObject);
- // There is no need to send ICQ_LISTS_CLI_MODIFYSTART or
- // ICQ_LISTS_CLI_MODIFYEND when modifying the avatar hash
-}
-
-// Should be called before the server list is modified. When all
-// modifications are done, call icq_sendServerEndOperation().
-// Called automatically thru server-list update board!
-void CIcqProto::icq_sendServerBeginOperation(int bImport)
-{
- WORD wImportID = getWord("SrvImportID", 0);
-
- if (bImport && wImportID) { // we should be importing, check if already have import item
- if (getDword("ImportTS", 0) + 604800 < getDword("LogonTS", 0)) { // is the timestamp week older, clear it and begin new import
- DWORD dwCookie;
- cookie_servlist_action* ack;
-
- if (ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action))) { // we have cookie good, go on
- ack->dwAction = SSA_IMPORT;
- dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_REMOVEFROMLIST, 0, ack);
-
- icq_sendSimpleItem(dwCookie, ICQ_LISTS_REMOVEFROMLIST, 0, "ImportTime", 0, wImportID, SSI_ITEM_IMPORTTIME, SSOP_ITEM_ACTION | SSOF_SEND_DIRECTLY, 100);
- }
- }
- }
-
- icq_packet packet;
- serverPacketInit(&packet, (WORD)(bImport ? 14 : 10));
- packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_MODIFYSTART);
- if (bImport)
- packDWord(&packet, 1 << 0x10);
- sendServPacket(&packet);
-}
-
-// Should be called after the server list has been modified to inform
-// the server that we are done.
-// Called automatically thru server-list update board!
-void CIcqProto::icq_sendServerEndOperation()
-{
- icq_packet packet;
- serverPacketInit(&packet, 10);
- packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_MODIFYEND);
- sendServPacket(&packet);
-}
-
-// Sent when the last roster packet has been received
-void CIcqProto::sendRosterAck(void)
-{
- icq_packet packet;
- serverPacketInit(&packet, 10);
- packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_GOTLIST);
- sendServPacket(&packet);
-
- debugLogA("Sent SNAC(x13,x07) - CLI_ROSTERACK");
-}
+// ---------------------------------------------------------------------------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-2014 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.
+// -----------------------------------------------------------------------------
+
+#include "icqoscar.h"
+
+static int unpackServerListItem(BYTE **pbuf, WORD *pwLen, char *pszRecordName, WORD *pwGroupId, WORD *pwItemId, WORD *pwItemType, WORD *pwTlvLength);
+
+
+void CIcqProto::handleServCListFam(BYTE *pBuffer, WORD wBufferLength, snac_header* pSnacHeader, serverthread_info *info)
+{
+ switch (pSnacHeader->wSubtype) {
+ case ICQ_LISTS_ACK: // UPDATE_ACK
+ if (wBufferLength >= 2) {
+ WORD wError;
+ cookie_servlist_action* sc;
+
+ unpackWord(&pBuffer, &wError);
+
+ if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // look for action cookie
+ debugLogA("Received expected server list ack, action: %d, result: %d", sc->dwAction, wError);
+ FreeCookie(pSnacHeader->dwRef); // release cookie
+
+ if (sc->dwAction == SSA_ACTION_GROUP) { // group cookie, handle sub-items
+ int i;
+
+ debugLogA("Server-List: Grouped action contains %d actions.", sc->dwGroupCount);
+
+ pBuffer -= 2; // revoke unpack
+ if (wBufferLength != 2 * sc->dwGroupCount)
+ debugLogA("Error: Server list ack does not contain expected amount of result codes (%u != %u)", wBufferLength / 2, sc->dwGroupCount);
+
+ for (i = 0; i < sc->dwGroupCount; i++) {
+ if (wBufferLength >= 2) { // get proper result code
+ unpackWord(&pBuffer, &wError);
+ wBufferLength -= 2;
+ }
+ else // missing result code, give some special
+ wError = -1;
+
+ debugLogA("Action: %d, ack result: %d", sc->pGroupItems[i]->dwAction, wError);
+
+ // call normal ack handler
+ handleServerCListAck(sc->pGroupItems[i], wError);
+ }
+ // Release cookie
+ SAFE_FREE((void**)&sc->pGroupItems);
+ SAFE_FREE((void**)&sc);
+ }
+ else // single ack
+ handleServerCListAck(sc, wError);
+ }
+ else debugLogA("Received unexpected server list ack %u", wError);
+ }
+ break;
+
+ case ICQ_LISTS_SRV_REPLYLISTS:
+ /* received server-list rights */
+ handleServerCListRightsReply(pBuffer, wBufferLength);
+ debugLogA("Server sent SNAC(x13,x03) - SRV_REPLYLISTS");
+ break;
+
+ case ICQ_LISTS_LIST: // SRV_REPLYROSTER
+ {
+ cookie_servlist_action* sc;
+ BOOL blWork = bIsSyncingCL;
+ bIsSyncingCL = TRUE; // this is not used if cookie takes place
+
+ if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // we do it by reliable cookie
+ if (!sc->lParam) { // is this first packet ?
+ ResetSettingsOnListReload();
+ sc->lParam = 1;
+ }
+ handleServerCListReply(pBuffer, wBufferLength, pSnacHeader->wFlags, info);
+ if (!(pSnacHeader->wFlags & 0x0001)) // was that last packet ?
+ ReleaseCookie(pSnacHeader->dwRef); // yes, release cookie
+ }
+ else { // use old fake
+ if (!blWork) // this can fail on some crazy situations
+ ResetSettingsOnListReload();
+
+ handleServerCListReply(pBuffer, wBufferLength, pSnacHeader->wFlags, info);
+ }
+ }
+ break;
+
+ case ICQ_LISTS_UPTODATE: // SRV_REPLYROSTEROK
+ bIsSyncingCL = FALSE;
+ {
+ cookie_servlist_action* sc;
+ if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // we requested servlist check
+ debugLogA("Server stated roster is ok.");
+ ReleaseCookie(pSnacHeader->dwRef);
+ LoadServerIDs();
+ }
+ else debugLogA("Server sent unexpected SNAC(x13,x0F) - SRV_REPLYROSTEROK");
+
+ // This will activate the server side list
+ sendRosterAck(); // this must be here, cause of failures during cookie alloc
+ handleServUINSettings(wListenPort, info);
+ }
+ break;
+
+ case ICQ_LISTS_CLI_MODIFYSTART:
+ debugLogA("Server sent SNAC(x13,x%02x) - %s", ICQ_LISTS_CLI_MODIFYSTART, "Server is modifying contact list");
+ break;
+
+ case ICQ_LISTS_CLI_MODIFYEND:
+ debugLogA("Server sent SNAC(x13,x%02x) - %s", ICQ_LISTS_CLI_MODIFYEND, "End of server modification");
+ break;
+
+ case ICQ_LISTS_ADDTOLIST:
+ case ICQ_LISTS_UPDATEGROUP:
+ case ICQ_LISTS_REMOVEFROMLIST:
+ {
+ int nItems = 0;
+
+ while (wBufferLength >= 10) {
+ WORD wGroupId, wItemId, wItemType, wTlvLen;
+ uid_str szRecordName;
+
+ if (unpackServerListItem(&pBuffer, &wBufferLength, szRecordName, &wGroupId, &wItemId, &wItemType, &wTlvLen)) {
+ BYTE *buf = pBuffer;
+ oscar_tlv_chain *pChain = NULL;
+
+ nItems++;
+
+ // parse possible item's data
+ if (wBufferLength >= wTlvLen && wTlvLen > 0) {
+ pChain = readIntoTLVChain(&buf, wTlvLen, 0);
+ pBuffer += wTlvLen;
+ wBufferLength -= wTlvLen;
+ }
+ else if (wTlvLen > 0)
+ wBufferLength = 0;
+
+ // process item change
+ if (pSnacHeader->wSubtype == ICQ_LISTS_ADDTOLIST)
+ handleServerCListItemAdd(szRecordName, wGroupId, wItemId, wItemType, pChain);
+ else if (pSnacHeader->wSubtype == ICQ_LISTS_UPDATEGROUP)
+ handleServerCListItemUpdate(szRecordName, wGroupId, wItemId, wItemType, pChain);
+ else if (pSnacHeader->wSubtype == ICQ_LISTS_REMOVEFROMLIST)
+ handleServerCListItemDelete(szRecordName, wGroupId, wItemId, wItemType, pChain);
+
+ // release memory
+ disposeChain(&pChain);
+ }
+ }
+ {
+ // log packet basics
+ char *szChange;
+
+ if (pSnacHeader->wSubtype == ICQ_LISTS_ADDTOLIST)
+ szChange = "Server added %u item(s) to list";
+ else if (pSnacHeader->wSubtype == ICQ_LISTS_UPDATEGROUP)
+ szChange = "Server updated %u item(s) on list";
+ else if (pSnacHeader->wSubtype == ICQ_LISTS_REMOVEFROMLIST)
+ szChange = "Server removed %u item(s) from list";
+
+ char szLogText[MAX_PATH];
+ mir_snprintf(szLogText, MAX_PATH, szChange, nItems);
+ debugLogA("Server sent SNAC(x13,x%02x) - %s", pSnacHeader->wSubtype, szLogText);
+ }
+ }
+ break;
+
+ case ICQ_LISTS_AUTHREQUEST:
+ handleRecvAuthRequest(pBuffer, wBufferLength);
+ break;
+
+ case ICQ_LISTS_SRV_AUTHRESPONSE:
+ handleRecvAuthResponse(pBuffer, wBufferLength);
+ break;
+
+ case ICQ_LISTS_AUTHGRANTED:
+ debugLogA("Server sent SNAC(x13,x%02x) - %s", ICQ_LISTS_AUTHGRANTED, "User granted us future authorization");
+ break;
+
+ case ICQ_LISTS_YOUWEREADDED:
+ handleRecvAdded(pBuffer, wBufferLength);
+ break;
+
+ case ICQ_LISTS_ERROR:
+ if (wBufferLength >= 2) {
+ WORD wError;
+ cookie_servlist_action* sc;
+
+ unpackWord(&pBuffer, &wError);
+
+ if (FindCookie(pSnacHeader->dwRef, NULL, (void**)&sc)) { // look for action cookie
+ debugLogA("Received server list error, action: %d, result: %d", sc->dwAction, wError);
+ FreeCookie(pSnacHeader->dwRef); // release cookie
+
+ if (sc->dwAction == SSA_CHECK_ROSTER) { // the serv-list is unavailable turn it off
+ icq_LogMessage(LOG_ERROR, LPGEN("Server contact list is unavailable, Miranda will use local contact list."));
+ m_bSsiEnabled = 0;
+ handleServUINSettings(wListenPort, info);
+ }
+ /// FIXME: properly release pending operations & cookie memory
+ SAFE_FREE((void**)&sc);
+ }
+ else LogFamilyError(ICQ_LISTS_FAMILY, wError);
+ }
+ break;
+
+ default:
+ debugLogA("Warning: Ignoring SNAC(x%02x,x%02x) - Unknown SNAC (Flags: %u, Ref: %u)", ICQ_LISTS_FAMILY, pSnacHeader->wSubtype, pSnacHeader->wFlags, pSnacHeader->dwRef);
+ break;
+ }
+}
+
+
+static int unpackServerListItem(BYTE **pbuf, WORD *pwLen, char *pszRecordName, WORD *pwGroupId, WORD *pwItemId, WORD *pwItemType, WORD *pwTlvLength)
+{
+ WORD wRecordNameLen;
+
+ // The name of the entry. If this is a group header, then this
+ // is the name of the group. If it is a plain contact list entry,
+ // then it's the UIN of the contact.
+ unpackWord(pbuf, &wRecordNameLen);
+ if (*pwLen < 10 + wRecordNameLen || wRecordNameLen >= MAX_PATH)
+ return 0; // Failure
+
+ unpackString(pbuf, pszRecordName, wRecordNameLen);
+ if (pszRecordName)
+ pszRecordName[wRecordNameLen] = '\0';
+
+ // The group identifier this entry belongs to. If 0, this is meta information or
+ // a contact without a group
+ unpackWord(pbuf, pwGroupId);
+
+ // The ID of this entry. Group headers have ID 0. Otherwise, this
+ // is a random number generated when the user is added to the
+ // contact list, or when the user is ignored. See CLI_ADDBUDDY.
+ unpackWord(pbuf, pwItemId);
+
+ // This field indicates what type of entry this is
+ unpackWord(pbuf, pwItemType);
+
+ // The length in bytes of the following TLV chain
+ unpackWord(pbuf, pwTlvLength);
+
+ *pwLen -= wRecordNameLen + 10;
+
+ return 1; // Success
+}
+
+
+void CIcqProto::handleServerCListRightsReply(BYTE *buf, WORD wLen)
+{
+ /* received list rights, store the item limits for future use */
+ oscar_tlv_chain* chain;
+
+ memset(m_wServerListLimits, -1, sizeof(m_wServerListLimits));
+ m_wServerListGroupMaxContacts = 0;
+ m_wServerListRecordNameMaxLength = 0xFFFF;
+
+ if (chain = readIntoTLVChain(&buf, wLen, 0)) {
+ // determine max number of contacts in a group
+ m_wServerListGroupMaxContacts = chain->getWord(0x0C, 1);
+ // determine length limit for server-list item's name
+ m_wServerListRecordNameMaxLength = chain->getWord(0x06, 1);
+
+ if (oscar_tlv *pTLV = chain->getTLV(0x04, 1)) { // limits for item types
+ WORD *pLimits = (WORD*)pTLV->pData;
+ for (int i = 0; i < pTLV->wLen / 2; i++) {
+ m_wServerListLimits[i] = (pLimits[i] & 0xFF) << 8 | (pLimits[i] >> 8);
+
+ if (i + 1 >= SIZEOF(m_wServerListLimits))
+ break;
+ }
+
+ debugLogA("SSI: Max %d contacts (%d per group), %d groups, %d permit, %d deny, %d ignore items.", m_wServerListLimits[SSI_ITEM_BUDDY], m_wServerListGroupMaxContacts, m_wServerListLimits[SSI_ITEM_GROUP], m_wServerListLimits[SSI_ITEM_PERMIT], m_wServerListLimits[SSI_ITEM_DENY], m_wServerListLimits[SSI_ITEM_IGNORE]);
+ }
+
+ disposeChain(&chain);
+ }
+}
+
+DWORD CIcqProto::updateServerGroupData(WORD wGroupId, void *groupData, int groupSize, DWORD dwOperationFlags)
+{
+ cookie_servlist_action* ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
+ if (!ack) {
+ debugLogA("Updating of group on server list failed (malloc error)");
+ return 0;
+ }
+ ack->dwAction = SSA_GROUP_UPDATE;
+ ack->szGroupName = getServListGroupName(wGroupId);
+ ack->wGroupId = wGroupId;
+
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack);
+ return icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, ack->wGroupId, ack->szGroupName, groupData, groupSize, dwOperationFlags);
+}
+
+void CIcqProto::handleServerCListAck(cookie_servlist_action* sc, WORD wError)
+{
+ switch (sc->dwAction) {
+ case SSA_VISIBILITY:
+ if (wError)
+ debugLogA("Server visibility update failed, error %d", wError);
+ break;
+
+ case SSA_CONTACT_UPDATE:
+ servlistPendingRemoveContact(sc->hContact, sc->wContactId, sc->wGroupId, wError ? PENDING_RESULT_FAILED : PENDING_RESULT_SUCCESS);
+ if (wError) {
+ debugLogA("Updating of server contact failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Updating of server contact failed."));
+ }
+ break;
+
+ case SSA_PRIVACY_ADD:
+ if (wError) {
+ debugLogA("Adding of privacy item to server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Adding of privacy item to server list failed."));
+ }
+ break;
+
+ case SSA_PRIVACY_REMOVE:
+ if (wError) {
+ debugLogA("Removing of privacy item from server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Removing of privacy item from server list failed."));
+ }
+ FreeServerID(sc->wContactId, SSIT_ITEM); // release server id
+ break;
+
+ case SSA_CONTACT_ADD:
+ if (wError) {
+ if (wError == 0xE) { // server refused to add contact w/o auth, add with
+ debugLogA("Contact could not be added without authorization, add with await auth flag.");
+
+ setByte(sc->hContact, "Auth", 1); // we need auth
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_ADDTOLIST, sc->hContact, sc);
+ icq_sendServerContact(sc->hContact, dwCookie, ICQ_LISTS_ADDTOLIST, sc->wGroupId, sc->wContactId, SSOP_ITEM_ACTION | SSOF_CONTACT, 500, NULL);
+
+ sc = NULL; // we do not want it to be freed now
+ break;
+ }
+ FreeServerID(sc->wContactId, SSIT_ITEM);
+
+ debugLogA("Adding of contact to server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Adding of contact to server list failed."));
+
+ servlistPendingRemoveContact(sc->hContact, 0, sc->wGroupId, PENDING_RESULT_FAILED);
+
+ servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
+ }
+ else {
+ void* groupData;
+ int groupSize;
+
+ setWord(sc->hContact, DBSETTING_SERVLIST_ID, sc->wContactId);
+ setWord(sc->hContact, DBSETTING_SERVLIST_GROUP, sc->wGroupId);
+
+ servlistPendingRemoveContact(sc->hContact, sc->wContactId, sc->wGroupId, PENDING_RESULT_SUCCESS);
+
+ if (groupData = collectBuddyGroup(sc->wGroupId, &groupSize)) { // the group is not empty, just update it
+ updateServerGroupData(sc->wGroupId, groupData, groupSize, SSOF_END_OPERATION);
+ SAFE_FREE((void**)&groupData);
+ }
+ else { // this should never happen
+ debugLogA("Group update failed.");
+ servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
+ }
+ }
+ break;
+
+ case SSA_GROUP_ADD:
+ if (wError) {
+ FreeServerID(sc->wGroupId, SSIT_GROUP);
+ debugLogA("Adding of group to server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Adding of group to server list failed."));
+
+ servlistPendingRemoveGroup(sc->szGroup, 0, PENDING_RESULT_FAILED);
+ }
+ else { // group added, we need to update master group
+ void* groupData;
+ int groupSize;
+
+ setServListGroupName(sc->wGroupId, sc->szGroupName); // add group to namelist
+ { // add group to known
+ char *szCListGroup = getServListGroupCListPath(sc->wGroupId);
+
+ // create link to the original CList group
+ setServListGroupLinkID(sc->szGroup, sc->wGroupId);
+
+ servlistPendingRemoveGroup(sc->szGroup, sc->wGroupId, PENDING_RESULT_SUCCESS);
+ SAFE_FREE((void**)&szCListGroup);
+ }
+
+ groupData = collectGroups(&groupSize);
+ groupData = SAFE_REALLOC(groupData, groupSize + 2);
+ *(((WORD*)groupData) + (groupSize >> 1)) = sc->wGroupId; // add this new group id
+ groupSize += 2;
+
+ cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
+ if (ack) {
+ ack->dwAction = SSA_GROUP_UPDATE;
+
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack);
+ icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, 0, ack->szGroupName, groupData, groupSize, SSOF_END_OPERATION);
+ }
+ else // end server modifications here
+ servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100);
+
+ SAFE_FREE((void**)&groupData);
+ }
+ if (sc->szGroup != sc->szGroupName)
+ SAFE_FREE((void**)&sc->szGroup);
+
+ SAFE_FREE((void**)&sc->szGroupName);
+ break;
+
+ case SSA_CONTACT_REMOVE:
+ if (!wError) {
+ void* groupData;
+ int groupSize;
+
+ setWord(sc->hContact, DBSETTING_SERVLIST_ID, 0); // clear the values
+ setWord(sc->hContact, DBSETTING_SERVLIST_GROUP, 0);
+
+ FreeServerID(sc->wContactId, SSIT_ITEM);
+
+ servlistPendingRemoveContact(sc->hContact, 0, sc->wGroupId, PENDING_RESULT_SUCCESS);
+
+ if (groupData = collectBuddyGroup(sc->wGroupId, &groupSize)) { // the group is still not empty, just update it
+ updateServerGroupData(sc->wGroupId, groupData, groupSize, SSOF_END_OPERATION);
+ }
+ else // the group is empty, delete it
+ {
+ char *szGroup = getServListGroupCListPath(sc->wGroupId);
+
+ servlistRemoveGroup(szGroup, sc->wGroupId);
+ SAFE_FREE((void**)&szGroup);
+ }
+ SAFE_FREE((void**)&groupData); // free the memory
+ }
+ else {
+ debugLogA("Removing of contact from server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Removing of contact from server list failed."));
+
+ servlistPendingRemoveContact(sc->hContact, sc->wContactId, sc->wGroupId, PENDING_RESULT_FAILED);
+
+ servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
+ }
+ break;
+
+ case SSA_GROUP_UPDATE:
+ if (wError) {
+ debugLogA("Updating of group on server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Updating of group on server list failed."));
+ }
+ SAFE_FREE((void**)&sc->szGroupName);
+ break;
+
+ case SSA_GROUP_REMOVE:
+ SAFE_FREE((void**)&sc->szGroupName);
+ if (wError) {
+ debugLogA("Removing of group from server list failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Removing of group from server list failed."));
+
+ servlistPendingRemoveGroup(sc->szGroup, 0, PENDING_RESULT_FAILED);
+
+ servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
+ SAFE_FREE((void**)&sc->szGroup);
+ }
+ else { // group removed, we need to update master group
+ void* groupData;
+ int groupSize;
+
+ setServListGroupName(sc->wGroupId, NULL); // clear group from namelist
+ FreeServerID(sc->wGroupId, SSIT_GROUP);
+ removeGroupPathLinks(sc->wGroupId);
+
+ servlistPendingRemoveGroup(sc->szGroup, 0, PENDING_RESULT_SUCCESS);
+ SAFE_FREE((void**)&sc->szGroup);
+
+ groupData = collectGroups(&groupSize);
+ sc->wGroupId = 0;
+ sc->dwAction = SSA_GROUP_UPDATE;
+ sc->szGroupName = NULL;
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, sc);
+
+ icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, 0, sc->szGroupName, groupData, groupSize, SSOF_END_OPERATION);
+ // end server modifications here
+
+ sc = NULL; // we do not want to be freed here
+
+ SAFE_FREE((void**)&groupData);
+ }
+ break;
+
+ case SSA_CONTACT_SET_GROUP:
+ // we moved contact to another group
+ if (sc->lParam == -1) // the first was an error
+ break;
+
+ if (wError) {
+ if (wError == 0x0E && sc->lParam == 1) { // second ack - adding failed with error 0x0E, try to add with AVAIT_AUTH flag
+ if (!getByte(sc->hContact, "Auth", 0)) { // we tried without AWAIT_AUTH, try again with it
+ debugLogA("Contact could not be added without authorization, add with await auth flag.");
+ setByte(sc->hContact, "Auth", 1); // we need auth
+ }
+ else { // we tried with AWAIT_AUTH, try again without
+ debugLogA("Contact count not be added awaiting authorization, try authorized.");
+ setByte(sc->hContact, "Auth", 0);
+ }
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_ADDTOLIST, sc->hContact, sc);
+ icq_sendServerContact(sc->hContact, dwCookie, ICQ_LISTS_ADDTOLIST, sc->wNewGroupId, sc->wNewContactId, SSOP_ITEM_ACTION | SSOF_CONTACT, 400, NULL);
+
+ sc->lParam = 2; // do not cycle
+ sc = NULL; // we do not want to be freed here
+ break;
+ }
+ FreeServerID(sc->wNewContactId, SSIT_ITEM);
+ debugLogA("Moving of user to another group on server list failed, error %d", wError);
+ icq_LogMessage(LOG_ERROR, LPGEN("Moving of user to another group on server list failed."));
+
+ servlistPendingRemoveContact(sc->hContact, 0, (WORD)(sc->lParam ? sc->wGroupId : sc->wNewGroupId), PENDING_RESULT_FAILED);
+
+ servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); // end server modifications here
+
+ if (!sc->lParam) { // is this first ack ?
+ sc->lParam = -1;
+ sc = NULL; // this can't be freed here
+ }
+ break;
+ }
+
+ if (sc->lParam) { // is this the second ack ?
+ void* groupData;
+ int groupSize;
+ int bEnd = 1; // shall we end the sever modifications
+
+ setWord(sc->hContact, DBSETTING_SERVLIST_ID, sc->wNewContactId);
+ setWord(sc->hContact, DBSETTING_SERVLIST_GROUP, sc->wNewGroupId);
+
+ servlistPendingRemoveContact(sc->hContact, sc->wNewContactId, sc->wNewGroupId, PENDING_RESULT_SUCCESS);
+
+ if (groupData = collectBuddyGroup(sc->wGroupId, &groupSize)) // update the group we moved from
+ { // the group is still not empty, just update it
+ updateServerGroupData(sc->wGroupId, groupData, groupSize, 0);
+ SAFE_FREE((void**)&groupData); // free the memory
+ }
+ else { // the group is empty, delete it
+ char* szGroup = getServListGroupCListPath(sc->wGroupId);
+
+ servlistRemoveGroup(szGroup, sc->wGroupId);
+ SAFE_FREE((void**)&szGroup);
+ bEnd = 0; // here the modifications go on
+ }
+
+ groupData = collectBuddyGroup(sc->wNewGroupId, &groupSize); // update the group we moved to
+ updateServerGroupData(sc->wNewGroupId, groupData, groupSize, bEnd ? SSOF_END_OPERATION : 0);
+ // end server modifications here
+ SAFE_FREE((void**)&groupData);
+
+ }
+ else // contact was deleted from server-list
+ {
+ delSetting(sc->hContact, DBSETTING_SERVLIST_ID);
+ delSetting(sc->hContact, DBSETTING_SERVLIST_GROUP);
+ FreeServerID(sc->wContactId, SSIT_ITEM); // release old contact id
+ sc->lParam = 1;
+ sc = NULL; // wait for second ack
+ }
+ break;
+
+ case SSA_CONTACT_FIX_AUTH:
+ if (wError) { // FIXME: something failed, we should handle it properly
+ }
+ break;
+
+ case SSA_GROUP_RENAME:
+ if (wError) {
+ debugLogA("Renaming of server group failed, error %d", wError);
+ icq_LogMessage(LOG_WARNING, LPGEN("Renaming of server group failed."));
+
+ servlistPendingRemoveGroup(sc->szGroup, sc->wGroupId, PENDING_RESULT_FAILED);
+ }
+ else {
+ setServListGroupName(sc->wGroupId, sc->szGroupName);
+ removeGroupPathLinks(sc->wGroupId);
+ { // add group to known
+ char *szCListGroup = getServListGroupCListPath(sc->wGroupId);
+
+ /// FIXME: need to create link to the new group name before unique item name correction as well
+ setServListGroupLinkID(szCListGroup, sc->wGroupId);
+ SAFE_FREE((void**)&szCListGroup);
+ }
+ servlistPendingRemoveGroup(sc->szGroup, sc->wGroupId, PENDING_RESULT_SUCCESS);
+ }
+ SAFE_FREE((void**)&sc->szGroupName);
+ SAFE_FREE((void**)&sc->szGroup);
+ break;
+
+ case SSA_SETAVATAR:
+ if (wError) {
+ debugLogA("Uploading of avatar hash failed.");
+ if (sc->wGroupId) { // is avatar added or updated?
+ FreeServerID(sc->wContactId, SSIT_ITEM);
+ delSetting(DBSETTING_SERVLIST_AVATAR); // to fix old versions
+ }
+ }
+ else setWord(DBSETTING_SERVLIST_AVATAR, sc->wContactId);
+ break;
+
+ case SSA_REMOVEAVATAR:
+ if (wError)
+ debugLogA("Removing of avatar hash failed.");
+ else {
+ FreeServerID(sc->wContactId, SSIT_ITEM);
+ delSetting(DBSETTING_SERVLIST_AVATAR);
+ }
+ break;
+
+ case SSA_SERVLIST_ACK:
+ ProtoBroadcastAck(sc->hContact, ICQACKTYPE_SERVERCLIST, wError ? ACKRESULT_FAILED : ACKRESULT_SUCCESS, (HANDLE)sc->lParam, wError);
+ break;
+
+ case SSA_IMPORT:
+ if (wError)
+ debugLogA("Re-starting import sequence failed, error %d", wError);
+ else {
+ setWord("SrvImportID", 0);
+ delSetting("ImportTS");
+ }
+ break;
+
+ default:
+ debugLogA("Server ack cookie type (%d) not recognized.", sc->dwAction);
+ }
+
+ SAFE_FREE((void**)&sc); // free the memory
+ return;
+}
+
+MCONTACT CIcqProto::HContactFromRecordName(const char* szRecordName, int *bAdded)
+{
+ MCONTACT hContact = INVALID_CONTACT_ID;
+
+ if (!IsStringUIN(szRecordName)) // probably AIM contact
+ hContact = HContactFromUID(0, szRecordName, bAdded);
+ else { // this should be ICQ number
+ DWORD dwUin = atoi(szRecordName);
+ hContact = HContactFromUIN(dwUin, bAdded);
+ }
+ return hContact;
+}
+
+int CIcqProto::getServerDataFromItemTLV(oscar_tlv_chain* pChain, unsigned char *buf) /// FIXME: need to keep original order
+{
+ // get server-list item's TLV data
+ oscar_tlv_chain* list = pChain;
+ int datalen = 0;
+ icq_packet pBuf;
+
+ // Initialize our handy data buffer
+ pBuf.wPlace = 0;
+ pBuf.pData = buf;
+
+ while (list) { // collect non-standard TLVs and save them to DB
+ if (list->tlv.wType != SSI_TLV_AWAITING_AUTH &&
+ list->tlv.wType != SSI_TLV_NAME &&
+ list->tlv.wType != SSI_TLV_COMMENT &&
+ list->tlv.wType != SSI_TLV_METAINFO_TOKEN &&
+ list->tlv.wType != SSI_TLV_METAINFO_TIME) { // only TLVs which we do not handle on our own
+ packTLV(&pBuf, list->tlv.wType, list->tlv.wLen, list->tlv.pData);
+
+ datalen += list->tlv.wLen + 4;
+ }
+ list = list->next;
+ }
+ return datalen;
+}
+
+void CIcqProto::handleServerCListReply(BYTE *buf, WORD wLen, WORD wFlags, serverthread_info *info)
+{
+ BYTE bySSIVersion;
+ WORD wRecordCount;
+ WORD wRecord;
+ WORD wGroupId;
+ WORD wItemId;
+ WORD wTlvType;
+ WORD wTlvLength;
+ BOOL bIsLastPacket;
+ uid_str szRecordName;
+ oscar_tlv_chain* pChain = NULL;
+ oscar_tlv* pTLV = NULL;
+ char *szActiveSrvGroup = NULL;
+ WORD wActiveSrvGroupId = -1;
+
+
+ // If flag bit 1 is set, this is not the last
+ // packet. If it is 0, this is the last packet
+ // and there will be a timestamp at the end.
+ if (wFlags & 0x0001)
+ bIsLastPacket = FALSE;
+ else
+ bIsLastPacket = TRUE;
+
+ if (wLen < 3)
+ return;
+
+ // Version number of SSI protocol?
+ unpackByte(&buf, &bySSIVersion);
+ wLen -= 1;
+
+ // Total count of following entries. This is the size of the server
+ // side contact list and should be saved and sent with CLI_CHECKROSTER.
+ // NOTE: When the entries are split up in several packets, each packet
+ // has it's own count and they must be added to get the total size of
+ // server list.
+ unpackWord(&buf, &wRecordCount);
+ wLen -= 2;
+ debugLogA("SSI: number of entries is %u, version is %u", wRecordCount, bySSIVersion);
+
+ // Loop over all items in the packet
+ for (wRecord = 0; wRecord < wRecordCount; wRecord++) {
+ debugLogA("SSI: parsing record %u", wRecord + 1);
+
+ if (wLen < 10) { // minimum: name length (zero), group ID, item ID, empty TLV
+ debugLogA("Warning: SSI parsing error (%d)", 0);
+ break;
+ }
+
+ if (!unpackServerListItem(&buf, &wLen, szRecordName, &wGroupId, &wItemId, &wTlvType, &wTlvLength)) { // unpack basic structure
+ debugLogA("Warning: SSI parsing error (%d)", 1);
+ break;
+ }
+
+ debugLogA("Name: '%s', GroupID: %u, EntryID: %u, EntryType: %u, TLVlength: %u",
+ szRecordName, wGroupId, wItemId, wTlvType, wTlvLength);
+
+ if (wLen < wTlvLength) {
+ debugLogA("Warning: SSI parsing error (%d)", 2);
+ break;
+ }
+
+ // Initialize the tlv chain
+ if (wTlvLength > 0) {
+ pChain = readIntoTLVChain(&buf, wTlvLength, 0);
+ wLen -= wTlvLength;
+ }
+ else pChain = NULL;
+
+ switch (wTlvType) {
+ case SSI_ITEM_BUDDY:
+ {
+ /* this is a contact */
+ int bAdded;
+ MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
+
+ if (hContact != INVALID_CONTACT_ID) {
+ int bRegroup = 0;
+ int bNicked = 0;
+
+ if (bAdded) { // Not already on list: added
+ debugLogA("SSI added new %s contact '%s'", "ICQ", szRecordName);
+
+ AddJustAddedContact(hContact);
+ }
+ else { // we should add new contacts and this contact was just added, show it
+ if (IsContactJustAdded(hContact)) {
+ setContactHidden(hContact, 0);
+ bAdded = 1; // we want details for new contacts
+ }
+ else debugLogA("SSI ignoring existing contact '%s'", szRecordName);
+
+ // Contact on server is always on list
+ db_set_b(hContact, "CList", "NotOnList", 0);
+ }
+
+ // Save group and item ID
+ setWord(hContact, DBSETTING_SERVLIST_ID, wItemId);
+ setWord(hContact, DBSETTING_SERVLIST_GROUP, wGroupId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+
+ if (!bAdded && getByte("LoadServerDetails", DEFAULT_SS_LOAD)) { // check if the contact has been moved on the server
+ if (wActiveSrvGroupId != wGroupId || !szActiveSrvGroup) {
+ SAFE_FREE(&szActiveSrvGroup);
+ szActiveSrvGroup = getServListGroupCListPath(wGroupId);
+ wActiveSrvGroupId = wGroupId;
+ }
+ char *szLocalGroup = getContactCListGroup(hContact);
+
+ if (!strlennull(szLocalGroup)) { // no CListGroup
+ SAFE_FREE(&szLocalGroup);
+
+ szLocalGroup = null_strdup(DEFAULT_SS_GROUP);
+ }
+
+ if (strcmpnull(szActiveSrvGroup, szLocalGroup) &&
+ (strlennull(szActiveSrvGroup) >= strlennull(szLocalGroup) || (szActiveSrvGroup && _strnicmp(szActiveSrvGroup, szLocalGroup, strlennull(szLocalGroup))))) { // contact moved to new group or sub-group or not to master group
+ bRegroup = 1;
+ }
+ if (bRegroup && !stricmpnull(DEFAULT_SS_GROUP, szActiveSrvGroup)) /// TODO: invent something more clever for "root" group
+ { // is it the default "General" group ?
+ bRegroup = 0; // if yes, do not move to it - cause it would hide the contact
+ }
+ SAFE_FREE(&szLocalGroup);
+ }
+
+ if (bRegroup || bAdded) { // if we should load server details or contact was just added, update its group
+ if (wActiveSrvGroupId != wGroupId || !szActiveSrvGroup) {
+ SAFE_FREE(&szActiveSrvGroup);
+ szActiveSrvGroup = getServListGroupCListPath(wGroupId);
+ wActiveSrvGroupId = wGroupId;
+ }
+
+ if (szActiveSrvGroup) { // try to get Miranda Group path from groupid, if succeeded save to db
+ moveContactToCListGroup(hContact, szActiveSrvGroup);
+ }
+ }
+
+ if (pChain) { // Look for nickname TLV and copy it to the db if necessary
+ if (pTLV = pChain->getTLV(SSI_TLV_NAME, 1)) {
+ if (pTLV->pData && (pTLV->wLen > 0)) {
+ char *pszNick;
+ WORD wNickLength;
+
+ wNickLength = pTLV->wLen;
+
+ pszNick = (char*)SAFE_MALLOC(wNickLength + 1);
+ // Copy buffer to utf-8 buffer
+ memcpy(pszNick, pTLV->pData, wNickLength);
+ pszNick[wNickLength] = 0; // Terminate string
+
+ debugLogA("Nickname is '%s'", pszNick);
+
+ bNicked = 1;
+
+ // Write nickname to database
+ if (getByte("LoadServerDetails", DEFAULT_SS_LOAD) || bAdded) { // if just added contact, save details always - does no harm
+ char *szOldNick;
+
+ if (szOldNick = getSettingStringUtf(hContact, "CList", "MyHandle", NULL)) {
+ if ((strcmpnull(szOldNick, pszNick)) && (strlennull(pszNick) > 0)) { // check if the truncated nick changed, i.e. do not overwrite locally stored longer nick
+ if (strlennull(szOldNick) <= strlennull(pszNick) || strncmp(szOldNick, pszNick, null_strcut(szOldNick, MAX_SSI_TLV_NAME_SIZE))) {
+ // Yes, we really do need to delete it first. Otherwise the CLUI nick
+ // cache isn't updated (I'll look into it)
+ db_unset(hContact, "CList", "MyHandle");
+ db_set_utf(hContact, "CList", "MyHandle", pszNick);
+ }
+ }
+ SAFE_FREE(&szOldNick);
+ }
+ else if (strlennull(pszNick) > 0) {
+ db_unset(hContact, "CList", "MyHandle");
+ db_set_utf(hContact, "CList", "MyHandle", pszNick);
+ }
+ }
+ SAFE_FREE(&pszNick);
+ }
+ else debugLogA("Invalid nickname");
+ }
+ if (bAdded && !bNicked)
+ icq_QueueUser(hContact); // queue user without nick for fast auto info update
+
+ // Look for comment TLV and copy it to the db if necessary
+ if (pTLV = pChain->getTLV(SSI_TLV_COMMENT, 1)) {
+ if (pTLV->pData && (pTLV->wLen > 0)) {
+ char *pszComment;
+ WORD wCommentLength;
+
+
+ wCommentLength = pTLV->wLen;
+
+ pszComment = (char*)SAFE_MALLOC(wCommentLength + 1);
+ // Copy buffer to utf-8 buffer
+ memcpy(pszComment, pTLV->pData, wCommentLength);
+ pszComment[wCommentLength] = 0; // Terminate string
+
+ debugLogA("Comment is '%s'", pszComment);
+
+ // Write comment to database
+ if (getByte("LoadServerDetails", DEFAULT_SS_LOAD) || bAdded) { // if just added contact, save details always - does no harm
+ char *szOldComment;
+
+ if (szOldComment = getSettingStringUtf(hContact, "UserInfo", "MyNotes", NULL)) {
+ if ((strcmpnull(szOldComment, pszComment)) && (strlennull(pszComment) > 0)) // check if the truncated comment changed, i.e. do not overwrite locally stored longer comment
+ if (strlennull(szOldComment) <= strlennull(pszComment) || strncmp((char*)szOldComment, (char*)pszComment, null_strcut(szOldComment, MAX_SSI_TLV_COMMENT_SIZE)))
+ db_set_utf(hContact, "UserInfo", "MyNotes", pszComment);
+
+ SAFE_FREE((void**)&szOldComment);
+ }
+ else if (strlennull(pszComment) > 0)
+ db_set_utf(hContact, "UserInfo", "MyNotes", pszComment);
+ }
+ SAFE_FREE((void**)&pszComment);
+ }
+ else debugLogA("Invalid comment");
+ }
+
+ // Look for need-authorization TLV
+ if (pChain->getTLV(SSI_TLV_AWAITING_AUTH, 1)) {
+ setByte(hContact, "Auth", 1);
+ debugLogA("SSI contact need authorization");
+ }
+ else setByte(hContact, "Auth", 0);
+
+ if (pTLV = pChain->getTLV(SSI_TLV_METAINFO_TOKEN, 1)) {
+ setSettingBlob(hContact, DBSETTING_METAINFO_TOKEN, pTLV->pData, pTLV->wLen);
+ if (pChain->getTLV(SSI_TLV_METAINFO_TIME, 1))
+ setSettingDouble(hContact, DBSETTING_METAINFO_TIME, pChain->getDouble(SSI_TLV_METAINFO_TIME, 1));
+ debugLogA("SSI contact has meta info token");
+ }
+ else {
+ delSetting(hContact, DBSETTING_METAINFO_TOKEN);
+ delSetting(hContact, DBSETTING_METAINFO_TIME);
+ }
+
+ { // store server-list item's TLV data
+ BYTE* data = (BYTE*)SAFE_MALLOC(wTlvLength);
+ int datalen = getServerDataFromItemTLV(pChain, data);
+
+ if (datalen > 0)
+ setSettingBlob(hContact, DBSETTING_SERVLIST_DATA, data, datalen);
+ else
+ delSetting(hContact, DBSETTING_SERVLIST_DATA);
+
+ SAFE_FREE((void**)&data);
+ }
+ }
+ }
+ else // failed to add or other error
+ debugLogA("SSI failed to handle %s Item '%s'", "Buddy", szRecordName);
+ }
+ break;
+
+ case SSI_ITEM_GROUP:
+ if ((wGroupId == 0) && (wItemId == 0)) {
+ /* list of groups. wTlvType=1, data is TLV(C8) containing list of WORDs which */
+ /* is the group ids
+ /* we don't need to use this. Our processing is on-the-fly */
+ /* this record is always sent first in the first packet only, */
+ }
+ else if (wGroupId != 0) {
+ /* wGroupId != 0: a group record */
+ if (wItemId == 0) { /* no item ID: this is a group */
+ /* pszRecordName is the name of the group */
+ ReserveServerID(wGroupId, SSIT_GROUP, 0);
+
+ setServListGroupName(wGroupId, szRecordName);
+
+ debugLogA("Group %s added to known groups.", szRecordName);
+
+ /* demangle full grouppath, set it to known */
+ SAFE_FREE(&szActiveSrvGroup);
+ szActiveSrvGroup = getServListGroupCListPath(wGroupId);
+ wActiveSrvGroupId = wGroupId;
+
+ /* TLV contains a TLV(C8) with a list of WORDs of contained contact IDs */
+ /* our processing is good enough that we don't need this duplication */
+ }
+ else debugLogA("Unhandled type 0x01, wItemID != 0");
+ }
+ else debugLogA("Unhandled type 0x01");
+ break;
+
+ case SSI_ITEM_PERMIT:
+ {
+ /* item on visible list */
+ /* wItemId not related to contact ID */
+ /* pszRecordName is the UIN */
+ int bAdded;
+ MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
+
+ if (hContact != INVALID_CONTACT_ID) {
+ if (bAdded) {
+ debugLogA("SSI added new %s contact '%s'", "Permit", szRecordName);
+ // It wasn't previously in the list, we hide it so it only appears in the visible list
+ setContactHidden(hContact, 1);
+ // Add it to the list, so it can be added properly if proper contact
+ AddJustAddedContact(hContact);
+ }
+ else debugLogA("SSI %s contact already exists '%s'", "Permit", szRecordName);
+
+ // Save permit ID
+ setWord(hContact, DBSETTING_SERVLIST_PERMIT, wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+ // Set apparent mode
+ setWord(hContact, "ApparentMode", ID_STATUS_ONLINE);
+ debugLogA("Visible-contact (%s)", szRecordName);
+ }
+ else { // failed to add or other error
+ debugLogA("SSI failed to handle %s Item '%s'", "Permit", szRecordName);
+ ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ }
+ }
+ break;
+
+ case SSI_ITEM_DENY:
+ {
+ /* Item on invisible list */
+ /* wItemId not related to contact ID */
+ /* pszRecordName is the UIN */
+ int bAdded;
+ MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
+
+ if (hContact != INVALID_CONTACT_ID) {
+ if (bAdded) {
+ /* not already on list: added */
+ debugLogA("SSI added new %s contact '%s'", "Deny", szRecordName);
+ // It wasn't previously in the list, we hide it so it only appears in the visible list
+ setContactHidden(hContact, 1);
+ // Add it to the list, so it can be added properly if proper contact
+ AddJustAddedContact(hContact);
+ }
+ else debugLogA("SSI %s contact already exists '%s'", "Deny", szRecordName);
+
+ // Save Deny ID
+ setWord(hContact, DBSETTING_SERVLIST_DENY, wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+
+ // Set apparent mode
+ setWord(hContact, "ApparentMode", ID_STATUS_OFFLINE);
+ debugLogA("Invisible-contact (%s)", szRecordName);
+ }
+ else { // failed to add or other error
+ debugLogA("SSI failed to handle %s Item '%s'", "Deny", szRecordName);
+ ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ }
+ }
+ break;
+
+ case SSI_ITEM_VISIBILITY: /* My visibility settings */
+ // Look for visibility TLV
+ if (BYTE bVisibility = pChain->getByte(SSI_TLV_VISIBILITY, 1)) { // found it, store the id, we do not need current visibility - we do not rely on it
+ setWord(DBSETTING_SERVLIST_PRIVACY, wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+
+ debugLogA("Visibility is %u", bVisibility);
+ }
+ else ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ break;
+
+ case SSI_ITEM_IGNORE:
+ {
+ /* item on ignore list */
+ /* wItemId not related to contact ID */
+ /* pszRecordName is the UIN */
+ int bAdded;
+ MCONTACT hContact = HContactFromRecordName(szRecordName, &bAdded);
+
+ if (hContact != INVALID_CONTACT_ID) {
+ if (bAdded) {
+ /* not already on list: add */
+ debugLogA("SSI added new %s contact '%s'", "Ignore", szRecordName);
+ // It wasn't previously in the list, we hide it
+ setContactHidden(hContact, 1);
+ // Add it to the list, so it can be added properly if proper contact
+ AddJustAddedContact(hContact);
+ }
+ else debugLogA("SSI %s contact already exists '%s'", "Ignore", szRecordName);
+
+ // Save Ignore ID
+ setWord(hContact, DBSETTING_SERVLIST_IGNORE, wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+
+ // Set apparent mode & ignore
+ setWord(hContact, "ApparentMode", ID_STATUS_OFFLINE);
+ // set ignore all events
+ CallService(MS_IGNORE_IGNORE, hContact, IGNOREEVENT_ALL);
+ debugLogA("Ignore-contact (%s)", szRecordName);
+ }
+ else { // failed to add or other error
+ debugLogA("SSI failed to handle %s Item '%s'", "Ignore", szRecordName);
+ ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ }
+ }
+ break;
+
+ case SSI_ITEM_UNKNOWN2:
+ debugLogA("SSI unknown type 0x11");
+
+ ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ break;
+
+ case SSI_ITEM_IMPORTTIME:
+ if (wGroupId == 0) {
+ /* time our list was first imported */
+ /* pszRecordName is "Import Time" */
+ /* data is TLV(13) {TLV(D4) {time_t importTime}} */
+ setDword("ImportTS", pChain->getDWord(SSI_TLV_TIMESTAMP, 1));
+ setWord("SrvImportID", wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+ debugLogA("SSI %s item recognized", "first import");
+ }
+ break;
+
+ case SSI_ITEM_BUDDYICON:
+ if (wGroupId == 0) {
+ /* our avatar MD5-hash */
+ /* pszRecordName is "1" */
+ /* data is TLV(D5) hash */
+ /* we ignore this, just save the id */
+ /* cause we get the hash again after login */
+ if (!strcmpnull(szRecordName, "12")) { // need to handle Photo Item separately
+ setWord(DBSETTING_SERVLIST_PHOTO, wItemId);
+ debugLogA("SSI %s item recognized", "Photo");
+ }
+ else {
+ setWord(DBSETTING_SERVLIST_AVATAR, wItemId);
+ debugLogA("SSI %s item recognized", "Avatar");
+ }
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+ }
+ break;
+
+ case SSI_ITEM_METAINFO:
+ if (wGroupId == 0) {
+ /* our meta info token & last update time */
+ /* pszRecordName is "ICQ-MDIR" */
+ /* data is TLV(15C) and TLV(15D) */
+ oscar_tlv* pToken = pChain->getTLV(SSI_TLV_METAINFO_TOKEN, 1);
+ oscar_tlv* pTime = pChain->getTLV(SSI_TLV_METAINFO_TIME, 1);
+ if (pToken)
+ setSettingBlob(NULL, DBSETTING_METAINFO_TOKEN, pToken->pData, pToken->wLen);
+ if (pTime)
+ setSettingDouble(NULL, DBSETTING_METAINFO_TIME, pChain->getDouble(SSI_TLV_METAINFO_TIME, 1));
+
+ setWord(DBSETTING_SERVLIST_METAINFO, wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+
+ debugLogA("SSI %s item recognized", "Meta info");
+ }
+ break;
+
+ case SSI_ITEM_CLIENTDATA:
+ if (wGroupId == 0) {
+ /* ICQ2k ShortcutBar Items */
+ /* data is TLV(CD) text */
+ if (wItemId)
+ ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ }
+
+ case SSI_ITEM_SAVED:
+ case SSI_ITEM_PREAUTH:
+ break;
+
+ default:
+ debugLogA("SSI unhandled item %2x", wTlvType);
+
+ if (wItemId)
+ ReserveServerID(wItemId, SSIT_ITEM, SSIF_UNHANDLED);
+ break;
+ }
+
+ disposeChain(&pChain);
+ } // end for
+
+ // Release Memory
+ SAFE_FREE(&szActiveSrvGroup);
+
+ debugLogA("Bytes left: %u", wLen);
+
+ setWord("SrvRecordCount", (WORD)(wRecord + getWord("SrvRecordCount", 0)));
+
+ if (bIsLastPacket) {
+ // No contacts left to sync
+ bIsSyncingCL = FALSE;
+
+ StoreServerIDs();
+
+ icq_RescanInfoUpdate();
+
+ if (wLen >= 4) {
+ DWORD dwLastUpdateTime;
+
+ /* finally we get a time_t of the last update time */
+ unpackDWord(&buf, &dwLastUpdateTime);
+ setDword("SrvLastUpdate", dwLastUpdateTime);
+ debugLogA("Last update of server list was (%u) %s", dwLastUpdateTime, time2text(dwLastUpdateTime));
+
+ sendRosterAck();
+ handleServUINSettings(wListenPort, info);
+
+ servlistProcessLogin();
+ }
+ else debugLogA("Last packet missed update time...");
+
+ if (getWord("SrvRecordCount", 0) == 0) { // we got empty serv-list, create master group
+ cookie_servlist_action* ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
+ if (ack) {
+ DWORD dwCookie;
+
+ ack->dwAction = SSA_GROUP_UPDATE;
+ ack->szGroupName = null_strdup("");
+ dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_ADDTOLIST, 0, ack);
+ icq_sendServerGroup(dwCookie, ICQ_LISTS_ADDTOLIST, 0, ack->szGroupName, NULL, 0, 0);
+ }
+ }
+ // serv-list sync finished, clear just added contacts
+ FlushJustAddedContacts();
+ }
+ else debugLogA("Waiting for more packets");
+}
+
+void CIcqProto::handleServerCListItemAdd(const char *szRecordName, WORD wGroupId, WORD wItemId, WORD wItemType, oscar_tlv_chain *pItemData)
+{
+ if (wItemType == SSI_ITEM_IMPORTTIME) {
+ if (pItemData) {
+ setDword("ImportTS", pItemData->getDWord(SSI_TLV_TIMESTAMP, 1));
+ setWord("SrvImportID", wItemId);
+ ReserveServerID(wItemId, SSIT_ITEM, 0);
+
+ debugLogA("Server added Import timestamp to list");
+ return;
+ }
+ }
+ // Reserve server-list ID
+ ReserveServerID(wItemId, wItemType == SSI_ITEM_GROUP ? SSIT_GROUP : SSIT_ITEM, SSIF_UNHANDLED);
+}
+
+void CIcqProto::handleServerCListItemUpdate(const char *szRecordName, WORD wGroupId, WORD wItemId, WORD wItemType, oscar_tlv_chain *pItemData)
+{
+ MCONTACT hContact = (wItemType == SSI_ITEM_BUDDY || wItemType == SSI_ITEM_DENY || wItemType == SSI_ITEM_PERMIT || wItemType == SSI_ITEM_IGNORE) ? HContactFromRecordName(szRecordName, NULL) : NULL;
+
+ if (hContact != INVALID_CONTACT_ID && wItemType == SSI_ITEM_BUDDY) { // a contact was updated on server
+ if (pItemData) {
+ oscar_tlv *pAuth = pItemData->getTLV(SSI_TLV_AWAITING_AUTH, 1);
+ BYTE bAuth = getByte(hContact, "Auth", 0);
+
+ if (bAuth && !pAuth) { // server authorized our contact
+ char str[MAX_PATH];
+ char msg[MAX_PATH];
+ char *nick = NickFromHandleUtf(hContact);
+
+ setByte(hContact, "Auth", 0);
+ mir_snprintf(str, MAX_PATH, ICQTranslateUtfStatic(LPGEN("Contact \"%s\" was authorized in the server list."), msg, MAX_PATH), nick);
+ icq_LogMessage(LOG_WARNING, str);
+ SAFE_FREE(&nick);
+ }
+ else if (!bAuth && pAuth) { // server took away authorization of our contact
+ char str[MAX_PATH];
+ char msg[MAX_PATH];
+ char *nick = NickFromHandleUtf(hContact);
+
+ setByte(hContact, "Auth", 1);
+ mir_snprintf(str, MAX_PATH, ICQTranslateUtfStatic(LPGEN("Contact \"%s\" lost its authorization in the server list."), msg, MAX_PATH), nick);
+ icq_LogMessage(LOG_WARNING, str);
+ SAFE_FREE(&nick);
+ }
+ {
+ // update metainfo data
+ DBVARIANT dbv = { 0 };
+ oscar_tlv *pToken = pItemData->getTLV(SSI_TLV_METAINFO_TOKEN, 1);
+ oscar_tlv *pTime = pItemData->getTLV(SSI_TLV_METAINFO_TIME, 1);
+
+ if (!getSetting(hContact, DBSETTING_METAINFO_TOKEN, &dbv)) {
+ if (!pToken || dbv.cpbVal != pToken->wLen || memcmp(dbv.pbVal, pToken->pData, dbv.cpbVal)) {
+ if (!pToken)
+ debugLogA("Contact %s, meta info token removed", szRecordName);
+ else
+ debugLogA("Contact %s, meta info token changed", szRecordName);
+
+ // user info was changed, refresh
+ if (IsMetaInfoChanged(hContact))
+ icq_QueueUser(hContact);
+ }
+
+ db_free(&dbv);
+ }
+ else if (pToken) {
+ debugLogA("Contact %s, meta info token added", szRecordName);
+
+ // user info was changed, refresh
+ if (IsMetaInfoChanged(hContact))
+ icq_QueueUser(hContact);
+ }
+
+ if (pToken)
+ setSettingBlob(hContact, DBSETTING_METAINFO_TOKEN, pToken->pData, pToken->wLen);
+ if (pTime)
+ setSettingDouble(hContact, DBSETTING_METAINFO_TIME, pItemData->getDouble(SSI_TLV_METAINFO_TIME, 1));
+ }
+ {
+ // update server's data - otherwise consequent operations can fail with 0x0E
+ BYTE *data = (BYTE*)_alloca(pItemData->getChainLength());
+ int datalen = getServerDataFromItemTLV(pItemData, data);
+
+ if (datalen > 0)
+ setSettingBlob(hContact, DBSETTING_SERVLIST_DATA, data, datalen);
+ else
+ delSetting(hContact, DBSETTING_SERVLIST_DATA);
+ }
+ }
+ }
+ else if (wItemType == SSI_ITEM_METAINFO) { // owner MetaInfo data updated
+ if (pItemData) {
+ DBVARIANT dbv = { 0 };
+ oscar_tlv *pToken = pItemData->getTLV(SSI_TLV_METAINFO_TOKEN, 1);
+ oscar_tlv *pTime = pItemData->getTLV(SSI_TLV_METAINFO_TIME, 1);
+
+ if (!getSetting(hContact, DBSETTING_METAINFO_TOKEN, &dbv)) {
+ if (!pToken || dbv.cpbVal != pToken->wLen || memcmp(dbv.pbVal, pToken->pData, dbv.cpbVal)) {
+ if (!pToken)
+ debugLogA("Owner meta info token removed");
+ else
+ debugLogA("Owner meta info token changed");
+ }
+
+ db_free(&dbv);
+ }
+
+ if (pToken)
+ setSettingBlob(hContact, DBSETTING_METAINFO_TOKEN, pToken->pData, pToken->wLen);
+ if (pTime)
+ setSettingDouble(hContact, DBSETTING_METAINFO_TIME, pItemData->getDouble(SSI_TLV_METAINFO_TIME, 1));
+ }
+ }
+ else if (wItemType == SSI_ITEM_GROUP) // group updated
+ debugLogA("Server updated our group \"%s\" on list", szRecordName);
+}
+
+void CIcqProto::handleServerCListItemDelete(const char *szRecordName, WORD wGroupId, WORD wItemId, WORD wItemType, oscar_tlv_chain *pItemData)
+{
+ MCONTACT hContact = (wItemType == SSI_ITEM_BUDDY || wItemType == SSI_ITEM_DENY || wItemType == SSI_ITEM_PERMIT || wItemType == SSI_ITEM_IGNORE) ? HContactFromRecordName(szRecordName, NULL) : NULL;
+
+ if (hContact != INVALID_CONTACT_ID && wItemType == SSI_ITEM_BUDDY) { // a contact was removed from our list
+ if (getWord(hContact, DBSETTING_SERVLIST_ID, 0) == wItemId) {
+ delSetting(hContact, DBSETTING_SERVLIST_ID);
+ delSetting(hContact, DBSETTING_SERVLIST_GROUP);
+ delSetting(hContact, "Auth");
+
+ char str[MAX_PATH];
+ char msg[MAX_PATH];
+ char *nick = NickFromHandleUtf(hContact);
+
+ mir_snprintf(str, MAX_PATH, ICQTranslateUtfStatic(LPGEN("User \"%s\" was removed from server list."), msg, MAX_PATH), nick);
+ icq_LogMessage(LOG_WARNING, str);
+ SAFE_FREE(&nick);
+ }
+ }
+ // Release server-list ID
+ FreeServerID(wItemId, wItemType == SSI_ITEM_GROUP ? SSIT_GROUP : SSIT_ITEM);
+}
+
+void CIcqProto::handleRecvAuthRequest(unsigned char *buf, WORD wLen)
+{
+ DWORD dwUin;
+ uid_str szUid;
+ if (!unpackUID(&buf, &wLen, &dwUin, &szUid))
+ return;
+
+ if (dwUin && IsOnSpammerList(dwUin)) {
+ debugLogA("Ignored Message from known Spammer");
+ return;
+ }
+
+ WORD wReasonLen;
+ unpackWord(&buf, &wReasonLen);
+ wLen -= 2;
+ if (wReasonLen > wLen)
+ return;
+
+ int bAdded;
+ MCONTACT hContact = HContactFromUID(dwUin, szUid, &bAdded);
+
+ PROTORECVEVENT pre = { 0 };
+ pre.timestamp = time(NULL);
+ pre.lParam = sizeof(DWORD) * 2 + 5;
+ // Prepare reason
+ char *szReason = (char*)SAFE_MALLOC(wReasonLen + 1);
+ int nReasonLen = 0;
+ if (szReason) {
+ memcpy(szReason, buf, wReasonLen);
+ szReason[wReasonLen] = '\0';
+ nReasonLen = strlennull(szReason);
+
+ char *temp = (char*)_alloca(nReasonLen + 2);
+ if (!IsUSASCII(szReason, nReasonLen) && UTF8_IsValid(szReason) && utf8_decode_static(szReason, temp, nReasonLen + 1))
+ pre.flags |= PREF_UTF;
+ }
+
+ // Read nick name from DB
+ char *szNick = NULL;
+ if (dwUin) {
+ DBVARIANT dbv = { 0 };
+ if (pre.flags & PREF_UTF)
+ szNick = getSettingStringUtf(hContact, "Nick", NULL);
+ else if (!getString(hContact, "Nick", &dbv)) {
+ szNick = null_strdup(dbv.pszVal);
+ db_free(&dbv);
+ }
+ }
+ else szNick = null_strdup(szUid);
+
+ int nNickLen = strlennull(szNick);
+
+ pre.lParam += nNickLen + nReasonLen;
+
+ setByte(hContact, "Grant", 1);
+
+ /*blob is: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)*/
+ char *szBlob = (char *)_alloca(pre.lParam);
+ char *pCurBlob = szBlob;
+ *(DWORD*)pCurBlob = dwUin; pCurBlob += sizeof(DWORD);
+ *(DWORD*)pCurBlob = DWORD(hContact); pCurBlob += sizeof(DWORD);
+
+ if (nNickLen) { // if we have nick we add it, otherwise keep trailing zero
+ memcpy(pCurBlob, szNick, nNickLen);
+ pCurBlob += nNickLen;
+ }
+ *pCurBlob = 0; pCurBlob++; // Nick
+ *pCurBlob = 0; pCurBlob++; // FirstName
+ *pCurBlob = 0; pCurBlob++; // LastName
+ *pCurBlob = 0; pCurBlob++; // email
+ if (nReasonLen) {
+ memcpy(pCurBlob, szReason, nReasonLen);
+ pCurBlob += nReasonLen;
+ }
+ *pCurBlob = 0; // Reason
+ pre.szMessage = szBlob;
+
+ // TODO: Change for new auth system, include all known informations
+ ProtoChainRecv(hContact, PSR_AUTH, 0, (LPARAM)&pre);
+
+ SAFE_FREE(&szNick);
+ SAFE_FREE(&szReason);
+}
+
+void CIcqProto::handleRecvAdded(unsigned char *buf, WORD wLen)
+{
+ DWORD dwUin;
+ uid_str szUid;
+ DWORD cbBlob;
+ PBYTE pBlob, pCurBlob;
+ int bAdded;
+ char* szNick;
+ int nNickLen;
+ DBVARIANT dbv = { 0 };
+
+ if (!unpackUID(&buf, &wLen, &dwUin, &szUid)) return;
+
+ if (dwUin && IsOnSpammerList(dwUin)) {
+ debugLogA("Ignored Message from known Spammer");
+ return;
+ }
+
+ MCONTACT hContact = HContactFromUID(dwUin, szUid, &bAdded);
+
+ cbBlob = sizeof(DWORD) * 2 + 4;
+
+ if (dwUin) {
+ if (getString(hContact, "Nick", &dbv))
+ nNickLen = 0;
+ else {
+ szNick = dbv.pszVal;
+ nNickLen = strlennull(szNick);
+ }
+ }
+ else nNickLen = strlennull(szUid);
+
+ cbBlob += nNickLen;
+
+ pCurBlob = pBlob = (PBYTE)_alloca(cbBlob);
+ /*blob is: uin(DWORD), hContact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) */
+ *(DWORD*)pCurBlob = dwUin; pCurBlob += sizeof(DWORD);
+ *(DWORD*)pCurBlob = DWORD(hContact); pCurBlob += sizeof(DWORD);
+ if (nNickLen && dwUin) { // if we have nick we add it, otherwise keep trailing zero
+ memcpy(pCurBlob, szNick, nNickLen);
+ pCurBlob += nNickLen;
+ }
+ else {
+ memcpy(pCurBlob, szUid, nNickLen);
+ pCurBlob += nNickLen;
+ }
+ *(char *)pCurBlob = 0; pCurBlob++;
+ *(char *)pCurBlob = 0; pCurBlob++;
+ *(char *)pCurBlob = 0; pCurBlob++;
+ *(char *)pCurBlob = 0;
+ // TODO: Change for new auth system
+
+ AddEvent(NULL, EVENTTYPE_ADDED, time(NULL), 0, cbBlob, pBlob);
+}
+
+void CIcqProto::handleRecvAuthResponse(unsigned char *buf, WORD wLen)
+{
+ DWORD dwUin;
+ uid_str szUid;
+ char* szNick = NULL;
+ WORD nReasonLen;
+ char* szReason;
+ int bAdded;
+
+ BYTE bResponse = 0xFF;
+
+ if (!unpackUID(&buf, &wLen, &dwUin, &szUid)) return;
+
+ if (dwUin && IsOnSpammerList(dwUin)) {
+ debugLogA("Ignored Message from known Spammer");
+ return;
+ }
+
+ MCONTACT hContact = HContactFromUID(dwUin, szUid, &bAdded);
+ if (hContact != INVALID_CONTACT_ID)
+ szNick = NickFromHandle(hContact);
+
+ if (wLen > 0) {
+ unpackByte(&buf, &bResponse);
+ wLen -= 1;
+ }
+ if (wLen >= 2) {
+ unpackWord(&buf, &nReasonLen);
+ wLen -= 2;
+ if (wLen >= nReasonLen) {
+ szReason = (char*)_alloca(nReasonLen + 1);
+ unpackString(&buf, szReason, nReasonLen);
+ szReason[nReasonLen] = '\0';
+ }
+ }
+
+ switch (bResponse) {
+ case 0:
+ debugLogA("Authorization request %s by %s", "denied", strUID(dwUin, szUid));
+ // TODO: Add to system history as soon as new auth system is ready
+ break;
+
+ case 1:
+ setByte(hContact, "Auth", 0);
+ debugLogA("Authorization request %s by %s", "granted", strUID(dwUin, szUid));
+ // TODO: Add to system history as soon as new auth system is ready
+ break;
+
+ default:
+ debugLogA("Unknown Authorization request response (%u) from %s", bResponse, strUID(dwUin, szUid));
+ break;
+
+ }
+ SAFE_FREE(&szNick);
+}
+
+// Updates the visibility code used while in SSI mode. If a server ID is
+// not stored in the local DB, a new ID will be added to the server list.
+//
+// Possible values are:
+// 01 - Allow all users to see you
+// 02 - Block all users from seeing you
+// 03 - Allow only users in the permit list to see you
+// 04 - Block only users in the invisible list from seeing you
+// 05 - Allow only users in the buddy list to see you
+//
+void CIcqProto::updateServVisibilityCode(BYTE bCode)
+{
+ icq_packet packet;
+ WORD wVisibilityID;
+ WORD wCommand;
+
+ if ((bCode > 0) && (bCode < 6)) {
+ cookie_servlist_action* ack;
+ DWORD dwCookie;
+ BYTE bVisibility = getByte("SrvVisibility", 0);
+
+ if (bVisibility == bCode) // if no change was made, not necescary to update that
+ return;
+ setByte("SrvVisibility", bCode);
+
+ // Do we have a known server visibility ID? We should, unless we just subscribed to the serv-list for the first time
+ if ((wVisibilityID = getWord(DBSETTING_SERVLIST_PRIVACY, 0)) == 0) {
+ // No, create a new random ID
+ wVisibilityID = GenerateServerID(SSIT_ITEM, 0);
+ setWord(DBSETTING_SERVLIST_PRIVACY, wVisibilityID);
+ wCommand = ICQ_LISTS_ADDTOLIST;
+
+ debugLogA("Made new srvVisibilityID, id is %u, code is %u", wVisibilityID, bCode);
+ }
+ else {
+ debugLogA("Reused srvVisibilityID, id is %u, code is %u", wVisibilityID, bCode);
+ wCommand = ICQ_LISTS_UPDATEGROUP;
+ }
+
+ ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
+ if (!ack) {
+ debugLogA("Cookie alloc failure.");
+ return; // out of memory, go away
+ }
+
+ ack->dwAction = SSA_VISIBILITY; // update visibility
+ dwCookie = AllocateCookie(CKT_SERVERLIST, wCommand, 0, ack); // take cookie
+
+ // Build and send packet
+ serverPacketInit(&packet, 25);
+ packFNACHeader(&packet, ICQ_LISTS_FAMILY, wCommand, 0, dwCookie);
+ packWord(&packet, 0); // Name (null)
+ packWord(&packet, 0); // GroupID (0 if not relevant)
+ packWord(&packet, wVisibilityID); // EntryID
+ packWord(&packet, SSI_ITEM_VISIBILITY); // EntryType
+ packWord(&packet, 5); // Length in bytes of following TLV
+ packTLV(&packet, SSI_TLV_VISIBILITY, 1, &bCode); // TLV (Visibility)
+ sendServPacket(&packet);
+ // There is no need to send ICQ_LISTS_CLI_MODIFYSTART or
+ // ICQ_LISTS_CLI_MODIFYEND when modifying the visibility code
+ }
+}
+
+// Updates the avatar hash used while in SSI mode. If a server ID is
+// not stored in the local DB, a new ID will be added to the server list.
+void CIcqProto::updateServAvatarHash(BYTE *pHash, int size)
+{
+ void** pDoubleObject = NULL;
+ void* doubleObject = NULL;
+ DWORD dwOperationFlags = 0;
+ WORD wAvatarID;
+ WORD wCommand;
+ char szItemName[2] = { 0, 0 };
+ int bResetHash = 0;
+ DBVARIANT dbvHash;
+
+ if (!pHash)
+ return;
+
+ if (!getSetting(NULL, "AvatarHash", &dbvHash)) {
+ szItemName[0] = 0x30 + dbvHash.pbVal[1];
+
+ if (memcmp(pHash, dbvHash.pbVal, 2) != 0) // add code to remove old hash from server
+ bResetHash = 1;
+
+ db_free(&dbvHash);
+ }
+
+ if (bResetHash) { // start update session
+ // pair the packets (need to be send in the correct order
+ dwOperationFlags |= SSOF_BEGIN_OPERATION | SSOF_END_OPERATION;
+ pDoubleObject = &doubleObject;
+ }
+
+ if (bResetHash) {
+ // Do we have a known server avatar ID?
+ if (wAvatarID = getWord(DBSETTING_SERVLIST_AVATAR, 0)) {
+ cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
+ if (!ack) {
+ debugLogA("Cookie alloc failure.");
+ return; // out of memory, go away
+ }
+ ack->dwAction = SSA_REMOVEAVATAR; // update avatar hash
+ ack->wContactId = wAvatarID;
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_REMOVEFROMLIST, 0, ack); // take cookie
+ icq_sendServerItem(dwCookie, ICQ_LISTS_REMOVEFROMLIST, 0, wAvatarID, szItemName, NULL, 0, SSI_ITEM_BUDDYICON, SSOP_ITEM_ACTION | dwOperationFlags, 400, pDoubleObject);
+ }
+ }
+
+ WORD hashsize = size - 2;
+
+ // Do we have a known server avatar ID? We should, unless we just subscribed to the serv-list for the first time
+ if (bResetHash || (wAvatarID = getWord(DBSETTING_SERVLIST_AVATAR, 0)) == 0) {
+ // No, create a new random ID
+ wAvatarID = GenerateServerID(SSIT_ITEM, 0);
+ wCommand = ICQ_LISTS_ADDTOLIST;
+ debugLogA("Made new srvAvatarID, id is %u", wAvatarID);
+ }
+ else {
+ debugLogA("Reused srvAvatarID, id is %u", wAvatarID);
+ wCommand = ICQ_LISTS_UPDATEGROUP;
+ }
+
+ cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action));
+ if (!ack) {
+ debugLogA("Cookie alloc failure.");
+ return; // out of memory, go away
+ }
+ ack->dwAction = SSA_SETAVATAR; // update avatar hash
+ ack->wContactId = wAvatarID;
+ DWORD dwCookie = AllocateCookie(CKT_SERVERLIST, wCommand, 0, ack); // take cookie
+
+ szItemName[0] = 0x30 + pHash[1];
+
+ // Build the packet
+ WORD wTLVlen = 8 + hashsize;
+
+ // Initialize our handy data buffer
+ icq_packet pBuffer;
+ pBuffer.wPlace = 0;
+ pBuffer.pData = (BYTE *)_alloca(wTLVlen);
+ pBuffer.wLen = wTLVlen;
+
+ packTLV(&pBuffer, SSI_TLV_NAME, 0, NULL); // TLV (Name)
+ packTLV(&pBuffer, SSI_TLV_AVATARHASH, hashsize, pHash + 2); // TLV (Hash)
+
+ icq_sendServerItem(dwCookie, wCommand, 0, wAvatarID, szItemName, pBuffer.pData, wTLVlen, SSI_ITEM_BUDDYICON, SSOP_ITEM_ACTION | dwOperationFlags, 400, pDoubleObject);
+ // There is no need to send ICQ_LISTS_CLI_MODIFYSTART or
+ // ICQ_LISTS_CLI_MODIFYEND when modifying the avatar hash
+}
+
+// Should be called before the server list is modified. When all
+// modifications are done, call icq_sendServerEndOperation().
+// Called automatically thru server-list update board!
+void CIcqProto::icq_sendServerBeginOperation(int bImport)
+{
+ WORD wImportID = getWord("SrvImportID", 0);
+
+ if (bImport && wImportID) { // we should be importing, check if already have import item
+ if (getDword("ImportTS", 0) + 604800 < getDword("LogonTS", 0)) { // is the timestamp week older, clear it and begin new import
+ DWORD dwCookie;
+ cookie_servlist_action* ack;
+
+ if (ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action))) { // we have cookie good, go on
+ ack->dwAction = SSA_IMPORT;
+ dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_REMOVEFROMLIST, 0, ack);
+
+ icq_sendSimpleItem(dwCookie, ICQ_LISTS_REMOVEFROMLIST, 0, "ImportTime", 0, wImportID, SSI_ITEM_IMPORTTIME, SSOP_ITEM_ACTION | SSOF_SEND_DIRECTLY, 100);
+ }
+ }
+ }
+
+ icq_packet packet;
+ serverPacketInit(&packet, (WORD)(bImport ? 14 : 10));
+ packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_MODIFYSTART);
+ if (bImport)
+ packDWord(&packet, 1 << 0x10);
+ sendServPacket(&packet);
+}
+
+// Should be called after the server list has been modified to inform
+// the server that we are done.
+// Called automatically thru server-list update board!
+void CIcqProto::icq_sendServerEndOperation()
+{
+ icq_packet packet;
+ serverPacketInit(&packet, 10);
+ packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_MODIFYEND);
+ sendServPacket(&packet);
+}
+
+// Sent when the last roster packet has been received
+void CIcqProto::sendRosterAck(void)
+{
+ icq_packet packet;
+ serverPacketInit(&packet, 10);
+ packFNACHeader(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_GOTLIST);
+ sendServPacket(&packet);
+
+ debugLogA("Sent SNAC(x13,x07) - CLI_ROSTERACK");
+}
diff --git a/protocols/IcqOscarJ/src/icq_avatar.cpp b/protocols/IcqOscarJ/src/icq_avatar.cpp
index 0787c06764..6a9f733296 100644
--- a/protocols/IcqOscarJ/src/icq_avatar.cpp
+++ b/protocols/IcqOscarJ/src/icq_avatar.cpp
@@ -283,7 +283,7 @@ void CIcqProto::handleAvatarOwnerHash(WORD wItemID, BYTE bFlags, BYTE *pData, BY
else if (memcmp(hash, pData + 4, 0x10)) { // we have different avatar, sync that
if (m_bSsiEnabled && getByte("ForceOurAvatar", 1)) { // we want our avatar, update hash
DWORD dwPaFormat = ::ProtoGetAvatarFileFormat(file);
- BYTE *pHash = (BYTE*)_alloca(0x14);
+ BYTE pHash[0x14];
debugLogA("Our avatar is different, setting our new hash.");
@@ -291,7 +291,7 @@ void CIcqProto::handleAvatarOwnerHash(WORD wItemID, BYTE bFlags, BYTE *pData, BY
pHash[1] = dwPaFormat == PA_FORMAT_XML ? AVATAR_HASH_FLASH : AVATAR_HASH_STATIC;
pHash[2] = 1; // state of the hash
pHash[3] = 0x10; // len of the hash
- memcpy(pHash + 4, hash, 0x10);
+ memcpy((pHash + 4), hash, 0x10);
updateServAvatarHash(pHash, 0x14);
}
else { // get avatar from server
@@ -346,7 +346,7 @@ void CIcqProto::handleAvatarOwnerHash(WORD wItemID, BYTE bFlags, BYTE *pData, BY
SAFE_FREE((void**)&hash);
}
else {
- BYTE *pHash = (BYTE*)_alloca(0x14);
+ BYTE pHash[0x14];
debugLogA("Our file is different, set our new hash.");
@@ -354,7 +354,7 @@ void CIcqProto::handleAvatarOwnerHash(WORD wItemID, BYTE bFlags, BYTE *pData, BY
pHash[1] = dwPaFormat == PA_FORMAT_XML ? AVATAR_HASH_FLASH : AVATAR_HASH_STATIC;
pHash[2] = 1; // state of the hash
pHash[3] = 0x10; // len of the hash
- memcpy(pHash + 4, hash, 0x10);
+ memcpy((pHash + 4), hash, 0x10);
updateServAvatarHash(pHash, 0x14);
SAFE_FREE((void**)&hash);
diff --git a/protocols/IcqOscarJ/src/icqosc_svcs.cpp b/protocols/IcqOscarJ/src/icqosc_svcs.cpp
index 18accdf412..52ff1339da 100644
--- a/protocols/IcqOscarJ/src/icqosc_svcs.cpp
+++ b/protocols/IcqOscarJ/src/icqosc_svcs.cpp
@@ -509,13 +509,13 @@ INT_PTR CIcqProto::SetMyAvatar(WPARAM wParam, LPARAM lParam)
BYTE *hash = calcMD5HashOfFile(tszMyFile);
if (hash) {
- BYTE *ihash = (BYTE*)_alloca(0x14);
+ BYTE ihash[0x14];
// upload hash to server
ihash[0] = 0; //unknown
ihash[1] = dwPaFormat == PA_FORMAT_XML ? AVATAR_HASH_FLASH : AVATAR_HASH_STATIC; //hash type
ihash[2] = 1; //hash status
ihash[3] = 0x10; //hash len
- memcpy(ihash + 4, hash, 0x10);
+ memcpy((ihash + 4), hash, 0x10);
updateServAvatarHash(ihash, 0x14);
if (setSettingBlob(NULL, "AvatarHash", ihash, 0x14))
diff --git a/protocols/JabberG/src/jabber_search.cpp b/protocols/JabberG/src/jabber_search.cpp
index 78e2feaabb..b63f8e4672 100644
--- a/protocols/JabberG/src/jabber_search.cpp
+++ b/protocols/JabberG/src/jabber_search.cpp
@@ -1,763 +1,769 @@
-/*
-
-Jabber Protocol Plugin for Miranda NG
-
-Copyright (c) 2002-04 Santithorn Bunchua
-Copyright (c) 2005-12 George Hazan
-Copyright (c) 2007 Artem Shpynov
-Copyright (c) 2012-14 Miranda NG project
-
-Module implements a search according to XEP-0055: Jabber Search
-http://www.xmpp.org/extensions/xep-0055.html
-
-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.
-
-*/
-
-#include "jabber.h"
-#include <CommCtrl.h>
-#include "jabber_iq.h"
-#include "jabber_caps.h"
-
-///////////////////////////////////////////////////////////////////////////////
-// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling
-//
-static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam)
-{
- if (msg == WM_COMMAND && lParam != 0) {
- HWND hwndDlg=GetParent(hwnd);
- JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
- if (dat && lParam) {
- int pos=dat->curPos;
- RECT MineRect;
- RECT FrameRect;
- GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME),&FrameRect);
- GetWindowRect((HWND)lParam, &MineRect);
- if (MineRect.top-10 < FrameRect.top) {
- pos=dat->curPos+(MineRect.top-14-FrameRect.top);
- if (pos<0) pos=0;
- }
- else if (MineRect.bottom > FrameRect.bottom) {
- pos=dat->curPos+(MineRect.bottom-FrameRect.bottom);
- if (dat->frameHeight+pos>dat->CurrentHeight)
- pos=dat->CurrentHeight-dat->frameHeight;
- }
- if (pos != dat->curPos) {
- ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, NULL, &(dat->frameRect));
- SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE);
- RECT Invalid=dat->frameRect;
- if (dat->curPos - pos >0)
- Invalid.bottom=Invalid.top+(dat->curPos - pos);
- else
- Invalid.top=Invalid.bottom+(dat->curPos - pos);
-
- RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), NULL, NULL, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW |RDW_ALLCHILDREN);
- dat->curPos = pos;
- }
- }
- if (HIWORD(wParam)==EN_SETFOCUS) { //Transmit focus set notification to parent window
- PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg);
- }
- }
-
- if (msg == WM_PAINT) {
- PAINTSTRUCT ps;
- HDC hdc=BeginPaint(hwnd, &ps);
- FillRect(hdc,&(ps.rcPaint),GetSysColorBrush(COLOR_BTNFACE));
- EndPaint(hwnd, &ps);
- }
-
- return DefWindowProc(hwnd,msg,wParam,lParam);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Add Search field to form
-//
-static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat)
-{
- if (!FieldDat || !FieldDat->Label || !FieldDat->Var)
- return FALSE;
-
- HFONT hFont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0);
- HWND hwndParent=GetDlgItem(hwndDlg,IDC_FRAME);
- LONG_PTR frameExStyle = GetWindowLongPtr(hwndParent, GWL_EXSTYLE);
- frameExStyle |= WS_EX_CONTROLPARENT;
- SetWindowLongPtr(hwndParent, GWL_EXSTYLE, frameExStyle);
- SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_FRAME),GWLP_WNDPROC,(LONG_PTR)JabberSearchFrameProc);
-
- int CornerX=1;
- int CornerY=1;
- RECT rect;
- GetClientRect(hwndParent,&rect);
- int width=rect.right-5-CornerX;
-
- int Order=(FieldDat->bHidden) ? -1 : FieldDat->Order;
-
- HWND hwndLabel=CreateWindowEx(0,_T("STATIC"),(LPCTSTR)TranslateTS(FieldDat->Label),WS_CHILD, CornerX, CornerY + Order*40, width, 13,hwndParent,NULL,hInst,0);
- HWND hwndVar=CreateWindowEx(0|WS_EX_CLIENTEDGE,_T("EDIT"),(LPCTSTR)FieldDat->defValue,WS_CHILD|WS_TABSTOP, CornerX+5, CornerY + Order*40+14, width ,20,hwndParent,NULL,hInst,0);
- SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont,0);
- SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont,0);
- if (!FieldDat->bHidden) {
- ShowWindow(hwndLabel,SW_SHOW);
- ShowWindow(hwndVar,SW_SHOW);
- EnableWindow(hwndLabel,!FieldDat->bReadOnly);
- SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly,0);
- }
- //remade list
- //reallocation
- JabberSearchData *dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
- if (dat) {
- dat->pJSInf=(JabberSearchFieldsInfo*)realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount+1));
- dat->pJSInf[dat->nJSInfCount].hwndCaptionItem=hwndLabel;
- dat->pJSInf[dat->nJSInfCount].hwndValueItem=hwndVar;
- dat->pJSInf[dat->nJSInfCount].szFieldCaption=_tcsdup(FieldDat->Label);
- dat->pJSInf[dat->nJSInfCount].szFieldName=_tcsdup(FieldDat->Var);
- dat->nJSInfCount++;
- }
- return CornerY + Order*40+14 +20;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Available search field request result handler (XEP-0055. Examples 2, 7)
-
-void CJabberProto::OnIqResultGetSearchFields(HXML iqNode, CJabberIqInfo *pInfo)
-{
- if (!searchHandleDlg)
- return;
-
- LPCTSTR type = xmlGetAttrValue(iqNode, _T("type"));
- if (type == NULL)
- return;
-
- if (!lstrcmp(type, _T("result"))) {
- HXML queryNode = xmlGetNthChild(iqNode, _T("query"), 1);
- HXML xNode = xmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
-
- ShowWindow(searchHandleDlg,SW_HIDE);
- if (xNode) {
- //1. Form
- PostMessage(searchHandleDlg, WM_USER+11, (WPARAM)xi.copyNode(xNode), 0);
- HXML xcNode = xmlGetNthChild(xNode, _T("instructions"), 1);
- if (xcNode)
- SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, xmlGetText(xcNode));
- }
- else {
- int Order=0;
- for (int i=0; ; i++) {
- HXML chNode = xmlGetChild(queryNode, i);
- if (!chNode)
- break;
-
- if (!_tcsicmp(xmlGetName(chNode), _T("instructions")) && xmlGetText(chNode))
- SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,TranslateTS(xmlGetText(chNode)));
- else if (xmlGetName(chNode)) {
- Data *MyData=(Data*)malloc(sizeof(Data));
- memset(MyData,0,sizeof(Data));
-
- MyData->Label = mir_tstrdup(xmlGetName(chNode));
- MyData->Var = mir_tstrdup(xmlGetName(chNode));
- MyData->defValue = mir_tstrdup(xmlGetText(chNode));
- MyData->Order = Order;
- if (MyData->defValue) MyData->bReadOnly = TRUE;
- PostMessage(searchHandleDlg,WM_USER+10,FALSE,(LPARAM)MyData);
- Order++;
- }
- }
- }
-
- const TCHAR *szFrom = xmlGetAttrValue(iqNode, _T("from"));
- if (szFrom)
- SearchAddToRecent(szFrom,searchHandleDlg);
- PostMessage(searchHandleDlg,WM_USER+10,0,0);
- ShowWindow(searchHandleDlg,SW_SHOW);
- }
- else if (!lstrcmp(type, _T("error"))) {
- const TCHAR *code=NULL;
- const TCHAR *description=NULL;
- TCHAR buff[255];
- HXML errorNode = xmlGetChild(iqNode, "error");
- if (errorNode) {
- code = xmlGetAttrValue(errorNode, _T("code"));
- description = xmlGetText(errorNode);
- }
- mir_sntprintf(buff, SIZEOF(buff), TranslateT("Error %s %s\r\nPlease select other server"), code ? code : _T(""), description ? description : _T(""));
- SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff);
- }
- else SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateT("Error: unknown reply received\r\nPlease select other server"));
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Return results to search dialog
-// The pmFields is the pointer to map of <field Name, field Label> Not unical but ordered
-// This can help to made result parser routines more simple
-
-void CJabberProto::SearchReturnResults(HANDLE id, void * pvUsersInfo, U_TCHAR_MAP * pmAllFields)
-{
- LIST<TCHAR> ListOfNonEmptyFields(20,(LIST<TCHAR>::FTSortFunc)TCharKeyCmp);
- LIST<TCHAR> ListOfFields(20);
- LIST<void>* plUsersInfo = (LIST<void>*)pvUsersInfo;
- int i, nUsersFound = plUsersInfo->getCount();
-
- // lets fill the ListOfNonEmptyFields but in users order
- for (i=0; i < nUsersFound; i++) {
- U_TCHAR_MAP* pmUserData = (U_TCHAR_MAP*)plUsersInfo->operator [](i);
- int nUserFields = pmUserData->getCount();
- for (int j=0; j < nUserFields; j++) {
- TCHAR *var = pmUserData->getKeyName(j);
- if (var && ListOfNonEmptyFields.getIndex(var) < 0)
- ListOfNonEmptyFields.insert(var);
- } }
-
- // now fill the ListOfFields but order is from pmAllFields
- int nAllCount = pmAllFields->getCount();
- for (i=0; i < nAllCount; i++) {
- TCHAR * var=pmAllFields->getUnOrderedKeyName(i);
- if (var && ListOfNonEmptyFields.getIndex(var) < 0)
- continue;
- ListOfFields.insert(var);
- }
-
- // now lets transfer field names
- int nFieldCount = ListOfFields.getCount();
-
- JABBER_CUSTOMSEARCHRESULTS Results={0};
- Results.nSize=sizeof(Results);
- Results.pszFields=(TCHAR**)mir_alloc(sizeof(TCHAR*)*nFieldCount);
- Results.nFieldCount=nFieldCount;
-
- /* Sending Columns Titles */
- for (i=0; i < nFieldCount; i++) {
- TCHAR *var = ListOfFields[i];
- if (var)
- Results.pszFields[i] = pmAllFields->operator [](var);
- }
-
- Results.jsr.hdr.cbSize = 0; // sending column names
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results);
-
- /* Sending Users Data */
- Results.jsr.hdr.cbSize = sizeof(Results.jsr); // sending user data
-
- for (i=0; i < nUsersFound; i++) {
- TCHAR buff[200]; buff[0] = 0;
- Results.jsr.jid[0]=0;
- U_TCHAR_MAP * pmUserData = (U_TCHAR_MAP *) plUsersInfo->operator [](i);
- for (int j=0; j < nFieldCount; j++) {
- TCHAR* var = ListOfFields[j];
- TCHAR* value = pmUserData->operator [](var);
- Results.pszFields[j] = value ? value : (TCHAR *)_T(" ");
- if (!_tcsicmp(var,_T("jid")) && value)
- _tcsncpy_s(Results.jsr.jid, value, _TRUNCATE);
- }
- {
- TCHAR * nickfields[]={ _T("nick"), _T("nickname"),
- _T("fullname"), _T("name"),
- _T("given"), _T("first"),
- _T("jid"), NULL };
- TCHAR * nick=NULL;
- int k=0;
- while (nickfields[k] && !nick) nick=pmUserData->operator [](nickfields[k++]);
- if (_tcsicmp(nick, Results.jsr.jid))
- mir_sntprintf(buff, SIZEOF(buff), _T("%s (%s)"), nick, Results.jsr.jid);
- else
- _tcsncpy_s(buff, nick, _TRUNCATE);
- Results.jsr.hdr.nick = nick ? buff : NULL;
- Results.jsr.hdr.flags = PSR_TCHAR;
- }
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results);
- Results.jsr.hdr.nick=NULL;
-
- }
- mir_free(Results.pszFields);
-}
-
-void DestroyKey(TCHAR* key)
-{
- mir_free(key);
-}
-
-TCHAR* CopyKey(TCHAR* key)
-{
- return mir_tstrdup(key);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Search field request result handler (XEP-0055. Examples 3, 8)
-
-void CJabberProto::OnIqResultAdvancedSearch(HXML iqNode, CJabberIqInfo *pInfo)
-{
- const TCHAR *type;
- int id;
-
- U_TCHAR_MAP mColumnsNames(10);
- LIST<void> SearchResults(2);
-
- if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = xmlGetAttrValue(iqNode, _T("type"))) == NULL)) {
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0);
- return;
- }
-
- if (!lstrcmp(type, _T("result"))) {
- HXML queryNode = xmlGetNthChild(iqNode, _T("query"), 1);
- HXML xNode = xmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
- if (xNode) {
- //1. Form search results info
- HXML reportNode = xmlGetNthChild(xNode, _T("reported"), 1);
- if (reportNode) {
- int i = 1;
- while (HXML fieldNode = xmlGetNthChild(reportNode, _T("field"), i++)) {
- TCHAR *var = (TCHAR*)xmlGetAttrValue(fieldNode, _T("var"));
- if (var) {
- TCHAR *Label = (TCHAR*)xmlGetAttrValue(fieldNode, _T("label"));
- mColumnsNames.insert(var, (Label != NULL) ? Label : var);
- } } }
-
- int i=1;
- HXML itemNode;
- while (itemNode = xmlGetNthChild(xNode, _T("item"), i++)) {
- U_TCHAR_MAP *pUserColumn = new U_TCHAR_MAP(10);
- int j = 1;
- while (HXML fieldNode = xmlGetNthChild(itemNode, _T("field"), j++)) {
- if (TCHAR* var = (TCHAR*)xmlGetAttrValue(fieldNode, _T("var"))) {
- if (TCHAR* Text = (TCHAR*)xmlGetText(xmlGetChild(fieldNode, _T("value")))) {
- if (!mColumnsNames[var])
- mColumnsNames.insert(var,var);
- pUserColumn->insert(var,Text);
- } } }
-
- SearchResults.insert((void*)pUserColumn);
- }
- }
- else {
- //2. Field list search results info
- int i=1;
- while (HXML itemNode = xmlGetNthChild(queryNode, _T("item"), i++)) {
- U_TCHAR_MAP *pUserColumn=new U_TCHAR_MAP(10);
-
- TCHAR *jid = (TCHAR*)xmlGetAttrValue(itemNode, _T("jid"));
- TCHAR *keyReturned;
- mColumnsNames.insertCopyKey(_T("jid"),_T("jid"),&keyReturned, CopyKey, DestroyKey);
- mColumnsNames.insert(_T("jid"), keyReturned);
- pUserColumn->insertCopyKey(_T("jid"), jid, NULL, CopyKey, DestroyKey);
-
- for (int j=0; ; j++) {
- HXML child = xmlGetChild(itemNode, j);
- if (!child)
- break;
-
- const TCHAR *szColumnName = xmlGetName(child);
- if (szColumnName) {
- LPCTSTR ptszChild = xmlGetText(child);
- if (ptszChild && *ptszChild) {
- mColumnsNames.insertCopyKey((TCHAR*)szColumnName,_T(""),&keyReturned, CopyKey, DestroyKey);
- mColumnsNames.insert((TCHAR*)szColumnName,keyReturned);
- pUserColumn->insertCopyKey((TCHAR*)szColumnName, (TCHAR*)ptszChild, NULL, CopyKey, DestroyKey);
- } } }
-
- SearchResults.insert((void*)pUserColumn);
- } }
- }
- else if (!lstrcmp(type, _T("error"))) {
- const TCHAR *code = NULL;
- const TCHAR *description = NULL;
- TCHAR buff[255];
- HXML errorNode = xmlGetChild(iqNode , "error");
- if (errorNode) {
- code = xmlGetAttrValue(errorNode, _T("code"));
- description = xmlGetText(errorNode);
- }
-
- mir_sntprintf(buff, SIZEOF(buff), TranslateT("Error %s %s\r\nTry to specify more detailed"), code ? code : _T(""), description ? description : _T(""));
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0);
- if (searchHandleDlg)
- SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff);
- else
- MessageBox(NULL, buff, TranslateT("Search error"), MB_OK|MB_ICONSTOP);
- return;
- }
-
- SearchReturnResults((HANDLE)id, (void*)&SearchResults, (U_TCHAR_MAP *)&mColumnsNames);
-
- for (int i=0; i < SearchResults.getCount(); i++)
- delete ((U_TCHAR_MAP *)SearchResults[i]);
-
- //send success to finish searching
- ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0);
-}
-
-static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM)
-{
- DestroyWindow(hwnd);
- return TRUE;
-}
-
-static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat)
-{
- //lock
- if (!dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf) {
- for (int i=0; i < dat->nJSInfCount; i++) {
- if (dat->pJSInf[i].hwndValueItem)
- DestroyWindow(dat->pJSInf[i].hwndValueItem);
- if (dat->pJSInf[i].hwndCaptionItem)
- DestroyWindow(dat->pJSInf[i].hwndCaptionItem);
- if (dat->pJSInf[i].szFieldCaption)
- free(dat->pJSInf[i].szFieldCaption);
- if (dat->pJSInf[i].szFieldName)
- free(dat->pJSInf[i].szFieldName);
- }
- free(dat->pJSInf);
- dat->pJSInf=NULL;
- }
- else EnumChildWindows(GetDlgItem(hwndDlg,IDC_FRAME),DeleteChildWindowsProc,0);
-
- if (dat->xNode)
- xi.destroyNode(dat->xNode);
-
- SendMessage(GetDlgItem(hwndDlg,IDC_FRAME), WM_SETFONT, (WPARAM)SendMessage(hwndDlg, WM_GETFONT, 0, 0),0);
- dat->nJSInfCount=0;
- ShowWindow(GetDlgItem(hwndDlg,IDC_VSCROLL),SW_HIDE);
- SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,TranslateT("Select/type search service URL above and press <Go>"));
- //unlock
-}
-
-static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData * dat)
-{
- HWND hFrame = GetDlgItem(hwndDlg, IDC_FRAME);
- HWND hwndScroll = GetDlgItem(hwndDlg, IDC_VSCROLL);
- RECT rc;
- GetClientRect(hFrame, &rc);
- GetClientRect(hFrame, &dat->frameRect);
- dat->frameHeight = rc.bottom-rc.top;
- if (dat->frameHeight < dat->CurrentHeight) {
- ShowWindow(hwndScroll, SW_SHOW);
- EnableWindow(hwndScroll, TRUE);
- }
- else ShowWindow(hwndScroll, SW_HIDE);
-
- SetScrollRange(hwndScroll, SB_CTL, 0, dat->CurrentHeight-dat->frameHeight, FALSE);
-}
-
-int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData * dat)
-{
- TCHAR szServerName[100];
- EnableWindow(GetDlgItem(hwndDlg, IDC_GO),FALSE);
- GetDlgItemText(hwndDlg,IDC_SERVER,szServerName,SIZEOF(szServerName));
- dat->CurrentHeight = 0;
- dat->curPos = 0;
- SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
-
- JabberSearchFreeData(hwndDlg, dat);
- JabberSearchRefreshFrameScroll(hwndDlg, dat);
-
- SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server"));
-
- if (!m_bJabberOnline)
- return 0;
-
- searchHandleDlg = hwndDlg;
-
- CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultGetSearchFields, JABBER_IQ_TYPE_GET, szServerName);
- m_ThreadInfo->send( XmlNodeIq(pInfo) << XQUERY(_T("jabber:iq:search")));
- return pInfo->GetIqId();
-}
-
-static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const TCHAR *szAddr)
-{
- int lResult = SendMessage(GetDlgItem(hwndDlg,IDC_SERVER), (UINT) CB_FINDSTRING, 0, (LPARAM)szAddr);
- if (lResult == -1)
- SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szAddr);
-}
-
-void CJabberProto::SearchDeleteFromRecent(const TCHAR *szAddr, BOOL deleteLastFromDB)
-{
- //search in recent
- for (int i=0; i<10; i++) {
- char key[30];
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", i);
- ptrT szValue( getTStringA(key));
- if (szValue == NULL || _tcsicmp(szAddr, szValue))
- continue;
-
- for (int j=i; j < 10; j++) {
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j + 1);
- szValue = getTStringA(key);
- if (szValue != NULL) {
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j);
- setTString(NULL, key, szValue);
- }
- else {
- if (deleteLastFromDB) {
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j);
- delSetting(NULL,key);
- }
- break;
- } }
- break;
-} }
-
-void CJabberProto::SearchAddToRecent(const TCHAR *szAddr, HWND hwndDialog)
-{
- char key[30];
- SearchDeleteFromRecent(szAddr);
-
- for (int j=9; j > 0; j--) {
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j - 1);
- ptrT szValue( getTStringA(key));
- if (szValue != NULL) {
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j);
- setTString(NULL, key, szValue);
- } }
-
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", 0);
- setTString(key, szAddr);
- if (hwndDialog)
- JabberSearchAddUrlToRecentCombo(hwndDialog, szAddr);
-}
-
-static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- JabberSearchData* dat = (JabberSearchData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
- switch (msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- {
- dat = (JabberSearchData *)mir_calloc(sizeof(JabberSearchData));
- dat->ppro = (CJabberProto*)lParam;
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
-
- /* Server Combo box */
- ptrA jud(dat->ppro->getStringA("Jud"));
- char *szServerName = (jud == NULL) ? "users.jabber.org": jud;
- SetDlgItemTextA(hwndDlg,IDC_SERVER,szServerName);
- SendDlgItemMessageA(hwndDlg,IDC_SERVER,CB_ADDSTRING,0,(LPARAM)szServerName);
- //TO DO: Add Transports here
- int i, transpCount = dat->ppro->m_lstTransports.getCount();
- for (i=0; i < transpCount; i++) {
- TCHAR *szTransp = dat->ppro->m_lstTransports[i];
- if (szTransp)
- JabberSearchAddUrlToRecentCombo(hwndDlg, szTransp);
- }
-
- for (i=0; i < 10; i++) {
- char key[30];
- mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", i);
- ptrT szValue( dat->ppro->getTStringA(key));
- if (szValue != NULL)
- JabberSearchAddUrlToRecentCombo(hwndDlg, szValue);
- }
-
- //TO DO: Add 4 recently used
- dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg,dat);
- }
- return TRUE;
-
- case WM_COMMAND:
- if (LOWORD(wParam) == IDC_SERVER) {
- switch (HIWORD(wParam)) {
- case CBN_SETFOCUS:
- PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg);
- return TRUE;
-
- case CBN_EDITCHANGE:
- EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
- return TRUE;
-
- case CBN_EDITUPDATE:
- JabberSearchFreeData(hwndDlg, dat);
- EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
- return TRUE;
-
- case CBN_SELENDOK:
- EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
- PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_GO,BN_CLICKED),0);
- return TRUE;
- }
- }
- else if (LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED) {
- dat->ppro->SearchRenewFields(hwndDlg, dat);
- return TRUE;
- }
- break;
-
- case WM_SIZE:
- {
- //Resize IDC_FRAME to take full size
- RECT rcForm;
- GetWindowRect(hwndDlg, &rcForm);
- RECT rcFrame;
- GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame);
- rcFrame.bottom = rcForm.bottom;
- SetWindowPos(GetDlgItem(hwndDlg,IDC_FRAME),NULL,0,0,rcFrame.right-rcFrame.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE);
- GetWindowRect(GetDlgItem(hwndDlg,IDC_VSCROLL), &rcForm);
- SetWindowPos(GetDlgItem(hwndDlg,IDC_VSCROLL),NULL,0,0,rcForm.right-rcForm.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE);
- JabberSearchRefreshFrameScroll(hwndDlg, dat);
- }
- return TRUE;
-
- case WM_USER+11:
- {
- dat->fSearchRequestIsXForm=TRUE;
- dat->xNode = (HXML)wParam;
- JabberFormCreateUI(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight,TRUE);
- ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW);
- dat->nJSInfCount=1;
- return TRUE;
- }
- case WM_USER+10:
- {
- Data* MyDat = (Data*)lParam;
- if (MyDat) {
- dat->fSearchRequestIsXForm = (BOOL)wParam;
- dat->CurrentHeight = JabberSearchAddField(hwndDlg,MyDat);
- mir_free(MyDat->Label);
- mir_free(MyDat->Var);
- mir_free(MyDat->defValue);
- free(MyDat);
- }
- else
- {
- JabberSearchRefreshFrameScroll(hwndDlg,dat);
- ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - 0, NULL, &(dat->frameRect));
- SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
- dat->curPos=0;
- }
- return TRUE;
- }
- case WM_MOUSEWHEEL:
- {
- int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
- if (zDelta) {
- int nScrollLines=0;
- SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,(void*)&nScrollLines,0);
- for (int i=0; i<(nScrollLines+1)/2; i++)
- SendMessage(hwndDlg,WM_VSCROLL, (zDelta<0)?SB_LINEDOWN:SB_LINEUP,0);
- } }
- return TRUE;
-
- case WM_VSCROLL:
- {
- int pos;
- if (dat != NULL) {
- pos = dat->curPos;
- switch (LOWORD(wParam)) {
- case SB_LINEDOWN:
- pos += 10;
- break;
- case SB_LINEUP:
- pos -= 10;
- break;
- case SB_PAGEDOWN:
- pos += (dat->CurrentHeight - 10);
- break;
- case SB_PAGEUP:
- pos -= (dat->CurrentHeight - 10);
- break;
- case SB_THUMBTRACK:
- pos = HIWORD(wParam);
- break;
- }
- if (pos > (dat->CurrentHeight - dat->frameHeight))
- pos = dat->CurrentHeight - dat->frameHeight;
- if (pos < 0)
- pos = 0;
- if (dat->curPos != pos) {
- ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, NULL , &(dat->frameRect));
- SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE);
- RECT Invalid=dat->frameRect;
- if (dat->curPos - pos >0)
- Invalid.bottom=Invalid.top+(dat->curPos - pos);
- else
- Invalid.top=Invalid.bottom+(dat->curPos - pos);
-
- RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), NULL, NULL, RDW_UPDATENOW |RDW_ALLCHILDREN);
- dat->curPos = pos;
- } } }
- return TRUE;
-
- case WM_DESTROY:
- JabberSearchFreeData(hwndDlg, dat);
- JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME));
- mir_free(dat);
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- return TRUE;
- }
- return FALSE;
-}
-
-HWND __cdecl CJabberProto::CreateExtendedSearchUI(HWND parent)
-{
- if (parent && hInst) {
- ptrT szServer( getTStringA("LoginServer"));
- if (szServer == NULL || _tcsicmp(szServer, _T("S.ms")))
- return CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this);
- }
-
- return 0; // Failure
-}
-
-//////////////////////////////////////////////////////////////////////////
-// The function formats request to server
-
-HWND __cdecl CJabberProto::SearchAdvanced(HWND hwndDlg)
-{
- if (!m_bJabberOnline || !hwndDlg)
- return 0; //error
-
- JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
- if (!dat)
- return 0; //error
-
- // check if server connected (at least one field exists)
- if (dat->nJSInfCount == 0)
- return 0;
-
- // formating request
- BOOL fRequestNotEmpty=FALSE;
-
- // get server name
- TCHAR szServerName[100];
- GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, SIZEOF(szServerName));
-
- // formating query
- CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, szServerName);
- XmlNodeIq iq(pInfo);
- HXML query = iq << XQUERY(_T("jabber:iq:search"));
-
- if (m_tszSelectedLang)
- iq << XATTR(_T("xml:lang"), m_tszSelectedLang); // i'm sure :)
-
- // next can be 2 cases:
- // Forms: XEP-0055 Example 7
- if (dat->fSearchRequestIsXForm) {
- fRequestNotEmpty=TRUE;
- HXML n = JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode);
- xmlAddChild(query, n);
- xi.destroyNode(n);
- }
- else { //and Simple fields: XEP-0055 Example 3
- for (int i=0; i<dat->nJSInfCount; i++) {
- TCHAR szFieldValue[100];
- GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, SIZEOF(szFieldValue));
- if (szFieldValue[0] != 0) {
- xmlAddChild(query, dat->pJSInf[i].szFieldName, szFieldValue);
- fRequestNotEmpty=TRUE;
- } } }
-
- if (fRequestNotEmpty) {
- m_ThreadInfo->send(iq);
- return (HWND)pInfo->GetIqId();
- }
- return 0;
-}
+/*
+
+Jabber Protocol Plugin for Miranda NG
+
+Copyright (c) 2002-04 Santithorn Bunchua
+Copyright (c) 2005-12 George Hazan
+Copyright (c) 2007 Artem Shpynov
+Copyright (c) 2012-14 Miranda NG project
+
+Module implements a search according to XEP-0055: Jabber Search
+http://www.xmpp.org/extensions/xep-0055.html
+
+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.
+
+*/
+
+#include "jabber.h"
+#include <CommCtrl.h>
+#include "jabber_iq.h"
+#include "jabber_caps.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Subclassing of IDC_FRAME to implement more user-friendly fields scrolling
+//
+static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_COMMAND && lParam != 0) {
+ HWND hwndDlg=GetParent(hwnd);
+ JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if (dat && lParam) {
+ int pos=dat->curPos;
+ RECT MineRect;
+ RECT FrameRect;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME),&FrameRect);
+ GetWindowRect((HWND)lParam, &MineRect);
+ if (MineRect.top-10 < FrameRect.top) {
+ pos=dat->curPos+(MineRect.top-14-FrameRect.top);
+ if (pos<0) pos=0;
+ }
+ else if (MineRect.bottom > FrameRect.bottom) {
+ pos=dat->curPos+(MineRect.bottom-FrameRect.bottom);
+ if (dat->frameHeight+pos>dat->CurrentHeight)
+ pos=dat->CurrentHeight-dat->frameHeight;
+ }
+ if (pos != dat->curPos) {
+ ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, NULL, &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE);
+ RECT Invalid=dat->frameRect;
+ if (dat->curPos - pos >0)
+ Invalid.bottom=Invalid.top+(dat->curPos - pos);
+ else
+ Invalid.top=Invalid.bottom+(dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), NULL, NULL, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW |RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ }
+ }
+ if (HIWORD(wParam)==EN_SETFOCUS) { //Transmit focus set notification to parent window
+ PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg);
+ }
+ }
+
+ if (msg == WM_PAINT) {
+ PAINTSTRUCT ps;
+ HDC hdc=BeginPaint(hwnd, &ps);
+ FillRect(hdc,&(ps.rcPaint),GetSysColorBrush(COLOR_BTNFACE));
+ EndPaint(hwnd, &ps);
+ }
+
+ return DefWindowProc(hwnd,msg,wParam,lParam);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Add Search field to form
+//
+static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat)
+{
+ if (!FieldDat || !FieldDat->Label || !FieldDat->Var)
+ return FALSE;
+
+ HFONT hFont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0);
+ HWND hwndParent=GetDlgItem(hwndDlg,IDC_FRAME);
+ LONG_PTR frameExStyle = GetWindowLongPtr(hwndParent, GWL_EXSTYLE);
+ frameExStyle |= WS_EX_CONTROLPARENT;
+ SetWindowLongPtr(hwndParent, GWL_EXSTYLE, frameExStyle);
+ SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_FRAME),GWLP_WNDPROC,(LONG_PTR)JabberSearchFrameProc);
+
+ int CornerX=1;
+ int CornerY=1;
+ RECT rect;
+ GetClientRect(hwndParent,&rect);
+ int width=rect.right-5-CornerX;
+
+ int Order=(FieldDat->bHidden) ? -1 : FieldDat->Order;
+
+ HWND hwndLabel=CreateWindowEx(0,_T("STATIC"),(LPCTSTR)TranslateTS(FieldDat->Label),WS_CHILD, CornerX, CornerY + Order*40, width, 13,hwndParent,NULL,hInst,0);
+ HWND hwndVar=CreateWindowEx(0|WS_EX_CLIENTEDGE,_T("EDIT"),(LPCTSTR)FieldDat->defValue,WS_CHILD|WS_TABSTOP, CornerX+5, CornerY + Order*40+14, width ,20,hwndParent,NULL,hInst,0);
+ SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont,0);
+ SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont,0);
+ if (!FieldDat->bHidden) {
+ ShowWindow(hwndLabel,SW_SHOW);
+ ShowWindow(hwndVar,SW_SHOW);
+ EnableWindow(hwndLabel,!FieldDat->bReadOnly);
+ SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly,0);
+ }
+ //remade list
+ //reallocation
+ JabberSearchData *dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if (dat) {
+ dat->pJSInf=(JabberSearchFieldsInfo*)realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount+1));
+ dat->pJSInf[dat->nJSInfCount].hwndCaptionItem=hwndLabel;
+ dat->pJSInf[dat->nJSInfCount].hwndValueItem=hwndVar;
+ dat->pJSInf[dat->nJSInfCount].szFieldCaption=_tcsdup(FieldDat->Label);
+ dat->pJSInf[dat->nJSInfCount].szFieldName=_tcsdup(FieldDat->Var);
+ dat->nJSInfCount++;
+ }
+ return CornerY + Order*40+14 +20;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Available search field request result handler (XEP-0055. Examples 2, 7)
+
+void CJabberProto::OnIqResultGetSearchFields(HXML iqNode, CJabberIqInfo *pInfo)
+{
+ if (!searchHandleDlg)
+ return;
+
+ LPCTSTR type = xmlGetAttrValue(iqNode, _T("type"));
+ if (type == NULL)
+ return;
+
+ if (!lstrcmp(type, _T("result"))) {
+ HXML queryNode = xmlGetNthChild(iqNode, _T("query"), 1);
+ HXML xNode = xmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
+
+ ShowWindow(searchHandleDlg,SW_HIDE);
+ if (xNode) {
+ //1. Form
+ PostMessage(searchHandleDlg, WM_USER+11, (WPARAM)xi.copyNode(xNode), 0);
+ HXML xcNode = xmlGetNthChild(xNode, _T("instructions"), 1);
+ if (xcNode)
+ SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, xmlGetText(xcNode));
+ }
+ else {
+ int Order=0;
+ for (int i=0; ; i++) {
+ HXML chNode = xmlGetChild(queryNode, i);
+ if (!chNode)
+ break;
+
+ if (!_tcsicmp(xmlGetName(chNode), _T("instructions")) && xmlGetText(chNode))
+ SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,TranslateTS(xmlGetText(chNode)));
+ else if (xmlGetName(chNode)) {
+ Data *MyData=(Data*)malloc(sizeof(Data));
+ memset(MyData,0,sizeof(Data));
+
+ MyData->Label = mir_tstrdup(xmlGetName(chNode));
+ MyData->Var = mir_tstrdup(xmlGetName(chNode));
+ MyData->defValue = mir_tstrdup(xmlGetText(chNode));
+ MyData->Order = Order;
+ if (MyData->defValue) MyData->bReadOnly = TRUE;
+ PostMessage(searchHandleDlg,WM_USER+10,FALSE,(LPARAM)MyData);
+ Order++;
+ }
+ }
+ }
+
+ const TCHAR *szFrom = xmlGetAttrValue(iqNode, _T("from"));
+ if (szFrom)
+ SearchAddToRecent(szFrom,searchHandleDlg);
+ PostMessage(searchHandleDlg,WM_USER+10,0,0);
+ ShowWindow(searchHandleDlg,SW_SHOW);
+ }
+ else if (!lstrcmp(type, _T("error"))) {
+ const TCHAR *code=NULL;
+ const TCHAR *description=NULL;
+ TCHAR buff[255];
+ HXML errorNode = xmlGetChild(iqNode, "error");
+ if (errorNode) {
+ code = xmlGetAttrValue(errorNode, _T("code"));
+ description = xmlGetText(errorNode);
+ }
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT("Error %s %s\r\nPlease select other server"), code ? code : _T(""), description ? description : _T(""));
+ SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff);
+ }
+ else SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateT("Error: unknown reply received\r\nPlease select other server"));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Return results to search dialog
+// The pmFields is the pointer to map of <field Name, field Label> Not unical but ordered
+// This can help to made result parser routines more simple
+
+void CJabberProto::SearchReturnResults(HANDLE id, void * pvUsersInfo, U_TCHAR_MAP * pmAllFields)
+{
+ LIST<TCHAR> ListOfNonEmptyFields(20,(LIST<TCHAR>::FTSortFunc)TCharKeyCmp);
+ LIST<TCHAR> ListOfFields(20);
+ LIST<void>* plUsersInfo = (LIST<void>*)pvUsersInfo;
+ int i, nUsersFound = plUsersInfo->getCount();
+
+ // lets fill the ListOfNonEmptyFields but in users order
+ for (i=0; i < nUsersFound; i++) {
+ U_TCHAR_MAP* pmUserData = (U_TCHAR_MAP*)plUsersInfo->operator [](i);
+ int nUserFields = pmUserData->getCount();
+ for (int j=0; j < nUserFields; j++) {
+ TCHAR *var = pmUserData->getKeyName(j);
+ if (var && ListOfNonEmptyFields.getIndex(var) < 0)
+ ListOfNonEmptyFields.insert(var);
+ } }
+
+ // now fill the ListOfFields but order is from pmAllFields
+ int nAllCount = pmAllFields->getCount();
+ for (i=0; i < nAllCount; i++) {
+ TCHAR * var=pmAllFields->getUnOrderedKeyName(i);
+ if (var && ListOfNonEmptyFields.getIndex(var) < 0)
+ continue;
+ ListOfFields.insert(var);
+ }
+
+ // now lets transfer field names
+ int nFieldCount = ListOfFields.getCount();
+
+ JABBER_CUSTOMSEARCHRESULTS Results={0};
+ Results.nSize=sizeof(Results);
+ Results.pszFields=(TCHAR**)mir_alloc(sizeof(TCHAR*)*nFieldCount);
+ Results.nFieldCount=nFieldCount;
+
+ /* Sending Columns Titles */
+ for (i=0; i < nFieldCount; i++) {
+ TCHAR *var = ListOfFields[i];
+ if (var)
+ Results.pszFields[i] = pmAllFields->operator [](var);
+ }
+
+ Results.jsr.hdr.cbSize = 0; // sending column names
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results);
+
+ /* Sending Users Data */
+ Results.jsr.hdr.cbSize = sizeof(Results.jsr); // sending user data
+
+ for (i=0; i < nUsersFound; i++) {
+ TCHAR buff[200];
+ buff[0] = 0;
+ Results.jsr.jid[0] = 0;
+ U_TCHAR_MAP * pmUserData = (U_TCHAR_MAP *) plUsersInfo->operator [](i);
+ for (int j=0; j < nFieldCount; j++) {
+ TCHAR* var = ListOfFields[j];
+ TCHAR* value = pmUserData->operator [](var);
+ Results.pszFields[j] = value ? value : (TCHAR *)_T(" ");
+ if (!_tcsicmp(var,_T("jid")) && value)
+ _tcsncpy_s(Results.jsr.jid, value, _TRUNCATE);
+ }
+ {
+ TCHAR * nickfields[]={ _T("nick"), _T("nickname"),
+ _T("fullname"), _T("name"),
+ _T("given"), _T("first"),
+ _T("jid"), NULL };
+ TCHAR * nick = NULL;
+ int k = 0;
+ while (nickfields[k] && !nick) {
+ nick = pmUserData->operator [](nickfields[k++]);
+ }
+ if (nick) {
+ if (_tcsicmp(nick, Results.jsr.jid)) {
+ mir_sntprintf(buff, SIZEOF(buff), _T("%s (%s)"), nick, Results.jsr.jid);
+ } else {
+ _tcsncpy_s(buff, nick, _TRUNCATE);
+ }
+ nick = buff;
+ }
+ Results.jsr.hdr.nick = nick;
+ Results.jsr.hdr.flags = PSR_TCHAR;
+ }
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results);
+ Results.jsr.hdr.nick=NULL;
+ }
+ mir_free(Results.pszFields);
+}
+
+void DestroyKey(TCHAR* key)
+{
+ mir_free(key);
+}
+
+TCHAR* CopyKey(TCHAR* key)
+{
+ return mir_tstrdup(key);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Search field request result handler (XEP-0055. Examples 3, 8)
+
+void CJabberProto::OnIqResultAdvancedSearch(HXML iqNode, CJabberIqInfo *pInfo)
+{
+ const TCHAR *type;
+ int id;
+
+ U_TCHAR_MAP mColumnsNames(10);
+ LIST<void> SearchResults(2);
+
+ if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = xmlGetAttrValue(iqNode, _T("type"))) == NULL)) {
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0);
+ return;
+ }
+
+ if (!lstrcmp(type, _T("result"))) {
+ HXML queryNode = xmlGetNthChild(iqNode, _T("query"), 1);
+ HXML xNode = xmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
+ if (xNode) {
+ //1. Form search results info
+ HXML reportNode = xmlGetNthChild(xNode, _T("reported"), 1);
+ if (reportNode) {
+ int i = 1;
+ while (HXML fieldNode = xmlGetNthChild(reportNode, _T("field"), i++)) {
+ TCHAR *var = (TCHAR*)xmlGetAttrValue(fieldNode, _T("var"));
+ if (var) {
+ TCHAR *Label = (TCHAR*)xmlGetAttrValue(fieldNode, _T("label"));
+ mColumnsNames.insert(var, (Label != NULL) ? Label : var);
+ } } }
+
+ int i=1;
+ HXML itemNode;
+ while (itemNode = xmlGetNthChild(xNode, _T("item"), i++)) {
+ U_TCHAR_MAP *pUserColumn = new U_TCHAR_MAP(10);
+ int j = 1;
+ while (HXML fieldNode = xmlGetNthChild(itemNode, _T("field"), j++)) {
+ if (TCHAR* var = (TCHAR*)xmlGetAttrValue(fieldNode, _T("var"))) {
+ if (TCHAR* Text = (TCHAR*)xmlGetText(xmlGetChild(fieldNode, _T("value")))) {
+ if (!mColumnsNames[var])
+ mColumnsNames.insert(var,var);
+ pUserColumn->insert(var,Text);
+ } } }
+
+ SearchResults.insert((void*)pUserColumn);
+ }
+ }
+ else {
+ //2. Field list search results info
+ int i=1;
+ while (HXML itemNode = xmlGetNthChild(queryNode, _T("item"), i++)) {
+ U_TCHAR_MAP *pUserColumn=new U_TCHAR_MAP(10);
+
+ TCHAR *jid = (TCHAR*)xmlGetAttrValue(itemNode, _T("jid"));
+ TCHAR *keyReturned;
+ mColumnsNames.insertCopyKey(_T("jid"),_T("jid"),&keyReturned, CopyKey, DestroyKey);
+ mColumnsNames.insert(_T("jid"), keyReturned);
+ pUserColumn->insertCopyKey(_T("jid"), jid, NULL, CopyKey, DestroyKey);
+
+ for (int j=0; ; j++) {
+ HXML child = xmlGetChild(itemNode, j);
+ if (!child)
+ break;
+
+ const TCHAR *szColumnName = xmlGetName(child);
+ if (szColumnName) {
+ LPCTSTR ptszChild = xmlGetText(child);
+ if (ptszChild && *ptszChild) {
+ mColumnsNames.insertCopyKey((TCHAR*)szColumnName,_T(""),&keyReturned, CopyKey, DestroyKey);
+ mColumnsNames.insert((TCHAR*)szColumnName,keyReturned);
+ pUserColumn->insertCopyKey((TCHAR*)szColumnName, (TCHAR*)ptszChild, NULL, CopyKey, DestroyKey);
+ } } }
+
+ SearchResults.insert((void*)pUserColumn);
+ } }
+ }
+ else if (!lstrcmp(type, _T("error"))) {
+ const TCHAR *code = NULL;
+ const TCHAR *description = NULL;
+ TCHAR buff[255];
+ HXML errorNode = xmlGetChild(iqNode , "error");
+ if (errorNode) {
+ code = xmlGetAttrValue(errorNode, _T("code"));
+ description = xmlGetText(errorNode);
+ }
+
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT("Error %s %s\r\nTry to specify more detailed"), code ? code : _T(""), description ? description : _T(""));
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0);
+ if (searchHandleDlg)
+ SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff);
+ else
+ MessageBox(NULL, buff, TranslateT("Search error"), MB_OK|MB_ICONSTOP);
+ return;
+ }
+
+ SearchReturnResults((HANDLE)id, (void*)&SearchResults, (U_TCHAR_MAP *)&mColumnsNames);
+
+ for (int i=0; i < SearchResults.getCount(); i++)
+ delete ((U_TCHAR_MAP *)SearchResults[i]);
+
+ //send success to finish searching
+ ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0);
+}
+
+static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM)
+{
+ DestroyWindow(hwnd);
+ return TRUE;
+}
+
+static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat)
+{
+ //lock
+ if (!dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf) {
+ for (int i=0; i < dat->nJSInfCount; i++) {
+ if (dat->pJSInf[i].hwndValueItem)
+ DestroyWindow(dat->pJSInf[i].hwndValueItem);
+ if (dat->pJSInf[i].hwndCaptionItem)
+ DestroyWindow(dat->pJSInf[i].hwndCaptionItem);
+ if (dat->pJSInf[i].szFieldCaption)
+ free(dat->pJSInf[i].szFieldCaption);
+ if (dat->pJSInf[i].szFieldName)
+ free(dat->pJSInf[i].szFieldName);
+ }
+ free(dat->pJSInf);
+ dat->pJSInf=NULL;
+ }
+ else EnumChildWindows(GetDlgItem(hwndDlg,IDC_FRAME),DeleteChildWindowsProc,0);
+
+ if (dat->xNode)
+ xi.destroyNode(dat->xNode);
+
+ SendMessage(GetDlgItem(hwndDlg,IDC_FRAME), WM_SETFONT, (WPARAM)SendMessage(hwndDlg, WM_GETFONT, 0, 0),0);
+ dat->nJSInfCount=0;
+ ShowWindow(GetDlgItem(hwndDlg,IDC_VSCROLL),SW_HIDE);
+ SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,TranslateT("Select/type search service URL above and press <Go>"));
+ //unlock
+}
+
+static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData * dat)
+{
+ HWND hFrame = GetDlgItem(hwndDlg, IDC_FRAME);
+ HWND hwndScroll = GetDlgItem(hwndDlg, IDC_VSCROLL);
+ RECT rc;
+ GetClientRect(hFrame, &rc);
+ GetClientRect(hFrame, &dat->frameRect);
+ dat->frameHeight = rc.bottom-rc.top;
+ if (dat->frameHeight < dat->CurrentHeight) {
+ ShowWindow(hwndScroll, SW_SHOW);
+ EnableWindow(hwndScroll, TRUE);
+ }
+ else ShowWindow(hwndScroll, SW_HIDE);
+
+ SetScrollRange(hwndScroll, SB_CTL, 0, dat->CurrentHeight-dat->frameHeight, FALSE);
+}
+
+int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData * dat)
+{
+ TCHAR szServerName[100];
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),FALSE);
+ GetDlgItemText(hwndDlg,IDC_SERVER,szServerName,SIZEOF(szServerName));
+ dat->CurrentHeight = 0;
+ dat->curPos = 0;
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
+
+ JabberSearchFreeData(hwndDlg, dat);
+ JabberSearchRefreshFrameScroll(hwndDlg, dat);
+
+ SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server"));
+
+ if (!m_bJabberOnline)
+ return 0;
+
+ searchHandleDlg = hwndDlg;
+
+ CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultGetSearchFields, JABBER_IQ_TYPE_GET, szServerName);
+ m_ThreadInfo->send( XmlNodeIq(pInfo) << XQUERY(_T("jabber:iq:search")));
+ return pInfo->GetIqId();
+}
+
+static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const TCHAR *szAddr)
+{
+ int lResult = SendMessage(GetDlgItem(hwndDlg,IDC_SERVER), (UINT) CB_FINDSTRING, 0, (LPARAM)szAddr);
+ if (lResult == -1)
+ SendDlgItemMessage(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, (LPARAM)szAddr);
+}
+
+void CJabberProto::SearchDeleteFromRecent(const TCHAR *szAddr, BOOL deleteLastFromDB)
+{
+ //search in recent
+ for (int i=0; i<10; i++) {
+ char key[30];
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", i);
+ ptrT szValue( getTStringA(key));
+ if (szValue == NULL || _tcsicmp(szAddr, szValue))
+ continue;
+
+ for (int j=i; j < 10; j++) {
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j + 1);
+ szValue = getTStringA(key);
+ if (szValue != NULL) {
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j);
+ setTString(NULL, key, szValue);
+ }
+ else {
+ if (deleteLastFromDB) {
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j);
+ delSetting(NULL,key);
+ }
+ break;
+ } }
+ break;
+} }
+
+void CJabberProto::SearchAddToRecent(const TCHAR *szAddr, HWND hwndDialog)
+{
+ char key[30];
+ SearchDeleteFromRecent(szAddr);
+
+ for (int j=9; j > 0; j--) {
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j - 1);
+ ptrT szValue( getTStringA(key));
+ if (szValue != NULL) {
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", j);
+ setTString(NULL, key, szValue);
+ } }
+
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", 0);
+ setTString(key, szAddr);
+ if (hwndDialog)
+ JabberSearchAddUrlToRecentCombo(hwndDialog, szAddr);
+}
+
+static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ JabberSearchData* dat = (JabberSearchData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ dat = (JabberSearchData *)mir_calloc(sizeof(JabberSearchData));
+ dat->ppro = (CJabberProto*)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ /* Server Combo box */
+ ptrA jud(dat->ppro->getStringA("Jud"));
+ char *szServerName = (jud == NULL) ? "users.jabber.org": jud;
+ SetDlgItemTextA(hwndDlg,IDC_SERVER,szServerName);
+ SendDlgItemMessageA(hwndDlg,IDC_SERVER,CB_ADDSTRING,0,(LPARAM)szServerName);
+ //TO DO: Add Transports here
+ int i, transpCount = dat->ppro->m_lstTransports.getCount();
+ for (i=0; i < transpCount; i++) {
+ TCHAR *szTransp = dat->ppro->m_lstTransports[i];
+ if (szTransp)
+ JabberSearchAddUrlToRecentCombo(hwndDlg, szTransp);
+ }
+
+ for (i=0; i < 10; i++) {
+ char key[30];
+ mir_snprintf(key, SIZEOF(key), "RecentlySearched_%d", i);
+ ptrT szValue( dat->ppro->getTStringA(key));
+ if (szValue != NULL)
+ JabberSearchAddUrlToRecentCombo(hwndDlg, szValue);
+ }
+
+ //TO DO: Add 4 recently used
+ dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg,dat);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_SERVER) {
+ switch (HIWORD(wParam)) {
+ case CBN_SETFOCUS:
+ PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg);
+ return TRUE;
+
+ case CBN_EDITCHANGE:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
+ return TRUE;
+
+ case CBN_EDITUPDATE:
+ JabberSearchFreeData(hwndDlg, dat);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
+ return TRUE;
+
+ case CBN_SELENDOK:
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE);
+ PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_GO,BN_CLICKED),0);
+ return TRUE;
+ }
+ }
+ else if (LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED) {
+ dat->ppro->SearchRenewFields(hwndDlg, dat);
+ return TRUE;
+ }
+ break;
+
+ case WM_SIZE:
+ {
+ //Resize IDC_FRAME to take full size
+ RECT rcForm;
+ GetWindowRect(hwndDlg, &rcForm);
+ RECT rcFrame;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame);
+ rcFrame.bottom = rcForm.bottom;
+ SetWindowPos(GetDlgItem(hwndDlg,IDC_FRAME),NULL,0,0,rcFrame.right-rcFrame.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE);
+ GetWindowRect(GetDlgItem(hwndDlg,IDC_VSCROLL), &rcForm);
+ SetWindowPos(GetDlgItem(hwndDlg,IDC_VSCROLL),NULL,0,0,rcForm.right-rcForm.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE);
+ JabberSearchRefreshFrameScroll(hwndDlg, dat);
+ }
+ return TRUE;
+
+ case WM_USER+11:
+ {
+ dat->fSearchRequestIsXForm=TRUE;
+ dat->xNode = (HXML)wParam;
+ JabberFormCreateUI(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight,TRUE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW);
+ dat->nJSInfCount=1;
+ return TRUE;
+ }
+ case WM_USER+10:
+ {
+ Data* MyDat = (Data*)lParam;
+ if (MyDat) {
+ dat->fSearchRequestIsXForm = (BOOL)wParam;
+ dat->CurrentHeight = JabberSearchAddField(hwndDlg,MyDat);
+ mir_free(MyDat->Label);
+ mir_free(MyDat->Var);
+ mir_free(MyDat->defValue);
+ free(MyDat);
+ }
+ else
+ {
+ JabberSearchRefreshFrameScroll(hwndDlg,dat);
+ ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - 0, NULL, &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
+ dat->curPos=0;
+ }
+ return TRUE;
+ }
+ case WM_MOUSEWHEEL:
+ {
+ int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
+ if (zDelta) {
+ int nScrollLines=0;
+ SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,(void*)&nScrollLines,0);
+ for (int i=0; i<(nScrollLines+1)/2; i++)
+ SendMessage(hwndDlg,WM_VSCROLL, (zDelta<0)?SB_LINEDOWN:SB_LINEUP,0);
+ } }
+ return TRUE;
+
+ case WM_VSCROLL:
+ {
+ int pos;
+ if (dat != NULL) {
+ pos = dat->curPos;
+ switch (LOWORD(wParam)) {
+ case SB_LINEDOWN:
+ pos += 10;
+ break;
+ case SB_LINEUP:
+ pos -= 10;
+ break;
+ case SB_PAGEDOWN:
+ pos += (dat->CurrentHeight - 10);
+ break;
+ case SB_PAGEUP:
+ pos -= (dat->CurrentHeight - 10);
+ break;
+ case SB_THUMBTRACK:
+ pos = HIWORD(wParam);
+ break;
+ }
+ if (pos > (dat->CurrentHeight - dat->frameHeight))
+ pos = dat->CurrentHeight - dat->frameHeight;
+ if (pos < 0)
+ pos = 0;
+ if (dat->curPos != pos) {
+ ScrollWindow(GetDlgItem(hwndDlg, IDC_FRAME), 0, dat->curPos - pos, NULL , &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, pos, TRUE);
+ RECT Invalid=dat->frameRect;
+ if (dat->curPos - pos >0)
+ Invalid.bottom=Invalid.top+(dat->curPos - pos);
+ else
+ Invalid.top=Invalid.bottom+(dat->curPos - pos);
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_FRAME), NULL, NULL, RDW_UPDATENOW |RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ } } }
+ return TRUE;
+
+ case WM_DESTROY:
+ JabberSearchFreeData(hwndDlg, dat);
+ JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME));
+ mir_free(dat);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+HWND __cdecl CJabberProto::CreateExtendedSearchUI(HWND parent)
+{
+ if (parent && hInst) {
+ ptrT szServer( getTStringA("LoginServer"));
+ if (szServer == NULL || _tcsicmp(szServer, _T("S.ms")))
+ return CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this);
+ }
+
+ return 0; // Failure
+}
+
+//////////////////////////////////////////////////////////////////////////
+// The function formats request to server
+
+HWND __cdecl CJabberProto::SearchAdvanced(HWND hwndDlg)
+{
+ if (!m_bJabberOnline || !hwndDlg)
+ return 0; //error
+
+ JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if (!dat)
+ return 0; //error
+
+ // check if server connected (at least one field exists)
+ if (dat->nJSInfCount == 0)
+ return 0;
+
+ // formating request
+ BOOL fRequestNotEmpty=FALSE;
+
+ // get server name
+ TCHAR szServerName[100];
+ GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, SIZEOF(szServerName));
+
+ // formating query
+ CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, szServerName);
+ XmlNodeIq iq(pInfo);
+ HXML query = iq << XQUERY(_T("jabber:iq:search"));
+
+ if (m_tszSelectedLang)
+ iq << XATTR(_T("xml:lang"), m_tszSelectedLang); // i'm sure :)
+
+ // next can be 2 cases:
+ // Forms: XEP-0055 Example 7
+ if (dat->fSearchRequestIsXForm) {
+ fRequestNotEmpty=TRUE;
+ HXML n = JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode);
+ xmlAddChild(query, n);
+ xi.destroyNode(n);
+ }
+ else { //and Simple fields: XEP-0055 Example 3
+ for (int i=0; i<dat->nJSInfCount; i++) {
+ TCHAR szFieldValue[100];
+ GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, SIZEOF(szFieldValue));
+ if (szFieldValue[0] != 0) {
+ xmlAddChild(query, dat->pJSInf[i].szFieldName, szFieldValue);
+ fRequestNotEmpty=TRUE;
+ } } }
+
+ if (fRequestNotEmpty) {
+ m_ThreadInfo->send(iq);
+ return (HWND)pInfo->GetIqId();
+ }
+ return 0;
+}
diff --git a/protocols/JabberG/src/jabber_userinfo.cpp b/protocols/JabberG/src/jabber_userinfo.cpp
index 785b6c6a66..cc320b01c0 100644
--- a/protocols/JabberG/src/jabber_userinfo.cpp
+++ b/protocols/JabberG/src/jabber_userinfo.cpp
@@ -1,847 +1,850 @@
-/*
-
-Jabber Protocol Plugin for Miranda NG
-
-Copyright (c) 2002-04 Santithorn Bunchua
-Copyright (c) 2005-12 George Hazan
-Copyright (c) 2007 Maxim Mluhov
-Copyright (c) 2012-14 Miranda NG project
-
-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.
-
-*/
-
-#include "jabber.h"
-
-#include <fcntl.h>
-#include <io.h>
-#include <sys/stat.h>
-
-#include "jabber_list.h"
-
-static HANDLE hUserInfoList = NULL;
-
-struct UserInfoStringBuf
-{
- enum { STRINGBUF_INCREMENT = 1024 };
-
- TCHAR *buf;
- int size;
- int offset;
-
- UserInfoStringBuf() { buf = 0; size = 0; offset = 0; }
- ~UserInfoStringBuf() { mir_free(buf); }
-
- void append(TCHAR *str) {
- if (!str) return;
-
- int length = lstrlen(str);
- if (size - offset < length + 1) {
- size += (length + STRINGBUF_INCREMENT);
- buf = (TCHAR *)mir_realloc(buf, size * sizeof(TCHAR));
- }
- lstrcpy(buf + offset, str);
- offset += length;
- }
-
- TCHAR *allocate(int length) {
- if (size - offset < length) {
- size += (length + STRINGBUF_INCREMENT);
- buf = (TCHAR *)mir_realloc(buf, size * sizeof(TCHAR));
- }
- return buf + offset;
- }
-
- void actualize() {
- if (buf) offset = lstrlen(buf);
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// JabberUserInfoDlgProc - main user info dialog
-
-struct JabberUserInfoDlgData
-{
- MCONTACT hContact;
- CJabberProto *ppro;
- JABBER_LIST_ITEM *item;
- int resourcesCount;
-};
-
-enum
-{
- INFOLINE_DELETE = 0x80000000,
- INFOLINE_MASK = 0x7fffffff,
- INFOLINE_BAD_ID = 0x7fffffff,
-
- INFOLINE_NAME = 1,
- INFOLINE_MOOD,
- INFOLINE_ACTIVITY,
- INFOLINE_TUNE,
- INFOLINE_OFFLINE,
- INFOLINE_MESSAGE,
- INFOLINE_SOFTWARE,
- INFOLINE_VERSION,
- INFOLINE_SYSTEM,
- INFOLINE_PRIORITY,
- INFOLINE_IDLE,
- INFOLINE_CAPS,
- INFOLINE_SOFTWARE_INFORMATION,
- INFOLINE_SUBSCRIPTION,
- INFOLINE_LOGOFF,
- INFOLINE_LOGOFF_MSG,
- INFOLINE_LASTACTIVE,
-};
-
-__forceinline DWORD sttInfoLineId(DWORD res, DWORD type, DWORD line=0)
-{
- return
- (type << 24) & 0x7f000000 |
- (res << 12) & 0x00fff000 |
- (line ) & 0x00000fff;
-}
-
-static HTREEITEM sttFindInfoLine(HWND hwndTree, HTREEITEM htiRoot, LPARAM id=INFOLINE_BAD_ID)
-{
- if (id == INFOLINE_BAD_ID) return NULL;
- for (HTREEITEM hti = TreeView_GetChild(hwndTree, htiRoot); hti; hti = TreeView_GetNextSibling(hwndTree, hti))
- {
- TVITEMEX tvi = {0};
- tvi.mask = TVIF_HANDLE|TVIF_PARAM;
- tvi.hItem = hti;
- TreeView_GetItem(hwndTree, &tvi);
- if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK))
- return hti;
- }
- return NULL;
-}
-
-void sttCleanupInfo(HWND hwndTree, int stage)
-{
- HTREEITEM hItem = TreeView_GetRoot(hwndTree);
- while (hItem) {
- TVITEMEX tvi = {0};
- tvi.mask = TVIF_HANDLE|TVIF_PARAM;
- tvi.hItem = hItem;
- TreeView_GetItem(hwndTree, &tvi);
-
- switch (stage) {
- case 0:
- tvi.lParam |= INFOLINE_DELETE;
- TreeView_SetItem(hwndTree, &tvi);
- break;
-
- case 1:
- if (tvi.lParam & INFOLINE_DELETE) {
- hItem = TreeView_GetNextSibling(hwndTree, hItem);
- TreeView_DeleteItem(hwndTree, tvi.hItem);
- continue;
- }
- break;
- }
-
- HTREEITEM hItemTmp = 0;
- if (hItemTmp = TreeView_GetChild(hwndTree, hItem))
- hItem = hItemTmp;
- else if (hItemTmp = TreeView_GetNextSibling(hwndTree, hItem))
- hItem = hItemTmp;
- else {
- while (1) {
- if (!(hItem = TreeView_GetParent(hwndTree, hItem))) break;
- if (hItemTmp = TreeView_GetNextSibling(hwndTree, hItem)) {
- hItem = hItemTmp;
- break;
- }
- }
- }
- }
-}
-
-static HTREEITEM sttFillInfoLine(HWND hwndTree, HTREEITEM htiRoot, HICON hIcon, TCHAR *title, TCHAR *value, LPARAM id=INFOLINE_BAD_ID, bool expand=false)
-{
- HTREEITEM hti = sttFindInfoLine(hwndTree, htiRoot, id);
-
- TCHAR buf[256];
- if (title)
- mir_sntprintf(buf, SIZEOF(buf), _T("%s: %s"), title, value);
- else
- lstrcpyn(buf, value, SIZEOF(buf));
-
- TVINSERTSTRUCT tvis = {0};
- tvis.hParent = htiRoot;
- tvis.hInsertAfter = TVI_LAST;
- tvis.itemex.mask = TVIF_TEXT|TVIF_PARAM;
- tvis.itemex.pszText = buf;
- tvis.itemex.lParam = id;
-
- if (hIcon) {
- HIMAGELIST himl = TreeView_GetImageList(hwndTree, TVSIL_NORMAL);
- tvis.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
- tvis.itemex.iImage =
- tvis.itemex.iSelectedImage = ImageList_AddIcon(himl, hIcon);
- g_ReleaseIcon(hIcon);
- }
-
- if (hti) {
- tvis.itemex.mask |= TVIF_HANDLE;
- tvis.itemex.hItem = hti;
- TreeView_SetItem(hwndTree, &tvis.itemex);
- }
- else {
- tvis.itemex.mask |= TVIF_STATE;
- tvis.itemex.stateMask = TVIS_EXPANDED;
- tvis.itemex.state = expand ? TVIS_EXPANDED : 0;
- hti = TreeView_InsertItem(hwndTree, &tvis);
- }
-
- return hti;
-}
-
-static void sttFillResourceInfo(CJabberProto *ppro, HWND hwndTree, HTREEITEM htiRoot, JABBER_LIST_ITEM *item, int resource)
-{
- TCHAR buf[256];
- HTREEITEM htiResource = htiRoot;
- pResourceStatus r = resource ? item->arResources[resource-1] : item->getTemp();
-
- if (r->m_tszResourceName && *r->m_tszResourceName)
- htiResource = sttFillInfoLine(hwndTree, htiRoot, LoadSkinnedProtoIcon(ppro->m_szModuleName, r->m_iStatus),
- TranslateT("Resource"), r->m_tszResourceName, sttInfoLineId(resource, INFOLINE_NAME), true);
-
- // StatusMsg
- sttFillInfoLine(hwndTree, htiResource, NULL /*LoadSkinnedIcon(SKINICON_EVENT_MESSAGE)*/,
- TranslateT("Message"), r->m_tszStatusMessage ? r->m_tszStatusMessage : TranslateT("<not specified>"),
- sttInfoLineId(resource, INFOLINE_MESSAGE));
-
- // Software
- HICON hIcon = NULL;
- if ( ServiceExists(MS_FP_GETCLIENTICONT)) {
- if (r->m_tszSoftware != NULL) {
- mir_sntprintf(buf, SIZEOF(buf), _T("%s %s"), r->m_tszSoftware, r->m_tszSoftwareVersion);
- hIcon = Finger_GetClientIcon(buf, 0);
- }
- }
-
- sttFillInfoLine(hwndTree, htiResource, hIcon, TranslateT("Software"),
- r->m_tszSoftware ? r->m_tszSoftware : TranslateT("<not specified>"),
- sttInfoLineId(resource, INFOLINE_SOFTWARE));
-
- if (hIcon)
- DestroyIcon(hIcon);
-
- // Version
- sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("Version"),
- r->m_tszSoftwareVersion ? r->m_tszSoftwareVersion : TranslateT("<not specified>"),
- sttInfoLineId(resource, INFOLINE_VERSION));
-
- // System
- sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("System"),
- r->m_tszOs ? r->m_tszOs : TranslateT("<not specified>"),
- sttInfoLineId(resource, INFOLINE_SYSTEM));
-
- // Resource priority
- TCHAR szPriority[128];
- mir_sntprintf(szPriority, SIZEOF(szPriority), _T("%d"), (int)r->m_iPriority);
- sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("Resource priority"), szPriority, sttInfoLineId(resource, INFOLINE_PRIORITY));
-
- // Idle
- if (r->m_dwIdleStartTime > 0) {
- lstrcpyn(buf, _tctime(&r->m_dwIdleStartTime), SIZEOF(buf));
- int len = lstrlen(buf);
- if (len > 0) buf[len-1] = 0;
- }
- else if (!r->m_dwIdleStartTime)
- lstrcpyn(buf, TranslateT("unknown"), SIZEOF(buf));
- else
- lstrcpyn(buf, TranslateT("<not specified>"), SIZEOF(buf));
-
- sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("Idle since"), buf, sttInfoLineId(resource, INFOLINE_IDLE));
-
- // caps
- mir_sntprintf(buf, SIZEOF(buf), _T("%s/%s"), item->jid, r->m_tszResourceName);
- JabberCapsBits jcb = ppro->GetResourceCapabilites(buf, TRUE);
-
- if (!(jcb & JABBER_RESOURCE_CAPS_ERROR)) {
- HTREEITEM htiCaps = sttFillInfoLine(hwndTree, htiResource, ppro->LoadIconEx("main"), NULL, TranslateT("Client capabilities"), sttInfoLineId(resource, INFOLINE_CAPS));
- int i;
- for (i=0; g_JabberFeatCapPairs[i].szFeature; i++)
- if (jcb & g_JabberFeatCapPairs[i].jcbCap) {
- TCHAR szDescription[ 1024 ];
- if (g_JabberFeatCapPairs[i].tszDescription)
- mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s (%s)"), TranslateTS(g_JabberFeatCapPairs[i].tszDescription), g_JabberFeatCapPairs[i].szFeature);
- else
- mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s"), g_JabberFeatCapPairs[i].szFeature);
- sttFillInfoLine(hwndTree, htiCaps, NULL, NULL, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
- }
-
- for (int j = 0; j < ppro->m_lstJabberFeatCapPairsDynamic.getCount(); j++, i++)
- if (jcb & ppro->m_lstJabberFeatCapPairsDynamic[j]->jcbCap) {
- TCHAR szDescription[ 1024 ];
- if (ppro->m_lstJabberFeatCapPairsDynamic[j]->szDescription)
- mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s (%s)"), TranslateTS(ppro->m_lstJabberFeatCapPairsDynamic[j]->szDescription), ppro->m_lstJabberFeatCapPairsDynamic[j]->szFeature);
- else
- mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s"), ppro->m_lstJabberFeatCapPairsDynamic[j]->szFeature);
- sttFillInfoLine(hwndTree, htiCaps, NULL, NULL, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
- }
- }
-
- // Software info
- HTREEITEM htiSoftwareInfo = sttFillInfoLine(hwndTree, htiResource, ppro->LoadIconEx("main"), NULL, TranslateT("Software information"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION));
- int nLineId = 0;
- if (r->m_tszOs)
- sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Operating system"), r->m_tszOs, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
- if (r->m_tszOsVersion)
- sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Operating system version"), r->m_tszOsVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
- if (r->m_tszSoftware)
- sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Software"), r->m_tszSoftware, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
- if (r->m_tszSoftwareVersion)
- sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Software version"), r->m_tszSoftwareVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
- if (r->m_tszXMirandaCoreVersion)
- sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Miranda core version"), r->m_tszXMirandaCoreVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
-}
-
-static void sttFillAdvStatusInfo(CJabberProto *ppro, HWND hwndTree, HTREEITEM htiRoot, DWORD dwInfoLine, MCONTACT hContact, TCHAR *szTitle, char *pszSlot)
-{
- char *szAdvStatusIcon = ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON);
- TCHAR *szAdvStatusTitle = ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE);
- TCHAR *szAdvStatusText = ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT);
-
- if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) {
- TCHAR szText[2048];
- if (szAdvStatusText && *szAdvStatusText)
- mir_sntprintf(szText, 2047, _T("%s (%s)"), TranslateTS(szAdvStatusTitle), szAdvStatusText);
- else
- mir_sntprintf(szText, 2047, _T("%s"), TranslateTS(szAdvStatusTitle));
- sttFillInfoLine(hwndTree, htiRoot, Skin_GetIcon(szAdvStatusIcon), szTitle, szText, dwInfoLine);
- }
-
- mir_free(szAdvStatusIcon);
- mir_free(szAdvStatusTitle);
- mir_free(szAdvStatusText);
-}
-
-static void sttFillUserInfo(CJabberProto *ppro, HWND hwndTree, JABBER_LIST_ITEM *item)
-{
- SendMessage(hwndTree, WM_SETREDRAW, FALSE, 0);
-
- sttCleanupInfo(hwndTree, 0);
-
- HTREEITEM htiRoot = sttFillInfoLine(hwndTree, NULL, ppro->LoadIconEx("main"), _T("JID"), item->jid, sttInfoLineId(0, INFOLINE_NAME), true);
- TCHAR buf[256];
-
- if (MCONTACT hContact = ppro->HContactFromJID(item->jid)) {
- sttFillAdvStatusInfo(ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD);
- sttFillAdvStatusInfo(ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY);
- sttFillAdvStatusInfo(ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE);
- }
-
- // subscription
- switch (item->subscription) {
- case SUB_BOTH:
- sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("both"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
- break;
- case SUB_TO:
- sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("to"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
- break;
- case SUB_FROM:
- sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("from"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
- break;
- default:
- sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("none"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
- break;
- }
-
- // logoff
- JABBER_RESOURCE_STATUS *r = item->getTemp();
- if (r->m_dwIdleStartTime > 0) {
- lstrcpyn(buf, _tctime(&r->m_dwIdleStartTime), SIZEOF(buf));
- int len = lstrlen(buf);
- if (len > 0) buf[len-1] = 0;
- }
- else if (!r->m_dwIdleStartTime)
- lstrcpyn(buf, TranslateT("unknown"), SIZEOF(buf));
- else
- lstrcpyn(buf, TranslateT("<not specified>"), SIZEOF(buf));
-
- sttFillInfoLine(hwndTree, htiRoot, NULL,
- (item->jid && _tcschr(item->jid, _T('@'))) ? TranslateT("Last logoff time") : TranslateT("Uptime"), buf,
- sttInfoLineId(0, INFOLINE_LOGOFF));
-
- sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Logoff message"),
- r->m_tszStatusMessage ? r->m_tszStatusMessage : TranslateT("<not specified>"), sttInfoLineId(0, INFOLINE_LOGOFF_MSG));
-
- // activity
- if (item->m_pLastSeenResource)
- lstrcpyn(buf, item->m_pLastSeenResource->m_tszResourceName, SIZEOF(buf));
- else
- lstrcpyn(buf, TranslateT("<no information available>"), SIZEOF(buf));
-
- sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Last active resource"), buf,
- sttInfoLineId(0, INFOLINE_LASTACTIVE));
-
- // resources
- if (item->arResources.getCount()) {
- for (int i=0; i < item->arResources.getCount(); i++)
- sttFillResourceInfo(ppro, hwndTree, htiRoot, item, i+1);
- }
- else if (!_tcschr(item->jid, _T('@')) || (r->m_iStatus != ID_STATUS_OFFLINE))
- sttFillResourceInfo(ppro, hwndTree, htiRoot, item, 0);
-
- sttCleanupInfo(hwndTree, 1);
- SendMessage(hwndTree, WM_SETREDRAW, TRUE, 0);
-
- RedrawWindow(hwndTree, NULL, NULL, RDW_INVALIDATE);
-}
-
-static void sttGetNodeText(HWND hwndTree, HTREEITEM hti, UserInfoStringBuf *buf, int indent = 0)
-{
- for (int i=0; i < indent; i++)
- buf->append(_T("\t"));
-
- TVITEMEX tvi = {0};
- tvi.mask = TVIF_HANDLE|TVIF_TEXT|TVIF_STATE;
- tvi.hItem = hti;
- tvi.cchTextMax = 256;
- tvi.pszText = buf->allocate(tvi.cchTextMax);
- if (!TreeView_GetItem(hwndTree, &tvi)) { // failure, maybe item was removed...
- buf->buf[ buf->offset ] = 0;
- buf->actualize();
- return;
- }
-
- buf->actualize();
- buf->append(_T("\r\n"));
-
- if (tvi.state & TVIS_EXPANDED)
- for (hti = TreeView_GetChild(hwndTree, hti); hti; hti = TreeView_GetNextSibling(hwndTree, hti))
- sttGetNodeText(hwndTree, hti, buf, indent + 1);
-}
-
-static INT_PTR CALLBACK JabberUserInfoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- JabberUserInfoDlgData *dat = (JabberUserInfoDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-
- switch (msg) {
- case WM_INITDIALOG:
- // lParam is hContact
- TranslateDialogDefault(hwndDlg);
-
- SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_OTHER_USERDETAILS));
- SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_USERDETAILS));
-
- dat = (JabberUserInfoDlgData *)mir_alloc(sizeof(JabberUserInfoDlgData));
- ZeroMemory(dat, sizeof(JabberUserInfoDlgData));
- dat->resourcesCount = -1;
-
- if (CallService(MS_DB_CONTACT_IS, (WPARAM)lParam, 0))
- dat->hContact = lParam;
- else if (!IsBadReadPtr((void*)lParam, sizeof(JABBER_LIST_ITEM))) {
- dat->hContact = NULL;
- dat->item = (JABBER_LIST_ITEM *)lParam;
- }
-
- {
- RECT rc; GetClientRect(hwndDlg, &rc);
- MoveWindow(GetDlgItem(hwndDlg, IDC_TV_INFO), 5, 5, rc.right-10, rc.bottom-10, TRUE);
-
- HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR|ILC_COLOR32|ILC_MASK, 5, 1);
- ImageList_AddIcon_Icolib(himl, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT));
- TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TV_INFO), himl, TVSIL_NORMAL);
-
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
- WindowList_Add(hUserInfoList, hwndDlg, dat->hContact);
- }
- break;
-
- case WM_JABBER_REFRESH:
- if (!dat) break;
-
- if (!dat->item) {
- ptrT jid( dat->ppro->getTStringA(dat->hContact, "jid"));
- if (jid == NULL)
- break;
-
- if (!(dat->item = dat->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid)))
- dat->item = dat->ppro->ListGetItemPtr(LIST_ROSTER, jid);
-
- if (!dat->item) {
- HWND hwndTree = GetDlgItem(hwndDlg, IDC_TV_INFO);
- TreeView_DeleteAllItems(hwndTree);
- HTREEITEM htiRoot = sttFillInfoLine(hwndTree, NULL, dat->ppro->LoadIconEx("main"), _T("JID"), jid, sttInfoLineId(0, INFOLINE_NAME), true);
- sttFillInfoLine(hwndTree, htiRoot, dat->ppro->LoadIconEx("vcard"), NULL,
- TranslateT("Please switch online to see more details."));
- break;
- }
- }
- sttFillUserInfo(dat->ppro, GetDlgItem(hwndDlg, IDC_TV_INFO), dat->item);
- break;
-
- case WM_SIZE:
- MoveWindow(GetDlgItem(hwndDlg, IDC_TV_INFO), 5, 5, LOWORD(lParam)-10, HIWORD(lParam)-10, TRUE);
- break;
-
- case WM_CONTEXTMENU:
- if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_TV_INFO) {
- HWND hwndTree = GetDlgItem(hwndDlg, IDC_TV_INFO);
- POINT pt = { (signed short)LOWORD(lParam), (signed short)HIWORD(lParam) };
- HTREEITEM hItem = 0;
-
- if ((pt.x == -1) && (pt.y == -1)) {
- if (hItem = TreeView_GetSelection(hwndTree)) {
- RECT rc;
- TreeView_GetItemRect(hwndTree, hItem, &rc, TRUE);
- pt.x = rc.left;
- pt.y = rc.bottom;
- ClientToScreen(hwndTree, &pt);
- }
- }
- else {
- TVHITTESTINFO tvhti = {0};
- tvhti.pt = pt;
- ScreenToClient(hwndTree, &tvhti.pt);
- TreeView_HitTest(hwndTree, &tvhti);
- if (tvhti.flags & TVHT_ONITEM) {
- hItem = tvhti.hItem;
- TreeView_Select(hwndTree, hItem, TVGN_CARET);
- } }
-
- if (hItem) {
- HMENU hMenu = CreatePopupMenu();
- AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy"));
- AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value"));
- AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
- AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel"));
- int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
- if (nReturnCmd == 1) {
- UserInfoStringBuf buf;
- sttGetNodeText(hwndTree, hItem, &buf);
- JabberCopyText(hwndDlg, buf.buf);
- }
- else if (nReturnCmd == 2) {
- TCHAR szBuffer[ 1024 ];
- TVITEMEX tvi = {0};
- tvi.mask = TVIF_HANDLE|TVIF_TEXT|TVIF_STATE;
- tvi.hItem = hItem;
- tvi.cchTextMax = SIZEOF(szBuffer);
- tvi.pszText = szBuffer;
- if (TreeView_GetItem(hwndTree, &tvi)) {
- if (TCHAR *str = _tcsstr(szBuffer, _T(": ")))
- JabberCopyText(hwndDlg, str+2);
- else
- JabberCopyText(hwndDlg, szBuffer);
- } }
- DestroyMenu(hMenu);
- } }
- break;
-
- case WM_NOTIFY:
- if (((LPNMHDR)lParam)->idFrom == 0) {
- switch (((LPNMHDR)lParam)->code) {
- case PSN_INFOCHANGED:
- {
- MCONTACT hContact = (MCONTACT)((LPPSHNOTIFY)lParam)->lParam;
- SendMessage(hwndDlg, WM_JABBER_REFRESH, 0, hContact);
- }
- break;
-
- case PSN_PARAMCHANGED:
- dat->ppro = (CJabberProto*)((PSHNOTIFY*)lParam)->lParam;
- if (dat->hContact != NULL) {
- ptrT jid( dat->ppro->getTStringA(dat->hContact, "jid"));
- if (jid != NULL)
- if (!(dat->item = dat->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid)))
- dat->item = dat->ppro->ListGetItemPtr(LIST_ROSTER, jid);
- }
- break;
- } }
- break;
-
- case WM_CLOSE:
- DestroyWindow(hwndDlg);
- break;
-
- case WM_DESTROY:
- WindowList_Remove(hUserInfoList, hwndDlg);
- if (dat) {
- mir_free(dat);
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- }
- ImageList_Destroy(TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TV_INFO), NULL, TVSIL_NORMAL));
- WindowFreeIcon(hwndDlg);
- break;
- }
- return FALSE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// JabberUserPhotoDlgProc - Jabber photo dialog
-
-struct USER_PHOTO_INFO
-{
- MCONTACT hContact;
- HBITMAP hBitmap;
- CJabberProto *ppro;
-};
-
-static INT_PTR CALLBACK JabberUserPhotoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- USER_PHOTO_INFO *photoInfo;
-
- photoInfo = (USER_PHOTO_INFO *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-
- switch (msg) {
- case WM_INITDIALOG:
- // lParam is hContact
- TranslateDialogDefault(hwndDlg);
- photoInfo = (USER_PHOTO_INFO *) mir_alloc(sizeof(USER_PHOTO_INFO));
- photoInfo->hContact = lParam;
- photoInfo->ppro = NULL;
- photoInfo->hBitmap = NULL;
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) photoInfo);
- SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadImage(hInst, MAKEINTRESOURCE(IDI_SAVE), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0));
- SendDlgItemMessage(hwndDlg, IDC_SAVE, BUTTONSETASFLATBTN, TRUE, 0);
- ShowWindow(GetDlgItem(hwndDlg, IDC_LOAD), SW_HIDE);
- ShowWindow(GetDlgItem(hwndDlg, IDC_DELETE), SW_HIDE);
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case 0:
- switch (((LPNMHDR)lParam)->code) {
- case PSN_INFOCHANGED:
- SendMessage(hwndDlg, WM_JABBER_REFRESH, 0, 0);
- break;
-
- case PSN_PARAMCHANGED:
- photoInfo->ppro = (CJabberProto*)((PSHNOTIFY*)lParam)->lParam;
- break;
- }
- break;
- }
- break;
-
- case WM_JABBER_REFRESH:
- {
- if (photoInfo->hBitmap) {
- DeleteObject(photoInfo->hBitmap);
- photoInfo->hBitmap = NULL;
- }
- ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_HIDE);
- ptrT jid( photoInfo->ppro->getTStringA(photoInfo->hContact, "jid"));
- if (jid != NULL) {
- JABBER_LIST_ITEM *item = photoInfo->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid);
- if (item == NULL)
- item = photoInfo->ppro->ListGetItemPtr(LIST_ROSTER, jid);
- if (item != NULL) {
- if (item->photoFileName) {
- photoInfo->ppro->debugLog(_T("Showing picture from %s"), item->photoFileName);
- photoInfo->hBitmap = (HBITMAP) CallService(MS_UTILS_LOADBITMAPT, 0, (LPARAM)item->photoFileName);
- FIP->FI_Premultiply(photoInfo->hBitmap);
- ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_SHOW);
- }
- }
- }
- InvalidateRect(hwndDlg, NULL, TRUE);
- UpdateWindow(hwndDlg);
- }
- break;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDC_SAVE:
- static TCHAR szFilter[512];
-
- ptrT jid(photoInfo->ppro->getTStringA(photoInfo->hContact, "jid"));
- if (jid == NULL)
- break;
-
- JABBER_LIST_ITEM *item = photoInfo->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid);
- if (item == NULL)
- if ((item = photoInfo->ppro->ListGetItemPtr(LIST_ROSTER, jid)) == NULL)
- break;
-
- switch (ProtoGetAvatarFileFormat(item->photoFileName)) {
- case PA_FORMAT_BMP:
- mir_sntprintf(szFilter, SIZEOF(szFilter), _T("BMP %s (*.bmp)%c*.BMP"), TranslateT("format"), 0);
- break;
-
- case PA_FORMAT_GIF:
- mir_sntprintf(szFilter, SIZEOF(szFilter), _T("GIF %s (*.gif)%c*.GIF"), TranslateT("format"), 0);
- break;
-
- case PA_FORMAT_JPEG:
- mir_sntprintf(szFilter, SIZEOF(szFilter), _T("JPEG %s (*.jpg;*.jpeg)%c*.JPG;*.JPEG"), TranslateT("format"), 0);
- break;
-
- default:
- mir_sntprintf(szFilter, SIZEOF(szFilter), _T("%s (*.*)%c*.*"), TranslateT("Unknown format"), 0);
- }
-
- TCHAR szFileName[MAX_PATH]; szFileName[0] = '\0';
- OPENFILENAME ofn = { 0 };
- ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
- ofn.hwndOwner = hwndDlg;
- ofn.lpstrFilter = szFilter;
- ofn.lpstrFile = szFileName;
- ofn.nMaxFile = _MAX_PATH;
- ofn.Flags = OFN_OVERWRITEPROMPT;
- if ( GetSaveFileName(&ofn)) {
- photoInfo->ppro->debugLog(_T("File selected is %s"), szFileName);
- CopyFile(item->photoFileName, szFileName, FALSE);
- }
- }
- break;
-
- case WM_PAINT:
- if (!photoInfo->ppro->m_bJabberOnline)
- SetDlgItemText(hwndDlg, IDC_CANVAS, TranslateT("<Photo not available while offline>"));
- else if (!photoInfo->hBitmap)
- SetDlgItemText(hwndDlg, IDC_CANVAS, TranslateT("<No photo>"));
- else {
- BITMAP bm;
- POINT ptSize, ptOrg, pt, ptFitSize;
- RECT rect;
-
- SetDlgItemTextA(hwndDlg, IDC_CANVAS, "");
- HBITMAP hBitmap = photoInfo->hBitmap;
- HWND hwndCanvas = GetDlgItem(hwndDlg, IDC_CANVAS);
- HDC hdcCanvas = GetDC(hwndCanvas);
- HDC hdcMem = CreateCompatibleDC(hdcCanvas);
- SelectObject(hdcMem, hBitmap);
- SetMapMode(hdcMem, GetMapMode(hdcCanvas));
- GetObject(hBitmap, sizeof(BITMAP), (LPVOID) &bm);
- ptSize.x = bm.bmWidth;
- ptSize.y = bm.bmHeight;
- DPtoLP(hdcCanvas, &ptSize, 1);
- ptOrg.x = ptOrg.y = 0;
- DPtoLP(hdcMem, &ptOrg, 1);
- GetClientRect(hwndCanvas, &rect);
- InvalidateRect(hwndCanvas, NULL, TRUE);
- UpdateWindow(hwndCanvas);
- if (ptSize.x<=rect.right && ptSize.y<=rect.bottom) {
- pt.x = (rect.right - ptSize.x)/2;
- pt.y = (rect.bottom - ptSize.y)/2;
- ptFitSize = ptSize;
- }
- else {
- if (((float)(ptSize.x-rect.right))/ptSize.x > ((float)(ptSize.y-rect.bottom))/ptSize.y) {
- ptFitSize.x = rect.right;
- ptFitSize.y = (ptSize.y*rect.right)/ptSize.x;
- pt.x = 0;
- pt.y = (rect.bottom - ptFitSize.y)/2;
- }
- else {
- ptFitSize.x = (ptSize.x*rect.bottom)/ptSize.y;
- ptFitSize.y = rect.bottom;
- pt.x = (rect.right - ptFitSize.x)/2;
- pt.y = 0;
- }
- }
-
- if (IsThemeActive()) {
- RECT rc; GetClientRect(hwndCanvas, &rc);
- DrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc);
- }
- else {
- RECT rc; GetClientRect(hwndCanvas, &rc);
- FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE));
- }
-
- if (bm.bmBitsPixel == 32) {
- BLENDFUNCTION bf = {0};
- bf.AlphaFormat = AC_SRC_ALPHA;
- bf.BlendOp = AC_SRC_OVER;
- bf.SourceConstantAlpha = 255;
- GdiAlphaBlend(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf);
- }
- else {
- SetStretchBltMode(hdcCanvas, COLORONCOLOR);
- StretchBlt(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY);
- }
-
- DeleteDC(hdcMem);
- }
- break;
-
- case WM_DESTROY:
- DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, 0));
- if (photoInfo->hBitmap) {
- photoInfo->ppro->debugLogA("Delete bitmap");
- DeleteObject(photoInfo->hBitmap);
- }
- if (photoInfo) mir_free(photoInfo);
- break;
- }
- return FALSE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// OnInfoInit - initializes user info option dialogs
-
-int CJabberProto::OnUserInfoInit(WPARAM wParam, LPARAM lParam)
-{
- if (!CallService(MS_PROTO_ISPROTOCOLLOADED, 0, (LPARAM)m_szModuleName))
- return 0;
-
- MCONTACT hContact = lParam;
- if (hContact == NULL) {
- // Show our vcard
- OnUserInfoInit_VCard(wParam, lParam);
- return 0;
- }
-
- char *szProto = GetContactProto(hContact);
- if (szProto != NULL && !strcmp(szProto, m_szModuleName)) {
- OPTIONSDIALOGPAGE odp = { sizeof(odp) };
- odp.hInstance = hInst;
- odp.dwInitParam = (LPARAM)this;
-
- odp.pfnDlgProc = JabberUserInfoDlgProc;
- odp.position = -2000000000;
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_JABBER);
- odp.pszTitle = LPGEN("Account");
- UserInfo_AddPage(wParam, &odp);
-
- odp.pfnDlgProc = JabberUserPhotoDlgProc;
- odp.position = 2000000000;
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_PHOTO);
- odp.pszTitle = LPGEN("Photo");
- UserInfo_AddPage(wParam, &odp);
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// JabberUserInfoUpdate
-
-void JabberUserInfoInit()
-{
- hUserInfoList = WindowList_Create();
-}
-
-void JabberUserInfoUninit()
-{
- WindowList_Destroy(hUserInfoList);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// JabberUserInfoUpdate
-
-void JabberUserInfoUpdate(MCONTACT hContact)
-{
- if (!hContact)
- WindowList_BroadcastAsync(hUserInfoList, WM_JABBER_REFRESH, 0, 0);
- else if (HWND hwnd = WindowList_Find(hUserInfoList, hContact))
- PostMessage(hwnd, WM_JABBER_REFRESH, 0, 0);
-}
+/*
+
+Jabber Protocol Plugin for Miranda NG
+
+Copyright (c) 2002-04 Santithorn Bunchua
+Copyright (c) 2005-12 George Hazan
+Copyright (c) 2007 Maxim Mluhov
+Copyright (c) 2012-14 Miranda NG project
+
+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.
+
+*/
+
+#include "jabber.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+
+#include "jabber_list.h"
+
+static HANDLE hUserInfoList = NULL;
+
+struct UserInfoStringBuf
+{
+ enum { STRINGBUF_INCREMENT = 1024 };
+
+ TCHAR *buf;
+ int size;
+ int offset;
+
+ UserInfoStringBuf() { buf = 0; size = 0; offset = 0; }
+ ~UserInfoStringBuf() { mir_free(buf); }
+
+ void append(TCHAR *str) {
+ if (!str) return;
+
+ int length = lstrlen(str);
+ if (size - offset < length + 1) {
+ size += (length + STRINGBUF_INCREMENT);
+ buf = (TCHAR *)mir_realloc(buf, size * sizeof(TCHAR));
+ }
+ lstrcpy(buf + offset, str);
+ offset += length;
+ }
+
+ TCHAR *allocate(int length) {
+ if (size - offset < length) {
+ size += (length + STRINGBUF_INCREMENT);
+ buf = (TCHAR *)mir_realloc(buf, size * sizeof(TCHAR));
+ }
+ return buf + offset;
+ }
+
+ void actualize() {
+ if (buf) offset = lstrlen(buf);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoDlgProc - main user info dialog
+
+struct JabberUserInfoDlgData
+{
+ MCONTACT hContact;
+ CJabberProto *ppro;
+ JABBER_LIST_ITEM *item;
+ int resourcesCount;
+};
+
+enum
+{
+ INFOLINE_DELETE = 0x80000000,
+ INFOLINE_MASK = 0x7fffffff,
+ INFOLINE_BAD_ID = 0x7fffffff,
+
+ INFOLINE_NAME = 1,
+ INFOLINE_MOOD,
+ INFOLINE_ACTIVITY,
+ INFOLINE_TUNE,
+ INFOLINE_OFFLINE,
+ INFOLINE_MESSAGE,
+ INFOLINE_SOFTWARE,
+ INFOLINE_VERSION,
+ INFOLINE_SYSTEM,
+ INFOLINE_PRIORITY,
+ INFOLINE_IDLE,
+ INFOLINE_CAPS,
+ INFOLINE_SOFTWARE_INFORMATION,
+ INFOLINE_SUBSCRIPTION,
+ INFOLINE_LOGOFF,
+ INFOLINE_LOGOFF_MSG,
+ INFOLINE_LASTACTIVE,
+};
+
+__forceinline DWORD sttInfoLineId(DWORD res, DWORD type, DWORD line=0)
+{
+ return
+ (type << 24) & 0x7f000000 |
+ (res << 12) & 0x00fff000 |
+ (line ) & 0x00000fff;
+}
+
+static HTREEITEM sttFindInfoLine(HWND hwndTree, HTREEITEM htiRoot, LPARAM id=INFOLINE_BAD_ID)
+{
+ if (id == INFOLINE_BAD_ID) return NULL;
+ for (HTREEITEM hti = TreeView_GetChild(hwndTree, htiRoot); hti; hti = TreeView_GetNextSibling(hwndTree, hti))
+ {
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem = hti;
+ TreeView_GetItem(hwndTree, &tvi);
+ if ((tvi.lParam&INFOLINE_MASK) == (id&INFOLINE_MASK))
+ return hti;
+ }
+ return NULL;
+}
+
+void sttCleanupInfo(HWND hwndTree, int stage)
+{
+ HTREEITEM hItem = TreeView_GetRoot(hwndTree);
+ while (hItem) {
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_PARAM;
+ tvi.hItem = hItem;
+ TreeView_GetItem(hwndTree, &tvi);
+
+ switch (stage) {
+ case 0:
+ tvi.lParam |= INFOLINE_DELETE;
+ TreeView_SetItem(hwndTree, &tvi);
+ break;
+
+ case 1:
+ if (tvi.lParam & INFOLINE_DELETE) {
+ hItem = TreeView_GetNextSibling(hwndTree, hItem);
+ TreeView_DeleteItem(hwndTree, tvi.hItem);
+ continue;
+ }
+ break;
+ }
+
+ HTREEITEM hItemTmp = 0;
+ if (hItemTmp = TreeView_GetChild(hwndTree, hItem))
+ hItem = hItemTmp;
+ else if (hItemTmp = TreeView_GetNextSibling(hwndTree, hItem))
+ hItem = hItemTmp;
+ else {
+ while (1) {
+ if (!(hItem = TreeView_GetParent(hwndTree, hItem))) break;
+ if (hItemTmp = TreeView_GetNextSibling(hwndTree, hItem)) {
+ hItem = hItemTmp;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static HTREEITEM sttFillInfoLine(HWND hwndTree, HTREEITEM htiRoot, HICON hIcon, TCHAR *title, TCHAR *value, LPARAM id=INFOLINE_BAD_ID, bool expand=false)
+{
+ HTREEITEM hti = sttFindInfoLine(hwndTree, htiRoot, id);
+
+ TCHAR buf[256];
+ if (title)
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s: %s"), title, value);
+ else
+ lstrcpyn(buf, value, SIZEOF(buf));
+
+ TVINSERTSTRUCT tvis = {0};
+ tvis.hParent = htiRoot;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.itemex.mask = TVIF_TEXT|TVIF_PARAM;
+ tvis.itemex.pszText = buf;
+ tvis.itemex.lParam = id;
+
+ if (hIcon) {
+ HIMAGELIST himl = TreeView_GetImageList(hwndTree, TVSIL_NORMAL);
+ tvis.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
+ tvis.itemex.iImage =
+ tvis.itemex.iSelectedImage = ImageList_AddIcon(himl, hIcon);
+ g_ReleaseIcon(hIcon);
+ }
+
+ if (hti) {
+ tvis.itemex.mask |= TVIF_HANDLE;
+ tvis.itemex.hItem = hti;
+ TreeView_SetItem(hwndTree, &tvis.itemex);
+ }
+ else {
+ tvis.itemex.mask |= TVIF_STATE;
+ tvis.itemex.stateMask = TVIS_EXPANDED;
+ tvis.itemex.state = expand ? TVIS_EXPANDED : 0;
+ hti = TreeView_InsertItem(hwndTree, &tvis);
+ }
+
+ return hti;
+}
+
+static void sttFillResourceInfo(CJabberProto *ppro, HWND hwndTree, HTREEITEM htiRoot, JABBER_LIST_ITEM *item, int resource)
+{
+ TCHAR buf[256];
+ HTREEITEM htiResource = htiRoot;
+ pResourceStatus r = resource ? item->arResources[resource-1] : item->getTemp();
+
+ if (r->m_tszResourceName && *r->m_tszResourceName)
+ htiResource = sttFillInfoLine(hwndTree, htiRoot, LoadSkinnedProtoIcon(ppro->m_szModuleName, r->m_iStatus),
+ TranslateT("Resource"), r->m_tszResourceName, sttInfoLineId(resource, INFOLINE_NAME), true);
+
+ // StatusMsg
+ sttFillInfoLine(hwndTree, htiResource, NULL /*LoadSkinnedIcon(SKINICON_EVENT_MESSAGE)*/,
+ TranslateT("Message"), r->m_tszStatusMessage ? r->m_tszStatusMessage : TranslateT("<not specified>"),
+ sttInfoLineId(resource, INFOLINE_MESSAGE));
+
+ // Software
+ HICON hIcon = NULL;
+ if ( ServiceExists(MS_FP_GETCLIENTICONT)) {
+ if (r->m_tszSoftware != NULL) {
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s %s"), r->m_tszSoftware, r->m_tszSoftwareVersion);
+ hIcon = Finger_GetClientIcon(buf, 0);
+ }
+ }
+
+ sttFillInfoLine(hwndTree, htiResource, hIcon, TranslateT("Software"),
+ r->m_tszSoftware ? r->m_tszSoftware : TranslateT("<not specified>"),
+ sttInfoLineId(resource, INFOLINE_SOFTWARE));
+
+ if (hIcon)
+ DestroyIcon(hIcon);
+
+ // Version
+ sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("Version"),
+ r->m_tszSoftwareVersion ? r->m_tszSoftwareVersion : TranslateT("<not specified>"),
+ sttInfoLineId(resource, INFOLINE_VERSION));
+
+ // System
+ sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("System"),
+ r->m_tszOs ? r->m_tszOs : TranslateT("<not specified>"),
+ sttInfoLineId(resource, INFOLINE_SYSTEM));
+
+ // Resource priority
+ TCHAR szPriority[128];
+ mir_sntprintf(szPriority, SIZEOF(szPriority), _T("%d"), (int)r->m_iPriority);
+ sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("Resource priority"), szPriority, sttInfoLineId(resource, INFOLINE_PRIORITY));
+
+ // Idle
+ if (r->m_dwIdleStartTime > 0) {
+ lstrcpyn(buf, _tctime(&r->m_dwIdleStartTime), SIZEOF(buf));
+ int len = lstrlen(buf);
+ if (len > 0) buf[len-1] = 0;
+ }
+ else if (!r->m_dwIdleStartTime)
+ lstrcpyn(buf, TranslateT("unknown"), SIZEOF(buf));
+ else
+ lstrcpyn(buf, TranslateT("<not specified>"), SIZEOF(buf));
+
+ sttFillInfoLine(hwndTree, htiResource, NULL, TranslateT("Idle since"), buf, sttInfoLineId(resource, INFOLINE_IDLE));
+
+ // caps
+ mir_sntprintf(buf, SIZEOF(buf), _T("%s/%s"), item->jid, r->m_tszResourceName);
+ JabberCapsBits jcb = ppro->GetResourceCapabilites(buf, TRUE);
+
+ if (!(jcb & JABBER_RESOURCE_CAPS_ERROR)) {
+ HTREEITEM htiCaps = sttFillInfoLine(hwndTree, htiResource, ppro->LoadIconEx("main"), NULL, TranslateT("Client capabilities"), sttInfoLineId(resource, INFOLINE_CAPS));
+ int i;
+ for (i=0; g_JabberFeatCapPairs[i].szFeature; i++)
+ if (jcb & g_JabberFeatCapPairs[i].jcbCap) {
+ TCHAR szDescription[ 1024 ];
+ if (g_JabberFeatCapPairs[i].tszDescription)
+ mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s (%s)"), TranslateTS(g_JabberFeatCapPairs[i].tszDescription), g_JabberFeatCapPairs[i].szFeature);
+ else
+ mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s"), g_JabberFeatCapPairs[i].szFeature);
+ sttFillInfoLine(hwndTree, htiCaps, NULL, NULL, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
+ }
+
+ for (int j = 0; j < ppro->m_lstJabberFeatCapPairsDynamic.getCount(); j++, i++)
+ if (jcb & ppro->m_lstJabberFeatCapPairsDynamic[j]->jcbCap) {
+ TCHAR szDescription[ 1024 ];
+ if (ppro->m_lstJabberFeatCapPairsDynamic[j]->szDescription)
+ mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s (%s)"), TranslateTS(ppro->m_lstJabberFeatCapPairsDynamic[j]->szDescription), ppro->m_lstJabberFeatCapPairsDynamic[j]->szFeature);
+ else
+ mir_sntprintf(szDescription, SIZEOF(szDescription), _T("%s"), ppro->m_lstJabberFeatCapPairsDynamic[j]->szFeature);
+ sttFillInfoLine(hwndTree, htiCaps, NULL, NULL, szDescription, sttInfoLineId(resource, INFOLINE_CAPS, i));
+ }
+ }
+
+ // Software info
+ HTREEITEM htiSoftwareInfo = sttFillInfoLine(hwndTree, htiResource, ppro->LoadIconEx("main"), NULL, TranslateT("Software information"), sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION));
+ int nLineId = 0;
+ if (r->m_tszOs)
+ sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Operating system"), r->m_tszOs, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (r->m_tszOsVersion)
+ sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Operating system version"), r->m_tszOsVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (r->m_tszSoftware)
+ sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Software"), r->m_tszSoftware, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (r->m_tszSoftwareVersion)
+ sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Software version"), r->m_tszSoftwareVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+ if (r->m_tszXMirandaCoreVersion)
+ sttFillInfoLine(hwndTree, htiSoftwareInfo, NULL, TranslateT("Miranda core version"), r->m_tszXMirandaCoreVersion, sttInfoLineId(resource, INFOLINE_SOFTWARE_INFORMATION, nLineId++));
+}
+
+static void sttFillAdvStatusInfo(CJabberProto *ppro, HWND hwndTree, HTREEITEM htiRoot, DWORD dwInfoLine, MCONTACT hContact, TCHAR *szTitle, char *pszSlot)
+{
+ char *szAdvStatusIcon = ppro->ReadAdvStatusA(hContact, pszSlot, ADVSTATUS_VAL_ICON);
+ TCHAR *szAdvStatusTitle = ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TITLE);
+ TCHAR *szAdvStatusText = ppro->ReadAdvStatusT(hContact, pszSlot, ADVSTATUS_VAL_TEXT);
+
+ if (szAdvStatusIcon && szAdvStatusTitle && *szAdvStatusTitle) {
+ TCHAR szText[2048];
+ if (szAdvStatusText && *szAdvStatusText)
+ mir_sntprintf(szText, 2047, _T("%s (%s)"), TranslateTS(szAdvStatusTitle), szAdvStatusText);
+ else
+ mir_sntprintf(szText, 2047, _T("%s"), TranslateTS(szAdvStatusTitle));
+ sttFillInfoLine(hwndTree, htiRoot, Skin_GetIcon(szAdvStatusIcon), szTitle, szText, dwInfoLine);
+ }
+
+ mir_free(szAdvStatusIcon);
+ mir_free(szAdvStatusTitle);
+ mir_free(szAdvStatusText);
+}
+
+static void sttFillUserInfo(CJabberProto *ppro, HWND hwndTree, JABBER_LIST_ITEM *item)
+{
+ SendMessage(hwndTree, WM_SETREDRAW, FALSE, 0);
+
+ sttCleanupInfo(hwndTree, 0);
+
+ HTREEITEM htiRoot = sttFillInfoLine(hwndTree, NULL, ppro->LoadIconEx("main"), _T("JID"), item->jid, sttInfoLineId(0, INFOLINE_NAME), true);
+ TCHAR buf[256];
+
+ if (MCONTACT hContact = ppro->HContactFromJID(item->jid)) {
+ sttFillAdvStatusInfo(ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_MOOD), hContact, TranslateT("Mood"), ADVSTATUS_MOOD);
+ sttFillAdvStatusInfo(ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_ACTIVITY), hContact, TranslateT("Activity"), ADVSTATUS_ACTIVITY);
+ sttFillAdvStatusInfo(ppro, hwndTree, htiRoot, sttInfoLineId(0, INFOLINE_TUNE), hContact, TranslateT("Tune"), ADVSTATUS_TUNE);
+ }
+
+ // subscription
+ switch (item->subscription) {
+ case SUB_BOTH:
+ sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("both"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ case SUB_TO:
+ sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("to"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ case SUB_FROM:
+ sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("from"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ default:
+ sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Subscription"), TranslateT("none"), sttInfoLineId(0, INFOLINE_SUBSCRIPTION));
+ break;
+ }
+
+ // logoff
+ JABBER_RESOURCE_STATUS *r = item->getTemp();
+ if (r->m_dwIdleStartTime > 0) {
+ lstrcpyn(buf, _tctime(&r->m_dwIdleStartTime), SIZEOF(buf));
+ int len = lstrlen(buf);
+ if (len > 0) buf[len-1] = 0;
+ }
+ else if (!r->m_dwIdleStartTime)
+ lstrcpyn(buf, TranslateT("unknown"), SIZEOF(buf));
+ else
+ lstrcpyn(buf, TranslateT("<not specified>"), SIZEOF(buf));
+
+ sttFillInfoLine(hwndTree, htiRoot, NULL,
+ (item->jid && _tcschr(item->jid, _T('@'))) ? TranslateT("Last logoff time") : TranslateT("Uptime"), buf,
+ sttInfoLineId(0, INFOLINE_LOGOFF));
+
+ sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Logoff message"),
+ r->m_tszStatusMessage ? r->m_tszStatusMessage : TranslateT("<not specified>"), sttInfoLineId(0, INFOLINE_LOGOFF_MSG));
+
+ // activity
+ if (item->m_pLastSeenResource)
+ lstrcpyn(buf, item->m_pLastSeenResource->m_tszResourceName, SIZEOF(buf));
+ else
+ lstrcpyn(buf, TranslateT("<no information available>"), SIZEOF(buf));
+
+ sttFillInfoLine(hwndTree, htiRoot, NULL, TranslateT("Last active resource"), buf,
+ sttInfoLineId(0, INFOLINE_LASTACTIVE));
+
+ // resources
+ if (item->arResources.getCount()) {
+ for (int i=0; i < item->arResources.getCount(); i++)
+ sttFillResourceInfo(ppro, hwndTree, htiRoot, item, i+1);
+ }
+ else if (!_tcschr(item->jid, _T('@')) || (r->m_iStatus != ID_STATUS_OFFLINE))
+ sttFillResourceInfo(ppro, hwndTree, htiRoot, item, 0);
+
+ sttCleanupInfo(hwndTree, 1);
+ SendMessage(hwndTree, WM_SETREDRAW, TRUE, 0);
+
+ RedrawWindow(hwndTree, NULL, NULL, RDW_INVALIDATE);
+}
+
+static void sttGetNodeText(HWND hwndTree, HTREEITEM hti, UserInfoStringBuf *buf, int indent = 0)
+{
+ for (int i=0; i < indent; i++)
+ buf->append(_T("\t"));
+
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_TEXT|TVIF_STATE;
+ tvi.hItem = hti;
+ tvi.cchTextMax = 256;
+ tvi.pszText = buf->allocate(tvi.cchTextMax);
+ if (!TreeView_GetItem(hwndTree, &tvi)) { // failure, maybe item was removed...
+ buf->buf[ buf->offset ] = 0;
+ buf->actualize();
+ return;
+ }
+
+ buf->actualize();
+ buf->append(_T("\r\n"));
+
+ if (tvi.state & TVIS_EXPANDED)
+ for (hti = TreeView_GetChild(hwndTree, hti); hti; hti = TreeView_GetNextSibling(hwndTree, hti))
+ sttGetNodeText(hwndTree, hti, buf, indent + 1);
+}
+
+static INT_PTR CALLBACK JabberUserInfoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ JabberUserInfoDlgData *dat = (JabberUserInfoDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ // lParam is hContact
+ TranslateDialogDefault(hwndDlg);
+
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_OTHER_USERDETAILS));
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_USERDETAILS));
+
+ dat = (JabberUserInfoDlgData *)mir_alloc(sizeof(JabberUserInfoDlgData));
+ ZeroMemory(dat, sizeof(JabberUserInfoDlgData));
+ dat->resourcesCount = -1;
+
+ if (CallService(MS_DB_CONTACT_IS, (WPARAM)lParam, 0))
+ dat->hContact = lParam;
+ else if (!IsBadReadPtr((void*)lParam, sizeof(JABBER_LIST_ITEM))) {
+ dat->hContact = NULL;
+ dat->item = (JABBER_LIST_ITEM *)lParam;
+ }
+
+ {
+ RECT rc; GetClientRect(hwndDlg, &rc);
+ MoveWindow(GetDlgItem(hwndDlg, IDC_TV_INFO), 5, 5, rc.right-10, rc.bottom-10, TRUE);
+
+ HIMAGELIST himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR|ILC_COLOR32|ILC_MASK, 5, 1);
+ ImageList_AddIcon_Icolib(himl, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT));
+ TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TV_INFO), himl, TVSIL_NORMAL);
+
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ WindowList_Add(hUserInfoList, hwndDlg, dat->hContact);
+ }
+ break;
+
+ case WM_JABBER_REFRESH:
+ if (!dat) break;
+
+ if (!dat->item) {
+ ptrT jid( dat->ppro->getTStringA(dat->hContact, "jid"));
+ if (jid == NULL)
+ break;
+
+ if (!(dat->item = dat->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid)))
+ dat->item = dat->ppro->ListGetItemPtr(LIST_ROSTER, jid);
+
+ if (!dat->item) {
+ HWND hwndTree = GetDlgItem(hwndDlg, IDC_TV_INFO);
+ TreeView_DeleteAllItems(hwndTree);
+ HTREEITEM htiRoot = sttFillInfoLine(hwndTree, NULL, dat->ppro->LoadIconEx("main"), _T("JID"), jid, sttInfoLineId(0, INFOLINE_NAME), true);
+ sttFillInfoLine(hwndTree, htiRoot, dat->ppro->LoadIconEx("vcard"), NULL,
+ TranslateT("Please switch online to see more details."));
+ break;
+ }
+ }
+ sttFillUserInfo(dat->ppro, GetDlgItem(hwndDlg, IDC_TV_INFO), dat->item);
+ break;
+
+ case WM_SIZE:
+ MoveWindow(GetDlgItem(hwndDlg, IDC_TV_INFO), 5, 5, LOWORD(lParam)-10, HIWORD(lParam)-10, TRUE);
+ break;
+
+ case WM_CONTEXTMENU:
+ if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_TV_INFO) {
+ HWND hwndTree = GetDlgItem(hwndDlg, IDC_TV_INFO);
+ POINT pt = { (signed short)LOWORD(lParam), (signed short)HIWORD(lParam) };
+ HTREEITEM hItem = 0;
+
+ if ((pt.x == -1) && (pt.y == -1)) {
+ if (hItem = TreeView_GetSelection(hwndTree)) {
+ RECT rc;
+ TreeView_GetItemRect(hwndTree, hItem, &rc, TRUE);
+ pt.x = rc.left;
+ pt.y = rc.bottom;
+ ClientToScreen(hwndTree, &pt);
+ }
+ }
+ else {
+ TVHITTESTINFO tvhti = {0};
+ tvhti.pt = pt;
+ ScreenToClient(hwndTree, &tvhti.pt);
+ TreeView_HitTest(hwndTree, &tvhti);
+ if (tvhti.flags & TVHT_ONITEM) {
+ hItem = tvhti.hItem;
+ TreeView_Select(hwndTree, hItem, TVGN_CARET);
+ } }
+
+ if (hItem) {
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)1, TranslateT("Copy"));
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)2, TranslateT("Copy only this value"));
+ AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(hMenu, MF_STRING, (UINT_PTR)0, TranslateT("Cancel"));
+ int nReturnCmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ if (nReturnCmd == 1) {
+ UserInfoStringBuf buf;
+ sttGetNodeText(hwndTree, hItem, &buf);
+ JabberCopyText(hwndDlg, buf.buf);
+ }
+ else if (nReturnCmd == 2) {
+ TCHAR szBuffer[ 1024 ];
+ TVITEMEX tvi = {0};
+ tvi.mask = TVIF_HANDLE|TVIF_TEXT|TVIF_STATE;
+ tvi.hItem = hItem;
+ tvi.cchTextMax = SIZEOF(szBuffer);
+ tvi.pszText = szBuffer;
+ if (TreeView_GetItem(hwndTree, &tvi)) {
+ if (TCHAR *str = _tcsstr(szBuffer, _T(": ")))
+ JabberCopyText(hwndDlg, str+2);
+ else
+ JabberCopyText(hwndDlg, szBuffer);
+ } }
+ DestroyMenu(hMenu);
+ } }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == 0) {
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_INFOCHANGED:
+ {
+ MCONTACT hContact = (MCONTACT)((LPPSHNOTIFY)lParam)->lParam;
+ SendMessage(hwndDlg, WM_JABBER_REFRESH, 0, hContact);
+ }
+ break;
+
+ case PSN_PARAMCHANGED:
+ dat->ppro = (CJabberProto*)((PSHNOTIFY*)lParam)->lParam;
+ if (dat->hContact != NULL) {
+ ptrT jid( dat->ppro->getTStringA(dat->hContact, "jid"));
+ if (jid != NULL)
+ if (!(dat->item = dat->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid)))
+ dat->item = dat->ppro->ListGetItemPtr(LIST_ROSTER, jid);
+ }
+ break;
+ } }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ WindowList_Remove(hUserInfoList, hwndDlg);
+ if (dat) {
+ mir_free(dat);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ }
+ ImageList_Destroy(TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_TV_INFO), NULL, TVSIL_NORMAL));
+ WindowFreeIcon(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserPhotoDlgProc - Jabber photo dialog
+
+struct USER_PHOTO_INFO
+{
+ MCONTACT hContact;
+ HBITMAP hBitmap;
+ CJabberProto *ppro;
+};
+
+static INT_PTR CALLBACK JabberUserPhotoDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ USER_PHOTO_INFO *photoInfo;
+
+ photoInfo = (USER_PHOTO_INFO *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ // lParam is hContact
+ TranslateDialogDefault(hwndDlg);
+ photoInfo = (USER_PHOTO_INFO *) mir_alloc(sizeof(USER_PHOTO_INFO));
+ photoInfo->hContact = lParam;
+ photoInfo->ppro = NULL;
+ photoInfo->hBitmap = NULL;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) photoInfo);
+ SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadImage(hInst, MAKEINTRESOURCE(IDI_SAVE), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0));
+ SendDlgItemMessage(hwndDlg, IDC_SAVE, BUTTONSETASFLATBTN, TRUE, 0);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOAD), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_DELETE), SW_HIDE);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_INFOCHANGED:
+ SendMessage(hwndDlg, WM_JABBER_REFRESH, 0, 0);
+ break;
+
+ case PSN_PARAMCHANGED:
+ photoInfo->ppro = (CJabberProto*)((PSHNOTIFY*)lParam)->lParam;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_JABBER_REFRESH:
+ {
+ if (photoInfo->hBitmap) {
+ DeleteObject(photoInfo->hBitmap);
+ photoInfo->hBitmap = NULL;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_HIDE);
+ ptrT jid( photoInfo->ppro->getTStringA(photoInfo->hContact, "jid"));
+ if (jid != NULL) {
+ JABBER_LIST_ITEM *item = photoInfo->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid);
+ if (item == NULL)
+ item = photoInfo->ppro->ListGetItemPtr(LIST_ROSTER, jid);
+ if (item != NULL) {
+ if (item->photoFileName) {
+ photoInfo->ppro->debugLog(_T("Showing picture from %s"), item->photoFileName);
+ photoInfo->hBitmap = (HBITMAP) CallService(MS_UTILS_LOADBITMAPT, 0, (LPARAM)item->photoFileName);
+ FIP->FI_Premultiply(photoInfo->hBitmap);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_SHOW);
+ }
+ }
+ }
+ InvalidateRect(hwndDlg, NULL, TRUE);
+ UpdateWindow(hwndDlg);
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_SAVE:
+ static TCHAR szFilter[512];
+
+ ptrT jid(photoInfo->ppro->getTStringA(photoInfo->hContact, "jid"));
+ if (jid == NULL)
+ break;
+
+ JABBER_LIST_ITEM *item = photoInfo->ppro->ListGetItemPtr(LIST_VCARD_TEMP, jid);
+ if (item == NULL)
+ if ((item = photoInfo->ppro->ListGetItemPtr(LIST_ROSTER, jid)) == NULL)
+ break;
+
+ switch (ProtoGetAvatarFileFormat(item->photoFileName)) {
+ case PA_FORMAT_BMP:
+ mir_sntprintf(szFilter, SIZEOF(szFilter), _T("BMP %s (*.bmp)%c*.BMP"), TranslateT("format"), 0);
+ break;
+
+ case PA_FORMAT_GIF:
+ mir_sntprintf(szFilter, SIZEOF(szFilter), _T("GIF %s (*.gif)%c*.GIF"), TranslateT("format"), 0);
+ break;
+
+ case PA_FORMAT_JPEG:
+ mir_sntprintf(szFilter, SIZEOF(szFilter), _T("JPEG %s (*.jpg;*.jpeg)%c*.JPG;*.JPEG"), TranslateT("format"), 0);
+ break;
+
+ default:
+ mir_sntprintf(szFilter, SIZEOF(szFilter), _T("%s (*.*)%c*.*"), TranslateT("Unknown format"), 0);
+ }
+
+ TCHAR szFileName[MAX_PATH]; szFileName[0] = '\0';
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.Flags = OFN_OVERWRITEPROMPT;
+ if ( GetSaveFileName(&ofn)) {
+ photoInfo->ppro->debugLog(_T("File selected is %s"), szFileName);
+ CopyFile(item->photoFileName, szFileName, FALSE);
+ }
+ }
+ break;
+
+ case WM_PAINT:
+ if (!photoInfo->ppro->m_bJabberOnline)
+ SetDlgItemText(hwndDlg, IDC_CANVAS, TranslateT("<Photo not available while offline>"));
+ else if (!photoInfo->hBitmap)
+ SetDlgItemText(hwndDlg, IDC_CANVAS, TranslateT("<No photo>"));
+ else {
+ BITMAP bm;
+ POINT ptSize, ptOrg, pt, ptFitSize;
+ RECT rect;
+
+ SetDlgItemTextA(hwndDlg, IDC_CANVAS, "");
+ HBITMAP hBitmap = photoInfo->hBitmap;
+ HWND hwndCanvas = GetDlgItem(hwndDlg, IDC_CANVAS);
+ HDC hdcCanvas = GetDC(hwndCanvas);
+ HDC hdcMem = CreateCompatibleDC(hdcCanvas);
+ SelectObject(hdcMem, hBitmap);
+ SetMapMode(hdcMem, GetMapMode(hdcCanvas));
+ GetObject(hBitmap, sizeof(BITMAP), (LPVOID) &bm);
+ ptSize.x = bm.bmWidth;
+ ptSize.y = bm.bmHeight;
+ DPtoLP(hdcCanvas, &ptSize, 1);
+ ptOrg.x = ptOrg.y = 0;
+ DPtoLP(hdcMem, &ptOrg, 1);
+ GetClientRect(hwndCanvas, &rect);
+ InvalidateRect(hwndCanvas, NULL, TRUE);
+ UpdateWindow(hwndCanvas);
+ if (ptSize.x<=rect.right && ptSize.y<=rect.bottom) {
+ pt.x = (rect.right - ptSize.x)/2;
+ pt.y = (rect.bottom - ptSize.y)/2;
+ ptFitSize = ptSize;
+ }
+ else {
+ if (((float)(ptSize.x-rect.right))/ptSize.x > ((float)(ptSize.y-rect.bottom))/ptSize.y) {
+ ptFitSize.x = rect.right;
+ ptFitSize.y = (ptSize.y*rect.right)/ptSize.x;
+ pt.x = 0;
+ pt.y = (rect.bottom - ptFitSize.y)/2;
+ }
+ else {
+ ptFitSize.x = (ptSize.x*rect.bottom)/ptSize.y;
+ ptFitSize.y = rect.bottom;
+ pt.x = (rect.right - ptFitSize.x)/2;
+ pt.y = 0;
+ }
+ }
+
+ if (IsThemeActive()) {
+ RECT rc; GetClientRect(hwndCanvas, &rc);
+ DrawThemeParentBackground(hwndCanvas, hdcCanvas, &rc);
+ }
+ else {
+ RECT rc; GetClientRect(hwndCanvas, &rc);
+ FillRect(hdcCanvas, &rc, (HBRUSH)GetSysColorBrush(COLOR_BTNFACE));
+ }
+
+ if (bm.bmBitsPixel == 32) {
+ BLENDFUNCTION bf = {0};
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.SourceConstantAlpha = 255;
+ GdiAlphaBlend(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, bf);
+ }
+ else {
+ SetStretchBltMode(hdcCanvas, COLORONCOLOR);
+ StretchBlt(hdcCanvas, pt.x, pt.y, ptFitSize.x, ptFitSize.y, hdcMem, ptOrg.x, ptOrg.y, ptSize.x, ptSize.y, SRCCOPY);
+ }
+
+ DeleteDC(hdcMem);
+ }
+ break;
+
+ case WM_DESTROY:
+ DestroyIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, 0));
+ if (!photoInfo)
+ break;
+ if (photoInfo->hBitmap) {
+ photoInfo->ppro->debugLogA("Delete bitmap");
+ DeleteObject(photoInfo->hBitmap);
+ }
+ mir_free(photoInfo);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnInfoInit - initializes user info option dialogs
+
+int CJabberProto::OnUserInfoInit(WPARAM wParam, LPARAM lParam)
+{
+ if (!CallService(MS_PROTO_ISPROTOCOLLOADED, 0, (LPARAM)m_szModuleName))
+ return 0;
+
+ MCONTACT hContact = lParam;
+ if (hContact == NULL) {
+ // Show our vcard
+ OnUserInfoInit_VCard(wParam, lParam);
+ return 0;
+ }
+
+ char *szProto = GetContactProto(hContact);
+ if (szProto != NULL && !strcmp(szProto, m_szModuleName)) {
+ OPTIONSDIALOGPAGE odp = { sizeof(odp) };
+ odp.hInstance = hInst;
+ odp.dwInitParam = (LPARAM)this;
+
+ odp.pfnDlgProc = JabberUserInfoDlgProc;
+ odp.position = -2000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO_JABBER);
+ odp.pszTitle = LPGEN("Account");
+ UserInfo_AddPage(wParam, &odp);
+
+ odp.pfnDlgProc = JabberUserPhotoDlgProc;
+ odp.position = 2000000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_VCARD_PHOTO);
+ odp.pszTitle = LPGEN("Photo");
+ UserInfo_AddPage(wParam, &odp);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoUpdate
+
+void JabberUserInfoInit()
+{
+ hUserInfoList = WindowList_Create();
+}
+
+void JabberUserInfoUninit()
+{
+ WindowList_Destroy(hUserInfoList);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JabberUserInfoUpdate
+
+void JabberUserInfoUpdate(MCONTACT hContact)
+{
+ if (!hContact)
+ WindowList_BroadcastAsync(hUserInfoList, WM_JABBER_REFRESH, 0, 0);
+ else if (HWND hwnd = WindowList_Find(hUserInfoList, hContact))
+ PostMessage(hwnd, WM_JABBER_REFRESH, 0, 0);
+}