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