File PrusaSlicer-2.9.1-pr14263-secretstorage.patch of Package PrusaSlicer

From 26b817ad8e6dc091bc948ca5278724a381c04197 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Tue, 11 Mar 2025 21:16:20 +0100
Subject: [PATCH 1/6] GUI:PhysicalPrinterDialog: Fix secret storage
 user/password check

---
 src/slic3r/GUI/PhysicalPrinterDialog.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
index bb685f60e..6351e676f 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
@@ -282,7 +282,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
         for (const std::string& preset_name : preset_names)
             m_presets.emplace_back(new PresetForPrinter(this, preset_name));
         // "stored" indicates data are stored secretly, load them from store.
-        if (m_printer.config.opt_string("printhost_password") == "stored" && m_printer.config.opt_string("printhost_password") == "stored") {
+        if (m_printer.config.opt_string("printhost_user") == "stored" && m_printer.config.opt_string("printhost_password") == "stored") {
             std::string username;
             std::string password;
             if (load_secret(m_printer.name, "printhost_password", username, password)) {
-- 
2.51.1


From f21c89476109b95c593af9c05e766cbc7cd6efdc Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Tue, 11 Mar 2025 20:00:54 +0100
Subject: [PATCH 2/6] GUI:PhysicalPrinterDialog: Also store the apikey in
 secret storage

---
 src/slic3r/GUI/PhysicalPrinterDialog.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
index 6351e676f..775fa99ce 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
@@ -295,6 +295,17 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
                 m_printer.config.opt_string("printhost_password") = std::string();
             }
         }
+
+        if (m_printer.config.opt_string("printhost_apikey") == "stored") {
+            std::string dummy;
+            std::string apikey;
+            if (load_secret(m_printer.name, "printhost_apikey", dummy, apikey)) {
+                if (!apikey.empty())
+                    m_printer.config.opt_string("printhost_apikey") = apikey;
+            } else {
+                m_printer.config.opt_string("printhost_apikey") = std::string();
+            }
+        }
     }
 
     if (m_presets.size() == 1)
@@ -972,6 +983,13 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event)
             m_printer.config.opt_string("printhost_password", false) = "stored";
         }
     }
+    if (!m_printer.config.opt_string("printhost_apikey").empty()) {
+        if (save_secret(m_printer.name, "printhost_apikey",
+                        "apikey", /* username will be ignored */
+                        m_printer.config.opt_string("printhost_apikey"))) {
+            m_printer.config.opt_string("printhost_apikey", false) = "stored";
+        }
+    }
 
     // save new physical printer
     printers.save_printer(m_printer, renamed_from);
-- 
2.51.1


From c9d141a47168e6b774eeeb86563f68d6d3582dee Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Tue, 11 Mar 2025 21:14:04 +0100
Subject: [PATCH 3/6] GUI:Plater: Fix secret storage username/password check

---
 src/slic3r/GUI/Plater.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 9c6b45e18..e70be6902 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -6698,7 +6698,7 @@ void Plater::send_gcode()
     // Passwords and API keys
     // "stored" indicates data are stored secretly, load them from store.
     std::string printer_name = wxGetApp().preset_bundle->physical_printers.get_selected_printer().name;
-    if (physical_printer_config->opt_string("printhost_password") == "stored" && physical_printer_config->opt_string("printhost_password") == "stored") {
+    if (physical_printer_config->opt_string("printhost_user") == "stored" && physical_printer_config->opt_string("printhost_password") == "stored") {
         std::string username;
         std::string password;
         if (load_secret(printer_name, "printhost_password", username, password)) {
-- 
2.51.1


From 92b046e5e00d9541210e367efacd9d519e2efec6 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Tue, 11 Mar 2025 20:13:45 +0100
Subject: [PATCH 4/6] GUI:Plater: Support reading apikey from secret storage

---
 src/slic3r/GUI/Plater.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index e70be6902..f70f007c0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -6712,16 +6712,14 @@ void Plater::send_gcode()
             physical_printer_config->opt_string("printhost_password") = std::string();
         }
     }
-    /*
     if (physical_printer_config->opt_string("printhost_apikey") == "stored") {
-        std::string username;
-        std::string password;
-        if (load_secret(printer_name, "printhost_apikey", username, password) && !password.empty())
-            physical_printer_config->opt_string("printhost_apikey") = password;
+        std::string dummy;
+        std::string apikey;
+        if (load_secret(printer_name, "printhost_apikey", dummy, apikey) && !apikey.empty())
+            physical_printer_config->opt_string("printhost_apikey") = apikey;
         else
             physical_printer_config->opt_string("printhost_apikey") = std::string();
     }
-    */
     send_gcode_inner(physical_printer_config);
 }
 
-- 
2.51.1


From 5eb46ec2b8143887fc63a05ab4f08bf8eb7915c9 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Tue, 11 Mar 2025 21:14:32 +0100
Subject: [PATCH 5/6] GUI:MainFrame: Add support for secret storage for
 physical printer

---
 src/slic3r/GUI/MainFrame.cpp | 57 ++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index aa1998427..94977aef3 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -922,6 +922,37 @@ void MainFrame::remove_printables_webview_tab()
     m_printables_webview->destroy_browser();
 }
 
+namespace {
+bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd)
+{
+#if wxUSE_SECRETSTORE
+    wxSecretStore store = wxSecretStore::GetDefault();
+    wxString errmsg;
+    if (!store.IsOk(&errmsg)) {
+        std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
+        BOOST_LOG_TRIVIAL(error) << msg;
+        show_error(nullptr, msg);
+        return false;
+    }
+    const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
+    wxString username;
+    wxSecretValue password;
+    if (!store.Load(service, username, password)) {
+        std::string msg(_u8L("Failed to load credentials from the system password store."));
+        BOOST_LOG_TRIVIAL(error) << msg;
+        show_error(nullptr, msg);
+        return false;
+    }
+    usr = into_u8(username);
+    psswd = into_u8(password.GetAsString());
+    return true;
+#else
+    BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store.";
+    return false;
+#endif // wxUSE_SECRETSTORE
+}
+}
+
 void MainFrame::show_printer_webview_tab(DynamicPrintConfig* dpc)
 {
     
@@ -932,11 +963,37 @@ void MainFrame::show_printer_webview_tab(DynamicPrintConfig* dpc)
         if (url.find("http://") != 0 && url.find("https://") != 0) {
             url = "http://" + url;
         }
+
         // set password / api key
+        std::string printer_name = wxGetApp().preset_bundle->physical_printers.get_selected_printer().name;
         if (dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(dpc->option("printhost_authorization_type"))->value == AuthorizationType::atKeyPassword) {
+
+            if (dpc->opt_string("printhost_apikey") == "stored") {
+                std::string dummy;
+                std::string apikey;
+                if (load_secret(printer_name, "printhost_apikey", dummy, apikey) && !apikey.empty())
+                    dpc->opt_string("printhost_apikey") = apikey;
+                else
+                    dpc->opt_string("printhost_apikey") = std::string();
+            }
+
             set_printer_webview_api_key(dpc->opt_string("printhost_apikey"));
         }
         else {
+            if (dpc->opt_string("printhost_user") == "stored" &&
+                dpc->opt_string("printhost_password") == "stored") {
+                std::string user;
+                std::string password;
+                if (load_secret(printer_name, "printhost_password", user, password) &&
+                    !user.empty() && !password.empty()) {
+                    dpc->opt_string("printhost_user") = user;
+                    dpc->opt_string("printhost_password") = password;
+                }
+            } else {
+                dpc->opt_string("printhost_user") = std::string();
+                dpc->opt_string("printhost_password") = std::string();
+            }
+
             set_printer_webview_credentials(dpc->opt_string("printhost_user"), dpc->opt_string("printhost_password"));
         }
         add_printer_webview_tab(from_u8(url));
-- 
2.51.1


From 616de75b7c9b2696b11e04d0483454c730b2741e Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn@cryptomilk.org>
Date: Mon, 10 Nov 2025 16:25:34 +0100
Subject: [PATCH 6/6] GUI: Refactor secret storage to eliminate code
 duplication

Creates a shared SecretStore module to centralize credential management
functionality that was previously duplicated across MainFrame, Plater,
and PhysicalPrinterDialog.
---
 src/slic3r/CMakeLists.txt                |   2 +
 src/slic3r/GUI/MainFrame.cpp             |  64 +-----
 src/slic3r/GUI/PhysicalPrinterDialog.cpp | 120 +----------
 src/slic3r/GUI/Plater.cpp                |  58 +-----
 src/slic3r/GUI/SecretStore.cpp           | 242 +++++++++++++++++++++++
 src/slic3r/GUI/SecretStore.hpp           |  89 +++++++++
 6 files changed, 350 insertions(+), 225 deletions(-)
 create mode 100644 src/slic3r/GUI/SecretStore.cpp
 create mode 100644 src/slic3r/GUI/SecretStore.hpp

diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 8ff466174..a89384e7c 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -157,6 +157,8 @@ set(SLIC3R_GUI_SOURCES
     GUI/BulkExportDialog.cpp
     GUI/PhysicalPrinterDialog.hpp
     GUI/PhysicalPrinterDialog.cpp
+    GUI/SecretStore.hpp
+    GUI/SecretStore.cpp
     GUI/GUI_Factories.cpp
     GUI/GUI_Factories.hpp
     GUI/GUI_ObjectList.cpp
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 94977aef3..65078c44c 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -66,6 +66,7 @@
 #include "Preferences.hpp"
 #include "WebViewPanel.hpp"
 #include "UserAccount.hpp"
+#include "SecretStore.hpp"
 
 #ifdef _WIN32
 #include <dbt.h>
@@ -922,37 +923,6 @@ void MainFrame::remove_printables_webview_tab()
     m_printables_webview->destroy_browser();
 }
 
-namespace {
-bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd)
-{
-#if wxUSE_SECRETSTORE
-    wxSecretStore store = wxSecretStore::GetDefault();
-    wxString errmsg;
-    if (!store.IsOk(&errmsg)) {
-        std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
-    wxString username;
-    wxSecretValue password;
-    if (!store.Load(service, username, password)) {
-        std::string msg(_u8L("Failed to load credentials from the system password store."));
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    usr = into_u8(username);
-    psswd = into_u8(password.GetAsString());
-    return true;
-#else
-    BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store.";
-    return false;
-#endif // wxUSE_SECRETSTORE
-}
-}
-
 void MainFrame::show_printer_webview_tab(DynamicPrintConfig* dpc)
 {
     
@@ -964,36 +934,14 @@ void MainFrame::show_printer_webview_tab(DynamicPrintConfig* dpc)
             url = "http://" + url;
         }
 
-        // set password / api key
+        // Load stored credentials
         std::string printer_name = wxGetApp().preset_bundle->physical_printers.get_selected_printer().name;
-        if (dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(dpc->option("printhost_authorization_type"))->value == AuthorizationType::atKeyPassword) {
-
-            if (dpc->opt_string("printhost_apikey") == "stored") {
-                std::string dummy;
-                std::string apikey;
-                if (load_secret(printer_name, "printhost_apikey", dummy, apikey) && !apikey.empty())
-                    dpc->opt_string("printhost_apikey") = apikey;
-                else
-                    dpc->opt_string("printhost_apikey") = std::string();
-            }
+        SecretStore::load_printer_credentials(printer_name, dpc);
 
+        // Set credentials for webview
+        if (dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(dpc->option("printhost_authorization_type"))->value == AuthorizationType::atKeyPassword) {
             set_printer_webview_api_key(dpc->opt_string("printhost_apikey"));
-        }
-        else {
-            if (dpc->opt_string("printhost_user") == "stored" &&
-                dpc->opt_string("printhost_password") == "stored") {
-                std::string user;
-                std::string password;
-                if (load_secret(printer_name, "printhost_password", user, password) &&
-                    !user.empty() && !password.empty()) {
-                    dpc->opt_string("printhost_user") = user;
-                    dpc->opt_string("printhost_password") = password;
-                }
-            } else {
-                dpc->opt_string("printhost_user") = std::string();
-                dpc->opt_string("printhost_password") = std::string();
-            }
-
+        } else {
             set_printer_webview_credentials(dpc->opt_string("printhost_user"), dpc->opt_string("printhost_password"));
         }
         add_printer_webview_tab(from_u8(url));
diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
index 775fa99ce..19b06fcc7 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
@@ -43,6 +43,7 @@
 #include "BitmapCache.hpp"
 #include "BonjourDialog.hpp"
 #include "MsgDialog.hpp"
+#include "SecretStore.hpp"
 
 namespace Slic3r {
 namespace GUI {
@@ -160,79 +161,6 @@ void PresetForPrinter::on_sys_color_changed()
     m_delete_preset_btn->sys_color_changed();
 }
 
-namespace {
-
-bool is_secret_store_ok()
-{
-#if wxUSE_SECRETSTORE 
-    wxSecretStore store = wxSecretStore::GetDefault();
-    wxString errmsg;
-    if (!store.IsOk(&errmsg)) {
-        BOOST_LOG_TRIVIAL(warning) << "wxSecretStore is not supported: " << errmsg;
-        return false;
-    }
-    return true;
-#else
-    return false;
-#endif
-}
-bool save_secret(const std::string& id, const std::string& opt, const std::string& usr, const std::string& psswd)
-{
-#if wxUSE_SECRETSTORE 
-    wxSecretStore store = wxSecretStore::GetDefault();
-    wxString errmsg;
-    if (!store.IsOk(&errmsg)) {
-        std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
-    const wxString username = boost::nowide::widen(usr);
-    const wxSecretValue password(boost::nowide::widen(psswd));
-    if (!store.Save(service, username, password)) {
-        std::string msg(_u8L("Failed to save credentials to the system password store."));
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    return true;
-#else
-    BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot save password to the system store.";
-    return false;
-#endif // wxUSE_SECRETSTORE 
-}
-bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd)
-{
-#if wxUSE_SECRETSTORE
-    wxSecretStore store = wxSecretStore::GetDefault();
-    wxString errmsg;
-    if (!store.IsOk(&errmsg)) {
-        std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
-    wxString username;
-    wxSecretValue password;
-    if (!store.Load(service, username, password)) {
-        std::string msg(_u8L("Failed to load credentials from the system password store."));
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    usr = into_u8(username);
-    psswd = into_u8(password.GetAsString());
-    return true;
-#else
-    BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store.";
-    return false;
-#endif // wxUSE_SECRETSTORE 
-}
-}
-
-
 //------------------------------------------
 //          PhysicalPrinterDialog
 //------------------------------------------
@@ -281,31 +209,8 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
         const std::set<std::string>& preset_names = printer->get_preset_names();
         for (const std::string& preset_name : preset_names)
             m_presets.emplace_back(new PresetForPrinter(this, preset_name));
-        // "stored" indicates data are stored secretly, load them from store.
-        if (m_printer.config.opt_string("printhost_user") == "stored" && m_printer.config.opt_string("printhost_password") == "stored") {
-            std::string username;
-            std::string password;
-            if (load_secret(m_printer.name, "printhost_password", username, password)) {
-                if (!username.empty())
-                    m_printer.config.opt_string("printhost_user") = username;
-                if (!password.empty())
-                    m_printer.config.opt_string("printhost_password") = password;
-            } else {
-                m_printer.config.opt_string("printhost_user") = std::string();
-                m_printer.config.opt_string("printhost_password") = std::string();
-            }
-        }
-
-        if (m_printer.config.opt_string("printhost_apikey") == "stored") {
-            std::string dummy;
-            std::string apikey;
-            if (load_secret(m_printer.name, "printhost_apikey", dummy, apikey)) {
-                if (!apikey.empty())
-                    m_printer.config.opt_string("printhost_apikey") = apikey;
-            } else {
-                m_printer.config.opt_string("printhost_apikey") = std::string();
-            }
-        }
+        // Load stored credentials
+        SecretStore::load_printer_credentials(m_printer.name, &m_printer.config);
     }
 
     if (m_presets.size() == 1)
@@ -542,8 +447,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
         m_optgroup->append_line(line);
     }
 
-    // Text line with info how passwords and api keys are stored 
-    if (is_secret_store_ok()) {
+    // Text line with info how passwords and api keys are stored
+    if (SecretStore::is_supported()) {
         Line line{ "", "" };
         line.full_width = 1;
         line.widget = [ca_file_hint](wxWindow* parent) {
@@ -977,19 +882,8 @@ void PhysicalPrinterDialog::OnOK(wxEvent& event)
     //update printer name, if it was changed
     m_printer.set_name(into_u8(printer_name));
 
-    // save access data secretly
-    if (!m_printer.config.opt_string("printhost_password").empty()) {
-        if (save_secret(m_printer.name, "printhost_password", m_printer.config.opt_string("printhost_user"), m_printer.config.opt_string("printhost_password"))) {
-            m_printer.config.opt_string("printhost_password", false) = "stored";
-        }
-    }
-    if (!m_printer.config.opt_string("printhost_apikey").empty()) {
-        if (save_secret(m_printer.name, "printhost_apikey",
-                        "apikey", /* username will be ignored */
-                        m_printer.config.opt_string("printhost_apikey"))) {
-            m_printer.config.opt_string("printhost_apikey", false) = "stored";
-        }
-    }
+    // Save credentials to secret store
+    SecretStore::save_printer_credentials(m_printer.name, &m_printer.config);
 
     // save new physical printer
     printers.save_printer(m_printer, renamed_from);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index f70f007c0..3cecd52c0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -133,6 +133,7 @@
 #include "FileArchiveDialog.hpp"
 #include "UserAccount.hpp"
 #include "UserAccountUtils.hpp"
+#include "SecretStore.hpp"
 #include "DesktopIntegrationDialog.hpp"
 #include "WebViewDialog.hpp"
 #include "WebViewPanel.hpp"
@@ -6499,36 +6500,6 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &
     this->reslice_until_step_inner(SLAPrintObjectStep(step), object, postpone_error_messages);
 }
 
-namespace {
-bool load_secret(const std::string& id, const std::string& opt, std::string& usr, std::string& psswd)
-{
-#if wxUSE_SECRETSTORE
-    wxSecretStore store = wxSecretStore::GetDefault();
-    wxString errmsg;
-    if (!store.IsOk(&errmsg)) {
-        std::string msg = GUI::format("%1% (%2%).", _u8L("This system doesn't support storing passwords securely"), errmsg);
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    const wxString service = GUI::format_wxstr(L"%1%/PhysicalPrinter/%2%/%3%", SLIC3R_APP_NAME, id, opt);
-    wxString username;
-    wxSecretValue password;
-    if (!store.Load(service, username, password)) {
-        std::string msg(_u8L("Failed to load credentials from the system password store."));
-        BOOST_LOG_TRIVIAL(error) << msg;
-        show_error(nullptr, msg);
-        return false;
-    }
-    usr = into_u8(username);
-    psswd = into_u8(password.GetAsString());
-    return true;
-#else
-    BOOST_LOG_TRIVIAL(error) << "wxUSE_SECRETSTORE not supported. Cannot load password from the system store.";
-    return false;
-#endif // wxUSE_SECRETSTORE 
-}
-}
 
 
 void Plater::printables_to_connect_gcode(const std::string& url)
@@ -6695,31 +6666,10 @@ void Plater::send_gcode()
     if (! physical_printer_config || p->model.objects.empty())
         return;
 
-    // Passwords and API keys
-    // "stored" indicates data are stored secretly, load them from store.
+    // Load stored credentials
     std::string printer_name = wxGetApp().preset_bundle->physical_printers.get_selected_printer().name;
-    if (physical_printer_config->opt_string("printhost_user") == "stored" && physical_printer_config->opt_string("printhost_password") == "stored") {
-        std::string username;
-        std::string password;
-        if (load_secret(printer_name, "printhost_password", username, password)) {
-            if (!username.empty())
-                physical_printer_config->opt_string("printhost_user") = username;
-            if (!password.empty())
-                physical_printer_config->opt_string("printhost_password") = password;
-        }
-        else {
-            physical_printer_config->opt_string("printhost_user") = std::string();
-            physical_printer_config->opt_string("printhost_password") = std::string();
-        }
-    }
-    if (physical_printer_config->opt_string("printhost_apikey") == "stored") {
-        std::string dummy;
-        std::string apikey;
-        if (load_secret(printer_name, "printhost_apikey", dummy, apikey) && !apikey.empty())
-            physical_printer_config->opt_string("printhost_apikey") = apikey;
-        else
-            physical_printer_config->opt_string("printhost_apikey") = std::string();
-    }
+    SecretStore::load_printer_credentials(printer_name, physical_printer_config);
+
     send_gcode_inner(physical_printer_config);
 }
 
diff --git a/src/slic3r/GUI/SecretStore.cpp b/src/slic3r/GUI/SecretStore.cpp
new file mode 100644
index 000000000..75b82b76a
--- /dev/null
+++ b/src/slic3r/GUI/SecretStore.cpp
@@ -0,0 +1,242 @@
+///|/ Copyright (c) Prusa Research 2025
+///|/
+///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
+///|/
+#include "SecretStore.hpp"
+
+#include <boost/log/trivial.hpp>
+#include <boost/nowide/convert.hpp>
+
+#if wxUSE_SECRETSTORE
+#include <wx/secretstore.h>
+#endif
+
+#include "libslic3r/libslic3r.h"
+#include "libslic3r/PrintConfig.hpp"
+
+#include "GUI.hpp"
+#include "I18N.hpp"
+#include "format.hpp"
+#include "MsgDialog.hpp"
+
+namespace Slic3r {
+namespace GUI {
+namespace SecretStore {
+
+bool is_supported(wxString* errmsg)
+{
+#if wxUSE_SECRETSTORE
+    wxSecretStore store = wxSecretStore::GetDefault();
+    wxString local_errmsg;
+    wxString* err_ptr = errmsg ? errmsg : &local_errmsg;
+    if (!store.IsOk(err_ptr)) {
+        BOOST_LOG_TRIVIAL(warning)
+            << "wxSecretStore is not supported: " << *err_ptr;
+        return false;
+    }
+    return true;
+#else
+    if (errmsg) {
+        *errmsg = "wxUSE_SECRETSTORE not supported";
+    }
+    return false;
+#endif
+}
+
+bool load_secret(const std::string& service_prefix,
+                 const std::string& id,
+                 const std::string& opt,
+                 std::string& usr,
+                 std::string& psswd)
+{
+    wxString errmsg;
+    if (!is_supported(&errmsg)) {
+        std::string msg = GUI::format(
+            "%1% (%2%).",
+            _u8L("This system doesn't support storing passwords "
+                 "securely"),
+            errmsg);
+        BOOST_LOG_TRIVIAL(error) << msg;
+        show_error(nullptr, msg);
+        return false;
+    }
+
+#if wxUSE_SECRETSTORE
+    // Build service name: "AppName/ServicePrefix/id/opt" or
+    // "AppName/ServicePrefix/opt"
+    wxString service;
+    if (id.empty()) {
+        service = GUI::format_wxstr(L"%1%/%2%/%3%",
+                                    SLIC3R_APP_NAME,
+                                    service_prefix,
+                                    opt);
+    } else {
+        service = GUI::format_wxstr(L"%1%/%2%/%3%/%4%",
+                                    SLIC3R_APP_NAME,
+                                    service_prefix,
+                                    id,
+                                    opt);
+    }
+
+    wxSecretStore store = wxSecretStore::GetDefault();
+    wxString username;
+    wxSecretValue password;
+    if (!store.Load(service, username, password)) {
+        std::string msg(_u8L("Failed to load credentials from the "
+                             "system password store."));
+        BOOST_LOG_TRIVIAL(error) << msg;
+        show_error(nullptr, msg);
+        return false;
+    }
+
+    usr = into_u8(username);
+    psswd = into_u8(password.GetAsString());
+    return true;
+#else
+    return false;
+#endif // wxUSE_SECRETSTORE
+}
+
+bool save_secret(const std::string& service_prefix,
+                 const std::string& id,
+                 const std::string& opt,
+                 const std::string& usr,
+                 const std::string& psswd)
+{
+    wxString errmsg;
+    if (!is_supported(&errmsg)) {
+        std::string msg = GUI::format(
+            "%1% (%2%).",
+            _u8L("This system doesn't support storing passwords "
+                 "securely"),
+            errmsg);
+        BOOST_LOG_TRIVIAL(error) << msg;
+        show_error(nullptr, msg);
+        return false;
+    }
+
+#if wxUSE_SECRETSTORE
+    // Build service name: "AppName/ServicePrefix/id/opt" or
+    // "AppName/ServicePrefix/opt"
+    wxString service;
+    if (id.empty()) {
+        service = GUI::format_wxstr(L"%1%/%2%/%3%",
+                                    SLIC3R_APP_NAME,
+                                    service_prefix,
+                                    opt);
+    } else {
+        service = GUI::format_wxstr(L"%1%/%2%/%3%/%4%",
+                                    SLIC3R_APP_NAME,
+                                    service_prefix,
+                                    id,
+                                    opt);
+    }
+
+    wxSecretStore store = wxSecretStore::GetDefault();
+    const wxString username = boost::nowide::widen(usr);
+    const wxSecretValue password(boost::nowide::widen(psswd));
+    if (!store.Save(service, username, password)) {
+        std::string msg(_u8L("Failed to save credentials to the "
+                             "system password store."));
+        BOOST_LOG_TRIVIAL(error) << msg;
+        show_error(nullptr, msg);
+        return false;
+    }
+
+    return true;
+#else
+    return false;
+#endif // wxUSE_SECRETSTORE
+}
+
+void load_printer_credentials(const std::string& printer_name,
+                               DynamicPrintConfig* config)
+{
+    if (!config) {
+        BOOST_LOG_TRIVIAL(error)
+            << "load_printer_credentials: config is null";
+        return;
+    }
+
+    // Load user/password if marked as "stored"
+    if (config->opt_string("printhost_user") == "stored" &&
+        config->opt_string("printhost_password") == "stored") {
+        std::string user;
+        std::string password;
+        if (load_secret("PhysicalPrinter",
+                        printer_name,
+                        "printhost_password",
+                        user,
+                        password)) {
+            if (!user.empty() && !password.empty()) {
+                config->opt_string("printhost_user") = user;
+                config->opt_string("printhost_password") = password;
+            } else {
+                config->opt_string("printhost_user") = std::string();
+                config->opt_string("printhost_password") =
+                    std::string();
+            }
+        } else {
+            config->opt_string("printhost_user") = std::string();
+            config->opt_string("printhost_password") = std::string();
+        }
+    }
+
+    // Load apikey if marked as "stored"
+    if (config->opt_string("printhost_apikey") == "stored") {
+        std::string dummy;
+        std::string apikey;
+        if (load_secret("PhysicalPrinter",
+                        printer_name,
+                        "printhost_apikey",
+                        dummy,
+                        apikey) &&
+            !apikey.empty()) {
+            config->opt_string("printhost_apikey") = apikey;
+        } else {
+            config->opt_string("printhost_apikey") = std::string();
+        }
+    }
+}
+
+void save_printer_credentials(const std::string& printer_name,
+                               DynamicPrintConfig* config)
+{
+    if (!config) {
+        BOOST_LOG_TRIVIAL(error)
+            << "save_printer_credentials: config is null";
+        return;
+    }
+
+    // Save user/password if not empty
+    const std::string& user = config->opt_string("printhost_user");
+    const std::string& password =
+        config->opt_string("printhost_password");
+    if (!user.empty() && !password.empty()) {
+        if (save_secret("PhysicalPrinter",
+                        printer_name,
+                        "printhost_password",
+                        user,
+                        password)) {
+            config->opt_string("printhost_user", false) = "stored";
+            config->opt_string("printhost_password", false) =
+                "stored";
+        }
+    }
+
+    // Save apikey if not empty
+    const std::string& apikey = config->opt_string("printhost_apikey");
+    if (!apikey.empty()) {
+        if (save_secret("PhysicalPrinter",
+                        printer_name,
+                        "printhost_apikey",
+                        "apikey",
+                        apikey)) {
+            config->opt_string("printhost_apikey", false) = "stored";
+        }
+    }
+}
+
+} // namespace SecretStore
+} // namespace GUI
+} // namespace Slic3r
diff --git a/src/slic3r/GUI/SecretStore.hpp b/src/slic3r/GUI/SecretStore.hpp
new file mode 100644
index 000000000..7bfeab79c
--- /dev/null
+++ b/src/slic3r/GUI/SecretStore.hpp
@@ -0,0 +1,89 @@
+///|/ Copyright (c) Prusa Research 2025
+///|/
+///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
+///|/
+#ifndef slic3r_GUI_SecretStore_hpp_
+#define slic3r_GUI_SecretStore_hpp_
+
+#include <string>
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+
+namespace GUI {
+
+namespace SecretStore {
+
+/**
+ * Check if the system secret store is available and working.
+ *
+ * @param errmsg Optional pointer to receive error message if not supported
+ * @return true if the secret store is available, false otherwise
+ */
+bool is_supported(wxString* errmsg = nullptr);
+
+/**
+ * Load credentials from the system secret store.
+ *
+ * @param service_prefix The service prefix (e.g., "PhysicalPrinter"
+ *                       or "PrusaAccount")
+ * @param id The identifier (e.g., printer name) - can be empty for
+ *           PrusaAccount
+ * @param opt The option name (e.g., "printhost_password",
+ *            "printhost_apikey")
+ * @param usr Output parameter for username (or dummy value for apikey)
+ * @param psswd Output parameter for password/apikey value
+ * @return true if credentials were loaded successfully, false otherwise
+ */
+bool load_secret(const std::string& service_prefix,
+                 const std::string& id,
+                 const std::string& opt,
+                 std::string& usr,
+                 std::string& psswd);
+
+/**
+ * Save credentials to the system secret store.
+ *
+ * @param service_prefix The service prefix (e.g., "PhysicalPrinter"
+ *                       or "PrusaAccount")
+ * @param id The identifier (e.g., printer name) - can be empty for
+ *           PrusaAccount
+ * @param opt The option name (e.g., "printhost_password",
+ *            "printhost_apikey")
+ * @param usr The username (or dummy value for apikey)
+ * @param psswd The password/apikey value
+ * @return true if credentials were saved successfully, false otherwise
+ */
+bool save_secret(const std::string& service_prefix,
+                 const std::string& id,
+                 const std::string& opt,
+                 const std::string& usr,
+                 const std::string& psswd);
+
+/**
+ * Load printer credentials from secret store if they are marked as
+ * "stored". Updates the config in-place with the loaded credentials.
+ *
+ * @param printer_name The physical printer name
+ * @param config The printer configuration to update
+ */
+void load_printer_credentials(const std::string& printer_name,
+                              DynamicPrintConfig* config);
+
+/**
+ * Save printer credentials to secret store and mark them as "stored"
+ * in the config. Updates the config in-place to replace actual
+ * credentials with "stored" marker.
+ *
+ * @param printer_name The physical printer name
+ * @param config The printer configuration to save and update
+ */
+void save_printer_credentials(const std::string& printer_name,
+                              DynamicPrintConfig* config);
+
+} // namespace SecretStore
+} // namespace GUI
+} // namespace Slic3r
+
+#endif // slic3r_GUI_SecretStore_hpp_
-- 
2.51.1

openSUSE Build Service is sponsored by