Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libfzssh for openSUSE:Factory 
checked in at 2026-05-05 15:16:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libfzssh (Old)
 and      /work/SRC/openSUSE:Factory/.libfzssh.new.30200 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libfzssh"

Tue May  5 15:16:55 2026 rev:2 rq:1350937 version:1.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libfzssh/libfzssh.changes        2026-04-20 
16:11:02.104505099 +0200
+++ /work/SRC/openSUSE:Factory/.libfzssh.new.30200/libfzssh.changes     
2026-05-05 15:18:12.742149919 +0200
@@ -1,0 +2,39 @@
+Mon May  4 10:12:26 UTC 2026 - ecsos <[email protected]> - 1.2.0
+
+- Update to 1.2.0
+  * New features:
+    - Added support for the [email protected] extension
+  * Bugfixes and minor changes:
+    - Send sftp_client::ready_event after obtaining peer version
+    - Small improvements to channel log messages
+- Changes from 1.1.10
+  * Bugfixes and minor changes:
+    - Default to a maximum large receive window of 2^31 - 1 octets
+      that safely fits into a signed 32-bit integer use by specification
+      violating servers, and white-list known-good servers for use
+      of 2^32 - 1 octet receive windows
+    - Allow larger incoming SFTP packets to match what OpenSSH accepts,
+      as there are servers that unfortunately send packets larger than
+      the safe 32768 octet limit from the specs
+- Changes from 1.1.9
+  * Bugfixes and minor changes:
+    - Handle nonsensical SSH_MSG_USERAUTH_INFO_REQUEST messages
+     with no name, no instructions and no prompt
+    - SFTP: Since the SSH_MSG_USERAUTH_BANNER packet can be safely
+      ignored, only loudly complain if it is malformed instead of
+      dropping the connection
+    - Require libfilezilla 0.55.3
+- Changes from 1.1.8
+  * Bugfixes and minor changes:
+    - SFTP: Allow empty longname in listing entries
+    - Some servers cannot handle large receive windows of up to 2^32 - 1 bytes,
+      despite RFC 4254 mandating that all implementations MUST support windows
+      of up to 2^32 - 1 bytes. If we believe to connect to such a server in
+      single-channel mode, use a smaller window of 2^31 - 1 bytes.
+
+-------------------------------------------------------------------
+Mon Apr 20 08:38:35 UTC 2026 - Jan Engelhardt <[email protected]>
+
+- Trim redundancies from descriptions.
+
+-------------------------------------------------------------------

Old:
----
  fzssh-1.1.7.tar.xz

New:
----
  fzssh-1.2.0.tar.xz

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

Other differences:
------------------
++++++ libfzssh.spec ++++++
--- /var/tmp/diff_new_pack.p60JNX/_old  2026-05-05 15:18:13.322173538 +0200
+++ /var/tmp/diff_new_pack.p60JNX/_new  2026-05-05 15:18:13.326173699 +0200
@@ -16,17 +16,17 @@
 #
 
 
-%define sover          9
+%define sover          11
 %define soname      %{sover}_0_0
 %define libname                %{name}%{soname}
 %define develname      %{name}-devel
 
-%define libfilezillaversion 0.55.0
+%define libfilezillaversion 0.55.3
 
 Name:           libfzssh
-Version:        1.1.7
+Version:        1.2.0
 Release:        0
-Summary:        The fzssh is a SSH/SFTP library based on libfilezilla
+Summary:        A C++ SSH/SFTP library based on libfilezilla
 License:        AGPL-3.0-or-later
 Group:          Development/Libraries/C and C++
 URL:            https://fzssh.filezilla-project.org/
@@ -42,21 +42,15 @@
 BuildRequires:  pkgconfig(libargon2)
 
 %description
-fzssh is a SSH/SFTP library based on libfilezilla
-This library is free software, it is distributed under the terms and 
conditions of
-the GNU Affero General Public License v3+ with attribution, see the library's 
README file for details.
-fzssh is a cross-platform library for all major operating systems, including 
but not limited to Linux, *BSD, macOS and Windows.
+fzssh is a SSH/SFTP library based on libfilezilla.
 
 %package -n    %{libname}
-Summary:        C++ library for libfzsh
+Summary:        A C++ SSH/SFTP library based on libfilezilla
 Group:          System/Libraries
 Provides:       %{name} = %{version}
 
 %description -n        %{libname}
 fzssh is a SSH/SFTP library based on libfilezilla
-This library is free software, it is distributed under the terms and 
conditions of
-the GNU Affero General Public License v3+ with attribution, see the library's 
README file for details.
-fzssh is a cross-platform library for all major operating systems, including 
but not limited to Linux, *BSD, macOS and Windows.
 
 %package -n    %{develname}
 Summary:        Development package for %{name}
@@ -76,8 +70,7 @@
 %install
 %meson_install
 
-%post -n %{libname} -p /sbin/ldconfig
-%postun -n %{libname} -p /sbin/ldconfig
+%ldconfig_scriptlets -n %{libname}
 
 %files
 %license agpl3.txt

++++++ fzssh-1.1.7.tar.xz -> fzssh-1.2.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/NEWS new/fzssh-1.2.0/NEWS
--- old/fzssh-1.1.7/NEWS        2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/NEWS        2026-04-16 15:54:25.000000000 +0200
@@ -1,3 +1,25 @@
+1.2.0 (2026-04-16)
+
++ Added support for the [email protected] extension
+- Send sftp_client::ready_event after obtaining peer version
+- Small improvements to channel log messages
+
+1.1.10 (2026-04-14)
+
+- Default to a maximum large receive window of 2^31 - 1 octets that safely 
fits into a signed 32-bit integer use by specification violating servers, and 
white-list known-good servers for use of 2^32 - 1 octet receive windows
+- Allow larger incoming SFTP packets to match what OpenSSH accepts, as there 
are servers that unfortunately send packets larger than the safe 32768 octet 
limit from the specs
+
+1.1.9 (2026-04-14)
+
+- Handle nonsensical SSH_MSG_USERAUTH_INFO_REQUEST messages with no name, no 
instructions and no prompt
+- SFTP: Since the SSH_MSG_USERAUTH_BANNER packet can be safely ignored, only 
loudly complain if it is malformed instead of dropping the connection
+- Require libfilezilla 0.55.3
+
+1.1.8 (2026-04-13)
+
+- SFTP: Allow empty longname in listing entries
+- Some servers cannot handle large receive windows of up to 2^32 - 1 bytes, 
despite RFC 4254 mandating that all implementations MUST support windows of up 
to 2^32 - 1 bytes. If we believe to connect to such a server in single-channel 
mode, use a smaller window of 2^31 - 1 bytes.
+
 1.1.7 (2026-03-24)
 
 - Fix incorrect uploads in SFTP client due to a superflous length field
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/buffer_util.cpp 
new/fzssh-1.2.0/lib/buffer_util.cpp
--- old/fzssh-1.1.7/lib/buffer_util.cpp 2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/buffer_util.cpp 2026-04-16 15:54:25.000000000 +0200
@@ -128,12 +128,6 @@
                                }
                        }
                        break;
-                       for (auto const c : *s) {
-                               if (!c) {
-                                       return {extract_fail, "Bad character in 
string"sv};
-                               }
-                       }
-                       break;
            case string_type::blob:
                        break;
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/client/connection_protocol.cpp 
new/fzssh-1.2.0/lib/client/connection_protocol.cpp
--- old/fzssh-1.1.7/lib/client/connection_protocol.cpp  2026-03-24 
10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/client/connection_protocol.cpp  2026-04-16 
15:54:25.000000000 +0200
@@ -176,6 +176,47 @@
        return continuation::next;
 }
 
+uint32_t client_connection_protocol::calculate_window_in_max()
+{
+       // RFC 4254 mandates that every implementation MUST be able to handle
+       // receive windows of 2^32 - 1 octets
+       uint32_t constexpr large_window_max = 0xffffffffu;
+
+       // Yet some servers violate the spec and seemingly use signed 32-bit 
integers,
+       // so only windows of up to 2^31 - 1 octets can be used
+       uint32_t constexpr large_window_signed_int_safe = 0x7fffffffu;
+
+       // If there are going to be multiple channels, use a very small value
+       // so that one channel cannot choke the others
+       uint32_t constexpr multi_channel_window = max_payload_size * 16;
+
+       if (no_flow_control_) {
+               return large_window_max;
+       }
+       if (single_channel_) {
+               auto peer_version = transport_.peer_version();
+
+               // Check for known-good
+               if (fz::starts_with(peer_version, "SSH-2.0-fzssh_"sv) ||
+                       fz::starts_with(peer_version, "SSH-2.0-OpenSSH"sv))
+               {
+                       return large_window_max;
+               }
+               // And known-bad servers
+               else if (
+                       fz::starts_with(peer_version, "SSH-2.0-CrushFTPSSHD"sv) 
||
+                       fz::starts_with(peer_version, "SSH-2.0-MOVEit "sv))
+               {
+                       logger_.log(logmsg::debug_warning, "Broken server 
detected. This server cannot large receive windows up to 2^32 - 1. As per RFC 
4254, implementations MUST correctly handle window sizes of up to 2^32 - 1 
bytes"sv);
+                       return large_window_signed_int_safe;
+               }
+
+               return large_window_signed_int_safe;
+       }
+
+       return multi_channel_window;
+}
+
 std::unique_ptr<socket_interface> 
client_connection_protocol::open_channel(channel_type type, std::string_view 
const& cmd)
 {
        auto tname = to_string(type);
@@ -220,7 +261,7 @@
        channel.type_ = type;
        channel.cmd_ = cmd;
        channel.own_id_ = own_id;
-       channel.window_in_max_ = (no_flow_control_ || single_channel_) ? 
0xffffffffu : (max_payload_size * 16); // TODO
+       channel.window_in_max_ = calculate_window_in_max();
        channel.window_in_ = channel.window_in_max_;
 
        if (transport_.service_ != service_type::connection) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/client/connection_protocol.hpp 
new/fzssh-1.2.0/lib/client/connection_protocol.hpp
--- old/fzssh-1.1.7/lib/client/connection_protocol.hpp  2026-03-24 
10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/client/connection_protocol.hpp  2026-04-16 
15:54:25.000000000 +0200
@@ -22,6 +22,7 @@
        continuation on_auth_success();
 
 private:
+       uint32_t calculate_window_in_max();
        bool send_channel_open(channel_data & channel);
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/client/sftp_client.cpp 
new/fzssh-1.2.0/lib/client/sftp_client.cpp
--- old/fzssh-1.1.7/lib/client/sftp_client.cpp  2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/lib/client/sftp_client.cpp  2026-04-16 15:54:25.000000000 
+0200
@@ -15,6 +15,10 @@
 using fz::ssh::extract_uint64;
 using fz::ssh::extract_string;
 
+namespace {
+size_t constexpr max_in_payload_size{256*1024};
+}
+
 class sftp_client_impl final : public sftp_base
 {
 public:
@@ -22,13 +26,20 @@
 
        virtual ~sftp_client_impl();
 
-       virtual void stop(bool send_sone) override;
+       virtual void stop(bool send_done) override;
+       void unblock_read();
 
 private:
        friend class sftp_client;
 
-       sftp_client* c_{};
+       sftp_client* const c_{};
 
+       struct dequeue_on_unblock_read_event_type;
+       typedef simple_event<dequeue_on_unblock_read_event_type> 
dequeue_on_unblock_read_event;
+       void on_dequeue_on_unblock_read_event();
+
+       virtual void operator()(event_base const & ev) override;
+       virtual void on_connect() override;
        virtual void on_can_send_packets() override;
 
        virtual continuation process_packet(message_type type, uint32_t id, 
std::string_view data) override;
@@ -41,6 +52,8 @@
        continuation process_names(response_handler* handler, message_type 
request_type, std::string_view data);
        continuation process_attributes(response_handler* handler, 
std::string_view data);
 
+       continuation process_pending_responses();
+
        uint32_t next_id_{};
 
        struct pending
@@ -61,6 +74,7 @@
        std::deque<pending> pending_;
 
        event_handler* waiting_for_on_can_send_{};
+       bool is_retrying_process_in_queue_{};
 
        friend class request_builder;
 };
@@ -166,11 +180,9 @@
 }
 
 sftp_client_impl::sftp_client_impl(std::unique_ptr<socket_interface> && 
channel, event_handler & handler, logger_interface & logger, sftp_client* c)
-       : sftp_base(std::move(channel), handler, logger, true)
+       : sftp_base(std::move(channel), handler, logger, max_in_payload_size, 
true)
        , c_(c)
 {
-       request_builder b(*this, message_type::SSH_FXP_INIT);
-       write_uint32(b.buf_, 3);
 }
 
 sftp_client_impl::~sftp_client_impl()
@@ -186,6 +198,9 @@
                if (ev.derived_type() == outbuf_empty_event::type()) {
                        return std::get<0>(static_cast<outbuf_empty_event 
const&>(ev).v_) == c_;
                }
+               else if (ev.derived_type() == sftp_client::ready_event::type()) 
{
+                       return std::get<0>(static_cast<sftp_client::ready_event 
const&>(ev).v_) == c_;
+               }
                return false;
        };
        event_loop_.filter_events(event_filter);
@@ -197,6 +212,57 @@
        socket_.reset();
 }
 
+void sftp_client_impl::unblock_read()
+{
+       if (wait_read_) {
+               if (!pending_.empty() && pending_.front().response_type_ != 
message_type::none) {
+                       send_event<dequeue_on_unblock_read_event>();
+                       return;
+               }
+
+               sftp_base::unblock_read();
+       }
+}
+
+void sftp_client_impl::on_dequeue_on_unblock_read_event()
+{
+       is_retrying_process_ = is_retrying_process_in_queue_;
+       is_retrying_process_in_queue_ = false;
+
+       auto c = process_pending_responses();
+
+       is_retrying_process_ = false;
+
+       if (c == continuation::next) {
+               sftp_base::unblock_read();
+               return;
+       }
+
+       if (c == continuation::error) {
+               stop(true);
+               return;
+       }
+
+       // Else, keep waiting.
+}
+
+void sftp_client_impl::operator()(const event_base &ev)
+{
+       if (dispatch<dequeue_on_unblock_read_event>(ev, this, 
&sftp_client_impl::on_dequeue_on_unblock_read_event)) {
+               return;
+       }
+       sftp_base::operator()(ev);
+}
+
+void sftp_client_impl::on_connect()
+{
+       if (!peer_version_) {
+               request_builder b(*this, message_type::SSH_FXP_INIT);
+               write_uint32(b.buf_, 3);
+       }
+       sftp_base::on_connect();
+}
+
 continuation sftp_client_impl::process_packet(message_type type, uint32_t id, 
std::string_view data)
 {
        if (type == message_type::SSH_FXP_VERSION) {
@@ -218,8 +284,6 @@
                return continuation::error;
        }
 
-
-
        // Re-order responses to match request order
 
        if (pending_.empty()) {
@@ -253,39 +317,28 @@
                pending_.pop_front();
                if (handler) {
                        auto ret = process_packet(handler, req_type, type, 
data);
+                       if (ret == continuation::wait_and_retry) {
+                               pending_.emplace_front(id, req_type, handler);
+                       }
                        if (ret != continuation::next) {
                                return ret;
                        }
                }
-
-               // TODO: in case of wait: unblock_read should consider pending
+               auto ret = process_pending_responses();
+               if (ret == continuation::wait_and_retry) {
+                       // The original packet process_packet has been called 
with got
+                       // processed already, hence convert wait_and_retry into 
wait
+                       // so that the buffer gets properly consumed.
+                       ret = continuation::wait;
+               }
+               return ret;
        }
        else {
                p.data_ = data;
                p.response_type_ = type;
+               return continuation::next;
        }
 
-       while (!pending_.empty()) {
-               pending &pref = pending_.front();
-               if (pref.data_.empty()) {
-                       return continuation::next;
-               }
-
-               pending p = std::move(pref);
-               pending_.pop_front();
-               if (!p.handler_) {
-                       continue;
-               }
-
-               auto ret = process_packet(p.handler_, p.request_type_, 
p.response_type_, p.data_);
-               if (ret != continuation::next) {
-                       return ret;
-               }
-
-               // TODO: in case of wait: unblock_read should consider pending
-       }
-
-       return continuation::next;
 }
 
 continuation sftp_client_impl::process_packet(response_handler* handler, 
message_type request_type, message_type response_type, std::string_view data)
@@ -311,17 +364,17 @@
 {
        uint32_t count;
        if (!extract_uint32(data, count)) {
-               logger_.log(logmsg::error, "Could not extract count of 
names"sv);
+               logger_.log(logmsg::error, "Could not extract count of names 
from received SSH_FXP_NAME packet"sv);
                return handler->failure();
        }
 
        if (count > data.size() / 12) {
-               logger_.log(logmsg::error, "Nonsensical name count"sv);
+               logger_.log(logmsg::error, "Nonsensical name count in received 
SSH_FXP_NAME packet"sv);
                return handler->failure();
        }
 
        if (request_type == message_type::SSH_FXP_REALPATH && count != 1) {
-               logger_.log(logmsg::error, "Name count must be one in 
SSH_FXP_REALPATH response"sv);
+               logger_.log(logmsg::error, "Received a SSH_FXP_NAME packet with 
a name count different from 1 in a reply to a SSH_FXP_REALPATH packet"sv);
                return handler->failure();
        }
 
@@ -329,14 +382,14 @@
                entry e;
                auto name = extract_string(data, string_type::text, false);
                if (!name) {
-                       logger_.log(fz::logmsg::error, "Could not extract 
name"sv);
+                       logger_.log(fz::logmsg::error, "Could not extract name 
%u from received SSH_FXP_NAME packet: %s"sv, i, *name);
                        return handler->failure();
                }
                e.name_ = *name;
 
-               auto longname = extract_string(data, string_type::text, false);
+               auto longname = extract_string(data, string_type::text, true);
                if (!longname) {
-                       logger_.log(fz::logmsg::error, "Could not extract 
longname"sv);
+                       logger_.log(fz::logmsg::error, "Could not extract 
longname %u from received SSH_FXP_NAME packet: %s"sv, i, *longname);
                        return handler->failure();
                }
                e.longname_ = *longname;
@@ -366,6 +419,34 @@
        return handler->process_attributes(*attrs);
 }
 
+continuation sftp_client_impl::process_pending_responses()
+{
+       auto ret = continuation::next;
+
+       while (ret == continuation::next && !pending_.empty()) {
+               if (pending_.front().response_type_ == message_type::none) {
+                       break;
+               }
+
+               auto p = std::move(pending_.front());
+               pending_.pop_front();
+
+               if (!p.handler_) {
+                       is_retrying_process_ = false;
+                       continue;
+               }
+
+               ret = process_packet(p.handler_, p.request_type_, 
p.response_type_, p.data_);
+               is_retrying_process_ = false;
+               if (ret == continuation::wait_and_retry) {
+                       is_retrying_process_in_queue_ = true;
+                       pending_.push_front(std::move(p));
+               }
+       }
+
+       return ret;
+}
+
 continuation sftp_client_impl::process_version(std::string_view data)
 {
        if (!extract_uint32(data, peer_version_)) {
@@ -388,6 +469,7 @@
                logger_.log(logmsg::debug_info, "Peer SFTP supports extension 
%s", *name);
        }
 
+       event_handler_.send_event<sftp_client::ready_event>(c_);
        return continuation::next;
 }
 
@@ -488,6 +570,11 @@
        return impl_->unblock_read();
 }
 
+bool sftp_client::is_retrying_process()
+{
+       return impl_->is_retrying_process();
+}
+
 bool sftp_client::can_send_packets()
 {
        return impl_->can_send_packets();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/client/transport.cpp 
new/fzssh-1.2.0/lib/client/transport.cpp
--- old/fzssh-1.1.7/lib/client/transport.cpp    2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/lib/client/transport.cpp    2026-04-16 15:54:25.000000000 
+0200
@@ -286,6 +286,15 @@
                        }
                }
        }
+       else if (name == "[email protected]"sv) {
+               if (value == "0"sv) {
+                       logger_.log(logmsg::debug_info, "Extension version is 
\"0\", enabling use of this extension."sv);
+                       
static_cast<client_userauth&>(*auth_).hostbound_pubkey_auth_ = true;
+               }
+               else {
+                       logger_.log(logmsg::debug_warning, "Unsupported 
extension version, cannot use this extension."sv);
+               }
+       }
 
        return continuation::next;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/client/userauth.cpp 
new/fzssh-1.2.0/lib/client/userauth.cpp
--- old/fzssh-1.1.7/lib/client/userauth.cpp     2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/lib/client/userauth.cpp     2026-04-16 15:54:25.000000000 
+0200
@@ -1,3 +1,4 @@
+#include "transport.hpp"
 #include "userauth.hpp"
 #include "../buffer_util.hpp"
 #include "../transport.hpp"
@@ -5,6 +6,7 @@
 #include "../fzssh/privkey.hpp"
 
 #include <libfilezilla/logger.hpp>
+#include <libfilezilla/translate.hpp>
 
 namespace fz::ssh {
 
@@ -116,10 +118,13 @@
                
sigdata.append(static_cast<uint8_t>(message_id::SSH_MSG_USERAUTH_REQUEST));
                write_string(sigdata, user_);
                write_string(sigdata, "ssh-connection"sv);
-               write_string(sigdata, "publickey"sv);
+               write_string(sigdata, hostbound_pubkey_auth_ ? 
"[email protected]"sv : "publickey"sv);
                sigdata.append(1);
                write_string(sigdata, signature_algorithm);
                write_string(sigdata, privkey_->pubkey_blob());
+               if (hostbound_pubkey_auth_) {
+                       write_string(sigdata, 
static_cast<client_transport&>(transport_).previous_hostkey_);
+               }
                if (privkey_->sign(sigdata.to_view(), signature_algorithm, 
*this)) {
                        signature_algorithm_ = signature_algorithm;
                }
@@ -183,10 +188,15 @@
 
 continuation client_userauth::process_auth_banner(std::string_view packet)
 {
-       auto msg = extract_string(packet, string_type::utf8, false);
+       auto msg = extract_string(packet, string_type::utf8, true);
+       if (!msg) {
+               logger_.log(logmsg::error, fztranslate("Received malformed 
SSH_MSG_USERAUTH_BANNER, cannot extract banner: %s"), *msg);
+               return continuation::next;
+       }
        auto lang = extract_string(packet, string_type::ascii, true);
-       if (!msg || !lang) {
-               return 
transport_.send_disconnect(disconnect_reason::SSH_DISCONNECT_PROTOCOL_ERROR, 
"Malformed SSH_MSG_USERAUTH_BANNER, cannot extract banner"sv);
+       if (!lang) {
+               logger_.log(logmsg::error, fztranslate("Received malformed 
SSH_MSG_USERAUTH_BANNER, cannot extract language tag: %s"), *lang);
+               return continuation::next;
        }
 
        // TODO: Filter and display message
@@ -269,13 +279,16 @@
                return;
        }
 
-       packet_builder b(transport_, message_id::SSH_MSG_USERAUTH_REQUEST, 
sprintf("method=publickey, with %s signature"sv, signature_algorithm_));
+       packet_builder b(transport_, message_id::SSH_MSG_USERAUTH_REQUEST, 
sprintf("method=%s, with %s signature"sv, hostbound_pubkey_auth_ ? 
"[email protected]"sv : "publickey"sv, signature_algorithm_));
        write_string(b.buf_, user_);
        write_string(b.buf_, "ssh-connection"sv);
-       write_string(b.buf_, "publickey"sv);
+       write_string(b.buf_, hostbound_pubkey_auth_ ? 
"[email protected]"sv : "publickey"sv);
        b.buf_.append(1);
        write_string(b.buf_, signature_algorithm_);
        write_string(b.buf_, privkey_->pubkey_blob());
+       if (hostbound_pubkey_auth_) {
+               write_string(b.buf_, 
static_cast<client_transport&>(transport_).previous_hostkey_);
+       }
        write_string(b.buf_, sig);
        b.commit();
        privkey_.reset();
@@ -322,7 +335,7 @@
 
        std::vector<keyboard_interactive_prompt> prompts;
        if (count > 10) {
-               return 
transport_.send_disconnect(disconnect_reason::SSH_DISCONNECT_PROTOCOL_ERROR, 
"Recieved SSH_MSG_USERAUTH_INFO_REQUEST with more than 10 prompts"sv);
+               return 
transport_.send_disconnect(disconnect_reason::SSH_DISCONNECT_PROTOCOL_ERROR, 
"Received SSH_MSG_USERAUTH_INFO_REQUEST with more than 10 prompts"sv);
        }
 
        for (size_t i = 0; i < count; ++i) {
@@ -339,6 +352,19 @@
                prompts.emplace_back(std::move(prompt));
        }
 
+       // For reasons unknown, OpenSSH sends an empty request
+       if (prompts.empty() && name->empty() && instruction->empty()) {
+               if (pending_auths_.front().flags_) {
+                       return 
transport_.send_disconnect(disconnect_reason::SSH_DISCONNECT_PROTOCOL_ERROR, 
"Received multiple SSH_MSG_USERAUTH_INFO_REQUEST messages with no name, no 
instructions and no prompt."sv);
+               }
+               pending_auths_.front().request_only_ = false;
+               pending_auths_.front().flags_ = 1;
+
+               packet_builder b(transport_, 
message_id::SSH_MSG_USERAUTH_INFO_RESPONSE);
+               write_uint32(b.buf_, 0);
+               return b.commit() ? continuation::next : continuation::error;
+       }
+
        
transport_.handler_.send_event<auth_keyboard_interactive_prompt_event>(static_cast<client*>(&transport_.session_),
 *name, *instruction, std::move(prompts));
        return continuation::next;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/client/userauth.hpp 
new/fzssh-1.2.0/lib/client/userauth.hpp
--- old/fzssh-1.1.7/lib/client/userauth.hpp     2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/lib/client/userauth.hpp     2026-04-16 15:54:25.000000000 
+0200
@@ -25,6 +25,8 @@
        void auth_keyboard_interactive_response(std::vector<std::string> const& 
responses);
 
 private:
+       friend class client_transport;
+
        void auth_with_key(std::string_view const& pubblob, std::string_view 
const& signature_algorithm);
 
        virtual void operator()(event_base const& ev) override;
@@ -43,10 +45,12 @@
                {}
 
                std::string_view name_{};
+               size_t flags_{};
                bool request_only_{};
        };
 
        std::list<pending_auth> pending_auths_;
+       bool hostbound_pubkey_auth_{};
 };
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/connection_protocol.cpp 
new/fzssh-1.2.0/lib/connection_protocol.cpp
--- old/fzssh-1.1.7/lib/connection_protocol.cpp 2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/lib/connection_protocol.cpp 2026-04-16 15:54:25.000000000 
+0200
@@ -318,8 +318,6 @@
                return continuation::next;
        }
 
-       logger_.log(logmsg::debug_verbose, "Request is for channel %u"sv, 
own_id);
-
        auto & channel = *it->second;
        return process_channel_packet(id, channel, packet);
 }
@@ -439,7 +437,7 @@
                return continuation::wait;
        }
 #if DUMP_CHANNEL_DATA
-       logger_.log(logmsg::debug_debug, "Incoming data on channel %u: %s"sv, 
id, fz::hex_encode<std::string>(data));
+       logger_.log(logmsg::debug_debug, "Incoming data on channel %u: %s"sv, 
channel.own_id_, fz::hex_encode<std::string>(*data));
 #endif
 
        channel.in_buf_.append(*data);
@@ -494,7 +492,7 @@
        // For now, discard the extended data.
 
 #if DUMP_CHANNEL_DATA
-       logger_.log(logmsg::debug_debug, "Incoming extended data on channel %u: 
%s", id, fz::hex_encode<std::string>(*data));
+       logger_.log(logmsg::debug_debug, "Incoming extended data on channel %u: 
%s", channel.own_id_, fz::hex_encode<std::string>(*data));
 #endif
 
        if (!no_flow_control_) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/fzssh/sftp/sftp.hpp 
new/fzssh-1.2.0/lib/fzssh/sftp/sftp.hpp
--- old/fzssh-1.1.7/lib/fzssh/sftp/sftp.hpp     2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/lib/fzssh/sftp/sftp.hpp     2026-04-16 15:54:25.000000000 
+0200
@@ -60,6 +60,7 @@
        std::optional<uint32_t> gid_;
 
        bool is_directory() const;
+       bool is_symlink() const;
 };
 
 inline bool operator&(file_flags lhs, file_flags rhs) {
@@ -87,6 +88,7 @@
 enum class continuation {
        next,
        wait,
+       wait_and_retry,
        error
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/fzssh/sftp/sftp_client.hpp 
new/fzssh-1.2.0/lib/fzssh/sftp/sftp_client.hpp
--- old/fzssh-1.1.7/lib/fzssh/sftp/sftp_client.hpp      2026-03-24 
10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/fzssh/sftp/sftp_client.hpp      2026-04-16 
15:54:25.000000000 +0200
@@ -51,6 +51,7 @@
        ~sftp_client();
 
        void unblock_read();
+       bool is_retrying_process();
 
        void cancel(response_handler* handler);
        void cancel_wait(fz::event_handler* handler);
@@ -90,6 +91,11 @@
 
        struct done_event_type;
        typedef simple_event<done_event_type, sftp_client*> done_event;
+
+       /// Sent once protocol version has been negotiated
+       struct ready_event_type;
+       typedef simple_event<ready_event_type, sftp_client*> ready_event;
+
 private:
        std::unique_ptr<sftp_client_impl> impl_;
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/sftp.cpp new/fzssh-1.2.0/lib/sftp.cpp
--- old/fzssh-1.1.7/lib/sftp.cpp        2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/sftp.cpp        2026-04-16 15:54:25.000000000 +0200
@@ -1,4 +1,3 @@
-#include "fzssh/sftp/sftp.hpp"
 #include "fzssh/channel.hpp"
 
 #include "buffer_util.hpp"
@@ -12,12 +11,12 @@
 
 #define idtos(id) \
        case message_type:: id: \
-           return #id ""sv \
+               return #id ""sv \
 
 std::string_view FZSSH_PUBLIC_SYMBOL to_string(message_type id)
 {
        switch (id) {
-           idtos(SSH_FXP_INIT);
+               idtos(SSH_FXP_INIT);
                idtos(SSH_FXP_VERSION);
                idtos(SSH_FXP_OPEN);
                idtos(SSH_FXP_CLOSE);
@@ -53,12 +52,12 @@
 
 #define idtos(id) \
        case status_code:: id: \
-           return #id ""sv \
+               return #id ""sv \
 
 std::string_view to_string(status_code id)
 {
        switch (id) {
-           idtos(SSH_FX_OK);
+               idtos(SSH_FX_OK);
                idtos(SSH_FX_EOF);
                idtos(SSH_FX_NO_SUCH_FILE);
                idtos(SSH_FX_PERMISSION_DENIED);
@@ -76,18 +75,22 @@
 
 bool attributes::is_directory() const
 {
-       if (!perms_) {
-               return false;
-       }
+       // Intentionally not using S_IFDIR
+       return perms_ && (*perms_ & 040000);
+}
 
-       return *perms_ & 040000;
+bool attributes::is_symlink() const
+{
+       // Intentionally not using S_IFLNK
+       return perms_ && (*perms_ & 0120000);
 }
 
-sftp_base::sftp_base(std::unique_ptr<socket_interface> && channel, 
event_handler & handler, logger_interface & logger, bool server)
+sftp_base::sftp_base(std::unique_ptr<socket_interface> && channel, 
event_handler & handler, logger_interface & logger, size_t max_in_payload_size, 
bool server)
        : event_handler(handler, child_event_handler)
        , event_handler_(handler)
        , logger_(logger)
        , socket_(std::move(channel))
+       , max_in_payload_size_(max_in_payload_size)
        , server_(server)
 {
        socket_->set_event_handler(this);
@@ -96,12 +99,14 @@
 
 void sftp_base::dump()
 {
+       auto [type, len] = in_packet_ ? std::pair((int)in_packet_->type, 
in_packet_->len) : std::pair(-1, size_t{});
        if (channel_is_fzssh_) {
+               logger_.log(logmsg::error, "wait_read_=%u wait_write_=%u 
outbuf_.size()=%u", wait_read_, wait_write_, outbuf_.size());
        }
        else {
                logger_.log(logmsg::error, "wait_read_=%u inbuf_.size()=%u 
wait_write_=%u outbuf_.size()=%u", wait_read_, inbuf_.size(), wait_write_, 
outbuf_.size());
-               logger_.log(logmsg::error, "%d %u", in_type_ ? (int)*in_type_ : 
-1, in_packet_len_);
        }
+       logger_.log(logmsg::error, "%d %u", type, len);
 }
 
 void sftp_base::operator()(event_base const& ev)
@@ -125,15 +130,22 @@
        if (type == socket_event_flag::read) {
                on_read();
        }
-       else if (type == socket_event_flag::write || type == 
socket_event_flag::connection) {
+       else if (type == socket_event_flag::write) {
                on_send();
        }
+       else if (type == socket_event_flag::connection) {
+               on_connect();
+       }
 }
 
 void sftp_base::on_read()
 {
        wait_read_ = false;
 
+       if (!in_packet_) {
+               in_packet_.emplace();
+       }
+
        std::string_view packet;
 
        if (channel_is_fzssh_) {
@@ -145,6 +157,12 @@
                                wait_read_ = true;
                        }
                        else {
+                               if (!disconnecting_ && !server_) {
+                                       logger_.log(logmsg::error, "SSH channel 
got unexpectedly closed by server"sv);
+                               }
+                               else {
+                                       logger_.log(logmsg::debug_info, "SSH 
channel closed by peer"sv);
+                               }
                                stop(true);
                        }
                        return;
@@ -161,11 +179,11 @@
                }
        }
        else {
-               bool need_recv = disconnecting_ || inbuf_.size() < 
(in_packet_len_ + 4);
+               bool need_recv = disconnecting_ || inbuf_.size() < 
(in_packet_->len + 4);
                logger_.log(logmsg::debug_debug, "sftp_base::on_read 
(need_recv=%d)"sv, need_recv);
                if (need_recv) {
                        int err{};
-                       int res = socket_->read(inbuf_.get(max_in_payload_size 
+ 5), max_in_payload_size + 5, err);
+                       int res = socket_->read(inbuf_.get(max_in_payload_size_ 
+ 5), max_in_payload_size_ + 5, err);
                        if (res > 0 && !disconnecting_) {
                                inbuf_.add(res);
                        }
@@ -174,6 +192,12 @@
                                        wait_read_ = true;
                                }
                                else {
+                                       if (!disconnecting_ && !server_) {
+                                               logger_.log(logmsg::error, "SSH 
channel got unexpectedly closed by server"sv);
+                                       }
+                                       else {
+                                               logger_.log(logmsg::debug_info, 
"SSH channel closed by peer"sv);
+                                       }
                                        stop(true);
                                }
                                return;
@@ -192,9 +216,30 @@
                packet = inbuf_.to_view();
        }
 
-       // Fetch the type
-       if (!in_type_) {
-               if (packet.size() < 5) {
+       std::string_view payload;
+       if (!is_retrying_process_) {
+               // Fetch the type
+               if (in_packet_->len == 0) {
+                       if (packet.size() < 5) {
+                               if (channel_is_fzssh_) {
+                                       
static_cast<ssh_channel&>(*socket_).want_more();
+                               }
+                               else {
+                                       resend_current_event();
+                               }
+                               return;
+                       }
+                       in_packet_->len = read_uint32(packet.data());
+                       in_packet_->type = static_cast<message_type>(packet[4]);
+                       if (!in_packet_->len || in_packet_->len > 
max_in_payload_size_) {
+                               logger_.log(logmsg::error, "Received SFTP 
packet header with invalid length %u for packet type %u"sv, in_packet_->len, 
in_packet_->type);
+                               stop(true);
+                               return;
+                       }
+               }
+
+               // Do we have full packet?
+               if (packet.size() < (4 + in_packet_->len)) {
                        if (channel_is_fzssh_) {
                                static_cast<ssh_channel&>(*socket_).want_more();
                        }
@@ -203,67 +248,65 @@
                        }
                        return;
                }
-               in_packet_len_ = read_uint32(packet.data());
-               in_type_ = static_cast<message_type>(packet[4]);
-               if (!in_packet_len_ || in_packet_len_ > max_in_payload_size) {
-                       logger_.log(logmsg::debug_verbose, "Received packet 
with invalid length %u"sv, in_packet_len_);
+
+               payload = packet.substr(5, in_packet_->len - 1);
+
+               auto t = to_string(in_packet_->type);
+               if (t.empty()) {
+                       logger_.log(logmsg::error, "Received SFTP packet of 
unknown type %u", in_packet_->type);
                        stop(true);
                        return;
                }
-       }
-
-       // Do we have full packet?
-       if (packet.size() < (4 + in_packet_len_)) {
-               if (channel_is_fzssh_) {
-                       static_cast<ssh_channel&>(*socket_).want_more();
-               }
                else {
-                       resend_current_event();
+                       if (in_packet_->type > message_type::SSH_FXP_VERSION) {
+                               if (!extract_uint32(payload, in_packet_->id)) {
+                                       logger_.log(logmsg::error, "Could not 
extract id from incoming %s packet"sv, t);
+                                       stop(true);
+                                       return;
+                               }
+                               else {
+                                       logger_.log(logmsg::debug_verbose, 
"Processing %s, id=%u, len=%u"sv, t, in_packet_->id, in_packet_->len);
+                               }
+                       }
+                       else {
+                               logger_.log(logmsg::debug_verbose, "Processing 
%s, len=%u"sv, t, in_packet_->len);
+                       }
                }
-               return;
        }
+       else {
+               payload = packet.substr(in_packet_->type > 
message_type::SSH_FXP_VERSION ? 5+4 : 5);
 
-       packet = packet.substr(5, in_packet_len_ - 1);
-
-       uint32_t id{};
-       auto t = to_string(*in_type_);
-       if (t.empty()) {
-               logger_.log(logmsg::debug_verbose, "Received packet of unknown 
type %u", *in_type_);
-               stop(true);
-               return;
+               logger_.log(logmsg::debug_verbose, L"Retrying %s, id=%u, 
len=%u"sv, to_string(in_packet_->type), in_packet_->id, in_packet_->len);
        }
-       else {
-               if (*in_type_ > message_type::SSH_FXP_VERSION) {
-                       if (!extract_uint32(packet, id)) {
-                               logger_.log(logmsg::debug_verbose, "Could not 
extract id on incoming packet of type %s"sv, t);
-                               stop(true);
-                               return;
-                       }
-                       else {
-                               logger_.log(logmsg::debug_verbose, "Received 
packet of type %s, id=%u, len=%u"sv, t, id, in_packet_len_);
+
+       auto consume_buffer = [&] {
+               if (channel_is_fzssh_) {
+                       if (socket_) {
+                               
static_cast<ssh_channel&>(*socket_).consume_inbuf(in_packet_->len + 4);
                        }
                }
                else {
-                       logger_.log(logmsg::debug_verbose, "Received packet of 
type %s, len=%u"sv, t, in_packet_len_);
+                       inbuf_.consume(in_packet_->len + 4);
                }
-       }
 
-       continuation c = process_packet(*in_type_, id, packet);
-       in_type_.reset();
-       if (channel_is_fzssh_) {
-               if (socket_) {
-                       
static_cast<ssh_channel&>(*socket_).consume_inbuf(in_packet_len_ + 4);
-               }
-       }
-       else {
-               inbuf_.consume(in_packet_len_ + 4);
-       }
-       in_packet_len_ = 0;
+               in_packet_.reset();
+               is_retrying_process_ = false;
+       };
+
+       continuation c = process_packet(in_packet_->type, in_packet_->id, 
payload);
 
        if (c == continuation::wait) {
+               consume_buffer();
+
+               wait_read_ = true;
+       }
+       else if (c == continuation::wait_and_retry) {
                wait_read_ = true;
+               is_retrying_process_ = true;
        }
        else if (c == continuation::next) {
+               consume_buffer();
+
                if (!server_ || outbuf_.empty()) {
                        // Server-side, send out what we have before reading 
more.
                        resend_current_event();
@@ -285,7 +328,7 @@
                int sent = socket_->write(outbuf_.get(), outbuf_.size(), error);
                if (!sent) {
                        if (!disconnecting_) {
-                               logger_.log(logmsg::error, "Could not write to 
socket, got eof"sv);
+                               logger_.log(logmsg::error, "Could not write to 
SSH channel, got eof"sv);
                        }
                        stop(true);
                        return;
@@ -296,7 +339,7 @@
                        }
                        else {
                                if (!disconnecting_) {
-                                       logger_.log(logmsg::error, "Could not 
write to socket, error %d"sv, error);
+                                       logger_.log(logmsg::error, "Could not 
write to SSH channel, error %d"sv, error);
                                }
                                stop(true);
                        }
@@ -310,6 +353,11 @@
        }
 }
 
+void sftp_base::on_connect()
+{
+       on_send();
+}
+
 void sftp_base::on_send()
 {
        logger_.log(logmsg::debug_debug, "sftp_base::on_send");
@@ -378,6 +426,11 @@
        }
 }
 
+bool sftp_base::is_retrying_process()
+{
+       return is_retrying_process_;
+}
+
 void write_attributes(buffer& buf, attributes const& attrs)
 {
        attribute_flags flags{};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/sftp.hpp new/fzssh-1.2.0/lib/sftp.hpp
--- old/fzssh-1.1.7/lib/sftp.hpp        2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/sftp.hpp        2026-04-16 15:54:25.000000000 +0200
@@ -8,6 +8,7 @@
 #include <libfilezilla/socket.hpp>
 
 #include "fzssh/visibility.hpp"
+#include "fzssh/sftp/sftp.hpp"
 
 namespace fz {
 
@@ -15,7 +16,6 @@
 
 namespace ssh::sftp {
 
-size_t constexpr max_in_payload_size{128*1024};
 size_t constexpr max_out_payload_size{64*1024};
 
 enum class message_type : uint8_t
@@ -56,13 +56,14 @@
 class FZSSH_PUBLIC_SYMBOL sftp_base : public event_handler
 {
 public:
-       sftp_base(std::unique_ptr<socket_interface> && channel, event_handler & 
handler, logger_interface & logger, bool server);
+       sftp_base(std::unique_ptr<socket_interface> && channel, event_handler & 
handler, logger_interface & logger, size_t max_in_payload_size, bool server);
        virtual ~sftp_base() = default;
 
        void dump();
 
        bool can_send_packets();
        void unblock_read();
+       bool is_retrying_process();
 
        // Immediate
        virtual void stop(bool send_done) = 0;
@@ -73,7 +74,7 @@
 protected:
        virtual continuation process_packet(message_type type, uint32_t id, 
std::string_view data) = 0;
        bool can_send_packets(bool wait);
-       virtual void on_can_send_packets() {};
+       virtual void on_can_send_packets() {}
 
        event_handler & event_handler_;
        logger_interface & logger_;
@@ -81,12 +82,14 @@
 
        virtual void operator()(event_base const& ev) override;
        void on_socket_event(socket_event_source *s, fz::socket_event_flag 
type, int error);
+       virtual void on_connect();
        void on_read();
        void on_send();
        void do_send();
 
        std::unique_ptr<socket_interface> socket_;
 
+       size_t const max_in_payload_size_{};
        bool const server_{};
        bool wait_read_{true};
        bool wait_write_{true};
@@ -98,8 +101,17 @@
        buffer outbuf_;
        buffer inbuf_;
 
-       std::optional<message_type> in_type_;
-       size_t in_packet_len_{};
+       bool is_retrying_process_{};
+
+private:
+       struct packet_type
+       {
+               size_t len{};
+               message_type type{};
+               uint32_t id{};
+       };
+
+       std::optional<packet_type> in_packet_;
 
        bool channel_is_fzssh_{};
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/lib/transport.cpp 
new/fzssh-1.2.0/lib/transport.cpp
--- old/fzssh-1.1.7/lib/transport.cpp   2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/lib/transport.cpp   2026-04-16 15:54:25.000000000 +0200
@@ -755,7 +755,15 @@
 {
        auto name = to_string(id);
        if (!name.empty()) {
-               logger_.log(logmsg::debug_info, "Processing binary packet with 
type %s and size %u"sv, name, packet.size());
+
+               // If channel packet, snoop channel id for better log 
readability
+               if (id >= message_id::SSH_MSG_CHANNEL_OPEN_CONFIRMATION && id 
<= message_id::SSH_MSG_CHANNEL_FAILURE && packet.size() >= 4) {
+                       uint32_t channel = read_uint32(packet.data());
+                       logger_.log(logmsg::debug_info, "Processing %s, 
channel=%s, size=%u"sv, name, channel, packet.size() - 4);
+               }
+               else {
+                       logger_.log(logmsg::debug_info, "Processing %s, 
size=%u"sv, name, packet.size());
+               }
        }
        else {
                logger_.log(logmsg::debug_warning, "Received binary packet with 
unimplemented type %d and size %u"sv, id, packet.size());
@@ -1009,8 +1017,6 @@
                return 
send_disconnect(disconnect_reason::SSH_DISCONNECT_PROTOCOL_ERROR, "Malformed 
SSH_MSG_KEXINIT, could not extract reserved field"sv);
        }
 
-       logger_.log(logmsg::debug_info, "Received SSH_MSG_KEXINIT is valid"sv);
-
        logger_.log(logmsg::debug_info, "Kex algorithms offered by peer: %s"sv, 
peer_kex_init_data.kex_);
        logger_.log(logmsg::debug_info, "Host key signature algorithms offered 
by peer: %s"sv, peer_kex_init_data.hostkey_);
        logger_.log(logmsg::debug_info, "Ciphers c2s offered by peer: %s"sv, 
peer_kex_init_data.enc_c2s_);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/meson.build new/fzssh-1.2.0/meson.build
--- old/fzssh-1.1.7/meson.build 2026-03-24 10:32:59.726321200 +0100
+++ new/fzssh-1.2.0/meson.build 2026-04-16 15:54:33.412575700 +0200
@@ -1,6 +1,6 @@
 project('fzssh', ['cpp'],
        meson_version: '>=1.3',
-       version: '1.1.7',
+       version: '1.2.0',
        default_options: ['cpp_std=c++20,c++2a', 'b_pie=true', 
'warning_level=3']
 )
 
@@ -10,7 +10,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
-soversion = '9.0.0'
+soversion = '11.0.0'
 
 if host_machine.system() == 'windows'
        platform = 'windows'
@@ -20,7 +20,7 @@
        platform = 'unix'
 endif
 
-lfz_dep = dependency('libfilezilla', version: '>= 0.55.2')
+lfz_dep = dependency('libfilezilla', version: '>= 0.55.3')
 nettle_dep = dependency('nettle', version: '>= 3.10')
 hogweed_dep = dependency('hogweed', version: '>= 3.10')
 gmp_dep = dependency('gmp', version: '>= 6.2')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/src/benchmark.cpp 
new/fzssh-1.2.0/src/benchmark.cpp
--- old/fzssh-1.1.7/src/benchmark.cpp   2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/src/benchmark.cpp   2026-04-16 15:54:25.000000000 +0200
@@ -215,6 +215,8 @@
 
 int main(int argc, char *argv[])
 {
+       std::setlocale(LC_ALL, "");
+
        fz::stdout_logger log;
        //log.set_all(fz::logmsg::type(0));
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/src/chatter_client.cpp 
new/fzssh-1.2.0/src/chatter_client.cpp
--- old/fzssh-1.1.7/src/chatter_client.cpp      2026-03-24 10:32:05.000000000 
+0100
+++ new/fzssh-1.2.0/src/chatter_client.cpp      2026-04-16 15:54:25.000000000 
+0200
@@ -174,6 +174,8 @@
        (void)argc;
        (void)argv;
 
+       std::setlocale(LC_ALL, "");
+
        fz::event_loop loop(fz::event_loop::threadless);
 
        runner r(loop);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzssh-1.1.7/src/client.cpp 
new/fzssh-1.2.0/src/client.cpp
--- old/fzssh-1.1.7/src/client.cpp      2026-03-24 10:32:05.000000000 +0100
+++ new/fzssh-1.2.0/src/client.cpp      2026-04-16 15:54:25.000000000 +0200
@@ -29,8 +29,6 @@
                , client_(std::move(client))
                , log_(log)
        {
-
-               prompt();
        }
 
        void prompt()
@@ -127,7 +125,11 @@
 
        virtual void operator()(fz::event_base const& ev) override
        {
-               fz::dispatch<fz::socket_event, fz::ssh::auth_done_event, 
fz::ssh::session_done_event, fz::ssh::hostkey_verification_event, 
fz::ssh::auth_requested_event, fz::ssh::available_keys_event, 
fz::ssh::auth_public_key_okay_event, fz::ssh::auth_signature_failure_event, 
fz::ssh::sftp::sftp_client::done_event>(ev, this,
+               fz::dispatch<
+                       fz::socket_event,
+                       fz::ssh::auth_done_event,
+                       fz::ssh::session_done_event, 
fz::ssh::hostkey_verification_event, fz::ssh::auth_requested_event, 
fz::ssh::available_keys_event, fz::ssh::auth_public_key_okay_event, 
fz::ssh::auth_signature_failure_event, fz::ssh::sftp::sftp_client::ready_event, 
fz::ssh::sftp::sftp_client::done_event
+               >(ev, this,
                        &runner::on_socket_event,
                        &runner::on_auth_done,
                        &runner::on_session_done,
@@ -136,6 +138,7 @@
                        &runner::on_agent_keys,
                        &runner::on_auth_pubkey_ok,
                        &runner::on_auth_signature_failure,
+                       &runner::on_sftp_ready,
                        &runner::on_sftp_done);
        }
 
@@ -294,6 +297,11 @@
                ask_method();
        }
 
+       void on_sftp_ready(fz::ssh::sftp::sftp_client*)
+       {
+               sftp_->prompt();
+       }
+
        void on_sftp_done(fz::ssh::sftp::sftp_client*)
        {
                sftp_.reset();
@@ -311,6 +319,8 @@
 
 int main(int argc, char *argv[])
 {
+       std::setlocale(LC_ALL, "");
+
        fz::stdout_logger log;
        log.set_all(fz::logmsg::type(-1));
 

Reply via email to