Upstream tarball 20080324
[amule.git] / src / SearchList.cpp
blob3d7a415b8000cfaeb2a171285171ea811982caa2
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 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.
20 //
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/FileTags.h>
32 #include "updownclient.h" // Needed for CUpDownClient
33 #include "MemFile.h" // Needed for CMemFile
34 #include "amule.h" // Needed for theApp
35 #include "ServerConnect.h" // Needed for theApp->serverconnect
36 #include "Server.h" // Needed for CServer
37 #include "ServerList.h" // Needed for theApp->serverlist
38 #include "Statistics.h" // Needed for theStats
39 #include "ObservableQueue.h"// Needed for CQueueObserver
40 #include <common/Format.h>
41 #include "Logger.h" // Needed for AddLogLineM/...
42 #include "Packet.h" // Needed for CPacket
43 #include "GuiEvents.h" // Needed for Notify_*
46 #ifndef AMULE_DAEMON
47 #include "amuleDlg.h" // Needed for CamuleDlg
48 #include "SearchDlg.h" // Needed for CSearchDlg
49 #endif
51 #include "kademlia/kademlia/Kademlia.h"
52 #include "kademlia/kademlia/Search.h"
54 #include "SearchExpr.h"
55 #include "Scanner.h.in"
58 extern int yyparse();
59 extern int yyerror(const char* errstr);
60 extern int yyerror(wxString errstr);
62 static wxString s_strCurKadKeyword;
64 static CSearchExpr _SearchExpr;
66 wxArrayString _astrParserErrors;
69 // Helper function for lexer.
70 void ParsedSearchExpression(const CSearchExpr* pexpr)
72 int iOpAnd = 0;
73 int iOpOr = 0;
74 int iOpNot = 0;
76 for (unsigned int i = 0; i < pexpr->m_aExpr.Count(); i++) {
77 wxString str(pexpr->m_aExpr[i]);
78 if (str == SEARCHOPTOK_AND) {
79 iOpAnd++;
80 } else if (str == SEARCHOPTOK_OR) {
81 iOpOr++;
82 } else if (str == SEARCHOPTOK_NOT) {
83 iOpNot++;
87 // this limit (+ the additional operators which will be added later) has to match the limit in 'CreateSearchExpressionTree'
88 // +1 Type (Audio, Video)
89 // +1 MinSize
90 // +1 MaxSize
91 // +1 Avail
92 // +1 Extension
93 // +1 Complete sources
94 // +1 Codec
95 // +1 Bitrate
96 // +1 Length
97 // +1 Title
98 // +1 Album
99 // +1 Artist
100 // ---------------
101 // 12
102 if (iOpAnd + iOpOr + iOpNot > 10) {
103 yyerror(wxT("Search expression is too complex"));
106 _SearchExpr.m_aExpr.Empty();
108 // optimize search expression, if no OR nor NOT specified
109 if (iOpAnd > 0 && iOpOr == 0 && iOpNot == 0) {
110 wxString strAndTerms;
111 for (unsigned int i = 0; i < pexpr->m_aExpr.Count(); i++) {
112 if (pexpr->m_aExpr[i] != SEARCHOPTOK_AND) {
113 // Minor optimization: Because we added the Kad keyword to the boolean search expression,
114 // we remove it here (and only here) again because we know that the entire search expression
115 // does only contain (implicit) ANDed strings.
116 if (pexpr->m_aExpr[i] != s_strCurKadKeyword) {
117 if (!strAndTerms.IsEmpty()) {
118 strAndTerms += ' ';
120 strAndTerms += pexpr->m_aExpr[i];
124 wxASSERT( _SearchExpr.m_aExpr.Count() == 0);
125 _SearchExpr.m_aExpr.Add(strAndTerms);
126 } else {
127 if (pexpr->m_aExpr.GetCount() != 1 || pexpr->m_aExpr[0] != s_strCurKadKeyword)
128 _SearchExpr.Add(pexpr);
133 //! Helper class for packet creation
134 class CSearchExprTarget
136 public:
137 CSearchExprTarget(CMemFile* pData, EUtf8Str eStrEncode)
139 m_data = pData;
140 m_eStrEncode = eStrEncode;
143 void WriteBooleanAND()
145 m_data->WriteUInt8(0); // boolean operator parameter type
146 m_data->WriteUInt8(0x00); // "AND"
149 void WriteBooleanOR()
151 m_data->WriteUInt8(0); // boolean operator parameter type
152 m_data->WriteUInt8(0x01); // "OR"
155 void WriteBooleanNOT()
157 m_data->WriteUInt8(0); // boolean operator parameter type
158 m_data->WriteUInt8(0x02); // "NOT"
161 void WriteMetaDataSearchParam(const wxString& rstrValue)
163 m_data->WriteUInt8(1); // string parameter type
164 m_data->WriteString(rstrValue, m_eStrEncode); // string value
167 void WriteMetaDataSearchParam(uint8 uMetaTagID, const wxString& rstrValue)
169 m_data->WriteUInt8(2); // string parameter type
170 m_data->WriteString(rstrValue, m_eStrEncode); // string value
171 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
172 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
175 void WriteMetaDataSearchParamASCII(uint8 uMetaTagID, const wxString& rstrValue)
177 m_data->WriteUInt8(2); // string parameter type
178 m_data->WriteString(rstrValue, utf8strNone); // string value
179 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
180 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
183 void WriteMetaDataSearchParam(const wxString& pszMetaTagID, const wxString& rstrValue)
185 m_data->WriteUInt8(2); // string parameter type
186 m_data->WriteString(rstrValue, m_eStrEncode); // string value
187 m_data->WriteString(pszMetaTagID); // meta tag ID
190 void WriteMetaDataSearchParam(uint8 uMetaTagID, uint8 uOperator, uint32 uValue, bool WXUNUSED(bEd2k))
192 m_data->WriteUInt8(3); // numeric parameter type
193 m_data->WriteUInt32(uValue); // numeric value
194 m_data->WriteUInt8(uOperator); // comparison operator
195 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
196 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
199 void WriteMetaDataSearchParam(const wxString& pszMetaTagID, uint8 uOperator, uint32 uValue, bool WXUNUSED(bEd2k))
201 m_data->WriteUInt8(3); // numeric parameter type
202 m_data->WriteUInt32(uValue); // numeric value
203 m_data->WriteUInt8(uOperator); // comparison operator
204 m_data->WriteString(pszMetaTagID); // meta tag ID
207 void WriteOldMinMetaDataSearchParam(uint8 uMetaTagID, uint32 uValue, bool bEd2k)
209 uint8 uOperator;
210 if (bEd2k){
211 uOperator = ED2K_SEARCH_OP_GREATER;
212 uValue -= 1;
213 } else {
214 uOperator = KAD_SEARCH_OP_GREATER_EQUAL;
216 WriteMetaDataSearchParam(uMetaTagID, uOperator, uValue, bEd2k);
219 void WriteOldMinMetaDataSearchParam(const wxString& pszMetaTagID, uint32 uValue, bool bEd2k)
221 uint8 uOperator;
222 if (bEd2k){
223 uOperator = ED2K_SEARCH_OP_GREATER;
224 uValue -= 1;
225 } else {
226 uOperator = KAD_SEARCH_OP_GREATER_EQUAL;
228 WriteMetaDataSearchParam(pszMetaTagID, uOperator, uValue, bEd2k);
231 void WriteOldMaxMetaDataSearchParam(const wxString& pszMetaTagID, uint32 uValue, bool bEd2k)
233 uint8 uOperator;
234 if (bEd2k){
235 uOperator = ED2K_SEARCH_OP_LESS;
236 uValue += 1;
237 } else {
238 uOperator = KAD_SEARCH_OP_LESS_EQUAL;
240 WriteMetaDataSearchParam(pszMetaTagID, uOperator, uValue, bEd2k);
243 void WriteOldMaxMetaDataSearchParam(uint8 uMetaTagID, uint32 uValue, bool bEd2k)
245 uint8 uOperator;
246 if (bEd2k){
247 uOperator = ED2K_SEARCH_OP_LESS;
248 uValue += 1;
249 } else {
250 uOperator = KAD_SEARCH_OP_LESS_EQUAL;
252 WriteMetaDataSearchParam(uMetaTagID, uOperator, uValue, bEd2k);
255 protected:
256 CMemFile* m_data;
257 EUtf8Str m_eStrEncode;
263 ///////////////////////////////////////////////////////////
264 // CSearchList
266 BEGIN_EVENT_TABLE(CSearchList, wxEvtHandler)
267 EVT_MULE_TIMER(wxID_ANY, CSearchList::OnGlobalSearchTimer)
268 END_EVENT_TABLE()
271 CSearchList::CSearchList()
272 : m_searchTimer(this, 0 /* Timer-id doesn't matter. */ ),
273 m_searchType(LocalSearch),
274 m_searchInProgress(false),
275 m_currentSearch(-1),
276 m_searchPacket(NULL)
281 CSearchList::~CSearchList()
283 StopGlobalSearch();
285 while (!m_results.empty()) {
286 RemoveResults(m_results.begin()->first);
291 void CSearchList::RemoveResults(long searchID)
293 // A non-existant search id will just be ignored
294 Kademlia::CSearchManager::StopSearch(searchID, true);
296 ResultMap::iterator it = m_results.find(searchID);
297 if ( it != m_results.end() ) {
298 CSearchResultList& list = it->second;
300 for (size_t i = 0; i < list.size(); ++i) {
301 delete list.at(i);
304 m_results.erase( it );
309 wxString CSearchList::StartNewSearch(uint32* searchID, SearchType type, const CSearchParams& params)
311 // Check that we can actually perform the specified desired search.
312 if ((type == KadSearch) && !Kademlia::CKademlia::IsRunning()) {
313 return _("Kad search can't be done if Kad is not running");
314 } else if ((type != KadSearch) && !theApp->IsConnectedED2K()) {
315 return _("ED2K search can't be done if ED2K is not connected");
318 if (params.typeText != ED2KFTSTR_PROGRAM) {
319 if (params.typeText.CmpNoCase(wxT("Any"))) {
320 m_resultType = params.typeText;
321 } else {
322 m_resultType.Clear();
324 } else {
325 // No check is to be made on returned results if the
326 // type is 'Programs', since this returns multiple types.
327 m_resultType.Clear();
330 // This MemFile is automatically free'd, except for kad searches.
331 CMemFilePtr data = CreateSearchData(params, type);
333 if (data.get() == NULL) {
334 wxASSERT(_astrParserErrors.Count());
335 wxString error;
337 for (unsigned int i = 0; i < _astrParserErrors.Count(); ++i) {
338 error += _astrParserErrors[i] + wxT("\n");
341 return error;
344 m_searchType = type;
345 if (type == KadSearch) {
346 try {
347 if (*searchID == 0xffffffff) {
348 Kademlia::CSearchManager::StopSearch(0xffffffff, false);
351 // Kad search takes ownership of data and searchstring will get tokenized there
352 // The tab must be created with the Kad search ID, so seardhID is updated.
353 Kademlia::CSearch* search = Kademlia::CSearchManager::PrepareFindKeywords(
354 params.searchString, data.release(), *searchID);
356 *searchID = search->GetSearchID();
357 } catch (const wxString& what) {
358 AddLogLineM(true, what);
359 return _("Unexpected error while attempting Kad search: ") + what;
361 } else {
362 // This is an ed2k search, local or global
363 m_currentSearch = *(searchID);
364 m_searchInProgress = true;
366 CPacket* searchPacket = new CPacket(*data.get(), OP_EDONKEYPROT, OP_SEARCHREQUEST);
368 theStats::AddUpOverheadServer(searchPacket->GetPacketSize());
369 theApp->serverconnect->SendPacket(searchPacket, (type == LocalSearch));
371 if (type == GlobalSearch) {
372 m_searchPacket = searchPacket;
374 // The OPCode must be changed since global searches are UDP requests
375 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ);
379 return wxEmptyString;
383 void CSearchList::LocalSearchEnd()
385 if (m_searchType == GlobalSearch) {
386 wxCHECK_RET(m_searchPacket, wxT("Global search, but no packet"));
388 // Ensure that every global search starts over.
389 theApp->serverlist->RemoveObserver(&m_serverQueue);
390 m_searchTimer.Start(750);
391 } else {
392 m_searchInProgress = false;
393 Notify_SearchLocalEnd();
398 uint32 CSearchList::GetSearchProgress() const
400 if (m_searchInProgress == false) {
401 // No search, no progress ;)
402 return 0;
405 switch (m_searchType) {
406 case LocalSearch:
407 return 0xffff;
409 case GlobalSearch:
410 return 100 - (m_serverQueue.GetRemaining() * 100)
411 / theApp->serverlist->GetServerCount();
413 case KadSearch:
414 // We cannot meassure the progress of Kad searches.
415 return 0;
417 default:
418 wxCHECK(false, 0);
423 void CSearchList::OnGlobalSearchTimer(CTimerEvent& WXUNUSED(evt))
425 // Ensure that the server-queue contains the current servers.
426 if (m_searchPacket == NULL) {
427 // This was a pending event, handled after 'Stop' was pressed.
428 return;
429 } else if (!m_serverQueue.IsActive()) {
430 theApp->serverlist->AddObserver(&m_serverQueue);
433 // UDP requests must not be sent to this server.
434 const CServer* localServer = theApp->serverconnect->GetCurrentServer();
435 if (localServer) {
436 uint32 localIP = localServer->GetIP();
437 uint16 localPort = localServer->GetPort();
438 while (m_serverQueue.GetRemaining()) {
439 CServer* server = m_serverQueue.GetNext();
441 // Compare against the currently connected server.
442 if ((server->GetPort() == localPort) && (server->GetIP() == localIP)) {
443 // We've already requested from the local server.
444 continue;
445 } else {
446 theStats::AddUpOverheadServer(m_searchPacket->GetPacketSize());
447 theApp->serverconnect->SendUDPPacket(m_searchPacket, server, false);
448 CoreNotify_Search_Update_Progress(GetSearchProgress());
449 return;
453 // No more servers left to ask.
454 StopGlobalSearch();
458 void CSearchList::ProcessSharedFileList(const byte* in_packet, uint32 size,
459 CUpDownClient* sender, bool *moreResultsAvailable, const wxString& directory)
461 wxCHECK_RET(sender, wxT("No sender in search-results from client."));
463 long searchID = reinterpret_cast<wxUIntPtr>(sender);
465 #ifndef AMULE_DAEMON
466 if (!theApp->amuledlg->m_searchwnd->CheckTabNameExists(sender->GetUserName())) {
467 theApp->amuledlg->m_searchwnd->CreateNewTab(sender->GetUserName() + wxT(" (0)"), searchID);
469 #endif
471 const CMemFile packet(in_packet, size);
472 uint32 results = packet.ReadUInt32();
473 bool unicoded = sender->GetUnicodeSupport();
474 for (unsigned int i = 0; i != results; ++i){
475 CSearchFile* toadd = new CSearchFile(packet, unicoded, searchID, 0, 0, directory);
476 if (sender){
477 toadd->SetClientID(sender->GetUserIDHybrid());
478 toadd->SetClientPort(sender->GetUserPort());
481 AddToList(toadd, true);
484 if (moreResultsAvailable)
485 *moreResultsAvailable = false;
487 int iAddData = (int)(packet.GetLength() - packet.GetPosition());
488 if (iAddData == 1) {
489 uint8 ucMore = packet.ReadUInt8();
490 if (ucMore == 0x00 || ucMore == 0x01){
491 if (moreResultsAvailable) {
492 *moreResultsAvailable = (bool)ucMore;
499 void CSearchList::ProcessSearchAnswer(const byte* in_packet, uint32 size, bool optUTF8, uint32 WXUNUSED(serverIP), uint16 WXUNUSED(serverPort))
501 CMemFile packet(in_packet,size);
503 uint32 results = packet.ReadUInt32();
504 for (unsigned int i = 0; i != results; ++i) {
505 AddToList(new CSearchFile(packet, optUTF8, m_currentSearch));
510 void CSearchList::ProcessUDPSearchAnswer(const CMemFile& packet, bool optUTF8, uint32 serverIP, uint16 serverPort)
512 AddToList(new CSearchFile(packet, optUTF8, m_currentSearch, serverIP, serverPort));
516 bool CSearchList::AddToList(CSearchFile* toadd, bool clientResponse)
518 const uint64 fileSize = toadd->GetFileSize();
519 // If filesize is 0, or file is too large for the network, drop it
520 if ((fileSize == 0) || (fileSize > MAX_FILE_SIZE)) {
521 AddDebugLogLineM(false, logSearch,
522 CFormat(wxT("Dropped result with filesize %u: %s"))
523 % fileSize
524 % toadd->GetFileName());
526 delete toadd;
527 return false;
530 // If the result was not the type the user wanted, drop it.
531 if ((clientResponse == false) && !m_resultType.IsEmpty()) {
532 if (GetFileTypeByName(toadd->GetFileName()) != m_resultType) {
533 AddDebugLogLineM( false, logSearch,
534 CFormat( wxT("Dropped result type %s != %s, file %s") )
535 % GetFileTypeByName(toadd->GetFileName())
536 % m_resultType
537 % toadd->GetFileName());
539 delete toadd;
540 return false;
545 // Get, or implictly create, the map of results for this search
546 CSearchResultList& results = m_results[toadd->GetSearchID()];
548 for (size_t i = 0; i < results.size(); ++i) {
549 CSearchFile* item = results.at(i);
551 if ((toadd->GetFileHash() == item->GetFileHash())
552 && (toadd->GetFileSize() == item->GetFileSize())) {
554 AddDebugLogLineM(false, logSearch,
555 CFormat(wxT("Received duplicate results for '%s' : %s"))
556 % item->GetFileName() % item->GetFileHash().Encode());
558 // If no children exists, then we add the current item. The
559 // "parent" item will show the most common filename and the
560 // sum of sources for all variants.
561 if (item->GetChildren().empty()) {
562 if (toadd->GetFileName() == item->GetFileName()) {
563 AddDebugLogLineM( false, logSearch,
564 CFormat(wxT("Merged results for '%s'"))
565 % item->GetFileName());
567 // Merge duplicate items rather than creating a child item
568 item->AddSources(toadd->GetSourceCount(), toadd->GetCompleteSourceCount());
569 Notify_Search_Update_Sources(item);
570 delete toadd;
571 return true;
572 } else {
573 AddDebugLogLineM(false, logSearch,
574 CFormat(wxT("Created initial child for result '%s'"))
575 % item->GetFileName());
577 // The first child will always be the first result we received.
578 item->AddChild(new CSearchFile(*item));
582 AddDebugLogLineM( false, logSearch,
583 CFormat(wxT("Adding child '%s' to result '%s'"))
584 % toadd->GetFileName() % item->GetFileName());
586 // Parent item includes sum of all sources for this file
587 item->AddSources(toadd->GetSourceCount(), toadd->GetCompleteSourceCount());
588 // Add the child, possibly updating the parents filename.
589 item->AddChild(toadd);
591 Notify_Search_Update_Sources(item);
593 return true;
597 AddDebugLogLineM(false, logSearch,
598 CFormat(wxT("Added new result '%s' : %s"))
599 % toadd->GetFileName() % toadd->GetFileHash().Encode());
601 // New unique result, simply add and display.
602 results.push_back(toadd);
603 Notify_Search_Add_Result(toadd);
605 return true;
609 const CSearchResultList& CSearchList::GetSearchResults(long searchID) const
611 ResultMap::const_iterator it = m_results.find(searchID);
612 if (it != m_results.end()) {
613 return it->second;
616 // TODO: Should we assert in this case?
617 static CSearchResultList list;
618 return list;
622 void CSearchList::AddFileToDownloadByHash(const CMD4Hash& hash, uint8 cat)
624 ResultMap::iterator it = m_results.begin();
625 for ( ; it != m_results.end(); ++it ) {
626 CSearchResultList& list = it->second;
628 for ( unsigned int i = 0; i < list.size(); ++i ) {
629 if ( list[i]->GetFileHash() == hash ) {
630 CoreNotify_Search_Add_Download( list[i], cat );
632 return;
639 void CSearchList::StopGlobalSearch()
641 m_searchTimer.Stop();
643 m_currentSearch = -1;
644 delete m_searchPacket;
645 m_searchPacket = NULL;
646 m_searchInProgress = false;
648 CoreNotify_Search_Update_Progress(0xffff);
652 CSearchList::CMemFilePtr CSearchList::CreateSearchData(const CSearchParams& params, SearchType type)
654 const bool kad = (type == KadSearch);
656 // Count the number of used parameters
657 unsigned int parametercount = 0;
658 if ( !params.typeText.IsEmpty() ) ++parametercount;
659 if ( params.minSize > 0 ) ++parametercount;
660 if ( params.maxSize > 0 ) ++parametercount;
661 if ( params.availability > 0 ) ++parametercount;
662 if ( !params.extension.IsEmpty() ) ++parametercount;
664 wxString typeText = params.typeText;
665 if (typeText == ED2KFTSTR_ARCHIVE){
666 // eDonkeyHybrid 0.48 uses type "Pro" for archives files
667 // www.filedonkey.com uses type "Pro" for archives files
668 typeText = ED2KFTSTR_PROGRAM;
669 } else if (typeText == ED2KFTSTR_CDIMAGE){
670 // eDonkeyHybrid 0.48 uses *no* type for iso/nrg/cue/img files
671 // www.filedonkey.com uses type "Pro" for CD-image files
672 typeText = ED2KFTSTR_PROGRAM;
675 // Must write parametercount - 1 parameter headers
676 CMemFilePtr data(new CMemFile(100));
678 if (type == KadSearch) {
679 // We need to make some room for the keyword hash
680 data->WriteUInt128(CUInt128());
681 // and the search type (0/1 if there is ed2k data or not)
682 // There will obviously be... at least the search string.
683 data->WriteUInt8(0);
686 _astrParserErrors.Empty();
687 _SearchExpr.m_aExpr.Empty();
689 LexInit(params.searchString);
690 int iParseResult = yyparse();
691 LexFree();
693 #ifdef __DEBUG__
694 printf("Search parsing result for \"%s\": %i\n",
695 (const char*)unicode2UTF8(params.searchString),iParseResult);
696 #endif
697 if (_astrParserErrors.Count() > 0) {
698 for (unsigned int i=0; i < _astrParserErrors.Count(); ++i) {
699 printf("Error %u: %s\n",i,(const char*)unicode2UTF8(_astrParserErrors[i]));
702 return CMemFilePtr(NULL);
705 if (iParseResult != 0) {
706 _astrParserErrors.Add(wxString::Format(wxT("Undefined error %i on search expression"),iParseResult));
708 return CMemFilePtr(NULL);
711 #ifdef __DEBUG__
712 printf("Search expression: ");
713 for (unsigned int i = 0; i < _SearchExpr.m_aExpr.Count(); i++){
714 printf("%s ",(const char*)unicode2char(_SearchExpr.m_aExpr[i]));
716 printf("\nExpression count: %i\n",(int)_SearchExpr.m_aExpr.GetCount());
717 #endif
719 parametercount += _SearchExpr.m_aExpr.GetCount();
721 #ifdef __DEBUG__
722 printf("Parameters: %i\n",parametercount);
723 #endif
725 /* Leave the unicode comment there, please... */
726 CSearchExprTarget target(data.get(), true /*I assume everyone is unicoded */ ? utf8strRaw : utf8strNone);
728 unsigned int iParameterCount = 0;
729 if (_SearchExpr.m_aExpr.GetCount() <= 1) {
730 // lugdunummaster requested that searchs without OR or NOT operators,
731 // and hence with no more expressions than the string itself, be sent
732 // using a series of ANDed terms, intersecting the ANDs on the terms
733 // (but prepending them) instead of putting the boolean tree at the start
734 // like other searches. This type of search is supposed to take less load
735 // on servers. Go figure.
737 // input: "a" AND min=1 AND max=2
738 // instead of: AND AND "a" min=1 max=2
739 // we use: AND "a" AND min=1 max=2
741 if (_SearchExpr.m_aExpr.GetCount() > 0) {
742 if (++iParameterCount < parametercount) {
743 target.WriteBooleanAND();
745 target.WriteMetaDataSearchParam(_SearchExpr.m_aExpr[0]);
748 if (!typeText.IsEmpty()) {
749 if (++iParameterCount < parametercount) {
750 target.WriteBooleanAND();
752 // Type is always ascii string
753 target.WriteMetaDataSearchParamASCII(FT_FILETYPE, typeText);
756 if (params.minSize > 0) {
757 if (++iParameterCount < parametercount) {
758 target.WriteBooleanAND();
760 target.WriteOldMinMetaDataSearchParam(FT_FILESIZE, params.minSize, !kad);
763 if (params.maxSize > 0){
764 if (++iParameterCount < parametercount) {
765 target.WriteBooleanAND();
767 target.WriteOldMaxMetaDataSearchParam(FT_FILESIZE, params.maxSize, !kad);
770 if (params.availability > 0){
771 if (++iParameterCount < parametercount) {
772 target.WriteBooleanAND();
774 target.WriteOldMinMetaDataSearchParam(FT_SOURCES, params.availability, !kad);
777 if (!params.extension.IsEmpty()){
778 if (++iParameterCount < parametercount) {
779 target.WriteBooleanAND();
781 target.WriteMetaDataSearchParam(FT_FILEFORMAT, params.extension);
784 //#warning TODO - I keep this here, ready if we ever allow such searches...
785 #if 0
786 if (complete > 0){
787 if (++iParameterCount < parametercount) {
788 target.WriteBooleanAND();
790 target.WriteOldMinMetaDataSearchParam(FT_COMPLETE_SOURCES, complete, !kad);
793 if (minBitrate > 0){
794 if (++iParameterCount < parametercount) {
795 target.WriteBooleanAND();
797 target.WriteOldMinMetaDataSearchParam(kad ? TAG_MEDIA_BITRATE : FT_ED2K_MEDIA_BITRATE, minBitrate, !kad);
800 if (minLength > 0){
801 if (++iParameterCount < parametercount) {
802 target.WriteBooleanAND();
804 target.WriteOldMinMetaDataSearchParam(kad ? TAG_MEDIA_LENGTH : FT_ED2K_MEDIA_LENGTH, minLength, !kad);
807 if (!codec.IsEmpty()){
808 if (++iParameterCount < parametercount) {
809 target.WriteBooleanAND();
811 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_CODEC : FT_ED2K_MEDIA_CODEC, codec);
814 if (!title.IsEmpty()){
815 if (++iParameterCount < parametercount) {
816 target.WriteBooleanAND();
818 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_TITLE : FT_ED2K_MEDIA_TITLE, title);
821 if (!album.IsEmpty()){
822 if (++iParameterCount < parametercount) {
823 target.WriteBooleanAND();
825 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_ALBUM : FT_ED2K_MEDIA_ALBUM, album);
828 if (!artist.IsEmpty()){
829 if (++iParameterCount < parametercount) {
830 target.WriteBooleanAND();
832 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_ARTIST : FT_ED2K_MEDIA_ARTIST, artist);
834 #endif // 0
836 // If this assert fails... we're seriously fucked up
838 wxASSERT( iParameterCount == parametercount );
840 } else {
841 if (!params.extension.IsEmpty()) {
842 if (++iParameterCount < parametercount) {
843 target.WriteBooleanAND();
847 if (params.availability > 0) {
848 if (++iParameterCount < parametercount) {
849 target.WriteBooleanAND();
853 if (params.maxSize > 0){
854 if (++iParameterCount < parametercount) {
855 target.WriteBooleanAND();
859 if (params.minSize > 0) {
860 if (++iParameterCount < parametercount) {
861 target.WriteBooleanAND();
865 if (!typeText.IsEmpty()){
866 if (++iParameterCount < parametercount) {
867 target.WriteBooleanAND();
871 //#warning TODO - same as above...
872 #if 0
873 if (complete > 0){
874 if (++iParameterCount < parametercount) {
875 target.WriteBooleanAND();
879 if (minBitrate > 0){
880 if (++iParameterCount < parametercount) {
881 target.WriteBooleanAND();
885 if (minLength > 0) {
886 if (++iParameterCount < parametercount) {
887 target.WriteBooleanAND();
891 if (!codec.IsEmpty()){
892 if (++iParameterCount < parametercount) {
893 target.WriteBooleanAND();
897 if (!title.IsEmpty()){
898 if (++iParameterCount < parametercount) {
899 target.WriteBooleanAND();
903 if (!album.IsEmpty()) {
904 if (++iParameterCount < parametercount) {
905 target.WriteBooleanAND();
909 if (!artist.IsEmpty()) {
910 if (++iParameterCount < parametercount) {
911 target.WriteBooleanAND();
914 #endif // 0
916 // As above, if this fails, we're seriously fucked up.
917 wxASSERT( iParameterCount + _SearchExpr.m_aExpr.GetCount() == parametercount );
919 for (unsigned int j = 0; j < _SearchExpr.m_aExpr.GetCount(); ++j) {
920 if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_AND) {
921 target.WriteBooleanAND();
922 } else if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_OR) {
923 target.WriteBooleanOR();
924 } else if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_NOT) {
925 target.WriteBooleanNOT();
926 } else {
927 target.WriteMetaDataSearchParam(_SearchExpr.m_aExpr[j]);
931 if (!params.typeText.IsEmpty()) {
932 // Type is always ASCII string
933 target.WriteMetaDataSearchParamASCII(FT_FILETYPE, params.typeText);
936 if (params.minSize > 0) {
937 target.WriteOldMinMetaDataSearchParam(FT_FILESIZE, params.minSize, !kad);
940 if (params.maxSize > 0) {
941 target.WriteOldMaxMetaDataSearchParam(FT_FILESIZE, params.maxSize, !kad);
944 if (params.availability > 0) {
945 target.WriteOldMinMetaDataSearchParam(FT_SOURCES, params.availability, !kad);
948 if (!params.extension.IsEmpty()) {
949 target.WriteMetaDataSearchParam(FT_FILEFORMAT, params.extension);
952 //#warning TODO - third and last warning of the same series.
953 #if 0
954 if (complete > 0) {
955 target.WriteOldMinMetaDataSearchParam(FT_COMPLETE_SOURCES, pParams->uComplete, !kad);
958 if (minBitrate > 0) {
959 target.WriteOldMinMetaDataSearchParam(kad ? TAG_MEDIA_BITRATE : FT_ED2K_MEDIA_BITRATE, minBitrate, !kad);
962 if (minLength > 0) {
963 target.WriteOldMinMetaDataSearchParam(kad ? TAG_MEDIA_LENGTH : FT_ED2K_MEDIA_LENGTH, minLength, bEd2k);
966 if (!codec.IsEmpty()) {
967 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_CODEC : FT_ED2K_MEDIA_CODEC, codec);
970 if (!title.IsEmpty()) {
971 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_TITLE : FT_ED2K_MEDIA_TITLE, title);
974 if (!album.IsEmpty()) {
975 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_ALBUM : FT_ED2K_MEDIA_ALBUM, album);
978 if (!artist.IsEmpty()) {
979 target.WriteMetaDataSearchParam(kad ? TAG_MEDIA_ARTIST : FT_ED2K_MEDIA_ARTIST, artist);
982 #endif // 0
985 // Packet ready to go.
986 return data;
990 void CSearchList::KademliaSearchKeyword(uint32 searchID, const Kademlia::CUInt128* fileID,
991 const wxString& name, uint64 size, const wxString& type, const TagPtrList& taglist)
993 EUtf8Str eStrEncode = utf8strRaw;
995 CMemFile temp(250);
996 byte fileid[16];
997 fileID->ToByteArray(fileid);
998 temp.WriteHash(CMD4Hash(fileid));
1000 temp.WriteUInt32(0); // client IP
1001 temp.WriteUInt16(0); // client port
1003 // write tag list
1004 unsigned int uFilePosTagCount = temp.GetPosition();
1005 uint32 tagcount = 0;
1006 temp.WriteUInt32(tagcount); // dummy tag count, will be filled later
1008 // standard tags
1009 CTagString tagName(FT_FILENAME, name);
1010 tagName.WriteTagToFile(&temp, eStrEncode);
1011 tagcount++;
1013 CTagInt64 tagSize(FT_FILESIZE, size);
1014 tagSize.WriteTagToFile(&temp, eStrEncode);
1015 tagcount++;
1017 if (!type.IsEmpty()) {
1018 CTagString tagType(FT_FILETYPE, type);
1019 tagType.WriteTagToFile(&temp, eStrEncode);
1020 tagcount++;
1023 // Misc tags (bitrate, etc)
1024 for (TagPtrList::const_iterator it = taglist.begin(); it != taglist.end(); ++it) {
1025 (*it)->WriteTagToFile(&temp,eStrEncode);
1026 tagcount++;
1029 temp.Seek(uFilePosTagCount, wxFromStart);
1030 temp.WriteUInt32(tagcount);
1032 temp.Seek(0, wxFromStart);
1034 AddToList(new CSearchFile(temp, (eStrEncode == utf8strRaw), searchID, 0, 0, wxEmptyString, true));
1037 // File_checked_for_headers