Namespace Archiver.Core.Services
Classes
- AntivirusScanService
T-F146. Scans an archive's expanded contents for threats via AMSI — deliberately not an extension of IArchiveService/ITarService (see docs/DECISIONS.md's T-F146 entry): this never writes anything to a real destination, has no conflict/MOTW dimension, and adding a method to those interfaces would ripple through every hand-rolled test fake in the repo for a capability that isn't a variant of extraction. Never throws — every failure becomes an Inconclusive ThreatFinding.
- ArchiveCreationRouter
Routes archive creation to IArchiveService (ZIP) or ITarService (tar-family) based on ArchiveOptions.Format. Unlike IExtractionRouter, this needs no per-path format detection — creation format is a single explicit choice for the whole operation.
- ArchiveDownloadMark
Whether an archive carries the "downloaded from the internet" mark (its Zone.Identifier stream, T-F45) — what the App asks before it offers to leave the mark off (T-F360).
- ArchiveFormatDetector
Detects archive format from magic bytes only — no extension reliance, matching this codebase's existing ZIP signature-sniffing convention (see ZipArchiveService.IsZipFile). Used by ExtractionRouter to decide which service (IArchiveService vs ITarService) should handle a given archive path.
- ArchiveFormatPolicy
The one classifier every operation uses to decide which engine may open an archive (T-F261): Group Policy first (AllowedFormats/BlockedFormats, then DisableTarExtraction), then what the system's tar.exe can read. Extraction, testing, listing and scanning all route through it, so no operation can drift from what another would allow or refuse.
- ArchiveFormatPolicy.Classification
A selection split by engine. ZipPaths also holds unrecognized paths, so the ZIP engine's own "not a recognized archive" handling reports them.
- ArchiveFormatRegistryNames
Maps ArchiveFormat/ArchiveContainerFormat to the registry-string vocabulary used by GroupPolicyOptions.AllowedFormats/BlockedFormats (T-F51). The two enums don't line up 1:1 — e.g. creating ArchiveContainerFormat.TarGz is later detected on extraction as ArchiveFormat.GZip — so both map onto the same 9-name vocabulary rather than each having its own registry string set.
- ArchiveListingRouter
Routes a single archive's ListEntriesAsync call to IArchiveService (ZIP) or ITarService (tar-family), based on ArchiveFormatDetector — same dispatch IExtractionRouter uses for extraction. A separate interface from IExtractionRouter because listing and extracting return different result shapes; one archive path in, one ArchiveListResult out (not a batch, unlike ExtractAsync — the archive browser always lists exactly one archive at a time). T-F250: Group Policy applies exactly as for extraction (ArchiveFormatPolicy) — a blocked format, or a tar-family format under DisableTarExtraction, is a failed result and no engine is called.
- ArchiveNaming
Derives a base name (for a destination folder, or an auto-named archive) from an archive's file name. Path.GetFileNameWithoutExtension only strips the last extension, which is wrong for the compound extensions tar.exe itself produces (T-F103: "archive.tar.gz" must strip to "archive", not "archive.tar"). Kept in sync with ShellExtUtils.cpp's native equivalent.
- EncryptionPasswordRule
T-F193: which passwords Pakko accepts when creating an AES-256 ZIP. Public so every frontend can refuse bad input inside its own prompt with localized text, while ArchiveAsync(ArchiveOptions, IProgress<ProgressReport>?, CancellationToken) enforces the same rule as the last line of defence. Reading (T-F189) accepts any password.
- ExtractionRouter
Routes ExtractAsync calls to IArchiveService (ZIP) or ITarService (tar-family) per archive, based on ArchiveFormatDetector, and merges the results. Never throws — all errors are captured in ArchiveResult.Errors.
- FileHashService
T-F128: computes CRC-32/SHA-256 for the Explorer context menu's "Хеш-суми" submenu. A single folder gets NanaZip-compatible combined DataSum (all file contents) and NamesSum (all file names+paths+contents) values, via Archiver.Core.IO.HashDigestAccumulator — the algorithm of 7-Zip's
HashCalc.cpp, checked live against the vendored7za hbyFolderHashParityTests(T-F225). NamesSum includes one item per directory, the selected folder itself included, hashed with an all-zero digest (7-Zip resets it before every item), so the result does not depend on enumeration order. Remaining difference: symbolic links and junctions are skipped here (T-F251), where 7-Zip follows them.Files are hashed in parallel (ForEachAsync<TSource>(IEnumerable<TSource>, ParallelOptions, Func<TSource, CancellationToken, ValueTask>), up to ProcessorCount at once) — safe specifically because Add(ReadOnlySpan<byte>) is commutative, so combining DataSum/NamesSum in whatever order files finish hashing produces the exact same result as combining them sequentially (already relied on for the recursion-safety argument above).
T-F128 follow-up: a single large CRC-32 file is also hashed in parallel — the across-files parallelism above gives no benefit to a folder containing one huge file (or to a lone large file passed to ComputeAsync(IReadOnlyList<string>, HashAlgorithmKind, IProgress<ProgressReport>?, CancellationToken) directly). Files at or above Archiver.Core.Services.FileHashService.ParallelCrc32MinFileBytes are split into independently-hashed chunks and folded back together with Combine(uint, uint, long) — safe for the same reason cross-file combining is: CRC-32 combining is associative/order-preserving as long as chunks are folded back in their original byte order (unlike DataSum/NamesSum, chunk order here does matter, so chunks are combined sequentially by index after all finish, not as they complete).
- FolderHashSummary
Combined DataSum/NamesSum for a single recursively-hashed folder — see FileHashService's doc comment for what these mean and their NanaZip parity.
- GroupPolicyService
Reads Pakko's Group Policy settings (T-F51) from HKLM\Software\Policies\Pakko. Both Archiver.App (via DI) and Archiver.Shell (no DI container) call Load() once at startup and thread the resulting GroupPolicyOptions into every consumer. Never throws — an absent key, an absent registry hive entirely, or a malformed value all fall back to today's shipped (unrestricted) behavior.
- HashEntry
Per-file hash result. Error is set instead of Hash when the file couldn't be read, or when it was skipped (e.g. a folder in a multi-item selection).
- HashResult
Result of a ComputeAsync(IReadOnlyList<string>, HashAlgorithmKind, IProgress<ProgressReport>?, CancellationToken) call.
- LaunchArguments
The launch-argument string Archiver.Shell passes to Archiver.App through
IApplicationActivationManager::ActivateApplication— the single owner of both sides of the format, so the producer and the consumer cannot drift apart. Replaced thepakko://URI scheme (T-F232): a registered scheme could be launched by any web page or document link, while an activation argument can only come from a process already running on the machine. Shape:--browse|--extract|--archive <base64 of a UTF-8 JSON string array>— base64 keeps paths with spaces, quotes or a trailing backslash out of command-line quoting rules.
- MessageTemplates
The English template of every MessageCode (T-F209) — the one place Core's user-visible English text is written. Frontends translate these templates; their placeholders ({0}, {1}) must stay the same.
- PakkoServices
The one place a frontend without a DI container (Archiver.Shell, Archiver.CLI) gets its Core services from (T-F261), so every service is built with the same Group Policy — a hand-built composition root per command is how the CLI once shipped a policy-less listing. The tar.exe capability probe runs at most once per instance (T-F85), only when an operation meets a tar-family archive that policy allows (T-F350), and not at all when Group Policy disables tar.exe.
- PreviewPolicy
T-F97: safe-preview-type allowlist for the Archive Browser's double-click-to-preview feature. Deliberately conservative — only formats with no known code/macro/script execution path via their typical OS default handler. See SECURITY.md.
- ProgressSpeedSampler
EMA-smoothed transfer-speed sampler shared between Archiver.App's status line and Archiver.Shell's progress dialog (T-F142) — extracted from what was private, untested arithmetic inline in Archiver.App's MainViewModel.UpdateOperationStatus. Public (not internal), matching Archiver.Core.IO.Crc32's own precedent for a dependency-free algorithm needed by multiple frontends. Sampling only: byte/speed string formatting (localization-adjacent in the App, plain text in the Shell dialog) and ETA (derived from Percent, not bytes — works already, isn't shared) both stay separate per frontend, not folded in here. Callers gate on
TotalBytes > 0themselves before calling Sample(long, DateTime) — a report with no real byte total carries no speed signal at all, and deciding to skip it is the caller's job, not this sampler's, so it stays a pure function of (bytes, time). Construct a fresh instance per operation (Archive/Extract) rather than reusing one across operations — there is noReset()by design, so a stale carried-over speed from reusing an instance across two operations is not possible.
- RecoveryDataLookup
What a frontend asks about PAR2 recovery data before any test runs (T-F275 step 3c). The two lookups that read the disk are reached through IRecoveryService (step 4c), which holds the policy; only the name check is public here.
- RecoveryService
PAR2 recovery data next to an archive: what is there, and repair from it (T-F275 step 4). The checks themselves are TestAsync(IReadOnlyList<string>, IProgress<ProgressReport>?, Func<PasswordPromptInfo, Task<PasswordDecision>>?, bool, CancellationToken).
- StickyCallback<TInfo, TDecision>
Widens an "apply to all/remaining" answer beyond the single Core call that asked for it. Core's own ConflictResolver/PasswordResolver already remember such an answer, but only for the lifetime of ONE ExtractAsync/TestAsync call. A frontend that makes several calls for one user action — Archiver.Shell's per-archive loop (T-F155/T-F192), Archiver.CLI's zip/tar split through ExtractionRouter (T-F160) — wraps its prompt in one instance of this, created once per user action, so the answer spans every call. Replaced two hand-written copies of this pattern.
- TarSandboxedService
Extracts tar-family archives (tar, tar.gz, tar.bz2, tar.xz, tar.zst, tar.lzma, 7z, rar) via the system's tar.exe, launched inside a Windows AppContainer (no network capability) with a Job Object (ActiveProcessLimit = 1, RAM/CPU limits) — see TASKS.md's T-F52 entry for the full design and DECISIONS.md for the empirical trail. Never throws to callers — all errors are captured in ArchiveResult.Errors. Replaces the deleted TarProcessService.
- TarVersionParser
Parses
tar.exe --versionoutput into TarCapabilities. Extracted into a separate class so T-F48 can unit-test format detection without launching a process.
- Win32RegistryReader
Minimal seam over the Windows registry for GroupPolicyService (T-F51) — kept deliberately small so tests use a hand-rolled fake instead of a mocking library (none is used in this repo). Both members return null for an absent key/value or a value of the wrong registry type; implementations never throw.
- ZipArchiveService
ZIP archive service using System.IO.Compression. Never throws to callers — all errors are captured in ArchiveResult.Errors.
Enums
- EncryptionPasswordProblem
Why Check(string) refused a password for a new encrypted ZIP.
- LaunchOperation
What Archiver.App should do with the files handed to it by Archiver.Shell (T-F232).