summaryrefslogtreecommitdiff
path: root/protocols/JabberG/src/jabber_search.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/JabberG/src/jabber_search.cpp')
-rw-r--r--protocols/JabberG/src/jabber_search.cpp1530
1 files changed, 765 insertions, 765 deletions
diff --git a/protocols/JabberG/src/jabber_search.cpp b/protocols/JabberG/src/jabber_search.cpp
index 8beaf9fb7b..5102211399 100644
--- a/protocols/JabberG/src/jabber_search.cpp
+++ b/protocols/JabberG/src/jabber_search.cpp
@@ -1,765 +1,765 @@
-/*
-
-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-22 Miranda NG team
-
-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 "stdafx.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, nullptr, &(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), nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
- dat->curPos = pos;
- }
- }
-
- // Transmit focus set notification to parent window
- if (HIWORD(wParam) == EN_SETFOCUS)
- 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, L"STATIC", (const wchar_t *)TranslateW(FieldDat->Label), WS_CHILD, CornerX, CornerY + Order * 40, width, 13, hwndParent, nullptr, g_plugin.getInst(), nullptr);
- HWND hwndVar = CreateWindowEx(0 | WS_EX_CLIENTEDGE, L"EDIT", FieldDat->defValue, WS_CHILD | WS_TABSTOP, CornerX + 5, CornerY + Order * 40 + 14, width, 20, hwndParent, nullptr, g_plugin.getInst(), nullptr);
- 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 = wcsdup(FieldDat->Label);
- dat->pJSInf[dat->nJSInfCount].szFieldName = wcsdup(FieldDat->Var);
- dat->nJSInfCount++;
- }
- return CornerY + Order * 40 + 14 + 20;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Available search field request result handler (XEP-0055. Examples 2, 7)
-
-void CJabberProto::OnIqResultGetSearchFields(const TiXmlElement *iqNode, CJabberIqInfo*)
-{
- if (!searchHandleDlg)
- return;
-
- const char *type = XmlGetAttr(iqNode, "type");
- if (type == nullptr)
- return;
-
- if (!mir_strcmp(type, "result")) {
- auto *queryNode = XmlFirstChild(iqNode, "query");
- auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
-
- ShowWindow(searchHandleDlg, SW_HIDE);
- if (xNode) {
- // 1. Form
- PostMessage(searchHandleDlg, WM_USER + 11, (WPARAM)xNode, 0);
- auto *xcNode = XmlFirstChild(xNode, "instructions");
- if (xcNode)
- SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, xcNode->GetText());
- }
- else {
- int Order = 0;
- for (auto *chNode : TiXmlEnum(queryNode)) {
- if (!mir_strcmpi(chNode->Name(), "instructions") && chNode->GetText())
- SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateW(Utf2T(chNode->GetText())));
- else if (chNode->Name()) {
- Data *MyData = (Data*)malloc(sizeof(Data));
- memset(MyData, 0, sizeof(Data));
-
- MyData->Label = mir_utf8decodeW(chNode->Name());
- MyData->Var = mir_utf8decodeW(chNode->Name());
- MyData->defValue = mir_utf8decodeW(chNode->GetText());
- MyData->Order = Order;
- if (MyData->defValue)
- MyData->bReadOnly = true;
- PostMessage(searchHandleDlg, WM_USER + 10, FALSE, (LPARAM)MyData);
- Order++;
- }
- }
- }
-
- const char *szFrom = XmlGetAttr(iqNode, "from");
- if (szFrom)
- SearchAddToRecent(szFrom, searchHandleDlg);
- PostMessage(searchHandleDlg, WM_USER + 10, 0, 0);
- ShowWindow(searchHandleDlg, SW_SHOW);
- }
- else if (!mir_strcmp(type, "error")) {
- const char *code = "";
- const char *description = "";
- auto *errorNode = XmlFirstChild(iqNode, "error");
- if (errorNode) {
- code = XmlGetAttr(errorNode, "code");
- description = errorNode->GetText();
- }
-
- char buff[255];
- mir_snprintf(buff, TranslateU("Error %s %s\r\nPlease select other server"), code, description);
- SetDlgItemTextUtf(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
-
-static char *nickfields[] = { "nick", "nickname", "fullname", "name", "given", "first", "jid", nullptr };
-
-static int TCharKeyCmp(const char *p1, const char *p2)
-{
- return mir_strcmpi(p1, p2);
-}
-
-static void SearchReturnResults(CJabberProto *ppro, HANDLE id, LIST<UNIQUE_MAP> &plUsersInfo, UNIQUE_MAP &pmAllFields)
-{
- LIST<char> ListOfNonEmptyFields(20, TCharKeyCmp);
- LIST<char> ListOfFields(20);
-
- // lets fill the ListOfNonEmptyFields but in users order
- for (auto &pmUserData : plUsersInfo) {
- int nUserFields = pmUserData->getCount();
- for (int j = 0; j < nUserFields; j++) {
- char *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 (int i = 0; i < nAllCount; i++) {
- char *var = pmAllFields.getUnOrderedKeyName(i);
- if (var && ListOfNonEmptyFields.getIndex(var) < 0)
- continue;
- ListOfFields.insert(var);
- }
-
- // now lets transfer field names
- int nFieldCount = ListOfFields.getCount();
-
- CUSTOMSEARCHRESULTS Results = { 0 };
- Results.nSize = sizeof(Results);
- Results.pszFields = (wchar_t**)mir_alloc(sizeof(wchar_t*)*nFieldCount);
- Results.nFieldCount = nFieldCount;
-
- // Sending Columns Titles
- for (int i = 0; i < nFieldCount; i++) {
- char *var = ListOfFields[i];
- if (var)
- Results.pszFields[i] = mir_utf8decodeW(pmAllFields[var]);
- }
-
- Results.psr.cbSize = 0; // sending column names
- ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results);
- for (int i = 0; i < nFieldCount; i++)
- replaceStrW(Results.pszFields[i], nullptr);
-
- // Sending Users Data
- Results.psr.cbSize = sizeof(Results.psr);
-
- for (auto &pmUserData : plUsersInfo) {
- for (int j = 0; j < nFieldCount; j++) {
- char *var = ListOfFields[j];
- char *value = pmUserData->operator [](var);
- Results.pszFields[j] = value ? mir_utf8decodeW(value) : mir_wstrdup(L" ");
- if (!mir_strcmpi(var, "jid") && value)
- Results.psr.id.w = Results.pszFields[j];
- }
-
- const char *nick = nullptr;
- for (int k = 0; k < _countof(nickfields) && !nick; k++)
- nick = pmUserData->operator [](nickfields[k]);
-
- if (nick) {
- Utf2T wszNick(nick);
- wchar_t buff[200];
- if (mir_wstrcmpi(wszNick, Results.psr.id.w))
- mir_snwprintf(buff, L"%s (%s)", wszNick.get(), Results.psr.id.w);
- else
- wcsncpy_s(buff, wszNick, _TRUNCATE);
-
- Results.psr.nick.w = buff;
- }
- else Results.psr.nick.w = L"";
- Results.psr.flags = PSR_UNICODE;
-
- ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results);
- for (int i = 0; i < nFieldCount; i++)
- replaceStrW(Results.pszFields[i], nullptr);
- }
-
- mir_free(Results.pszFields);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Search field request result handler (XEP-0055. Examples 3, 8)
-
-void CJabberProto::OnIqResultAdvancedSearch(const TiXmlElement *iqNode, CJabberIqInfo*)
-{
- const char *type;
- int id;
-
- UNIQUE_MAP mColumnsNames(10);
- LIST<UNIQUE_MAP> SearchResults(2);
-
- if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = XmlGetAttr(iqNode, "type")) == nullptr)) {
- ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
- return;
- }
-
- if (!mir_strcmp(type, "result")) {
- auto *queryNode = XmlFirstChild(iqNode, "query");
- auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
- if (xNode) {
- // 1. Form search results info
- for (auto *fieldNode : TiXmlFilter(XmlFirstChild(xNode, "reported"), "field")) {
- auto *var = XmlGetAttr(fieldNode, "var");
- if (var) {
- auto *label = XmlGetAttr(fieldNode, "label");
- mColumnsNames.insert(var, (label != nullptr) ? label : var);
- }
- }
-
- for (auto *itemNode : TiXmlFilter(xNode, "item")) {
- UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10);
- for (auto *fieldNode : TiXmlFilter(itemNode, "field")) {
- if (auto *var = XmlGetAttr(fieldNode, "var")) {
- if (auto *textNode = XmlFirstChild(fieldNode, "value")) {
- if (!mColumnsNames[var])
- mColumnsNames.insert(var, var);
- pUserColumn->insert(var, textNode->GetText());
- }
- }
- }
-
- SearchResults.insert(pUserColumn);
- }
- }
- else {
- // 2. Field list search results info
- for (auto *itemNode : TiXmlFilter(queryNode, "item")) {
- UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10);
-
- auto *jid = XmlGetAttr(itemNode, "jid");
- char *keyReturned;
- mColumnsNames.insertCopyKey("jid", "jid", &keyReturned);
- mColumnsNames.insert("jid", keyReturned);
- pUserColumn->insertCopyKey("jid", jid, nullptr);
-
- for (auto *child : TiXmlEnum(itemNode)) {
- const char *szColumnName = child->Name();
- if (szColumnName) {
- const char *pszChild = child->GetText();
- if (pszChild && *pszChild) {
- mColumnsNames.insertCopyKey(szColumnName, "", &keyReturned);
- mColumnsNames.insert(szColumnName, keyReturned);
- pUserColumn->insertCopyKey(szColumnName, pszChild, nullptr);
- }
- }
- }
-
- SearchResults.insert(pUserColumn);
- }
- }
- }
- else if (!mir_strcmp(type, "error")) {
- const char *code = "";
- const char *description = "";
- auto *errorNode = XmlFirstChild(iqNode, "error");
- if (errorNode) {
- code = XmlGetAttr(errorNode, "code");
- description = errorNode->GetText();
- }
-
- ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
-
- char buff[255];
- mir_snprintf(buff, TranslateU("Error %s %s\r\nTry to specify more detailed"), code, description);
- if (searchHandleDlg)
- SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff);
- else
- MessageBox(nullptr, Utf2T(buff), TranslateT("Search error"), MB_OK | MB_ICONSTOP);
- return;
- }
-
- SearchReturnResults(this, (HANDLE)id, SearchResults, mColumnsNames);
-
- for (auto &it : SearchResults)
- delete ((UNIQUE_MAP*)it);
-
- //send success to finish searching
- ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
-}
-
-static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM)
-{
- DestroyWindow(hwnd);
- return TRUE;
-}
-
-static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat)
-{
- 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 = nullptr;
- }
- else EnumChildWindows(GetDlgItem(hwndDlg, IDC_FRAME), DeleteChildWindowsProc, 0);
-
- SendDlgItemMessage(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>"));
-}
-
-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)
-{
- wchar_t szServerName[100];
- EnableWindow(GetDlgItem(hwndDlg, IDC_GO), FALSE);
- GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(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, T2Utf(szServerName));
- m_ThreadInfo->send(XmlNodeIq(pInfo) << XQUERY(JABBER_FEAT_JUD));
- return pInfo->GetIqId();
-}
-
-static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const wchar_t *szAddr)
-{
- int lResult = SendDlgItemMessage(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 char *szAddr, bool deleteLastFromDB)
-{
- // search in recent
- for (int i = 0; i < 10; i++) {
- char key[30];
- mir_snprintf(key, "RecentlySearched_%d", i);
- ptrA szValue(getUStringA(key));
- if (szValue == nullptr || mir_strcmpi(szAddr, szValue))
- continue;
-
- for (int j = i; j < 10; j++) {
- mir_snprintf(key, "RecentlySearched_%d", j + 1);
- szValue = getUStringA(key);
- if (szValue != nullptr) {
- mir_snprintf(key, "RecentlySearched_%d", j);
- setUString(0, key, szValue);
- }
- else {
- if (deleteLastFromDB) {
- mir_snprintf(key, "RecentlySearched_%d", j);
- delSetting(0, key);
- }
- break;
- }
- }
- break;
- }
-}
-
-void CJabberProto::SearchAddToRecent(const char *szAddr, HWND hwndDialog)
-{
- char key[30];
- SearchDeleteFromRecent(szAddr, true);
-
- for (int j = 9; j > 0; j--) {
- mir_snprintf(key, "RecentlySearched_%d", j - 1);
- ptrW szValue(getWStringA(key));
- if (szValue != nullptr) {
- mir_snprintf(key, "RecentlySearched_%d", j);
- setWString(0, key, szValue);
- }
- }
-
- mir_snprintf(key, "RecentlySearched_%d", 0);
- setUString(key, szAddr);
- if (hwndDialog)
- JabberSearchAddUrlToRecentCombo(hwndDialog, Utf2T(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 = new JabberSearchData();
- dat->ppro = (CJabberProto *)lParam;
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
-
- /* Server Combo box */
- ptrA jud(dat->ppro->getStringA("Jud"));
- if (jud != nullptr) {
- SetDlgItemTextA(hwndDlg, IDC_SERVER, jud);
- SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, jud);
- }
-
- //TO DO: Add Transports here
- for (auto &it : dat->ppro->m_lstTransports)
- if (it != nullptr)
- JabberSearchAddUrlToRecentCombo(hwndDlg, Utf2T(it));
-
- for (int i = 0; i < 10; i++) {
- char key[30];
- mir_snprintf(key, "RecentlySearched_%d", i);
- ptrW szValue(dat->ppro->getWStringA(key));
- if (szValue != nullptr)
- 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), nullptr, 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), nullptr, 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;
- if (dat->xNode) {
- dat->doc.DeleteNode(dat->xNode);
- dat->xNode = nullptr;
- }
- TiXmlElement *pNode = (TiXmlElement *)wParam;
- if (pNode) {
- dat->xNode = pNode->DeepClone(&dat->doc)->ToElement();
- 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, nullptr, &(dat->frameRect));
- SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
- dat->curPos = 0;
- }
- }
- return TRUE;
-
- case WM_MOUSEWHEEL:
- {
- short 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 != nullptr) {
- 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, nullptr, &(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), nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN);
- dat->curPos = pos;
- }
- }
- }
- return TRUE;
-
- case WM_DESTROY:
- JabberSearchFreeData(hwndDlg, dat);
- JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME));
- delete dat;
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- return TRUE;
- }
- return FALSE;
-}
-
-HWND CJabberProto::CreateExtendedSearchUI(HWND parent)
-{
- if (parent && g_plugin.getInst()) {
- ptrW szServer(getWStringA("LoginServer"));
- if (szServer == nullptr || mir_wstrcmpi(szServer, L"S.ms"))
- return CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this);
- }
-
- return nullptr; // Failure
-}
-
-//////////////////////////////////////////////////////////////////////////
-// The function formats request to server
-
-HWND CJabberProto::SearchAdvanced(HWND hwndDlg)
-{
- if (!m_bJabberOnline || !hwndDlg)
- return nullptr; //error
-
- JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
- if (!dat)
- return nullptr; //error
-
- // check if server connected (at least one field exists)
- if (dat->nJSInfCount == 0)
- return nullptr;
-
- // formating request
- bool fRequestNotEmpty = false;
-
- // get server name
- wchar_t szServerName[100];
- GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName));
-
- // formating query
- CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, T2Utf(szServerName));
- XmlNodeIq iq(pInfo);
- TiXmlElement *query = iq << XQUERY(JABBER_FEAT_JUD);
-
- if (m_tszSelectedLang)
- iq << XATTR("xml:lang", m_tszSelectedLang); // i'm sure :)
-
- // next can be 2 cases:
- // Forms: XEP-0055 Example 7
- if (dat->fSearchRequestIsXForm) {
- fRequestNotEmpty = true;
- JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), query, dat->xNode);
- }
- else { //and Simple fields: XEP-0055 Example 3
- for (int i = 0; i < dat->nJSInfCount; i++) {
- wchar_t szFieldValue[100];
- GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, _countof(szFieldValue));
- if (szFieldValue[0] != 0) {
- XmlAddChildA(query, T2Utf(dat->pJSInf[i].szFieldName).get(), T2Utf(szFieldValue).get());
- fRequestNotEmpty = true;
- }
- }
- }
-
- if (fRequestNotEmpty) {
- m_ThreadInfo->send(iq);
- return (HWND)pInfo->GetIqId();
- }
- return nullptr;
-}
+/*
+
+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-23 Miranda NG team
+
+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 "stdafx.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, nullptr, &(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), nullptr, nullptr, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ }
+ }
+
+ // Transmit focus set notification to parent window
+ if (HIWORD(wParam) == EN_SETFOCUS)
+ 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, L"STATIC", (const wchar_t *)TranslateW(FieldDat->Label), WS_CHILD, CornerX, CornerY + Order * 40, width, 13, hwndParent, nullptr, g_plugin.getInst(), nullptr);
+ HWND hwndVar = CreateWindowEx(0 | WS_EX_CLIENTEDGE, L"EDIT", FieldDat->defValue, WS_CHILD | WS_TABSTOP, CornerX + 5, CornerY + Order * 40 + 14, width, 20, hwndParent, nullptr, g_plugin.getInst(), nullptr);
+ 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 = wcsdup(FieldDat->Label);
+ dat->pJSInf[dat->nJSInfCount].szFieldName = wcsdup(FieldDat->Var);
+ dat->nJSInfCount++;
+ }
+ return CornerY + Order * 40 + 14 + 20;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Available search field request result handler (XEP-0055. Examples 2, 7)
+
+void CJabberProto::OnIqResultGetSearchFields(const TiXmlElement *iqNode, CJabberIqInfo*)
+{
+ if (!searchHandleDlg)
+ return;
+
+ const char *type = XmlGetAttr(iqNode, "type");
+ if (type == nullptr)
+ return;
+
+ if (!mir_strcmp(type, "result")) {
+ auto *queryNode = XmlFirstChild(iqNode, "query");
+ auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
+
+ ShowWindow(searchHandleDlg, SW_HIDE);
+ if (xNode) {
+ // 1. Form
+ PostMessage(searchHandleDlg, WM_USER + 11, (WPARAM)xNode, 0);
+ auto *xcNode = XmlFirstChild(xNode, "instructions");
+ if (xcNode)
+ SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, xcNode->GetText());
+ }
+ else {
+ int Order = 0;
+ for (auto *chNode : TiXmlEnum(queryNode)) {
+ if (!mir_strcmpi(chNode->Name(), "instructions") && chNode->GetText())
+ SetDlgItemText(searchHandleDlg, IDC_INSTRUCTIONS, TranslateW(Utf2T(chNode->GetText())));
+ else if (chNode->Name()) {
+ Data *MyData = (Data*)malloc(sizeof(Data));
+ memset(MyData, 0, sizeof(Data));
+
+ MyData->Label = mir_utf8decodeW(chNode->Name());
+ MyData->Var = mir_utf8decodeW(chNode->Name());
+ MyData->defValue = mir_utf8decodeW(chNode->GetText());
+ MyData->Order = Order;
+ if (MyData->defValue)
+ MyData->bReadOnly = true;
+ PostMessage(searchHandleDlg, WM_USER + 10, FALSE, (LPARAM)MyData);
+ Order++;
+ }
+ }
+ }
+
+ const char *szFrom = XmlGetAttr(iqNode, "from");
+ if (szFrom)
+ SearchAddToRecent(szFrom, searchHandleDlg);
+ PostMessage(searchHandleDlg, WM_USER + 10, 0, 0);
+ ShowWindow(searchHandleDlg, SW_SHOW);
+ }
+ else if (!mir_strcmp(type, "error")) {
+ const char *code = "";
+ const char *description = "";
+ auto *errorNode = XmlFirstChild(iqNode, "error");
+ if (errorNode) {
+ code = XmlGetAttr(errorNode, "code");
+ description = errorNode->GetText();
+ }
+
+ char buff[255];
+ mir_snprintf(buff, TranslateU("Error %s %s\r\nPlease select other server"), code, description);
+ SetDlgItemTextUtf(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
+
+static char *nickfields[] = { "nick", "nickname", "fullname", "name", "given", "first", "jid", nullptr };
+
+static int TCharKeyCmp(const char *p1, const char *p2)
+{
+ return mir_strcmpi(p1, p2);
+}
+
+static void SearchReturnResults(CJabberProto *ppro, HANDLE id, LIST<UNIQUE_MAP> &plUsersInfo, UNIQUE_MAP &pmAllFields)
+{
+ LIST<char> ListOfNonEmptyFields(20, TCharKeyCmp);
+ LIST<char> ListOfFields(20);
+
+ // lets fill the ListOfNonEmptyFields but in users order
+ for (auto &pmUserData : plUsersInfo) {
+ int nUserFields = pmUserData->getCount();
+ for (int j = 0; j < nUserFields; j++) {
+ char *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 (int i = 0; i < nAllCount; i++) {
+ char *var = pmAllFields.getUnOrderedKeyName(i);
+ if (var && ListOfNonEmptyFields.getIndex(var) < 0)
+ continue;
+ ListOfFields.insert(var);
+ }
+
+ // now lets transfer field names
+ int nFieldCount = ListOfFields.getCount();
+
+ CUSTOMSEARCHRESULTS Results = { 0 };
+ Results.nSize = sizeof(Results);
+ Results.pszFields = (wchar_t**)mir_alloc(sizeof(wchar_t*)*nFieldCount);
+ Results.nFieldCount = nFieldCount;
+
+ // Sending Columns Titles
+ for (int i = 0; i < nFieldCount; i++) {
+ char *var = ListOfFields[i];
+ if (var)
+ Results.pszFields[i] = mir_utf8decodeW(pmAllFields[var]);
+ }
+
+ Results.psr.cbSize = 0; // sending column names
+ ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results);
+ for (int i = 0; i < nFieldCount; i++)
+ replaceStrW(Results.pszFields[i], nullptr);
+
+ // Sending Users Data
+ Results.psr.cbSize = sizeof(Results.psr);
+
+ for (auto &pmUserData : plUsersInfo) {
+ for (int j = 0; j < nFieldCount; j++) {
+ char *var = ListOfFields[j];
+ char *value = pmUserData->operator [](var);
+ Results.pszFields[j] = value ? mir_utf8decodeW(value) : mir_wstrdup(L" ");
+ if (!mir_strcmpi(var, "jid") && value)
+ Results.psr.id.w = Results.pszFields[j];
+ }
+
+ const char *nick = nullptr;
+ for (int k = 0; k < _countof(nickfields) && !nick; k++)
+ nick = pmUserData->operator [](nickfields[k]);
+
+ if (nick) {
+ Utf2T wszNick(nick);
+ wchar_t buff[200];
+ if (mir_wstrcmpi(wszNick, Results.psr.id.w))
+ mir_snwprintf(buff, L"%s (%s)", wszNick.get(), Results.psr.id.w);
+ else
+ wcsncpy_s(buff, wszNick, _TRUNCATE);
+
+ Results.psr.nick.w = buff;
+ }
+ else Results.psr.nick.w = L"";
+ Results.psr.flags = PSR_UNICODE;
+
+ ppro->ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM)&Results);
+ for (int i = 0; i < nFieldCount; i++)
+ replaceStrW(Results.pszFields[i], nullptr);
+ }
+
+ mir_free(Results.pszFields);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Search field request result handler (XEP-0055. Examples 3, 8)
+
+void CJabberProto::OnIqResultAdvancedSearch(const TiXmlElement *iqNode, CJabberIqInfo*)
+{
+ const char *type;
+ int id;
+
+ UNIQUE_MAP mColumnsNames(10);
+ LIST<UNIQUE_MAP> SearchResults(2);
+
+ if (((id = JabberGetPacketID(iqNode)) == -1) || ((type = XmlGetAttr(iqNode, "type")) == nullptr)) {
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
+ return;
+ }
+
+ if (!mir_strcmp(type, "result")) {
+ auto *queryNode = XmlFirstChild(iqNode, "query");
+ auto *xNode = XmlGetChildByTag(queryNode, "x", "xmlns", JABBER_FEAT_DATA_FORMS);
+ if (xNode) {
+ // 1. Form search results info
+ for (auto *fieldNode : TiXmlFilter(XmlFirstChild(xNode, "reported"), "field")) {
+ auto *var = XmlGetAttr(fieldNode, "var");
+ if (var) {
+ auto *label = XmlGetAttr(fieldNode, "label");
+ mColumnsNames.insert(var, (label != nullptr) ? label : var);
+ }
+ }
+
+ for (auto *itemNode : TiXmlFilter(xNode, "item")) {
+ UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10);
+ for (auto *fieldNode : TiXmlFilter(itemNode, "field")) {
+ if (auto *var = XmlGetAttr(fieldNode, "var")) {
+ if (auto *textNode = XmlFirstChild(fieldNode, "value")) {
+ if (!mColumnsNames[var])
+ mColumnsNames.insert(var, var);
+ pUserColumn->insert(var, textNode->GetText());
+ }
+ }
+ }
+
+ SearchResults.insert(pUserColumn);
+ }
+ }
+ else {
+ // 2. Field list search results info
+ for (auto *itemNode : TiXmlFilter(queryNode, "item")) {
+ UNIQUE_MAP *pUserColumn = new UNIQUE_MAP(10);
+
+ auto *jid = XmlGetAttr(itemNode, "jid");
+ char *keyReturned;
+ mColumnsNames.insertCopyKey("jid", "jid", &keyReturned);
+ mColumnsNames.insert("jid", keyReturned);
+ pUserColumn->insertCopyKey("jid", jid, nullptr);
+
+ for (auto *child : TiXmlEnum(itemNode)) {
+ const char *szColumnName = child->Name();
+ if (szColumnName) {
+ const char *pszChild = child->GetText();
+ if (pszChild && *pszChild) {
+ mColumnsNames.insertCopyKey(szColumnName, "", &keyReturned);
+ mColumnsNames.insert(szColumnName, keyReturned);
+ pUserColumn->insertCopyKey(szColumnName, pszChild, nullptr);
+ }
+ }
+ }
+
+ SearchResults.insert(pUserColumn);
+ }
+ }
+ }
+ else if (!mir_strcmp(type, "error")) {
+ const char *code = "";
+ const char *description = "";
+ auto *errorNode = XmlFirstChild(iqNode, "error");
+ if (errorNode) {
+ code = XmlGetAttr(errorNode, "code");
+ description = errorNode->GetText();
+ }
+
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
+
+ char buff[255];
+ mir_snprintf(buff, TranslateU("Error %s %s\r\nTry to specify more detailed"), code, description);
+ if (searchHandleDlg)
+ SetDlgItemTextUtf(searchHandleDlg, IDC_INSTRUCTIONS, buff);
+ else
+ MessageBox(nullptr, Utf2T(buff), TranslateT("Search error"), MB_OK | MB_ICONSTOP);
+ return;
+ }
+
+ SearchReturnResults(this, (HANDLE)id, SearchResults, mColumnsNames);
+
+ for (auto &it : SearchResults)
+ delete ((UNIQUE_MAP*)it);
+
+ //send success to finish searching
+ ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id);
+}
+
+static BOOL CALLBACK DeleteChildWindowsProc(HWND hwnd, LPARAM)
+{
+ DestroyWindow(hwnd);
+ return TRUE;
+}
+
+static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat)
+{
+ 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 = nullptr;
+ }
+ else EnumChildWindows(GetDlgItem(hwndDlg, IDC_FRAME), DeleteChildWindowsProc, 0);
+
+ SendDlgItemMessage(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>"));
+}
+
+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)
+{
+ wchar_t szServerName[100];
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GO), FALSE);
+ GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(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, T2Utf(szServerName));
+ m_ThreadInfo->send(XmlNodeIq(pInfo) << XQUERY(JABBER_FEAT_JUD));
+ return pInfo->GetIqId();
+}
+
+static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const wchar_t *szAddr)
+{
+ int lResult = SendDlgItemMessage(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 char *szAddr, bool deleteLastFromDB)
+{
+ // search in recent
+ for (int i = 0; i < 10; i++) {
+ char key[30];
+ mir_snprintf(key, "RecentlySearched_%d", i);
+ ptrA szValue(getUStringA(key));
+ if (szValue == nullptr || mir_strcmpi(szAddr, szValue))
+ continue;
+
+ for (int j = i; j < 10; j++) {
+ mir_snprintf(key, "RecentlySearched_%d", j + 1);
+ szValue = getUStringA(key);
+ if (szValue != nullptr) {
+ mir_snprintf(key, "RecentlySearched_%d", j);
+ setUString(0, key, szValue);
+ }
+ else {
+ if (deleteLastFromDB) {
+ mir_snprintf(key, "RecentlySearched_%d", j);
+ delSetting(0, key);
+ }
+ break;
+ }
+ }
+ break;
+ }
+}
+
+void CJabberProto::SearchAddToRecent(const char *szAddr, HWND hwndDialog)
+{
+ char key[30];
+ SearchDeleteFromRecent(szAddr, true);
+
+ for (int j = 9; j > 0; j--) {
+ mir_snprintf(key, "RecentlySearched_%d", j - 1);
+ ptrW szValue(getWStringA(key));
+ if (szValue != nullptr) {
+ mir_snprintf(key, "RecentlySearched_%d", j);
+ setWString(0, key, szValue);
+ }
+ }
+
+ mir_snprintf(key, "RecentlySearched_%d", 0);
+ setUString(key, szAddr);
+ if (hwndDialog)
+ JabberSearchAddUrlToRecentCombo(hwndDialog, Utf2T(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 = new JabberSearchData();
+ dat->ppro = (CJabberProto *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ /* Server Combo box */
+ ptrA jud(dat->ppro->getStringA("Jud"));
+ if (jud != nullptr) {
+ SetDlgItemTextA(hwndDlg, IDC_SERVER, jud);
+ SendDlgItemMessageA(hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, jud);
+ }
+
+ //TO DO: Add Transports here
+ for (auto &it : dat->ppro->m_lstTransports)
+ if (it != nullptr)
+ JabberSearchAddUrlToRecentCombo(hwndDlg, Utf2T(it));
+
+ for (int i = 0; i < 10; i++) {
+ char key[30];
+ mir_snprintf(key, "RecentlySearched_%d", i);
+ ptrW szValue(dat->ppro->getWStringA(key));
+ if (szValue != nullptr)
+ 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), nullptr, 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), nullptr, 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;
+ if (dat->xNode) {
+ dat->doc.DeleteNode(dat->xNode);
+ dat->xNode = nullptr;
+ }
+ TiXmlElement *pNode = (TiXmlElement *)wParam;
+ if (pNode) {
+ dat->xNode = pNode->DeepClone(&dat->doc)->ToElement();
+ 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, nullptr, &(dat->frameRect));
+ SetScrollPos(GetDlgItem(hwndDlg, IDC_VSCROLL), SB_CTL, 0, FALSE);
+ dat->curPos = 0;
+ }
+ }
+ return TRUE;
+
+ case WM_MOUSEWHEEL:
+ {
+ short 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 != nullptr) {
+ 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, nullptr, &(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), nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN);
+ dat->curPos = pos;
+ }
+ }
+ }
+ return TRUE;
+
+ case WM_DESTROY:
+ JabberSearchFreeData(hwndDlg, dat);
+ JabberFormDestroyUI(GetDlgItem(hwndDlg, IDC_FRAME));
+ delete dat;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+HWND CJabberProto::CreateExtendedSearchUI(HWND parent)
+{
+ if (parent && g_plugin.getInst()) {
+ ptrW szServer(getWStringA("LoginServer"));
+ if (szServer == nullptr || mir_wstrcmpi(szServer, L"S.ms"))
+ return CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, (LPARAM)this);
+ }
+
+ return nullptr; // Failure
+}
+
+//////////////////////////////////////////////////////////////////////////
+// The function formats request to server
+
+HWND CJabberProto::SearchAdvanced(HWND hwndDlg)
+{
+ if (!m_bJabberOnline || !hwndDlg)
+ return nullptr; //error
+
+ JabberSearchData *dat = (JabberSearchData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (!dat)
+ return nullptr; //error
+
+ // check if server connected (at least one field exists)
+ if (dat->nJSInfCount == 0)
+ return nullptr;
+
+ // formating request
+ bool fRequestNotEmpty = false;
+
+ // get server name
+ wchar_t szServerName[100];
+ GetDlgItemText(hwndDlg, IDC_SERVER, szServerName, _countof(szServerName));
+
+ // formating query
+ CJabberIqInfo *pInfo = AddIQ(&CJabberProto::OnIqResultAdvancedSearch, JABBER_IQ_TYPE_SET, T2Utf(szServerName));
+ XmlNodeIq iq(pInfo);
+ TiXmlElement *query = iq << XQUERY(JABBER_FEAT_JUD);
+
+ if (m_tszSelectedLang)
+ iq << XATTR("xml:lang", m_tszSelectedLang); // i'm sure :)
+
+ // next can be 2 cases:
+ // Forms: XEP-0055 Example 7
+ if (dat->fSearchRequestIsXForm) {
+ fRequestNotEmpty = true;
+ JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), query, dat->xNode);
+ }
+ else { //and Simple fields: XEP-0055 Example 3
+ for (int i = 0; i < dat->nJSInfCount; i++) {
+ wchar_t szFieldValue[100];
+ GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, _countof(szFieldValue));
+ if (szFieldValue[0] != 0) {
+ XmlAddChildA(query, T2Utf(dat->pJSInf[i].szFieldName).get(), T2Utf(szFieldValue).get());
+ fRequestNotEmpty = true;
+ }
+ }
+ }
+
+ if (fRequestNotEmpty) {
+ m_ThreadInfo->send(iq);
+ return (HWND)pInfo->GetIqId();
+ }
+ return nullptr;
+}