Merge with develop

--HG--
branch : compatibility-develop
This commit is contained in:
Nimetu 2017-07-12 21:23:08 +03:00
commit 30512804d5
16 changed files with 707 additions and 11 deletions

View file

@ -0,0 +1,35 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CL_CURL_CERTIFICATES_HTML_H
#define CL_CURL_CERTIFICATES_HTML_H
#include <curl/curl.h>
#include "nel/misc/types_nl.h"
namespace NLGUI
{
#if defined(NL_OS_WINDOWS)
class CCurlCertificates {
public:
// cURL SSL certificate loading
static CURLcode sslCtxFunction(CURL *curl, void *sslctx, void *parm);
};
#endif // NL_OS_WINDOWS
} // namespace
#endif

View file

@ -264,6 +264,7 @@ namespace NLGUI
// because of multiline, thz parent container will be moved to top
// The good position can be restored by a press on enter then
bool _WantReturn : 1; // Want return char, don't call the enter action handler
bool _ClearOnEscape : 1; // clear content when ESC is pressed?
bool _Savable : 1; // should content be saved ?
bool _DefaultInputString : 1; // Is the current input string the default one (should not be edited)
bool _Frozen : 1; // is the control frozen? (cannot edit in it)

View file

@ -793,7 +793,7 @@ namespace NLGUI
void doBrowseLocalFile(const std::string &filename);
// load remote content using either GET or POST
void doBrowseRemoteUrl(const std::string &url, const std::string &referer, bool doPost = false, const SFormFields &formfields = SFormFields());
void doBrowseRemoteUrl(std::string url, const std::string &referer, bool doPost = false, const SFormFields &formfields = SFormFields());
// render html string as new browser page
bool renderHtmlString(const std::string &html);
@ -861,7 +861,7 @@ namespace NLGUI
// BnpDownload system
void initBnpDownload();
void checkBnpDownload();
bool addBnpDownload(const std::string &url, const std::string &action, const std::string &script, const std::string &md5sum);
bool addBnpDownload(std::string url, const std::string &action, const std::string &script, const std::string &md5sum);
std::string localBnpName(const std::string &url);
void releaseDownloads();

View file

@ -0,0 +1,73 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CL_HTTP_HSTS_H
#define CL_HTTP_HSTS_H
#include "nel/misc/types_nl.h"
namespace NLGUI
{
// ********************************************************************************
struct SHSTSObject
{
public:
SHSTSObject(uint64 expires = 0, bool includeSubDomains = false)
: Expires(expires)
, IncludeSubDomains(includeSubDomains)
{ }
uint64 Expires;
bool IncludeSubDomains;
};
/**
* Keeping track of HSTS header
* \author Meelis Mägi (nimetu)
* \date 2017
*/
class CStrictTransportSecurity
{
public:
typedef std::map<std::string, SHSTSObject> THSTSObject;
static CStrictTransportSecurity* getInstance();
static void release();
public:
bool isSecureHost(const std::string &domain) const;
// ************************************************************************
void init(const std::string& fname);
void save();
void erase(const std::string &domain);
void set(const std::string &domain, uint64 expires, bool includeSubDomains);
bool get(const std::string &domain, SHSTSObject &hsts) const;
void setFromHeader(const std::string &domain, const std::string &header);
void serial(NLMISC::IStream& f);
private:
static CStrictTransportSecurity* instance;
~CStrictTransportSecurity();
std::string _Filename;
THSTSObject _Domains;
};
}
#endif

View file

@ -0,0 +1,123 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//#include <crtdbg.h>
#include "stdpch.h"
#include "nel/gui/curl_certificates.h"
#include <openssl/x509.h>
#include <openssl/ssl.h>
#if defined(NL_OS_WINDOWS)
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
using namespace std;
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI
{
#if defined(NL_OS_WINDOWS)
static std::vector<X509 *> x509CertList;
//
// x509CertList lifetime manager
//
class SX509Certificates {
public:
SX509Certificates()
{
curl_version_info_data *data;
data = curl_version_info(CURLVERSION_NOW);
if (!(data && data->features & CURL_VERSION_SSPI))
{
addCertificatesFrom("CA");
addCertificatesFrom("AuthRoot");
addCertificatesFrom("ROOT");
}
}
~SX509Certificates()
{
for (uint i = 0; i < x509CertList.size(); ++i)
{
X509_free(x509CertList[i]);
}
x509CertList.clear();
}
void addCertificatesFrom(LPCSTR root)
{
HCERTSTORE hStore;
PCCERT_CONTEXT pContext = NULL;
X509 *x509;
hStore = CertOpenSystemStore(NULL, root);
if (hStore)
{
while (pContext = CertEnumCertificatesInStore(hStore, pContext))
{
x509 = NULL;
x509 = d2i_X509(NULL, (const unsigned char **)&pContext->pbCertEncoded, pContext->cbCertEncoded);
if (x509)
{
x509CertList.push_back(x509);
}
}
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
}
// this is called before debug context is set and log ends up in log.log
//nlinfo("Loaded %d certificates from '%s' certificate store", List.size(), root);
}
};
/// this will be initialized on startup and cleared on exit
static SX509Certificates x509CertListManager;
// ***************************************************************************
// static
CURLcode CCurlCertificates::sslCtxFunction(CURL *curl, void *sslctx, void *parm)
{
if (x509CertList.size() > 0)
{
SSL_CTX *ctx = (SSL_CTX*)sslctx;
X509_STORE *x509store = SSL_CTX_get_cert_store(ctx);
if (x509store)
{
for (uint i = 0; i < x509CertList.size(); ++i)
{
X509_STORE_add_cert(x509store, x509CertList[i]);
}
}
else
{
nlwarning("SSL_CTX_get_cert_store returned NULL");
}
}
return CURLE_OK;
}
#endif // NL_OS_WINDOWS
}// namespace

View file

@ -77,6 +77,7 @@ namespace NLGUI
_ResetFocusOnHide(false),
_BackupFatherContainerPos(false),
_WantReturn(false),
_ClearOnEscape(false),
_Savable(true),
_DefaultInputString(false),
_Frozen(false),
@ -239,6 +240,11 @@ namespace NLGUI
return toString( _WantReturn );
}
else
if( name == "clear_on_escape" )
{
return toString( _ClearOnEscape );
}
else
if( name == "savable" )
{
return toString( _Savable );
@ -413,6 +419,14 @@ namespace NLGUI
return;
}
else
if( name == "clear_on_escape" )
{
bool b;
if( fromString( value, b ) )
_ClearOnEscape = b;
return;
}
else
if( name == "savable" )
{
bool b;
@ -514,6 +528,7 @@ namespace NLGUI
xmlSetProp( node, BAD_CAST "backup_father_container_pos",
BAD_CAST toString( _BackupFatherContainerPos ).c_str() );
xmlSetProp( node, BAD_CAST "want_return", BAD_CAST toString( _WantReturn ).c_str() );
xmlSetProp( node, BAD_CAST "clear_on_escape", BAD_CAST toString( _ClearOnEscape ).c_str() );
xmlSetProp( node, BAD_CAST "savable", BAD_CAST toString( _Savable ).c_str() );
xmlSetProp( node, BAD_CAST "max_float_prec", BAD_CAST toString( _MaxFloatPrec ).c_str() );
@ -620,6 +635,9 @@ namespace NLGUI
prop = (char*) xmlGetProp( cur, (xmlChar*)"want_return" );
if (prop) _WantReturn = convertBool(prop);
prop = (char*) xmlGetProp( cur, (xmlChar*)"clear_on_escape" );
if (prop) _ClearOnEscape = convertBool(prop);
prop = (char*) xmlGetProp( cur, (xmlChar*)"savable" );
if (prop) _Savable = convertBool(prop);
@ -991,6 +1009,11 @@ namespace NLGUI
// stop selection
_CurrSelection = NULL;
_CursorAtPreviousLineEnd = false;
if (_ClearOnEscape)
{
setInputString(ucstring(""));
triggerOnChangeAH();
}
break;
case KeyTAB:
makeTopWindow();

View file

@ -46,6 +46,8 @@
#include "nel/misc/big_file.h"
#include "nel/gui/url_parser.h"
#include "nel/gui/http_cache.h"
#include "nel/gui/http_hsts.h"
#include "nel/gui/curl_certificates.h"
using namespace std;
using namespace NLMISC;
@ -70,6 +72,25 @@ namespace NLGUI
CGroupHTML::SWebOptions CGroupHTML::options;
// Return URL with https is host is in HSTS list
static std::string upgradeInsecureUrl(const std::string &url)
{
if (toLower(url.substr(0, 7)) != "http://") {
return url;
}
CUrlParser uri(url);
if (!CStrictTransportSecurity::getInstance()->isSecureHost(uri.host)){
return url;
}
#ifdef LOG_DL
nlwarning("HSTS url : '%s', using https", url.c_str());
#endif
uri.scheme = "https";
return uri.toString();
}
// Active cURL www transfer
class CCurlWWWData
@ -149,6 +170,27 @@ namespace NLGUI
return "";
}
bool hasHSTSHeader()
{
// ignore header if not secure connection
if (toLower(Url.substr(0, 8)) != "https://")
{
return false;
}
return HeadersRecv.count("strict-transport-security") > 0;
}
const std::string getHSTSHeader()
{
if (hasHSTSHeader())
{
return HeadersRecv["strict-transport-security"];
}
return "";
}
public:
CURL *Request;
@ -356,6 +398,14 @@ namespace NLGUI
return false;
}
#if defined(NL_OS_WINDOWS)
// https://
if (toLower(download.url.substr(0, 8)) == "https://")
{
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, &CCurlCertificates::sslCtxFunction);
}
#endif
download.data = new CCurlWWWData(curl, download.url);
download.fp = fp;
@ -398,7 +448,7 @@ namespace NLGUI
// Add a image download request in the multi_curl
void CGroupHTML::addImageDownload(const string &url, CViewBase *img, const CStyleParams &style, TImageType type)
{
string finalUrl = getAbsoluteUrl(url);
string finalUrl = upgradeInsecureUrl(getAbsoluteUrl(url));
// Search if we are not already downloading this url.
for(uint i = 0; i < Curls.size(); i++)
@ -413,7 +463,7 @@ namespace NLGUI
}
}
// use requested url for local name
// use requested url for local name (cache)
string dest = localImageName(url);
#ifdef LOG_DL
nlwarning("add to download '%s' dest '%s' img %p", finalUrl.c_str(), dest.c_str(), img);
@ -459,8 +509,10 @@ namespace NLGUI
}
// Add a bnp download request in the multi_curl, return true if already downloaded
bool CGroupHTML::addBnpDownload(const string &url, const string &action, const string &script, const string &md5sum)
bool CGroupHTML::addBnpDownload(string url, const string &action, const string &script, const string &md5sum)
{
url = upgradeInsecureUrl(getAbsoluteUrl(url));
// Search if we are not already downloading this url.
for(uint i = 0; i < Curls.size(); i++)
{
@ -569,6 +621,12 @@ namespace NLGUI
#ifdef LOG_DL
nlwarning("(%s) web transfer '%p' completed with status %d, http %d, url (len %d) '%s'", _Id.c_str(), _CurlWWW->Request, res, code, _CurlWWW->Url.size(), _CurlWWW->Url.c_str());
#endif
// save HSTS header from all requests regardless of HTTP code
if (res == CURLE_OK && _CurlWWW->hasHSTSHeader())
{
CUrlParser uri(_CurlWWW->Url);
CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, _CurlWWW->getHSTSHeader());
}
if (res != CURLE_OK)
{
@ -655,6 +713,12 @@ namespace NLGUI
#endif
curl_multi_remove_handle(MultiCurl, it->data->Request);
// save HSTS header from all requests regardless of HTTP code
if (res == CURLE_OK && it->data->hasHSTSHeader())
{
CStrictTransportSecurity::getInstance()->setFromHeader(uri.host, it->data->getHSTSHeader());
}
string tmpfile = it->dest + ".tmp";
if(res != CURLE_OK || r < 200 || r >= 300 || (!it->md5sum.empty() && (it->md5sum != getMD5(tmpfile).toString())))
{
@ -5231,7 +5295,7 @@ namespace NLGUI
}
// ***************************************************************************
void CGroupHTML::doBrowseRemoteUrl(const std::string &url, const std::string &referer, bool doPost, const SFormFields &formfields)
void CGroupHTML::doBrowseRemoteUrl(std::string url, const std::string &referer, bool doPost, const SFormFields &formfields)
{
// Stop previous request and remove content
stopBrowse ();
@ -5245,6 +5309,8 @@ namespace NLGUI
else
setTitle (_TitlePrefix + " - " + CI18N::get("uiPleaseWait"));
url = upgradeInsecureUrl(url);
#if LOG_DL
nlwarning("(%s) browse url (trusted=%s) '%s', referer='%s', post='%s', nb form values %d",
_Id.c_str(), (_TrustedDomain ? "true" :"false"), url.c_str(), referer.c_str(), (doPost ? "true" : "false"), formfields.Values.size());
@ -5264,6 +5330,14 @@ namespace NLGUI
return;
}
#if defined(NL_OS_WINDOWS)
// https://
if (toLower(url.substr(0, 8)) == "https://")
{
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, &CCurlCertificates::sslCtxFunction);
}
#endif
// do not follow redirects, we have own handler
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0);
// after redirect

View file

@ -0,0 +1,245 @@
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdpch.h"
#include "nel/gui/http_hsts.h"
using namespace std;
using namespace NLMISC;
#ifdef DEBUG_NEW
#define new DEBUG_NEW
#endif
namespace NLGUI {
CStrictTransportSecurity* CStrictTransportSecurity::instance = NULL;
CStrictTransportSecurity* CStrictTransportSecurity::getInstance()
{
if (!instance)
{
instance= new CStrictTransportSecurity();
}
return instance;
}
void CStrictTransportSecurity::release()
{
delete instance;
instance = NULL;
}
CStrictTransportSecurity::~CStrictTransportSecurity()
{
save();
}
// ************************************************************************
bool CStrictTransportSecurity::isSecureHost(const std::string &domain) const
{
SHSTSObject hsts;
if (get(domain, hsts))
{
time_t currentTime;
time(&currentTime);
return (hsts.Expires < currentTime);
}
return false;
}
// ************************************************************************
void CStrictTransportSecurity::erase(const std::string &domain)
{
if (_Domains.count(domain) > 0)
{
_Domains.erase(domain);
}
}
void CStrictTransportSecurity::set(const std::string &domain, uint64 expires, bool includeSubDomains)
{
if (expires == 0)
{
erase(domain);
return;
}
_Domains[domain].Expires = expires;
_Domains[domain].IncludeSubDomains = includeSubDomains;
}
bool CStrictTransportSecurity::get(const std::string &domain, SHSTSObject &hsts) const
{
if (domain.empty() || _Domains.empty())
return false;
if (_Domains.count(domain) > 0)
{
hsts = _Domains.at(domain);
return true;
}
size_t firstOf = domain.find_first_of(".");
size_t lastOf = domain.find_last_of(".");
while(firstOf != lastOf)
{
std::string tmp;
tmp = domain.substr(firstOf+1);
if (_Domains.count(tmp))
{
if (_Domains.at(tmp).IncludeSubDomains)
{
hsts = _Domains.at(tmp);
return true;
}
return false;
}
firstOf = domain.find_first_of(".", firstOf + 1);
}
return false;
}
void CStrictTransportSecurity::init(const std::string &fname)
{
_Domains.clear();
_Filename = fname;
if (_Filename.empty() || !CFile::fileExists(_Filename))
{
return;
}
CIFile in;
if (!in.open(_Filename))
{
nlwarning("Unable to open %s for reading", _Filename.c_str());
return;
}
serial(in);
}
void CStrictTransportSecurity::save()
{
if (_Filename.empty())
return;
if (_Domains.empty())
{
CFile::deleteFile(_Filename);
return;
}
COFile out;
if (!out.open(_Filename))
{
nlwarning("Unable to open %s for writing", _Filename.c_str());
return;
}
serial(out);
out.close();
}
void CStrictTransportSecurity::serial(NLMISC::IStream& f)
{
try
{
f.serialVersion(1);
// HSTS
f.serialCheck(NELID("STSH"));
if (f.isReading())
{
uint32 nbItems;
f.serial(nbItems);
for(uint32 k = 0; k < nbItems; ++k)
{
std::string domain;
f.serial(domain);
f.serial(_Domains[domain].Expires);
f.serial(_Domains[domain].IncludeSubDomains);
}
}
else
{
uint32 nbItems = _Domains.size();
f.serial(nbItems);
for (THSTSObject::iterator it = _Domains.begin(); it != _Domains.end(); ++it)
{
std::string domain(it->first);
f.serial(domain);
f.serial(_Domains[domain].Expires);
f.serial(_Domains[domain].IncludeSubDomains);
}
}
}
catch (...)
{
_Domains.clear();
nlwarning("Invalid HTST file format (%s)", _Filename.c_str());
}
}
// ***************************************************************************
void CStrictTransportSecurity::setFromHeader(const std::string &domain, const std::string &header)
{
// max-age=<seconds>; includeSubdomains; preload;
std::vector<std::string> elements;
NLMISC::splitString(toLower(header), ";", elements);
if (elements.empty()) return;
time_t currentTime;
time(&currentTime);
uint64 expire = 0;
bool includeSubDomains = false;
for(uint i=0; i< elements.size(); ++i)
{
std::string str(trim(elements[i]));
if (str.substr(0, 8) == "max-age=")
{
uint64 ttl;
if (fromString(str.substr(8), ttl))
{
if (ttl > 0)
{
expire = currentTime + ttl;
}
}
}
else if (str == "includesubdomains")
{
includeSubDomains = true;
}
}
if (expire == 0)
{
erase(domain);
}
else
{
set(domain, expire, includeSubDomains);
}
}
}

View file

@ -549,7 +549,7 @@
pop_min_h="240"
pop_max_w="920"
pop_max_h="1600"
w="300"
w="400"
h="400"
movable="true"
active="false"

View file

@ -2655,6 +2655,7 @@
fontsize="10"
backup_father_container_pos="false"
want_return="false"
clear_on_escape="false"
color="255 255 255 255"
continuous_text_update="false"
bg_texture="W_box_blank.tga"
@ -2695,6 +2696,7 @@
menu_r="#menu_r"
max_historic="#max_historic"
want_return="#want_return"
clear_on_escape="#clear_on_escape"
backup_father_container_pos="#backup_father_container_pos"
on_focus_lost="#on_focus_lost"
on_focus_lost_params="#on_focus_lost_params"
@ -6587,6 +6589,18 @@
dblink="UI:SAVE:#inv_type:FILTER_ARMOR"
texture="filter_armor.tga"
tooltip="uittFilterArmor" />
<instance template="edit_box_widget"
id="inv_query_eb"
posref="BR BR"
x="-190"
y="1"
w="100"
clear_on_escape="true"
enter_recover_focus="false"
max_num_chars="20"
max_historic="0"
onenter="inv_set_search"
onchange="inv_set_search" onchange_params="#inv_type" />
<!-- details -->
<instance template="tinv_item_list_icon_swap"
id="detail"

View file

@ -69,6 +69,7 @@
#include "interface_v3/sbrick_manager.h"
#include "nel/gui/widget_manager.h"
#include "nel/gui/http_cache.h"
#include "nel/gui/http_hsts.h"
//
#include "gabarit.h"
#include "hair_set.h"
@ -1370,6 +1371,8 @@ void prelogInit()
CHttpCache::getInstance()->setCacheIndex("cache/cache.index");
CHttpCache::getInstance()->init();
CStrictTransportSecurity::getInstance()->init("save/hsts-list.save");
// Register the reflected classes
registerInterfaceElements();

View file

@ -2960,6 +2960,8 @@ void CDBCtrlSheet::swapSheet(CDBCtrlSheet *other)
swapDBProps(getItemRMClassTypePtr(), other->getItemRMClassTypePtr());
swapDBProps(getItemRMFaberStatTypePtr(), other->getItemRMFaberStatTypePtr());
swapDBProps(getItemPrerequisitValidPtr(), other->getItemPrerequisitValidPtr());
swapDBProps(getItemSerialPtr(), other->getItemSerialPtr());
swapDBProps(getItemCreateTimePtr(), other->getItemCreateTimePtr());
}
}
@ -3539,6 +3541,10 @@ void CDBCtrlSheet::copyAspect(CDBCtrlSheet *dest)
dest->setItemRMFaberStatType(getItemRMFaberStatType());
// copy prerequisit valid flag
dest->setItemPrerequisitValid(getItemPrerequisitValid());
// copy item serial
dest->setItemSerial(getItemSerial());
// copy item create time
dest->setItemCreateTime(getItemCreateTime());
}
// if brick, sphrase or sphraseId
if(isSBrick() || isSPhrase() || isSPhraseId())

View file

@ -39,6 +39,7 @@
// For handlers
#include "nel/gui/action_handler.h"
#include "nel/gui/group_editbox.h"
#include "dbctrl_sheet.h"
#include "../sheet_manager.h"
@ -2010,6 +2011,18 @@ bool SBagOptions::parse(xmlNodePtr cur, CInterfaceGroup * /* parentGroup */)
return true;
}
// ***************************************************************************
void SBagOptions::setSearchFilter(const ucstring &s)
{
SearchFilter.clear();
SearchFilterChanged = true;
if (!s.empty())
{
splitUCString(toLower(s), ucstring(" "), SearchFilter);
}
}
// ***************************************************************************
bool SBagOptions::isSomethingChanged()
{
@ -2057,6 +2070,12 @@ bool SBagOptions::isSomethingChanged()
LastDbFilterTP = (DbFilterTP->getValue8() != 0);
}
if (SearchFilterChanged)
{
bRet = true;
SearchFilterChanged = false;
}
return bRet;
}
@ -2075,6 +2094,26 @@ bool SBagOptions::canDisplay(CDBCtrlSheet *pCS) const
const CItemSheet *pIS = pCS->asItemSheet();
if (pIS != NULL)
{
if (SearchFilter.size() > 0)
{
bool match = true;
ucstring lcName = toLower(pCS->getItemActualName());
// add item quality as a keyword to match
if (pCS->getQuality() > 1)
{
lcName += ucstring(" " + toString(pCS->getQuality()));
}
for (uint i = 0; i< SearchFilter.size(); ++i)
{
if (lcName.find(SearchFilter[i]) == ucstring::npos)
{
return false;
}
}
}
// Armor
if ((pIS->Family == ITEMFAMILY::ARMOR) ||
(pIS->Family == ITEMFAMILY::JEWELRY))
@ -2455,6 +2494,30 @@ class CHandlerInvDrag : public IActionHandler
};
REGISTER_ACTION_HANDLER( CHandlerInvDrag, "inv_drag" );
// **********************************************************************************************************
class CHandlerInvSetSearch : public IActionHandler
{
void execute (CCtrlBase *pCaller, const std::string &sParams)
{
if (!pCaller) return;
CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(pCaller);
if (!eb) return;
CInterfaceManager *pIM = CInterfaceManager::getInstance();
// ui:interface:inventory:content:bag:iil:inv_query_eb:eb
string invId = pCaller->getParent()->getParent()->getId();
CDBGroupListSheetBag *pList = dynamic_cast<CDBGroupListSheetBag*>(CWidgetManager::getInstance()->getElementFromId(invId + ":bag_list"));
if (pList != NULL) pList->setSearchFilter(eb->getInputString());
CDBGroupIconListBag *pIcons = dynamic_cast<CDBGroupIconListBag*>(CWidgetManager::getInstance()->getElementFromId(invId + ":bag_icons"));
if (pIcons != NULL) pIcons->setSearchFilter(eb->getInputString());
}
};
REGISTER_ACTION_HANDLER( CHandlerInvSetSearch, "inv_set_search" );
// ***************************************************************************
// COMMON INVENTORIES Test if we can drop an item to a slot or a list
class CHandlerInvCanDropTo : public IActionHandler

View file

@ -519,18 +519,26 @@ struct SBagOptions
bool LastDbFilterMP;
bool LastDbFilterMissMP;
bool LastDbFilterTP;
bool SearchFilterChanged;
std::vector<ucstring> SearchFilter;
// -----------------------
SBagOptions()
{
InvType = CInventoryManager::InvUnknown;
DbFilterArmor = DbFilterWeapon = DbFilterTool = DbFilterMP = DbFilterMissMP = DbFilterTP = NULL;
LastDbFilterArmor = LastDbFilterWeapon = LastDbFilterTool = LastDbFilterMP = LastDbFilterMissMP = LastDbFilterTP = false;
SearchFilterChanged = false;
}
bool parse (xmlNodePtr cur, CInterfaceGroup *parentGroup);
bool isSomethingChanged(); // From last call ?
bool isSearchFilterChanged() const { return SearchFilterChanged; }
void setSearchFilter(const ucstring &s);
bool getFilterArmor() const
{
if (DbFilterArmor == NULL) return true;
@ -621,6 +629,8 @@ public:
// Return true if the sheet can be displayed due to filters
bool canDisplay(CDBCtrlSheet *pCS) { return _BO.canDisplay(pCS); }
void setSearchFilter(const ucstring &s) { _BO.setSearchFilter(s); }
private:
SBagOptions _BO;
@ -652,6 +662,8 @@ public:
// Return true if the sheet can be displayed due to filters
bool canDisplay(CDBCtrlSheet *pCS) const { return _BO.canDisplay(pCS); }
void setSearchFilter(const ucstring &s) { _BO.setSearchFilter(s); }
//////////////////////////////////////////////////////////////////////////
// A child node

View file

@ -73,14 +73,29 @@ bool CItemGroup::contains(CDBCtrlSheet *other, SLOT_EQUIPMENT::TSlotEquipment &s
void CItemGroup::addItem(sint32 createTime, sint32 serial, SLOT_EQUIPMENT::TSlotEquipment slot)
{
//Don't add an item if it already exists, this could cause issue
// It's happening either if we are creating a group with a 2 hands items (and the item is found both in handR and handL)
// Or if an user incorrectly edit his group file
for(int i=0; i<Items.size(); i++)
{
if( Items[i].createTime == createTime && Items[i].serial == serial)
{
nldebug("Not adding duplicate item, createTime: %d, serial: %d", createTime, serial);
//In this case, we are adding the duplicate item for a 2 hands item
//If it's saved as a left hand item, save it as a right hand item instead (so we have only 1 correct item)
if(Items[i].slot == SLOT_EQUIPMENT::TSlotEquipment::HANDL && slot == SLOT_EQUIPMENT::TSlotEquipment::HANDR)
Items[i].slot = SLOT_EQUIPMENT::TSlotEquipment::HANDR;
return;
}
}
Items.push_back(CItem(createTime, serial, slot));
}
void CItemGroup::addRemove(std::string slotName)
{
SLOT_EQUIPMENT::TSlotEquipment slot = SLOT_EQUIPMENT::stringToSlotEquipment(NLMISC::toUpper(slotName));
if(slot)
removeBeforeEquip.push_back(slot);
if(slot != SLOT_EQUIPMENT::UNDEFINED)
addRemove(slot);
}
void CItemGroup::addRemove(SLOT_EQUIPMENT::TSlotEquipment slot)
@ -158,9 +173,16 @@ void CItemGroup::readFrom(xmlNodePtr node)
ptrName = (char*) xmlGetProp(curNode, (xmlChar*)"maxPrice");
if (ptrName) NLMISC::fromString((const char*)ptrName, item.maxPrice);
item.usePrice = (item.minPrice != 0 || item.maxPrice != std::numeric_limits<uint32>::max());
if(item.createTime != 0)
{
addItem(item.createTime, item.serial, item.slot);
}
// Old load : keep for compatibility / migration reasons
else
{
Items.push_back(item);
}
}
if (strcmp((char*)curNode->name, "remove") == 0)
{
std::string slot;

View file

@ -95,6 +95,7 @@
#include "nel/gui/lua_manager.h"
#include "item_group_manager.h"
#include "nel/gui/http_cache.h"
#include "nel/gui/http_hsts.h"
///////////
// USING //
@ -688,6 +689,7 @@ void release()
CViewRenderer::release();
CIXml::releaseLibXml();
CHttpCache::release();
CStrictTransportSecurity::release();
#if FINAL_VERSION
// openURL ("http://ryzom.com/exit/");