From 0dfd5af8b3fa812c4a4616580da4aa11f920b7fa Mon Sep 17 00:00:00 2001 From: karu Date: Sat, 1 Jul 2017 21:25:19 +0300 Subject: [PATCH 1/9] Added: Load Certificates from Windows into OpenSSL for HTTPS (issue #314) --HG-- branch : develop --- code/nel/src/gui/group_html.cpp | 103 ++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/code/nel/src/gui/group_html.cpp b/code/nel/src/gui/group_html.cpp index 8c309aa46..ec20d58d2 100644 --- a/code/nel/src/gui/group_html.cpp +++ b/code/nel/src/gui/group_html.cpp @@ -47,6 +47,16 @@ #include "nel/gui/url_parser.h" #include "nel/gui/http_cache.h" +#if defined(NL_OS_WINDOWS) +#include +#include +#include + +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "cryptui.lib") +#endif + + using namespace std; using namespace NLMISC; @@ -70,6 +80,83 @@ namespace NLGUI CGroupHTML::SWebOptions CGroupHTML::options; +#if defined(NL_OS_WINDOWS) + 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 < List.size(); ++i) + { + X509_free(List[i]); + } + + List.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) + { + List.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); + } + public: + std::vector List; + }; + + /// this will be initialized on startup and cleared on exit + static SX509Certificates x509CertList; + + static CURLcode ssl_ctx_function(CURL *curl, void *sslctx, void *parm) + { + if (x509CertList.List.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.List.size(); ++i) + { + X509_STORE_add_cert(x509store, x509CertList.List[i]); + } + } + else + { + nlwarning("SSL_CTX_get_cert_store returned NULL"); + } + } + return CURLE_OK; + } +#endif // Active cURL www transfer class CCurlWWWData @@ -356,6 +443,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, &ssl_ctx_function); + } +#endif + download.data = new CCurlWWWData(curl, download.url); download.fp = fp; @@ -5264,6 +5359,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, &ssl_ctx_function); + } +#endif + // do not follow redirects, we have own handler curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0); // after redirect From 368e1389f2511f88ec588dedd3cf6d570ce62fce Mon Sep 17 00:00:00 2001 From: Nimetu Date: Mon, 3 Jul 2017 21:54:35 +0300 Subject: [PATCH 2/9] Fixed: Clean up dyn bubbles from chat/webig to prevent crash after character change. --HG-- branch : develop --- .../src/interface_v3/group_in_scene_bubble.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp index e9c7776c5..5660ef212 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp +++ b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp @@ -312,6 +312,18 @@ void CGroupInSceneBubbleManager::release () _CurrentBubble = 0; _PopupCount = 0; + + // + for (i=0; i<_DynBubbles.size(); i++) + { + CWidgetManager::getInstance()->unMakeWindow(_DynBubbles[i].Bubble); + if (_DynBubbles[i].Bubble->getParent()) + _DynBubbles[i].Bubble->getParent()->delGroup(_DynBubbles[i].Bubble); + else + delete _DynBubbles[i].Bubble; + } + _DynBubbles.clear(); + _GroupToDelete.clear(); } // *************************************************************************** From 9a3a35214284265e19fad3a74f758fd254f5c8fb Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 4 Jul 2017 00:34:11 +0300 Subject: [PATCH 3/9] Changed: Refactor curl certificate loading (issue #314) --HG-- branch : develop --- code/nel/include/nel/gui/curl_certificates.h | 35 ++++++ code/nel/src/gui/curl_certificates.cpp | 123 +++++++++++++++++++ code/nel/src/gui/group_html.cpp | 93 +------------- 3 files changed, 161 insertions(+), 90 deletions(-) create mode 100644 code/nel/include/nel/gui/curl_certificates.h create mode 100644 code/nel/src/gui/curl_certificates.cpp 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/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_html.cpp b/code/nel/src/gui/group_html.cpp index ec20d58d2..e99149cec 100644 --- a/code/nel/src/gui/group_html.cpp +++ b/code/nel/src/gui/group_html.cpp @@ -46,16 +46,7 @@ #include "nel/misc/big_file.h" #include "nel/gui/url_parser.h" #include "nel/gui/http_cache.h" - -#if defined(NL_OS_WINDOWS) -#include -#include -#include - -#pragma comment(lib, "crypt32.lib") -#pragma comment(lib, "cryptui.lib") -#endif - +#include "nel/gui/curl_certificates.h" using namespace std; using namespace NLMISC; @@ -80,84 +71,6 @@ namespace NLGUI CGroupHTML::SWebOptions CGroupHTML::options; -#if defined(NL_OS_WINDOWS) - 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 < List.size(); ++i) - { - X509_free(List[i]); - } - - List.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) - { - List.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); - } - public: - std::vector List; - }; - - /// this will be initialized on startup and cleared on exit - static SX509Certificates x509CertList; - - static CURLcode ssl_ctx_function(CURL *curl, void *sslctx, void *parm) - { - if (x509CertList.List.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.List.size(); ++i) - { - X509_STORE_add_cert(x509store, x509CertList.List[i]); - } - } - else - { - nlwarning("SSL_CTX_get_cert_store returned NULL"); - } - } - return CURLE_OK; - } -#endif - // Active cURL www transfer class CCurlWWWData { @@ -447,7 +360,7 @@ namespace NLGUI // https:// if (toLower(download.url.substr(0, 8)) == "https://") { - curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, &ssl_ctx_function); + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, &CCurlCertificates::sslCtxFunction); } #endif @@ -5363,7 +5276,7 @@ namespace NLGUI // https:// if (toLower(url.substr(0, 8)) == "https://") { - curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, &ssl_ctx_function); + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, &CCurlCertificates::sslCtxFunction); } #endif From 59ce9437c84bd2f5fd259e973187966007ab957f Mon Sep 17 00:00:00 2001 From: Guillaume Dupuy Date: Sat, 8 Jul 2017 13:43:51 +0200 Subject: [PATCH 4/9] Swap item serial and create time properties when swapping items --HG-- branch : fix_item_group_08072017 --- code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp | 6 ++++++ 1 file changed, 6 insertions(+) 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()) From bc17b5eb1a961bc0833765d04287b3d890cc828a Mon Sep 17 00:00:00 2001 From: Guillaume Dupuy Date: Sat, 8 Jul 2017 14:55:18 +0200 Subject: [PATCH 5/9] Don't add multiples identical items to a group Solves the issue where 2 hand weapon were not properly equipped --HG-- branch : fix_item_group_08072017 --- code/ryzom/client/src/item_group_manager.cpp | 26 ++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/code/ryzom/client/src/item_group_manager.cpp b/code/ryzom/client/src/item_group_manager.cpp index bd94f4231..3cc45f5f9 100644 --- a/code/ryzom/client/src/item_group_manager.cpp +++ b/code/ryzom/client/src/item_group_manager.cpp @@ -73,6 +73,21 @@ 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) { From 63138ba784b255b2e451030d47b66c67535aaaaa Mon Sep 17 00:00:00 2001 From: Guillaume Dupuy Date: Tue, 11 Jul 2017 00:27:04 +0200 Subject: [PATCH 6/9] Use correct comparison between enum Was previously (badly) relying on int as boolean behaviour Solves a bug where remove slot was not working with headdress (value 0 in enum TSlotEquipment) --HG-- branch : develop --- code/ryzom/client/src/item_group_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/ryzom/client/src/item_group_manager.cpp b/code/ryzom/client/src/item_group_manager.cpp index 3cc45f5f9..e72f2d730 100644 --- a/code/ryzom/client/src/item_group_manager.cpp +++ b/code/ryzom/client/src/item_group_manager.cpp @@ -94,8 +94,8 @@ void CItemGroup::addItem(sint32 createTime, sint32 serial, SLOT_EQUIPMENT::TSlot 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) From 71d969c40b33286902ff22f3d97e8d18199a6fec Mon Sep 17 00:00:00 2001 From: Nimetu Date: Tue, 11 Jul 2017 22:23:38 +0300 Subject: [PATCH 7/9] Changed: Keep track of HSTS header and rewrite http to https as needed --HG-- branch : develop --- code/nel/include/nel/gui/group_html.h | 4 +- code/nel/include/nel/gui/http_hsts.h | 73 ++++++++ code/nel/src/gui/group_html.cpp | 66 ++++++- code/nel/src/gui/http_hsts.cpp | 245 ++++++++++++++++++++++++++ code/ryzom/client/src/init.cpp | 3 + code/ryzom/client/src/release.cpp | 2 + 6 files changed, 387 insertions(+), 6 deletions(-) create mode 100644 code/nel/include/nel/gui/http_hsts.h create mode 100644 code/nel/src/gui/http_hsts.cpp 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/group_html.cpp b/code/nel/src/gui/group_html.cpp index e99149cec..0c082ae58 100644 --- a/code/nel/src/gui/group_html.cpp +++ b/code/nel/src/gui/group_html.cpp @@ -46,6 +46,7 @@ #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; @@ -71,6 +72,26 @@ 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; @@ -406,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++) @@ -421,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); @@ -467,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++) { @@ -577,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) { @@ -663,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()))) { @@ -5239,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 (); @@ -5253,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()); 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/src/init.cpp b/code/ryzom/client/src/init.cpp index 900f3c2c1..244ef3384 100644 --- a/code/ryzom/client/src/init.cpp +++ b/code/ryzom/client/src/init.cpp @@ -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(); diff --git a/code/ryzom/client/src/release.cpp b/code/ryzom/client/src/release.cpp index a1f3a29c2..ff569b22a 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://www.ryzomcore.org/exit/"); From 455a923031e20ad72d14f08ebdbfea4600d984c3 Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 12 Jul 2017 20:32:42 +0300 Subject: [PATCH 8/9] Added: clear_on_escape property to edit box --HG-- branch : develop --- code/nel/include/nel/gui/group_editbox.h | 1 + code/nel/src/gui/group_editbox.cpp | 23 +++++++++++++++++++ .../data/gamedev/interfaces_v3/widgets.xml | 2 ++ 3 files changed, 26 insertions(+) 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/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/ryzom/client/data/gamedev/interfaces_v3/widgets.xml b/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml index 193678ec8..bf3c8d44b 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" From 7350291a560fd594b8ca7dcb15f18b32d5f25eff Mon Sep 17 00:00:00 2001 From: Nimetu Date: Wed, 12 Jul 2017 21:19:40 +0300 Subject: [PATCH 9/9] Added: Text search to inventory --HG-- branch : develop --- .../data/gamedev/interfaces_v3/inventory.xml | 2 +- .../data/gamedev/interfaces_v3/widgets.xml | 12 ++++ .../src/interface_v3/inventory_manager.cpp | 63 +++++++++++++++++++ .../src/interface_v3/inventory_manager.h | 12 ++++ 4 files changed, 88 insertions(+), 1 deletion(-) 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 bf3c8d44b..e5ece5680 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml +++ b/code/ryzom/client/data/gamedev/interfaces_v3/widgets.xml @@ -6589,6 +6589,18 @@ dblink="UI:SAVE:#inv_type:FILTER_ARMOR" texture="filter_armor.tga" tooltip="uittFilterArmor" /> + 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