OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::anonymous_namespace{PluginFsUtils.cpp} Namespace Reference

Classes

struct  ZipReaderGuard
struct  MzHeapFree

Enumerations

enum class  TomlSection { Root , OrcaPlugin , OrcaPluginSettings , InDepsArray }

Functions

void assign_discovered_plugin_key (PluginDescriptor &descriptor, const boost::filesystem::path &name_source)
void scan_plugin_directory (const std::string &dir_path, std::vector< PluginDescriptor > &out)
bool is_safe_archive_entry_path (const boost::filesystem::path &path)
std::string decode_zip_entry_extra_path (const std::string &extra, const std::string &fallback)
std::string zip_entry_name (mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
std::string normalize_zip_entry_name (std::string entry_name)
bool read_zip_text_file (mz_zip_archive &archive, const char *filename, std::string &out, std::string &error)
std::string unquote_toml_string (const std::string &val)
std::vector< std::string > parse_toml_inline_array (const std::string &val)
bool parse_pep723_toml (const std::string &toml_content, std::vector< std::string > &out_deps, std::string &out_requires_python, std::string &out_name, std::string &out_description, std::string &out_author, std::string &out_version, std::string &error)
std::string normalize_package_name (const std::string &name)
void parse_metadata_rfc822 (const std::string &content, std::string &out_name, std::string &out_version, std::string &out_summary, std::string &out_author, std::string &out_requires_python, std::string &out_import_name, std::vector< std::string > &out_requires_dist, std::string &error)
void apply_install_metadata (PluginInstallState &state, const PluginDescriptor &entry)

Enumeration Type Documentation

◆ TomlSection

enum class Slic3r::anonymous_namespace{PluginFsUtils.cpp}::TomlSection
strong
Enumerator
Root 
OrcaPlugin 
OrcaPluginSettings 
InDepsArray 

Function Documentation

◆ apply_install_metadata()

void Slic3r::anonymous_namespace{PluginFsUtils.cpp}::apply_install_metadata ( PluginInstallState & state,
const PluginDescriptor & entry )
841{
842 state.installed_from = entry.is_cloud_plugin() ? "cloud" : "local";
843 state.installed_version = !entry.installed_version.empty() ? entry.installed_version : entry.version;
844 state.plugin_name = entry.name;
845 state.cloud_uuid = entry.cloud_uuid();
846}
std::string installed_from
Definition PluginFsUtils.hpp:116
std::string cloud_uuid
Definition PluginFsUtils.hpp:119
std::string plugin_name
Definition PluginFsUtils.hpp:118
std::string installed_version
Definition PluginFsUtils.hpp:117
bool is_cloud_plugin() const
Definition PluginDescriptor.hpp:82
std::string version
Definition PluginDescriptor.hpp:58
std::string name
Definition PluginDescriptor.hpp:55
std::string installed_version
Definition PluginDescriptor.hpp:60
std::string cloud_uuid() const
Definition PluginDescriptor.hpp:83

References apply_install_metadata(), Slic3r::PluginDescriptor::cloud_uuid(), Slic3r::PluginInstallState::cloud_uuid, Slic3r::PluginInstallState::installed_from, Slic3r::PluginDescriptor::installed_version, Slic3r::PluginInstallState::installed_version, Slic3r::PluginDescriptor::is_cloud_plugin(), Slic3r::PluginDescriptor::name, Slic3r::PluginInstallState::plugin_name, and Slic3r::PluginDescriptor::version.

Referenced by apply_install_metadata().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ assign_discovered_plugin_key()

void Slic3r::anonymous_namespace{PluginFsUtils.cpp}::assign_discovered_plugin_key ( PluginDescriptor & descriptor,
const boost::filesystem::path & name_source )
147{
148 if (descriptor.is_cloud_plugin())
149 descriptor.plugin_key = descriptor.cloud_uuid();
150 else
151 descriptor.plugin_key = make_local_plugin_key(name_source.stem().string());
152}
std::string make_local_plugin_key(const std::string &stem)
Definition PluginDescriptor.hpp:214
std::string plugin_key
Definition PluginDescriptor.hpp:54

References assign_discovered_plugin_key(), Slic3r::PluginDescriptor::cloud_uuid(), Slic3r::PluginDescriptor::is_cloud_plugin(), Slic3r::make_local_plugin_key(), and Slic3r::PluginDescriptor::plugin_key.

Referenced by assign_discovered_plugin_key(), and scan_plugin_directory().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ decode_zip_entry_extra_path()

std::string Slic3r::anonymous_namespace{PluginFsUtils.cpp}::decode_zip_entry_extra_path ( const std::string & extra,
const std::string & fallback )
298{
299 const char* p = extra.data();
300 const char* e = p + extra.length();
301 while (p + 4 <= e) {
302 const auto len = static_cast<std::uint16_t>(static_cast<unsigned char>(p[2])) |
303 static_cast<std::uint16_t>(static_cast<unsigned char>(p[3]) << 8);
304 if (p[0] == '\x75' && p[1] == '\x70' && len >= 5 && p + 4 + len <= e && p[4] == '\x01')
305 return std::string(p + 9, p + 4 + len);
306 p += 4 + len;
307 }
308
309 return decode_path(fallback.c_str());
310}
std::string decode_path(const char *src)
Definition utils.cpp:1401

References Slic3r::decode_path(), and decode_zip_entry_extra_path().

Referenced by decode_zip_entry_extra_path(), and zip_entry_name().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_safe_archive_entry_path()

bool Slic3r::anonymous_namespace{PluginFsUtils.cpp}::is_safe_archive_entry_path ( const boost::filesystem::path & path)
284{
285 if (!is_safe_relative_path(path))
286 return false;
287
288 for (const auto& part : path) {
289 const std::string token = part.string();
290 if (token.empty() || token == "." || token == "..")
291 return false;
292 }
293
294 return true;
295}
bool is_safe_relative_path(const boost::filesystem::path &path)
Definition PluginFsUtils.cpp:645

References is_safe_archive_entry_path(), and Slic3r::is_safe_relative_path().

Referenced by is_safe_archive_entry_path().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ normalize_package_name()

std::string Slic3r::anonymous_namespace{PluginFsUtils.cpp}::normalize_package_name ( const std::string & name)
547{
548 std::string result;
549 result.reserve(name.size());
550 for (unsigned char ch : name) {
551 if (ch == '-' || ch == '.')
552 result += '_';
553 else
554 result += static_cast<char>(std::tolower(ch));
555 }
556 return result;
557}

References normalize_package_name().

Referenced by normalize_package_name().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ normalize_zip_entry_name()

std::string Slic3r::anonymous_namespace{PluginFsUtils.cpp}::normalize_zip_entry_name ( std::string entry_name)
323{
324 std::replace(entry_name.begin(), entry_name.end(), '\\', '/');
325 while (!entry_name.empty() && entry_name.back() == '/')
326 entry_name.pop_back();
327 return entry_name;
328}

References normalize_zip_entry_name().

Referenced by normalize_zip_entry_name().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_metadata_rfc822()

void Slic3r::anonymous_namespace{PluginFsUtils.cpp}::parse_metadata_rfc822 ( const std::string & content,
std::string & out_name,
std::string & out_version,
std::string & out_summary,
std::string & out_author,
std::string & out_requires_python,
std::string & out_import_name,
std::vector< std::string > & out_requires_dist,
std::string & error )
570{
571 std::istringstream stream(content);
572 std::string line;
573 std::string current_header;
574 std::string current_value;
575
576 auto flush = [&]() {
577 if (current_header.empty())
578 return;
579 std::string lower = current_header;
580 std::transform(lower.begin(), lower.end(), lower.begin(),
581 [](unsigned char c) { return std::tolower(c); });
582
583 if (lower == "name")
584 out_name = current_value;
585 else if (lower == "version")
586 out_version = current_value;
587 else if (lower == "summary")
588 out_summary = current_value;
589 else if (lower == "author")
590 out_author = current_value;
591 else if (lower == "requires-python")
592 out_requires_python = current_value;
593 else if (lower == "import-name")
594 out_import_name = current_value;
595 else if (lower == "requires-dist")
596 out_requires_dist.push_back(current_value);
597
598 current_header.clear();
599 current_value.clear();
600 };
601
602 while (std::getline(stream, line)) {
603 // Blank line after headers marks the start of the body.
604 if (line.empty() && current_header.empty())
605 continue;
606 if (line.empty()) {
607 flush();
608 // Remaining content is the body (description); stop parsing headers.
609 break;
610 }
611
612 // Continuation line.
613 if (line[0] == ' ' || line[0] == '\t') {
614 if (!current_value.empty())
615 current_value += '\n';
616 size_t pos = line.find_first_not_of(" \t");
617 current_value += (pos != std::string::npos) ? line.substr(pos) : "";
618 continue;
619 }
620
621 flush();
622
623 size_t colon = line.find(':');
624 if (colon == std::string::npos)
625 continue;
626
627 current_header = line.substr(0, colon);
628 size_t val_start = colon + 1;
629 while (val_start < line.size() && (line[val_start] == ' ' || line[val_start] == '\t'))
630 ++val_start;
631 current_value = line.substr(val_start);
632 }
633
634 flush();
635}

References parse_metadata_rfc822().

Referenced by parse_metadata_rfc822().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_pep723_toml()

bool Slic3r::anonymous_namespace{PluginFsUtils.cpp}::parse_pep723_toml ( const std::string & toml_content,
std::vector< std::string > & out_deps,
std::string & out_requires_python,
std::string & out_name,
std::string & out_description,
std::string & out_author,
std::string & out_version,
std::string & error )
446{
447 out_deps.clear();
448 out_requires_python.clear();
449 out_name.clear();
450 out_description.clear();
451 out_author.clear();
452 out_version.clear();
453
455
456 std::istringstream stream(toml_content);
457 std::string line;
458
459 while (std::getline(stream, line)) {
460 // Trim leading/trailing whitespace.
461 size_t start = 0;
462 while (start < line.size() && (line[start] == ' ' || line[start] == '\t'))
463 ++start;
464 size_t end = line.size();
465 while (end > start && (line[end - 1] == ' ' || line[end - 1] == '\t'))
466 --end;
467 std::string trimmed = line.substr(start, end - start);
468
469 if (trimmed.empty() || trimmed[0] == '#')
470 continue;
471
472 // TOML section header.
473 if (trimmed[0] == '[') {
474 if (trimmed == "[tool.orcaslicer.plugin]") {
475 section = TomlSection::OrcaPlugin;
476 } else if (trimmed == "[tool.orcaslicer.plugin.settings]") {
477 // Legacy table, superseded by PluginConfig. Recognized so its keys are ignored
478 // rather than falling through to Root, where a stray dependencies/requires-python
479 // key inside it would be parsed as package metadata.
481 } else {
482 section = TomlSection::Root; // Unknown section — skip.
483 }
484 continue;
485 }
486
487 if (section == TomlSection::InDepsArray) {
488 if (trimmed == "]") {
489 section = TomlSection::Root;
490 continue;
491 }
492 std::string val = trimmed;
493 if (!val.empty() && val.back() == ',')
494 val.pop_back();
495 val = unquote_toml_string(val);
496 if (!val.empty())
497 out_deps.push_back(val);
498 continue;
499 }
500
501 // Look for key = value.
502 size_t eq = trimmed.find('=');
503 if (eq == std::string::npos)
504 continue;
505
506 std::string key = trimmed.substr(0, eq);
507 while (!key.empty() && (key.back() == ' ' || key.back() == '\t'))
508 key.pop_back();
509
510 std::string val = trimmed.substr(eq + 1);
511 while (!val.empty() && (val.front() == ' ' || val.front() == '\t'))
512 val.erase(0, 1);
513 // Trim trailing.
514 while (!val.empty() && (val.back() == ' ' || val.back() == '\t'))
515 val.pop_back();
516
517 if (section == TomlSection::Root) {
518 if (key == "requires-python") {
519 out_requires_python = unquote_toml_string(val);
520 } else if (key == "dependencies") {
521 if (val == "[") {
522 section = TomlSection::InDepsArray;
523 } else {
524 // Inline array: dependencies = ["a", "b"]
525 out_deps = parse_toml_inline_array(val);
526 }
527 }
528 } else if (section == TomlSection::OrcaPlugin) {
529 if (key == "name") out_name = unquote_toml_string(val);
530 else if (key == "description") out_description = unquote_toml_string(val);
531 else if (key == "author") out_author = unquote_toml_string(val);
532 else if (key == "version") out_version = unquote_toml_string(val);
533 }
534 }
535
536 // Check for unclosed arrays.
537 if (section == TomlSection::InDepsArray) {
538 error = "PEP 723 metadata: unclosed dependencies array";
539 return false;
540 }
541 return true;
542}
std::string unquote_toml_string(const std::string &val)
Definition PluginFsUtils.cpp:390
TomlSection
Definition PluginFsUtils.cpp:386
std::vector< std::string > parse_toml_inline_array(const std::string &val)
Definition PluginFsUtils.cpp:399

References InDepsArray, OrcaPlugin, OrcaPluginSettings, parse_pep723_toml(), parse_toml_inline_array(), Root, and unquote_toml_string().

Referenced by parse_pep723_toml().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_toml_inline_array()

std::vector< std::string > Slic3r::anonymous_namespace{PluginFsUtils.cpp}::parse_toml_inline_array ( const std::string & val)
400{
401 std::vector<std::string> result;
402 std::string inner = val;
403 // Strip outer brackets.
404 if (!inner.empty() && inner.front() == '[')
405 inner.erase(0, 1);
406 if (!inner.empty() && inner.back() == ']')
407 inner.pop_back();
408
409 // Simple split by comma, strip quotes and whitespace.
410 std::istringstream ss(inner);
411 std::string item;
412 while (std::getline(ss, item, ',')) {
413 // Trim whitespace.
414 size_t s = 0, e = item.size();
415 while (s < e && (item[s] == ' ' || item[s] == '\t')) ++s;
416 while (e > s && (item[e - 1] == ' ' || item[e - 1] == '\t')) --e;
417 item = item.substr(s, e - s);
418 if (!item.empty())
419 result.push_back(unquote_toml_string(item));
420 }
421 return result;
422}

References parse_toml_inline_array(), and unquote_toml_string().

Referenced by parse_pep723_toml(), and parse_toml_inline_array().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_zip_text_file()

bool Slic3r::anonymous_namespace{PluginFsUtils.cpp}::read_zip_text_file ( mz_zip_archive & archive,
const char * filename,
std::string & out,
std::string & error )
373{
374 size_t size = 0;
375 void* data = mz_zip_reader_extract_file_to_heap(&archive, filename, &size, 0);
376 if (!data) {
377 error = std::string("Wheel does not contain ") + filename;
378 return false;
379 }
380 MzHeapFree guard{data};
381 out.assign(static_cast<const char*>(data), size);
382 return true;
383}

References read_zip_text_file().

Referenced by read_zip_text_file().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ scan_plugin_directory()

void Slic3r::anonymous_namespace{PluginFsUtils.cpp}::scan_plugin_directory ( const std::string & dir_path,
std::vector< PluginDescriptor > & out )
155{
156 namespace fs = boost::filesystem;
157
158 if (!fs::exists(dir_path) || !fs::is_directory(dir_path))
159 return;
160
161 BOOST_LOG_TRIVIAL(debug) << "Scanning plugin directory: " << dir_path;
162
163 try {
164 for (fs::directory_iterator it(dir_path); it != fs::directory_iterator(); ++it) {
165 if (!fs::is_directory(it->status()))
166 continue;
167
168 const fs::path plugin_dir = it->path();
169 if (is_ignored_plugin_directory(plugin_dir))
170 continue;
171
172 PluginDescriptor descriptor;
173 descriptor.plugin_root = plugin_dir.string();
174
175 std::string entry_error;
176 const fs::path entry_path = find_installed_plugin_entry(plugin_dir, entry_error);
177
178 // No usable entry file: keep the package as an invalid row so the UI can show it and
179 // its error, rather than dropping it silently.
180 if (entry_path.empty()) {
181 descriptor.set_error(entry_error);
182 descriptor.install_state_valid = read_install_state(plugin_dir, descriptor);
183 assign_discovered_plugin_key(descriptor, plugin_dir);
184 out.push_back(std::move(descriptor));
185 BOOST_LOG_TRIVIAL(warning) << "Invalid plugin package: " << plugin_dir.string() << " - " << out.back().error;
186 continue;
187 }
188
189 std::string meta_error;
190 const bool is_wheel = entry_path.extension() == ".whl";
191 const bool parsed = is_wheel ? read_wheel_plugin_metadata(entry_path, descriptor, meta_error) :
192 read_python_plugin_metadata(entry_path, descriptor, meta_error);
193 if (!parsed) {
194 descriptor.set_error(meta_error);
195 descriptor.install_state_valid = read_install_state(plugin_dir, descriptor);
196 assign_discovered_plugin_key(descriptor, entry_path);
197 out.push_back(std::move(descriptor));
198 BOOST_LOG_TRIVIAL(warning) << (is_wheel ? "Invalid wheel plugin: " : "Invalid .py plugin: ")
199 << plugin_dir.string() << " - " << out.back().error;
200 continue;
201 }
202
203 descriptor.entry_path = entry_path.string();
204 descriptor.set_metadata_valid(true);
205 descriptor.clear_error();
206
207 // Cloud identity and the package-level auto-load flag. plugin_key is always derived
208 // below, never read from the sidecar.
209 descriptor.install_state_valid = read_install_state(plugin_dir, descriptor);
210 assign_discovered_plugin_key(descriptor, entry_path);
211
212 out.push_back(std::move(descriptor));
213 BOOST_LOG_TRIVIAL(info) << "Discovered plugin: " << out.back().name << " (version: " << out.back().version << ")";
214 }
215 } catch (const std::exception& ex) {
216 BOOST_LOG_TRIVIAL(error) << "Error scanning directory " << dir_path << ": " << ex.what();
217 }
218}
void assign_discovered_plugin_key(PluginDescriptor &descriptor, const boost::filesystem::path &name_source)
Definition PluginFsUtils.cpp:146
bool is_ignored_plugin_directory(const boost::filesystem::path &path)
Definition PluginFsUtils.cpp:639
bool read_python_plugin_metadata(const boost::filesystem::path &py_path, PluginDescriptor &descriptor, std::string &error)
Definition PluginFsUtils.cpp:919
bool read_wheel_plugin_metadata(const boost::filesystem::path &whl_path, PluginDescriptor &descriptor, std::string &error)
Definition PluginFsUtils.cpp:1004
bool read_install_state(const boost::filesystem::path &plugin_dir, PluginDescriptor &entry)
Definition PluginFsUtils.cpp:738
boost::filesystem::path find_installed_plugin_entry(const boost::filesystem::path &plugin_dir, std::string &error)
Definition PluginFsUtils.cpp:1209
Definition Utils.hpp:97
Definition PluginDescriptor.hpp:53
void set_metadata_valid(bool is_valid)
Definition PluginDescriptor.hpp:129
std::string entry_path
Definition PluginDescriptor.hpp:63
void set_error(std::string message)
Definition PluginDescriptor.hpp:146
std::string plugin_root
Definition PluginDescriptor.hpp:62
void clear_error()
Definition PluginDescriptor.hpp:145
bool install_state_valid
Definition PluginDescriptor.hpp:74

References assign_discovered_plugin_key(), Slic3r::PluginDescriptor::clear_error(), Slic3r::PluginDescriptor::entry_path, Slic3r::find_installed_plugin_entry(), Slic3r::PluginDescriptor::install_state_valid, Slic3r::is_ignored_plugin_directory(), Slic3r::PluginDescriptor::plugin_root, Slic3r::read_install_state(), Slic3r::read_python_plugin_metadata(), Slic3r::read_wheel_plugin_metadata(), scan_plugin_directory(), Slic3r::PluginDescriptor::set_error(), and Slic3r::PluginDescriptor::set_metadata_valid().

Referenced by scan_plugin_directory().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unquote_toml_string()

std::string Slic3r::anonymous_namespace{PluginFsUtils.cpp}::unquote_toml_string ( const std::string & val)
391{
392 if (val.size() >= 2 && ((val.front() == '"' && val.back() == '"') || (val.front() == '\'' && val.back() == '\'')))
393 return val.substr(1, val.size() - 2);
394 return val;
395}

References unquote_toml_string().

Referenced by parse_pep723_toml(), parse_toml_inline_array(), and unquote_toml_string().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ zip_entry_name()

std::string Slic3r::anonymous_namespace{PluginFsUtils.cpp}::zip_entry_name ( mz_zip_archive & archive,
const mz_zip_archive_file_stat & stat )
313{
314 if (stat.m_is_utf8)
315 return stat.m_filename;
316
317 std::string extra(1024, 0);
318 const size_t n = mz_zip_reader_get_extra(&archive, stat.m_file_index, extra.data(), extra.size());
319 return decode_zip_entry_extra_path(extra.substr(0, n), stat.m_filename);
320}
std::string decode_zip_entry_extra_path(const std::string &extra, const std::string &fallback)
Definition PluginFsUtils.cpp:297

References decode_zip_entry_extra_path(), and zip_entry_name().

Referenced by zip_entry_name().

Here is the call graph for this function:
Here is the caller graph for this function: