Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libfilezilla for openSUSE:Factory 
checked in at 2021-05-07 16:45:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libfilezilla (Old)
 and      /work/SRC/openSUSE:Factory/.libfilezilla.new.2988 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libfilezilla"

Fri May  7 16:45:58 2021 rev:39 rq:891273 version:0.28.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libfilezilla/libfilezilla.changes        
2021-03-20 21:26:36.413221010 +0100
+++ /work/SRC/openSUSE:Factory/.libfilezilla.new.2988/libfilezilla.changes      
2021-05-07 16:46:24.276151980 +0200
@@ -1,0 +2,15 @@
+Fri May  7 10:49:25 UTC 2021 - ecsos <[email protected]>
+
+- Update to 0.28.0
+  * New features:
+    - Added fz::hostname_lookup
+    - Added fz::datetime::set_rfc3339
+    - Added fz::load_certificates and fz::load_certificates_file
+    - Added fz::base64_encode_append
+  * Bugfixes and minor changes:
+    - Moved some common functionality shared by multiple layers to
+      socket_layer itself
+    - Fixed a crash with older versions of GnuTLS if the system
+      trust store cannot be loaded
+
+-------------------------------------------------------------------

Old:
----
  libfilezilla-0.27.1.tar.bz2

New:
----
  libfilezilla-0.28.0.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libfilezilla.spec ++++++
--- /var/tmp/diff_new_pack.ueSkzQ/_old  2021-05-07 16:46:24.756149805 +0200
+++ /var/tmp/diff_new_pack.ueSkzQ/_new  2021-05-07 16:46:24.760149787 +0200
@@ -16,11 +16,11 @@
 #
 
 
-%define major          12
+%define major          13
 %define libname                %{name}%{major}
 %define develname      %{name}-devel
 Name:           libfilezilla
-Version:        0.27.1
+Version:        0.28.0
 Release:        0
 Summary:        C++ library for filezilla
 License:        GPL-2.0-or-later

++++++ libfilezilla-0.27.1.tar.bz2 -> libfilezilla-0.28.0.tar.bz2 ++++++
++++ 1634 lines of diff (skipped)
++++    retrying with extended exclude list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/NEWS new/libfilezilla-0.28.0/NEWS
--- old/libfilezilla-0.27.1/NEWS        2021-03-15 09:01:00.000000000 +0100
+++ new/libfilezilla-0.28.0/NEWS        2021-05-04 10:46:55.000000000 +0200
@@ -1,3 +1,12 @@
+0.28.0 (2021-05-03)
+
++ Added fz::hostname_lookup
++ Added fz::datetime::set_rfc3339
++ Added fz::load_certificates and fz::load_certificates_file
++ Added fz::base64_encode_append
+- Moved some common functionality shared by multiple layers to socket_layer 
itself
+- Fixed a crash with older versions of GnuTLS if the system trust store cannot 
be loaded
+
 0.27.1 (2021-03-15)
 
 - Fixed a socket event sequencing invariant violation in tls_layer
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/configure.ac new/libfilezilla-0.28.0/configure.ac
--- old/libfilezilla-0.27.1/configure.ac        2021-03-15 09:01:00.000000000 
+0100
+++ new/libfilezilla-0.28.0/configure.ac        2021-05-04 10:46:55.000000000 
+0200
@@ -1,4 +1,4 @@
-???AC_INIT([libfilezilla],[0.27.1],[[email protected]],[],[https://lib.filezilla-project.org/])
+???AC_INIT([libfilezilla],[0.28.0],[[email protected]],[],[https://lib.filezilla-project.org/])
 
 # Update the version information only immediately before a public release of 
your software
 # If the library source code has changed at all since the last update, then 
increment revision (???c:r:a??? becomes ???c:r+1:a???).
@@ -6,7 +6,7 @@
 # If any interfaces have been added since the last public release, then 
increment age.
 # If any interfaces have been removed or changed since the last public 
release, then set age to 0.
 # CURRENT:REVISION:AGE
-LIBRARY_VERSION=12:1:0
+LIBRARY_VERSION=13:0:0
 
 
 AC_CONFIG_HEADERS([config/config.hpp])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/Makefile.am new/libfilezilla-0.28.0/lib/Makefile.am
--- old/libfilezilla-0.27.1/lib/Makefile.am     2021-03-04 14:27:18.000000000 
+0100
+++ new/libfilezilla-0.28.0/lib/Makefile.am     2021-05-04 10:46:55.000000000 
+0200
@@ -9,6 +9,7 @@
        event_loop.cpp \
        file.cpp \
        hash.cpp \
+       hostname_lookup.cpp \
        invoker.cpp \
        iputils.cpp \
        local_filesys.cpp \
@@ -45,6 +46,7 @@
        libfilezilla/file.hpp \
        libfilezilla/format.hpp \
        libfilezilla/hash.hpp \
+       libfilezilla/hostname_lookup.hpp \
        libfilezilla/invoker.hpp \
        libfilezilla/iputils.hpp \
        libfilezilla/libfilezilla.hpp \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/buffer.cpp new/libfilezilla-0.28.0/lib/buffer.cpp
--- old/libfilezilla-0.27.1/lib/buffer.cpp      2020-12-17 14:46:00.000000000 
+0100
+++ new/libfilezilla-0.28.0/lib/buffer.cpp      2021-05-04 10:46:55.000000000 
+0200
@@ -2,6 +2,7 @@
 
 #include <algorithm>
 #include <cstdlib>
+#include <limits>
 
 #include <string.h>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/encode.cpp new/libfilezilla-0.28.0/lib/encode.cpp
--- old/libfilezilla-0.27.1/lib/encode.cpp      2020-07-07 14:06:30.000000000 
+0200
+++ new/libfilezilla-0.28.0/lib/encode.cpp      2021-05-04 10:46:55.000000000 
+0200
@@ -4,7 +4,7 @@
 
 namespace {
 template<typename DataContainer>
-std::string base64_encode_impl(DataContainer const& in, base64_type type, bool 
pad)
+void base64_encode_impl(std::string & out, DataContainer const& in, 
base64_type type, bool pad)
 {
        static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad 
container type");
 
@@ -13,12 +13,13 @@
                        ? 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
                        : 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 
-       std::string ret;
-
        size_t len = in.size();
        size_t pos{};
 
-       ret.reserve(((len + 2) / 3) * 4);
+       size_t const newcap = out.size() + ((len + 2) / 3) * 4;
+       if (out.capacity() < newcap) {
+               out.reserve(newcap);
+       }
 
        while (len >= 3) {
                len -= 3;
@@ -26,42 +27,49 @@
                auto const c2 = static_cast<unsigned char>(in[pos++]);
                auto const c3 = static_cast<unsigned char>(in[pos++]);
 
-               ret += base64_chars[(c1 >> 2) & 0x3fu];
-               ret += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 0xfu)];
-               ret += base64_chars[((c2 & 0xfu) << 2) | ((c3 >> 6) & 0x3u)];
-               ret += base64_chars[(c3 & 0x3fu)];
+               out += base64_chars[(c1 >> 2) & 0x3fu];
+               out += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 0xfu)];
+               out += base64_chars[((c2 & 0xfu) << 2) | ((c3 >> 6) & 0x3u)];
+               out += base64_chars[(c3 & 0x3fu)];
        }
        if (len) {
                auto const c1 = static_cast<unsigned char>(in[pos++]);
-               ret += base64_chars[(c1 >> 2) & 0x3fu];
+               out += base64_chars[(c1 >> 2) & 0x3fu];
                if (len == 2) {
                        auto const c2 = static_cast<unsigned char>(in[pos++]);
-                       ret += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 
0xfu)];
-                       ret += base64_chars[(c2 & 0xfu) << 2];
+                       out += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 
0xfu)];
+                       out += base64_chars[(c2 & 0xfu) << 2];
                }
                else {
-                       ret += base64_chars[(c1 & 0x3u) << 4];
+                       out += base64_chars[(c1 & 0x3u) << 4];
                        if (pad) {
-                               ret += '=';
+                               out += '=';
                        }
                }
                if (pad) {
-                       ret += '=';
+                       out += '=';
                }
        }
-
-       return ret;
 }
 }
 
 std::string base64_encode(std::string_view const& in, base64_type type, bool 
pad)
 {
-       return base64_encode_impl(in, type, pad);
+       std::string ret;
+       base64_encode_impl(ret, in, type, pad);
+       return ret;
 }
 
 std::string base64_encode(std::vector<uint8_t> const& in, base64_type type, 
bool pad)
 {
-       return base64_encode_impl(in, type, pad);
+       std::string ret;
+       base64_encode_impl(ret, in, type, pad);
+       return ret;
+}
+
+void base64_encode_append(std::string& result, std::string_view const& in, 
base64_type type, bool pad)
+{
+       base64_encode_impl(result, in, type, pad);
 }
 
 namespace {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/hostname_lookup.cpp 
new/libfilezilla-0.28.0/lib/hostname_lookup.cpp
--- old/libfilezilla-0.27.1/lib/hostname_lookup.cpp     1970-01-01 
01:00:00.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/hostname_lookup.cpp     2021-05-04 
10:46:55.000000000 +0200
@@ -0,0 +1,197 @@
+#include "libfilezilla/hostname_lookup.hpp"
+
+#ifdef FZ_WINDOWS
+#include "libfilezilla/private/windows.hpp"
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <mstcpip.h>
+#endif
+
+#include "libfilezilla/socket.hpp"
+#include "libfilezilla/thread_pool.hpp"
+
+#ifndef FZ_WINDOWS
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+namespace fz {
+
+class hostname_lookup::impl
+{
+public:
+       impl(hostname_lookup * parent, thread_pool& pool, event_handler* h)
+               : parent_(parent), pool_(pool), handler_(h)
+       {
+       }
+
+       bool spawn() {
+               if (!thread_) {
+                       thread_ = pool_.spawn([this](){ entry(); });
+               }
+               return thread_.operator bool();
+       }
+
+       void entry();
+       void do_lookup(scoped_lock & l);
+
+       mutex mtx_{false};
+       hostname_lookup* parent_;
+       thread_pool & pool_;
+       event_handler* handler_{};
+       condition cond_;
+       async_task thread_;
+       std::string host_;
+       address_type family_{};
+};
+
+void hostname_lookup::impl::entry()
+{
+       scoped_lock l(mtx_);
+       if (thread_) {
+               do {
+                       cond_.wait(l);
+                       do_lookup(l);
+
+               } while (thread_);
+       }
+
+       l.unlock();
+       delete this;
+}
+
+int convert_msw_error_code(int error);
+
+void hostname_lookup::impl::do_lookup(scoped_lock& l)
+{
+       if (host_.empty()) {
+               return;
+       }
+
+       l.unlock();
+
+       addrinfo hints{};
+       switch (family_) {
+       case address_type::ipv4:
+               hints.ai_family = AF_INET;
+               break;
+       case address_type::ipv6:
+               hints.ai_family = AF_INET6;
+               break;
+       default:
+               hints.ai_family = AF_UNSPEC;
+               break;
+       }
+       hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_IDN
+       hints.ai_flags |= AI_IDN;
+#endif
+
+       addrinfo* addressList{};
+       int res = getaddrinfo(host_.c_str(), nullptr, &hints, &addressList);
+
+       l.lock();
+
+       if (!thread_) {
+               if (!res) {
+                       freeaddrinfo(addressList);
+               }
+               return;
+       }
+
+       std::vector<std::string> addrs;
+       if (res) {
+#ifdef FZ_WINDOWS
+               res = convert_msw_error_code(res);
+#endif
+       }
+       else {
+               for (addrinfo* addr = addressList; addr && !res; addr = 
addr->ai_next) {
+                       auto s = socket::address_to_string(addr->ai_addr, 
addr->ai_addrlen, false);
+                       if (!s.empty()) {
+                               addrs.emplace_back(std::move(s));
+                       }
+               }
+       }
+
+       freeaddrinfo(addressList);
+
+       handler_->send_event<hostname_lookup_event>(parent_, res, 
std::move(addrs));
+       host_.clear();
+}
+
+hostname_lookup::hostname_lookup(thread_pool& pool, event_handler& evt_handler)
+       : impl_(new impl(this, pool, &evt_handler))
+{
+}
+
+bool hostname_lookup::lookup(native_string const& host, address_type family)
+{
+       if (host.empty()) {
+               return false;
+       }
+
+       scoped_lock l(impl_->mtx_);
+       if (!impl_->host_.empty()) {
+               return false;
+       }
+
+       if (!impl_->spawn()) {
+               return false;
+       }
+
+       impl_->host_ = fz::to_string(host);
+       impl_->family_ = family;
+       impl_->cond_.signal(l);
+       return true;
+}
+
+namespace {
+void filter_hostname_events(fz::hostname_lookup* lookup, fz::event_handler* 
handler)
+{
+       auto filter = [&](event_loop::Events::value_type const& ev) -> bool {
+               if (ev.first != handler) {
+                       return false;
+               }
+               else if (ev.second->derived_type() != 
hostname_lookup_event::type()) {
+                       return false;
+               }
+               return std::get<0>(static_cast<hostname_lookup_event 
const&>(*ev.second).v_) == lookup;
+       };
+
+       handler->event_loop_.filter_events(filter);
+}
+}
+
+hostname_lookup::~hostname_lookup()
+{
+       scoped_lock l(impl_->mtx_);
+       if (!impl_->thread_) {
+               l.unlock();
+               delete impl_;
+       }
+       else {
+               filter_hostname_events(this, impl_->handler_);
+
+               impl_->thread_.detach();
+               impl_->cond_.signal(l);
+       }
+}
+
+void hostname_lookup::reset()
+{
+       scoped_lock l(impl_->mtx_);
+       if (!impl_->thread_) {
+               return;
+       }
+
+       filter_hostname_events(this, impl_->handler_);
+       if (!impl_->host_.empty()) {
+               impl_->thread_.detach();
+               impl_->cond_.signal(l);
+               impl_ = new impl(this, impl_->pool_, impl_->handler_);
+       }
+}
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/encode.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/encode.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/encode.hpp 2020-10-13 
14:16:48.000000000 +0200
+++ new/libfilezilla-0.28.0/lib/libfilezilla/encode.hpp 2021-05-04 
10:46:55.000000000 +0200
@@ -114,6 +114,14 @@
 std::string FZ_PUBLIC_SYMBOL base64_encode(std::vector<uint8_t> const& in, 
base64_type type = base64_type::standard, bool pad = true);
 
 /**
+ * \brief base64-encodes input and appends it to result.
+ *
+ * Multiple inputs concatenated this way cannot be passed to a single 
base64_decode. The parts need to be
+ * individually decoded.
+ */
+void FZ_PUBLIC_SYMBOL base64_encode_append(std::string& result, 
std::string_view const& in, base64_type type = base64_type::standard, bool pad 
= true);
+
+/**
  * \brief Decodes base64, ignores whitespace. Returns empty string on invalid 
input.
  *
  * Padding is optional, alphabet is auto-detected.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/format.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/format.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/format.hpp 2020-10-13 
14:16:48.000000000 +0200
+++ new/libfilezilla-0.28.0/lib/libfilezilla/format.hpp 2021-05-04 
10:46:55.000000000 +0200
@@ -7,7 +7,7 @@
 #include <cstdlib>
 #include <type_traits>
 
-#ifdef FORMAT_DEBUG
+#ifdef LFZ_FORMAT_DEBUG
 #include <assert.h>
 #define format_assert(pred) assert((pred))
 #else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/glue/wxinvoker.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/glue/wxinvoker.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/glue/wxinvoker.hpp 2021-03-04 
14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla/glue/wxinvoker.hpp 2021-05-04 
10:46:55.000000000 +0200
@@ -11,19 +11,25 @@
 
 namespace fz {
 
-/// \brief Alternative version of fz::invoke that accepts wxEvtHandler
-template<typename F>
-auto make_invoker(wxEvtHandler& handler, F && f)
+/// \private
+template<typename... Args>
+std::function<void(Args...)> do_make_invoker(wxEvtHandler& handler, 
std::function<void(Args...)> && f)
 {
-       return [&handler, cf = std::forward<F>(f)](auto &&... args) mutable -> 
decltype(f(std::forward<decltype(args)>(args)...), void()) 
-       {
-               auto cb = [cf = std::move(cf), targs = 
std::make_tuple(std::forward<decltype(args)>(args)...)] {
+       return [&handler, cf = f](Args&&... args) mutable {
+               auto cb = [cf, targs = 
std::make_tuple(std::forward<Args>(args)...)] {
                        std::apply(cf, targs);
                };
                handler.CallAfter(cb);
        };
 }
 
+/// \brief Alternative version of fz::invoke that accepts wxEvtHandler
+template<typename F>
+auto make_invoker(wxEvtHandler& handler, F && f)
+{
+       return do_make_invoker(handler, 
decltype(get_func_type(&F::operator()))(std::forward<F>(f)));
+}
+
 /// \brief Returns an invoker factory utilizing the event system of of wx.
 inline invoker_factory get_invoker_factory(wxEvtHandler& handler)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/hostname_lookup.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/hostname_lookup.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/hostname_lookup.hpp        
1970-01-01 01:00:00.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla/hostname_lookup.hpp        
2021-05-04 10:46:55.000000000 +0200
@@ -0,0 +1,46 @@
+#ifndef LIBFILEZILLA_HOSTNAME_LOOKUP_HEADER
+#define LIBFILEZILLA_HOSTNAME_LOOKUP_HEADER
+
+/** \file
+ * \brief Header for the \ref fz::hostname_lookup class
+ */
+
+#include "libfilezilla.hpp"
+#include "iputils.hpp"
+#include "event_handler.hpp"
+
+namespace fz {
+class FZ_PUBLIC_SYMBOL hostname_lookup
+{
+public:
+       hostname_lookup(thread_pool& pool, event_handler& evt_handler);
+       ~hostname_lookup();
+
+       hostname_lookup(hostname_lookup const&) = delete;
+       hostname_lookup& operator=(hostname_lookup const&) = delete;
+
+       /**
+        * \brief Looks up the passed host
+        *
+        * If family is unknown, looks up both IPv4 and IPv6 addresses, if the 
operating system
+        * is configured to resolve IPv6 addresses.
+        *
+        * If the function returns true, wait for the \ref 
hostname_lookup_event before you can call it again.
+        */
+       bool lookup(native_string const& host, address_type family = 
address_type::unknown);
+
+       void reset();
+
+private:
+       class impl;
+       impl* impl_{};
+};
+
+/// \private
+struct hostname_lookup_event_type {};
+
+/// Results of hostname_lookup. On success, second argument is zero, otherwise 
an error code.
+typedef simple_event<hostname_lookup_event_type, hostname_lookup*, int, 
std::vector<std::string>> hostname_lookup_event;
+}
+
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/invoker.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/invoker.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/invoker.hpp        2021-03-04 
14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla/invoker.hpp        2021-05-04 
10:46:55.000000000 +0200
@@ -25,6 +25,24 @@
        virtual void operator()(event_base const& ev) override;
 };
 
+/// \private
+template<typename... Args>
+std::function<void(Args...)> do_make_invoker(event_loop& loop, 
std::function<void(Args...)> && f)
+{
+       return [handler = thread_invoker(loop), f](Args&&... args) mutable {
+               auto cb = [f, targs = 
std::make_tuple(std::forward<Args>(args)...)] {
+                       std::apply(f, targs);
+               };
+               handler.send_event<invoker_event>(std::move(cb));
+       };
+}
+
+// libc++ as shipped with Xcode does not have deduction guides for lambda -> 
std::function
+// Help it along a bit.
+/// \private
+template<typename Ret, typename F, typename ... Args>
+constexpr std::function<Ret(Args...)> get_func_type(Ret(F::*)(Args...) const);
+
 /**
  * \brief Wraps the passed function, so that it is always invoked in the 
context of the loop.
  *
@@ -35,19 +53,12 @@
 template<typename F>
 auto make_invoker(event_loop& loop, F && f)
 {
-       return [handler = thread_invoker(loop), cf = std::forward<F>(f)](auto 
&&... args) mutable -> decltype(f(std::forward<decltype(args)>(args)...), 
void())
-       {
-               auto cb = [cf = std::move(cf), targs = 
std::make_tuple(std::forward<decltype(args)>(args)...)] {
-                       std::apply(cf, targs);
-               };
-               handler.send_event<invoker_event>(std::move(cb));
-       };
+       return do_make_invoker(loop, 
decltype(get_func_type(&F::operator()))(std::forward<F>(f)));
 }
-
 template<typename F>
 auto make_invoker(event_handler& h, F && f)
 {
-       return make_invoker(h.event_loop_, std::forward<F>(f));
+       return do_make_invoker(h.event_loop_, 
decltype(get_func_type(&F::operator()))(std::forward<F>(f)));
 }
 
 
@@ -61,6 +72,18 @@
  */
 invoker_factory FZ_PUBLIC_SYMBOL get_invoker_factory(event_loop& loop);
 
+/// \private
+template<typename... Args>
+std::function<void(Args...)> do_make_invoker(invoker_factory const& inv, 
std::function<void(Args...)> && f)
+{
+       return [inv, f](Args&&... args) mutable {
+               auto cb = [f, targs = 
std::make_tuple(std::forward<Args>(args)...)] {
+                       std::apply(f, targs);
+               };
+               inv(cb);
+       };
+}
+
 /**
  * \brief Creates an invoker using the given factory
  *
@@ -71,13 +94,7 @@
 template<typename F>
 auto make_invoker(invoker_factory const& inv, F && f)
 {
-       return [inv, cf = std::forward<F>(f)](auto &&... args) mutable -> 
decltype(f(std::forward<decltype(args)>(args)...), void())
-       {
-               auto cb = [cf = std::move(cf), targs = 
std::make_tuple(std::forward<decltype(args)>(args)...)] {
-                       std::apply(cf, targs);
-               };
-               inv(cb);
-       };
+       return do_make_invoker(inv, 
decltype(get_func_type(&F::operator()))(std::forward<F>(f)));
 }
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/rate_limited_layer.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/rate_limited_layer.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/rate_limited_layer.hpp     
2021-03-04 14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla/rate_limited_layer.hpp     
2021-05-04 10:46:55.000000000 +0200
@@ -24,18 +24,6 @@
        virtual int read(void* buffer, unsigned int size, int& error) override;
        virtual int write(void const* buffer, unsigned int size, int& error) 
override;
 
-       virtual socket_state get_state() const override {
-               return next_layer_.get_state();
-       }
-
-       virtual int connect(native_string const& host, unsigned int port, 
address_type family = address_type::unknown) override {
-               return next_layer_.connect(host, port, family);
-       }
-
-       virtual int shutdown() override {
-               return next_layer_.shutdown();
-       }
-
        virtual void set_event_handler(event_handler* handler, 
socket_event_flag retrigger_block = socket_event_flag{}) override;
 
 protected:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/socket.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/socket.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/socket.hpp 2021-03-04 
14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla/socket.hpp 2021-05-04 
10:46:55.000000000 +0200
@@ -94,7 +94,7 @@
        }
 
 protected:
-       socket_event_source() = default;
+       socket_event_source() = delete;
        explicit socket_event_source(socket_event_source* root)
                : root_(root)
        {}
@@ -364,6 +364,44 @@
        virtual int read(void* buffer, unsigned int size, int& error) = 0;
        virtual int write(void const* buffer, unsigned int size, int& error) = 
0;
 
+       template<typename T, std::enable_if_t<std::is_signed_v<T>, int> = 0>
+       int read(void* buffer, T size, int& error)
+       {
+               if (size < 0) {
+                       error = EINVAL;
+                       return -1;
+               }
+
+               return read(buffer, static_cast<unsigned int>(size), error);
+       }
+       template<typename T, std::enable_if_t<std::is_unsigned_v<T> && 
(sizeof(T) > sizeof(unsigned int)), int> = 0>
+       int read(void* buffer, T size, int& error)
+       {
+               if (size > std::numeric_limits<unsigned int>::max()) {
+                       size = std::numeric_limits<unsigned int>::max();
+               }
+               return read(buffer, static_cast<unsigned int>(size), error);
+       }
+
+       template<typename T, std::enable_if_t<std::is_signed_v<T>, int> = 0>
+       int write(void const* buffer, T size, int& error)
+       {
+               if (size < 0) {
+                       error = EINVAL;
+                       return -1;
+               }
+
+               return write(buffer, 
static_cast<std::make_unsigned_t<T>>(size), error);
+       }
+       template<typename T, std::enable_if_t<std::is_unsigned_v<T> && 
(sizeof(T) > sizeof(unsigned int)), int> = 0>
+       int write(void const* buffer, T size, int& error)
+       {
+               if (size > std::numeric_limits<unsigned int>::max()) {
+                       size = std::numeric_limits<unsigned int>::max();
+               }
+               return write(buffer, static_cast<unsigned int>(size), error);
+       }
+       
        virtual void set_event_handler(event_handler* pEvtHandler, 
fz::socket_event_flag retrigger_block = fz::socket_event_flag{}) = 0;
 
        virtual native_string peer_host() const = 0;
@@ -616,6 +654,18 @@
         */
        virtual int shutdown_read() override;
 
+       virtual int connect(native_string const& host, unsigned int port, 
address_type family = address_type::unknown) override {
+               return next_layer_.connect(host, port, family);
+       }
+
+       virtual int shutdown() override {
+               return next_layer_.shutdown();
+       }
+
+       virtual socket_state get_state() const override {
+               return next_layer_.get_state();
+       }
+
 protected:
        /**
         * Call in a derived classes handler for fz::socket_event. Results in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/time.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/time.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/time.hpp   2020-10-13 
14:16:49.000000000 +0200
+++ new/libfilezilla-0.28.0/lib/libfilezilla/time.hpp   2021-05-04 
10:46:55.000000000 +0200
@@ -248,6 +248,20 @@
        bool set_rfc822(std::string_view const& str);
        bool set_rfc822(std::wstring_view const& str);
 
+       /**
+        * Parses a date in the format specified in RFC 3339
+        *
+        * On errors, object gets clears
+        *
+        * \return whether setting the timestamp succeeded. On failure object 
gets cleared.
+        *
+        * \par Examples:
+        * 1985-04-12T23:20:50.52Z
+        * 1996-12-19T16:39:57-08:00
+        */
+       bool set_rfc3339(std::string_view const& str);
+       bool set_rfc3339(std::wstring_view const& str);
+
 private:
        int FZ_PRIVATE_SYMBOL compare_slow(datetime const& op) const;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/tls_info.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/tls_info.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/tls_info.hpp       2020-07-07 
14:06:31.000000000 +0200
+++ new/libfilezilla-0.28.0/lib/libfilezilla/tls_info.hpp       2021-05-04 
10:46:55.000000000 +0200
@@ -8,6 +8,8 @@
 #include "time.hpp"
 
 namespace fz {
+class logger_interface;
+
 /**
  * \brief Represents all relevant information of a X.509 certificate as used 
by TLS.
  */
@@ -120,6 +122,16 @@
 };
 
 /**
+ * \brief Gets the certificate information for the certificates in the file.
+ *
+ * If the sort flag is not set, certificates are returned in input order.
+ * If the sort flag is set, a chain is built, with certificate i signed by i+1.
+ * If building the chain fails, nothing is returned.
+ */
+std::vector<x509_certificate> FZ_PUBLIC_SYMBOL 
load_certificates_file(native_string const& certsfile, bool pem, bool sort, 
logger_interface * logger = nullptr);
+std::vector<x509_certificate> FZ_PUBLIC_SYMBOL 
load_certificates(std::string_view const& certdata, bool pem, bool sort, 
logger_interface * logger = nullptr);
+
+/**
  * \brief Information about a TLS session
  *
  * Includes information about the used ciphers and details on the certificates
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla/tls_layer.hpp 
new/libfilezilla-0.28.0/lib/libfilezilla/tls_layer.hpp
--- old/libfilezilla-0.27.1/lib/libfilezilla/tls_layer.hpp      2021-03-04 
14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla/tls_layer.hpp      2021-05-04 
10:46:55.000000000 +0200
@@ -133,7 +133,7 @@
         *
         * If the pem flag is set, the input is assumed to be in PEM, otherwise 
DER.
         */
-       bool set_certificate(std::string const& key, std::string const& certs, 
native_string const& password, bool pem = true);
+       bool set_certificate(std::string_view const& key, std::string_view 
const& certs, native_string const& password, bool pem = true);
 
        /// Returns the version of the loaded GnuTLS library, may be different 
than the version used at compile-time.
        static std::string get_gnutls_version();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/libfilezilla.vcxproj 
new/libfilezilla-0.28.0/lib/libfilezilla.vcxproj
--- old/libfilezilla-0.27.1/lib/libfilezilla.vcxproj    2020-12-17 
14:46:00.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/libfilezilla.vcxproj    2021-05-04 
10:46:55.000000000 +0200
@@ -76,6 +76,9 @@
   <PropertyGroup>
     <IntDir>$(OutDir)</IntDir>
   </PropertyGroup>
+  <PropertyGroup 
Condition="'$(Configuration)|$(Platform)'=='static_debug|x64'">
+    <LibraryPath>$(gnutls_lib);$(nettle_lib);$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
   <ItemDefinitionGroup>
     <ClCompile>
       <LanguageStandard>stdcpp17</LanguageStandard>
@@ -201,6 +204,7 @@
     <ClCompile Include="event_loop.cpp" />
     <ClCompile Include="file.cpp" />
     <ClCompile Include="hash.cpp" />
+    <ClCompile Include="hostname_lookup.cpp" />
     <ClCompile Include="invoker.cpp" />
     <ClCompile Include="iputils.cpp" />
     <ClCompile Include="local_filesys.cpp" />
@@ -237,6 +241,7 @@
     <ClInclude Include="libfilezilla\file.hpp" />
     <ClInclude Include="libfilezilla\format.hpp" />
     <ClInclude Include="libfilezilla\hash.hpp" />
+    <ClInclude Include="libfilezilla\hostname_lookup.hpp" />
     <ClInclude Include="libfilezilla\invoker.hpp" />
     <ClInclude Include="libfilezilla\iputils.hpp" />
     <ClInclude Include="libfilezilla\libfilezilla.hpp" />
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/rate_limited_layer.cpp 
new/libfilezilla-0.28.0/lib/rate_limited_layer.cpp
--- old/libfilezilla-0.27.1/lib/rate_limited_layer.cpp  2021-03-04 
14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/rate_limited_layer.cpp  2021-05-04 
10:46:55.000000000 +0200
@@ -142,7 +142,7 @@
 
        rate::type max_{};
 
-       std::atomic<bool> waiting_[2];
+       std::atomic<bool> waiting_[2]{};
 };
 
 compound_rate_limited_layer::compound_rate_limited_layer(event_handler* 
handler, socket_interface& next_layer)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/rate_limiter.cpp 
new/libfilezilla-0.28.0/lib/rate_limiter.cpp
--- old/libfilezilla-0.27.1/lib/rate_limiter.cpp        2021-03-04 
14:27:18.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/rate_limiter.cpp        2021-05-04 
10:46:55.000000000 +0200
@@ -648,7 +648,7 @@
        return ret;
 }
 
-bool bucket::waiting(scoped_lock & l, direction::type d)
+bool bucket::waiting(scoped_lock &, direction::type d)
 {
        if (d != direction::inbound && d != direction::outbound) {
                return false;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/socket.cpp new/libfilezilla-0.28.0/lib/socket.cpp
--- old/libfilezilla-0.27.1/lib/socket.cpp      2021-03-04 14:27:18.000000000 
+0100
+++ new/libfilezilla-0.28.0/lib/socket.cpp      2021-05-04 10:46:55.000000000 
+0200
@@ -185,9 +185,8 @@
 }
 #endif
 
-namespace {
 #ifdef FZ_WINDOWS
-static int convert_msw_error_code(int error)
+int convert_msw_error_code(int error)
 {
        // Takes an MSW socket error and converts it into an equivalent POSIX 
error code.
        switch (error)
@@ -236,7 +235,10 @@
                return error;
        }
 }
+#endif
 
+namespace {
+#ifdef FZ_WINDOWS
 int last_socket_error()
 {
        return convert_msw_error_code(WSAGetLastError());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/time.cpp new/libfilezilla-0.28.0/lib/time.cpp
--- old/libfilezilla-0.27.1/lib/time.cpp        2020-07-07 14:06:31.000000000 
+0200
+++ new/libfilezilla-0.28.0/lib/time.cpp        2021-05-04 10:46:55.000000000 
+0200
@@ -879,4 +879,98 @@
        return do_set_rfc822(*this, str);
 }
 
+namespace {
+template<typename String>
+bool do_set_rfc3339(fz::datetime& dt, String str)
+{
+       if (str.size() < 19) {
+               dt.clear();
+               return false;
+       }
+
+       auto separator_pos = str.find_first_of(fzS(typename String::value_type, 
"tT ")); // Including space, there is a lowercase 'may' in section 5.6 of the 
RFC
+       if (separator_pos == String::npos) {
+               dt.clear();
+               return false;
+       }
+
+       auto date_part = str.substr(0, separator_pos);
+       auto const date_tokens = fz::strtok_view(date_part, fzS(typename 
String::value_type, "-"));
+
+       auto offset_pos = str.find_first_of(fzS(typename String::value_type, 
"+-Zz"), separator_pos);
+
+       String time_part;
+       if (offset_pos == String::npos) {
+               // Allow the offset part to be missing
+               time_part = str.substr(separator_pos + 1);
+       }
+       else {
+               time_part = str.substr(separator_pos + 1, offset_pos - 
separator_pos - 1);
+       }
+
+       auto const time_tokens = fz::strtok_view(time_part, fzS(typename 
String::value_type, ":."));
+       if (date_tokens.size() == 3 && (time_tokens.size() == 3 || 
time_tokens.size() == 4)) {
+               int year = fz::to_integral<int>(date_tokens[0]);
+               if (year < 1000) {
+                       if (year < 1000) {
+                               year += 1900;
+                       }
+               }
+               int month = fz::to_integral<int>(date_tokens[1]);
+               int day = fz::to_integral<int>(date_tokens[2]);
+
+               int hour = fz::to_integral<int>(time_tokens[0]);
+               int minute = fz::to_integral<int>(time_tokens[1]);
+               int second = fz::to_integral<int>(time_tokens[2]);
+
+               bool set{};
+               if (time_tokens.size() == 4) {
+                       // Convert fraction, .82 is 820ms
+                       int ms = fz::to_integral<int>(time_tokens[3].substr(0, 
3));
+                       if (time_tokens[3].size() == 1) {
+                               ms *= 100;
+                       }
+                       else if (time_tokens[3].size() == 2) {
+                               ms *= 10;
+                       }
+                       set = dt.set(fz::datetime::utc, year, month, day, hour, 
minute, second, ms);
+               }
+               else {
+                       set = dt.set(fz::datetime::utc, year, month, day, hour, 
minute, second);
+               }
+
+               if (set && offset_pos != String::npos && str[offset_pos] != 
'Z') {
+                       auto const offset_tokens = 
fz::strtok_view(str.substr(offset_pos + 1), ':');
+                       if (offset_tokens.size() != 2) {
+                               dt.clear();
+                               return false;
+                       }
+
+                       int minutes = fz::to_integral<int>(offset_tokens[0], 
10009) * 60 + fz::to_integral<int>(offset_tokens[1], 10000);
+                       if (minutes < 10000) {
+                               if (str[offset_pos] == '+') {
+                                       minutes = -minutes;
+                               }
+                               dt += fz::duration::from_minutes(minutes);
+                       }
+               }
+
+               return set;
+       }
+
+       dt.clear();
+       return false;
+}
+}
+
+bool datetime::set_rfc3339(std::string_view const& str)
+{
+       return do_set_rfc3339(*this, str);
+}
+
+bool datetime::set_rfc3339(std::wstring_view const& str)
+{
+       return do_set_rfc3339(*this, str);
+}
+
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/tls_info.cpp 
new/libfilezilla-0.28.0/lib/tls_info.cpp
--- old/libfilezilla-0.27.1/lib/tls_info.cpp    2020-07-07 14:06:31.000000000 
+0200
+++ new/libfilezilla-0.28.0/lib/tls_info.cpp    2021-05-04 10:46:55.000000000 
+0200
@@ -1,4 +1,5 @@
 #include "libfilezilla/tls_info.hpp"
+#include "tls_layer_impl.hpp"
 
 namespace fz {
 x509_certificate::x509_certificate(
@@ -78,4 +79,38 @@
        , hostname_mismatch_(hostname_mismatch)
 {
 }
+
+std::vector<x509_certificate> load_certificates_file(native_string const& 
certsfile, bool pem, bool sort, logger_interface * logger)
+{
+       std::string certdata = read_certificates_file(certsfile, logger);
+       if (certdata.empty()) {
+               return {};
+       }
+
+       return load_certificates(certdata, pem, sort, logger);
+}
+
+std::vector<x509_certificate> load_certificates(std::string_view const& 
certdata, bool pem, bool sort, logger_interface * logger)
+{
+       cert_list_holder certs;
+       if (tls_layer_impl::load_certificates(certdata, pem, certs.certs, 
certs.certs_size, sort) != GNUTLS_E_SUCCESS) {
+               return {};
+       }
+
+       std::vector<x509_certificate> certificates;
+       certificates.reserve(certs.certs_size);
+       for (unsigned int i = 0; i < certs.certs_size; ++i) {
+               x509_certificate cert;
+               if (tls_layer_impl::extract_cert(certs.certs[i], cert, i + 1 == 
certs.certs_size, logger)) {
+                       certificates.emplace_back(std::move(cert));
+               }
+               else {
+                       certificates.clear();
+                       break;
+               }
+       }
+
+       return certificates;
+}
+
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/tls_layer.cpp 
new/libfilezilla-0.28.0/lib/tls_layer.cpp
--- old/libfilezilla-0.27.1/lib/tls_layer.cpp   2021-03-04 14:27:18.000000000 
+0100
+++ new/libfilezilla-0.28.0/lib/tls_layer.cpp   2021-05-04 10:46:55.000000000 
+0200
@@ -96,7 +96,7 @@
        return impl_->set_certificate_file(keyfile, certsfile, password, pem);
 }
 
-bool tls_layer::set_certificate(std::string const& key, std::string const& 
certs, native_string const& password, bool pem)
+bool tls_layer::set_certificate(std::string_view const& key, std::string_view 
const& certs, native_string const& password, bool pem)
 {
        return impl_->set_certificate(key, certs, password, pem);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/tls_layer_impl.cpp 
new/libfilezilla-0.28.0/lib/tls_layer_impl.cpp
--- old/libfilezilla-0.27.1/lib/tls_layer_impl.cpp      2021-03-15 
09:01:00.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/tls_layer_impl.cpp      2021-05-04 
10:46:55.000000000 +0200
@@ -15,6 +15,8 @@
 
 #include <string.h>
 
+using namespace std::literals;
+
 #if DEBUG_SOCKETEVENTS
 #include <assert.h>
 
@@ -129,27 +131,18 @@
        return tls_layerCallbacks::handshake_hook_func(session, htype, post, 
incoming);
 }
 
-struct cert_list_holder final
+std::string to_string(gnutls_datum_t const& d)
 {
-       cert_list_holder() = default;
-       ~cert_list_holder() {
-               for (unsigned int i = 0; i < certs_size; ++i) {
-                       gnutls_x509_crt_deinit(certs[i]);
-               }
-               gnutls_free(certs);
+       if (d.data && d.size) {
+               return std::string(d.data, d.data + d.size);
        }
+       return {};
+}
 
-       cert_list_holder(cert_list_holder const&) = delete;
-       cert_list_holder& operator=(cert_list_holder const&) = delete;
-
-       gnutls_x509_crt_t * certs{};
-       unsigned int certs_size{};
-};
-
-std::string to_string(gnutls_datum_t const& d)
+std::string_view to_view(gnutls_datum_t const& d)
 {
        if (d.data && d.size) {
-               return std::string(d.data, d.data + d.size);
+               return std::string_view(reinterpret_cast<char const*>(d.data), 
d.size);
        }
        return {};
 }
@@ -247,6 +240,34 @@
        return true;
 }
 
+std::string read_certificates_file(native_string const& certsfile, 
logger_interface * logger)
+{
+       file cf(certsfile, file::reading, file::existing);
+       if (!cf.opened()) {
+               if (logger) {
+                       logger->log(logmsg::error, fztranslate("Could not open 
certificate file."));
+               }
+               return {};
+       }
+       int64_t const cs = cf.size();
+       if (cs < 0 || cs > 1024 * 1024) {
+               if (logger) {
+                       logger->log(logmsg::error, fztranslate("Certificate 
file too big."));
+               }
+               return {};
+       }
+       std::string c;
+       c.resize(cs);
+       auto read = cf.read(c.data(), cs);
+       if (read != cs) {
+               if (logger) {
+                       logger->log(logmsg::error, fztranslate("Could not read 
certificate file."));
+               }
+               return {};
+       }
+       return c;
+}
+
 bool tls_layer_impl::set_certificate_file(native_string const& keyfile, 
native_string const& certsfile, native_string const& password, bool pem)
 {
        // Load the files ourselves instead of calling 
gnutls_certificate_set_x509_key_file2
@@ -270,29 +291,15 @@
                return false;
        }
 
-
-       file cf(certsfile, file::reading, file::existing);
-       if (!cf.opened()) {
-               logger_.log(logmsg::error, fztranslate("Could not open 
certificate file."));
-               return false;
-       }
-       int64_t const cs = cf.size();
-       if (cs < 0 || cs > 1024 * 1024) {
-               logger_.log(logmsg::error, fztranslate("Certificate file too 
big."));
-               return false;
-       }
-       std::string c;
-       c.resize(cs);
-       read = cf.read(c.data(), cs);
-       if (read != cs) {
-               logger_.log(logmsg::error, fztranslate("Could not read 
certificate file."));
+       std::string c = read_certificates_file(certsfile, &logger_);
+       if (c.empty()) {
                return false;
        }
 
        return set_certificate(k, c, password, pem);
 }
 
-bool tls_layer_impl::set_certificate(std::string const& key, std::string 
const& certs, native_string const& password, bool pem)
+bool tls_layer_impl::set_certificate(std::string_view const& key, 
std::string_view const& certs, native_string const& password, bool pem)
 {
        if (!init()) {
                return false;
@@ -320,6 +327,7 @@
 
        return true;
 }
+// Convert them all to PEM
 
 bool tls_layer_impl::init_session(bool client)
 {
@@ -1169,13 +1177,15 @@
 }
 
 
-bool tls_layer_impl::extract_cert(gnutls_x509_crt_t const& cert, 
x509_certificate& out, bool last)
+bool tls_layer_impl::extract_cert(gnutls_x509_crt_t const& cert, 
x509_certificate& out, bool last, logger_interface * logger)
 {
        datetime expiration_time(gnutls_x509_crt_get_expiration_time(cert), 
datetime::seconds);
        datetime activation_time(gnutls_x509_crt_get_activation_time(cert), 
datetime::seconds);
 
        if (!activation_time || !expiration_time || expiration_time < 
activation_time) {
-               logger_.log(logmsg::error, fztranslate("Could not extract 
validity period of certificate"));
+               if (logger) {
+                       logger->log(logmsg::error, fztranslate("Could not 
extract validity period of certificate"));
+               }
                return false;
        }
 
@@ -1211,28 +1221,38 @@
        std::string subject, issuer;
 
        datum_holder raw_subject;
-       if (!gnutls_x509_crt_get_dn3(cert, &raw_subject, 0)) {
+       res = gnutls_x509_crt_get_dn3(cert, &raw_subject, 0);
+       if (!res) {
                subject = raw_subject.to_string_view();
        }
        else {
-               log_error(res, L"gnutls_x509_crt_get_dn3");
+               if (logger) {
+                       logger->log(logmsg::debug_warning, 
"gnutls_x509_crt_get_dn3 failed with %d", res);
+               }
        }
        if (subject.empty()) {
-               logger_.log(logmsg::error, fztranslate("Could not get 
distinguished name of certificate subject, gnutls_x509_get_dn failed"));
+               if (logger) {
+                       logger->log(logmsg::error, fztranslate("Could not get 
distinguished name of certificate subject, gnutls_x509_get_dn failed"));
+               }
                return false;
        }
 
        std::vector<x509_certificate::subject_name> alt_subject_names = 
get_cert_subject_alt_names(cert);
 
        datum_holder raw_issuer;
-       if (!gnutls_x509_crt_get_issuer_dn3(cert, &raw_issuer, 0)) {
+       res = gnutls_x509_crt_get_issuer_dn3(cert, &raw_issuer, 0);
+       if (!res) {
                issuer = raw_issuer.to_string_view();
        }
        else {
-               log_error(res, L"gnutls_x509_crt_get_issuer_dn3");
+               if (logger) {
+                       logger->log(logmsg::debug_warning, 
"gnutls_x509_crt_get_issuer_dn3 failed with %d", res);
+               }
        }
        if (issuer.empty() ) {
-               logger_.log(logmsg::error, fztranslate("Could not get 
distinguished name of certificate issuer, gnutls_x509_get_issuer_dn failed"));
+               if (logger) {
+                       logger->log(logmsg::error, fztranslate("Could not get 
distinguished name of certificate issuer, gnutls_x509_get_issuer_dn failed"));
+               }
                return false;
        }
 
@@ -1253,7 +1273,9 @@
 
        datum_holder der;
        if (gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &der) != 
GNUTLS_E_SUCCESS || !der.data || !der.size) {
-               logger_.log(logmsg::error, L"gnutls_x509_crt_get_issuer_dn");
+               if (logger) {
+                       logger->log(logmsg::error, 
L"gnutls_x509_crt_get_issuer_dn");
+               }
                return false;
        }
        std::vector<uint8_t> data(der.data, der.data + der.size);
@@ -1389,6 +1411,29 @@
        return algorithmWarnings;
 }
 
+int tls_layer_impl::load_certificates(std::string_view const& in, bool pem, 
gnutls_x509_crt_t *& certs, unsigned int & certs_size, bool & sort)
+{
+       gnutls_datum_t dpem;
+       dpem.data = reinterpret_cast<unsigned char*>(const_cast<char 
*>(in.data()));
+       dpem.size = in.size();
+       unsigned int flags{};
+       if (sort) {
+               flags |= GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED;
+       }
+
+       int res = gnutls_x509_crt_list_import2(&certs, &certs_size, &dpem, pem 
? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER, flags);
+       if (res == GNUTLS_E_CERTIFICATE_LIST_UNSORTED) {
+               sort = false;
+               flags |= GNUTLS_X509_CRT_LIST_SORT;
+               res = gnutls_x509_crt_list_import2(&certs, &certs_size, &dpem, 
pem ? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER, flags);
+       }
+
+       if (res != GNUTLS_E_SUCCESS) {
+               certs = nullptr;
+               certs_size = 0;
+       }
+       return res;
+}
 
 bool tls_layer_impl::get_sorted_peer_certificates(gnutls_x509_crt_t *& certs, 
unsigned int & certs_size)
 {
@@ -1404,52 +1449,35 @@
        }
 
        // Convert them all to PEM
-       gnutls_datum_t *pem_cert_list = new gnutls_datum_t[cert_list_size];
-       for (unsigned i = 0; i < cert_list_size; ++i) {
-               if (gnutls_pem_base64_encode2("CERTIFICATE", cert_list + i, 
pem_cert_list + i) != 0) {
-                       for (unsigned int j = 0; j < i; ++j) {
-                               gnutls_free(pem_cert_list[j].data);
-                       }
-                       delete [] pem_cert_list;
-                       logger_.log(logmsg::error, 
fztranslate("gnutls_pem_base64_encode2 failed"));
-                       return false;
-               }
-       }
+       // Avoid gnutls_pem_base64_encode2, excessive allocations.
+       auto constexpr header = "-----BEGIN CERTIFICATE-----\r\n"sv;
+       auto constexpr footer = "\r\n-----END CERTIFICATE-----\r\n"sv;
 
-       // Concatenate them
-       gnutls_datum_t concated_certs{};
+       size_t cap = cert_list_size * header.size() + footer.size();
        for (unsigned i = 0; i < cert_list_size; ++i) {
-               concated_certs.size += pem_cert_list[i].size;
-       }
-       concated_certs.data = new unsigned char[concated_certs.size];
-       concated_certs.size = 0;
-       for (unsigned i = 0; i < cert_list_size; ++i) {
-               memcpy(concated_certs.data + concated_certs.size, 
pem_cert_list[i].data, pem_cert_list[i].size);
-               concated_certs.size += pem_cert_list[i].size;
+               cap += ((cert_list[i].size + 2) / 3) * 4;
        }
 
+       std::string pem;
+       pem.reserve(cap);
+
        for (unsigned i = 0; i < cert_list_size; ++i) {
-               gnutls_free(pem_cert_list[i].data);
+               pem += header;
+               base64_encode_append(pem, to_view(cert_list[i]), 
base64_type::standard, true);
+               pem += footer;
        }
-       delete[] pem_cert_list;
 
        // And now import the certificates
-       int res = gnutls_x509_crt_list_import2(&certs, &certs_size, 
&concated_certs, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED);
+       bool sort = true;
+       int res = load_certificates(pem, true, certs, certs_size, sort);
        if (res == GNUTLS_E_CERTIFICATE_LIST_UNSORTED) {
-               logger_.log(logmsg::error, fztranslate("Server sent unsorted 
certificate chain in violation of the TLS specifications"));
-               res = gnutls_x509_crt_list_import2(&certs, &certs_size, 
&concated_certs, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_SORT);
-       }
-
-       delete[] concated_certs.data;
-
-       if (res != GNUTLS_E_SUCCESS) {
-               certs = nullptr;
-               certs_size = 0;
                logger_.log(logmsg::error, fztranslate("Could not sort peer 
certificates"));
-               return false;
+       }
+       else if (!sort) {
+               logger_.log(logmsg::error, fztranslate("Server sent unsorted 
certificate chain in violation of the TLS specifications"));
        }
 
-       return true;
+       return res == GNUTLS_E_SUCCESS;
 }
 
 void tls_layer_impl::log_verification_error(int status)
@@ -1661,8 +1689,8 @@
                certificates.reserve(certs.certs_size);
                for (unsigned int i = 0; i < certs.certs_size; ++i) {
                        x509_certificate cert;
-                       if (extract_cert(certs.certs[i], cert, i + 1 == 
certs.certs_size)) {
-                               certificates.push_back(cert);
+                       if (extract_cert(certs.certs[i], cert, i + 1 == 
certs.certs_size, &logger_)) {
+                               certificates.emplace_back(std::move(cert));
                        }
                        else {
                                failure(0, true);
@@ -1690,7 +1718,7 @@
                                        }
 
                                        x509_certificate out;
-                                       if (!extract_cert(issuer, out, true)) {
+                                       if (!extract_cert(issuer, out, true, 
&logger_)) {
                                                failure(0, true);
                                                return ECONNABORTED;
                                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/tls_layer_impl.hpp 
new/libfilezilla-0.28.0/lib/tls_layer_impl.hpp
--- old/libfilezilla-0.27.1/lib/tls_layer_impl.hpp      2021-03-15 
09:01:00.000000000 +0100
+++ new/libfilezilla-0.28.0/lib/tls_layer_impl.hpp      2021-05-04 
10:46:55.000000000 +0200
@@ -6,6 +6,7 @@
 #endif
 
 #include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
 
 #include "libfilezilla/buffer.hpp"
 #include "libfilezilla/logger.hpp"
@@ -15,9 +16,23 @@
 namespace fz {
 class tls_system_trust_store;
 class logger_interface;
-namespace {
-struct cert_list_holder;
-}
+
+struct cert_list_holder final
+{
+       cert_list_holder() = default;
+       ~cert_list_holder() {
+               for (unsigned int i = 0; i < certs_size; ++i) {
+                       gnutls_x509_crt_deinit(certs[i]);
+               }
+               gnutls_free(certs);
+       }
+
+       cert_list_holder(cert_list_holder const&) = delete;
+       cert_list_holder& operator=(cert_list_holder const&) = delete;
+
+       gnutls_x509_crt_t * certs{};
+       unsigned int certs_size{};
+};
 
 class tls_layer;
 class tls_layer_impl final
@@ -58,7 +73,7 @@
 
        bool set_certificate_file(native_string const& keyfile, native_string 
const& certsfile, native_string const& password, bool pem);
 
-       bool set_certificate(std::string const& key, std::string const& certs, 
native_string const& password, bool pem);
+       bool set_certificate(std::string_view const& key, std::string_view 
const& certs, native_string const& password, bool pem);
 
        static std::string get_gnutls_version();
 
@@ -74,6 +89,9 @@
        std::string get_alpn() const;
        native_string get_hostname() const;
 
+       static int load_certificates(std::string_view const& in, bool pem, 
gnutls_x509_crt_t *& certs, unsigned int & certs_size, bool & sort);
+       static bool extract_cert(gnutls_x509_crt_t const& cert, 
x509_certificate& out, bool last, logger_interface * logger);
+
 private:
        bool init();
        void deinit();
@@ -106,8 +124,7 @@
 
        bool get_sorted_peer_certificates(gnutls_x509_crt_t *& certs, unsigned 
int & certs_size);
 
-       bool extract_cert(gnutls_x509_crt_t const& cert, x509_certificate& out, 
bool last);
-       std::vector<x509_certificate::subject_name> 
get_cert_subject_alt_names(gnutls_x509_crt_t cert);
+       static std::vector<x509_certificate::subject_name> 
get_cert_subject_alt_names(gnutls_x509_crt_t cert);
 
        void log_verification_error(int status);
 
@@ -175,6 +192,9 @@
        bool debug_can_write_{};
 #endif
 };
+
+std::string read_certificates_file(native_string const& certsfile, 
logger_interface * logger);
+
 }
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/lib/tls_system_trust_store.cpp 
new/libfilezilla-0.28.0/lib/tls_system_trust_store.cpp
--- old/libfilezilla-0.27.1/lib/tls_system_trust_store.cpp      2020-07-07 
14:06:31.000000000 +0200
+++ new/libfilezilla-0.28.0/lib/tls_system_trust_store.cpp      2021-05-04 
10:46:55.000000000 +0200
@@ -25,7 +25,9 @@
 {
        task_.join();
 
-       gnutls_certificate_free_credentials(credentials_);
+       if (credentials_) {
+               gnutls_certificate_free_credentials(credentials_);
+       }
 }
 
 std::tuple<gnutls_certificate_credentials_t, scoped_lock> 
tls_system_trust_store_impl::lease()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/libfilezilla-0.27.1/tests/time.cpp new/libfilezilla-0.28.0/tests/time.cpp
--- old/libfilezilla-0.27.1/tests/time.cpp      2020-07-07 14:06:31.000000000 
+0200
+++ new/libfilezilla-0.28.0/tests/time.cpp      2021-05-04 10:46:55.000000000 
+0200
@@ -12,6 +12,7 @@
        CPPUNIT_TEST(testPreEpoch);
        CPPUNIT_TEST(testAlternateMidnight);
        CPPUNIT_TEST(testRFC822);
+       CPPUNIT_TEST(testRFC3339);
        CPPUNIT_TEST_SUITE_END();
 
 public:
@@ -24,6 +25,7 @@
        void testAlternateMidnight();
 
        void testRFC822();
+       void testRFC3339();
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(TimeTest);
@@ -124,3 +126,18 @@
        CPPUNIT_ASSERT(t.set_rfc822(offset2));
        CPPUNIT_ASSERT(t == t1);
 }
+
+void TimeTest::testRFC3339()
+{
+       fz::datetime const t1(fz::datetime::utc, 1985, 4, 12, 23, 20, 50, 520);
+       fz::datetime const t2(fz::datetime::utc, 1996, 12, 20, 0, 39, 57);
+
+       std::string const s1 = "1985-04-12T23:20:50.52Z";
+       std::string const s2 = "1996-12-19T16:39:57-08:00";
+
+       fz::datetime t;
+       CPPUNIT_ASSERT(t.set_rfc3339(s1));
+       CPPUNIT_ASSERT(t == t1);
+       CPPUNIT_ASSERT(t.set_rfc3339(s2));
+       CPPUNIT_ASSERT(t == t2);
+}

Reply via email to