diff --git a/code/nel/include/nel/gui/curl_certificates.h b/code/nel/include/nel/gui/curl_certificates.h
new file mode 100644
index 000000000..021b13360
--- /dev/null
+++ b/code/nel/include/nel/gui/curl_certificates.h
@@ -0,0 +1,35 @@
+// Ryzom - MMORPG Framework
+// 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 .
+
+#ifndef CL_CURL_CERTIFICATES_HTML_H
+#define CL_CURL_CERTIFICATES_HTML_H
+
+#include
+
+#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
diff --git a/code/nel/include/nel/gui/group_editbox.h b/code/nel/include/nel/gui/group_editbox.h
index 771bdc482..14d9d55a5 100644
--- a/code/nel/include/nel/gui/group_editbox.h
+++ b/code/nel/include/nel/gui/group_editbox.h
@@ -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)
diff --git a/code/nel/include/nel/gui/group_html.h b/code/nel/include/nel/gui/group_html.h
index da90095a3..969eac1f7 100644
--- a/code/nel/include/nel/gui/group_html.h
+++ b/code/nel/include/nel/gui/group_html.h
@@ -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();
diff --git a/code/nel/include/nel/gui/http_hsts.h b/code/nel/include/nel/gui/http_hsts.h
new file mode 100644
index 000000000..2693461cd
--- /dev/null
+++ b/code/nel/include/nel/gui/http_hsts.h
@@ -0,0 +1,73 @@
+// Ryzom - MMORPG Framework
+// 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 .
+
+#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 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
diff --git a/code/nel/src/gui/curl_certificates.cpp b/code/nel/src/gui/curl_certificates.cpp
new file mode 100644
index 000000000..6cf34c8b8
--- /dev/null
+++ b/code/nel/src/gui/curl_certificates.cpp
@@ -0,0 +1,123 @@
+// Ryzom - MMORPG Framework
+// 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 .
+
+//#include
+
+#include "stdpch.h"
+#include "nel/gui/curl_certificates.h"
+
+#include
+#include
+
+#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 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
+
diff --git a/code/nel/src/gui/group_editbox.cpp b/code/nel/src/gui/group_editbox.cpp
index f0a41c8a0..e7ed22583 100644
--- a/code/nel/src/gui/group_editbox.cpp
+++ b/code/nel/src/gui/group_editbox.cpp
@@ -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();
diff --git a/code/nel/src/gui/group_html.cpp b/code/nel/src/gui/group_html.cpp
index 8c309aa46..0c082ae58 100644
--- a/code/nel/src/gui/group_html.cpp
+++ b/code/nel/src/gui/group_html.cpp
@@ -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
diff --git a/code/nel/src/gui/http_hsts.cpp b/code/nel/src/gui/http_hsts.cpp
new file mode 100644
index 000000000..980bdabda
--- /dev/null
+++ b/code/nel/src/gui/http_hsts.cpp
@@ -0,0 +1,245 @@
+// Ryzom - MMORPG Framework
+// 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 .
+
+#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(¤tTime);
+
+ 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=; includeSubdomains; preload;
+ std::vector elements;
+ NLMISC::splitString(toLower(header), ";", elements);
+ if (elements.empty()) return;
+
+ time_t currentTime;
+ time(¤tTime);
+
+ 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);
+ }
+ }
+
+}
diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/inventory.xml b/code/ryzom/client/data/gamedev/interfaces_v3/inventory.xml
index e9ff0719b..2864894ab 100644
--- a/code/ryzom/client/data/gamedev/interfaces_v3/inventory.xml
+++ b/code/ryzom/client/data/gamedev/interfaces_v3/inventory.xml
@@ -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"
diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml b/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml
index 193678ec8..e5ece5680 100644
--- a/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml
+++ b/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml
@@ -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" />
+
setCacheIndex("cache/cache.index");
CHttpCache::getInstance()->init();
+ CStrictTransportSecurity::getInstance()->init("save/hsts-list.save");
+
// Register the reflected classes
registerInterfaceElements();
diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp
index 46fea9593..88b454279 100644
--- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp
+++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp
@@ -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())
diff --git a/code/ryzom/client/src/interface_v3/inventory_manager.cpp b/code/ryzom/client/src/interface_v3/inventory_manager.cpp
index 4f882fd21..745d211d0 100644
--- a/code/ryzom/client/src/interface_v3/inventory_manager.cpp
+++ b/code/ryzom/client/src/interface_v3/inventory_manager.cpp
@@ -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(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(CWidgetManager::getInstance()->getElementFromId(invId + ":bag_list"));
+ if (pList != NULL) pList->setSearchFilter(eb->getInputString());
+
+ CDBGroupIconListBag *pIcons = dynamic_cast(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
diff --git a/code/ryzom/client/src/interface_v3/inventory_manager.h b/code/ryzom/client/src/interface_v3/inventory_manager.h
index 0f6d0ea69..64114963c 100644
--- a/code/ryzom/client/src/interface_v3/inventory_manager.h
+++ b/code/ryzom/client/src/interface_v3/inventory_manager.h
@@ -519,18 +519,26 @@ struct SBagOptions
bool LastDbFilterMP;
bool LastDbFilterMissMP;
bool LastDbFilterTP;
+
+ bool SearchFilterChanged;
+ std::vector 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
diff --git a/code/ryzom/client/src/item_group_manager.cpp b/code/ryzom/client/src/item_group_manager.cpp
index bd94f4231..e72f2d730 100644
--- a/code/ryzom/client/src/item_group_manager.cpp
+++ b/code/ryzom/client/src/item_group_manager.cpp
@@ -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::max());
-
- Items.push_back(item);
+ 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)
{
diff --git a/code/ryzom/client/src/release.cpp b/code/ryzom/client/src/release.cpp
index 581d4c2b8..cd96f03b1 100644
--- a/code/ryzom/client/src/release.cpp
+++ b/code/ryzom/client/src/release.cpp
@@ -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/");