Upstream tarball 9445
[amule.git] / src / SearchList.cpp
blob0f123e44ce68be4001e32425bafef97438d54f93
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-2008 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/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.Count(); i++) {
78 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 wxString strAndTerms;
112 for (unsigned int i = 0; i < pexpr->m_aExpr.Count(); i++) {
113 if (pexpr->m_aExpr[i] != SEARCHOPTOK_AND) {
114 // Minor optimization: Because we added the Kad keyword to the boolean search expression,
115 // we remove it here (and only here) again because we know that the entire search expression
116 // does only contain (implicit) ANDed strings.
117 if (pexpr->m_aExpr[i] != s_strCurKadKeyword) {
118 if (!strAndTerms.IsEmpty()) {
119 strAndTerms += ' ';
121 strAndTerms += pexpr->m_aExpr[i];
125 wxASSERT( _SearchExpr.m_aExpr.Count() == 0);
126 _SearchExpr.m_aExpr.Add(strAndTerms);
127 } else {
128 if (pexpr->m_aExpr.GetCount() != 1 || pexpr->m_aExpr[0] != s_strCurKadKeyword)
129 _SearchExpr.Add(pexpr);
134 //! Helper class for packet creation
135 class CSearchExprTarget
137 public:
138 CSearchExprTarget(CMemFile* pData, EUtf8Str eStrEncode, bool supports64bit, bool& using64bit)
139 : m_data(pData),
140 m_eStrEncode(eStrEncode),
141 m_supports64bit(supports64bit),
142 m_using64bit(using64bit)
144 m_using64bit = false;
147 void WriteBooleanAND()
149 m_data->WriteUInt8(0); // boolean operator parameter type
150 m_data->WriteUInt8(0x00); // "AND"
153 void WriteBooleanOR()
155 m_data->WriteUInt8(0); // boolean operator parameter type
156 m_data->WriteUInt8(0x01); // "OR"
159 void WriteBooleanNOT()
161 m_data->WriteUInt8(0); // boolean operator parameter type
162 m_data->WriteUInt8(0x02); // "NOT"
165 void WriteMetaDataSearchParam(const wxString& rstrValue)
167 m_data->WriteUInt8(1); // string parameter type
168 m_data->WriteString(rstrValue, m_eStrEncode); // string value
171 void WriteMetaDataSearchParam(uint8 uMetaTagID, const wxString& rstrValue)
173 m_data->WriteUInt8(2); // string parameter type
174 m_data->WriteString(rstrValue, m_eStrEncode); // string value
175 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
176 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
179 void WriteMetaDataSearchParamASCII(uint8 uMetaTagID, const wxString& rstrValue)
181 m_data->WriteUInt8(2); // string parameter type
182 m_data->WriteString(rstrValue, utf8strNone); // string value
183 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
184 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
187 void WriteMetaDataSearchParam(const wxString& pszMetaTagID, const wxString& rstrValue)
189 m_data->WriteUInt8(2); // string parameter type
190 m_data->WriteString(rstrValue, m_eStrEncode); // string value
191 m_data->WriteString(pszMetaTagID); // meta tag ID
194 void WriteMetaDataSearchParam(uint8_t uMetaTagID, uint8_t uOperator, uint64_t value)
196 bool largeValue = value > wxULL(0xFFFFFFFF);
197 if (largeValue && m_supports64bit) {
198 m_using64bit = true;
199 m_data->WriteUInt8(8); // numeric parameter type (int64)
200 m_data->WriteUInt64(value); // numeric value
201 } else {
202 if (largeValue) {
203 value = 0xFFFFFFFFu;
205 m_data->WriteUInt8(3); // numeric parameter type (int32)
206 m_data->WriteUInt32(value); // numeric value
208 m_data->WriteUInt8(uOperator); // comparison operator
209 m_data->WriteUInt16(sizeof(uint8)); // meta tag ID length
210 m_data->WriteUInt8(uMetaTagID); // meta tag ID name
213 void WriteMetaDataSearchParam(const wxString& pszMetaTagID, 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->WriteString(pszMetaTagID); // meta tag ID
231 protected:
232 CMemFile* m_data;
233 EUtf8Str m_eStrEncode;
234 bool m_supports64bit;
235 bool& m_using64bit;
241 ///////////////////////////////////////////////////////////
242 // CSearchList
244 BEGIN_EVENT_TABLE(CSearchList, wxEvtHandler)
245 EVT_MULE_TIMER(wxID_ANY, CSearchList::OnGlobalSearchTimer)
246 END_EVENT_TABLE()
249 CSearchList::CSearchList()
250 : m_searchTimer(this, 0 /* Timer-id doesn't matter. */ ),
251 m_searchType(LocalSearch),
252 m_searchInProgress(false),
253 m_currentSearch(-1),
254 m_searchPacket(NULL),
255 m_64bitSearchPacket(false)
260 CSearchList::~CSearchList()
262 StopGlobalSearch();
264 while (!m_results.empty()) {
265 RemoveResults(m_results.begin()->first);
270 void CSearchList::RemoveResults(long searchID)
272 // A non-existant search id will just be ignored
273 Kademlia::CSearchManager::StopSearch(searchID, true);
275 ResultMap::iterator it = m_results.find(searchID);
276 if ( it != m_results.end() ) {
277 CSearchResultList& list = it->second;
279 for (size_t i = 0; i < list.size(); ++i) {
280 delete list.at(i);
283 m_results.erase( it );
288 wxString CSearchList::StartNewSearch(uint32* searchID, SearchType type, const CSearchParams& params)
290 // Check that we can actually perform the specified desired search.
291 if ((type == KadSearch) && !Kademlia::CKademlia::IsRunning()) {
292 return _("Kad search can't be done if Kad is not running");
293 } else if ((type != KadSearch) && !theApp->IsConnectedED2K()) {
294 return _("eD2k search can't be done if eD2k is not connected");
297 if (params.typeText != ED2KFTSTR_PROGRAM) {
298 if (params.typeText.CmpNoCase(wxT("Any"))) {
299 m_resultType = params.typeText;
300 } else {
301 m_resultType.Clear();
303 } else {
304 // No check is to be made on returned results if the
305 // type is 'Programs', since this returns multiple types.
306 m_resultType.Clear();
309 bool supports64bit = type == KadSearch ? true : theApp->serverconnect->GetCurrentServer() != NULL && (theApp->serverconnect->GetCurrentServer()->GetTCPFlags() & SRV_TCPFLG_LARGEFILES);
310 bool packetUsing64bit;
312 // This MemFile is automatically free'd
313 CMemFilePtr data = CreateSearchData(params, type, supports64bit, packetUsing64bit);
315 if (data.get() == NULL) {
316 wxASSERT(_astrParserErrors.Count());
317 wxString error;
319 for (unsigned int i = 0; i < _astrParserErrors.Count(); ++i) {
320 error += _astrParserErrors[i] + wxT("\n");
323 return error;
326 m_searchType = type;
327 if (type == KadSearch) {
328 try {
329 if (*searchID == 0xffffffff) {
330 Kademlia::CSearchManager::StopSearch(0xffffffff, false);
333 // searchstring will get tokenized there
334 // The tab must be created with the Kad search ID, so seardhID is updated.
335 Kademlia::CSearch* search = Kademlia::CSearchManager::PrepareFindKeywords(
336 params.searchString, data->GetLength(), data->GetRawBuffer(), *searchID);
338 *searchID = search->GetSearchID();
339 } catch (const wxString& what) {
340 AddLogLineM(true, what);
341 return _("Unexpected error while attempting Kad search: ") + what;
343 } else {
344 // This is an ed2k search, local or global
345 m_currentSearch = *(searchID);
346 m_searchInProgress = true;
348 CPacket* searchPacket = new CPacket(*data.get(), OP_EDONKEYPROT, OP_SEARCHREQUEST);
350 theStats::AddUpOverheadServer(searchPacket->GetPacketSize());
351 theApp->serverconnect->SendPacket(searchPacket, (type == LocalSearch));
353 if (type == GlobalSearch) {
354 m_searchPacket = searchPacket;
355 m_64bitSearchPacket = packetUsing64bit;
356 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ); // will be changed later when actually sending the packet!!
360 return wxEmptyString;
364 void CSearchList::LocalSearchEnd()
366 if (m_searchType == GlobalSearch) {
367 wxCHECK_RET(m_searchPacket, wxT("Global search, but no packet"));
369 // Ensure that every global search starts over.
370 theApp->serverlist->RemoveObserver(&m_serverQueue);
371 m_searchTimer.Start(750);
372 } else {
373 m_searchInProgress = false;
374 Notify_SearchLocalEnd();
379 uint32 CSearchList::GetSearchProgress() const
381 if (m_searchInProgress == false) {
382 // No search, no progress ;)
383 return 0;
386 switch (m_searchType) {
387 case LocalSearch:
388 return 0xffff;
390 case GlobalSearch:
391 return 100 - (m_serverQueue.GetRemaining() * 100)
392 / theApp->serverlist->GetServerCount();
394 case KadSearch:
395 // We cannot measure the progress of Kad searches.
396 return 0;
398 default:
399 wxCHECK(false, 0);
404 void CSearchList::OnGlobalSearchTimer(CTimerEvent& WXUNUSED(evt))
406 // Ensure that the server-queue contains the current servers.
407 if (m_searchPacket == NULL) {
408 // This was a pending event, handled after 'Stop' was pressed.
409 return;
410 } else if (!m_serverQueue.IsActive()) {
411 theApp->serverlist->AddObserver(&m_serverQueue);
414 // UDP requests must not be sent to this server.
415 const CServer* localServer = theApp->serverconnect->GetCurrentServer();
416 if (localServer) {
417 uint32 localIP = localServer->GetIP();
418 uint16 localPort = localServer->GetPort();
419 while (m_serverQueue.GetRemaining()) {
420 CServer* server = m_serverQueue.GetNext();
422 // Compare against the currently connected server.
423 if ((server->GetPort() == localPort) && (server->GetIP() == localIP)) {
424 // We've already requested from the local server.
425 continue;
426 } else {
427 if (server->SupportsLargeFilesUDP() && (server->GetUDPFlags() & SRV_UDPFLG_EXT_GETFILES)) {
428 CMemFile data(50);
429 uint32_t tagCount = 1;
430 data.WriteUInt32(tagCount);
431 CTagVarInt flags(CT_SERVER_UDPSEARCH_FLAGS, SRVCAP_UDP_NEWTAGS_LARGEFILES);
432 flags.WriteNewEd2kTag(&data);
433 CPacket *extSearchPacket = new CPacket(OP_GLOBSEARCHREQ3, m_searchPacket->GetPacketSize() + (uint32_t)data.GetLength(), OP_EDONKEYPROT);
434 extSearchPacket->CopyToDataBuffer(0, data.GetRawBuffer(), data.GetLength());
435 extSearchPacket->CopyToDataBuffer(data.GetLength(), m_searchPacket->GetDataBuffer(), m_searchPacket->GetPacketSize());
436 theStats::AddUpOverheadServer(extSearchPacket->GetPacketSize());
437 theApp->serverconnect->SendUDPPacket(extSearchPacket, server, true);
438 AddDebugLogLineM(false, logServerUDP, wxT("Sending OP_GLOBSEARCHREQ3 to server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()));
439 } else if (server->GetUDPFlags() & SRV_UDPFLG_EXT_GETFILES) {
440 if (!m_64bitSearchPacket || server->SupportsLargeFilesUDP()) {
441 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ2);
442 AddDebugLogLineM(false, logServerUDP, wxT("Sending OP_GLOBSEARCHREQ2 to server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()));
443 theStats::AddUpOverheadServer(m_searchPacket->GetPacketSize());
444 theApp->serverconnect->SendUDPPacket(m_searchPacket, server, false);
445 } else {
446 AddDebugLogLineM(false, logServerUDP, wxT("Skipped UDP search on server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()) + wxT(": No large file support"));
448 } else {
449 if (!m_64bitSearchPacket || server->SupportsLargeFilesUDP()) {
450 m_searchPacket->SetOpCode(OP_GLOBSEARCHREQ);
451 AddDebugLogLineM(false, logServerUDP, wxT("Sending OP_GLOBSEARCHREQ to server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()));
452 theStats::AddUpOverheadServer(m_searchPacket->GetPacketSize());
453 theApp->serverconnect->SendUDPPacket(m_searchPacket, server, false);
454 } else {
455 AddDebugLogLineM(false, logServerUDP, wxT("Skipped UDP search on server ") + Uint32_16toStringIP_Port(server->GetIP(), server->GetPort()) + wxT(": No large file support"));
458 CoreNotify_Search_Update_Progress(GetSearchProgress());
459 return;
463 // No more servers left to ask.
464 StopGlobalSearch();
468 void CSearchList::ProcessSharedFileList(const byte* in_packet, uint32 size,
469 CUpDownClient* sender, bool *moreResultsAvailable, const wxString& directory)
471 wxCHECK_RET(sender, wxT("No sender in search-results from client."));
473 long searchID = reinterpret_cast<wxUIntPtr>(sender);
475 #ifndef AMULE_DAEMON
476 if (!theApp->amuledlg->m_searchwnd->CheckTabNameExists(sender->GetUserName())) {
477 theApp->amuledlg->m_searchwnd->CreateNewTab(sender->GetUserName() + wxT(" (0)"), searchID);
479 #endif
481 const CMemFile packet(in_packet, size);
482 uint32 results = packet.ReadUInt32();
483 bool unicoded = (sender->GetUnicodeSupport() != utf8strNone);
484 for (unsigned int i = 0; i != results; ++i){
485 CSearchFile* toadd = new CSearchFile(packet, unicoded, searchID, 0, 0, directory);
486 if (sender) {
487 toadd->SetClientID(sender->GetUserIDHybrid());
488 toadd->SetClientPort(sender->GetUserPort());
491 AddToList(toadd, true);
494 if (moreResultsAvailable)
495 *moreResultsAvailable = false;
497 int iAddData = (int)(packet.GetLength() - packet.GetPosition());
498 if (iAddData == 1) {
499 uint8 ucMore = packet.ReadUInt8();
500 if (ucMore == 0x00 || ucMore == 0x01){
501 if (moreResultsAvailable) {
502 *moreResultsAvailable = (ucMore == 1);
509 void CSearchList::ProcessSearchAnswer(const uint8_t* in_packet, uint32_t size, bool optUTF8, uint32_t serverIP, uint16_t serverPort)
511 CMemFile packet(in_packet, size);
513 uint32_t results = packet.ReadUInt32();
514 for (; results > 0; --results) {
515 AddToList(new CSearchFile(packet, optUTF8, m_currentSearch, serverIP, serverPort), false);
520 void CSearchList::ProcessUDPSearchAnswer(const CMemFile& packet, bool optUTF8, uint32_t serverIP, uint16_t serverPort)
522 AddToList(new CSearchFile(packet, optUTF8, m_currentSearch, serverIP, serverPort), false);
526 bool CSearchList::AddToList(CSearchFile* toadd, bool clientResponse)
528 const uint64 fileSize = toadd->GetFileSize();
529 // If filesize is 0, or file is too large for the network, drop it
530 if ((fileSize == 0) || (fileSize > MAX_FILE_SIZE)) {
531 AddDebugLogLineM(false, logSearch,
532 CFormat(wxT("Dropped result with filesize %u: %s"))
533 % fileSize
534 % toadd->GetFileName());
536 delete toadd;
537 return false;
540 // If the result was not the type the user wanted, drop it.
541 if ((clientResponse == false) && !m_resultType.IsEmpty()) {
542 if (GetFileTypeByName(toadd->GetFileName()) != m_resultType) {
543 AddDebugLogLineM( false, logSearch,
544 CFormat( wxT("Dropped result type %s != %s, file %s") )
545 % GetFileTypeByName(toadd->GetFileName())
546 % m_resultType
547 % toadd->GetFileName());
549 delete toadd;
550 return false;
555 // Get, or implictly create, the map of results for this search
556 CSearchResultList& results = m_results[toadd->GetSearchID()];
558 for (size_t i = 0; i < results.size(); ++i) {
559 CSearchFile* item = results.at(i);
561 if ((toadd->GetFileHash() == item->GetFileHash()) && (toadd->GetFileSize() == item->GetFileSize())) {
562 AddDebugLogLineM(false, logSearch, CFormat(wxT("Received duplicate results for '%s' : %s")) % item->GetFileName() % item->GetFileHash().Encode());
563 // Add the child, possibly updating the parents filename.
564 item->AddChild(toadd);
565 Notify_Search_Update_Sources(item);
566 return true;
570 AddDebugLogLineM(false, logSearch,
571 CFormat(wxT("Added new result '%s' : %s"))
572 % toadd->GetFileName() % toadd->GetFileHash().Encode());
574 // New unique result, simply add and display.
575 results.push_back(toadd);
576 Notify_Search_Add_Result(toadd);
578 return true;
582 const CSearchResultList& CSearchList::GetSearchResults(long searchID) const
584 ResultMap::const_iterator it = m_results.find(searchID);
585 if (it != m_results.end()) {
586 return it->second;
589 // TODO: Should we assert in this case?
590 static CSearchResultList list;
591 return list;
595 void CSearchList::AddFileToDownloadByHash(const CMD4Hash& hash, uint8 cat)
597 ResultMap::iterator it = m_results.begin();
598 for ( ; it != m_results.end(); ++it ) {
599 CSearchResultList& list = it->second;
601 for ( unsigned int i = 0; i < list.size(); ++i ) {
602 if ( list[i]->GetFileHash() == hash ) {
603 CoreNotify_Search_Add_Download( list[i], cat );
605 return;
612 void CSearchList::StopGlobalSearch()
614 m_currentSearch = -1;
615 delete m_searchPacket;
616 m_searchPacket = NULL;
617 m_searchInProgress = false;
619 // Order is crucial here: on wx_MSW an additional event can be generated during the stop.
620 // So the packet has to be deleted first, so that OnGlobalSearchTimer() returns immediately
621 // without calling StopGlobalSearch() again.
622 m_searchTimer.Stop();
624 CoreNotify_Search_Update_Progress(0xffff);
628 CSearchList::CMemFilePtr CSearchList::CreateSearchData(const CSearchParams& params, SearchType WXUNUSED(type), bool supports64bit, bool& packetUsing64bit)
630 // Count the number of used parameters
631 unsigned int parametercount = 0;
632 if ( !params.typeText.IsEmpty() ) ++parametercount;
633 if ( params.minSize > 0 ) ++parametercount;
634 if ( params.maxSize > 0 ) ++parametercount;
635 if ( params.availability > 0 ) ++parametercount;
636 if ( !params.extension.IsEmpty() ) ++parametercount;
638 wxString typeText = params.typeText;
639 if (typeText == ED2KFTSTR_ARCHIVE){
640 // eDonkeyHybrid 0.48 uses type "Pro" for archives files
641 // www.filedonkey.com uses type "Pro" for archives files
642 typeText = ED2KFTSTR_PROGRAM;
643 } else if (typeText == ED2KFTSTR_CDIMAGE){
644 // eDonkeyHybrid 0.48 uses *no* type for iso/nrg/cue/img files
645 // www.filedonkey.com uses type "Pro" for CD-image files
646 typeText = ED2KFTSTR_PROGRAM;
649 // Must write parametercount - 1 parameter headers
650 CMemFilePtr data(new CMemFile(100));
652 _astrParserErrors.Empty();
653 _SearchExpr.m_aExpr.Empty();
655 LexInit(params.searchString);
656 int iParseResult = yyparse();
657 LexFree();
659 #ifdef __DEBUG__
660 AddLogLineNS(CFormat(wxT("Search parsing result for \"%s\": %i"))
661 % params.searchString % iParseResult);
662 #endif
663 if (_astrParserErrors.Count() > 0) {
664 for (unsigned int i=0; i < _astrParserErrors.Count(); ++i) {
665 AddLogLineNS(CFormat(wxT("Error %u: %s\n")) % i % _astrParserErrors[i]);
668 return CMemFilePtr(NULL);
671 if (iParseResult != 0) {
672 _astrParserErrors.Add(wxString::Format(wxT("Undefined error %i on search expression"),iParseResult));
674 return CMemFilePtr(NULL);
677 #ifdef __DEBUG__
678 wxString mes(wxT("Search expression:"));
679 for (unsigned int i = 0; i < _SearchExpr.m_aExpr.Count(); i++) {
680 mes << wxT(" ") << _SearchExpr.m_aExpr[i];
682 AddLogLineNS(mes);
683 AddLogLineNS(CFormat(wxT("Expression count: %i")) % _SearchExpr.m_aExpr.GetCount());
684 #endif
686 parametercount += _SearchExpr.m_aExpr.GetCount();
688 #ifdef __DEBUG__
689 AddLogLineNS(CFormat(wxT("Parameters: %i")) % parametercount);
690 #endif
692 /* Leave the unicode comment there, please... */
693 CSearchExprTarget target(data.get(), true /*I assume everyone is unicoded */ ? utf8strRaw : utf8strNone, supports64bit, packetUsing64bit);
695 unsigned int iParameterCount = 0;
696 if (_SearchExpr.m_aExpr.GetCount() <= 1) {
697 // lugdunummaster requested that searchs without OR or NOT operators,
698 // and hence with no more expressions than the string itself, be sent
699 // using a series of ANDed terms, intersecting the ANDs on the terms
700 // (but prepending them) instead of putting the boolean tree at the start
701 // like other searches. This type of search is supposed to take less load
702 // on servers. Go figure.
704 // input: "a" AND min=1 AND max=2
705 // instead of: AND AND "a" min=1 max=2
706 // we use: AND "a" AND min=1 max=2
708 if (_SearchExpr.m_aExpr.GetCount() > 0) {
709 if (++iParameterCount < parametercount) {
710 target.WriteBooleanAND();
712 target.WriteMetaDataSearchParam(_SearchExpr.m_aExpr[0]);
715 if (!typeText.IsEmpty()) {
716 if (++iParameterCount < parametercount) {
717 target.WriteBooleanAND();
719 // Type is always ascii string
720 target.WriteMetaDataSearchParamASCII(FT_FILETYPE, typeText);
723 if (params.minSize > 0) {
724 if (++iParameterCount < parametercount) {
725 target.WriteBooleanAND();
727 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_GREATER, params.minSize);
730 if (params.maxSize > 0){
731 if (++iParameterCount < parametercount) {
732 target.WriteBooleanAND();
734 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_LESS, params.maxSize);
737 if (params.availability > 0){
738 if (++iParameterCount < parametercount) {
739 target.WriteBooleanAND();
741 target.WriteMetaDataSearchParam(FT_SOURCES, ED2K_SEARCH_OP_GREATER, params.availability);
744 if (!params.extension.IsEmpty()){
745 if (++iParameterCount < parametercount) {
746 target.WriteBooleanAND();
748 target.WriteMetaDataSearchParam(FT_FILEFORMAT, params.extension);
751 //#warning TODO - I keep this here, ready if we ever allow such searches...
752 #if 0
753 if (complete > 0){
754 if (++iParameterCount < parametercount) {
755 target.WriteBooleanAND();
757 target.WriteMetaDataSearchParam(FT_COMPLETE_SOURCES, ED2K_SEARCH_OP_GREATER, complete);
760 if (minBitrate > 0){
761 if (++iParameterCount < parametercount) {
762 target.WriteBooleanAND();
764 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_BITRATE : FT_ED2K_MEDIA_BITRATE, ED2K_SEARCH_OP_GREATER, minBitrate);
767 if (minLength > 0){
768 if (++iParameterCount < parametercount) {
769 target.WriteBooleanAND();
771 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_LENGTH : FT_ED2K_MEDIA_LENGTH, ED2K_SEARCH_OP_GREATER, minLength);
774 if (!codec.IsEmpty()){
775 if (++iParameterCount < parametercount) {
776 target.WriteBooleanAND();
778 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_CODEC : FT_ED2K_MEDIA_CODEC, codec);
781 if (!title.IsEmpty()){
782 if (++iParameterCount < parametercount) {
783 target.WriteBooleanAND();
785 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_TITLE : FT_ED2K_MEDIA_TITLE, title);
788 if (!album.IsEmpty()){
789 if (++iParameterCount < parametercount) {
790 target.WriteBooleanAND();
792 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ALBUM : FT_ED2K_MEDIA_ALBUM, album);
795 if (!artist.IsEmpty()){
796 if (++iParameterCount < parametercount) {
797 target.WriteBooleanAND();
799 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ARTIST : FT_ED2K_MEDIA_ARTIST, artist);
801 #endif // 0
803 // If this assert fails... we're seriously fucked up
805 wxASSERT( iParameterCount == parametercount );
807 } else {
808 if (!params.extension.IsEmpty()) {
809 if (++iParameterCount < parametercount) {
810 target.WriteBooleanAND();
814 if (params.availability > 0) {
815 if (++iParameterCount < parametercount) {
816 target.WriteBooleanAND();
820 if (params.maxSize > 0){
821 if (++iParameterCount < parametercount) {
822 target.WriteBooleanAND();
826 if (params.minSize > 0) {
827 if (++iParameterCount < parametercount) {
828 target.WriteBooleanAND();
832 if (!typeText.IsEmpty()){
833 if (++iParameterCount < parametercount) {
834 target.WriteBooleanAND();
838 //#warning TODO - same as above...
839 #if 0
840 if (complete > 0){
841 if (++iParameterCount < parametercount) {
842 target.WriteBooleanAND();
846 if (minBitrate > 0){
847 if (++iParameterCount < parametercount) {
848 target.WriteBooleanAND();
852 if (minLength > 0) {
853 if (++iParameterCount < parametercount) {
854 target.WriteBooleanAND();
858 if (!codec.IsEmpty()){
859 if (++iParameterCount < parametercount) {
860 target.WriteBooleanAND();
864 if (!title.IsEmpty()){
865 if (++iParameterCount < parametercount) {
866 target.WriteBooleanAND();
870 if (!album.IsEmpty()) {
871 if (++iParameterCount < parametercount) {
872 target.WriteBooleanAND();
876 if (!artist.IsEmpty()) {
877 if (++iParameterCount < parametercount) {
878 target.WriteBooleanAND();
881 #endif // 0
883 // As above, if this fails, we're seriously fucked up.
884 wxASSERT( iParameterCount + _SearchExpr.m_aExpr.GetCount() == parametercount );
886 for (unsigned int j = 0; j < _SearchExpr.m_aExpr.GetCount(); ++j) {
887 if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_AND) {
888 target.WriteBooleanAND();
889 } else if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_OR) {
890 target.WriteBooleanOR();
891 } else if (_SearchExpr.m_aExpr[j] == SEARCHOPTOK_NOT) {
892 target.WriteBooleanNOT();
893 } else {
894 target.WriteMetaDataSearchParam(_SearchExpr.m_aExpr[j]);
898 if (!params.typeText.IsEmpty()) {
899 // Type is always ASCII string
900 target.WriteMetaDataSearchParamASCII(FT_FILETYPE, params.typeText);
903 if (params.minSize > 0) {
904 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_GREATER, params.minSize);
907 if (params.maxSize > 0) {
908 target.WriteMetaDataSearchParam(FT_FILESIZE, ED2K_SEARCH_OP_LESS, params.maxSize);
911 if (params.availability > 0) {
912 target.WriteMetaDataSearchParam(FT_SOURCES, ED2K_SEARCH_OP_GREATER, params.availability);
915 if (!params.extension.IsEmpty()) {
916 target.WriteMetaDataSearchParam(FT_FILEFORMAT, params.extension);
919 //#warning TODO - third and last warning of the same series.
920 #if 0
921 if (complete > 0) {
922 target.WriteMetaDataSearchParam(FT_COMPLETE_SOURCES, ED2K_SEARCH_OP_GREATER, pParams->uComplete);
925 if (minBitrate > 0) {
926 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_BITRATE : FT_ED2K_MEDIA_BITRATE, ED2K_SEARCH_OP_GREATER, minBitrate);
929 if (minLength > 0) {
930 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_LENGTH : FT_ED2K_MEDIA_LENGTH, ED2K_SEARCH_OP_GREATER, minLength);
933 if (!codec.IsEmpty()) {
934 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_CODEC : FT_ED2K_MEDIA_CODEC, codec);
937 if (!title.IsEmpty()) {
938 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_TITLE : FT_ED2K_MEDIA_TITLE, title);
941 if (!album.IsEmpty()) {
942 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ALBUM : FT_ED2K_MEDIA_ALBUM, album);
945 if (!artist.IsEmpty()) {
946 target.WriteMetaDataSearchParam(type == KadSearch ? TAG_MEDIA_ARTIST : FT_ED2K_MEDIA_ARTIST, artist);
949 #endif // 0
952 // Packet ready to go.
953 return data;
957 void CSearchList::KademliaSearchKeyword(uint32_t searchID, const Kademlia::CUInt128 *fileID,
958 const wxString& name, uint64_t size, const wxString& type, uint32_t kadPublishInfo, const TagPtrList& taglist)
960 EUtf8Str eStrEncode = utf8strRaw;
962 CMemFile temp(250);
963 byte fileid[16];
964 fileID->ToByteArray(fileid);
965 temp.WriteHash(CMD4Hash(fileid));
967 temp.WriteUInt32(0); // client IP
968 temp.WriteUInt16(0); // client port
970 // write tag list
971 unsigned int uFilePosTagCount = temp.GetPosition();
972 uint32 tagcount = 0;
973 temp.WriteUInt32(tagcount); // dummy tag count, will be filled later
975 // standard tags
976 CTagString tagName(FT_FILENAME, name);
977 tagName.WriteTagToFile(&temp, eStrEncode);
978 tagcount++;
980 CTagInt64 tagSize(FT_FILESIZE, size);
981 tagSize.WriteTagToFile(&temp, eStrEncode);
982 tagcount++;
984 if (!type.IsEmpty()) {
985 CTagString tagType(FT_FILETYPE, type);
986 tagType.WriteTagToFile(&temp, eStrEncode);
987 tagcount++;
990 // Misc tags (bitrate, etc)
991 for (TagPtrList::const_iterator it = taglist.begin(); it != taglist.end(); ++it) {
992 (*it)->WriteTagToFile(&temp,eStrEncode);
993 tagcount++;
996 temp.Seek(uFilePosTagCount, wxFromStart);
997 temp.WriteUInt32(tagcount);
999 temp.Seek(0, wxFromStart);
1001 CSearchFile *tempFile = new CSearchFile(temp, (eStrEncode == utf8strRaw), searchID, 0, 0, wxEmptyString, true);
1002 tempFile->SetKadPublishInfo(kadPublishInfo);
1004 AddToList(tempFile);
1007 // File_checked_for_headers