Fix compilation with wxWidgets 2.8.12
[amule.git] / src / SearchList.cpp
blob342a18f0d305280e7c86b976e1221cc456edcde5
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "SearchList.h" // Interface declarations.
28 #include <protocol/Protocols.h>
29 #include <protocol/kad/Constants.h>
30 #include <tags/ClientTags.h>
31 #include <tags/FileTags.h>
33 #include "updownclient.h" // Needed for CUpDownClient
34 #include "MemFile.h" // Needed for CMemFile
35 #include "amule.h" // Needed for theApp
36 #include "ServerConnect.h" // Needed for theApp->serverconnect
37 #include "Server.h" // Needed for CServer
38 #include "ServerList.h" // Needed for theApp->serverlist
39 #include "Statistics.h" // Needed for theStats
40 #include "ObservableQueue.h"// Needed for CQueueObserver
41 #include <common/Format.h>
42 #include "Logger.h" // Needed for AddLogLineM/...
43 #include "Packet.h" // Needed for CPacket
44 #include "GuiEvents.h" // Needed for Notify_*
47 #ifndef AMULE_DAEMON
48 #include "amuleDlg.h" // Needed for CamuleDlg
49 #include "SearchDlg.h" // Needed for CSearchDlg
50 #endif
52 #include "kademlia/kademlia/Kademlia.h"
53 #include "kademlia/kademlia/Search.h"
55 #include "SearchExpr.h"
56 #include "Scanner.h.in"
59 extern int yyparse();
60 extern int yyerror(const char* errstr);
61 extern int yyerror(wxString errstr);
63 static wxString s_strCurKadKeyword;
65 static CSearchExpr _SearchExpr;
67 wxArrayString _astrParserErrors;
70 // Helper function for lexer.
71 void ParsedSearchExpression(const CSearchExpr* pexpr)
73 int iOpAnd = 0;
74 int iOpOr = 0;
75 int iOpNot = 0;
77 for (unsigned int i = 0; i < pexpr->m_aExpr.GetCount(); i++) {
78 const wxString& str = pexpr->m_aExpr[i];
79 if (str == SEARCHOPTOK_AND) {
80 iOpAnd++;
81 } else if (str == SEARCHOPTOK_OR) {
82 iOpOr++;
83 } else if (str == SEARCHOPTOK_NOT) {
84 iOpNot++;
88 // this limit (+ the additional operators which will be added later) has to match the limit in 'CreateSearchExpressionTree'
89 // +1 Type (Audio, Video)
90 // +1 MinSize
91 // +1 MaxSize
92 // +1 Avail
93 // +1 Extension
94 // +1 Complete sources
95 // +1 Codec
96 // +1 Bitrate
97 // +1 Length
98 // +1 Title
99 // +1 Album
100 // +1 Artist
101 // ---------------
102 // 12
103 if (iOpAnd + iOpOr + iOpNot > 10) {
104 yyerror(wxT("Search expression is too complex"));
107 _SearchExpr.m_aExpr.Empty();
109 // optimize search expression, if no OR nor NOT specified
110 if (iOpAnd > 0 && iOpOr == 0 && iOpNot == 0) {
111 // figure out if we can use a better keyword than the one the user selected
112 // for example most user will search like this "The oxymoronaccelerator 2", which would ask the node which indexes "the"
113 // This causes higher traffic for such nodes and makes them a viable target to attackers, while the kad result should be
114 // the same or even better if we ask the node which indexes the rare keyword "oxymoronaccelerator", so we try to rearrange
115 // keywords and generally assume that the longer keywords are rarer
116 if (/*thePrefs::GetRearrangeKadSearchKeywords() &&*/ !s_strCurKadKeyword.IsEmpty()) {
117 for (unsigned int i = 0; i < pexpr->m_aExpr.GetCount(); i++) {
118 if (pexpr->m_aExpr[i] != SEARCHOPTOK_AND) {
119 if (pexpr->m_aExpr[i] != s_strCurKadKeyword
120 && pexpr->m_aExpr[i].find_first_of(Kademlia::CSearchManager::GetInvalidKeywordChars()) == wxString::npos
121 && pexpr->m_aExpr[i].Find('"') != 0 // no quoted expressions as keyword
122 && pexpr->m_aExpr[i].length() >= 3
123 && s_strCurKadKeyword.length() < pexpr->m_aExpr[i].length())
125 s_strCurKadKeyword = pexpr->m_aExpr[i];
130 wxString strAndTerms;
131 for (unsigned int i = 0; i < pexpr->m_aExpr.GetCount(); i++) {
132 if (pexpr->m_aExpr[i] != SEARCHOPTOK_AND) {
133 // Minor optimization: Because we added the Kad keyword to the boolean search expression,
134 // we remove it here (and only here) again because we know that the entire search expression
135 // does only contain (implicit) ANDed strings.
136 if (pexpr->m_aExpr[i] != s_strCurKadKeyword) {
137 if (!strAndTerms.IsEmpty()) {
138 strAndTerms += ' ';
140 strAndTerms += pexpr->m_aExpr[i];
144 wxASSERT( _SearchExpr.m_aExpr.GetCount() == 0);
145 _SearchExpr.m_aExpr.Add(strAndTerms);
146 } else {
147 if (pexpr->m_aExpr.GetCount() != 1 || pexpr->m_aExpr[0] != s_strCurKadKeyword)
148 _SearchExpr.Add(pexpr);
153 //! Helper class for packet creation
154 class CSearchExprTarget
156 public:
157 CSearchExprTarget(CMemFile* pData, EUtf8Str eStrEncode, bool supports64bit, bool& using64bit)
158 : m_data(pData),
159 m_eStrEncode(eStrEncode),
160 m_supports64bit(supports64bit),
161 m_using64bit(using64bit)
163 m_using64bit = false;
166 void WriteBooleanAND()
168 m_data->WriteUInt8(0); // boolean operator parameter type
169 m_data->WriteUInt8(0x00); // "AND"
172 void WriteBooleanOR()
174 m_data->WriteUInt8(0); // boolean operator parameter type
175 m_data->WriteUInt8(0x01); // "OR"
178 void WriteBooleanNOT()
180 m_data->WriteUInt8(0); // boolean operator parameter type
181 m_data->WriteUInt8(0x02); // "NOT"
184 void WriteMetaDataSearchParam(const wxString& rstrValue)
186 m_data->WriteUInt8(1); // string parameter type
187 m_data->WriteString(rstrValue, m_eStrEncode); // string value
190 void WriteMetaDataSearchParam(uint8 uMetaTagID, const wxString& rstrValue)
192 m_data->WriteUInt8(2); // string parameter type
193 m_data->WriteString(rstrValue, m_eStrEncode); // string value
194 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
195 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
198 void WriteMetaDataSearchParamASCII(uint8 uMetaTagID, const wxString& rstrValue)
200 m_data->WriteUInt8(2); // string parameter type
201 m_data->WriteString(rstrValue, utf8strNone); // string value
202 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
203 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
206 void WriteMetaDataSearchParam(const wxString& pszMetaTagID, const wxString& rstrValue)
208 m_data->WriteUInt8(2); // string parameter type
209 m_data->WriteString(rstrValue, m_eStrEncode); // string value
210 m_data->WriteString(pszMetaTagID); // meta tag ID
213 void WriteMetaDataSearchParam(uint8_t uMetaTagID, uint8_t uOperator, uint64_t value)
215 bool largeValue = value > wxULL(0xFFFFFFFF);
216 if (largeValue && m_supports64bit) {
217 m_using64bit = true;
218 m_data->WriteUInt8(8); // numeric parameter type (int64)
219 m_data->WriteUInt64(value); // numeric value
220 } else {
221 if (largeValue) {
222 value = 0xFFFFFFFFu;
224 m_data->WriteUInt8(3); // numeric parameter type (int32)
225 m_data->WriteUInt32(value); // numeric value
227 m_data->WriteUInt8(uOperator); // comparison operator
228 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
229 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
232 void WriteMetaDataSearchParam(const wxString& pszMetaTagID, uint8_t uOperator, uint64_t value)
234 bool largeValue = value > wxULL(0xFFFFFFFF);
235 if (largeValue && m_supports64bit) {
236 m_using64bit = true;
237 m_data->WriteUInt8(8); // numeric parameter type (int64)
238 m_data->WriteUInt64(value); // numeric value
239 } else {
240 if (largeValue) {
241 value = 0xFFFFFFFFu;
243 m_data->WriteUInt8(3); // numeric parameter type (int32)
244 m_data->WriteUInt32(value); // numeric value
246 m_data->WriteUInt8(uOperator); // comparison operator
247 m_data->WriteString(pszMetaTagID); // meta tag ID
250 protected:
251 CMemFile* m_data;
252 EUtf8Str m_eStrEncode;
253 bool m_supports64bit;
254 bool& m_using64bit;
260 ///////////////////////////////////////////////////////////
261 // CSearchList
263 BEGIN_EVENT_TABLE(CSearchList, wxEvtHandler)
264 EVT_MULE_TIMER(wxID_ANY, CSearchList::OnGlobalSearchTimer)
265 END_EVENT_TABLE()
268 CSearchList::CSearchList()
269 : m_searchTimer(this, 0 /* Timer-id doesn't matter. */ ),
270 m_searchType(LocalSearch),
271 m_searchInProgress(false),
272 m_currentSearch(-1),
273 m_searchPacket(NULL),
274 m_64bitSearchPacket(false),
275 m_KadSearchFinished(true)
279 CSearchList::~CSearchList()
281 StopSearch();
283 while (!m_results.empty()) {
284 RemoveResults(m_results.begin()->first);
289 void CSearchList::RemoveResults(long searchID)
291 // A non-existant search id will just be ignored
292 Kademlia::CSearchManager::StopSearch(searchID, true);
294 ResultMap::iterator it = m_results.find(searchID);
295 if ( it != m_results.end() ) {
296 CSearchResultList& list = it->second;
298 for (size_t i = 0; i < list.size(); ++i) {
299 delete list.at(i);
302 m_results.erase( it );
307 wxString CSearchList::StartNewSearch(uint32* searchID, SearchType type, CSearchParams& params)
309 // Check that we can actually perform the specified desired search.
310 if ((type == KadSearch) && !Kademlia::CKademlia::IsRunning()) {
311 return _("Kad search can't be done if Kad is not running");
312 } else if ((type != KadSearch) && !theApp->IsConnectedED2K()) {
313 return _("eD2k search can't be done if eD2k is not connected");
316 if (params.typeText != ED2KFTSTR_PROGRAM) {
317 if (params.typeText.CmpNoCase(wxT("Any"))) {
318 m_resultType = params.typeText;
319 } else {
320 m_resultType.Clear();
322 } else {
323 // No check is to be made on returned results if the
324 // type is 'Programs', since this returns multiple types.
325 m_resultType.Clear();
328 if (type == KadSearch) {
329 Kademlia::WordList words;
330 Kademlia::CSearchManager::GetWords(params.searchString, &words);
331 if (!words.empty()) {
332 params.strKeyword = words.front();
333 } else {
334 return _("No keyword for Kad search - aborting");
338 bool supports64bit = type == KadSearch ? true : theApp->serverconnect->GetCurrentServer() != NULL && (theApp->serverconnect->GetCurrentServer()->GetTCPFlags() & SRV_TCPFLG_LARGEFILES);
339 bool packetUsing64bit;
341 // This MemFile is automatically free'd
342 CMemFilePtr data = CreateSearchData(params, type, supports64bit, packetUsing64bit);
344 if (data.get() == NULL) {
345 wxASSERT(_astrParserErrors.GetCount());
346 wxString error;
348 for (unsigned int i = 0; i < _astrParserErrors.GetCount(); ++i) {
349 error += _astrParserErrors[i] + wxT("\n");
352 return error;
355 m_searchType = type;
356 if (type == KadSearch) {
357 try {
358 if (*searchID == 0xffffffff) {
359 Kademlia::CSearchManager::StopSearch(0xffffffff, false);
362 // searchstring will get tokenized there
363 // The tab must be created with the Kad search ID, so searchID is updated.
364 Kademlia::CSearch* search = Kademlia::CSearchManager::PrepareFindKeywords(params.strKeyword, data->GetLength(), data->GetRawBuffer(), *searchID);
366 *searchID = search->GetSearchID();
367 m_currentSearch = *searchID;
368 m_KadSearchFinished = false;
369 } catch (const wxString& what) {
370 AddLogLineC(what);
371 return _("Unexpected error while attempting Kad search: ") + what;
373 } else {
374 // This is an ed2k search, local or global
375 m_currentSearch = *(searchID);
376 m_searchInProgress = true;
378 CPacket* searchPacket = new CPacket(*data.get(), OP_EDONKEYPROT, OP_SEARCHREQUEST);
380 theStats::AddUpOverheadServer(searchPacket->GetPacketSize());
381 theApp->serverconnect->SendPacket(searchPacket, (type == LocalSearch));
383 if (type == GlobalSearch) {
384 delete m_searchPacket;
385 m_searchPacket = searchPacket;
386 m_64bitSearchPacket = packetUsing64bit;
387 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ); // will be changed later when actually sending the packet!!
391 return wxEmptyString;
395 void CSearchList::LocalSearchEnd()
397 if (m_searchType == GlobalSearch) {
398 wxCHECK_RET(m_searchPacket, wxT("Global search, but no packet"));
400 // Ensure that every global search starts over.
401 theApp->serverlist->RemoveObserver(&m_serverQueue);
402 m_searchTimer.Start(750);
403 } else {
404 m_searchInProgress = false;
405 Notify_SearchLocalEnd();
410 uint32 CSearchList::GetSearchProgress() const
412 if (m_searchType == KadSearch) {
413 // We cannot measure the progress of Kad searches.
414 // But we can tell when they are over.
415 return m_KadSearchFinished ? 0xfffe : 0;
417 if (m_searchInProgress == false) { // true only for ED2K search
418 // No search, no progress ;)
419 return 0;
422 switch (m_searchType) {
423 case LocalSearch:
424 return 0xffff;
426 case GlobalSearch:
427 return 100 - (m_serverQueue.GetRemaining() * 100)
428 / theApp->serverlist->GetServerCount();
430 default:
431 wxFAIL;
433 return 0;
437 void CSearchList::OnGlobalSearchTimer(CTimerEvent& WXUNUSED(evt))
439 // Ensure that the server-queue contains the current servers.
440 if (m_searchPacket == NULL) {
441 // This was a pending event, handled after 'Stop' was pressed.
442 return;
443 } else if (!m_serverQueue.IsActive()) {
444 theApp->serverlist->AddObserver(&m_serverQueue);
447 // UDP requests must not be sent to this server.
448 const CServer* localServer = theApp->serverconnect->GetCurrentServer();
449 if (localServer) {
450 uint32 localIP = localServer->GetIP();
451 uint16 localPort = localServer->GetPort();
452 while (m_serverQueue.GetRemaining()) {
453 CServer* server = m_serverQueue.GetNext();
455 // Compare against the currently connected server.
456 if ((server->GetPort() == localPort) && (server->GetIP() == localIP)) {
457 // We've already requested from the local server.
458 continue;
459 } else {
460 if (server->SupportsLargeFilesUDP() && (server->GetUDPFlags() & SRV_UDPFLG_EXT_GETFILES)) {
461 CMemFile data(50);
462 uint32_t tagCount = 1;
463 data.WriteUInt32(tagCount);
464 CTagVarInt flags(CT_SERVER_UDPSEARCH_FLAGS, SRVCAP_UDP_NEWTAGS_LARGEFILES);
465 flags.WriteNewEd2kTag(&data);
466 CPacket *extSearchPacket = new CPacket(OP_GLOBSEARCHREQ3, m_searchPacket->GetPacketSize() + (uint32_t)data.GetLength(), OP_EDONKEYPROT);
467 extSearchPacket->CopyToDataBuffer(0, data.GetRawBuffer(), data.GetLength());
468 extSearchPacket->CopyToDataBuffer(data.GetLength(), m_searchPacket->GetDataBuffer(), m_searchPacket->GetPacketSize());
469 theStats::AddUpOverheadServer(extSearchPacket->GetPacketSize());
470 theApp->serverconnect->SendUDPPacket(extSearchPacket, server, true);
471 AddDebugLogLineN(logServerUDP, wxT("Sending OP_GLOBSEARCHREQ3 to server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()));
472 } else if (server->GetUDPFlags() & SRV_UDPFLG_EXT_GETFILES) {
473 if (!m_64bitSearchPacket || server->SupportsLargeFilesUDP()) {
474 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ2);
475 AddDebugLogLineN(logServerUDP, wxT("Sending OP_GLOBSEARCHREQ2 to server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()));
476 theStats::AddUpOverheadServer(m_searchPacket->GetPacketSize());
477 theApp->serverconnect->SendUDPPacket(m_searchPacket, server, false);
478 } else {
479 AddDebugLogLineN(logServerUDP, wxT("Skipped UDP search on server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()) + wxT(": No large file support"));
481 } else {
482 if (!m_64bitSearchPacket || server->SupportsLargeFilesUDP()) {
483 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ);
484 AddDebugLogLineN(logServerUDP, wxT("Sending OP_GLOBSEARCHREQ to server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()));
485 theStats::AddUpOverheadServer(m_searchPacket->GetPacketSize());
486 theApp->serverconnect->SendUDPPacket(m_searchPacket, server, false);
487 } else {
488 AddDebugLogLineN(logServerUDP, wxT("Skipped UDP search on server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()) + wxT(": No large file support"));
491 CoreNotify_Search_Update_Progress(GetSearchProgress());
492 return;
496 // No more servers left to ask.
497 StopSearch(true);
501 void CSearchList::ProcessSharedFileList(const byte* in_packet, uint32 size,
502 CUpDownClient* sender, bool *moreResultsAvailable, const wxString& directory)
504 wxCHECK_RET(sender, wxT("No sender in search-results from client."));
506 long searchID = reinterpret_cast<wxUIntPtr>(sender);
508 #ifndef AMULE_DAEMON
509 if (!theApp->amuledlg->m_searchwnd->CheckTabNameExists(sender->GetUserName())) {
510 theApp->amuledlg->m_searchwnd->CreateNewTab(sender->GetUserName() + wxT(" (0)"), searchID);
512 #endif
514 const CMemFile packet(in_packet, size);
515 uint32 results = packet.ReadUInt32();
516 bool unicoded = (sender->GetUnicodeSupport() != utf8strNone);
517 for (unsigned int i = 0; i != results; ++i){
518 CSearchFile* toadd = new CSearchFile(packet, unicoded, searchID, 0, 0, directory);
519 toadd->SetClientID(sender->GetUserIDHybrid());
520 toadd->SetClientPort(sender->GetUserPort());
521 AddToList(toadd, true);
524 if (moreResultsAvailable)
525 *moreResultsAvailable = false;
527 int iAddData = (int)(packet.GetLength() - packet.GetPosition());
528 if (iAddData == 1) {
529 uint8 ucMore = packet.ReadUInt8();
530 if (ucMore == 0x00 || ucMore == 0x01){
531 if (moreResultsAvailable) {
532 *moreResultsAvailable = (ucMore == 1);
539 void CSearchList::ProcessSearchAnswer(const uint8_t* in_packet, uint32_t size, bool optUTF8, uint32_t serverIP, uint16_t serverPort)
541 CMemFile packet(in_packet, size);
543 uint32_t results = packet.ReadUInt32();
544 for (; results > 0; --results) {
545 AddToList(new CSearchFile(packet, optUTF8, m_currentSearch, serverIP, serverPort), false);
550 void CSearchList::ProcessUDPSearchAnswer(const CMemFile& packet, bool optUTF8, uint32_t serverIP, uint16_t serverPort)
552 AddToList(new CSearchFile(packet, optUTF8, m_currentSearch, serverIP, serverPort), false);
556 bool CSearchList::AddToList(CSearchFile* toadd, bool clientResponse)
558 const uint64 fileSize = toadd->GetFileSize();
559 // If filesize is 0, or file is too large for the network, drop it
560 if ((fileSize == 0) || (fileSize > MAX_FILE_SIZE)) {
561 AddDebugLogLineN(logSearch,
562 CFormat(wxT("Dropped result with filesize %u: %s"))
563 % fileSize
564 % toadd->GetFileName());
566 delete toadd;
567 return false;
570 // If the result was not the type the user wanted, drop it.
571 if ((clientResponse == false) && !m_resultType.IsEmpty()) {
572 if (GetFileTypeByName(toadd->GetFileName()) != m_resultType) {
573 AddDebugLogLineN(logSearch,
574 CFormat( wxT("Dropped result type %s != %s, file %s") )
575 % GetFileTypeByName(toadd->GetFileName())
576 % m_resultType
577 % toadd->GetFileName());
579 delete toadd;
580 return false;
585 // Get, or implictly create, the map of results for this search
586 CSearchResultList& results = m_results[toadd->GetSearchID()];
588 for (size_t i = 0; i < results.size(); ++i) {
589 CSearchFile* item = results.at(i);
591 if ((toadd->GetFileHash() == item->GetFileHash()) && (toadd->GetFileSize() == item->GetFileSize())) {
592 AddDebugLogLineN(logSearch, CFormat(wxT("Received duplicate results for '%s' : %s")) % item->GetFileName() % item->GetFileHash().Encode());
593 // Add the child, possibly updating the parents filename.
594 item->AddChild(toadd);
595 Notify_Search_Update_Sources(item);
596 return true;
600 AddDebugLogLineN(logSearch,
601 CFormat(wxT("Added new result '%s' : %s"))
602 % toadd->GetFileName() % toadd->GetFileHash().Encode());
604 // New unique result, simply add and display.
605 results.push_back(toadd);
606 Notify_Search_Add_Result(toadd);
608 return true;
612 const CSearchResultList& CSearchList::GetSearchResults(long searchID) const
614 ResultMap::const_iterator it = m_results.find(searchID);
615 if (it != m_results.end()) {
616 return it->second;
619 // TODO: Should we assert in this case?
620 static CSearchResultList list;
621 return list;
625 void CSearchList::AddFileToDownloadByHash(const CMD4Hash& hash, uint8 cat)
627 ResultMap::iterator it = m_results.begin();
628 for ( ; it != m_results.end(); ++it ) {
629 CSearchResultList& list = it->second;
631 for ( unsigned int i = 0; i < list.size(); ++i ) {
632 if ( list[i]->GetFileHash() == hash ) {
633 CoreNotify_Search_Add_Download( list[i], cat );
635 return;
642 void CSearchList::StopSearch(bool globalOnly)
644 if (m_searchType == GlobalSearch) {
645 m_currentSearch = -1;
646 delete m_searchPacket;
647 m_searchPacket = NULL;
648 m_searchInProgress = false;
650 // Order is crucial here: on wx_MSW an additional event can be generated during the stop.
651 // So the packet has to be deleted first, so that OnGlobalSearchTimer() returns immediately
652 // without calling StopGlobalSearch() again.
653 m_searchTimer.Stop();
655 CoreNotify_Search_Update_Progress(0xffff);
656 } else if (m_searchType == KadSearch && !globalOnly) {
657 Kademlia::CSearchManager::StopSearch(m_currentSearch, false);
658 m_currentSearch = -1;
663 CSearchList::CMemFilePtr CSearchList::CreateSearchData(CSearchParams& params, SearchType type, bool supports64bit, bool& packetUsing64bit)
665 // Count the number of used parameters
666 unsigned int parametercount = 0;
667 if ( !params.typeText.IsEmpty() ) ++parametercount;
668 if ( params.minSize > 0 ) ++parametercount;
669 if ( params.maxSize > 0 ) ++parametercount;
670 if ( params.availability > 0 ) ++parametercount;
671 if ( !params.extension.IsEmpty() ) ++parametercount;
673 wxString typeText = params.typeText;
674 if (typeText == ED2KFTSTR_ARCHIVE){
675 // eDonkeyHybrid 0.48 uses type "Pro" for archives files
676 // www.filedonkey.com uses type "Pro" for archives files
677 typeText = ED2KFTSTR_PROGRAM;
678 } else if (typeText == ED2KFTSTR_CDIMAGE){
679 // eDonkeyHybrid 0.48 uses *no* type for iso/nrg/cue/img files
680 // www.filedonkey.com uses type "Pro" for CD-image files
681 typeText = ED2KFTSTR_PROGRAM;
684 // Must write parametercount - 1 parameter headers
685 CMemFilePtr data(new CMemFile(100));
687 _astrParserErrors.Empty();
688 _SearchExpr.m_aExpr.Empty();
690 s_strCurKadKeyword.Clear();
691 if (type == KadSearch) {
692 wxASSERT( !params.strKeyword.IsEmpty() );
693 s_strCurKadKeyword = params.strKeyword;
696 LexInit(params.searchString);
697 int iParseResult = yyparse();
698 LexFree();
700 if (_astrParserErrors.GetCount() > 0) {
701 for (unsigned int i=0; i < _astrParserErrors.GetCount(); ++i) {
702 AddLogLineNS(CFormat(wxT("Error %u: %s\n")) % i % _astrParserErrors[i]);
705 return CMemFilePtr(NULL);
708 if (iParseResult != 0) {
709 _astrParserErrors.Add(CFormat(wxT("Undefined error %i on search expression")) % iParseResult);
711 return CMemFilePtr(NULL);
714 if (type == KadSearch && s_strCurKadKeyword != params.strKeyword) {
715 AddDebugLogLineN(logSearch, CFormat(wxT("Keyword was rearranged, using '%s' instead of '%s'")) % s_strCurKadKeyword % params.strKeyword);
716 params.strKeyword = s_strCurKadKeyword;
719 parametercount += _SearchExpr.m_aExpr.GetCount();
721 /* Leave the unicode comment there, please... */
722 CSearchExprTarget target(data.get(), true /*I assume everyone is unicoded */ ? utf8strRaw : utf8strNone, supports64bit, packetUsing64bit);
724 unsigned int iParameterCount = 0;
725 if (_SearchExpr.m_aExpr.GetCount() <= 1) {
726 // lugdunummaster requested that searchs without OR or NOT operators,
727 // and hence with no more expressions than the string itself, be sent
728 // using a series of ANDed terms, intersecting the ANDs on the terms
729 // (but prepending them) instead of putting the boolean tree at the start
730 // like other searches. This type of search is supposed to take less load
731 // on servers. Go figure.
733 // input: "a" AND min=1 AND max=2
734 // instead of: AND AND "a" min=1 max=2
735 // we use: AND "a" AND min=1 max=2
737 if (_SearchExpr.m_aExpr.GetCount() > 0) {
738 if (++iParameterCount < parametercount) {
739 target.WriteBooleanAND();
741 target.WriteMetaDataSearchParam(_SearchExpr.m_aExpr[0]);
744 if (!typeText.IsEmpty()) {
745 if (++iParameterCount < parametercount) {
746 target.WriteBooleanAND();
748 // Type is always ascii string
749 target.WriteMetaDataSearchParamASCII(FT_FILETYPE, typeText);
752 if (params.minSize > 0) {
753 if (++iParameterCount < parametercount) {
754 target.WriteBooleanAND();
756 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_GREATER, params.minSize);
759 if (params.maxSize > 0){
760 if (++iParameterCount < parametercount) {
761 target.WriteBooleanAND();
763 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_LESS, params.maxSize);
766 if (params.availability > 0){
767 if (++iParameterCount < parametercount) {
768 target.WriteBooleanAND();
770 target.WriteMetaDataSearchParam(FT_SOURCES, ED2K_SEARCH_OP_GREATER, params.availability);
773 if (!params.extension.IsEmpty()){
774 if (++iParameterCount < parametercount) {
775 target.WriteBooleanAND();
777 target.WriteMetaDataSearchParam(FT_FILEFORMAT, params.extension);
780 //#warning TODO - I keep this here, ready if we ever allow such searches...
781 #if 0
782 if (complete > 0){
783 if (++iParameterCount < parametercount) {
784 target.WriteBooleanAND();
786 target.WriteMetaDataSearchParam(FT_COMPLETE_SOURCES, ED2K_SEARCH_OP_GREATER, complete);
789 if (minBitrate > 0){
790 if (++iParameterCount < parametercount) {
791 target.WriteBooleanAND();
793 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_BITRATE : FT_ED2K_MEDIA_BITRATE, ED2K_SEARCH_OP_GREATER, minBitrate);
796 if (minLength > 0){
797 if (++iParameterCount < parametercount) {
798 target.WriteBooleanAND();
800 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_LENGTH : FT_ED2K_MEDIA_LENGTH, ED2K_SEARCH_OP_GREATER, minLength);
803 if (!codec.IsEmpty()){
804 if (++iParameterCount < parametercount) {
805 target.WriteBooleanAND();
807 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_CODEC : FT_ED2K_MEDIA_CODEC, codec);
810 if (!title.IsEmpty()){
811 if (++iParameterCount < parametercount) {
812 target.WriteBooleanAND();
814 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_TITLE : FT_ED2K_MEDIA_TITLE, title);
817 if (!album.IsEmpty()){
818 if (++iParameterCount < parametercount) {
819 target.WriteBooleanAND();
821 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ALBUM : FT_ED2K_MEDIA_ALBUM, album);
824 if (!artist.IsEmpty()){
825 if (++iParameterCount < parametercount) {
826 target.WriteBooleanAND();
828 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ARTIST : FT_ED2K_MEDIA_ARTIST, artist);
830 #endif // 0
832 // If this assert fails... we're seriously fucked up
834 wxASSERT( iParameterCount == parametercount );
836 } else {
837 if (!params.extension.IsEmpty()) {
838 if (++iParameterCount < parametercount) {
839 target.WriteBooleanAND();
843 if (params.availability > 0) {
844 if (++iParameterCount < parametercount) {
845 target.WriteBooleanAND();
849 if (params.maxSize > 0){
850 if (++iParameterCount < parametercount) {
851 target.WriteBooleanAND();
855 if (params.minSize > 0) {
856 if (++iParameterCount < parametercount) {
857 target.WriteBooleanAND();
861 if (!typeText.IsEmpty()){
862 if (++iParameterCount < parametercount) {
863 target.WriteBooleanAND();
867 //#warning TODO - same as above...
868 #if 0
869 if (complete > 0){
870 if (++iParameterCount < parametercount) {
871 target.WriteBooleanAND();
875 if (minBitrate > 0){
876 if (++iParameterCount < parametercount) {
877 target.WriteBooleanAND();
881 if (minLength > 0) {
882 if (++iParameterCount < parametercount) {
883 target.WriteBooleanAND();
887 if (!codec.IsEmpty()){
888 if (++iParameterCount < parametercount) {
889 target.WriteBooleanAND();
893 if (!title.IsEmpty()){
894 if (++iParameterCount < parametercount) {
895 target.WriteBooleanAND();
899 if (!album.IsEmpty()) {
900 if (++iParameterCount < parametercount) {
901 target.WriteBooleanAND();
905 if (!artist.IsEmpty()) {
906 if (++iParameterCount < parametercount) {
907 target.WriteBooleanAND();
910 #endif // 0
912 // As above, if this fails, we're seriously fucked up.
913 wxASSERT( iParameterCount + _SearchExpr.m_aExpr.GetCount() == parametercount );
915 for (unsigned int j = 0; j < _SearchExpr.m_aExpr.GetCount(); ++j) {
916 if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_AND) {
917 target.WriteBooleanAND();
918 } else if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_OR) {
919 target.WriteBooleanOR();
920 } else if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_NOT) {
921 target.WriteBooleanNOT();
922 } else {
923 target.WriteMetaDataSearchParam(_SearchExpr.m_aExpr[j]);
927 if (!params.typeText.IsEmpty()) {
928 // Type is always ASCII string
929 target.WriteMetaDataSearchParamASCII(FT_FILETYPE, params.typeText);
932 if (params.minSize > 0) {
933 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_GREATER, params.minSize);
936 if (params.maxSize > 0) {
937 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_LESS, params.maxSize);
940 if (params.availability > 0) {
941 target.WriteMetaDataSearchParam(FT_SOURCES, ED2K_SEARCH_OP_GREATER, params.availability);
944 if (!params.extension.IsEmpty()) {
945 target.WriteMetaDataSearchParam(FT_FILEFORMAT, params.extension);
948 //#warning TODO - third and last warning of the same series.
949 #if 0
950 if (complete > 0) {
951 target.WriteMetaDataSearchParam(FT_COMPLETE_SOURCES, ED2K_SEARCH_OP_GREATER, pParams->uComplete);
954 if (minBitrate > 0) {
955 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_BITRATE : FT_ED2K_MEDIA_BITRATE, ED2K_SEARCH_OP_GREATER, minBitrate);
958 if (minLength > 0) {
959 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_LENGTH : FT_ED2K_MEDIA_LENGTH, ED2K_SEARCH_OP_GREATER, minLength);
962 if (!codec.IsEmpty()) {
963 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_CODEC : FT_ED2K_MEDIA_CODEC, codec);
966 if (!title.IsEmpty()) {
967 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_TITLE : FT_ED2K_MEDIA_TITLE, title);
970 if (!album.IsEmpty()) {
971 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ALBUM : FT_ED2K_MEDIA_ALBUM, album);
974 if (!artist.IsEmpty()) {
975 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ARTIST : FT_ED2K_MEDIA_ARTIST, artist);
978 #endif // 0
981 // Packet ready to go.
982 return data;
986 void CSearchList::KademliaSearchKeyword(uint32_t searchID, const Kademlia::CUInt128 *fileID,
987 const wxString& name, uint64_t size, const wxString& type, uint32_t kadPublishInfo, const TagPtrList& taglist)
989 EUtf8Str eStrEncode = utf8strRaw;
991 CMemFile temp(250);
992 byte fileid[16];
993 fileID->ToByteArray(fileid);
994 temp.WriteHash(CMD4Hash(fileid));
996 temp.WriteUInt32(0); // client IP
997 temp.WriteUInt16(0); // client port
999 // write tag list
1000 unsigned int uFilePosTagCount = temp.GetPosition();
1001 uint32 tagcount = 0;
1002 temp.WriteUInt32(tagcount); // dummy tag count, will be filled later
1004 // standard tags
1005 CTagString tagName(FT_FILENAME, name);
1006 tagName.WriteTagToFile(&temp, eStrEncode);
1007 tagcount++;
1009 CTagInt64 tagSize(FT_FILESIZE, size);
1010 tagSize.WriteTagToFile(&temp, eStrEncode);
1011 tagcount++;
1013 if (!type.IsEmpty()) {
1014 CTagString tagType(FT_FILETYPE, type);
1015 tagType.WriteTagToFile(&temp, eStrEncode);
1016 tagcount++;
1019 // Misc tags (bitrate, etc)
1020 for (TagPtrList::const_iterator it = taglist.begin(); it != taglist.end(); ++it) {
1021 (*it)->WriteTagToFile(&temp,eStrEncode);
1022 tagcount++;
1025 temp.Seek(uFilePosTagCount, wxFromStart);
1026 temp.WriteUInt32(tagcount);
1028 temp.Seek(0, wxFromStart);
1030 CSearchFile *tempFile = new CSearchFile(temp, (eStrEncode == utf8strRaw), searchID, 0, 0, wxEmptyString, true);
1031 tempFile->SetKadPublishInfo(kadPublishInfo);
1033 AddToList(tempFile);
1036 void CSearchList::UpdateSearchFileByHash(const CMD4Hash& hash)
1038 for (ResultMap::iterator it = m_results.begin(); it != m_results.end(); ++it) {
1039 CSearchResultList& results = it->second;
1040 for (size_t i = 0; i < results.size(); ++i) {
1041 CSearchFile* item = results.at(i);
1043 if (hash == item->GetFileHash()) {
1044 // This covers only parent items,
1045 // child items have to be updated separately.
1046 Notify_Search_Update_Sources(item);
1052 // File_checked_for_headers