mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-30 20:31:09 +00:00
feat: add foundation for configurable printer agent connection
This commit is contained in:
@@ -898,7 +898,7 @@ target_include_directories(libslic3r_gui PRIVATE Utils ${CMAKE_CURRENT_BINARY_DI
|
||||
|
||||
if (WIN32)
|
||||
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/WebView2/include)
|
||||
target_link_libraries(libslic3r_gui Advapi32)
|
||||
target_link_libraries(libslic3r_gui Advapi32 Crypt32)
|
||||
endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
|
||||
|
||||
@@ -273,7 +273,10 @@ void MonitorPanel::select_machine(std::string machine_sn)
|
||||
|
||||
void MonitorPanel::on_timer(wxTimerEvent& event)
|
||||
{
|
||||
if (update_flag) {
|
||||
// MediaPlayCtrl may yield the event loop while it joins its camera worker
|
||||
// during window teardown. Do not let a queued monitor refresh touch panels
|
||||
// that are already being destroyed.
|
||||
if (!wxGetApp().is_closing() && update_flag) {
|
||||
update_all();
|
||||
//Layout();
|
||||
}
|
||||
|
||||
@@ -20,14 +20,17 @@
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/filedlg.h>
|
||||
#include <miniz.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevStorage.h"
|
||||
#include "../Utils/Http.hpp"
|
||||
#include "md4c/src/md4c-html.h"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -1461,6 +1464,45 @@ InputIpAddressDialog::InputIpAddressDialog(wxWindow *parent)
|
||||
m_input_top_sizer->Add(0, 0, 0, wxTOP, FromDIP(4));
|
||||
m_input_top_sizer->Add(m_input_area, 0, wxRIGHT | wxEXPAND, FromDIP(18));
|
||||
|
||||
m_tips_cafile = new Label(ip_input_top_panel, _L("HTTPS CA File"));
|
||||
m_input_cafile = new wxTextCtrl(ip_input_top_panel, wxID_ANY);
|
||||
m_input_cafile->SetMinSize(wxSize(FromDIP(260), FromDIP(28)));
|
||||
m_input_cafile->SetMaxSize(wxSize(FromDIP(260), FromDIP(28)));
|
||||
|
||||
m_button_cafile = new Button(ip_input_top_panel, _L("Browse") + " " + dots);
|
||||
m_button_cafile->SetStyle(ButtonStyle::Regular, ButtonType::Parameter);
|
||||
m_button_cafile->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||
static const auto filemasks = _L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*");
|
||||
wxFileDialog openFileDialog(this, _L("Open CA certificate file"), "", "", filemasks,
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (openFileDialog.ShowModal() != wxID_CANCEL)
|
||||
m_input_cafile->SetValue(openFileDialog.GetPath());
|
||||
});
|
||||
|
||||
auto cafile_input_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
cafile_input_sizer->Add(m_input_cafile, 1, wxALIGN_CENTER_VERTICAL);
|
||||
cafile_input_sizer->Add(m_button_cafile, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
|
||||
|
||||
m_cafile_hint = new Label(ip_input_top_panel, _L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."));
|
||||
m_cafile_hint->Wrap(FromDIP(352));
|
||||
|
||||
m_input_top_sizer->Add(m_tips_cafile, 0, wxTOP | wxEXPAND, FromDIP(10));
|
||||
m_input_top_sizer->Add(cafile_input_sizer, 0, wxTOP | wxEXPAND, FromDIP(4));
|
||||
m_input_top_sizer->Add(m_cafile_hint, 0, wxTOP | wxEXPAND, FromDIP(4));
|
||||
|
||||
if (!Http::ca_file_supported()) {
|
||||
m_input_cafile->Disable();
|
||||
m_button_cafile->Disable();
|
||||
m_cafile_hint->SetLabel(_L("This system uses HTTPS certificates from the system Certificate Store or Keychain. To use a custom CA file, import it there."));
|
||||
m_cafile_hint->Wrap(FromDIP(352));
|
||||
}
|
||||
|
||||
if (wxGetApp().preset_bundle) {
|
||||
const auto& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
if (config.has("printhost_cafile"))
|
||||
m_input_cafile->SetValue(from_u8(config.opt_string("printhost_cafile")));
|
||||
}
|
||||
|
||||
ip_input_top_panel->SetSizer(m_input_top_sizer);
|
||||
ip_input_top_panel->Layout();
|
||||
ip_input_top_panel->Fit();
|
||||
@@ -1736,7 +1778,8 @@ void InputIpAddressDialog::set_machine_obj(MachineObject* obj)
|
||||
auto str_ip = m_input_ip->GetTextCtrl()->GetValue();
|
||||
auto str_access_code = m_input_access_code->GetTextCtrl()->GetValue();
|
||||
// ORCA enabling / disabling buttons with conditions enough to change its style
|
||||
m_button_ok->Enable(isIp(str_ip.ToStdString()) && str_access_code.Length() == 8);
|
||||
m_button_ok->Enable(isValidEndpoint(str_ip.ToStdString()) &&
|
||||
(str_access_code.IsEmpty() || str_access_code.Length() >= 8));
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
@@ -1773,19 +1816,29 @@ void InputIpAddressDialog::update_test_msg(wxString msg,bool connected)
|
||||
Fit();
|
||||
}
|
||||
|
||||
bool InputIpAddressDialog::isIp(std::string ipstr)
|
||||
bool InputIpAddressDialog::isValidEndpoint(std::string endpoint)
|
||||
{
|
||||
istringstream ipstream(ipstr);
|
||||
int num[4];
|
||||
char point[3];
|
||||
string end;
|
||||
ipstream >> num[0] >> point[0] >> num[1] >> point[1] >> num[2] >> point[2] >> num[3] >> end;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (num[i] < 0 || num[i]>255) return false;
|
||||
if (point[i] != '.') return false;
|
||||
}
|
||||
if (num[3] < 0 || num[3]>255) return false;
|
||||
if (!end.empty()) return false;
|
||||
if (endpoint.empty() || std::any_of(endpoint.begin(), endpoint.end(), [](unsigned char c) {
|
||||
return std::isspace(c) != 0;
|
||||
}))
|
||||
return false;
|
||||
|
||||
const bool has_http_scheme = endpoint.rfind("http://", 0) == 0;
|
||||
const bool has_https_scheme = endpoint.rfind("https://", 0) == 0;
|
||||
const auto scheme_pos = endpoint.find("://");
|
||||
if (scheme_pos != std::string::npos && !has_http_scheme && !has_https_scheme)
|
||||
return false;
|
||||
|
||||
std::string port;
|
||||
const std::string host = Http::get_host_from_url(endpoint, &port);
|
||||
if (host.empty())
|
||||
return false;
|
||||
|
||||
// get_host_from_url returns its input when libcurl cannot parse it. For a
|
||||
// URL with a scheme, that means a failed parse still needs to be rejected.
|
||||
if (scheme_pos != std::string::npos && host == endpoint)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1801,6 +1854,8 @@ void InputIpAddressDialog::on_ok(wxMouseEvent& evt)
|
||||
m_trouble_shoot->Hide();
|
||||
std::string str_ip = m_input_ip->GetTextCtrl()->GetValue().ToStdString();
|
||||
std::string str_access_code = m_input_access_code->GetTextCtrl()->GetValue().ToStdString();
|
||||
if (str_access_code.empty())
|
||||
str_access_code = "88888888";
|
||||
std::string str_name = m_input_printer_name->GetTextCtrl()->GetValue().Strip(wxString::both).ToStdString();
|
||||
// Serial number should not contain lower case letters, and bambu_network plugin crashes
|
||||
// if user entered the wrong serial number, so we call `Upper()` here.
|
||||
@@ -1820,6 +1875,17 @@ void InputIpAddressDialog::on_ok(wxMouseEvent& evt)
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
if (wxGetApp().preset_bundle) {
|
||||
auto& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
std::string port;
|
||||
Http::get_host_from_url(str_ip, &port);
|
||||
config.opt_string("print_host") = str_ip;
|
||||
if (!port.empty())
|
||||
config.opt_string("printhost_port") = port;
|
||||
if (Http::ca_file_supported())
|
||||
config.opt_string("printhost_cafile") = m_input_cafile->GetValue().ToStdString();
|
||||
}
|
||||
|
||||
token_.reset(this, nop_deleter);
|
||||
m_thread = new boost::thread(boost::bind(&InputIpAddressDialog::workerThreadFunc, this, str_ip, str_access_code, str_sn, str_model_id, str_name));
|
||||
}
|
||||
@@ -1835,6 +1901,8 @@ void InputIpAddressDialog::on_send_retry()
|
||||
Fit();
|
||||
wxString ip = m_input_ip->GetTextCtrl()->GetValue();
|
||||
wxString str_access_code = m_input_access_code->GetTextCtrl()->GetValue();
|
||||
if (str_access_code.IsEmpty())
|
||||
str_access_code = "88888888";
|
||||
|
||||
// check support function
|
||||
if (!m_obj) return;
|
||||
@@ -2056,7 +2124,7 @@ void InputIpAddressDialog::on_text(wxCommandEvent &evt)
|
||||
auto str_ip = m_input_ip->GetTextCtrl()->GetValue();
|
||||
auto str_access_code = m_input_access_code->GetTextCtrl()->GetValue();
|
||||
|
||||
if (str_access_code.empty()) {
|
||||
if (str_access_code.IsEmpty()) {
|
||||
str_access_code = "88888888";
|
||||
}
|
||||
|
||||
@@ -2072,7 +2140,8 @@ void InputIpAddressDialog::on_text(wxCommandEvent &evt)
|
||||
}
|
||||
|
||||
// ORCA enabling / disabling buttons with conditions enough to change its style
|
||||
bool enable_btns = isIp(str_ip.ToStdString()) && str_access_code.Length() == 8 && invalid_access_code;
|
||||
bool valid_access_code_length = str_access_code.IsEmpty() || str_access_code.Length() >= 8;
|
||||
bool enable_btns = isValidEndpoint(str_ip.ToStdString()) && valid_access_code_length && invalid_access_code;
|
||||
m_button_manual_setup->Enable(enable_btns);
|
||||
m_button_ok->Enable(enable_btns);
|
||||
|
||||
@@ -2087,7 +2156,7 @@ InputIpAddressDialog::~InputIpAddressDialog()
|
||||
|
||||
void InputIpAddressDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
|
||||
m_button_cafile->Rescale();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ public:
|
||||
void update_title(wxString title);
|
||||
void set_machine_obj(MachineObject* obj);
|
||||
void update_test_msg(wxString msg, bool connected);
|
||||
bool isIp(std::string ipstr);
|
||||
bool isValidEndpoint(std::string endpoint);
|
||||
void check_ip_address_failed(int result);
|
||||
void on_check_ip_address_failed(wxCommandEvent& evt);
|
||||
void on_ok(wxMouseEvent& evt);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Http.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
@@ -14,8 +15,19 @@
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#ifdef OPENSSL_CERT_OVERRIDE
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509err.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <wincrypt.h>
|
||||
// wincrypt.h uses this token for a certificate-name property identifier.
|
||||
# undef X509_NAME
|
||||
#endif
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
@@ -122,7 +134,7 @@ struct Http::priv
|
||||
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
|
||||
std::string headers;
|
||||
size_t limit;
|
||||
bool cancel;
|
||||
std::atomic_bool cancel;
|
||||
std::unique_ptr<form_file> putFile;
|
||||
|
||||
std::thread io_thread;
|
||||
@@ -260,9 +272,9 @@ int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_o
|
||||
self->progressfn(progress, cb_cancel);
|
||||
}
|
||||
|
||||
if (cb_cancel) { self->cancel = true; }
|
||||
if (cb_cancel) { self->cancel.store(true); }
|
||||
|
||||
return self->cancel;
|
||||
return self->cancel.load();
|
||||
}
|
||||
|
||||
int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
@@ -473,7 +485,7 @@ void Http::priv::http_perform()
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
if (res == CURLE_ABORTED_BY_CALLBACK) {
|
||||
if (cancel) {
|
||||
if (cancel.load()) {
|
||||
// The abort comes from the request being cancelled programatically
|
||||
Progress dummyprogress(0, 0, 0, 0, std::string());
|
||||
bool cancel = true;
|
||||
@@ -784,7 +796,7 @@ void Http::perform_sync()
|
||||
|
||||
void Http::cancel()
|
||||
{
|
||||
if (p) { p->cancel = true; }
|
||||
if (p) { p->cancel.store(true); }
|
||||
}
|
||||
|
||||
void Http::print() const
|
||||
@@ -939,6 +951,45 @@ std::string Http::tls_system_cert_store()
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Http::add_platform_root_certificates(SSL_CTX* ssl_context)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
X509_STORE* openssl_store = SSL_CTX_get_cert_store(ssl_context);
|
||||
if (!openssl_store)
|
||||
throw std::runtime_error("unable to get OpenSSL certificate store");
|
||||
|
||||
const auto load_store = [&](DWORD location) {
|
||||
HCERTSTORE windows_store = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0,
|
||||
location | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG,
|
||||
L"ROOT");
|
||||
if (!windows_store)
|
||||
return;
|
||||
|
||||
PCCERT_CONTEXT windows_certificate = nullptr;
|
||||
while ((windows_certificate = CertEnumCertificatesInStore(windows_store, windows_certificate)) != nullptr) {
|
||||
const unsigned char* encoded = windows_certificate->pbCertEncoded;
|
||||
X509* certificate = d2i_X509(nullptr, &encoded, static_cast<long>(windows_certificate->cbCertEncoded));
|
||||
if (!certificate) {
|
||||
ERR_clear_error();
|
||||
continue;
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
if (X509_STORE_add_cert(openssl_store, certificate) != 1)
|
||||
ERR_clear_error();
|
||||
X509_free(certificate);
|
||||
}
|
||||
|
||||
CertCloseStore(windows_store, 0);
|
||||
};
|
||||
|
||||
load_store(CERT_SYSTEM_STORE_CURRENT_USER);
|
||||
load_store(CERT_SYSTEM_STORE_LOCAL_MACHINE);
|
||||
#else
|
||||
(void)ssl_context;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Http::url_encode(const std::string &str)
|
||||
{
|
||||
::CURL *curl = ::curl_easy_init();
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r_version.h"
|
||||
|
||||
typedef struct ssl_ctx_st SSL_CTX;
|
||||
|
||||
#define MAX_SIZE_TO_FILE 3*1024
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -198,6 +200,10 @@ public:
|
||||
// Return empty string on success or error message on fail.
|
||||
static std::string tls_global_init();
|
||||
static std::string tls_system_cert_store();
|
||||
// Add platform root certificates to a standalone OpenSSL context. This
|
||||
// supplements set_default_verify_paths() on platforms where OpenSSL does
|
||||
// not use the native certificate store.
|
||||
static void add_platform_root_certificates(SSL_CTX* ssl_context);
|
||||
|
||||
// converts the given string to an url_encoded_string
|
||||
static std::string url_encode(const std::string &str);
|
||||
|
||||
@@ -38,12 +38,25 @@ wxString Moonraker::get_test_failed_msg(wxString &msg) const
|
||||
|
||||
std::string Moonraker::make_url(const std::string &path) const
|
||||
{
|
||||
if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
|
||||
if (m_host.back() == '/')
|
||||
return (boost::format("%1%%2%") % m_host % path).str();
|
||||
return (boost::format("%1%/%2%") % m_host % path).str();
|
||||
const bool has_scheme = m_host.find("http://") == 0 || m_host.find("https://") == 0;
|
||||
const bool use_ssl = m_host.find("https://") == 0;
|
||||
std::string base = has_scheme ? m_host : ("http://" + m_host);
|
||||
|
||||
const size_t authority_start = base.find("://") + 3;
|
||||
const size_t authority_end = base.find('/', authority_start);
|
||||
const std::string authority = base.substr(authority_start, authority_end - authority_start);
|
||||
const size_t closing_bracket = authority.rfind(']');
|
||||
const bool has_port = closing_bracket != std::string::npos
|
||||
? closing_bracket + 1 < authority.size() && authority[closing_bracket + 1] == ':'
|
||||
: authority.find(':') != std::string::npos;
|
||||
if (!has_port) {
|
||||
const std::string default_port = use_ssl ? ":7130" : ":7125";
|
||||
base.insert(authority_end == std::string::npos ? base.size() : authority_end, default_port);
|
||||
}
|
||||
return (boost::format("http://%1%/%2%") % m_host % path).str();
|
||||
|
||||
if (base.back() == '/')
|
||||
return (boost::format("%1%%2%") % base % path).str();
|
||||
return (boost::format("%1%/%2%") % base % path).str();
|
||||
}
|
||||
|
||||
void Moonraker::set_auth(Http &http) const
|
||||
@@ -53,6 +66,8 @@ void Moonraker::set_auth(Http &http) const
|
||||
// filled the user/password fields — those are PrusaLink/OctoPrint conventions.
|
||||
if (!m_apikey.empty())
|
||||
http.header("X-Api-Key", m_apikey);
|
||||
const bool use_ssl = m_host.find("https://") == 0;
|
||||
http.tls_verify(use_ssl);
|
||||
if (!m_cafile.empty())
|
||||
http.ca_file(m_cafile);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user