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
)
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
)
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
209 EUtf8Str m_eStrEncode
;
215 ///////////////////////////////////////////////////////////
218 BEGIN_EVENT_TABLE(CSearchList
, wxEvtHandler
)
219 EVT_MULE_TIMER(wxID_ANY
, CSearchList::OnGlobalSearchTimer
)
223 CSearchList::CSearchList()
224 : m_searchTimer(this, 0 /* Timer-id doesn't matter. */ ),
225 m_searchType(LocalSearch
),
226 m_searchInProgress(false),
233 CSearchList::~CSearchList()
237 while (!m_results
.empty()) {
238 RemoveResults(m_results
.begin()->first
);
243 void CSearchList::RemoveResults(long searchID
)
245 // A non-existant search id will just be ignored
246 Kademlia::CSearchManager::StopSearch(searchID
, true);
248 ResultMap::iterator it
= m_results
.find(searchID
);
249 if ( it
!= m_results
.end() ) {
250 CSearchResultList
& list
= it
->second
;
252 for (size_t i
= 0; i
< list
.size(); ++i
) {
256 m_results
.erase( it
);
261 wxString
CSearchList::StartNewSearch(uint32
* searchID
, SearchType type
, const CSearchParams
& params
)
263 // Check that we can actually perform the specified desired search.
264 if ((type
== KadSearch
) && !Kademlia::CKademlia::IsRunning()) {
265 return _("Kad search can't be done if Kad is not running");
266 } else if ((type
!= KadSearch
) && !theApp
->IsConnectedED2K()) {
267 return _("ED2K search can't be done if ED2K is not connected");
270 if (params
.typeText
!= ED2KFTSTR_PROGRAM
) {
271 if (params
.typeText
.CmpNoCase(wxT("Any"))) {
272 m_resultType
= params
.typeText
;
274 m_resultType
.Clear();
277 // No check is to be made on returned results if the
278 // type is 'Programs', since this returns multiple types.
279 m_resultType
.Clear();
282 // This MemFile is automatically free'd, except for kad searches.
283 CMemFilePtr data
= CreateSearchData(params
, type
);
285 if (data
.get() == NULL
) {
286 wxASSERT(_astrParserErrors
.Count());
289 for (unsigned int i
= 0; i
< _astrParserErrors
.Count(); ++i
) {
290 error
+= _astrParserErrors
[i
] + wxT("\n");
297 if (type
== KadSearch
) {
299 if (*searchID
== 0xffffffff) {
300 Kademlia::CSearchManager::StopSearch(0xffffffff, false);
303 // searchstring will get tokenized there
304 // The tab must be created with the Kad search ID, so seardhID is updated.
305 Kademlia::CSearch
* search
= Kademlia::CSearchManager::PrepareFindKeywords(
306 params
.searchString
, data
->GetLength(), data
->GetRawBuffer(), *searchID
);
308 *searchID
= search
->GetSearchID();
309 } catch (const wxString
& what
) {
310 AddLogLineM(true, what
);
311 return _("Unexpected error while attempting Kad search: ") + what
;
314 // This is an ed2k search, local or global
315 m_currentSearch
= *(searchID
);
316 m_searchInProgress
= true;
318 CPacket
* searchPacket
= new CPacket(*data
.get(), OP_EDONKEYPROT
, OP_SEARCHREQUEST
);
320 theStats::AddUpOverheadServer(searchPacket
->GetPacketSize());
321 theApp
->serverconnect
->SendPacket(searchPacket
, (type
== LocalSearch
));
323 if (type
== GlobalSearch
) {
324 m_searchPacket
= searchPacket
;
326 // The OPCode must be changed since global searches are UDP requests
327 m_searchPacket
->SetOpCode(OP_GLOBSEARCHREQ
);
331 return wxEmptyString
;
335 void CSearchList::LocalSearchEnd()
337 if (m_searchType
== GlobalSearch
) {
338 wxCHECK_RET(m_searchPacket
, wxT("Global search, but no packet"));
340 // Ensure that every global search starts over.
341 theApp
->serverlist
->RemoveObserver(&m_serverQueue
);
342 m_searchTimer
.Start(750);
344 m_searchInProgress
= false;
345 Notify_SearchLocalEnd();
350 uint32
CSearchList::GetSearchProgress() const
352 if (m_searchInProgress
== false) {
353 // No search, no progress ;)
357 switch (m_searchType
) {
362 return 100 - (m_serverQueue
.GetRemaining() * 100)
363 / theApp
->serverlist
->GetServerCount();
366 // We cannot meassure the progress of Kad searches.
375 void CSearchList::OnGlobalSearchTimer(CTimerEvent
& WXUNUSED(evt
))
377 // Ensure that the server-queue contains the current servers.
378 if (m_searchPacket
== NULL
) {
379 // This was a pending event, handled after 'Stop' was pressed.
381 } else if (!m_serverQueue
.IsActive()) {
382 theApp
->serverlist
->AddObserver(&m_serverQueue
);
385 // UDP requests must not be sent to this server.
386 const CServer
* localServer
= theApp
->serverconnect
->GetCurrentServer();
388 uint32 localIP
= localServer
->GetIP();
389 uint16 localPort
= localServer
->GetPort();
390 while (m_serverQueue
.GetRemaining()) {
391 CServer
* server
= m_serverQueue
.GetNext();
393 // Compare against the currently connected server.
394 if ((server
->GetPort() == localPort
) && (server
->GetIP() == localIP
)) {
395 // We've already requested from the local server.
398 theStats::AddUpOverheadServer(m_searchPacket
->GetPacketSize());
399 theApp
->serverconnect
->SendUDPPacket(m_searchPacket
, server
, false);
400 CoreNotify_Search_Update_Progress(GetSearchProgress());
405 // No more servers left to ask.
410 void CSearchList::ProcessSharedFileList(const byte
* in_packet
, uint32 size
,
411 CUpDownClient
* sender
, bool *moreResultsAvailable
, const wxString
& directory
)
413 wxCHECK_RET(sender
, wxT("No sender in search-results from client."));
415 long searchID
= reinterpret_cast<wxUIntPtr
>(sender
);
418 if (!theApp
->amuledlg
->m_searchwnd
->CheckTabNameExists(sender
->GetUserName())) {
419 theApp
->amuledlg
->m_searchwnd
->CreateNewTab(sender
->GetUserName() + wxT(" (0)"), searchID
);
423 const CMemFile
packet(in_packet
, size
);
424 uint32 results
= packet
.ReadUInt32();
425 bool unicoded
= (sender
->GetUnicodeSupport() != utf8strNone
);
426 for (unsigned int i
= 0; i
!= results
; ++i
){
427 CSearchFile
* toadd
= new CSearchFile(packet
, unicoded
, searchID
, 0, 0, directory
);
429 toadd
->SetClientID(sender
->GetUserIDHybrid());
430 toadd
->SetClientPort(sender
->GetUserPort());
433 AddToList(toadd
, true);
436 if (moreResultsAvailable
)
437 *moreResultsAvailable
= false;
439 int iAddData
= (int)(packet
.GetLength() - packet
.GetPosition());
441 uint8 ucMore
= packet
.ReadUInt8();
442 if (ucMore
== 0x00 || ucMore
== 0x01){
443 if (moreResultsAvailable
) {
444 *moreResultsAvailable
= (ucMore
== 1);
451 void CSearchList::ProcessSearchAnswer(const byte
* in_packet
, uint32 size
, bool optUTF8
, uint32
WXUNUSED(serverIP
), uint16
WXUNUSED(serverPort
))
453 CMemFile
packet(in_packet
,size
);
455 uint32 results
= packet
.ReadUInt32();
456 for (unsigned int i
= 0; i
!= results
; ++i
) {
457 AddToList(new CSearchFile(packet
, optUTF8
, m_currentSearch
));
462 void CSearchList::ProcessUDPSearchAnswer(const CMemFile
& packet
, bool optUTF8
, uint32 serverIP
, uint16 serverPort
)
464 AddToList(new CSearchFile(packet
, optUTF8
, m_currentSearch
, serverIP
, serverPort
));
468 bool CSearchList::AddToList(CSearchFile
* toadd
, bool clientResponse
)
470 const uint64 fileSize
= toadd
->GetFileSize();
471 // If filesize is 0, or file is too large for the network, drop it
472 if ((fileSize
== 0) || (fileSize
> MAX_FILE_SIZE
)) {
473 AddDebugLogLineM(false, logSearch
,
474 CFormat(wxT("Dropped result with filesize %u: %s"))
476 % toadd
->GetFileName());
482 // If the result was not the type the user wanted, drop it.
483 if ((clientResponse
== false) && !m_resultType
.IsEmpty()) {
484 if (GetFileTypeByName(toadd
->GetFileName()) != m_resultType
) {
485 AddDebugLogLineM( false, logSearch
,
486 CFormat( wxT("Dropped result type %s != %s, file %s") )
487 % GetFileTypeByName(toadd
->GetFileName())
489 % toadd
->GetFileName());
497 // Get, or implictly create, the map of results for this search
498 CSearchResultList
& results
= m_results
[toadd
->GetSearchID()];
500 for (size_t i
= 0; i
< results
.size(); ++i
) {
501 CSearchFile
* item
= results
.at(i
);
503 if ((toadd
->GetFileHash() == item
->GetFileHash())
504 && (toadd
->GetFileSize() == item
->GetFileSize())) {
506 AddDebugLogLineM(false, logSearch
,
507 CFormat(wxT("Received duplicate results for '%s' : %s"))
508 % item
->GetFileName() % item
->GetFileHash().Encode());
510 // If no children exists, then we add the current item. The
511 // "parent" item will show the most common filename and the
512 // sum of sources for all variants.
513 if (item
->GetChildren().empty()) {
514 if (toadd
->GetFileName() == item
->GetFileName()) {
515 AddDebugLogLineM( false, logSearch
,
516 CFormat(wxT("Merged results for '%s'"))
517 % item
->GetFileName());
519 // Merge duplicate items rather than creating a child item
520 item
->AddSources(toadd
->GetSourceCount(), toadd
->GetCompleteSourceCount());
521 Notify_Search_Update_Sources(item
);
525 AddDebugLogLineM(false, logSearch
,
526 CFormat(wxT("Created initial child for result '%s'"))
527 % item
->GetFileName());
529 // The first child will always be the first result we received.
530 item
->AddChild(new CSearchFile(*item
));
534 AddDebugLogLineM( false, logSearch
,
535 CFormat(wxT("Adding child '%s' to result '%s'"))
536 % toadd
->GetFileName() % item
->GetFileName());
538 // Parent item includes sum of all sources for this file
539 item
->AddSources(toadd
->GetSourceCount(), toadd
->GetCompleteSourceCount());
540 // Add the child, possibly updating the parents filename.
541 item
->AddChild(toadd
);
543 Notify_Search_Update_Sources(item
);
549 AddDebugLogLineM(false, logSearch
,
550 CFormat(wxT("Added new result '%s' : %s"))
551 % toadd
->GetFileName() % toadd
->GetFileHash().Encode());
553 // New unique result, simply add and display.
554 results
.push_back(toadd
);
555 Notify_Search_Add_Result(toadd
);
561 const CSearchResultList
& CSearchList::GetSearchResults(long searchID
) const
563 ResultMap::const_iterator it
= m_results
.find(searchID
);
564 if (it
!= m_results
.end()) {
568 // TODO: Should we assert in this case?
569 static CSearchResultList list
;
574 void CSearchList::AddFileToDownloadByHash(const CMD4Hash
& hash
, uint8 cat
)
576 ResultMap::iterator it
= m_results
.begin();
577 for ( ; it
!= m_results
.end(); ++it
) {
578 CSearchResultList
& list
= it
->second
;
580 for ( unsigned int i
= 0; i
< list
.size(); ++i
) {
581 if ( list
[i
]->GetFileHash() == hash
) {
582 CoreNotify_Search_Add_Download( list
[i
], cat
);
591 void CSearchList::StopGlobalSearch()
593 m_searchTimer
.Stop();
595 m_currentSearch
= -1;
596 delete m_searchPacket
;
597 m_searchPacket
= NULL
;
598 m_searchInProgress
= false;
600 CoreNotify_Search_Update_Progress(0xffff);
604 CSearchList::CMemFilePtr
CSearchList::CreateSearchData(const CSearchParams
& params
, SearchType type
)
606 // Count the number of used parameters
607 unsigned int parametercount
= 0;
608 if ( !params
.typeText
.IsEmpty() ) ++parametercount
;
609 if ( params
.minSize
> 0 ) ++parametercount
;
610 if ( params
.maxSize
> 0 ) ++parametercount
;
611 if ( params
.availability
> 0 ) ++parametercount
;
612 if ( !params
.extension
.IsEmpty() ) ++parametercount
;
614 wxString typeText
= params
.typeText
;
615 if (typeText
== ED2KFTSTR_ARCHIVE
){
616 // eDonkeyHybrid 0.48 uses type "Pro" for archives files
617 // www.filedonkey.com uses type "Pro" for archives files
618 typeText
= ED2KFTSTR_PROGRAM
;
619 } else if (typeText
== ED2KFTSTR_CDIMAGE
){
620 // eDonkeyHybrid 0.48 uses *no* type for iso/nrg/cue/img files
621 // www.filedonkey.com uses type "Pro" for CD-image files
622 typeText
= ED2KFTSTR_PROGRAM
;
625 // Must write parametercount - 1 parameter headers
626 CMemFilePtr
data(new CMemFile(100));
628 _astrParserErrors
.Empty();
629 _SearchExpr
.m_aExpr
.Empty();
631 LexInit(params
.searchString
);
632 int iParseResult
= yyparse();
636 printf("Search parsing result for \"%s\": %i\n",
637 (const char*)unicode2UTF8(params
.searchString
),iParseResult
);
639 if (_astrParserErrors
.Count() > 0) {
640 for (unsigned int i
=0; i
< _astrParserErrors
.Count(); ++i
) {
641 printf("Error %u: %s\n",i
,(const char*)unicode2UTF8(_astrParserErrors
[i
]));
644 return CMemFilePtr(NULL
);
647 if (iParseResult
!= 0) {
648 _astrParserErrors
.Add(wxString::Format(wxT("Undefined error %i on search expression"),iParseResult
));
650 return CMemFilePtr(NULL
);
654 printf("Search expression: ");
655 for (unsigned int i
= 0; i
< _SearchExpr
.m_aExpr
.Count(); i
++){
656 printf("%s ",(const char*)unicode2char(_SearchExpr
.m_aExpr
[i
]));
658 printf("\nExpression count: %i\n",(int)_SearchExpr
.m_aExpr
.GetCount());
661 parametercount
+= _SearchExpr
.m_aExpr
.GetCount();
664 printf("Parameters: %i\n",parametercount
);
667 /* Leave the unicode comment there, please... */
668 CSearchExprTarget
target(data
.get(), true /*I assume everyone is unicoded */ ? utf8strRaw
: utf8strNone
);
670 unsigned int iParameterCount
= 0;
671 if (_SearchExpr
.m_aExpr
.GetCount() <= 1) {
672 // lugdunummaster requested that searchs without OR or NOT operators,
673 // and hence with no more expressions than the string itself, be sent
674 // using a series of ANDed terms, intersecting the ANDs on the terms
675 // (but prepending them) instead of putting the boolean tree at the start
676 // like other searches. This type of search is supposed to take less load
677 // on servers. Go figure.
679 // input: "a" AND min=1 AND max=2
680 // instead of: AND AND "a" min=1 max=2
681 // we use: AND "a" AND min=1 max=2
683 if (_SearchExpr
.m_aExpr
.GetCount() > 0) {
684 if (++iParameterCount
< parametercount
) {
685 target
.WriteBooleanAND();
687 target
.WriteMetaDataSearchParam(_SearchExpr
.m_aExpr
[0]);
690 if (!typeText
.IsEmpty()) {
691 if (++iParameterCount
< parametercount
) {
692 target
.WriteBooleanAND();
694 // Type is always ascii string
695 target
.WriteMetaDataSearchParamASCII(FT_FILETYPE
, typeText
);
698 if (params
.minSize
> 0) {
699 if (++iParameterCount
< parametercount
) {
700 target
.WriteBooleanAND();
702 target
.WriteMetaDataSearchParam(FT_FILESIZE
, ED2K_SEARCH_OP_GREATER
, params
.minSize
);
705 if (params
.maxSize
> 0){
706 if (++iParameterCount
< parametercount
) {
707 target
.WriteBooleanAND();
709 target
.WriteMetaDataSearchParam(FT_FILESIZE
, ED2K_SEARCH_OP_LESS
, params
.maxSize
);
712 if (params
.availability
> 0){
713 if (++iParameterCount
< parametercount
) {
714 target
.WriteBooleanAND();
716 target
.WriteMetaDataSearchParam(FT_SOURCES
, ED2K_SEARCH_OP_GREATER
, params
.availability
);
719 if (!params
.extension
.IsEmpty()){
720 if (++iParameterCount
< parametercount
) {
721 target
.WriteBooleanAND();
723 target
.WriteMetaDataSearchParam(FT_FILEFORMAT
, params
.extension
);
726 //#warning TODO - I keep this here, ready if we ever allow such searches...
729 if (++iParameterCount
< parametercount
) {
730 target
.WriteBooleanAND();
732 target
.WriteMetaDataSearchParam(FT_COMPLETE_SOURCES
, ED2K_SEARCH_OP_GREATER
, complete
);
736 if (++iParameterCount
< parametercount
) {
737 target
.WriteBooleanAND();
739 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_BITRATE
: FT_ED2K_MEDIA_BITRATE
, ED2K_SEARCH_OP_GREATER
, minBitrate
);
743 if (++iParameterCount
< parametercount
) {
744 target
.WriteBooleanAND();
746 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_LENGTH
: FT_ED2K_MEDIA_LENGTH
, ED2K_SEARCH_OP_GREATER
, minLength
);
749 if (!codec
.IsEmpty()){
750 if (++iParameterCount
< parametercount
) {
751 target
.WriteBooleanAND();
753 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_CODEC
: FT_ED2K_MEDIA_CODEC
, codec
);
756 if (!title
.IsEmpty()){
757 if (++iParameterCount
< parametercount
) {
758 target
.WriteBooleanAND();
760 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_TITLE
: FT_ED2K_MEDIA_TITLE
, title
);
763 if (!album
.IsEmpty()){
764 if (++iParameterCount
< parametercount
) {
765 target
.WriteBooleanAND();
767 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_ALBUM
: FT_ED2K_MEDIA_ALBUM
, album
);
770 if (!artist
.IsEmpty()){
771 if (++iParameterCount
< parametercount
) {
772 target
.WriteBooleanAND();
774 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_ARTIST
: FT_ED2K_MEDIA_ARTIST
, artist
);
778 // If this assert fails... we're seriously fucked up
780 wxASSERT( iParameterCount
== parametercount
);
783 if (!params
.extension
.IsEmpty()) {
784 if (++iParameterCount
< parametercount
) {
785 target
.WriteBooleanAND();
789 if (params
.availability
> 0) {
790 if (++iParameterCount
< parametercount
) {
791 target
.WriteBooleanAND();
795 if (params
.maxSize
> 0){
796 if (++iParameterCount
< parametercount
) {
797 target
.WriteBooleanAND();
801 if (params
.minSize
> 0) {
802 if (++iParameterCount
< parametercount
) {
803 target
.WriteBooleanAND();
807 if (!typeText
.IsEmpty()){
808 if (++iParameterCount
< parametercount
) {
809 target
.WriteBooleanAND();
813 //#warning TODO - same as above...
816 if (++iParameterCount
< parametercount
) {
817 target
.WriteBooleanAND();
822 if (++iParameterCount
< parametercount
) {
823 target
.WriteBooleanAND();
828 if (++iParameterCount
< parametercount
) {
829 target
.WriteBooleanAND();
833 if (!codec
.IsEmpty()){
834 if (++iParameterCount
< parametercount
) {
835 target
.WriteBooleanAND();
839 if (!title
.IsEmpty()){
840 if (++iParameterCount
< parametercount
) {
841 target
.WriteBooleanAND();
845 if (!album
.IsEmpty()) {
846 if (++iParameterCount
< parametercount
) {
847 target
.WriteBooleanAND();
851 if (!artist
.IsEmpty()) {
852 if (++iParameterCount
< parametercount
) {
853 target
.WriteBooleanAND();
858 // As above, if this fails, we're seriously fucked up.
859 wxASSERT( iParameterCount
+ _SearchExpr
.m_aExpr
.GetCount() == parametercount
);
861 for (unsigned int j
= 0; j
< _SearchExpr
.m_aExpr
.GetCount(); ++j
) {
862 if (_SearchExpr
.m_aExpr
[j
] == SEARCHOPTOK_AND
) {
863 target
.WriteBooleanAND();
864 } else if (_SearchExpr
.m_aExpr
[j
] == SEARCHOPTOK_OR
) {
865 target
.WriteBooleanOR();
866 } else if (_SearchExpr
.m_aExpr
[j
] == SEARCHOPTOK_NOT
) {
867 target
.WriteBooleanNOT();
869 target
.WriteMetaDataSearchParam(_SearchExpr
.m_aExpr
[j
]);
873 if (!params
.typeText
.IsEmpty()) {
874 // Type is always ASCII string
875 target
.WriteMetaDataSearchParamASCII(FT_FILETYPE
, params
.typeText
);
878 if (params
.minSize
> 0) {
879 target
.WriteMetaDataSearchParam(FT_FILESIZE
, ED2K_SEARCH_OP_GREATER
, params
.minSize
);
882 if (params
.maxSize
> 0) {
883 target
.WriteMetaDataSearchParam(FT_FILESIZE
, ED2K_SEARCH_OP_LESS
, params
.maxSize
);
886 if (params
.availability
> 0) {
887 target
.WriteMetaDataSearchParam(FT_SOURCES
, ED2K_SEARCH_OP_GREATER
, params
.availability
);
890 if (!params
.extension
.IsEmpty()) {
891 target
.WriteMetaDataSearchParam(FT_FILEFORMAT
, params
.extension
);
894 //#warning TODO - third and last warning of the same series.
897 target
.WriteMetaDataSearchParam(FT_COMPLETE_SOURCES
, ED2K_SEARCH_OP_GREATER
, pParams
->uComplete
);
900 if (minBitrate
> 0) {
901 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_BITRATE
: FT_ED2K_MEDIA_BITRATE
, ED2K_SEARCH_OP_GREATER
, minBitrate
);
905 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_LENGTH
: FT_ED2K_MEDIA_LENGTH
, ED2K_SEARCH_OP_GREATER
, minLength
);
908 if (!codec
.IsEmpty()) {
909 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_CODEC
: FT_ED2K_MEDIA_CODEC
, codec
);
912 if (!title
.IsEmpty()) {
913 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_TITLE
: FT_ED2K_MEDIA_TITLE
, title
);
916 if (!album
.IsEmpty()) {
917 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_ALBUM
: FT_ED2K_MEDIA_ALBUM
, album
);
920 if (!artist
.IsEmpty()) {
921 target
.WriteMetaDataSearchParam(type
== KadSearch
? TAG_MEDIA_ARTIST
: FT_ED2K_MEDIA_ARTIST
, artist
);
927 // Packet ready to go.
932 void CSearchList::KademliaSearchKeyword(uint32_t searchID
, const Kademlia::CUInt128
*fileID
,
933 const wxString
& name
, uint64_t size
, const wxString
& type
, uint32_t kadPublishInfo
, const TagPtrList
& taglist
)
935 EUtf8Str eStrEncode
= utf8strRaw
;
939 fileID
->ToByteArray(fileid
);
940 temp
.WriteHash(CMD4Hash(fileid
));
942 temp
.WriteUInt32(0); // client IP
943 temp
.WriteUInt16(0); // client port
946 unsigned int uFilePosTagCount
= temp
.GetPosition();
948 temp
.WriteUInt32(tagcount
); // dummy tag count, will be filled later
951 CTagString
tagName(FT_FILENAME
, name
);
952 tagName
.WriteTagToFile(&temp
, eStrEncode
);
955 CTagInt64
tagSize(FT_FILESIZE
, size
);
956 tagSize
.WriteTagToFile(&temp
, eStrEncode
);
959 if (!type
.IsEmpty()) {
960 CTagString
tagType(FT_FILETYPE
, type
);
961 tagType
.WriteTagToFile(&temp
, eStrEncode
);
965 // Misc tags (bitrate, etc)
966 for (TagPtrList::const_iterator it
= taglist
.begin(); it
!= taglist
.end(); ++it
) {
967 (*it
)->WriteTagToFile(&temp
,eStrEncode
);
971 temp
.Seek(uFilePosTagCount
, wxFromStart
);
972 temp
.WriteUInt32(tagcount
);
974 temp
.Seek(0, wxFromStart
);
976 CSearchFile
*tempFile
= new CSearchFile(temp
, (eStrEncode
== utf8strRaw
), searchID
, 0, 0, wxEmptyString
, true);
977 tempFile
->SetKadPublishInfo(kadPublishInfo
);
982 // File_checked_for_headers