Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libfilezilla for openSUSE:Factory 
checked in at 2026-06-02 16:09:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libfilezilla (Old)
 and      /work/SRC/openSUSE:Factory/.libfilezilla.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libfilezilla"

Tue Jun  2 16:09:34 2026 rev:66 rq:1356691 version:0.56.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libfilezilla/libfilezilla.changes        
2026-05-04 14:42:07.942535469 +0200
+++ /work/SRC/openSUSE:Factory/.libfilezilla.new.1937/libfilezilla.changes      
2026-06-02 16:11:03.263623448 +0200
@@ -1,0 +2,18 @@
+Tue Jun  2 10:43:25 UTC 2026 - ecsos <[email protected]> 0.56.0
+
+- Update to 0.56.0
+  * New features:
+    - event_loop timers are now processed in a round-robin fashion
+      if there are multiple expired timers
+    - Added shared_value::use_count
+  * Bugfixes and minor changes:
+    - HTTP client: Fixed handling of 1yz responses
+    - fz::socket::connect now returns early on bind failure
+    - Minor fixes to socket classes
+- Changes from 0.55.5
+  * New features:
+    - Added %o support to fz::sprintf
+  * Bugfixes and minor changes:
+    - Support building with Nettle version 4
+
+-------------------------------------------------------------------

Old:
----
  libfilezilla-0.55.4.tar.xz

New:
----
  libfilezilla-0.56.0.tar.xz

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

Other differences:
------------------
++++++ libfilezilla.spec ++++++
--- /var/tmp/diff_new_pack.H7J662/_old  2026-06-02 16:11:04.023654499 +0200
+++ /var/tmp/diff_new_pack.H7J662/_new  2026-06-02 16:11:04.027654662 +0200
@@ -16,11 +16,11 @@
 #
 
 
-%define major          57
+%define major          58
 %define libname                %{name}%{major}
 %define develname      %{name}-devel
 Name:           libfilezilla
-Version:        0.55.4
+Version:        0.56.0
 Release:        0
 Summary:        C++ library for filezilla
 License:        GPL-2.0-or-later

++++++ libfilezilla-0.55.4.tar.xz -> libfilezilla-0.56.0.tar.xz ++++++
++++ 1721 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.55.4/NEWS new/libfilezilla-0.56.0/NEWS
--- old/libfilezilla-0.55.4/NEWS        2026-04-16 13:35:05.000000000 +0200
+++ new/libfilezilla-0.56.0/NEWS        2026-05-28 11:09:15.000000000 +0200
@@ -1,3 +1,16 @@
+0.56.0 (2026-05-27)
+
++ event_loop timers are now processed in a round-robin fashion if there are 
multiple expired timers
++ Added shared_value::use_count
+- HTTP client: Fixed handling of 1yz responses
+- fz::socket::connect now returns early on bind failure
+- Minor fixes to socket classes
+
+0.55.5 (2026-05-05)
+
++ Added %o support to fz::sprintf
+- Support building with Nettle version 4
+
 0.55.4 (2026-04-16)
 
 - Fixed a crash if a nested event handler gets deleted
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.55.4/configure.ac new/libfilezilla-0.56.0/configure.ac
--- old/libfilezilla-0.55.4/configure.ac        2026-04-16 13:35:05.000000000 
+0200
+++ new/libfilezilla-0.56.0/configure.ac        2026-05-28 11:09:15.000000000 
+0200
@@ -1,4 +1,4 @@
-AC_INIT([libfilezilla],[0.55.4],[[email protected]],[],[https://lib.filezilla-project.org/])
+AC_INIT([libfilezilla],[0.56.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=57:0:0
+LIBRARY_VERSION=58:0:0
 
 AH_TOP([
 #ifndef LIBFILEZILLA_CONFIG_HEADER
@@ -142,6 +142,8 @@
 
   CHECK_RANDOM
   CHECK_LDL_FOR_DLSYM
+
+  CHECK_ATOMIC
 fi
 
 if test "$windows" = "0" && test "$mac" = "0"; then
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.55.4/doc/Doxyfile.in new/libfilezilla-0.56.0/doc/Doxyfile.in
--- old/libfilezilla-0.55.4/doc/Doxyfile.in     2023-04-12 14:41:33.000000000 
+0200
+++ new/libfilezilla-0.56.0/doc/Doxyfile.in     2026-05-06 14:51:40.000000000 
+0200
@@ -2385,3 +2385,6 @@
 # This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_CLEANUP            = YES
+
+
+HTML_COLORSTYLE = LIGHT
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.55.4/lib/encryption.cpp 
new/libfilezilla-0.56.0/lib/encryption.cpp
--- old/libfilezilla-0.55.4/lib/encryption.cpp  2021-03-04 14:27:18.000000000 
+0100
+++ new/libfilezilla-0.56.0/lib/encryption.cpp  2026-05-05 11:13:24.000000000 
+0200
@@ -177,7 +177,11 @@
                        // Return 
ephemeral_pub.key_||ephemeral_pub.salt_||ciphertext||tag
                        memcpy(ret.data(), ephemeral_pub.key_.data(), 
public_key::key_size);
                        memcpy(ret.data() + public_key::key_size, 
ephemeral_pub.salt_.data(), public_key::salt_size);
+#if NETTLE_VERSION_MAJOR >= 4
+                       nettle_gcm_aes256_digest(&ctx, ret.data() + 
public_key::key_size + public_key::salt_size + size);
+#else
                        nettle_gcm_aes256_digest(&ctx, GCM_DIGEST_SIZE, 
ret.data() + public_key::key_size + public_key::salt_size + size);
+#endif
                }
                else {
                        std::vector<uint8_t> ctr = 
hash_accumulator(hash_algorithm::sha256) << ephemeral_pub.salt_ << 1 << secret 
<< ephemeral_pub.key_ << pub.key_ << pub.salt_;
@@ -276,7 +280,11 @@
 
                        // Last but not least, verify the tag
                        uint8_t tag[GCM_DIGEST_SIZE];
+#if NETTLE_VERSION_MAJOR >= 4
+                       nettle_gcm_aes256_digest(&ctx, tag);
+#else
                        nettle_gcm_aes256_digest(&ctx, GCM_DIGEST_SIZE, tag);
+#endif
                        if (!nettle_memeql_sec(tag, cipher + size - 
GCM_DIGEST_SIZE, GCM_DIGEST_SIZE)) {
                                ret.clear();
                        }
@@ -470,7 +478,11 @@
 
                // Return nonce||ciphertext||tag
                memcpy(ret.data(), nonce.data(), symmetric_key::salt_size);
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_gcm_aes256_digest(&ctx, ret.data() + 
symmetric_key::salt_size + size);
+#else
                nettle_gcm_aes256_digest(&ctx, GCM_DIGEST_SIZE, ret.data() + 
symmetric_key::salt_size + size);
+#endif
        }
 
        return ret;
@@ -535,7 +547,11 @@
 
                // Last but not least, verify the tag
                uint8_t tag[GCM_DIGEST_SIZE];
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_gcm_aes256_digest(&ctx, tag);
+#else
                nettle_gcm_aes256_digest(&ctx, GCM_DIGEST_SIZE, tag);
+#endif
                if (!nettle_memeql_sec(tag, cipher + size - GCM_DIGEST_SIZE, 
GCM_DIGEST_SIZE)) {
                        ret.clear();
                }
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.55.4/lib/event.cpp new/libfilezilla-0.56.0/lib/event.cpp
--- old/libfilezilla-0.55.4/lib/event.cpp       2020-07-07 14:06:30.000000000 
+0200
+++ new/libfilezilla-0.56.0/lib/event.cpp       2026-05-28 11:09:15.000000000 
+0200
@@ -2,6 +2,7 @@
 #include "libfilezilla/mutex.hpp"
 
 #include <map>
+#include <string>
 
 namespace fz {
 
@@ -10,7 +11,7 @@
        std::string name = id.name();
 
        static mutex m;
-       
+
        scoped_lock l(m);
 
        static std::map<std::string, size_t> eventTypes;
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.55.4/lib/event_loop.cpp 
new/libfilezilla-0.56.0/lib/event_loop.cpp
--- old/libfilezilla-0.55.4/lib/event_loop.cpp  2026-04-16 13:35:05.000000000 
+0200
+++ new/libfilezilla-0.56.0/lib/event_loop.cpp  2026-05-28 11:09:15.000000000 
+0200
@@ -208,7 +208,9 @@
 
                                if (timers_.empty()) {
                                        deadline_ = monotonic_clock();
+                                       do_timers_ = false;
                                }
+                               // else: Updating deadline_ is done lazily in 
process_timers
                                break;
                        }
                }
@@ -217,6 +219,11 @@
 
 timer_id event_loop::stop_add_timer(timer_id id, event_handler* handler, 
monotonic_clock const &deadline, duration const& interval)
 {
+       if (!deadline) {
+               stop_timer(id);
+               return 0;
+       }
+
        scoped_lock lock(sync_);
 
        if (id) {
@@ -250,7 +257,7 @@
        d.id_ = ++next_timer_id_; // 64bit, can this really ever overflow?
 
        if (!deadline_ || d.deadline_ < deadline_) {
-               // Our new time is the next timer to trigger
+               // Our new timer is the next timer to trigger
                deadline_ = d.deadline_;
 
                switch (mode_) {
@@ -413,27 +420,45 @@
                return false;
        }
 
-       // Update deadline_, stop at first expired timer
+       // Find first expired timer (if any)
+       // and update deadline_ with the earliest expiration, excluding the 
first expired timer
+
+       // We guarantee fairness between expired timers by starting
+       // from deadline_index_, increasing it every time process_timers
+       // is called.
        deadline_ = monotonic_clock();
-       auto it = timers_.begin();
-       for (; it != timers_.end(); ++it) {
-               if (!deadline_ || it->deadline_ < deadline_) {
-                       if (it->deadline_ <= now) {
-                               break;
+       auto it = [&](){
+               Timers::iterator expired = timers_.end();
+
+               if (++deadline_index_ >= timers_.size()) {
+                       deadline_index_ = 0;
+               }
+               auto pivot = timers_.begin() + deadline_index_;
+               for (auto it = pivot; it < timers_.end(); ++it) {
+                       if (!deadline_ || it->deadline_ < deadline_) {
+                               if (it->deadline_ <= now && expired == 
timers_.end()) {
+                                       expired = it;
+                               }
+                               else {
+                                       deadline_ = it->deadline_;
+                               }
                        }
-                       deadline_ = it->deadline_;
                }
-       }
-
-       if (it != timers_.end()) {
-               // 'it' is now expired
-               // deadline_ has been updated with prior timers
-               // go through remaining elements to update deadline_
-               for (auto it2 = std::next(it); it2 != timers_.end(); ++it2) {
-                       if (!deadline_ || it2->deadline_ < deadline_) {
-                               deadline_ = it2->deadline_;
+               for (auto it = timers_.begin(); it < pivot; ++it) {
+                       if (!deadline_ || it->deadline_ < deadline_) {
+                               if (it->deadline_ <= now && expired == 
timers_.end()) {
+                                       expired = it;
+                               }
+                               else {
+                                       deadline_ = it->deadline_;
+                               }
                        }
                }
+               return expired;
+       }();
+
+       if (it != timers_.end()) {
+               // 'it' has already expired
 
                event_handler *const handler = it->handler_;
                auto const id = it->id_;
@@ -453,6 +478,11 @@
                        }
                }
 
+               if (deadline_ && !threadless_ && deadline_ > now) {
+                       do_timers_ = false;
+                       timer_cond_.signal(l);
+               }
+
                // Call event handler
                event_assert(!handler->removing_);
 
@@ -461,16 +491,18 @@
                l.unlock();
                (*handler)(timer_event(id));
                l.lock();
+               event_assert(!resend_);
 
                active_handler_ = nullptr;
                active_handler_removed_ = false;
 
                return true;
        }
-
-       if (deadline_ && !threadless_) {
-               do_timers_ = false;
-               timer_cond_.signal(l);
+       else {
+               if (deadline_ && !threadless_) {
+                       do_timers_ = false;
+                       timer_cond_.signal(l);
+               }
        }
 
        return false;
@@ -504,4 +536,10 @@
        }
 }
 
+void event_loop::resend_current_event()
+{
+       event_assert(thread::own_id() == thread_id_);
+       resend_ = true;
+}
+
 }
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.55.4/lib/hash.cpp new/libfilezilla-0.56.0/lib/hash.cpp
--- old/libfilezilla-0.55.4/lib/hash.cpp        2026-04-13 23:37:32.000000000 
+0200
+++ new/libfilezilla-0.56.0/lib/hash.cpp        2026-05-05 11:13:24.000000000 
+0200
@@ -9,6 +9,7 @@
 #include <nettle/memops.h>
 #include <nettle/pbkdf2.h>
 #include <nettle/sha3.h>
+#include <nettle/version.h>
 
 // Undo Nettle's horrible namespace mangling fuckery
 #ifdef pbkdf2_hmac_sha256
@@ -95,7 +96,11 @@
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_md5_digest(&ctx_, out);
+#else
                nettle_md5_digest(&ctx_, MD5_DIGEST_SIZE, out);
+#endif
        }
 
 private:
@@ -228,7 +233,11 @@
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha1_digest(&ctx_, out);
+#else
                nettle_sha1_digest(&ctx_, SHA1_DIGEST_SIZE, out);
+#endif
        }
 
 private:
@@ -257,7 +266,11 @@
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha256_digest(&ctx_, out);
+#else
                nettle_sha256_digest(&ctx_, SHA256_DIGEST_SIZE, out);
+#endif
        }
 
 
@@ -287,7 +300,11 @@
 
        virtual void digest(uint8_t* out) override final
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha512_digest(&ctx_, out);
+#else
                nettle_sha512_digest(&ctx_, SHA512_DIGEST_SIZE, out);
+#endif
        }
 
 protected:
@@ -316,7 +333,11 @@
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha384_digest(&ctx_, out);
+#else
                nettle_sha384_digest(&ctx_, SHA384_DIGEST_SIZE, out);
+#endif
        }
 
 protected:
@@ -340,12 +361,20 @@
 
        virtual void reinit() override final
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha3_init(&ctx_);
+#else
                nettle_sha3_256_init(&ctx_);
+#endif
        }
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha3_256_digest(&ctx_, out);
+#else
                nettle_sha3_256_digest(&ctx_, SHA3_256_DIGEST_SIZE, out);
+#endif
        }
 
 protected:
@@ -370,12 +399,20 @@
 
        virtual void reinit() override final
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha3_init(&ctx_);
+#else
                nettle_sha3_384_init(&ctx_);
+#endif
        }
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha3_384_digest(&ctx_, out);
+#else
                nettle_sha3_384_digest(&ctx_, SHA3_384_DIGEST_SIZE, out);
+#endif
        }
 
 protected:
@@ -400,12 +437,20 @@
 
        virtual void reinit() override final
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha3_init(&ctx_);
+#else
                nettle_sha3_512_init(&ctx_);
+#endif
        }
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_sha3_512_digest(&ctx_, out);
+#else
                nettle_sha3_512_digest(&ctx_, SHA3_512_DIGEST_SIZE, out);
+#endif
        }
 
 protected:
@@ -431,12 +476,20 @@
        virtual void reinit() override
        {
                uint8_t buf[SHA256_DIGEST_SIZE];
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_hmac_sha256_digest(&ctx_, buf);
+#else
                nettle_hmac_sha256_digest(&ctx_, SHA256_DIGEST_SIZE, buf);
+#endif
        }
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_hmac_sha256_digest(&ctx_, out);
+#else
                nettle_hmac_sha256_digest(&ctx_, SHA256_DIGEST_SIZE, out);
+#endif
        }
 
 private:
@@ -461,12 +514,20 @@
        virtual void reinit() override
        {
                uint8_t buf[SHA512_DIGEST_SIZE];
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_hmac_sha512_digest(&ctx_, buf);
+#else
                nettle_hmac_sha512_digest(&ctx_, SHA512_DIGEST_SIZE, buf);
+#endif
        }
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_hmac_sha512_digest(&ctx_, out);
+#else
                nettle_hmac_sha512_digest(&ctx_, SHA512_DIGEST_SIZE, out);
+#endif
        }
 
 private:
@@ -491,12 +552,20 @@
        virtual void reinit() override
        {
                uint8_t buf[SHA1_DIGEST_SIZE];
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_hmac_sha1_digest(&ctx_, buf);
+#else
                nettle_hmac_sha1_digest(&ctx_, SHA1_DIGEST_SIZE, buf);
+#endif
        }
 
        virtual void digest(uint8_t* out) override
        {
+#if NETTLE_VERSION_MAJOR >= 4
+               nettle_hmac_sha1_digest(&ctx_, out);
+#else
                nettle_hmac_sha1_digest(&ctx_, SHA1_DIGEST_SIZE, out);
+#endif
        }
 
 private:
@@ -771,7 +840,11 @@
        }
 
        ret.resize(SHA1_DIGEST_SIZE);
+#if NETTLE_VERSION_MAJOR >= 4
+       nettle_hmac_sha1_digest(&ctx, ret.data());
+#else
        nettle_hmac_sha1_digest(&ctx, ret.size(), ret.data());
+#endif
 
        return ret;
 }
@@ -792,7 +865,11 @@
        }
 
        ret.resize(SHA256_DIGEST_SIZE);
+#if NETTLE_VERSION_MAJOR >= 4
+       nettle_hmac_sha256_digest(&ctx, ret.data());
+#else
        nettle_hmac_sha256_digest(&ctx, ret.size(), ret.data());
+#endif
 
        return ret;
 }
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.55.4/lib/http/client.cpp 
new/libfilezilla-0.56.0/lib/http/client.cpp
--- old/libfilezilla-0.55.4/lib/http/client.cpp 2024-10-15 14:59:21.000000000 
+0200
+++ new/libfilezilla-0.56.0/lib/http/client.cpp 2026-05-28 11:09:15.000000000 
+0200
@@ -100,7 +100,7 @@
                        finalizing
                };
                state state_{state::header};
-
+               uint8_t interim_responses_{};
                bool keep_alive_{};
                bool eof_{};
        };
@@ -163,7 +163,7 @@
 }
 
 client::impl::impl(client & c, aio_buffer_pool * buffer_pool, event_handler & 
handler, logger_interface & logger, std::string && user_agent)
-       : event_handler(handler.event_loop_)
+       : event_handler(handler, child_event_handler)
        , client_(c)
        , handler_(handler)
        , buffer_pool_(buffer_pool)
@@ -471,7 +471,7 @@
                        }
                        else {
                                if (body_buffer_->empty()) {
-                                       send_buffer_.append("0\r\n\r\n\r\n"sv);
+                                       send_buffer_.append("0\r\n\r\n"sv);
                                        request_send_state_ = 
request_send_state::finalizing;
                                }
                                else {
@@ -767,11 +767,9 @@
                        }
 
                        unsigned int code = res.code_ = (recv_buffer_[9] - '0') 
* 100 + (recv_buffer_[10] - '0') * 10 + recv_buffer_[11] - '0';
-                       if (code != 100) {
-                               res.code_ = code;
-                               res.reason_ = recv_buffer_.to_view().substr(13, 
i - 13);
-                               res.flags_ |= response::flag_got_code;
-                       }
+                       res.code_ = code;
+                       res.reason_ = recv_buffer_.to_view().substr(13, i - 13);
+                       res.flags_ |= response::flag_got_code;
 
                        if (!send_pos_) {
                                if (res.success()) {
@@ -842,6 +840,25 @@
        auto & req = srr->req();
        auto & res = srr->res();
 
+       if (res.code_ >= 100 && res.code_ < 200) {
+               if (res.code_ == 101) {
+                       logger_.log(logmsg::error, fztranslate("Switching 
protocols is not supported"));
+                       return continuation::error;
+               }
+               if (++read_state_.interim_responses_ >= 10) {
+                       logger_.log(logmsg::error, fztranslate("Server sent too 
many interimg responses"));
+                       return continuation::error;
+               }
+
+               res.code_ = 0;
+               res.reason_.clear();
+               res.flags_ &= ~response::flag_got_code;
+               res.headers_.clear();
+               logger_.log(logmsg::debug_info, fztranslate("Discarding interim 
response"));
+               return continuation::next;
+       }
+
+
        res.flags_ |= response::flag_got_header;
        if (req.verb_ == "HEAD" || res.code_prohobits_body()) {
                res.flags_ |= response::flag_no_body;
@@ -1260,7 +1277,7 @@
                        }
                }
 
-               if ((buffer_pool_ && buffer_pool_ == w) || 
requests_.back()->res().writer_.get() == w) {
+               if ((buffer_pool_ && buffer_pool_ == w) || 
requests_.front()->res().writer_.get() == w) {
                        read_loop();
                        return;
                }
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.55.4/lib/http/digest.cpp 
new/libfilezilla-0.56.0/lib/http/digest.cpp
--- old/libfilezilla-0.55.4/lib/http/digest.cpp 2023-04-12 14:41:33.000000000 
+0200
+++ new/libfilezilla-0.56.0/lib/http/digest.cpp 2026-05-28 11:09:15.000000000 
+0200
@@ -221,7 +221,9 @@
        if (!opaque.empty()) {
                auth += ", opaque=" + quote(opaque);
        }
-       auth += ", uri=" + quote(uri.to_string());
+
+       auto const digest_uri = uri.get_request();
+       auth += ", uri=" + quote(digest_uri);
 
        std::string full_algorithm = get(params, "algorithm");
        if (full_algorithm.empty()) {
@@ -267,7 +269,7 @@
        auth += ", cnonce=" + quote(cnonce);
 
        std::string a1 = hex_encode<std::string>(h(user + ":" + realm + ":" + 
password));
-       std::string ha2 = hex_encode<std::string>(h(verb + ":" + 
uri.to_string()));
+       std::string ha2 = hex_encode<std::string>(h(verb + ":" + digest_uri));
 
        std::string response;
        if (sess) {
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.55.4/lib/impersonation.cpp 
new/libfilezilla-0.56.0/lib/impersonation.cpp
--- old/libfilezilla-0.55.4/lib/impersonation.cpp       2025-03-26 
11:07:26.000000000 +0100
+++ new/libfilezilla-0.56.0/lib/impersonation.cpp       2026-05-05 
11:13:24.000000000 +0200
@@ -175,7 +175,9 @@
 std::vector<gid_t> get_supplementary(std::string const& username, gid_t 
primary)
 {
        std::vector<gid_t> ret;
-
+#if FZ_IOS
+       // Not supported
+#else
        int size = 100;
        while (true) {
                ret.resize(size);
@@ -198,6 +200,7 @@
                        break;
                }
        }
+#endif
        return ret;
 }
 
@@ -212,7 +215,7 @@
                        return true;
                }
        }
-#elif FZ_MAC
+#elif FZ_MAC && !FZ_IOS
        bool ret{};
 
        CFStringRef cfu = CFStringCreateWithCString(NULL, username.c_str(), 
kCFStringEncodingUTF8);
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.55.4/lib/json.cpp new/libfilezilla-0.56.0/lib/json.cpp
--- old/libfilezilla-0.55.4/lib/json.cpp        2024-10-15 14:59:21.000000000 
+0200
+++ new/libfilezilla-0.56.0/lib/json.cpp        2026-05-28 11:09:15.000000000 
+0200
@@ -1,5 +1,6 @@
 #include "libfilezilla/buffer.hpp"
 #include "libfilezilla/encode.hpp"
+#include "libfilezilla/format.hpp"
 #include "libfilezilla/json.hpp"
 
 #include "string.h"
@@ -166,8 +167,15 @@
                case '\f':
                        out += "\\f"sv;
                        break;
-               default:
-                       out += c;
+               default: {
+                               auto const uc = static_cast<unsigned char>(c);
+                               if (uc < 0x20) {
+                                       out += sprintf("\\u00%02x"sv, uc);
+                               }
+                               else {
+                                       out += c;
+                               }
+                       }
                }
        }
 }
@@ -412,7 +420,7 @@
                else if (c == '\\') {
                        in_escape = true;
                }
-               else if (!c && !allow_null) {
+               else if (static_cast<unsigned char>(c) < 0x20) {
                        return {};
                }
                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.55.4/lib/libfilezilla/event_handler.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/event_handler.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/event_handler.hpp  2026-03-23 
16:11:18.000000000 +0100
+++ new/libfilezilla-0.56.0/lib/libfilezilla/event_handler.hpp  2026-05-28 
11:09:15.000000000 +0200
@@ -3,6 +3,8 @@
 
 #include "event_loop.hpp"
 
+#include <tuple>
+
 /** \file
  * \brief Declares the \ref fz::event_handler "event_handler" class.
  */
@@ -48,7 +50,7 @@
 
        fz::event_loop loop;
        my_handler h(loop);
-       h.SendEvent<foo_event>(42, "Don't Panic");
+       h.send_event<foo_event>(42, "Don't Panic");
 \endcode
 */
 
@@ -72,7 +74,11 @@
 
        /** \brief Deactivates handler, removes all pending events and stops 
all timers for this handler.
         *
-        * When function returns, handler is not in its callback anymore.
+        * remove_handler implicitly removes all nested child event handlers as 
well.
+        *
+        * When this function returns, neither handler nor nested child event 
handlers are in their callback
+        * anymore, with the exception being handlers that self-remove inside 
their own or nested child
+        * handler callbacks.
         *
         * \warning You _MUST_ call remove_handler no later than inside the 
destructor of the most derived class.
         */
@@ -119,12 +125,10 @@
         * For periodic timers, the next event is scheduled right before the 
callback is called. If multiple
         * intervals expire before the timer fires, e.g. under heavy load, only 
one event is sent.
         *
-        * If multiple different timers have expired, the order in which the 
callbacks are executed is unspecified,
-        * there is no fairness guarantee.
-        *
-        * Timers take precedence over other queued events.
+        * If multiple different timers have expired, the order in which the 
callbacks are executed is unspecified, but
+        * they will get provessed eventually, high-frequency timers with slow 
handlers cannot completely starve other timers.
         *
-        * \note High-frequency timers doing heavy processing can starve other 
timers and queued events.
+        * Timers and other queued events are interleaved.
         */
        timer_id add_timer(monotonic_clock const &deadline, duration const& 
interval = {});
 
@@ -137,12 +141,10 @@
         * For periodic timers, the next event is scheduled right before the 
callback is called. If multiple
         * intervals expire before the timer fires, e.g. under heavy load, only 
one event is sent.
         *
-        * If multiple different timers have expired, the order in which the 
callbacks are executed is unspecified,
-        * there is no fairness guarantee.
+        * If multiple different timers have expired, the order in which the 
callbacks are executed is unspecified, but
+        * they will get provessed eventually, high-frequency timers with slow 
handlers cannot completely starve other timers.
         *
-        * Timers take precedence over other queued events.
-        *
-        * \note High-frequency timers doing heavy processing can starve other 
timers and queued events.
+        * Timers and other queued events are interleaved.
         */
        timer_id add_timer(duration const& interval, bool one_shot);
 
@@ -179,6 +181,11 @@
                });
        }
 
+       /**
+        * This must only be called inside the handler's operator()(event_base 
const&), calling it
+        * at other times or different threads is undefined behaviour.
+        * Must not be called with timer events.
+        */
        void resend_current_event() {
                event_loop_.resend_current_event();
        }
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.55.4/lib/libfilezilla/event_loop.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/event_loop.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/event_loop.hpp     2026-04-16 
13:35:05.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/libfilezilla/event_loop.hpp     2026-05-28 
11:09:15.000000000 +0200
@@ -24,9 +24,10 @@
 
 /** \brief A threaded event loop that supports sending events and timers
  *
- * Timers abd queued events are treated fairly, neither can starve the other 
by being too frequent.
+ * Timers and queued events are treated fairly, neither can starve the other 
by being too frequent.
  *
- * If the deadlines of multiple timers have expired, they get processed in an 
unspecified order.
+ * If the deadlines of multiple timers have expired, they get processed in an 
unspecified order, but
+ * eventually they will get processed.
  *
  * \sa event_handler for a complete usage example.
  */
@@ -82,9 +83,12 @@
 
        bool running() const;
 
-       void resend_current_event() {
-               resend_ = true;
-       }
+       /**
+        * This must only be called inside a handler's operator()(event_base 
const&), calling it
+        * at other times or different threads is undefined behaviour.
+        * Must not be called with timer events.
+        */
+       void resend_current_event();
 
 private:
        friend class event_handler;
@@ -132,6 +136,7 @@
        event_handler * active_handler_{};
 
        monotonic_clock deadline_;
+       size_t deadline_index_{};
 
        timer_id next_timer_id_{};
 
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.55.4/lib/libfilezilla/format.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/format.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/format.hpp 2026-01-16 
15:41:56.000000000 +0100
+++ new/libfilezilla-0.56.0/lib/libfilezilla/format.hpp 2026-05-05 
11:13:24.000000000 +0200
@@ -192,6 +192,36 @@
        }
 }
 
+// Converts integral type to hex string with desired string type
+template<typename String, typename Arg>
+String integral_to_octal_string(Arg && arg) noexcept
+{
+       if constexpr (std::is_enum_v<std::decay_t<Arg>>) {
+               // Special handling for enum, cast to underlying type
+               return 
integral_to_octal_string<String>(static_cast<std::underlying_type_t<std::decay_t<Arg>>>(arg));
+       }
+       else if constexpr (std::is_signed_v<std::decay_t<Arg>>) {
+               return 
integral_to_octal_string<String>(static_cast<std::make_unsigned_t<std::decay_t<Arg>>>(arg));
+       }
+       else if constexpr (std::is_integral_v<std::decay_t<Arg>>) {
+               std::decay_t<Arg> v = arg;
+               typename String::value_type buf[sizeof(v) * 3];
+               auto* const end = buf + sizeof(v) * 3;
+               auto* p = end;
+
+               do {
+                       *(--p) = (v & 07) + '0';
+                       v >>= 3;
+               } while (v);
+
+               return String(p, end);
+       }
+       else {
+               format_assert(0);
+               return String();
+       }
+}
+
 // Converts pointer to hex string
 template<typename String, typename Arg>
 String pointer_to_string(Arg&& arg) noexcept
@@ -264,6 +294,10 @@
                ret = integral_to_hex_string<String, 
false>(std::forward<Arg>(arg));
                pad_arg(ret, f);
        }
+       else if (f.type == 'o') {
+               ret = integral_to_octal_string<String>(std::forward<Arg>(arg));
+               pad_arg(ret, f);
+       }
        else if (f.type == 'p') {
                ret = pointer_to_string<String>(std::forward<Arg>(arg));
                pad_arg(ret, f);
@@ -442,7 +476,7 @@
 * \li Supported flags: 0, ' ', -, +
 * \li Field widths are supported as decimal integers not exceeding 10k, longer 
widths are truncated
 * \li precision is ignored
-* \li Supported types: d, i, u, s, x, X, p
+* \li Supported types: d, i, u, s, x, X, o, p
 *
 * For string arguments, mixing char*, wchar_t*, std::string and std::wstring 
is allowed. Converstion
 * to/from narrow strings is using the locale's encoding.
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.55.4/lib/libfilezilla/http/client_response.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/http/client_response.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/http/client_response.hpp   
2023-04-25 15:04:25.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/libfilezilla/http/client_response.hpp   
2026-05-28 11:09:15.000000000 +0200
@@ -24,13 +24,13 @@
        unsigned int code_{};
        std::string reason_;
 
-       enum flags {
+       enum flags : unsigned {
                flag_got_code = 0x01,
                flag_got_header = 0x02,
                flag_got_body = 0x04,
                flag_no_body = 0x08, // e.g. on HEAD requests, or 204/304 
responses
        };
-       int flags_{};
+       unsigned int flags_{};
 
        bool got_code() const { return flags_ & flag_got_code; }
        bool got_header() const { return flags_ & flag_got_header; }
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.55.4/lib/libfilezilla/json.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/json.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/json.hpp   2024-10-15 
14:59:21.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/libfilezilla/json.hpp   2026-05-28 
11:09:15.000000000 +0200
@@ -52,7 +52,13 @@
        }
 
 
-       /// Returns number and string values as the passed integer type
+       /**
+        * \brief Returns number and string values as the passed integer type
+        *
+        * Returns errorval on failure.
+        *
+        * Fractional values are rounded.
+        */
        template<typename T, std::enable_if_t<std::is_integral_v<typename 
std::decay_t<T>>, int> = 0>
        T number_value(T errorval = {}) const {
                auto v = number_value_o<T>();
@@ -62,6 +68,11 @@
                return errorval;
        }
 
+       /**
+        * \brief Returns values as passed integer type if it can be converted, 
nullopt otherwise.
+        *
+        * Fractional values are rounded.
+        */
        template<typename T, std::enable_if_t<std::is_integral_v<typename 
std::decay_t<T>>, int> = 0>
        std::optional<T> number_value_o() const {
                bool constexpr is_signed = std::is_signed_v<typename 
std::decay_t<T>>;
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.55.4/lib/libfilezilla/libfilezilla.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/libfilezilla.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/libfilezilla.hpp   2020-07-07 
14:06:31.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/libfilezilla/libfilezilla.hpp   2026-05-06 
14:55:18.000000000 +0200
@@ -58,13 +58,6 @@
  * To use libfilezilla in your project, you can use <a 
href="http://www.freedesktop.org/wiki/Software/pkg-config/";>pkg-config</a> to 
add the required compiler and linker flags.
  *
  * If your compiler does not enable C++17 (or higher) by default, you may need 
to add -std=c++17 or similar to your compiler flags. Check your compiler's 
manual for details.
- *
- * \subsection using_vs Using libfilezilla with Visual Studio
- *
- * You can compile libefilezilla using the provided Visual Studio solution.
- *
- * To use libfilezilla in your own project, add libfilezilla to the include 
and library directories and link against libfilezilla.lib
- * If you want to link against the DLL version of libfilezilla you must also 
add FZ_USING_DLL to your preprocessor defines.
  */
 #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.55.4/lib/libfilezilla/private/defs.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/private/defs.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/private/defs.hpp   2020-07-07 
14:06:31.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/libfilezilla/private/defs.hpp   2026-05-28 
11:09:15.000000000 +0200
@@ -22,6 +22,13 @@
        #endif
 #endif
 
+#if FZ_MAC
+       #include <TargetConditionals.h>
+       #if TARGET_OS_IPHONE
+               #define FZ_IOS 1
+       #endif
+#endif
+
 #if defined(BUILDING_LIBFILEZILLA) && defined(HAVE_CONFIG_H)
 #include "config.hpp"
 #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.55.4/lib/libfilezilla/shared.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/shared.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/shared.hpp 2022-07-08 
15:18:44.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/libfilezilla/shared.hpp 2026-05-28 
11:09:15.000000000 +0200
@@ -68,6 +68,9 @@
        explicit operator bool() const { return static_cast<bool>(data_); }
 
        bool empty() const { return !data_; }
+
+       long use_count() const { return data_.use_count(); }
+
 private:
        std::shared_ptr<T> data_;
 };
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.55.4/lib/libfilezilla/socket.hpp 
new/libfilezilla-0.56.0/lib/libfilezilla/socket.hpp
--- old/libfilezilla-0.55.4/lib/libfilezilla/socket.hpp 2026-03-18 
16:44:03.000000000 +0100
+++ new/libfilezilla-0.56.0/lib/libfilezilla/socket.hpp 2026-05-28 
11:09:15.000000000 +0200
@@ -589,7 +589,7 @@
        /**
         * Sets the interval between TCP keepalive packets.
         *
-        * Duration must not be smaller than 5 minutes. The default interval is 
2 hours.
+        * Duration must not be smaller than 1 minute. The default interval is 
2 hours.
         */
        void set_keepalive_interval(duration const& d);
 
@@ -848,6 +848,9 @@
 #ifndef ESOCKTNOSUPPORT
 #define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
 #endif
+#ifndef EADDRNOTAVAIL
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#endif
 
 // For the future:
 // Handle ERROR_NETNAME_DELETED=64
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.55.4/lib/local_filesys.cpp 
new/libfilezilla-0.56.0/lib/local_filesys.cpp
--- old/libfilezilla-0.55.4/lib/local_filesys.cpp       2025-12-10 
16:59:22.000000000 +0100
+++ new/libfilezilla-0.56.0/lib/local_filesys.cpp       2026-05-28 
11:09:15.000000000 +0200
@@ -10,6 +10,7 @@
 #include <winternl.h>
 #else
 #include <errno.h>
+#include <limits.h>
 #include <sys/fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.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.55.4/lib/mutex.cpp new/libfilezilla-0.56.0/lib/mutex.cpp
--- old/libfilezilla-0.55.4/lib/mutex.cpp       2025-11-10 15:14:41.000000000 
+0100
+++ new/libfilezilla-0.56.0/lib/mutex.cpp       2026-05-28 11:09:15.000000000 
+0200
@@ -33,7 +33,6 @@
                return mainthread_lock_stack;
        }
        else {
-
                return workerthread_lock_stack;
        }
 }
@@ -82,7 +81,8 @@
                // We're still to the left of the pivot.
 
                // Check if this a common guard mutex also on the lock stack. 
If that's the case, no deadlock due to inversion is possible
-               if (std::find(stack.begin(), stack.begin() + stack.size() - 1, 
order.mutexes_[i]) != stack.end()) {
+               auto end = stack.begin() + stack.size() - 1;
+               if (std::find(stack.begin(), end, order.mutexes_[i]) != end) {
                        return;
                }
        }
@@ -223,7 +223,7 @@
                                }
                                // This may establish a new order
                                stack.pop_back();
-                               record_order(*m, true);
+                               record_order(*stack[0], true);
                        }
                        else {
                                stack.pop_back();
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.55.4/lib/process.cpp new/libfilezilla-0.56.0/lib/process.cpp
--- old/libfilezilla-0.55.4/lib/process.cpp     2026-02-25 17:36:43.000000000 
+0100
+++ new/libfilezilla-0.56.0/lib/process.cpp     2026-05-05 11:13:24.000000000 
+0200
@@ -417,7 +417,7 @@
 #include <memory>
 #include <vector>
 
-#if FZ_MAC
+#if FZ_MAC && !FZ_IOS
 #include "libfilezilla/local_filesys.hpp"
 
 #include <CoreFoundation/CFArray.h>
@@ -969,7 +969,7 @@
 }
 #endif
 
-#if FZ_MAC
+#if FZ_MAC && !FZ_IOS
 namespace {
 template<typename T>
 class cfref final
@@ -1141,7 +1141,7 @@
                return false;
        }
 
-#if FZ_MAC
+#if FZ_MAC && !FZ_IOS
        // Special handling for application bundles if passed a single file name
        int res = try_launch_bundle(cmd_with_args);
        if (res != -1) {
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.55.4/lib/socket.cpp new/libfilezilla-0.56.0/lib/socket.cpp
--- old/libfilezilla-0.55.4/lib/socket.cpp      2026-03-18 16:44:03.000000000 
+0100
+++ new/libfilezilla-0.56.0/lib/socket.cpp      2026-05-28 11:09:15.000000000 
+0200
@@ -298,7 +298,7 @@
                if (res != 0) {
                        return last_socket_error();
                }
-#ifdef TCP_KEEPIDLE
+#ifdef TCP_KEEPINTVL
                int const idle = keepalive_interval.get_seconds();
                res = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (const 
char*)&idle, sizeof(idle));
                if (res != 0) {
@@ -540,8 +540,23 @@
                        return 0;
                }
 
-               if (bindAddr.sockaddr_.sa_family != AF_UNSPEC && 
bindAddr.sockaddr_.sa_family == addr.ai_family) {
-                       (void)::bind(socket_->fd_, &bindAddr.sockaddr_, 
sizeof(bindAddr));
+               if (bindAddr.sockaddr_.sa_family != AF_UNSPEC) {
+                       if (bindAddr.sockaddr_.sa_family == addr.ai_family) {
+                               if (bind(socket_->fd_, &bindAddr.sockaddr_, 
sizeof(bindAddr)) != 0) {
+                                       if (socket_->evt_handler_) {
+                                               
socket_->evt_handler_->send_event<socket_event>(socket_->ev_source_, 
addr.ai_next ? socket_event_flag::connection_next : 
socket_event_flag::connection, last_socket_error());
+                                       }
+                                       close_socket_fd(socket_->fd_);
+                                       return 0;
+                               }
+                       }
+                       else {
+                               if (socket_->evt_handler_) {
+                                       
socket_->evt_handler_->send_event<socket_event>(socket_->ev_source_, 
addr.ai_next ? socket_event_flag::connection_next : 
socket_event_flag::connection, EADDRNOTAVAIL);
+                               }
+                               close_socket_fd(socket_->fd_);
+                               return 0;
+                       }
                }
 
                auto* s = static_cast<socket*>(socket_);
@@ -659,9 +674,6 @@
                        }
 
                        if (socket_) {
-                               if (socket_->evt_handler_) {
-                                       
socket_->evt_handler_->send_event<socket_event>(socket_->ev_source_, 
socket_event_flag::connection, ECONNABORTED);
-                               }
                                static_cast<socket*>(socket_)->state_ = 
socket_state::failed;
                        }
 
@@ -675,13 +687,24 @@
                        bind_hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | 
AI_PASSIVE;
                        bind_hints.ai_socktype = SOCK_STREAM;
                        addrinfo *bindAddressList{};
-                       int res = getaddrinfo(bind.empty() ? nullptr : 
bind.c_str(), "0", &bind_hints, &bindAddressList);
+                       int res = getaddrinfo(bind.c_str(), "0", &bind_hints, 
&bindAddressList);
                        if (!res && bindAddressList) {
                                if (bindAddressList->ai_addr) {
                                        memcpy(&bindAddr.storage, 
bindAddressList->ai_addr, bindAddressList->ai_addrlen);
                                }
                                freeaddrinfo(bindAddressList);
                        }
+                       else {
+#ifdef FZ_WINDOWS
+                               res = convert_msw_error_code(res);
+#endif
+                               if (socket_->evt_handler_) {
+                                       
socket_->evt_handler_->send_event<socket_event>(socket_->ev_source_, 
socket_event_flag::connection, res);
+                               }
+                               static_cast<socket*>(socket_)->state_ = 
socket_state::failed;
+
+                               return false;
+                       }
                }
 
                addrinfo hints{};
@@ -742,9 +765,6 @@
                }
 
                if (socket_) {
-                       if (socket_->evt_handler_) {
-                               
socket_->evt_handler_->send_event<socket_event>(socket_->ev_source_, 
socket_event_flag::connection, ECONNABORTED);
-                       }
                        static_cast<socket*>(socket_)->state_ = 
socket_state::failed;
                }
 
@@ -1527,7 +1547,7 @@
                }
 
                if (fd == -1) {
-                       error = errno;
+                       error = last_socket_error();
                }
        }
 
@@ -1685,10 +1705,16 @@
        }
 #endif
 
+repeat:
        int res = recv(fd_, (char*)buffer, size, 0);
 
        if (res == -1) {
                error = last_socket_error();
+#if !FZ_WINDOWS
+               if (error == EINTR) {
+                       goto repeat;
+               }
+#endif
                if (error == EAGAIN) {
                        scoped_lock l(socket_thread_->mutex_);
                        if (!(socket_thread_->waiting_ & WAIT_READ)) {
@@ -1724,10 +1750,16 @@
        }
 #endif
 
+repeat:
        int res = send(fd_, (const char*)buffer, size, flags);
 
        if (res == -1) {
                error = last_socket_error();
+#if !FZ_WINDOWS
+               if (error == EINTR) {
+                       goto repeat;
+               }
+#endif
                if (error == EAGAIN) {
                        scoped_lock l (socket_thread_->mutex_);
 
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.55.4/lib/thread_pool.cpp 
new/libfilezilla-0.56.0/lib/thread_pool.cpp
--- old/libfilezilla-0.55.4/lib/thread_pool.cpp 2025-12-12 15:14:30.000000000 
+0100
+++ new/libfilezilla-0.56.0/lib/thread_pool.cpp 2026-05-05 11:13:24.000000000 
+0200
@@ -21,7 +21,7 @@
                , pool_(pool)
        {}
 
-       virtual ~pooled_thread_impl()
+       ~pooled_thread_impl()
        {
                thread_.join();
        }
@@ -31,7 +31,7 @@
                return thread_.run([this] { entry(); });
        }
 
-       virtual void entry() {
+       void entry() {
                scoped_lock l(m_);
                while (!quit_) {
                        thread_cond_.wait(l);
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.55.4/lib/tls_layer_impl.cpp 
new/libfilezilla-0.56.0/lib/tls_layer_impl.cpp
--- old/libfilezilla-0.55.4/lib/tls_layer_impl.cpp      2026-03-16 
16:04:46.000000000 +0100
+++ new/libfilezilla-0.56.0/lib/tls_layer_impl.cpp      2026-05-28 
11:09:15.000000000 +0200
@@ -599,11 +599,12 @@
 
 bool tls_layer_impl::set_key_and_certs(const_tls_param_ref key, 
const_tls_param_ref certs, native_string const& password, tls_data_format 
format)
 {
-       if (init()) {
-               if (!set_key_and_certs(cert_context_, key, certs, password, 
format)) {
-                       deinit();
-                       return false;
-               }
+       if (!init()) {
+               return false;
+       }
+       if (!set_key_and_certs(cert_context_, key, certs, password, format)) {
+               deinit();
+               return false;
        }
 
        return true;
@@ -1105,7 +1106,12 @@
                        in.size = required_certificate.size();
 
                        datum_holder der;
-                       gnutls_pem_base64_decode2(nullptr, &in, &der);
+                       int res = gnutls_pem_base64_decode2(nullptr, &in, &der);
+                       if (res != 0) {
+                               logger_.log(logmsg::debug_info, 
L"gnutls_pem_base64_decode2 failed: %d.", res);
+                               state_ = socket_state::failed;
+                               return false;
+                       }
 
                        required_certificate_.assign(der.data, der.data + 
der.size);
                }
@@ -1123,6 +1129,7 @@
                        logger_.log(logmsg::debug_info, 
L"gnutls_session_set_data failed: %d. Going to reinitialize session.", res);
                        deinit_session();
                        if (!init_session(true, extra_flags)) {
+                               state_ = socket_state::failed;
                                return false;
                        }
                }
@@ -1148,6 +1155,7 @@
        }
        else if (tls_layer_.next_layer_.get_state() != socket_state::connected) 
{
                // We're too late
+               state_ = socket_state::failed;
                return false;
        }
 
@@ -1227,6 +1235,7 @@
        }
        else if (tls_layer_.next_layer_.get_state() != socket_state::connected) 
{
                // We're too late
+               state_ = socket_state::failed;
                return false;
        }
 
@@ -1529,7 +1538,7 @@
 {
        logger_.log(logmsg::debug_verbose, L"set_verification_result(%s)", 
trusted ? "true"sv : "false"sv);
 
-       if (state_ != socket_state::connecting && !handshake_successful_) {
+       if (state_ != socket_state::connecting || !handshake_successful_) {
                logger_.log(logmsg::debug_warning, L"set_verification_result 
called at wrong time.");
                return;
        }
@@ -3082,7 +3091,7 @@
                        }
                }
 
-               res = gnutls_x509_crq_set_basic_constraints(crq, 0, -1);
+               res = gnutls_x509_crq_set_basic_constraints(crq, (type == 
tls_layer::cert_type::ca) ? 1 : 0, -1);
                if (res) {
                        ctx.log_gnutls_error(res, 
L"gnutls_x509_crq_set_basic_constraints");
                        return {};
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.55.4/lib/uri.cpp new/libfilezilla-0.56.0/lib/uri.cpp
--- old/libfilezilla-0.55.4/lib/uri.cpp 2025-07-16 11:21:37.000000000 +0200
+++ new/libfilezilla-0.56.0/lib/uri.cpp 2026-05-28 11:09:15.000000000 +0200
@@ -161,8 +161,8 @@
 
 std::string uri::get_request(bool with_query) const
 {
-       std::string ret = percent_encode(path_, true);
-       if (!ret.empty() && !query_.empty() && with_query) {
+       std::string ret = path_.empty() ? "/" : percent_encode(path_, true);
+       if (!query_.empty() && with_query) {
                ret += "?";
                ret += query_;
        }
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.55.4/m4/check_atomic.m4 
new/libfilezilla-0.56.0/m4/check_atomic.m4
--- old/libfilezilla-0.55.4/m4/check_atomic.m4  1970-01-01 01:00:00.000000000 
+0100
+++ new/libfilezilla-0.56.0/m4/check_atomic.m4  2026-05-28 11:09:15.000000000 
+0200
@@ -0,0 +1,46 @@
+# Some versions of gcc/libstdc++ require linking with -latomic if
+# using the C++ atomic library.
+
+# Copyright (c) 2015-2016 Tim Kosse <[email protected]>
+
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+m4_define([_CHECK_ATOMIC_testbody], [[
+  #include <atomic>
+  #include <cstdint>
+
+  int main() {
+    std::atomic<int64_t> a{};
+
+    int64_t v = 5;
+    int64_t r = a.fetch_add(v);
+    return static_cast<int>(r);
+  }
+]])
+
+AC_DEFUN([CHECK_ATOMIC], [
+
+  AC_LANG_PUSH(C++)
+
+  AC_MSG_CHECKING([whether std::atomic can be used without link library])
+
+  AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_ATOMIC_testbody])],[
+      AC_MSG_RESULT([yes])
+    ],[
+      AC_MSG_RESULT([no])
+      LIBS="$LIBS -latomic"
+      libdeps="$libdeps -latomic"
+      AC_MSG_CHECKING([whether std::atomic needs -latomic])
+      AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_ATOMIC_testbody])],[
+          AC_MSG_RESULT([yes])
+        ],[
+          AC_MSG_RESULT([no])
+          AC_MSG_FAILURE([cannot figure out how to use std::atomic])
+        ])
+    ])
+
+  AC_LANG_POP
+])
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.55.4/tests/eventloop.cpp 
new/libfilezilla-0.56.0/tests/eventloop.cpp
--- old/libfilezilla-0.55.4/tests/eventloop.cpp 2026-04-16 13:35:05.000000000 
+0200
+++ new/libfilezilla-0.56.0/tests/eventloop.cpp 2026-05-28 11:09:15.000000000 
+0200
@@ -1,5 +1,6 @@
 #include "../lib/libfilezilla/event_handler.hpp"
 #include "../lib/libfilezilla/event_loop.hpp"
+#include "../lib/libfilezilla/util.hpp"
 
 #include <cppunit/extensions/HelperMacros.h>
 
@@ -10,6 +11,7 @@
        CPPUNIT_TEST(testFilter);
        CPPUNIT_TEST(testCondition);
        CPPUNIT_TEST(testTimer);
+       CPPUNIT_TEST(testTimerFairness);
        CPPUNIT_TEST(testSelfremove);
        CPPUNIT_TEST_SUITE_END();
 
@@ -21,6 +23,7 @@
        void testFilter();
        void testCondition();
        void testTimer();
+       void testTimerFairness();
        void testSelfremove();
 };
 
@@ -258,6 +261,99 @@
 }
 
 namespace {
+class fairness_handler final : public fz::event_handler
+{
+public:
+       fairness_handler(fz::event_loop & l)
+       : fz::event_handler(l)
+       {}
+
+       virtual ~fairness_handler()
+       {
+               remove_handler();
+       }
+
+       virtual void operator()(fz::event_base const& ev) override {
+               bool res = fz::dispatch<event, fz::timer_event>(ev, this, 
&fairness_handler::on_event, &fairness_handler::on_timer);
+               CPPUNIT_ASSERT(res);
+       }
+
+       void on_event(bool stop)
+       {
+               if (stop) {
+                       fz::scoped_lock l(m_);
+                       cond_.signal(l);
+               }
+               else {
+                       send_event<event>(false);
+               }
+       }
+
+       void on_timer(fz::timer_id const& id)
+       {
+               if (id == stop_id_) {
+                       on_event(true);
+               }
+               else {
+                       fz::sleep(fz::duration::from_milliseconds(100));
+               }
+       }
+
+       fz::mutex m_;
+       fz::condition cond_;
+
+       fz::timer_id stop_id_{};
+
+       struct event_type;
+       using event = fz::simple_event<event_type, bool>;
+};
+}
+
+void EventloopTest::testTimerFairness()
+{
+       // Check that busy timers don't starve slow ones
+       {
+               fz::event_loop loop;
+
+               fairness_handler handler(loop);
+
+               fz::scoped_lock l(handler.m_);
+               handler.add_timer(fz::duration::from_milliseconds(1), false);
+               handler.add_timer(fz::duration::from_milliseconds(1), false);
+               handler.add_timer(fz::duration::from_milliseconds(1), false);
+               handler.stop_id_ = 
handler.add_timer(fz::duration::from_milliseconds(220), false);
+
+               CPPUNIT_ASSERT(handler.cond_.wait(l, 
fz::duration::from_seconds(1)));
+       }
+
+       // Check that busy timers don't starve normal events
+       {
+               fz::event_loop loop;
+
+               fairness_handler handler(loop);
+
+               handler.add_timer(fz::duration::from_milliseconds(1), false);
+
+               fz::sleep(fz::duration::from_milliseconds(50));
+               handler.send_event<fairness_handler::event>(true);
+
+               fz::scoped_lock l(handler.m_);
+               CPPUNIT_ASSERT(handler.cond_.wait(l, 
fz::duration::from_seconds(1)));
+       }
+
+               // Check that busy events don't starve timers
+       {
+               fz::event_loop loop;
+
+               fairness_handler handler(loop);
+               fz::scoped_lock l(handler.m_);
+               handler.stop_id_ = 
handler.add_timer(fz::duration::from_milliseconds(220), false);
+               handler.send_event<fairness_handler::event>(false);
+               CPPUNIT_ASSERT(handler.cond_.wait(l, 
fz::duration::from_seconds(1)));
+       }
+}
+
+namespace {
 struct selfremove_event_type;
 typedef fz::simple_event<selfremove_event_type, bool> selfremove_event;
 
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.55.4/tests/format.cpp 
new/libfilezilla-0.56.0/tests/format.cpp
--- old/libfilezilla-0.55.4/tests/format.cpp    2021-08-31 16:47:29.000000000 
+0200
+++ new/libfilezilla-0.56.0/tests/format.cpp    2026-05-05 11:13:24.000000000 
+0200
@@ -132,4 +132,6 @@
        CPPUNIT_ASSERT_EQUAL(std::string("ffffffd6"), fz::sprintf("%x", neg32));
        int64_t const neg64 = -42;
        CPPUNIT_ASSERT_EQUAL(std::string("ffffffffffffffd6"), fz::sprintf("%x", 
neg64));
+
+       CPPUNIT_ASSERT_EQUAL(std::string("361100"), fz::sprintf("%o", 123456));
 }

Reply via email to