2 // This file is part of the aMule Project.
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 )
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
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/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_*
47 #include "amuleDlg.h" // Needed for CamuleDlg
48 #include "SearchDlg.h" // Needed for CSearchDlg
51 #include "kademlia/kademlia/Kademlia.h"
52 #include "kademlia/kademlia/Search.h"
54 #include "SearchExpr.h"
55 #include "Scanner.h.in"
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
)
76 for (unsigned int i
= 0; i
< pexpr
->m_aExpr
.Count(); i
++) {
77 wxString
str(pexpr
->m_aExpr
[i
]);
78 if (str
== SEARCHOPTOK_AND
) {
80 } else if (str
== SEARCHOPTOK_OR
) {
82 } else if (str
== SEARCHOPTOK_NOT
) {
87 // this limit (+ the additional operators which will be added later) has to match the limit in 'CreateSearchExpressionTree'
88 // +1 Type (Audio, Video)
93 // +1 Complete sources
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()) {
120 strAndTerms
+= pexpr
->m_aExpr
[i
];
124 wxASSERT( _SearchExpr
.m_aExpr
.Count() == 0);
125 _SearchExpr
.m_aExpr
.Add(strAndTerms
);
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
137 CSearchExprTarget(CMemFile
* pData
, EUtf8Str eStrEncode
)
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
)
211 uOperator
= ED2K_SEARCH_OP_GREATER
;
214 uOperator
= KAD_SEARCH_OP_GREATER_EQUAL
;
216 WriteMetaDataSearchParam(uMetaTagID
, uOperator
, uValue
, bEd2k
);
219 void WriteOldMinMetaDataSearchParam(const wxString
& pszMetaTagID
, uint32 uValue
, bool bEd2k
)
223 uOperator
= ED2K_SEARCH_OP_GREATER
;
226 uOperator
= KAD_SEARCH_OP_GREATER_EQUAL
;
228 WriteMetaDataSearchParam(pszMetaTagID
, uOperator
, uValue
, bEd2k
);
231 void WriteOldMaxMetaDataSearchParam(const wxString
& pszMetaTagID
, uint32 uValue
, bool bEd2k
)
235 uOperator
= ED2K_SEARCH_OP_LESS
;
238 uOperator
= KAD_SEARCH_OP_LESS_EQUAL
;
240 WriteMetaDataSearchParam(pszMetaTagID
, uOperator
, uValue
, bEd2k
);
243 void WriteOldMaxMetaDataSearchParam(uint8 uMetaTagID
, uint32 uValue
, bool bEd2k
)
247 uOperator
= ED2K_SEARCH_OP_LESS
;
250 uOperator
= KAD_SEARCH_OP_LESS_EQUAL
;
252 WriteMetaDataSearchParam(uMetaTagID
, uOperator
, uValue
, bEd2k
);
257 EUtf8Str m_eStrEncode
;
263 ///////////////////////////////////////////////////////////
266 BEGIN_EVENT_TABLE(CSearchList
, wxEvtHandler
)
267 EVT_MULE_TIMER(wxID_ANY
, CSearchList::OnGlobalSearchTimer
)
271 CSearchList::CSearchList()
272 : m_searchTimer(this, 0 /* Timer-id doesn't matter. */ ),
273 m_searchType(LocalSearch
),
274 m_searchInProgress(false),
281 CSearchList::~CSearchList()
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
) {
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
;
322 m_resultType
.Clear();
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());
337 for (unsigned int i
= 0; i
< _astrParserErrors
.Count(); ++i
) {
338 error
+= _astrParserErrors
[i
] + wxT("\n");
345 if (type
== KadSearch
) {
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
;
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);
392 m_searchInProgress
= false;
393 Notify_SearchLocalEnd();
398 uint32
CSearchList::GetSearchProgress() const
400 if (m_searchInProgress
== false) {
401 // No search, no progress ;)
405 switch (m_searchType
) {
410 return 100 - (m_serverQueue
.GetRemaining() * 100)
411 / theApp
->serverlist
->GetServerCount();
414 // We cannot meassure the progress of Kad searches.
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.
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();
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.
446 theStats::AddUpOverheadServer(m_searchPacket
->GetPacketSize());
447 theApp
->serverconnect
->SendUDPPacket(m_searchPacket
, server
, false);
448 CoreNotify_Search_Update_Progress(GetSearchProgress());
453 // No more servers left to ask.
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
);
466 if (!theApp
->amuledlg
->m_searchwnd
->CheckTabNameExists(sender
->GetUserName())) {
467 theApp
->amuledlg
->m_searchwnd
->CreateNewTab(sender
->GetUserName() + wxT(" (0)"), searchID
);
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
);
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());
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"))
524 % toadd
->GetFileName());
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())
537 % toadd
->GetFileName());
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
);
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
);
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
);
609 const CSearchResultList
& CSearchList::GetSearchResults(long searchID
) const
611 ResultMap::const_iterator it
= m_results
.find(searchID
);
612 if (it
!= m_results
.end()) {
616 // TODO: Should we assert in this case?
617 static CSearchResultList 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
);
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.
686 _astrParserErrors
.Empty();
687 _SearchExpr
.m_aExpr
.Empty();
689 LexInit(params
.searchString
);
690 int iParseResult
= yyparse();
694 printf("Search parsing result for \"%s\": %i\n",
695 (const char*)unicode2UTF8(params
.searchString
),iParseResult
);
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
);
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());
719 parametercount
+= _SearchExpr
.m_aExpr
.GetCount();
722 printf("Parameters: %i\n",parametercount
);
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...
787 if (++iParameterCount
< parametercount
) {
788 target
.WriteBooleanAND();
790 target
.WriteOldMinMetaDataSearchParam(FT_COMPLETE_SOURCES
, complete
, !kad
);
794 if (++iParameterCount
< parametercount
) {
795 target
.WriteBooleanAND();
797 target
.WriteOldMinMetaDataSearchParam(kad
? TAG_MEDIA_BITRATE
: FT_ED2K_MEDIA_BITRATE
, minBitrate
, !kad
);
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
);
836 // If this assert fails... we're seriously fucked up
838 wxASSERT( iParameterCount
== parametercount
);
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...
874 if (++iParameterCount
< parametercount
) {
875 target
.WriteBooleanAND();
880 if (++iParameterCount
< parametercount
) {
881 target
.WriteBooleanAND();
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();
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();
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.
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
);
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
);
985 // Packet ready to go.
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
;
997 fileID
->ToByteArray(fileid
);
998 temp
.WriteHash(CMD4Hash(fileid
));
1000 temp
.WriteUInt32(0); // client IP
1001 temp
.WriteUInt16(0); // client port
1004 unsigned int uFilePosTagCount
= temp
.GetPosition();
1005 uint32 tagcount
= 0;
1006 temp
.WriteUInt32(tagcount
); // dummy tag count, will be filled later
1009 CTagString
tagName(FT_FILENAME
, name
);
1010 tagName
.WriteTagToFile(&temp
, eStrEncode
);
1013 CTagInt64
tagSize(FT_FILESIZE
, size
);
1014 tagSize
.WriteTagToFile(&temp
, eStrEncode
);
1017 if (!type
.IsEmpty()) {
1018 CTagString
tagType(FT_FILETYPE
, type
);
1019 tagType
.WriteTagToFile(&temp
, eStrEncode
);
1023 // Misc tags (bitrate, etc)
1024 for (TagPtrList::const_iterator it
= taglist
.begin(); it
!= taglist
.end(); ++it
) {
1025 (*it
)->WriteTagToFile(&temp
,eStrEncode
);
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