⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 searching.cpp

📁 Last change: 2008-02-03 This is the source code of KCeasy。
💻 CPP
字号:
/*
This file is part of KCeasy (http://www.kceasy.com)
Copyright (C) 2002-2004 Markus Kern <mkern@kceasy.com>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
*/
//---------------------------------------------------------------------------
#include <winsock.h> // inet_addr
#pragma hdrstop
#include <stdio.h>   // sscanf
#include "Searching.h"
#include "Engine.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

namespace KCeasyEngine {

// number of results immedetialy send to the UI before caching is turned on
static const int RESULTS_SENT_UNCACHED = 20;
// maximum cached results before they are written to the UI
static const int RESULTS_CACHE_SIZE = 40;

// class TSearchResult
// private

TSearchResult::TSearchResult()
:   FileSize(0),
    FileType(FTUnknown),
    Availability(0),
    Hashes(NULL)
{
    Hashes = new THashSet();
    User = new TUser();
}

TSearchResult::~TSearchResult()
{
    delete User;
    delete Hashes;
}

string TSearchResult::GetMagnetStr() const
{
    TMagnet* Magnet = new TMagnet();

    // add urns
    Magnet->SetExactTopicHashes(GetHashes());

    // add display name
    if(MetaData.Exists("artist") && MetaData.Exists("title"))
        Magnet->SetDisplayName(MetaData.Get("artist") + " - " + MetaData.Get("title"));
    else
        Magnet->SetDisplayName(FileName);

    string MagnetStr = Magnet->Assemble();
    delete Magnet;
    return MagnetStr;
}

bool TSearchResult::IsBannedByWord(const list<string>& Words)
{
    list<string>::const_iterator itr = Words.begin();
    for(;itr != Words.end(); ++itr) {
        if(string_tolower(FileName).find(*itr) != -1)
            return true;

        for(TMetaData::Iterator mitr = MetaData.Begin(); mitr != MetaData.End(); ++mitr) {
            if(string_tolower((*mitr).Value).find(*itr) != -1)
                return true;
        }
    }

    return false;
}

bool TSearchResult::IsBannedByExtension(const list<string>& Extensions)
{
    list<string>::const_iterator itr = Extensions.begin();
    int pos;

    if((pos = FileName.rfind('.')) == -1)
        return false;
    string Ext = string_tolower(FileName.substr(pos+1));

    for(;itr != Extensions.end(); ++itr)
        if(Ext == *itr)
            return true;

    return false;
}

// class TSearch
// private

TSearch::TSearch(TEngine* ParentEngine, const string& NQuery,
                 TSearchRealm NRealm, const string& NNetwork,
                 const TMetaData& NMetaData,
                 bool NBanWords, bool NBanExtensions, bool Intern)
:   Engine(ParentEngine),
    Internal(Intern),
    UData(NULL),
    State(New),
    GiftId(0),
    Realm(NRealm),
    Network(NNetwork),
    Query(NQuery),
    MetaData(NMetaData),
    ReceivedResults(0),
    BannedResults(0),
    BanWords(NBanWords),
    BanExtensions(NBanExtensions)
{

}

TSearch::~TSearch()
{
    LockResults();
    while(!Results.empty()) {
        delete Results.front();
        Results.pop_front();
    }
    ReleaseResults();
}

bool TSearch::ProcessItem(TGiftCommand* Cmd)
{
    if(Cmd->Name != "ITEM" || string_to_int(Cmd->Value) != GiftId || State != TSearch::Searching)
        return false;

    Engine->LockSearches();

    if(Cmd->Nodes.empty()) {
        // end of search
        State = Finished;
        GiftId = 0; // giFT reuses old IDs
        // notify UI
        if(IsInternal()) {
            if(GetUData())
                ((TDownload*)GetUData())->SourcesSearch = NULL;
            Engine->ReleaseSearches();
            Engine->RemoveSearch(this); // this deletes us!
        } else {
            Engine->ReleaseSearches();
            // send remaining results to ui
            CommitCachedResults();
            Engine->SendCallback(CbcSearchEnd,(void*)this,NULL);
        }
        return true;
    }

    TSearchResult* Result = new TSearchResult();
    Result->SourceId = Cmd->GetNode("url").Value;
    Result->Hashes->AddGiftHash(Cmd->GetNode("hash").Value);
    Result->User->SetId(Cmd->GetNode("user").Value);

    Result->FileName = FileFromPath(Cmd->GetNode("file").Value);
    Result->FileDir = DirFromPath(Cmd->GetNode("file").Value);
    Result->FileSize = string_to_int(Cmd->GetNode("size").Value);
    Result->MimeType = Cmd->GetNode("mime").Value;
    Result->FileType = FileTypeFromMime(Result->MimeType);
    Result->Availability = string_to_int(Cmd->GetNode("availability").Value);
    Result->Network = ProtocolFromUrl(Result->SourceId);

    // get meta data
    if(Cmd->NodeExists("META")) {
        TGiftCommand& MetaCmd = Cmd->GetNode("META");
        for(TGiftCommand::Iterator itr=MetaCmd.Nodes.begin();itr!=MetaCmd.Nodes.end();++itr)
            Result->MetaData.Set((*itr).Name,(*itr).Value);
    }

    // convert legacy resolution tag to width/height
    if(!Result->MetaData.Exists("width") && !Result->MetaData.Exists("height")) {
        int width = 0, height = 0;
        sscanf(Result->MetaData.Get("resolution").c_str(), "%dx%d",&width,&height);
        if(width > 0 && height > 0) {
            Result->MetaData.SetInt("width",width);
            Result->MetaData.SetInt("height",height);
            // remove resolution tag
            Result->MetaData.Remove("resolution");
        }
    }

    // HACK: giFT doesn't support downloading files > 2 GB, filter them here.
    if(Result->FileSize >= 2*1024*1024*1024) {
        delete Result;
        Engine->ReleaseSearches();
        return true;
    }

    // drop banned results if necessary
    if((BanWords && Result->IsBannedByWord(Engine->BannedWordsList)) ||
       (BanExtensions && Result->IsBannedByExtension(Engine->BannedExtensionsList)))
    {
        delete Result;
        BannedResults++;
        Engine->ReleaseSearches();
        return true;
    }

    LockResults();
#if 1
    // is this a redundant result from another search node?
    TSearch::TResultIterator ritr = Results.begin();
    for(;ritr!=Results.end();++ritr) {
        if((*ritr)->FileName == Result->FileName &&
           (*ritr)->GetUser()->GetId() == Result->GetUser()->GetId() &&
           (*ritr)->GetHashes()->Equals(Result->GetHashes()))
        {
            break;
        }
    }

    if(ritr != Results.end()) {
        delete Result;
        ReleaseResults();
        Engine->ReleaseSearches();
        return true;
    }
#endif

    if(IsInternal()) {
        // add source to download
        ((TDownload*)GetUData())->AddSource(Result);
        delete Result;
    } else {
        Results.push_back(Result);
        // add source to download if we have one with matching hash
        if(Result->GetHashes()->Size() != 0) {
            Engine->LockDownloads();
            TEngine::TDownloadIterator itr = Engine->GetDownloadsBegin();
            while(itr != Engine->GetDownloadsEnd() && *(*itr)->GetHashes() != *Result->GetHashes())
                ++itr;
            if(itr != Engine->GetDownloadsEnd() &&
               ((*itr)->GetState() != TDownload::Completed &&
               (*itr)->GetState() != TDownload::Cancelled))
            {
                (*itr)->AddSource(Result);
            }
            Engine->ReleaseDownloads();
        }
    }

    ReleaseResults();
    Engine->ReleaseSearches();
    // notify UI
    if(!IsInternal()) {
        ReceivedResults++;
        ResultCache.push_back(Result);
        // send first results immediately, after that cache
        if(ReceivedResults <= RESULTS_SENT_UNCACHED ||
           ResultCache.size() >= RESULTS_CACHE_SIZE)
        {
            CommitCachedResults();
        }
    } else {
/*
        if(Results.size() >= GetMaxResults() && GetMaxResults() != 0)
            Stop();
*/
    }

    return true;
}

void TSearch::CommitCachedResults()
{
    if(ResultCache.empty())
        return;

    Engine->SendCallback(CbcSearchResult,(void*)this,&ResultCache);
    ResultCache.clear();
}

// public

bool TSearch::Start()
{
    TGiftCommand* Cmd;

    if(State == Searching)
        return false;

    GiftId = Engine->GetNextGiftId();
    State = Searching;
    ReceivedResults = 0;

    if(Realm == SRSource) {
        Cmd = new TGiftCommand("LOCATE",int_to_string(GiftId));
        Cmd->AddNode(TGiftCommand("query",Query.c_str()));
    } else if(Realm == SRUser) {
        Cmd = new TGiftCommand("BROWSE",int_to_string(GiftId));
        Cmd->AddNode(TGiftCommand("query",Query.c_str()));
    } else {
        Cmd = new TGiftCommand("SEARCH",int_to_string(GiftId));
        Cmd->AddNode(TGiftCommand("query",Query.c_str()));
        switch(Realm) {
        case SRAudio:    Cmd->AddNode(TGiftCommand("realm","audio")); break;
        case SRVideo:    Cmd->AddNode(TGiftCommand("realm","video")); break;
        case SRImage:    Cmd->AddNode(TGiftCommand("realm","image")); break;
        case SRSoftware: Cmd->AddNode(TGiftCommand("realm","application")); break;
        case SRDocument: Cmd->AddNode(TGiftCommand("realm","text")); break;
        case SRTorrent:  Cmd->AddNode(TGiftCommand("realm","application/x-bittorrent")); break;
        };
        if(Network != "")
            Cmd->AddNode(TGiftCommand("protocol",Network.c_str()));
    }
    Engine->QueueCommand(Cmd);

    if(!IsInternal())
        Engine->SendCallback(CbcSearchBegin,(void*)this,NULL);

    return true;
}

bool TSearch::Cancel()
{
    if(State == New)
        return false;

    if(State == Searching) {
        TGiftCommand* Cmd = new TGiftCommand("SEARCH",int_to_string(GiftId));
        Cmd->AddNode(TGiftCommand("action","cancel"));
        Engine->QueueCommand(Cmd);

        State = Finished;
        GiftId = 0; // we don't care for further messages from this point on
        if(!IsInternal()) {
            CommitCachedResults();
            Engine->SendCallback(CbcSearchEnd,(void*)this,NULL);
        }
    }
    return true;
}

// Non-localized strings of realms (for config, etc)
static const struct
{
    TSearchRealm Realm;
    const char* String;
} Realms[] =
{
    { SRAny,      "Everything" },
    { SRAudio,    "Audio"      },
    { SRVideo,    "Video"      },
    { SRImage,    "Images"     },
    { SRDocument, "Documents"  },
    { SRSoftware, "Software"   },
    { SRSource,   "Sources"    },
    { SRUser,     "Users"      },
    { SRTorrent,  "Torrents"   },
    { SRAny,      NULL         }
};

const char* TSearch::RealmToString(TSearchRealm Realm)
{
    for(int i = 0;  Realms[i].String; i++)
        if(Realms[i].Realm == Realm)
            return Realms[i].String;
    return "";
}

const TSearchRealm TSearch::StringToRealm(const char* Str)
{
    for(int i = 0;  Realms[i].String; i++)
        if(strcmpi(Realms[i].String, Str) == 0)
            return Realms[i].Realm;
    return SRAny;
}

} // namespace KCeasyEngine

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -