/* Jabber Protocol Plugin for Miranda IM Copyright ( C ) 2002-04 Santithorn Bunchua Copyright ( C ) 2005-12 George Hazan Copyright ( C ) 2007 Artem Shpynov Module implements a search according to XEP-0055: Jabber Search http://www.xmpp.org/extensions/xep-0055.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or ( at your option ) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "jabber.h" #include #include "jabber_iq.h" #include "jabber_caps.h" /////////////////////////////////////////////////////////////////////////////// // Subclassing of IDC_FRAME to implement more user-friendly fields scrolling // static int JabberSearchFrameProc(HWND hwnd, int msg, WPARAM wParam, LPARAM lParam) { if ( msg == WM_COMMAND && lParam != 0 ) { HWND hwndDlg=GetParent(hwnd); JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); if ( dat && lParam ) { int pos=dat->curPos; RECT MineRect; RECT FrameRect; GetWindowRect(GetDlgItem( hwndDlg, IDC_FRAME ),&FrameRect); GetWindowRect((HWND)lParam, &MineRect); if ( MineRect.top-10 < FrameRect.top ) { pos=dat->curPos+(MineRect.top-14-FrameRect.top); if (pos<0) pos=0; } else if ( MineRect.bottom > FrameRect.bottom ) { pos=dat->curPos+(MineRect.bottom-FrameRect.bottom); if (dat->frameHeight+pos>dat->CurrentHeight) pos=dat->CurrentHeight-dat->frameHeight; } if ( pos != dat->curPos ) { ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - pos, NULL, &( dat->frameRect )); SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE ); RECT Invalid=dat->frameRect; if (dat->curPos - pos >0) Invalid.bottom=Invalid.top+(dat->curPos - pos); else Invalid.top=Invalid.bottom+(dat->curPos - pos); RedrawWindow(GetDlgItem( hwndDlg, IDC_FRAME ), NULL, NULL, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW |RDW_ALLCHILDREN); dat->curPos = pos; } } if (HIWORD(wParam)==EN_SETFOCUS) { //Transmit focus set notification to parent window PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg); } } if ( msg == WM_PAINT ) { PAINTSTRUCT ps; HDC hdc=BeginPaint(hwnd, &ps); FillRect(hdc,&(ps.rcPaint),GetSysColorBrush(COLOR_BTNFACE)); EndPaint(hwnd, &ps); } return DefWindowProc(hwnd,msg,wParam,lParam); } /////////////////////////////////////////////////////////////////////////////// // Add Search field to form // static int JabberSearchAddField(HWND hwndDlg, Data* FieldDat ) { if (!FieldDat || !FieldDat->Label || !FieldDat->Var) return FALSE; HFONT hFont = ( HFONT ) SendMessage( hwndDlg, WM_GETFONT, 0, 0 ); HWND hwndParent=GetDlgItem(hwndDlg,IDC_FRAME); LONG frameExStyle = GetWindowLongPtr( hwndParent, GWL_EXSTYLE ); frameExStyle |= WS_EX_CONTROLPARENT; SetWindowLongPtr( hwndParent, GWL_EXSTYLE, frameExStyle ); SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_FRAME),GWLP_WNDPROC,(LONG_PTR)JabberSearchFrameProc); int CornerX=1; int CornerY=1; RECT rect; GetClientRect(hwndParent,&rect); int width=rect.right-5-CornerX; int Order=(FieldDat->bHidden) ? -1 : FieldDat->Order; HWND hwndLabel=CreateWindowEx(0,_T("STATIC"),(LPCTSTR)TranslateTS(FieldDat->Label),WS_CHILD, CornerX, CornerY + Order*40, width, 13,hwndParent,NULL,hInst,0); HWND hwndVar=CreateWindowEx(0|WS_EX_CLIENTEDGE,_T("EDIT"),(LPCTSTR)FieldDat->defValue,WS_CHILD|WS_TABSTOP, CornerX+5, CornerY + Order*40+14, width ,20,hwndParent,NULL,hInst,0); SendMessage(hwndLabel, WM_SETFONT, (WPARAM)hFont,0); SendMessage(hwndVar, WM_SETFONT, (WPARAM)hFont,0); if (!FieldDat->bHidden) { ShowWindow(hwndLabel,SW_SHOW); ShowWindow(hwndVar,SW_SHOW); EnableWindow(hwndLabel,!FieldDat->bReadOnly); SendMessage(hwndVar, EM_SETREADONLY, (WPARAM)FieldDat->bReadOnly,0); } //remade list //reallocation JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); if ( dat ) { dat->pJSInf=(JabberSearchFieldsInfo*) realloc(dat->pJSInf, sizeof(JabberSearchFieldsInfo)*(dat->nJSInfCount+1)); dat->pJSInf[dat->nJSInfCount].hwndCaptionItem=hwndLabel; dat->pJSInf[dat->nJSInfCount].hwndValueItem=hwndVar; dat->pJSInf[dat->nJSInfCount].szFieldCaption=_tcsdup(FieldDat->Label); dat->pJSInf[dat->nJSInfCount].szFieldName=_tcsdup(FieldDat->Var); dat->nJSInfCount++; } return CornerY + Order*40+14 +20; } //////////////////////////////////////////////////////////////////////////////// // Available search field request result handler (XEP-0055. Examples 2, 7) void CJabberProto::OnIqResultGetSearchFields( HXML iqNode ) { if ( !searchHandleDlg ) return; LPCTSTR type = xmlGetAttrValue( iqNode, _T("type")); if ( !type ) return; if ( !lstrcmp( type, _T("result"))) { HXML queryNode = xmlGetNthChild( iqNode, _T("query"), 1 ); HXML xNode = xmlGetChildByTag( queryNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS)); ShowWindow(searchHandleDlg,SW_HIDE); if ( xNode ) { //1. Form PostMessage( searchHandleDlg, WM_USER+11, ( WPARAM )xi.copyNode( xNode ), ( LPARAM )0 ); HXML xcNode = xmlGetNthChild( xNode, _T("instructions"), 1 ); if ( xcNode ) SetDlgItemText( searchHandleDlg, IDC_INSTRUCTIONS, xmlGetText( xcNode )); } else { int Order=0; for ( int i = 0; ; i++ ) { HXML chNode = xmlGetChild( queryNode, i ); if ( !chNode ) break; if ( !_tcsicmp( xmlGetName( chNode ), _T("instructions")) && xmlGetText( chNode )) SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,TranslateTS(xmlGetText( chNode ))); else if ( xmlGetName( chNode )) { Data *MyData=(Data*)malloc(sizeof(Data)); memset(MyData,0,sizeof(Data)); MyData->Label = mir_tstrdup( xmlGetName( chNode )); MyData->Var = mir_tstrdup( xmlGetName( chNode )); MyData->defValue = mir_tstrdup(xmlGetText( chNode )); MyData->Order = Order; if (MyData->defValue) MyData->bReadOnly = TRUE; PostMessage(searchHandleDlg,WM_USER+10,(WPARAM)FALSE,(LPARAM)MyData); Order++; } } } const TCHAR* szFrom = xmlGetAttrValue( iqNode, _T("from")); if (szFrom) SearchAddToRecent(szFrom,searchHandleDlg); PostMessage(searchHandleDlg,WM_USER+10,(WPARAM)0,(LPARAM)0); ShowWindow(searchHandleDlg,SW_SHOW); } else if ( !lstrcmp( type, _T("error"))) { const TCHAR* code=NULL; const TCHAR* description=NULL; TCHAR buff[255]; HXML errorNode = xmlGetChild( iqNode, "error"); if ( errorNode ) { code = xmlGetAttrValue( errorNode, _T("code")); description=xmlGetText( errorNode ); } _sntprintf(buff,SIZEOF(buff),TranslateT("Error %s %s\r\nPlease select other server"),code ? code : _T(""),description?description:_T("")); SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff); } else SetDlgItemText( searchHandleDlg, IDC_INSTRUCTIONS, TranslateT( "Error Unknown reply recieved\r\nPlease select other server" )); } ////////////////////////////////////////////////////////////////////////////////////////// // Return results to search dialog // The pmFields is the pointer to map of Not unical but ordered // This can help to made result parser routines more simple void CJabberProto::SearchReturnResults( HANDLE id, void * pvUsersInfo, U_TCHAR_MAP * pmAllFields ) { LIST ListOfNonEmptyFields(20,(LIST::FTSortFunc)TCharKeyCmp); LIST ListOfFields(20); LIST* plUsersInfo = ( LIST* )pvUsersInfo; int i, nUsersFound = plUsersInfo->getCount(); // lets fill the ListOfNonEmptyFields but in users order for ( i=0; i < nUsersFound; i++ ) { U_TCHAR_MAP* pmUserData = ( U_TCHAR_MAP* )plUsersInfo->operator [](i); int nUserFields = pmUserData->getCount(); for (int j=0; j < nUserFields; j++) { TCHAR* var = pmUserData->getKeyName(j); if (var && ListOfNonEmptyFields.getIndex(var) < 0) ListOfNonEmptyFields.insert(var); } } // now fill the ListOfFields but order is from pmAllFields int nAllCount = pmAllFields->getCount(); for ( i=0; i < nAllCount; i++ ) { TCHAR * var=pmAllFields->getUnOrderedKeyName(i); if ( var && ListOfNonEmptyFields.getIndex(var) < 0 ) continue; ListOfFields.insert(var); } // now lets transfer field names int nFieldCount = ListOfFields.getCount(); JABBER_CUSTOMSEARCHRESULTS Results={0}; Results.nSize=sizeof(Results); Results.pszFields=(TCHAR**)mir_alloc(sizeof(TCHAR*)*nFieldCount); Results.nFieldCount=nFieldCount; /* Sending Columns Titles */ for ( i=0; i < nFieldCount; i++ ) { TCHAR* var = ListOfFields[i]; if ( var ) Results.pszFields[i] = pmAllFields->operator [](var); } Results.jsr.hdr.cbSize = 0; // sending column names JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results ); /* Sending Users Data */ Results.jsr.hdr.cbSize = sizeof(Results.jsr); // sending user data for ( i=0; i < nUsersFound; i++ ) { TCHAR buff[200]=_T(""); Results.jsr.jid[0]=_T('\0'); U_TCHAR_MAP * pmUserData = (U_TCHAR_MAP *) plUsersInfo->operator [](i); for ( int j=0; j < nFieldCount; j++ ) { TCHAR* var = ListOfFields[j]; TCHAR* value = pmUserData->operator [](var); Results.pszFields[j] = value ? value : (TCHAR *)_T(" "); if (!_tcsicmp(var,_T("jid")) && value ) _tcsncpy(Results.jsr.jid, value, SIZEOF(Results.jsr.jid)); } { TCHAR * nickfields[]={ _T("nick"), _T("nickname"), _T("fullname"), _T("name"), _T("given"), _T("first"), _T("jid"), NULL }; TCHAR * nick=NULL; int k=0; while (nickfields[k] && !nick) nick=pmUserData->operator [](nickfields[k++]); if (_tcsicmp(nick, Results.jsr.jid)) _sntprintf(buff,SIZEOF(buff),_T("%s ( %s )"),nick, Results.jsr.jid); else _tcsncpy(buff, nick, SIZEOF(buff)); Results.jsr.hdr.nick = nick ? buff : NULL; Results.jsr.hdr.flags = PSR_TCHAR; } JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, id, (LPARAM) &Results ); Results.jsr.hdr.nick=NULL; } mir_free( Results.pszFields ); } void DestroyKey( TCHAR* key ) { mir_free( key ); } TCHAR* CopyKey( TCHAR* key ) { return mir_tstrdup( key ); } //////////////////////////////////////////////////////////////////////////////// // Search field request result handler (XEP-0055. Examples 3, 8) void CJabberProto::OnIqResultAdvancedSearch( HXML iqNode ) { const TCHAR* type; int id; U_TCHAR_MAP mColumnsNames(10); LIST SearchResults(2); if ((( id = JabberGetPacketID( iqNode )) == -1 ) || (( type = xmlGetAttrValue( iqNode, _T("type"))) == NULL )) { JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 ); return; } if ( !lstrcmp( type, _T("result"))) { HXML queryNode = xmlGetNthChild( iqNode, _T("query"), 1 ); HXML xNode = xmlGetChildByTag( queryNode, "x", "xmlns", _T(JABBER_FEAT_DATA_FORMS)); if (xNode) { //1. Form search results info HXML reportNode = xmlGetNthChild( xNode, _T("reported"), 1 ); if (reportNode) { int i = 1; while ( HXML fieldNode = xmlGetNthChild( reportNode, _T("field"), i++ )) { TCHAR* var = ( TCHAR* )xmlGetAttrValue( fieldNode, _T( "var" )); if ( var ) { TCHAR* Label = ( TCHAR* )xmlGetAttrValue( fieldNode, _T( "label" )); mColumnsNames.insert(var, (Label!=NULL) ? Label : var); } } } int i=1; HXML itemNode; while ( itemNode = xmlGetNthChild( xNode, _T("item"), i++ )) { U_TCHAR_MAP *pUserColumn = new U_TCHAR_MAP(10); int j = 1; while ( HXML fieldNode = xmlGetNthChild( itemNode, _T("field"), j++ )) { if ( TCHAR* var = (TCHAR*)xmlGetAttrValue( fieldNode, _T("var"))) { if ( TCHAR* Text = (TCHAR*)xmlGetText( xmlGetChild( fieldNode, _T("value")))) { if ( !mColumnsNames[var] ) mColumnsNames.insert(var,var); pUserColumn->insert(var,Text); } } } SearchResults.insert((void*)pUserColumn); } } else { //2. Field list search results info int i=1; while ( HXML itemNode = xmlGetNthChild( queryNode, _T("item"), i++ )) { U_TCHAR_MAP *pUserColumn=new U_TCHAR_MAP(10); TCHAR* jid = (TCHAR*)xmlGetAttrValue( itemNode, _T("jid")); TCHAR* keyReturned; mColumnsNames.insertCopyKey( _T("jid"),_T("jid"),&keyReturned, CopyKey, DestroyKey ); mColumnsNames.insert( _T("jid"), keyReturned ); pUserColumn->insertCopyKey( _T("jid"), jid, NULL, CopyKey, DestroyKey ); for ( int j=0; ; j++ ) { HXML child = xmlGetChild( itemNode, j ); if ( !child ) break; const TCHAR* szColumnName = xmlGetName( child ); if ( szColumnName ) { if ( xmlGetText( child ) && xmlGetText( child )[0] != _T('\0')) { mColumnsNames.insertCopyKey(( TCHAR* )szColumnName,_T(""),&keyReturned, CopyKey, DestroyKey); mColumnsNames.insert(( TCHAR* )szColumnName,keyReturned); pUserColumn->insertCopyKey(( TCHAR* )szColumnName, ( TCHAR* )xmlGetText( child ),NULL, CopyKey, DestroyKey); } } } SearchResults.insert((void*)pUserColumn); } } } else if (!lstrcmp( type, _T("error"))) { const TCHAR* code=NULL; const TCHAR* description=NULL; TCHAR buff[255]; HXML errorNode = xmlGetChild( iqNode , "error" ); if (errorNode) { code = xmlGetAttrValue( errorNode, _T("code")); description = xmlGetText( errorNode ); } _sntprintf(buff,SIZEOF(buff),TranslateT("Error %s %s\r\nTry to specify more detailed"),code ? code : _T(""),description?description:_T("")); JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 ); if (searchHandleDlg ) SetDlgItemText(searchHandleDlg,IDC_INSTRUCTIONS,buff); else MessageBox(NULL, buff, TranslateT("Search error"), MB_OK|MB_ICONSTOP); return; } SearchReturnResults((HANDLE)id, (void*)&SearchResults, (U_TCHAR_MAP *)&mColumnsNames); for (int i=0; i < SearchResults.getCount(); i++ ) delete ((U_TCHAR_MAP *)SearchResults[i]); //send success to finish searching JSendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE ) id, 0 ); } static BOOL CALLBACK DeleteChildWindowsProc( HWND hwnd, LPARAM ) { DestroyWindow(hwnd); return TRUE; } static void JabberSearchFreeData(HWND hwndDlg, JabberSearchData * dat) { //lock if ( !dat->fSearchRequestIsXForm && dat->nJSInfCount && dat->pJSInf ) { for ( int i=0; i < dat->nJSInfCount; i++ ) { if (dat->pJSInf[i].hwndValueItem) DestroyWindow(dat->pJSInf[i].hwndValueItem); if (dat->pJSInf[i].hwndCaptionItem) DestroyWindow(dat->pJSInf[i].hwndCaptionItem); if (dat->pJSInf[i].szFieldCaption) free(dat->pJSInf[i].szFieldCaption); if (dat->pJSInf[i].szFieldName) free(dat->pJSInf[i].szFieldName); } free(dat->pJSInf); dat->pJSInf=NULL; } else EnumChildWindows(GetDlgItem(hwndDlg,IDC_FRAME),DeleteChildWindowsProc,0); if ( dat->xNode ) xi.destroyNode( dat->xNode ); SendMessage(GetDlgItem(hwndDlg,IDC_FRAME), WM_SETFONT, (WPARAM) SendMessage( hwndDlg, WM_GETFONT, 0, 0 ),0 ); dat->nJSInfCount=0; ShowWindow(GetDlgItem(hwndDlg,IDC_VSCROLL),SW_HIDE); SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,TranslateT("Select/type search service URL above and press ")); //unlock } static void JabberSearchRefreshFrameScroll(HWND hwndDlg, JabberSearchData * dat) { HWND hFrame = GetDlgItem( hwndDlg, IDC_FRAME ); HWND hwndScroll = GetDlgItem( hwndDlg, IDC_VSCROLL ); RECT rc; GetClientRect( hFrame, &rc ); GetClientRect( hFrame, &dat->frameRect ); dat->frameHeight = rc.bottom-rc.top; if ( dat->frameHeight < dat->CurrentHeight) { ShowWindow( hwndScroll, SW_SHOW ); EnableWindow( hwndScroll, TRUE ); } else ShowWindow( hwndScroll, SW_HIDE ); SetScrollRange( hwndScroll, SB_CTL, 0, dat->CurrentHeight-dat->frameHeight, FALSE ); } int CJabberProto::SearchRenewFields(HWND hwndDlg, JabberSearchData * dat) { TCHAR szServerName[100]; EnableWindow(GetDlgItem(hwndDlg, IDC_GO),FALSE); GetDlgItemText(hwndDlg,IDC_SERVER,szServerName,SIZEOF(szServerName)); dat->CurrentHeight = 0; dat->curPos = 0; SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, 0, FALSE ); JabberSearchFreeData( hwndDlg, dat ); JabberSearchRefreshFrameScroll( hwndDlg, dat ); SetDlgItemText(hwndDlg,IDC_INSTRUCTIONS,m_bJabberOnline ? TranslateT("Please wait...\r\nConnecting search server...") : TranslateT("You have to be connected to server")); if ( !m_bJabberOnline ) return 0; searchHandleDlg = hwndDlg; int iqId = SerialNext(); IqAdd( iqId, IQ_PROC_GETSEARCHFIELDS, &CJabberProto::OnIqResultGetSearchFields ); m_ThreadInfo->send( XmlNodeIq( _T("get"), iqId, szServerName ) << XQUERY( _T("jabber:iq:search"))); return iqId; } static void JabberSearchAddUrlToRecentCombo(HWND hwndDlg, const TCHAR* szAddr) { int lResult = SendMessage( GetDlgItem(hwndDlg,IDC_SERVER), (UINT) CB_FINDSTRING, 0, (LPARAM)szAddr ); if ( lResult == -1 ) SendDlgItemMessage( hwndDlg, IDC_SERVER, CB_ADDSTRING, 0, ( LPARAM )szAddr ); } void CJabberProto::SearchDeleteFromRecent( const TCHAR* szAddr, BOOL deleteLastFromDB ) { DBVARIANT dbv; char key[30]; //search in recent for ( int i=0; i<10; i++ ) { sprintf(key,"RecentlySearched_%d",i); if ( !JGetStringT( NULL, key, &dbv )) { if ( !_tcsicmp( szAddr, dbv.ptszVal )) { JFreeVariant( &dbv ); for ( int j=i; j<10; j++ ) { sprintf( key, "RecentlySearched_%d", j+1 ); if ( !JGetStringT( NULL, key, &dbv )) { sprintf(key,"RecentlySearched_%d",j); JSetStringT(NULL,key,dbv.ptszVal); JFreeVariant( &dbv ); } else { if ( deleteLastFromDB ) { sprintf(key,"RecentlySearched_%d",j); JDeleteSetting(NULL,key); } break; } } break; } else JFreeVariant( &dbv ); } } } void CJabberProto::SearchAddToRecent( const TCHAR* szAddr, HWND hwndDialog ) { DBVARIANT dbv; char key[30]; SearchDeleteFromRecent( szAddr ); for ( int j=9; j > 0; j-- ) { sprintf( key, "RecentlySearched_%d", j-1 ); if ( !JGetStringT( NULL, key, &dbv )) { sprintf(key,"RecentlySearched_%d",j); JSetStringT(NULL,key,dbv.ptszVal); JFreeVariant(&dbv); } } sprintf( key, "RecentlySearched_%d", 0 ); JSetStringT( NULL, key, szAddr ); if ( hwndDialog ) JabberSearchAddUrlToRecentCombo( hwndDialog, szAddr ); } static INT_PTR CALLBACK JabberSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { JabberSearchData* dat = ( JabberSearchData* )GetWindowLongPtr( hwndDlg, GWLP_USERDATA ); switch ( msg ) { case WM_INITDIALOG: { TranslateDialogDefault(hwndDlg); dat = ( JabberSearchData * )mir_alloc( sizeof( JabberSearchData )); memset( dat, 0, sizeof( JabberSearchData )); dat->ppro = ( CJabberProto* )lParam; SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)dat ); /* Server Combo box */ char szServerName[100]; if ( dat->ppro->JGetStaticString( "Jud", NULL, szServerName, sizeof szServerName )) strcpy( szServerName, "users.jabber.org" ); SetDlgItemTextA(hwndDlg,IDC_SERVER,szServerName); SendDlgItemMessageA(hwndDlg,IDC_SERVER,CB_ADDSTRING,0,(LPARAM)szServerName); //TO DO: Add Transports here int i, transpCount = dat->ppro->m_lstTransports.getCount(); for ( i=0; i < transpCount; i++ ) { TCHAR* szTransp = dat->ppro->m_lstTransports[i]; if ( szTransp ) JabberSearchAddUrlToRecentCombo(hwndDlg, szTransp ); } DBVARIANT dbv; char key[30]; for ( i=0; i < 10; i++ ) { sprintf(key,"RecentlySearched_%d",i); if ( !dat->ppro->JGetStringT( NULL, key, &dbv )) { JabberSearchAddUrlToRecentCombo(hwndDlg, dbv.ptszVal ); JFreeVariant( &dbv ); } } //TO DO: Add 4 recently used dat->lastRequestIq = dat->ppro->SearchRenewFields(hwndDlg,dat); } return TRUE; case WM_COMMAND: if ( LOWORD(wParam) == IDC_SERVER ) { switch ( HIWORD( wParam )) { case CBN_SETFOCUS: PostMessage(GetParent(hwndDlg),WM_COMMAND, MAKEWPARAM(0,EN_SETFOCUS), (LPARAM)hwndDlg); return TRUE; case CBN_EDITCHANGE: EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE); return TRUE; case CBN_EDITUPDATE: JabberSearchFreeData(hwndDlg, dat); EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE); return TRUE; case CBN_SELENDOK: EnableWindow(GetDlgItem(hwndDlg, IDC_GO),TRUE); PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_GO,BN_CLICKED),0); return TRUE; } } else if ( LOWORD(wParam) == IDC_GO && HIWORD(wParam) == BN_CLICKED ) { dat->ppro->SearchRenewFields( hwndDlg, dat ); return TRUE; } break; case WM_SIZE: { //Resize IDC_FRAME to take full size RECT rcForm; GetWindowRect(hwndDlg, &rcForm); RECT rcFrame; GetWindowRect( GetDlgItem(hwndDlg, IDC_FRAME), &rcFrame ); rcFrame.bottom = rcForm.bottom; SetWindowPos(GetDlgItem(hwndDlg,IDC_FRAME),NULL,0,0,rcFrame.right-rcFrame.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE); GetWindowRect(GetDlgItem(hwndDlg,IDC_VSCROLL), &rcForm); SetWindowPos(GetDlgItem(hwndDlg,IDC_VSCROLL),NULL,0,0,rcForm.right-rcForm.left,rcFrame.bottom-rcFrame.top,SWP_NOZORDER|SWP_NOMOVE); JabberSearchRefreshFrameScroll(hwndDlg, dat); } return TRUE; case WM_USER+11: { dat->fSearchRequestIsXForm=TRUE; dat->xNode = ( HXML )wParam; JabberFormCreateUI( GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode, &dat->CurrentHeight,TRUE); ShowWindow(GetDlgItem(hwndDlg, IDC_FRAME), SW_SHOW); dat->nJSInfCount=1; return TRUE; } case WM_USER+10: { Data* MyDat = ( Data* )lParam; if ( MyDat ) { dat->fSearchRequestIsXForm = ( BOOL )wParam; dat->CurrentHeight = JabberSearchAddField(hwndDlg,MyDat); mir_free( MyDat->Label ); mir_free( MyDat->Var ); mir_free( MyDat->defValue ); free( MyDat ); } else { JabberSearchRefreshFrameScroll(hwndDlg,dat); ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - 0, NULL, &( dat->frameRect )); SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, 0, FALSE ); dat->curPos=0; } return TRUE; } case WM_MOUSEWHEEL: { int zDelta = GET_WHEEL_DELTA_WPARAM(wParam); if ( zDelta ) { int nScrollLines=0; SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,(void*)&nScrollLines,0); for (int i=0; i<(nScrollLines+1)/2; i++) SendMessage(hwndDlg,WM_VSCROLL, (zDelta<0)?SB_LINEDOWN:SB_LINEUP,0); } } return TRUE; case WM_VSCROLL: { int pos; if ( dat != NULL ) { pos = dat->curPos; switch ( LOWORD( wParam )) { case SB_LINEDOWN: pos += 10; break; case SB_LINEUP: pos -= 10; break; case SB_PAGEDOWN: pos += ( dat->CurrentHeight - 10 ); break; case SB_PAGEUP: pos -= ( dat->CurrentHeight - 10 ); break; case SB_THUMBTRACK: pos = HIWORD( wParam ); break; } if ( pos > ( dat->CurrentHeight - dat->frameHeight )) pos = dat->CurrentHeight - dat->frameHeight; if ( pos < 0 ) pos = 0; if ( dat->curPos != pos ) { ScrollWindow( GetDlgItem( hwndDlg, IDC_FRAME ), 0, dat->curPos - pos, NULL , &( dat->frameRect )); SetScrollPos( GetDlgItem( hwndDlg, IDC_VSCROLL ), SB_CTL, pos, TRUE ); RECT Invalid=dat->frameRect; if (dat->curPos - pos >0) Invalid.bottom=Invalid.top+(dat->curPos - pos); else Invalid.top=Invalid.bottom+(dat->curPos - pos); RedrawWindow(GetDlgItem( hwndDlg, IDC_FRAME ), NULL, NULL, RDW_UPDATENOW |RDW_ALLCHILDREN); dat->curPos = pos; } } } return TRUE; case WM_DESTROY: JabberSearchFreeData( hwndDlg, dat ); JabberFormDestroyUI( GetDlgItem( hwndDlg, IDC_FRAME )); mir_free( dat ); SetWindowLongPtr( hwndDlg, GWLP_USERDATA, 0 ); return TRUE; } return FALSE; } HWND __cdecl CJabberProto::CreateExtendedSearchUI( HWND parent ) { TCHAR szServer[128]; if (parent && hInst && (!JGetStringT(NULL, "LoginServer", szServer, SIZEOF(szServer)) || _tcsicmp(szServer, _T("S.ms")))) return CreateDialogParam( hInst, MAKEINTRESOURCE(IDD_SEARCHUSER), parent, JabberSearchAdvancedDlgProc, ( LPARAM )this ); return 0; // Failure } ////////////////////////////////////////////////////////////////////////// // The function formats request to server HWND __cdecl CJabberProto::SearchAdvanced( HWND hwndDlg ) { if ( !m_bJabberOnline || !hwndDlg ) return 0; //error JabberSearchData * dat=(JabberSearchData *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); if ( !dat ) return 0; //error // check if server connected (at least one field exists) if ( dat->nJSInfCount == 0 ) return 0; // formating request BOOL fRequestNotEmpty=FALSE; // get server name TCHAR szServerName[100]; GetDlgItemText( hwndDlg, IDC_SERVER, szServerName, SIZEOF( szServerName )); // formating query int iqId = SerialNext(); XmlNodeIq iq( _T("set"), iqId, szServerName ); HXML query = iq << XQUERY( _T("jabber:iq:search")); if ( m_tszSelectedLang ) iq << XATTR( _T("xml:lang"), m_tszSelectedLang ); // i'm sure :) // next can be 2 cases: // Forms: XEP-0055 Example 7 if ( dat->fSearchRequestIsXForm ) { fRequestNotEmpty=TRUE; HXML n = JabberFormGetData(GetDlgItem(hwndDlg, IDC_FRAME), dat->xNode); xmlAddChild( query, n ); xi.destroyNode( n ); } else { //and Simple fields: XEP-0055 Example 3 for ( int i=0; inJSInfCount; i++ ) { TCHAR szFieldValue[100]; GetWindowText(dat->pJSInf[i].hwndValueItem, szFieldValue, SIZEOF(szFieldValue)); if ( szFieldValue[0] != _T('\0')) { xmlAddChild( query, dat->pJSInf[i].szFieldName, szFieldValue ); fRequestNotEmpty=TRUE; } } } if ( fRequestNotEmpty ) { // register search request result handler IqAdd( iqId, IQ_PROC_GETSEARCH, &CJabberProto::OnIqResultAdvancedSearch ); // send request m_ThreadInfo->send( iq ); return ( HWND )iqId; } return 0; }