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)); }
