mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-04-06 00:32:05 +02:00
3
.github/workflows/build_orca.yml
vendored
3
.github/workflows/build_orca.yml
vendored
@@ -22,6 +22,7 @@ jobs:
|
|||||||
date:
|
date:
|
||||||
ver:
|
ver:
|
||||||
ver_pure:
|
ver_pure:
|
||||||
|
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -317,7 +318,7 @@ jobs:
|
|||||||
- name: Install dependencies from build_linux.sh
|
- name: Install dependencies from build_linux.sh
|
||||||
if: inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04'
|
if: inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: sudo ./build_linux.sh -ur
|
run: sudo env "ORCA_UPDATER_SIG_KEY=$ORCA_UPDATER_SIG_KEY" ./build_linux.sh -ur
|
||||||
|
|
||||||
- name: Fix permissions
|
- name: Fix permissions
|
||||||
if: inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04'
|
if: inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04'
|
||||||
|
|||||||
23
AGENTS.md
Normal file
23
AGENTS.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Repository Guidelines
|
||||||
|
|
||||||
|
## Project Structure & Module Organization
|
||||||
|
OrcaSlicer’s C++17 sources live in `src/`, split by feature modules and platform adapters. User assets, icons, and printer presets are in `resources/`; translations stay in `localization/`. Tests sit in `tests/`, grouped by domain (`libslic3r/`, `sla_print/`, etc.) with fixtures under `tests/data/`. CMake helpers reside in `cmake/`, and longer references in `doc/` and `SoftFever_doc/`. Automation scripts belong in `scripts/` and `tools/`. Treat everything in `deps/` and `deps_src/` as vendored snapshots—do not modify without mirroring upstream tags.
|
||||||
|
|
||||||
|
## Build, Test, and Development Commands
|
||||||
|
Use out-of-source builds:
|
||||||
|
- `cmake -S . -B build -DCMAKE_BUILD_TYPE=Release` configures dependencies and generates build files.
|
||||||
|
- `cmake --build build --target OrcaSlicer --config Release` compiles the app; add `--parallel` to speed up.
|
||||||
|
- `cmake --build build --target tests` then `ctest --test-dir build --output-on-failure` runs automated suites.
|
||||||
|
Platform helpers such as `build_linux.sh`, `build_release_macos.sh`, and `build_release_vs2022.bat` wrap the same flow with toolchain flags. Use `build_release_macos.sh -sx` when reproducing macOS build issues, and `scripts/DockerBuild.sh` for reproducible container builds.
|
||||||
|
|
||||||
|
## Coding Style & Naming Conventions
|
||||||
|
`.clang-format` enforces 4-space indents, a 140-column limit, aligned initializers, and brace wrapping for classes and functions. Run `clang-format -i <file>` before committing; the CMake `clang-format` target is available when LLVM tools are on your PATH. Prefer `CamelCase` for classes, `snake_case` for functions and locals, and `SCREAMING_CASE` for constants, matching conventions in `src/`. Keep headers self-contained and align include order with the IWYU pragmas.
|
||||||
|
|
||||||
|
## Testing Guidelines
|
||||||
|
Unit tests rely on Catch2 (`tests/catch2/`). Name specs after the component under test—for example `tests/libslic3r/TestPlanarHole.cpp`—and tag long-running cases so `ctest -L fast` remains useful. Cover new algorithms with deterministic fixtures or sample G-code stored in `tests/data/`. Document manual printer validation or regression slicer checks in your PR when automated coverage is insufficient.
|
||||||
|
|
||||||
|
## Commit & Pull Request Guidelines
|
||||||
|
The history favors concise, sentence-style subject lines with optional issue references, e.g., `Fix grid lines origin for multiple plates (#10724)`. Squash fixups locally before opening a PR. Complete `.github/pull_request_template.md`, include reproduction steps or screenshots for UI changes, and mention impacted presets or translations. Link issues via `Closes #NNNN` when applicable, and call out dependency bumps or profile migrations for maintainer review.
|
||||||
|
|
||||||
|
## Security & Configuration Tips
|
||||||
|
Follow `SECURITY.md` for vulnerability reporting. Keep API tokens and printer credentials out of tracked configs; use `sandboxes/` for experimental settings. When touching third-party code in `deps_src/`, record the upstream commit or release in your PR description and run the relevant platform build script to confirm integration.
|
||||||
@@ -225,6 +225,9 @@ if [[ -n "${BUILD_ORCA}" ]] ; then
|
|||||||
if [[ -n "${BUILD_TESTS}" ]] ; then
|
if [[ -n "${BUILD_TESTS}" ]] ; then
|
||||||
BUILD_ARGS+=(-DBUILD_TESTS=ON)
|
BUILD_ARGS+=(-DBUILD_TESTS=ON)
|
||||||
fi
|
fi
|
||||||
|
if [[ -n "${ORCA_UPDATER_SIG_KEY}" ]] ; then
|
||||||
|
BUILD_ARGS+=(-DORCA_UPDATER_SIG_KEY="${ORCA_UPDATER_SIG_KEY}")
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Configuring OrcaSlicer..."
|
echo "Configuring OrcaSlicer..."
|
||||||
set -x
|
set -x
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ cd deps
|
|||||||
mkdir %build_dir%
|
mkdir %build_dir%
|
||||||
cd %build_dir%
|
cd %build_dir%
|
||||||
set DEPS=%CD%/OrcaSlicer_dep
|
set DEPS=%CD%/OrcaSlicer_dep
|
||||||
|
set "SIG_FLAG="
|
||||||
|
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
|
||||||
if "%1"=="slicer" (
|
if "%1"=="slicer" (
|
||||||
GOTO :slicer
|
GOTO :slicer
|
||||||
)
|
)
|
||||||
@@ -42,7 +44,7 @@ mkdir %build_dir%
|
|||||||
cd %build_dir%
|
cd %build_dir%
|
||||||
|
|
||||||
echo cmake .. -G "Visual Studio 16 2019" -A x64 -DBBL_RELEASE_TO_PUBLIC=1 -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./OrcaSlicer" -DCMAKE_BUILD_TYPE=%build_type%
|
echo cmake .. -G "Visual Studio 16 2019" -A x64 -DBBL_RELEASE_TO_PUBLIC=1 -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./OrcaSlicer" -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
cmake .. -G "Visual Studio 16 2019" -A x64 -DBBL_RELEASE_TO_PUBLIC=1 -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./OrcaSlicer" -DCMAKE_BUILD_TYPE=%build_type% -DWIN10SDK_PATH="C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0"
|
cmake .. -G "Visual Studio 16 2019" -A x64 -DBBL_RELEASE_TO_PUBLIC=1 %SIG_FLAG% -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./OrcaSlicer" -DCMAKE_BUILD_TYPE=%build_type% -DWIN10SDK_PATH="C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0"
|
||||||
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||||
cd ..
|
cd ..
|
||||||
call scripts/run_gettext.bat
|
call scripts/run_gettext.bat
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ function build_slicer() {
|
|||||||
-G "${SLICER_CMAKE_GENERATOR}" \
|
-G "${SLICER_CMAKE_GENERATOR}" \
|
||||||
-DBBL_RELEASE_TO_PUBLIC=1 \
|
-DBBL_RELEASE_TO_PUBLIC=1 \
|
||||||
-DORCA_TOOLS=ON \
|
-DORCA_TOOLS=ON \
|
||||||
|
${ORCA_UPDATER_SIG_KEY:+-DORCA_UPDATER_SIG_KEY="$ORCA_UPDATER_SIG_KEY"} \
|
||||||
-DCMAKE_PREFIX_PATH="$DEPS/usr/local" \
|
-DCMAKE_PREFIX_PATH="$DEPS/usr/local" \
|
||||||
-DCMAKE_INSTALL_PREFIX="$PWD/OrcaSlicer" \
|
-DCMAKE_INSTALL_PREFIX="$PWD/OrcaSlicer" \
|
||||||
-DCMAKE_BUILD_TYPE="$BUILD_CONFIG" \
|
-DCMAKE_BUILD_TYPE="$BUILD_CONFIG" \
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ cd deps
|
|||||||
mkdir %build_dir%
|
mkdir %build_dir%
|
||||||
cd %build_dir%
|
cd %build_dir%
|
||||||
set DEPS=%CD%/OrcaSlicer_dep
|
set DEPS=%CD%/OrcaSlicer_dep
|
||||||
|
set "SIG_FLAG="
|
||||||
|
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
|
||||||
|
|
||||||
if "%1"=="slicer" (
|
if "%1"=="slicer" (
|
||||||
GOTO :slicer
|
GOTO :slicer
|
||||||
@@ -58,7 +60,7 @@ mkdir %build_dir%
|
|||||||
cd %build_dir%
|
cd %build_dir%
|
||||||
|
|
||||||
echo on
|
echo on
|
||||||
cmake .. -G "Visual Studio 17 2022" -A x64 -DBBL_RELEASE_TO_PUBLIC=1 -DORCA_TOOLS=ON -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./OrcaSlicer" -DCMAKE_BUILD_TYPE=%build_type% -DWIN10SDK_PATH="%WindowsSdkDir%Include\%WindowsSDKVersion%\"
|
cmake .. -G "Visual Studio 17 2022" -A x64 -DBBL_RELEASE_TO_PUBLIC=1 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_PREFIX_PATH="%DEPS%/usr/local" -DCMAKE_INSTALL_PREFIX="./OrcaSlicer" -DCMAKE_BUILD_TYPE=%build_type% -DWIN10SDK_PATH="%WindowsSdkDir%Include\%WindowsSDKVersion%\"
|
||||||
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||||
@echo off
|
@echo off
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ using namespace nlohmann;
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
static const std::string VERSION_CHECK_URL_STABLE = "https://api.github.com/repos/softfever/OrcaSlicer/releases/latest";
|
static const std::string VERSION_CHECK_URL = "https://check-version.orcaslicer.com/latest";
|
||||||
static const std::string VERSION_CHECK_URL = "https://api.github.com/repos/softfever/OrcaSlicer/releases";
|
|
||||||
static const std::string PROFILE_UPDATE_URL = "https://api.github.com/repos/OrcaSlicer/orcaslicer-profiles/releases/tags";
|
static const std::string PROFILE_UPDATE_URL = "https://api.github.com/repos/OrcaSlicer/orcaslicer-profiles/releases/tags";
|
||||||
static const std::string MODELS_STR = "models";
|
static const std::string MODELS_STR = "models";
|
||||||
|
|
||||||
@@ -1383,10 +1382,10 @@ std::string AppConfig::config_path()
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AppConfig::version_check_url(bool stable_only/* = false*/) const
|
std::string AppConfig::version_check_url() const
|
||||||
{
|
{
|
||||||
auto from_settings = get("version_check_url");
|
auto from_settings = get("version_check_url");
|
||||||
return from_settings.empty() ? stable_only ? VERSION_CHECK_URL_STABLE : VERSION_CHECK_URL : from_settings;
|
return from_settings.empty() ? VERSION_CHECK_URL : from_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AppConfig::profile_update_url() const
|
std::string AppConfig::profile_update_url() const
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ public:
|
|||||||
|
|
||||||
// Get the Slic3r version check url.
|
// Get the Slic3r version check url.
|
||||||
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
|
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
|
||||||
std::string version_check_url(bool stable_only = false) const;
|
std::string version_check_url() const;
|
||||||
|
|
||||||
// Get the Orca profile update url.
|
// Get the Orca profile update url.
|
||||||
std::string profile_update_url() const;
|
std::string profile_update_url() const;
|
||||||
|
|||||||
@@ -537,6 +537,8 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
Utils/HexFile.hpp
|
Utils/HexFile.hpp
|
||||||
Utils/Http.cpp
|
Utils/Http.cpp
|
||||||
Utils/Http.hpp
|
Utils/Http.hpp
|
||||||
|
Utils/InstanceID.cpp
|
||||||
|
Utils/InstanceID.hpp
|
||||||
Utils/json_diff.cpp
|
Utils/json_diff.cpp
|
||||||
Utils/json_diff.hpp
|
Utils/json_diff.hpp
|
||||||
Utils/minilzo_extension.cpp
|
Utils/minilzo_extension.cpp
|
||||||
@@ -615,11 +617,28 @@ if (UNIX AND NOT APPLE)
|
|||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
set(ORCA_UPDATER_SIG_KEY_B64 "${ORCA_UPDATER_SIG_KEY}")
|
||||||
|
string(STRIP "${ORCA_UPDATER_SIG_KEY_B64}" ORCA_UPDATER_SIG_KEY_B64)
|
||||||
|
string(REPLACE "\n" "" ORCA_UPDATER_SIG_KEY_B64 "${ORCA_UPDATER_SIG_KEY_B64}")
|
||||||
|
string(REPLACE "\r" "" ORCA_UPDATER_SIG_KEY_B64 "${ORCA_UPDATER_SIG_KEY_B64}")
|
||||||
|
string(REPLACE "\t" "" ORCA_UPDATER_SIG_KEY_B64 "${ORCA_UPDATER_SIG_KEY_B64}")
|
||||||
|
string(REPLACE " " "" ORCA_UPDATER_SIG_KEY_B64 "${ORCA_UPDATER_SIG_KEY_B64}")
|
||||||
|
|
||||||
|
set(ORCA_UPDATER_SIG_KEY_AVAILABLE 0)
|
||||||
|
if(ORCA_UPDATER_SIG_KEY_B64)
|
||||||
|
set(ORCA_UPDATER_SIG_KEY_AVAILABLE 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/GeneratedConfig.hpp.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/GeneratedConfig.hpp
|
||||||
|
@ONLY)
|
||||||
|
|
||||||
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
||||||
target_include_directories(libslic3r_gui PRIVATE Utils)
|
target_include_directories(libslic3r_gui PRIVATE Utils ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/WebView2/include)
|
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../deps/WebView2/include)
|
||||||
|
target_link_libraries(libslic3r_gui Advapi32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
|
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
|
||||||
@@ -645,7 +664,7 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|||||||
${CURL_LIBRARIES}
|
${CURL_LIBRARIES}
|
||||||
)
|
)
|
||||||
elseif (APPLE)
|
elseif (APPLE)
|
||||||
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
|
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY} "-framework Security")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (SLIC3R_STATIC)
|
if (SLIC3R_STATIC)
|
||||||
@@ -670,7 +689,9 @@ endif ()
|
|||||||
# layer and sub-libraries. This forces us to use the include locations and
|
# layer and sub-libraries. This forces us to use the include locations and
|
||||||
# link these libraries.
|
# link these libraries.
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
find_package(GTK${SLIC3R_GTK} REQUIRED)
|
||||||
|
pkg_check_modules(LIBSECRET REQUIRED libsecret-1)
|
||||||
if (FLATPAK)
|
if (FLATPAK)
|
||||||
# I don't know why this is needed, but for whatever reason slic3r isn't
|
# I don't know why this is needed, but for whatever reason slic3r isn't
|
||||||
# linking to X11 and webkit2gtk. force it.
|
# linking to X11 and webkit2gtk. force it.
|
||||||
@@ -679,8 +700,8 @@ if (UNIX AND NOT APPLE)
|
|||||||
pkg_check_modules(webkit2gtk REQUIRED webkit2gtk-4.1)
|
pkg_check_modules(webkit2gtk REQUIRED webkit2gtk-4.1)
|
||||||
target_link_libraries (libslic3r_gui ${X11_LIBRARIES} ${webkit2gtk_LIBRARIES})
|
target_link_libraries (libslic3r_gui ${X11_LIBRARIES} ${webkit2gtk_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS})
|
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${GTK${SLIC3R_GTK}_INCLUDE_DIRS} ${LIBSECRET_INCLUDE_DIRS})
|
||||||
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig)
|
target_link_libraries(libslic3r_gui ${GTK${SLIC3R_GTK}_LIBRARIES} fontconfig ${LIBSECRET_LIBRARIES})
|
||||||
|
|
||||||
|
|
||||||
# We add GStreamer for bambu:/// support.
|
# We add GStreamer for bambu:/// support.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <boost/nowide/convert.hpp>
|
#include <boost/nowide/convert.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
#include <boost/beast/core/detail/base64.hpp>
|
||||||
|
|
||||||
#include <wx/stdpaths.h>
|
#include <wx/stdpaths.h>
|
||||||
#include <wx/imagpng.h>
|
#include <wx/imagpng.h>
|
||||||
@@ -54,6 +55,9 @@
|
|||||||
#include <wx/splash.h>
|
#include <wx/splash.h>
|
||||||
#include <wx/fontutil.h>
|
#include <wx/fontutil.h>
|
||||||
#include <wx/glcanvas.h>
|
#include <wx/glcanvas.h>
|
||||||
|
#include <wx/utils.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
@@ -70,12 +74,14 @@
|
|||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
#include "GLCanvas3D.hpp"
|
#include "GLCanvas3D.hpp"
|
||||||
|
#include "GeneratedConfig.hpp"
|
||||||
|
|
||||||
#include "../Utils/PresetUpdater.hpp"
|
#include "../Utils/PresetUpdater.hpp"
|
||||||
#include "../Utils/PrintHost.hpp"
|
#include "../Utils/PrintHost.hpp"
|
||||||
#include "../Utils/Process.hpp"
|
#include "../Utils/Process.hpp"
|
||||||
#include "../Utils/MacDarkMode.hpp"
|
#include "../Utils/MacDarkMode.hpp"
|
||||||
#include "../Utils/Http.hpp"
|
#include "../Utils/Http.hpp"
|
||||||
|
#include "../Utils/InstanceID.hpp"
|
||||||
#include "../Utils/UndoRedo.hpp"
|
#include "../Utils/UndoRedo.hpp"
|
||||||
#include "slic3r/Config/Snapshot.hpp"
|
#include "slic3r/Config/Snapshot.hpp"
|
||||||
#include "Preferences.hpp"
|
#include "Preferences.hpp"
|
||||||
@@ -4381,108 +4387,414 @@ Semver get_version(const std::string& str, const std::regex& regexp) {
|
|||||||
return Semver::invalid();
|
return Semver::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct UpdaterQuery
|
||||||
|
{
|
||||||
|
std::string iid;
|
||||||
|
std::string version;
|
||||||
|
std::string os;
|
||||||
|
std::string arch;
|
||||||
|
std::string os_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string detect_updater_os()
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
return "win";
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return "macos";
|
||||||
|
#elif defined(__linux__) || defined(__LINUX__)
|
||||||
|
return "linux";
|
||||||
|
#else
|
||||||
|
return "unknown";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string detect_updater_arch()
|
||||||
|
{
|
||||||
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
return "arm64";
|
||||||
|
#elif defined(__x86_64__) || defined(_M_X64)
|
||||||
|
return "x86_64";
|
||||||
|
#elif defined(__i386__) || defined(_M_IX86)
|
||||||
|
return "i386";
|
||||||
|
#else
|
||||||
|
std::string arch = wxPlatformInfo::Get().GetArchName().ToStdString();
|
||||||
|
boost::algorithm::to_lower(arch);
|
||||||
|
if (arch.find("aarch64") != std::string::npos || arch.find("arm64") != std::string::npos)
|
||||||
|
return "arm64";
|
||||||
|
if (arch.find("x86_64") != std::string::npos || arch.find("amd64") != std::string::npos)
|
||||||
|
return "x86_64";
|
||||||
|
if (arch.find("i686") != std::string::npos || arch.find("i386") != std::string::npos || arch.find("x86") != std::string::npos)
|
||||||
|
return "i386";
|
||||||
|
return "unknown";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string detect_updater_os_info()
|
||||||
|
{
|
||||||
|
wxString description = wxPlatformInfo::Get().GetOperatingSystemDescription();
|
||||||
|
#if defined(__LINUX__) || defined(__linux__)
|
||||||
|
wxLinuxDistributionInfo distro = wxGetLinuxDistributionInfo();
|
||||||
|
if (!distro.Id.empty()) {
|
||||||
|
wxString normalized = distro.Id;
|
||||||
|
if (!distro.Release.empty())
|
||||||
|
normalized << " " << distro.Release;
|
||||||
|
normalized.Trim(true);
|
||||||
|
normalized.Trim(false);
|
||||||
|
if (!normalized.empty())
|
||||||
|
description = normalized;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (description.empty())
|
||||||
|
description = wxGetOsDescription();
|
||||||
|
|
||||||
|
//Orca: workaround: wxGetOsVersion can't recognize Windows 11
|
||||||
|
// For Windows, use actual version numbers to properly detect Windows 11
|
||||||
|
// Windows 11 starts at build 22000
|
||||||
|
#if defined(_WIN32)
|
||||||
|
int major = 0, minor = 0, micro = 0;
|
||||||
|
wxGetOsVersion(&major, &minor, µ);
|
||||||
|
if (micro >= 22000) {
|
||||||
|
// replace Windows 10 with Windows 11
|
||||||
|
description.Replace("Windows 10", "Windows 11");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::string os_info = description.ToStdString();
|
||||||
|
boost::replace_all(os_info, "\r", " ");
|
||||||
|
boost::replace_all(os_info, "\n", " ");
|
||||||
|
boost::algorithm::trim(os_info);
|
||||||
|
if (os_info.size() > 120)
|
||||||
|
os_info.resize(120);
|
||||||
|
boost::algorithm::to_lower(os_info);
|
||||||
|
return os_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string detect_updater_version()
|
||||||
|
{
|
||||||
|
return SoftFever_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string detect_updater_iid(AppConfig* config)
|
||||||
|
{
|
||||||
|
if (config == nullptr)
|
||||||
|
return {};
|
||||||
|
return instance_id::ensure(*config);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string encode_uri_component(const std::string& value)
|
||||||
|
{
|
||||||
|
static constexpr const char* hex = "0123456789ABCDEF";
|
||||||
|
std::string out;
|
||||||
|
out.reserve(value.size());
|
||||||
|
for (unsigned char ch : value) {
|
||||||
|
if ((ch >= 'A' && ch <= 'Z') ||
|
||||||
|
(ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= '0' && ch <= '9') ||
|
||||||
|
ch == '-' || ch == '_' || ch == '.' || ch == '~' ||
|
||||||
|
ch == '!' || ch == '*' || ch == '(' || ch == ')' || ch == '\'') {
|
||||||
|
out.push_back(static_cast<char>(ch));
|
||||||
|
} else {
|
||||||
|
out.push_back('%');
|
||||||
|
out.push_back(hex[(ch >> 4) & 0xF]);
|
||||||
|
out.push_back(hex[ch & 0xF]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_updater_query(const UpdaterQuery& query)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> params;
|
||||||
|
|
||||||
|
auto add_param = [¶ms](const char* key, const std::string& value) {
|
||||||
|
if (!value.empty())
|
||||||
|
params.emplace_back(key, encode_uri_component(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
add_param("iid", query.iid);
|
||||||
|
add_param("v", query.version);
|
||||||
|
add_param("os", query.os);
|
||||||
|
add_param("arch", query.arch);
|
||||||
|
add_param("os_info", query.os_info);
|
||||||
|
|
||||||
|
std::sort(params.begin(), params.end(), [](const auto& lhs, const auto& rhs) {
|
||||||
|
return lhs.first < rhs.first;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (params.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::string encoded;
|
||||||
|
for (size_t idx = 0; idx < params.size(); ++idx) {
|
||||||
|
if (idx > 0)
|
||||||
|
encoded.push_back('&');
|
||||||
|
encoded += params[idx].first;
|
||||||
|
encoded.push_back('=');
|
||||||
|
encoded += params[idx].second;
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base64url_encode(const unsigned char* data, std::size_t length)
|
||||||
|
{
|
||||||
|
std::string encoded;
|
||||||
|
encoded.resize(boost::beast::detail::base64::encoded_size(length));
|
||||||
|
encoded.resize(boost::beast::detail::base64::encode(encoded.data(), data, length));
|
||||||
|
std::replace(encoded.begin(), encoded.end(), '+', '-');
|
||||||
|
std::replace(encoded.begin(), encoded.end(), '/', '_');
|
||||||
|
while (!encoded.empty() && encoded.back() == '=')
|
||||||
|
encoded.pop_back();
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<unsigned char>> load_signature_key()
|
||||||
|
{
|
||||||
|
#if ORCA_UPDATER_SIG_KEY_AVAILABLE
|
||||||
|
std::string key = ORCA_UPDATER_SIG_KEY_B64;
|
||||||
|
boost::algorithm::trim(key);
|
||||||
|
if (key.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
key.erase(std::remove_if(key.begin(), key.end(), [](unsigned char ch) { return std::isspace(ch); }), key.end());
|
||||||
|
std::replace(key.begin(), key.end(), '-', '+');
|
||||||
|
std::replace(key.begin(), key.end(), '_', '/');
|
||||||
|
while (key.size() % 4 != 0)
|
||||||
|
key.push_back('=');
|
||||||
|
|
||||||
|
std::string decoded;
|
||||||
|
decoded.resize(boost::beast::detail::base64::decoded_size(key.size()));
|
||||||
|
auto decode_result = boost::beast::detail::base64::decode(decoded.data(), key.data(), key.size());
|
||||||
|
if (!decode_result.second)
|
||||||
|
return std::nullopt;
|
||||||
|
decoded.resize(decode_result.first);
|
||||||
|
|
||||||
|
return std::vector<unsigned char>(decoded.begin(), decoded.end());
|
||||||
|
#else
|
||||||
|
return std::nullopt;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<std::vector<unsigned char>>& get_signature_key()
|
||||||
|
{
|
||||||
|
static std::optional<std::vector<unsigned char>> cached;
|
||||||
|
static bool loaded = false;
|
||||||
|
if (!loaded) {
|
||||||
|
cached = load_signature_key();
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string extract_path_from_url(const std::string& url)
|
||||||
|
{
|
||||||
|
if (url.empty())
|
||||||
|
return "/latest";
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
const auto scheme_pos = url.find("://");
|
||||||
|
if (scheme_pos != std::string::npos) {
|
||||||
|
const auto path_pos = url.find('/', scheme_pos + 3);
|
||||||
|
if (path_pos != std::string::npos)
|
||||||
|
path = url.substr(path_pos);
|
||||||
|
else
|
||||||
|
path = "/";
|
||||||
|
} else {
|
||||||
|
path = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fragment_pos = path.find('#');
|
||||||
|
if (fragment_pos != std::string::npos)
|
||||||
|
path = path.substr(0, fragment_pos);
|
||||||
|
|
||||||
|
const auto query_pos = path.find('?');
|
||||||
|
if (query_pos != std::string::npos)
|
||||||
|
path = path.substr(0, query_pos);
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
path = "/";
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void maybe_attach_updater_signature(Http& http, const std::string& canonical_query, const std::string& request_url)
|
||||||
|
{
|
||||||
|
if (canonical_query.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto& key = get_signature_key();
|
||||||
|
if (!key || key->empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
|
||||||
|
const std::string timestamp = std::to_string(now.time_since_epoch().count());
|
||||||
|
const std::string path = extract_path_from_url(request_url);
|
||||||
|
|
||||||
|
std::string string_to_sign = "GET\n";
|
||||||
|
string_to_sign += path;
|
||||||
|
string_to_sign += "\n";
|
||||||
|
string_to_sign += canonical_query;
|
||||||
|
string_to_sign += "\n";
|
||||||
|
string_to_sign += timestamp;
|
||||||
|
|
||||||
|
unsigned int digest_length = 0;
|
||||||
|
unsigned char digest[EVP_MAX_MD_SIZE] = {};
|
||||||
|
if (HMAC(EVP_sha256(), key->data(), static_cast<int>(key->size()),
|
||||||
|
reinterpret_cast<const unsigned char*>(string_to_sign.data()),
|
||||||
|
string_to_sign.size(), digest, &digest_length) == nullptr || digest_length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string signature = base64url_encode(digest, digest_length);
|
||||||
|
http.header("X-Orca-Ts", timestamp);
|
||||||
|
http.header("X-Orca-Sig", "v1:" + signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
||||||
{
|
{
|
||||||
AppConfig* app_config = wxGetApp().app_config;
|
AppConfig* app_config = wxGetApp().app_config;
|
||||||
bool check_stable_only = app_config->get_bool("check_stable_update_only");
|
bool check_stable_only = app_config->get_bool("check_stable_update_only");
|
||||||
auto version_check_url = app_config->version_check_url(check_stable_only);
|
auto version_check_url = app_config->version_check_url();
|
||||||
Http::get(version_check_url)
|
|
||||||
|
UpdaterQuery query{
|
||||||
|
detect_updater_iid(app_config),
|
||||||
|
detect_updater_version(),
|
||||||
|
detect_updater_os(),
|
||||||
|
detect_updater_arch(),
|
||||||
|
detect_updater_os_info()
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string query_string = build_updater_query(query);
|
||||||
|
if (!query_string.empty()) {
|
||||||
|
const bool has_query = version_check_url.find('?') != std::string::npos;
|
||||||
|
if (!has_query)
|
||||||
|
version_check_url.push_back('?');
|
||||||
|
else if (!version_check_url.empty() && version_check_url.back() != '&' && version_check_url.back() != '?')
|
||||||
|
version_check_url.push_back('&');
|
||||||
|
version_check_url += query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto http = Http::get(version_check_url);
|
||||||
|
maybe_attach_updater_signature(http, query_string, version_check_url);
|
||||||
|
|
||||||
|
http.header("accept", "application/vnd.github.v3+json")
|
||||||
|
.timeout_connect(5)
|
||||||
|
.timeout_max(10)
|
||||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||||
(void)body;
|
(void)body;
|
||||||
BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%", "check_new_version_sf", http_status,
|
BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%", "check_new_version_sf", http_status,
|
||||||
error);
|
error);
|
||||||
})
|
})
|
||||||
.timeout_connect(1)
|
.on_complete([this, by_user, check_stable_only](std::string body, unsigned http_status) {
|
||||||
.on_complete([this,by_user, check_stable_only](std::string body, unsigned http_status) {
|
|
||||||
// Http response OK
|
|
||||||
if (http_status != 200)
|
if (http_status != 200)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
boost::trim(body);
|
boost::trim(body);
|
||||||
// Orca: parse github release, inspired by SS
|
if (body.empty()) {
|
||||||
boost::property_tree::ptree root;
|
|
||||||
std::stringstream json_stream(body);
|
|
||||||
boost::property_tree::read_json(json_stream, root);
|
|
||||||
|
|
||||||
// at least two number, use '.' as separator. can be followed by -Az23 for prereleased and +Az42 for
|
|
||||||
// metadata
|
|
||||||
std::regex matcher("[0-9]+\\.[0-9]+(\\.[0-9]+)*(-[A-Za-z0-9]+)?(\\+[A-Za-z0-9]+)?");
|
|
||||||
|
|
||||||
Semver current_version = get_version(SoftFever_VERSION, matcher);
|
|
||||||
Semver best_pre(1, 0, 0);
|
|
||||||
Semver best_release(1, 0, 0);
|
|
||||||
std::string best_pre_url;
|
|
||||||
std::string best_release_url;
|
|
||||||
std::string best_release_content;
|
|
||||||
std::string best_pre_content;
|
|
||||||
const std::regex reg_num("([0-9]+)");
|
|
||||||
if (check_stable_only) {
|
|
||||||
std::string tag = root.get<std::string>("tag_name");
|
|
||||||
if (tag[0] == 'v')
|
|
||||||
tag.erase(0, 1);
|
|
||||||
for (std::regex_iterator it = std::sregex_iterator(tag.begin(), tag.end(), reg_num); it != std::sregex_iterator(); ++it) {}
|
|
||||||
Semver tag_version = get_version(tag, matcher);
|
|
||||||
if (root.get<bool>("prerelease")) {
|
|
||||||
if (best_pre < tag_version) {
|
|
||||||
best_pre = tag_version;
|
|
||||||
best_pre_url = root.get<std::string>("html_url");
|
|
||||||
best_pre_content = root.get<std::string>("body");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (best_release < tag_version) {
|
|
||||||
best_release = tag_version;
|
|
||||||
best_release_url = root.get<std::string>("html_url");
|
|
||||||
best_release_content = root.get<std::string>("body");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto json_version : root) {
|
|
||||||
std::string tag = json_version.second.get<std::string>("tag_name");
|
|
||||||
if (tag[0] == 'v')
|
|
||||||
tag.erase(0, 1);
|
|
||||||
for (std::regex_iterator it = std::sregex_iterator(tag.begin(), tag.end(), reg_num); it != std::sregex_iterator();
|
|
||||||
++it) {}
|
|
||||||
Semver tag_version = get_version(tag, matcher);
|
|
||||||
if (json_version.second.get<bool>("prerelease")) {
|
|
||||||
if (best_pre < tag_version) {
|
|
||||||
best_pre = tag_version;
|
|
||||||
best_pre_url = json_version.second.get<std::string>("html_url");
|
|
||||||
best_pre_content = json_version.second.get<std::string>("body");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (best_release < tag_version) {
|
|
||||||
best_release = tag_version;
|
|
||||||
best_release_url = json_version.second.get<std::string>("html_url");
|
|
||||||
best_release_content = json_version.second.get<std::string>("body");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if release is more recent than beta, use release anyway
|
|
||||||
if (best_pre < best_release) {
|
|
||||||
best_pre = best_release;
|
|
||||||
best_pre_url = best_release_url;
|
|
||||||
best_pre_content = best_release_content;
|
|
||||||
}
|
|
||||||
// if we're the most recent, don't do anything
|
|
||||||
if ((check_stable_only ? best_release : best_pre) <= current_version) {
|
|
||||||
if (by_user != 0)
|
if (by_user != 0)
|
||||||
this->no_new_version();
|
this->no_new_version();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
version_info.url = check_stable_only ? best_release_url : best_pre_url;
|
boost::property_tree::ptree root;
|
||||||
version_info.version_str = check_stable_only ? best_release.to_string_sf() : best_pre.to_string();
|
std::stringstream json_stream(body);
|
||||||
version_info.description = check_stable_only ? best_release_content : best_pre_content;
|
boost::property_tree::read_json(json_stream, root);
|
||||||
|
|
||||||
|
std::regex matcher("[0-9]+\\.[0-9]+(\\.[0-9]+)*(-[A-Za-z0-9]+)?(\\+[A-Za-z0-9]+)?");
|
||||||
|
Semver current_version = get_version(SoftFever_VERSION, matcher);
|
||||||
|
Semver best_pre(0, 0, 0);
|
||||||
|
Semver best_release(0, 0, 0);
|
||||||
|
bool best_pre_valid = false;
|
||||||
|
bool best_release_valid = false;
|
||||||
|
std::string best_pre_url;
|
||||||
|
std::string best_release_url;
|
||||||
|
std::string best_release_content;
|
||||||
|
std::string best_pre_content;
|
||||||
|
|
||||||
|
auto consider_release = [&](const boost::property_tree::ptree& node) {
|
||||||
|
auto tag_opt = node.get_optional<std::string>("tag_name");
|
||||||
|
if (!tag_opt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string tag = *tag_opt;
|
||||||
|
if (!tag.empty() && tag.front() == 'v')
|
||||||
|
tag.erase(0, 1);
|
||||||
|
|
||||||
|
Semver tag_version = get_version(tag, matcher);
|
||||||
|
if (!tag_version.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool is_prerelease = node.get_optional<bool>("prerelease").get_value_or(false);
|
||||||
|
const std::string html_url = node.get_optional<std::string>("html_url").get_value_or(std::string());
|
||||||
|
const std::string body_copy = node.get_optional<std::string>("body").get_value_or(std::string());
|
||||||
|
|
||||||
|
if (is_prerelease) {
|
||||||
|
if (!best_pre_valid || best_pre < tag_version) {
|
||||||
|
best_pre = tag_version;
|
||||||
|
best_pre_url = html_url;
|
||||||
|
best_pre_content = body_copy;
|
||||||
|
best_pre_valid = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!best_release_valid || best_release < tag_version) {
|
||||||
|
best_release = tag_version;
|
||||||
|
best_release_url = html_url;
|
||||||
|
best_release_content = body_copy;
|
||||||
|
best_release_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (root.get_optional<std::string>("tag_name")) {
|
||||||
|
consider_release(root);
|
||||||
|
} else {
|
||||||
|
for (const auto& child : root)
|
||||||
|
consider_release(child.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!best_release_valid && !best_pre_valid) {
|
||||||
|
if (by_user != 0)
|
||||||
|
this->no_new_version();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best_pre_valid && best_release_valid && best_pre < best_release) {
|
||||||
|
best_pre = best_release;
|
||||||
|
best_pre_url = best_release_url;
|
||||||
|
best_pre_content = best_release_content;
|
||||||
|
best_pre_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool prefer_release = check_stable_only || !best_pre_valid;
|
||||||
|
const Semver& chosen_version = prefer_release ? best_release : best_pre;
|
||||||
|
const bool chosen_valid = prefer_release ? best_release_valid : best_pre_valid;
|
||||||
|
|
||||||
|
if (!chosen_valid) {
|
||||||
|
if (by_user != 0)
|
||||||
|
this->no_new_version();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_version.valid() && chosen_version <= current_version) {
|
||||||
|
if (by_user != 0)
|
||||||
|
this->no_new_version();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
version_info.url = prefer_release ? best_release_url : best_pre_url;
|
||||||
|
version_info.version_str = prefer_release ? best_release.to_string_sf() : best_pre.to_string();
|
||||||
|
version_info.description = prefer_release ? best_release_content : best_pre_content;
|
||||||
version_info.force_upgrade = false;
|
version_info.force_upgrade = false;
|
||||||
|
|
||||||
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE);
|
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE);
|
||||||
evt->SetString((check_stable_only ? best_release : best_pre).to_string());
|
evt->SetString((prefer_release ? best_release : best_pre).to_string());
|
||||||
GUI::wxGetApp().QueueEvent(evt);
|
GUI::wxGetApp().QueueEvent(evt);
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
})
|
});
|
||||||
.perform();
|
|
||||||
|
http.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUI_App::process_network_msg(std::string dev_id, std::string msg)
|
void GUI_App::process_network_msg(std::string dev_id, std::string msg)
|
||||||
|
|||||||
5
src/slic3r/GeneratedConfig.hpp.in
Normal file
5
src/slic3r/GeneratedConfig.hpp.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define ORCA_UPDATER_SIG_KEY_B64 "@ORCA_UPDATER_SIG_KEY_B64@"
|
||||||
|
#define ORCA_UPDATER_SIG_KEY_AVAILABLE @ORCA_UPDATER_SIG_KEY_AVAILABLE@
|
||||||
|
|
||||||
209
src/slic3r/Utils/InstanceID.cpp
Normal file
209
src/slic3r/Utils/InstanceID.cpp
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
#include "InstanceID.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/uuid/random_generator.hpp>
|
||||||
|
#include <boost/uuid/string_generator.hpp>
|
||||||
|
#include <boost/uuid/uuid.hpp>
|
||||||
|
#include <boost/uuid/uuid_io.hpp>
|
||||||
|
|
||||||
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <boost/nowide/fstream.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace instance_id {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char* CONFIG_KEY = "updater_iid";
|
||||||
|
constexpr const char* LEGACY_KEY = "iid";
|
||||||
|
|
||||||
|
std::mutex& cache_mutex()
|
||||||
|
{
|
||||||
|
static std::mutex mtx;
|
||||||
|
return mtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& cached_iid()
|
||||||
|
{
|
||||||
|
static std::string value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool& cache_ready()
|
||||||
|
{
|
||||||
|
static bool ready = false;
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> normalize_uuid(std::string value)
|
||||||
|
{
|
||||||
|
boost::algorithm::trim(value);
|
||||||
|
boost::algorithm::to_lower(value);
|
||||||
|
if (value.size() != 36)
|
||||||
|
return std::nullopt;
|
||||||
|
try {
|
||||||
|
const boost::uuids::uuid parsed = boost::uuids::string_generator()(value);
|
||||||
|
if (parsed.version() != boost::uuids::uuid::version_random_number_based)
|
||||||
|
return std::nullopt;
|
||||||
|
return value;
|
||||||
|
} catch (...) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> read_config_value(AppConfig& config)
|
||||||
|
{
|
||||||
|
const auto read_key = [&](const char* key) -> std::optional<std::string> {
|
||||||
|
const std::string raw = config.get(key);
|
||||||
|
if (raw.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
if (auto normalized = normalize_uuid(raw))
|
||||||
|
return normalized;
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto value = read_key(CONFIG_KEY))
|
||||||
|
return value;
|
||||||
|
return read_key(LEGACY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_config_value(AppConfig& config, const std::string& value)
|
||||||
|
{
|
||||||
|
config.set(CONFIG_KEY, value);
|
||||||
|
if (config.get(LEGACY_KEY) != value)
|
||||||
|
config.set(LEGACY_KEY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prune_config_value(AppConfig& config)
|
||||||
|
{
|
||||||
|
if (config.has(CONFIG_KEY))
|
||||||
|
config.erase("app", CONFIG_KEY);
|
||||||
|
if (config.has(LEGACY_KEY))
|
||||||
|
config.erase("app", LEGACY_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::filesystem::path storage_path()
|
||||||
|
{
|
||||||
|
const std::string& base_dir = Slic3r::data_dir();
|
||||||
|
if (base_dir.empty())
|
||||||
|
return {};
|
||||||
|
return boost::filesystem::path(base_dir) / ".orcaslicer_machine_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> read_storage_file()
|
||||||
|
{
|
||||||
|
const auto path = storage_path();
|
||||||
|
if (path.empty() || !boost::filesystem::exists(path))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
boost::nowide::ifstream file(path.string());
|
||||||
|
if (!file)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string value;
|
||||||
|
std::getline(file, value);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return normalize_uuid(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_storage_file(const std::string& value)
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto path = storage_path();
|
||||||
|
if (path.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto parent = path.parent_path();
|
||||||
|
boost::system::error_code ec;
|
||||||
|
if (!parent.empty() && !boost::filesystem::exists(parent))
|
||||||
|
boost::filesystem::create_directories(parent, ec);
|
||||||
|
|
||||||
|
if (ec)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boost::nowide::ofstream file(path.string(), std::ios::trunc);
|
||||||
|
if (!file)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
file << value;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> read_secure()
|
||||||
|
{
|
||||||
|
return read_storage_file();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_secure(const std::string& value)
|
||||||
|
{
|
||||||
|
return write_storage_file(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string generate_uuid()
|
||||||
|
{
|
||||||
|
const auto uuid = boost::uuids::random_generator()();
|
||||||
|
return boost::uuids::to_string(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::string ensure(AppConfig& config)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(cache_mutex());
|
||||||
|
if (cache_ready())
|
||||||
|
return cached_iid();
|
||||||
|
|
||||||
|
if (auto secure = read_secure()) {
|
||||||
|
cached_iid() = *secure;
|
||||||
|
cache_ready() = true;
|
||||||
|
prune_config_value(config);
|
||||||
|
return cached_iid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto from_config = read_config_value(config)) {
|
||||||
|
cached_iid() = *from_config;
|
||||||
|
cache_ready() = true;
|
||||||
|
if (!write_secure(cached_iid()))
|
||||||
|
write_config_value(config, cached_iid());
|
||||||
|
else
|
||||||
|
prune_config_value(config);
|
||||||
|
return cached_iid();
|
||||||
|
}
|
||||||
|
|
||||||
|
cached_iid() = generate_uuid();
|
||||||
|
cache_ready() = true;
|
||||||
|
|
||||||
|
if (!write_secure(cached_iid()))
|
||||||
|
write_config_value(config, cached_iid());
|
||||||
|
else
|
||||||
|
prune_config_value(config);
|
||||||
|
|
||||||
|
return cached_iid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_cache_for_tests()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(cache_mutex());
|
||||||
|
cached_iid().clear();
|
||||||
|
cache_ready() = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace instance_id
|
||||||
|
} // namespace Slic3r
|
||||||
20
src/slic3r/Utils/InstanceID.hpp
Normal file
20
src/slic3r/Utils/InstanceID.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class AppConfig;
|
||||||
|
|
||||||
|
namespace instance_id {
|
||||||
|
|
||||||
|
// Returns the canonical IID, generating and storing one when missing.
|
||||||
|
std::string ensure(AppConfig& config);
|
||||||
|
|
||||||
|
// Clears the cached IID (primarily for tests).
|
||||||
|
void reset_cache_for_tests();
|
||||||
|
|
||||||
|
} // namespace instance_id
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
@@ -18,3 +18,7 @@ set(ORCA_VERSION_MINOR ${CMAKE_MATCH_2})
|
|||||||
set(ORCA_VERSION_PATCH ${CMAKE_MATCH_3})
|
set(ORCA_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||||
|
|
||||||
set(SLIC3R_VERSION "01.10.01.50")
|
set(SLIC3R_VERSION "01.10.01.50")
|
||||||
|
|
||||||
|
if (NOT DEFINED ORCA_UPDATER_SIG_KEY)
|
||||||
|
set(ORCA_UPDATER_SIG_KEY "" CACHE STRING "Base64url encoded updater signature key")
|
||||||
|
endif()
|
||||||
|
|||||||
Reference in New Issue
Block a user