/* Jabber Protocol Plugin for Miranda IM Copyright (C) 2002-04 Santithorn Bunchua Copyright (C) 2005-12 George Hazan Copyright (C) 2007 Maxim Mluhov Copyright (C) 2012-13 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 "jabber_iq.h" #include "jabber_disco.h" #define SD_FAKEJID_CONFERENCES "@@conferences" #define SD_FAKEJID_MYAGENTS "@@my-transports" #define SD_FAKEJID_AGENTS "@@transports" #define SD_FAKEJID_FAVORITES "@@favorites" enum { SD_BROWSE_NORMAL, SD_BROWSE_MYAGENTS, SD_BROWSE_AGENTS, SD_BROWSE_CONFERENCES, SD_BROWSE_FAVORITES }; static int sttBrowseMode = SD_BROWSE_NORMAL; #define REFRESH_TIMEOUT 500 #define REFRESH_TIMER 1607 static DWORD sttLastRefresh = 0; #define AUTODISCO_TIMEOUT 500 #define AUTODISCO_TIMER 1608 static DWORD sttLastAutoDisco = 0; enum { SD_OVERLAY_NONE, SD_OVERLAY_FAIL, SD_OVERLAY_PROGRESS, SD_OVERLAY_REGISTERED }; static struct { TCHAR *feature; TCHAR *category; TCHAR *type; char *iconName; int iconIndex; int listIndex; } sttNodeIcons[] = { // standard identities: http://www.xmpp.org/registrar/disco-categories.html#directory // {NULL, _T("account"), _T("admin"), NULL, 0}, // {NULL, _T("account"), _T("anonymous"), NULL, 0}, // {NULL, _T("account"), _T("registered"), NULL, 0}, {NULL, _T("account"), NULL, NULL, SKINICON_STATUS_ONLINE}, // {NULL, _T("auth"), _T("cert"), NULL, 0}, // {NULL, _T("auth"), _T("generic"), NULL, 0}, // {NULL, _T("auth"), _T("ldap"), NULL, 0}, // {NULL, _T("auth"), _T("ntlm"), NULL, 0}, // {NULL, _T("auth"), _T("pam"), NULL, 0}, // {NULL, _T("auth"), _T("radius"), NULL, 0}, {NULL, _T("auth"), NULL, "key", 0}, /// {NULL, _T("automation"), _T("command-list"), NULL, 0}, /// {NULL, _T("automation"), _T("command-node"), NULL, 0}, // {NULL, _T("automation"), _T("rpc"), NULL, 0}, // {NULL, _T("automation"), _T("soap"), NULL, 0}, {NULL, _T("automation"), NULL, "adhoc", 0}, // {NULL, _T("client"), _T("bot"), NULL, 0}, // {NULL, _T("client"), _T("console"), NULL, 0}, // {NULL, _T("client"), _T("handheld"), NULL, 0}, // {NULL, _T("client"), _T("pc"), NULL, 0}, // {NULL, _T("client"), _T("phone"), NULL, 0}, // {NULL, _T("client"), _T("web"), NULL, 0}, {NULL, _T("client"), NULL, NULL, SKINICON_STATUS_ONLINE}, // {NULL, _T("collaboration"), _T("whiteboard"), NULL, 0}, {NULL, _T("collaboration"), NULL, "group", 0}, // {NULL, _T("component"), _T("archive"), NULL, 0}, // {NULL, _T("component"), _T("c2s"), NULL, 0}, // {NULL, _T("component"), _T("generic"), NULL, 0}, // {NULL, _T("component"), _T("load"), NULL, 0}, // {NULL, _T("component"), _T("log"), NULL, 0}, // {NULL, _T("component"), _T("presence"), NULL, 0}, // {NULL, _T("component"), _T("router"), NULL, 0}, // {NULL, _T("component"), _T("s2s"), NULL, 0}, // {NULL, _T("component"), _T("sm"), NULL, 0}, // {NULL, _T("component"), _T("stats"), NULL, 0}, // {NULL, _T("conference"), _T("irc"), NULL, 0}, // {NULL, _T("conference"), _T("text"), NULL, 0}, {NULL, _T("conference"), NULL, "group", 0}, {NULL, _T("directory"), _T("chatroom"), "group", 0}, {NULL, _T("directory"), _T("group"), "group", 0}, {NULL, _T("directory"), _T("user"), NULL, SKINICON_OTHER_FINDUSER}, // {NULL, _T("directory"), _T("waitinglist"), NULL, 0}, {NULL, _T("directory"), NULL, NULL, SKINICON_OTHER_SEARCHALL}, {NULL, _T("gateway"), _T("aim"), "AIM", SKINICON_STATUS_ONLINE}, {NULL, _T("gateway"), _T("gadu-gadu"), "GG", SKINICON_STATUS_ONLINE}, // {NULL, _T("gateway"), _T("http-ws"), NUL, 0}, {NULL, _T("gateway"), _T("icq"), "ICQ", SKINICON_STATUS_ONLINE}, {NULL, _T("gateway"), _T("msn"), "MSN", SKINICON_STATUS_ONLINE}, {NULL, _T("gateway"), _T("qq"), "QQ", SKINICON_STATUS_ONLINE}, // {NULL, _T("gateway"), _T("sms"), NULL, 0}, // {NULL, _T("gateway"), _T("smtp"), NULL, 0}, {NULL, _T("gateway"), _T("tlen"), "TLEN", SKINICON_STATUS_ONLINE}, {NULL, _T("gateway"), _T("yahoo"), "YAHOO", SKINICON_STATUS_ONLINE}, {NULL, _T("gateway"), NULL, "Agents", 0}, // {NULL, _T("headline"), _T("newmail"), NULL, 0}, {NULL, _T("headline"), _T("rss"), "node_rss", 0}, {NULL, _T("headline"), _T("weather"), "node_weather", 0}, // {NULL, _T("hierarchy"), _T("branch"), NULL, 0}, // {NULL, _T("hierarchy"), _T("leaf"), NULL, 0}, // {NULL, _T("proxy"), _T("bytestreams"), NULL, 0}, {NULL, _T("proxy"), NULL, NULL, SKINICON_EVENT_FILE}, // {NULL, _T("pubsub"), _T("collection"), NULL, 0}, // {NULL, _T("pubsub"), _T("leaf"), NULL, 0}, // {NULL, _T("pubsub"), _T("pep"), NULL, 0}, // {NULL, _T("pubsub"), _T("service"), NULL, 0}, // {NULL, _T("server"), _T("im"), NULL, 0}, {NULL, _T("server"), NULL, "node_server", 0}, // {NULL, _T("store"), _T("berkeley"), NULL, 0}, /// {NULL, _T("store"), _T("file"), NULL, 0}, // {NULL, _T("store"), _T("generic"), NULL, 0}, // {NULL, _T("store"), _T("ldap"), NULL, 0}, // {NULL, _T("store"), _T("mysql"), NULL, 0}, // {NULL, _T("store"), _T("oracle"), NULL, 0}, // {NULL, _T("store"), _T("postgres"), NULL, 0}, {NULL, _T("store"), NULL, "node_store", 0}, // icons for non-standard identities {NULL, _T("x-service"), _T("x-rss"), "node_rss", 0}, {NULL, _T("application"), _T("x-weather"), "node_weather", 0}, {NULL, _T("user"), NULL, NULL, SKINICON_STATUS_ONLINE}, // icon suggestions based on supported features {_T("jabber:iq:gateway"), NULL,NULL, "Agents", 0}, {_T("jabber:iq:search"), NULL,NULL, NULL, SKINICON_OTHER_FINDUSER}, {_T(JABBER_FEAT_COMMANDS), NULL,NULL, "adhoc", 0}, {_T(JABBER_FEAT_REGISTER), NULL,NULL, "key", 0}, }; static void sttApplyNodeIcon(HTREELISTITEM hItem, CJabberSDNode *pNode); void CJabberProto::OnIqResultServiceDiscoveryInfo(HXML iqNode, CJabberIqInfo* pInfo) { m_SDManager.Lock(); CJabberSDNode* pNode = m_SDManager.FindByIqId(pInfo->GetIqId(), TRUE); if ( !pNode) { m_SDManager.Unlock(); return; } if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { HXML query = xmlGetChild(iqNode , "query"); if ( !query) pNode->SetInfoRequestId(JABBER_DISCO_RESULT_ERROR); else { HXML feature; int i; for (i = 1; (feature = xmlGetNthChild(query, _T("feature"), i)) != NULL; i++) pNode->AddFeature(xmlGetAttrValue(feature, _T("var"))); HXML identity; for (i = 1; (identity = xmlGetNthChild(query, _T("identity"), i)) != NULL; i++) pNode->AddIdentity(xmlGetAttrValue(identity, _T("category")), xmlGetAttrValue(identity, _T("type")), xmlGetAttrValue(identity, _T("name"))); pNode->SetInfoRequestId(JABBER_DISCO_RESULT_OK); pNode->SetInfoRequestErrorText(NULL); } } else { if (pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR) { HXML errorNode = xmlGetChild(iqNode , "error"); TCHAR* str = JabberErrorMsg(errorNode); pNode->SetInfoRequestErrorText(str); mir_free(str); } else pNode->SetInfoRequestErrorText(TranslateT("request timeout.")); pNode->SetInfoRequestId(JABBER_DISCO_RESULT_ERROR); } m_SDManager.Unlock(); if (m_pDlgServiceDiscovery) { ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode); PostMessage(m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0); } } void CJabberProto::OnIqResultServiceDiscoveryItems(HXML iqNode, CJabberIqInfo* pInfo) { m_SDManager.Lock(); CJabberSDNode* pNode = m_SDManager.FindByIqId(pInfo->GetIqId(), FALSE); if ( !pNode) { m_SDManager.Unlock(); return; } if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { HXML query = xmlGetChild(iqNode , "query"); if ( !query) pNode->SetItemsRequestId(JABBER_DISCO_RESULT_ERROR); else { HXML item; for (int i = 1; (item = xmlGetNthChild(query, _T("item"), i)) != NULL; i++) { pNode->AddChildNode(xmlGetAttrValue(item, _T("jid")), xmlGetAttrValue(item, _T("node")), xmlGetAttrValue(item, _T("name"))); } pNode->SetItemsRequestId(JABBER_DISCO_RESULT_OK); pNode->SetItemsRequestErrorText(NULL); } } else { if (pInfo->GetIqType() == JABBER_IQ_TYPE_ERROR) { HXML errorNode = xmlGetChild(iqNode , "error"); TCHAR* str = JabberErrorMsg(errorNode); pNode->SetItemsRequestErrorText(str); mir_free(str); } else { pNode->SetItemsRequestErrorText(_T("request timeout.")); } pNode->SetItemsRequestId(JABBER_DISCO_RESULT_ERROR); } m_SDManager.Unlock(); if (m_pDlgServiceDiscovery) { ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode); PostMessage(m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0); } } void CJabberProto::OnIqResultServiceDiscoveryRootInfo(HXML iqNode, CJabberIqInfo* pInfo) { if ( !pInfo->m_pUserData) return; m_SDManager.Lock(); if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { HXML query = xmlGetChild(iqNode , "query"); if (query) { HXML feature; int i; for (i = 1; (feature = xmlGetNthChild(query, _T("feature"), i)) != NULL; i++) { if ( !lstrcmp(xmlGetAttrValue(feature, _T("var")), (TCHAR *)pInfo->m_pUserData)) { CJabberSDNode *pNode = m_SDManager.AddPrimaryNode(pInfo->GetReceiver(), xmlGetAttrValue(iqNode, _T("node")), NULL); SendBothRequests(pNode, NULL); break; } } } } m_SDManager.Unlock(); UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_REFRESH); } void CJabberProto::OnIqResultServiceDiscoveryRootItems(HXML iqNode, CJabberIqInfo* pInfo) { if ( !pInfo->m_pUserData) return; XmlNode packet(NULL); m_SDManager.Lock(); if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) { HXML query = xmlGetChild(iqNode , "query"); if (query) { HXML item; for (int i = 1; (item = xmlGetNthChild(query, _T("item"), i)) != NULL; i++) { const TCHAR *szJid = xmlGetAttrValue(item, _T("jid")); const TCHAR *szNode = xmlGetAttrValue(item, _T("node")); CJabberIqInfo* pNewInfo = m_iqManager.AddHandler(&CJabberProto::OnIqResultServiceDiscoveryRootInfo, JABBER_IQ_TYPE_GET, szJid); pNewInfo->m_pUserData = pInfo->m_pUserData; pNewInfo->SetTimeout(30000); XmlNodeIq iq(pNewInfo); iq << XQUERY(_T(JABBER_FEAT_DISCO_INFO)) << XATTR(_T("node"), szNode); xmlAddChild(packet, iq); } } } m_SDManager.Unlock(); if (xmlGetChild(packet ,0)) m_ThreadInfo->send(packet); } BOOL CJabberProto::SendInfoRequest(CJabberSDNode* pNode, HXML parent) { if ( !pNode || !m_bJabberOnline) return FALSE; // disco#info if ( !pNode->GetInfoRequestId()) { CJabberIqInfo* pInfo = m_iqManager.AddHandler(&CJabberProto::OnIqResultServiceDiscoveryInfo, JABBER_IQ_TYPE_GET, pNode->GetJid()); pInfo->SetTimeout(30000); pNode->SetInfoRequestId(pInfo->GetIqId()); XmlNodeIq iq(pInfo); HXML query = iq << XQUERY(_T(JABBER_FEAT_DISCO_INFO)); if (pNode->GetNode()) xmlAddAttr(query, _T("node"), pNode->GetNode()); if (parent) xmlAddChild(parent, iq); else m_ThreadInfo->send(iq); } if (m_pDlgServiceDiscovery) { ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode); PostMessage(m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0); } return TRUE; } BOOL CJabberProto::SendBothRequests(CJabberSDNode* pNode, HXML parent) { if ( !pNode || !m_bJabberOnline) return FALSE; // disco#info if ( !pNode->GetInfoRequestId()) { CJabberIqInfo* pInfo = m_iqManager.AddHandler(&CJabberProto::OnIqResultServiceDiscoveryInfo, JABBER_IQ_TYPE_GET, pNode->GetJid()); pInfo->SetTimeout(30000); pNode->SetInfoRequestId(pInfo->GetIqId()); XmlNodeIq iq(pInfo); HXML query = iq << XQUERY(_T(JABBER_FEAT_DISCO_INFO)); if (pNode->GetNode()) xmlAddAttr(query, _T("node"), pNode->GetNode()); if (parent) xmlAddChild(parent, iq); else m_ThreadInfo->send(iq); } // disco#items if ( !pNode->GetItemsRequestId()) { CJabberIqInfo* pInfo = m_iqManager.AddHandler(&CJabberProto::OnIqResultServiceDiscoveryItems, JABBER_IQ_TYPE_GET, pNode->GetJid()); pInfo->SetTimeout(30000); pNode->SetItemsRequestId(pInfo->GetIqId()); XmlNodeIq iq(pInfo); HXML query = iq << XQUERY(_T(JABBER_FEAT_DISCO_ITEMS)); if (pNode->GetNode()) xmlAddAttr(query, _T("node"), pNode->GetNode()); if (parent) xmlAddChild(parent, iq); else m_ThreadInfo->send(iq); } if (m_pDlgServiceDiscovery) { ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode); PostMessage(m_pDlgServiceDiscovery->GetHwnd(), WM_JABBER_REFRESH, 0, 0); } return TRUE; } void CJabberProto::PerformBrowse(HWND hwndDlg) { TCHAR szJid[ JABBER_MAX_JID_LEN ]; TCHAR szNode[ 512 ]; if ( !GetDlgItemText(hwndDlg, IDC_COMBO_JID, szJid, SIZEOF(szJid))) szJid[ 0 ] = 0; if ( !GetDlgItemText(hwndDlg, IDC_COMBO_NODE, szNode, SIZEOF(szNode))) szNode[ 0 ] = 0; ComboAddRecentString(hwndDlg, IDC_COMBO_JID, "discoWnd_rcJid", szJid); ComboAddRecentString(hwndDlg, IDC_COMBO_NODE, "discoWnd_rcNode", szNode); if (_tcslen(szJid)) { HWND hwndList = GetDlgItem(hwndDlg, IDC_TREE_DISCO); TreeList_Reset(hwndList); m_SDManager.Lock(); m_SDManager.RemoveAll(); if ( !lstrcmp(szJid, _T(SD_FAKEJID_MYAGENTS))) { sttBrowseMode = SD_BROWSE_MYAGENTS; JABBER_LIST_ITEM *item = NULL; LISTFOREACH(i, this, LIST_ROSTER) { if ((item=ListGetItemPtrFromIndex(i)) != NULL) { if (_tcschr(item->jid, '@')==NULL && _tcschr(item->jid, '/')==NULL && item->subscription!=SUB_NONE) { HANDLE hContact = HContactFromJID(item->jid); if (hContact != NULL) JSetByte(hContact, "IsTransport", TRUE); if (m_lstTransports.getIndex(item->jid) == -1) m_lstTransports.insert(mir_tstrdup(item->jid)); CJabberSDNode* pNode = m_SDManager.AddPrimaryNode(item->jid, NULL, NULL); SendBothRequests(pNode, NULL); } } } } else if ( !lstrcmp(szJid, _T(SD_FAKEJID_CONFERENCES))) { sttBrowseMode = SD_BROWSE_CONFERENCES; TCHAR *szServerJid = mir_a2t(m_ThreadInfo->server); CJabberIqInfo* pInfo = m_iqManager.AddHandler(&CJabberProto::OnIqResultServiceDiscoveryRootItems, JABBER_IQ_TYPE_GET, szServerJid); pInfo->m_pUserData = (void*)_T(JABBER_FEAT_MUC); pInfo->SetTimeout(30000); XmlNodeIq iq(pInfo); iq << XQUERY(_T(JABBER_FEAT_DISCO_ITEMS)); m_ThreadInfo->send(iq); mir_free(szServerJid); } else if ( !lstrcmp(szJid, _T(SD_FAKEJID_AGENTS))) { sttBrowseMode = SD_BROWSE_AGENTS; TCHAR *szServerJid = mir_a2t(m_ThreadInfo->server); CJabberIqInfo* pInfo = m_iqManager.AddHandler(&CJabberProto::OnIqResultServiceDiscoveryRootItems, JABBER_IQ_TYPE_GET, szServerJid); pInfo->m_pUserData = (void*)_T("jabber:iq:gateway"); pInfo->SetTimeout(30000); XmlNodeIq iq(pInfo); iq << XQUERY(_T(JABBER_FEAT_DISCO_ITEMS)); m_ThreadInfo->send(iq); mir_free(szServerJid); } else if ( !lstrcmp(szJid, _T(SD_FAKEJID_FAVORITES))) { sttBrowseMode = SD_BROWSE_FAVORITES; int count = JGetDword(NULL, "discoWnd_favCount", 0); for (int i = 0; i < count; i++) { DBVARIANT dbv; char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", i); if ( !JGetStringT(NULL, setting, &dbv)) { DBVARIANT dbvJid, dbvNode; mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", i); JGetStringT(NULL, setting, &dbvJid); mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", i); JGetStringT(NULL, setting, &dbvNode); CJabberSDNode* pNode = m_SDManager.AddPrimaryNode(dbvJid.ptszVal, dbvNode.ptszVal, dbv.ptszVal); SendBothRequests(pNode, NULL); db_free(&dbv); db_free(&dbvJid); db_free(&dbvNode); } } } else { sttBrowseMode = SD_BROWSE_NORMAL; CJabberSDNode* pNode = m_SDManager.AddPrimaryNode(szJid, _tcslen(szNode) ? szNode : NULL, NULL); SendBothRequests(pNode, NULL); } m_SDManager.Unlock(); PostMessage(hwndDlg, WM_JABBER_REFRESH, 0, 0); } } BOOL CJabberProto::IsNodeRegistered(CJabberSDNode *pNode) { if (pNode->GetNode()) return FALSE; JABBER_LIST_ITEM *item; if (item = ListGetItemPtr(LIST_ROSTER, pNode->GetJid())) return (item->subscription != SUB_NONE) ? TRUE : FALSE; if (item = ListGetItemPtr(LIST_BOOKMARK, pNode->GetJid())) return TRUE; return FALSE; } void CJabberProto::ApplyNodeIcon(HTREELISTITEM hItem, CJabberSDNode *pNode) { if ( !hItem || !pNode) return; int iIcon = -1, iOverlay = -1; if ((pNode->GetInfoRequestId() > 0) || (pNode->GetItemsRequestId() > 0)) iOverlay = SD_OVERLAY_PROGRESS; else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_ERROR) iOverlay = SD_OVERLAY_FAIL; else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_NOT_REQUESTED) { if (IsNodeRegistered(pNode)) iOverlay = SD_OVERLAY_REGISTERED; else iOverlay = SD_OVERLAY_NONE; } else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_OK) { if (IsNodeRegistered(pNode)) iOverlay = SD_OVERLAY_REGISTERED; else if (pNode->GetInfoRequestId() == JABBER_DISCO_RESULT_ERROR) iOverlay = SD_OVERLAY_FAIL; else iOverlay = SD_OVERLAY_NONE; } for (int i = 0; i < SIZEOF(sttNodeIcons); i++) { if ( !sttNodeIcons[i].iconIndex && !sttNodeIcons[i].iconName) continue; if (sttNodeIcons[i].category) { CJabberSDIdentity *iIdentity; for (iIdentity = pNode->GetFirstIdentity(); iIdentity; iIdentity = iIdentity->GetNext()) if ( !lstrcmp(iIdentity->GetCategory(), sttNodeIcons[i].category) && (!sttNodeIcons[i].type || !lstrcmp(iIdentity->GetType(), sttNodeIcons[i].type))) { iIcon = sttNodeIcons[i].listIndex; break; } if (iIdentity) break; } if (sttNodeIcons[i].feature) { CJabberSDFeature *iFeature; for (iFeature = pNode->GetFirstFeature(); iFeature; iFeature = iFeature->GetNext()) if ( !lstrcmp(iFeature->GetVar(), sttNodeIcons[i].feature)) { iIcon = sttNodeIcons[i].listIndex; break; } if (iFeature) break; } } TreeList_SetIcon(pNode->GetTreeItemHandle(), iIcon, iOverlay); } BOOL CJabberProto::SyncTree(HTREELISTITEM hIndex, CJabberSDNode* pNode) { if ( !m_pDlgServiceDiscovery) return FALSE; CJabberSDNode* pTmp = pNode; while (pTmp) { if ( !pTmp->GetTreeItemHandle()) { HTREELISTITEM hNewItem = TreeList_AddItem( GetDlgItem(m_pDlgServiceDiscovery->GetHwnd(), IDC_TREE_DISCO), hIndex, pTmp->GetName() ? pTmp->GetName() : pTmp->GetJid(), (LPARAM)pTmp); TreeList_AppendColumn(hNewItem, pTmp->GetJid()); TreeList_AppendColumn(hNewItem, pTmp->GetNode()); if ( !pTmp->GetInfoRequestId()) TreeList_MakeFakeParent(hNewItem, TRUE); else TreeList_MakeFakeParent(hNewItem, FALSE); pTmp->SetTreeItemHandle(hNewItem); } ApplyNodeIcon(pNode->GetTreeItemHandle(), pNode); if (pTmp->GetFirstChildNode()) SyncTree(pTmp->GetTreeItemHandle(), pTmp->GetFirstChildNode()); pTmp = pTmp->GetNext(); } return TRUE; } /////////////////////////////////////////////////////////////////////////////// // CJabberDlgDiscovery class CJabberDlgDiscovery: public CJabberDlgBase { typedef CJabberDlgBase CSuper; public: CJabberDlgDiscovery(CJabberProto *proto, TCHAR *jid); protected: void OnInitDialog(); void OnClose(); void OnDestroy(); INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam); int Resizer(UTILRESIZECONTROL *urc); private: TCHAR *m_jid; bool m_focusEditAfterBrowse; CCtrlMButton m_btnViewAsTree; CCtrlMButton m_btnViewAsList; CCtrlMButton m_btnGoHome; CCtrlMButton m_btnBookmarks; CCtrlMButton m_btnRefresh; CCtrlMButton m_btnBrowse; CCtrlFilterListView m_lstDiscoTree; void btnViewAsTree_OnClick(CCtrlButton *); void btnViewAsList_OnClick(CCtrlButton *); void btnGoHome_OnClick(CCtrlButton *); void btnBookmarks_OnClick(CCtrlButton *); void btnRefresh_OnClick(CCtrlButton *); void btnBrowse_OnClick(CCtrlButton *); void lstDiscoTree_OnFilter(CCtrlFilterListView *); }; CJabberDlgDiscovery::CJabberDlgDiscovery(CJabberProto *proto, TCHAR *jid) : CJabberDlgBase(proto, IDD_SERVICE_DISCOVERY, NULL), m_jid(jid), m_btnViewAsTree(this, IDC_BTN_VIEWTREE, proto->LoadIconEx("sd_view_tree"), "View as tree"), m_btnViewAsList(this, IDC_BTN_VIEWLIST, proto->LoadIconEx("sd_view_list"), "View as list"), m_btnGoHome(this, IDC_BTN_NAVHOME, proto->LoadIconEx("sd_nav_home"), "Navigate home"), m_btnBookmarks(this, IDC_BTN_FAVORITE, proto->LoadIconEx("bookmarks"), "Favorites"), m_btnRefresh(this, IDC_BTN_REFRESH, proto->LoadIconEx("sd_nav_refresh"), "Refresh node"), m_btnBrowse(this, IDC_BUTTON_BROWSE, proto->LoadIconEx("sd_browse"), "Browse"), m_lstDiscoTree(this, IDC_TREE_DISCO, true, false) { m_btnViewAsTree.OnClick = Callback(this, &CJabberDlgDiscovery::btnViewAsTree_OnClick); m_btnViewAsList.OnClick = Callback(this, &CJabberDlgDiscovery::btnViewAsList_OnClick); m_btnGoHome.OnClick = Callback(this, &CJabberDlgDiscovery::btnGoHome_OnClick); m_btnBookmarks.OnClick = Callback(this, &CJabberDlgDiscovery::btnBookmarks_OnClick); m_btnRefresh.OnClick = Callback(this, &CJabberDlgDiscovery::btnRefresh_OnClick); m_btnBrowse.OnClick = Callback(this, &CJabberDlgDiscovery::btnBrowse_OnClick); m_lstDiscoTree.OnFilterChanged = Callback(this, &CJabberDlgDiscovery::lstDiscoTree_OnFilter); } void CJabberDlgDiscovery::OnInitDialog() { CSuper::OnInitDialog(); // TranslateDialogDefault(m_hwnd); WindowSetIcon(m_hwnd, m_proto, "servicediscovery"); int i; if (m_jid) { SetDlgItemText(m_hwnd, IDC_COMBO_JID, m_jid); SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T("")); m_focusEditAfterBrowse = false; } else { SetDlgItemTextA(m_hwnd, IDC_COMBO_JID, m_proto->m_ThreadInfo->server); SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T("")); m_focusEditAfterBrowse = true; } m_btnViewAsList.MakePush(); m_btnViewAsTree.MakePush(); m_btnBookmarks.MakePush(); CheckDlgButton(m_hwnd, db_get_b(NULL, m_proto->m_szModuleName, "discoWnd_useTree", 1) ? IDC_BTN_VIEWTREE : IDC_BTN_VIEWLIST, TRUE); EnableWindow(GetDlgItem(m_hwnd, IDC_BTN_FILTERRESET), FALSE); SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_CONFERENCES)); SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_MYAGENTS)); SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_AGENTS)); SendDlgItemMessage(m_hwnd, IDC_COMBO_JID, CB_ADDSTRING, 0, (LPARAM)_T(SD_FAKEJID_FAVORITES)); m_proto->ComboLoadRecentStrings(m_hwnd, IDC_COMBO_JID, "discoWnd_rcJid"); m_proto->ComboLoadRecentStrings(m_hwnd, IDC_COMBO_NODE, "discoWnd_rcNode"); HWND hwndList = m_lstDiscoTree.GetHwnd();//GetDlgItem(m_hwnd, IDC_TREE_DISCO); LVCOLUMN lvc = {0}; lvc.mask = LVCF_SUBITEM|LVCF_WIDTH|LVCF_TEXT; lvc.cx = db_get_w(NULL, m_proto->m_szModuleName, "discoWnd_cx0", 200); lvc.iSubItem = 0; lvc.pszText = TranslateT("Node hierarchy"); ListView_InsertColumn(hwndList, 0, &lvc); lvc.cx = db_get_w(NULL, m_proto->m_szModuleName, "discoWnd_cx1", 200); lvc.iSubItem = 1; lvc.pszText = _T("JID"); ListView_InsertColumn(hwndList, 1, &lvc); lvc.cx = db_get_w(NULL, m_proto->m_szModuleName, "discoWnd_cx2", 200); lvc.iSubItem = 2; lvc.pszText = TranslateT("Node"); ListView_InsertColumn(hwndList, 2, &lvc); TreeList_Create(hwndList); TreeList_AddIcon(hwndList, m_proto->LoadIconEx("main"), 0); for (i = 0; i < SIZEOF(sttNodeIcons); i++) { bool needDestroy = false; HICON hIcon; if ((sttNodeIcons[i].iconIndex == SKINICON_STATUS_ONLINE) && sttNodeIcons[i].iconName) { hIcon = (HICON)CallProtoService(sttNodeIcons[i].iconName, PS_LOADICON, PLI_PROTOCOL|PLIF_SMALL, 0); needDestroy = true; } else if (sttNodeIcons[i].iconName) hIcon = m_proto->LoadIconEx(sttNodeIcons[i].iconName); else if (sttNodeIcons[i].iconIndex) hIcon = LoadSkinnedIcon(sttNodeIcons[i].iconIndex); else continue; sttNodeIcons[i].listIndex = TreeList_AddIcon(hwndList, hIcon, 0); if (needDestroy) DestroyIcon(hIcon); } TreeList_AddIcon(hwndList, m_proto->LoadIconEx("disco_fail"), SD_OVERLAY_FAIL); TreeList_AddIcon(hwndList, m_proto->LoadIconEx("disco_progress"), SD_OVERLAY_PROGRESS); TreeList_AddIcon(hwndList, m_proto->LoadIconEx("disco_ok"), SD_OVERLAY_REGISTERED); TreeList_SetMode(hwndList, db_get_b(NULL, m_proto->m_szModuleName, "discoWnd_useTree", 1) ? TLM_TREE : TLM_REPORT); PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "discoWnd_"); } void CJabberDlgDiscovery::OnClose() { db_set_b(NULL, m_proto->m_szModuleName, "discoWnd_useTree", IsDlgButtonChecked(m_hwnd, IDC_BTN_VIEWTREE)); HWND hwndList = GetDlgItem(m_hwnd, IDC_TREE_DISCO); LVCOLUMN lvc = {0}; lvc.mask = LVCF_WIDTH; ListView_GetColumn(hwndList, 0, &lvc); db_set_w(NULL, m_proto->m_szModuleName, "discoWnd_cx0", lvc.cx); ListView_GetColumn(hwndList, 1, &lvc); db_set_w(NULL, m_proto->m_szModuleName, "discoWnd_cx1", lvc.cx); ListView_GetColumn(hwndList, 2, &lvc); db_set_w(NULL, m_proto->m_szModuleName, "discoWnd_cx2", lvc.cx); Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "discoWnd_"); DestroyWindow(m_hwnd); CSuper::OnClose(); } void CJabberDlgDiscovery::OnDestroy() { m_proto->m_pDlgServiceDiscovery = NULL; m_proto->m_SDManager.Lock(); m_proto->m_SDManager.RemoveAll(); m_proto->m_SDManager.Unlock(); TreeList_Destroy(GetDlgItem(m_hwnd, IDC_TREE_DISCO)); CSuper::OnDestroy(); } int CJabberDlgDiscovery::Resizer(UTILRESIZECONTROL *urc) { RECT rc; switch (urc->wId) { case IDC_COMBO_JID: { GetWindowRect(GetDlgItem(m_hwnd, urc->wId), &rc); urc->rcItem.right += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2; urc->rcItem.bottom = urc->rcItem.top + rc.bottom - rc.top; return 0; } case IDC_TXT_NODELABEL: { urc->rcItem.left += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2; urc->rcItem.right += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2; return 0; } case IDC_COMBO_NODE: { GetWindowRect(GetDlgItem(m_hwnd, urc->wId), &rc); urc->rcItem.left += (urc->dlgNewSize.cx - urc->dlgOriginalSize.cx) / 2; urc->rcItem.right += urc->dlgNewSize.cx - urc->dlgOriginalSize.cx; urc->rcItem.bottom = urc->rcItem.top + rc.bottom - rc.top; return 0; } case IDC_BUTTON_BROWSE: return RD_ANCHORX_RIGHT|RD_ANCHORY_TOP; case IDC_TREE_DISCO: return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT; case IDC_TXT_FILTER: return RD_ANCHORX_LEFT|RD_ANCHORY_BOTTOM; case IDC_TXT_FILTERTEXT: return RD_ANCHORX_WIDTH|RD_ANCHORY_BOTTOM; case IDC_BTN_FILTERAPPLY: case IDC_BTN_FILTERRESET: return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM; } return CSuper::Resizer(urc); } void CJabberDlgDiscovery::btnViewAsTree_OnClick(CCtrlButton *) { CheckDlgButton(m_hwnd, IDC_BTN_VIEWLIST, FALSE); CheckDlgButton(m_hwnd, IDC_BTN_VIEWTREE, TRUE); TreeList_SetMode(GetDlgItem(m_hwnd, IDC_TREE_DISCO), TLM_TREE); } void CJabberDlgDiscovery::btnViewAsList_OnClick(CCtrlButton *) { CheckDlgButton(m_hwnd, IDC_BTN_VIEWLIST, TRUE); CheckDlgButton(m_hwnd, IDC_BTN_VIEWTREE, FALSE); TreeList_SetMode(GetDlgItem(m_hwnd, IDC_TREE_DISCO), TLM_REPORT); } void CJabberDlgDiscovery::btnGoHome_OnClick(CCtrlButton *) { SetDlgItemTextA(m_hwnd, IDC_COMBO_JID, m_proto->m_ThreadInfo->server); SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T("")); PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); } void CJabberDlgDiscovery::btnBookmarks_OnClick(CCtrlButton *) { HMENU hMenu = CreatePopupMenu(); int count = m_proto->JGetDword(NULL, "discoWnd_favCount", 0); for (int i = 0; i < count; i++) { DBVARIANT dbv; char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", i); if ( !m_proto->JGetStringT(NULL, setting, &dbv)) { HMENU hSubMenu = CreatePopupMenu(); AppendMenu(hSubMenu, MF_STRING, 100+i*10+0, TranslateT("Navigate")); AppendMenu(hSubMenu, MF_SEPARATOR, 0, NULL); AppendMenu(hSubMenu, MF_STRING, 100+i*10+1, TranslateT("Remove")); AppendMenu(hMenu, MF_POPUP|MF_STRING, (UINT_PTR)hSubMenu, dbv.ptszVal); } db_free(&dbv); } int res = 0; if (GetMenuItemCount(hMenu)) { AppendMenu(hMenu, MF_SEPARATOR, 1, NULL); AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_FAVORITES, TranslateT("Browse all favorites")); AppendMenu(hMenu, MF_STRING, 1, TranslateT("Remove all favorites")); } if (GetMenuItemCount(hMenu)) AppendMenu(hMenu, MF_SEPARATOR, 1, NULL); AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_MYAGENTS, TranslateT("Registered transports")); AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_AGENTS, TranslateT("Browse local transports")); AppendMenu(hMenu, MF_STRING, 10+SD_BROWSE_CONFERENCES, TranslateT("Browse chatrooms")); RECT rc; GetWindowRect(GetDlgItem(m_hwnd, IDC_BTN_FAVORITE), &rc); CheckDlgButton(m_hwnd, IDC_BTN_FAVORITE, TRUE); res = TrackPopupMenu(hMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, NULL); CheckDlgButton(m_hwnd, IDC_BTN_FAVORITE, FALSE); DestroyMenu(hMenu); if (res >= 100) { res -= 100; if (res % 10) { res /= 10; char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", res); m_proto->JDeleteSetting(NULL, setting); mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", res); m_proto->JDeleteSetting(NULL, setting); mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", res); m_proto->JDeleteSetting(NULL, setting); } else { res /= 10; SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T("")); SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T("")); DBVARIANT dbv; char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", res); if ( !m_proto->JGetStringT(NULL, setting, &dbv)) SetDlgItemText(m_hwnd, IDC_COMBO_JID, dbv.ptszVal); db_free(&dbv); mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", res); if ( !m_proto->JGetStringT(NULL, setting, &dbv)) SetDlgItemText(m_hwnd, IDC_COMBO_NODE, dbv.ptszVal); db_free(&dbv); PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); } } else if (res == 1) { int count = m_proto->JGetDword(NULL, "discoWnd_favCount", 0); for (int i = 0; i < count; i++) { char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", i); m_proto->JDeleteSetting(NULL, setting); mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", i); m_proto->JDeleteSetting(NULL, setting); mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", i); m_proto->JDeleteSetting(NULL, setting); } m_proto->JDeleteSetting(NULL, "discoWnd_favCount"); } else if ((res >= 10) && (res <= 20)) { switch (res-10) { case SD_BROWSE_FAVORITES: SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_FAVORITES)); break; case SD_BROWSE_MYAGENTS: SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_MYAGENTS)); break; case SD_BROWSE_AGENTS: SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_AGENTS)); break; case SD_BROWSE_CONFERENCES: SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_CONFERENCES)); break; } SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T("")); PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); } CheckDlgButton(m_hwnd, IDC_BTN_FAVORITE, FALSE); } void CJabberDlgDiscovery::btnRefresh_OnClick(CCtrlButton *) { HTREELISTITEM hItem = (HTREELISTITEM)TreeList_GetActiveItem(GetDlgItem(m_hwnd, IDC_TREE_DISCO)); if ( !hItem) return; m_proto->m_SDManager.Lock(); XmlNode packet(NULL); CJabberSDNode* pNode = (CJabberSDNode*)TreeList_GetData(hItem); if (pNode) { TreeList_ResetItem(GetDlgItem(m_hwnd, IDC_TREE_DISCO), hItem); pNode->ResetInfo(); m_proto->SendBothRequests(pNode, packet); TreeList_MakeFakeParent(hItem, FALSE); } m_proto->m_SDManager.Unlock(); if (xmlGetChild(packet ,0)) m_proto->m_ThreadInfo->send(packet); } void CJabberDlgDiscovery::btnBrowse_OnClick(CCtrlButton *) { SetFocus(GetDlgItem(m_hwnd, m_focusEditAfterBrowse ? IDC_COMBO_JID : IDC_TREE_DISCO)); m_focusEditAfterBrowse = false; m_proto->PerformBrowse(m_hwnd); } void CJabberDlgDiscovery::lstDiscoTree_OnFilter(CCtrlFilterListView *) { TreeList_SetFilter(GetDlgItem(m_hwnd, IDC_TREE_DISCO), m_lstDiscoTree.GetFilterText()); } INT_PTR CJabberDlgDiscovery::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) { BOOL result; if (TreeList_ProcessMessage(m_hwnd, msg, wParam, lParam, IDC_TREE_DISCO, &result)) return result; switch (msg) { case WM_GETMINMAXINFO: { LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam; lpmmi->ptMinTrackSize.x = 538; lpmmi->ptMinTrackSize.y = 374; return 0; } case WM_JABBER_TRANSPORT_REFRESH: if (m_proto->m_nSDBrowseMode == SD_BROWSE_MYAGENTS) { SetDlgItemText(m_hwnd, IDC_COMBO_JID, _T(SD_FAKEJID_MYAGENTS)); SetDlgItemText(m_hwnd, IDC_COMBO_NODE, _T("")); PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); } break; case WM_JABBER_REFRESH: KillTimer(m_hwnd, REFRESH_TIMER); if (GetTickCount() - m_proto->m_dwSDLastRefresh < REFRESH_TIMEOUT) { SetTimer(m_hwnd, REFRESH_TIMER, REFRESH_TIMEOUT, NULL); return TRUE; } wParam = REFRESH_TIMER; // fall through case WM_TIMER: if (wParam == REFRESH_TIMER) { m_proto->m_SDManager.Lock(); CJabberSDNode* pNode = m_proto->m_SDManager.GetPrimaryNode(); while (pNode) { if (pNode->GetJid()) { if ( !pNode->GetTreeItemHandle()) { HTREELISTITEM hNewItem = TreeList_AddItem( GetDlgItem(m_hwnd, IDC_TREE_DISCO), NULL, pNode->GetName() ? pNode->GetName() : pNode->GetJid(), (LPARAM)pNode); TreeList_AppendColumn(hNewItem, pNode->GetJid()); TreeList_AppendColumn(hNewItem, pNode->GetNode()); pNode->SetTreeItemHandle(hNewItem); } } m_proto->SyncTree(NULL, pNode); pNode = pNode->GetNext(); } m_proto->m_SDManager.Unlock(); TreeList_Update(GetDlgItem(m_hwnd, IDC_TREE_DISCO)); KillTimer(m_hwnd, REFRESH_TIMER); m_proto->m_dwSDLastRefresh = GetTickCount(); return TRUE; } else if (wParam == AUTODISCO_TIMER) { HWND hwndList = GetDlgItem(m_hwnd, IDC_TREE_DISCO); RECT rcCtl; GetClientRect(hwndList, &rcCtl); RECT rcHdr; GetClientRect(ListView_GetHeader(hwndList), &rcHdr); LVHITTESTINFO lvhti = {0}; lvhti.pt.x = rcCtl.left + 5; lvhti.pt.y = rcHdr.bottom + 5; int iFirst = ListView_HitTest(hwndList, &lvhti); ZeroMemory(&lvhti, sizeof(lvhti)); lvhti.pt.x = rcCtl.left + 5; lvhti.pt.y = rcCtl.bottom - 5; int iLast = ListView_HitTest(hwndList, &lvhti); if (iFirst < 0) return FALSE; if (iLast < 0) iLast = ListView_GetItemCount(hwndList) - 1; m_proto->m_SDManager.Lock(); XmlNode packet(NULL); for (int i = iFirst; i <= iLast; i++) { LVITEM lvi = {0}; lvi.mask = LVIF_PARAM; lvi.iItem = i; ListView_GetItem(hwndList, &lvi); if ( !lvi.lParam) continue; CJabberSDNode *pNode = (CJabberSDNode *)TreeList_GetData((HTREELISTITEM)lvi.lParam); if ( !pNode || pNode->GetInfoRequestId()) continue; m_proto->SendInfoRequest(pNode, packet); } m_proto->m_SDManager.Unlock(); if (xmlGetChild(packet, 0)) m_proto->m_ThreadInfo->send(packet); KillTimer(m_hwnd, AUTODISCO_TIMER); m_proto->m_dwSDLastRefresh = GetTickCount(); return TRUE; } break; case WM_CONTEXTMENU: if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_TREE_DISCO) { HWND hwndList = (HWND)wParam; POINT pt = { (signed short)LOWORD(lParam), (signed short)HIWORD(lParam) }; if ((pt.x == -1) && (pt.y == -1)) { LVITEM lvi = {0}; lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED); if (lvi.iItem < 0) return FALSE; RECT rc; ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL); pt.x = rc.left; pt.y = rc.bottom; ClientToScreen(hwndList, &pt); } HTREELISTITEM hItem = TreeList_GetActiveItem(hwndList); if ( !hItem) break; CJabberSDNode *pNode = (CJabberSDNode *)TreeList_GetData(hItem); if ( !pNode) break; m_proto->ServiceDiscoveryShowMenu(pNode, hItem, pt); } break; case WM_NOTIFY: if (wParam == IDC_TREE_DISCO) { NMHDR* pHeader = (NMHDR*)lParam; if (pHeader->code == LVN_GETINFOTIP) { NMLVGETINFOTIP *pInfoTip = (NMLVGETINFOTIP *)lParam; LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iItem = pInfoTip->iItem; ListView_GetItem(pHeader->hwndFrom, &lvi); HTREELISTITEM hItem = (HTREELISTITEM)lvi.lParam; m_proto->m_SDManager.Lock(); CJabberSDNode* pNode = (CJabberSDNode*)TreeList_GetData(hItem); if (pNode) { pNode->GetTooltipText(pInfoTip->pszText, pInfoTip->cchTextMax); } m_proto->m_SDManager.Unlock(); } else if (pHeader->code == TVN_ITEMEXPANDED) { NMTREEVIEW *pNmTreeView = (NMTREEVIEW *)lParam; HTREELISTITEM hItem = (HTREELISTITEM)pNmTreeView->itemNew.hItem; m_proto->m_SDManager.Lock(); XmlNode packet(NULL); CJabberSDNode* pNode; pNode = (CJabberSDNode*)TreeList_GetData(hItem); if (pNode) { m_proto->SendBothRequests(pNode, packet); TreeList_MakeFakeParent(hItem, FALSE); } m_proto->m_SDManager.Unlock(); if (xmlGetChild(packet)) m_proto->m_ThreadInfo->send(packet); } else if (pHeader->code == NM_CUSTOMDRAW) { LPNMLVCUSTOMDRAW lpnmlvcd = (LPNMLVCUSTOMDRAW)lParam; if (lpnmlvcd->nmcd.dwDrawStage != CDDS_PREPAINT) return CDRF_DODEFAULT; KillTimer(m_hwnd, AUTODISCO_TIMER); if (GetTickCount() - sttLastAutoDisco < AUTODISCO_TIMEOUT) { SetTimer(m_hwnd, AUTODISCO_TIMER, AUTODISCO_TIMEOUT, NULL); return CDRF_DODEFAULT; } SendMessage(m_hwnd, WM_TIMER, AUTODISCO_TIMER, 0); return CDRF_DODEFAULT; } return TRUE; } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: { HWND hwndFocus = GetFocus(); if ( !hwndFocus) return TRUE; if (GetWindowLongPtr(hwndFocus, GWL_ID) == IDC_TXT_FILTERTEXT) PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BTN_FILTERAPPLY, 0), 0); else if (m_hwnd == (hwndFocus = GetParent(hwndFocus))) break; else if ((GetWindowLongPtr(hwndFocus, GWL_ID) == IDC_COMBO_NODE) || (GetWindowLongPtr(hwndFocus, GWL_ID) == IDC_COMBO_JID)) PostMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); return TRUE; } case IDCANCEL: { PostMessage(m_hwnd, WM_CLOSE, 0, 0); return TRUE; } } break; case WM_MEASUREITEM: return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); case WM_DRAWITEM: return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam); } return CSuper::DlgProc(msg, wParam, lParam); } // extern references to used functions: void SearchAddToRecent(TCHAR* szAddr, HWND hwndDialog = NULL); void CJabberProto::ServiceDiscoveryShowMenu(CJabberSDNode *pNode, HTREELISTITEM hItem, POINT pt) { //ClientToScreen(GetDlgItem(hwndServiceDiscovery, IDC_TREE_DISCO), &pt); enum { // This values are below CLISTMENUIDMAX and won't overlap SD_ACT_REFRESH = 1, SD_ACT_REFRESHCHILDREN, SD_ACT_FAVORITE, SD_ACT_ROSTER, SD_ACT_COPYJID, SD_ACT_COPYNODE, SD_ACT_USERMENU, SD_ACT_COPYINFO, SD_ACT_LOGON = 100, SD_ACT_LOGOFF, SD_ACT_UNREGISTER, SD_ACT_REGISTER = 200, SD_ACT_ADHOC, SD_ACT_ADDDIRECTORY, SD_ACT_JOIN, SD_ACT_BOOKMARK, SD_ACT_PROXY, SD_ACT_VCARD }; enum { SD_FLG_NONODE = 0x001, SD_FLG_NOTONROSTER = 0x002, SD_FLG_ONROSTER = 0x004, SD_FLG_SUBSCRIBED = 0x008, SD_FLG_NOTSUBSCRIBED = 0x010, SD_FLG_ONLINE = 0x020, SD_FLG_NOTONLINE = 0x040, SD_FLG_NORESOURCE = 0x080, SD_FLG_HASUSER = 0x100 }; static struct { TCHAR *feature; TCHAR *title; int action; DWORD flags; } items[] = { {NULL, _T("Contact Menu..."), SD_ACT_USERMENU, SD_FLG_NONODE}, {NULL, _T("View vCard"), SD_ACT_VCARD, SD_FLG_NONODE}, {_T(JABBER_FEAT_MUC), _T("Join chatroom"), SD_ACT_JOIN, SD_FLG_NORESOURCE}, {0}, {NULL, _T("Refresh Info"), SD_ACT_REFRESH}, {NULL, _T("Refresh Children"), SD_ACT_REFRESHCHILDREN}, {0}, {NULL, _T("Add to favorites"), SD_ACT_FAVORITE}, {NULL, _T("Add to roster"), SD_ACT_ROSTER, SD_FLG_NONODE|SD_FLG_NOTONROSTER}, {_T(JABBER_FEAT_MUC), _T("Bookmark chatroom"), SD_ACT_BOOKMARK, SD_FLG_NORESOURCE|SD_FLG_HASUSER}, {_T("jabber:iq:search"), _T("Add search directory"), SD_ACT_ADDDIRECTORY}, {_T(JABBER_FEAT_BYTESTREAMS), _T("Use this proxy"), SD_ACT_PROXY}, {0}, {_T(JABBER_FEAT_REGISTER), _T("Register"), SD_ACT_REGISTER}, {_T("jabber:iq:gateway"), _T("Unregister"), SD_ACT_UNREGISTER, SD_FLG_ONROSTER|SD_FLG_SUBSCRIBED}, {_T(JABBER_FEAT_COMMANDS), _T("Commands..."), SD_ACT_ADHOC}, {0}, {_T("jabber:iq:gateway"), _T("Logon"), SD_ACT_LOGON, SD_FLG_ONROSTER|SD_FLG_SUBSCRIBED|SD_FLG_ONLINE}, {_T("jabber:iq:gateway"), _T("Logoff"), SD_ACT_LOGOFF, SD_FLG_ONROSTER|SD_FLG_SUBSCRIBED|SD_FLG_NOTONLINE}, {0}, {NULL, _T("Copy JID"), SD_ACT_COPYJID}, {NULL, _T("Copy node name"), SD_ACT_COPYNODE}, {NULL, _T("Copy node information"),SD_ACT_COPYINFO}, }; HMENU hMenu = CreatePopupMenu(); BOOL lastSeparator = TRUE; bool bFilterItems = !GetAsyncKeyState(VK_CONTROL); for (int i = 0; i < SIZEOF(items); i++) { JABBER_LIST_ITEM *rosterItem = NULL; if (bFilterItems) { if ((items[i].flags&SD_FLG_NONODE) && pNode->GetNode()) continue; if ((items[i].flags&SD_FLG_NOTONROSTER) && (rosterItem = ListGetItemPtr(LIST_ROSTER, pNode->GetJid()))) continue; if ((items[i].flags&SD_FLG_ONROSTER) && !(rosterItem = ListGetItemPtr(LIST_ROSTER, pNode->GetJid()))) continue; if ((items[i].flags&SD_FLG_SUBSCRIBED) && (!rosterItem || (rosterItem->subscription == SUB_NONE))) continue; if ((items[i].flags&SD_FLG_NOTSUBSCRIBED) && (rosterItem && (rosterItem->subscription != SUB_NONE))) continue; if ((items[i].flags&SD_FLG_ONLINE) && rosterItem && (rosterItem->itemResource.status != ID_STATUS_OFFLINE)) continue; if ((items[i].flags&SD_FLG_NOTONLINE) && rosterItem && (rosterItem->itemResource.status == ID_STATUS_OFFLINE)) continue; if ((items[i].flags&SD_FLG_NORESOURCE) && _tcschr(pNode->GetJid(), _T('/'))) continue; if ((items[i].flags&SD_FLG_HASUSER) && !_tcschr(pNode->GetJid(), _T('@'))) continue; } if ( !items[i].feature) { if (items[i].title) { HANDLE hContact; if ((items[i].action == SD_ACT_USERMENU) && (hContact = HContactFromJID(pNode->GetJid()))) { HMENU hContactMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0); AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT_PTR)hContactMenu, TranslateTS(items[i].title)); } else AppendMenu(hMenu, MF_STRING, items[i].action, TranslateTS(items[i].title)); lastSeparator = FALSE; } else if ( !lastSeparator) { AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); lastSeparator = TRUE; } continue; } bool bFeatureOk = !bFilterItems; if (bFilterItems) for (CJabberSDFeature *iFeature = pNode->GetFirstFeature(); iFeature; iFeature = iFeature->GetNext()) if ( !lstrcmp(iFeature->GetVar(), items[i].feature)) { bFeatureOk = true; break; } if (bFeatureOk) { if (items[i].title) { AppendMenu(hMenu, MF_STRING, items[i].action, TranslateTS(items[i].title)); lastSeparator = FALSE; } else if ( !lastSeparator) { AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); lastSeparator = TRUE; } } } if ( !GetMenuItemCount(hMenu)) { DestroyMenu(hMenu); return; } int res = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlgServiceDiscovery->GetHwnd(), NULL); DestroyMenu(hMenu); switch (res) { case SD_ACT_REFRESH: { m_SDManager.Lock(); XmlNode packet(NULL); if (pNode) { TreeList_ResetItem(GetDlgItem(m_pDlgServiceDiscovery->GetHwnd(), IDC_TREE_DISCO), hItem); pNode->ResetInfo(); SendBothRequests(pNode, packet); TreeList_MakeFakeParent(hItem, FALSE); } m_SDManager.Unlock(); if (xmlGetChild(packet)) m_ThreadInfo->send(packet); break; } case SD_ACT_REFRESHCHILDREN: { m_SDManager.Lock(); XmlNode packet(NULL); for (int iChild = TreeList_GetChildrenCount(hItem); iChild--;) { HTREELISTITEM hNode = TreeList_GetChild(hItem, iChild); CJabberSDNode *pNode = (CJabberSDNode *)TreeList_GetData(hNode); if (pNode) { TreeList_ResetItem(GetDlgItem(m_pDlgServiceDiscovery->GetHwnd(), IDC_TREE_DISCO), hNode); pNode->ResetInfo(); SendBothRequests(pNode, packet); TreeList_MakeFakeParent(hNode, FALSE); } if (xmlGetChildCount(packet) > 50) { m_ThreadInfo->send(packet); packet = XmlNode(NULL); } } m_SDManager.Unlock(); if (xmlGetChildCount(packet)) m_ThreadInfo->send(packet); break; } case SD_ACT_COPYJID: JabberCopyText(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetJid()); break; case SD_ACT_COPYNODE: JabberCopyText(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetNode()); break; case SD_ACT_COPYINFO: { TCHAR buf[8192]; pNode->GetTooltipText(buf, SIZEOF(buf)); JabberCopyText(m_pDlgServiceDiscovery->GetHwnd(), buf); break; } case SD_ACT_FAVORITE: { char setting[MAXMODULELABELLENGTH]; int count = JGetDword(NULL, "discoWnd_favCount", 0); mir_snprintf(setting, sizeof(setting), "discoWnd_favName_%d", count); JSetStringT(NULL, setting, pNode->GetName() ? pNode->GetName() : pNode->GetJid()); mir_snprintf(setting, sizeof(setting), "discoWnd_favJID_%d", count); JSetStringT(NULL, setting, pNode->GetJid()); mir_snprintf(setting, sizeof(setting), "discoWnd_favNode_%d", count); JSetStringT(NULL, setting, pNode->GetNode() ? pNode->GetNode() : _T("")); JSetDword(NULL, "discoWnd_favCount", ++count); break; } case SD_ACT_REGISTER: RegisterAgent(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetJid()); break; case SD_ACT_ADHOC: ContactMenuAdhocCommands(new CJabberAdhocStartupParams(this, pNode->GetJid(), pNode->GetNode())); break; case SD_ACT_ADDDIRECTORY: SearchAddToRecent(pNode->GetJid()); break; case SD_ACT_PROXY: m_options.BsDirect = FALSE; m_options.BsProxyManual = TRUE; JSetStringT(NULL, "BsProxyServer", pNode->GetJid()); break; case SD_ACT_JOIN: if (jabberChatDllPresent) GroupchatJoinRoomByJid(m_pDlgServiceDiscovery->GetHwnd(), pNode->GetJid()); else JabberChatDllError(); break; case SD_ACT_BOOKMARK: { JABBER_LIST_ITEM* item = ListGetItemPtr(LIST_BOOKMARK, pNode->GetJid()); if (item == NULL) { item = ListGetItemPtr(LIST_BOOKMARK, pNode->GetJid()); if (item == NULL) { item = ListAdd(LIST_ROOM, pNode->GetJid()); item->name = mir_tstrdup(pNode->GetName()); } if (item != NULL) { item->type = _T("conference"); AddEditBookmark(item); } } break; } case SD_ACT_USERMENU: { HANDLE hContact = HContactFromJID(pNode->GetJid()); if ( !hContact) { hContact = DBCreateContact(pNode->GetJid(), pNode->GetName(), TRUE, FALSE); JABBER_LIST_ITEM* item = ListAdd(LIST_VCARD_TEMP, pNode->GetJid()); item->bUseResource = TRUE; } HMENU hContactMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0); GetCursorPos(&pt); int res = TrackPopupMenu(hContactMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlgServiceDiscovery->GetHwnd(), NULL); CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, MPCF_CONTACTMENU), (LPARAM)hContact); break; } case SD_ACT_VCARD: { TCHAR * jid = pNode->GetJid(); HANDLE hContact = HContactFromJID(pNode->GetJid()); if ( !hContact) { JABBER_SEARCH_RESULT jsr={0}; mir_sntprintf(jsr.jid, SIZEOF(jsr.jid), _T("%s"), jid); jsr.hdr.cbSize = sizeof(JABBER_SEARCH_RESULT); hContact = (HANDLE)CallProtoService(m_szModuleName, PS_ADDTOLIST, PALF_TEMPORARY, (LPARAM)&jsr); } if (ListGetItemPtr(LIST_VCARD_TEMP, pNode->GetJid()) == NULL) { JABBER_LIST_ITEM* item = ListAdd(LIST_VCARD_TEMP, pNode->GetJid()); item->bUseResource = TRUE; if (item->resource == NULL) ListAddResource(LIST_VCARD_TEMP, jid, ID_STATUS_OFFLINE, NULL, 0); } CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)hContact, 0); break; } case SD_ACT_ROSTER: { HANDLE hContact = DBCreateContact(pNode->GetJid(), pNode->GetName(), FALSE, FALSE); db_unset(hContact, "CList", "NotOnList"); JABBER_LIST_ITEM* item = ListAdd(LIST_VCARD_TEMP, pNode->GetJid()); item->bUseResource = TRUE; break; } case SD_ACT_LOGON: case SD_ACT_LOGOFF: m_ThreadInfo->send(XmlNode(_T("presence")) << XATTR(_T("to"), pNode->GetJid()) << XATTR(_T("type"), (res != SD_ACT_LOGON) ? _T("unavailable") : NULL)); break; case SD_ACT_UNREGISTER: m_ThreadInfo->send( XmlNodeIq(_T("set"), SerialNext(), pNode->GetJid()) << XQUERY(_T(JABBER_FEAT_REGISTER)) << XCHILD(_T("remove"))); m_ThreadInfo->send( XmlNodeIq(_T("set"), SerialNext()) << XQUERY(_T(JABBER_FEAT_IQ_ROSTER)) << XCHILD(_T("item")) << XATTR(_T("jid"), pNode->GetJid()) << XATTR(_T("subscription"), _T("remove"))); break; default: if ((res >= CLISTMENUIDMIN) && (res <= CLISTMENUIDMAX)) { HANDLE hContact = HContactFromJID(pNode->GetJid()); if (hContact) CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(res, MPCF_CONTACTMENU), (LPARAM)hContact); } break; } } void CJabberProto::LaunchServiceDiscovery(TCHAR *jid) { if (m_pDlgServiceDiscovery) { SetForegroundWindow(m_pDlgServiceDiscovery->GetHwnd()); if (jid) { SetDlgItemText(m_pDlgServiceDiscovery->GetHwnd(), IDC_COMBO_JID, jid); SetDlgItemTextA(m_pDlgServiceDiscovery->GetHwnd(), IDC_COMBO_NODE, ""); PostMessage(m_pDlgServiceDiscovery->GetHwnd(), WM_COMMAND, MAKEWPARAM(IDC_BUTTON_BROWSE, 0), 0); } } else { m_pDlgServiceDiscovery = new CJabberDlgDiscovery(this, jid); m_pDlgServiceDiscovery->Show(); } } INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscovery(WPARAM, LPARAM) { LaunchServiceDiscovery(NULL); return 0; } INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscoveryMyTransports(WPARAM, LPARAM) { LaunchServiceDiscovery(_T(SD_FAKEJID_MYAGENTS)); return 0; } INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscoveryTransports(WPARAM, LPARAM) { LaunchServiceDiscovery(_T(SD_FAKEJID_AGENTS)); return 0; } INT_PTR __cdecl CJabberProto::OnMenuHandleServiceDiscoveryConferences(WPARAM, LPARAM) { LaunchServiceDiscovery(_T(SD_FAKEJID_CONFERENCES)); return 0; }