No description
  • C++ 49.2%
  • C 47.8%
  • Python 2.5%
  • PowerShell 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-24 10:28:32 +11:00
assets Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
tests Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
vendor/zstd Initial DeployLab application with threaded FTP downloads and folder compression 2026-10-01 23:33:44 +10:00
.gitignore Initial DeployLab application with threaded FTP downloads and folder compression 2026-10-01 23:33:44 +10:00
app.manifest Initial DeployLab application with threaded FTP downloads and folder compression 2026-10-01 23:33:44 +10:00
app.rc Prepare v1.1.0 release and document changes since v1.0.0 2026-10-24 10:28:32 +11:00
archive_io.h Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
build.ps1 Polish desktop UI and add automatic FTP login with editable fallback 2026-10-05 21:36:52 +11:00
CHANGELOG.md Prepare v1.1.0 release and document changes since v1.0.0 2026-10-24 10:28:32 +11:00
config.example.h Initial DeployLab application with threaded FTP downloads and folder compression 2026-10-01 23:33:44 +10:00
date_format.h Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
delete_io.h Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
deploylab.example.json Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
DOWNLOAD-DESIGN.md Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
folder_tree.h Add independent download sessions and folder activity views 2026-10-04 16:03:33 +11:00
localisation.h Prepare v1.1.0 release and document changes since v1.0.0 2026-10-24 10:28:32 +11:00
main.cpp Prepare v1.1.0 release and document changes since v1.0.0 2026-10-24 10:28:32 +11:00
README.md Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
rename_io.h Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00
runtime.cpp Initial DeployLab application with threaded FTP downloads and folder compression 2026-10-01 23:33:44 +10:00
upload_io.h Add localisation, shared settings and download UI updates 2026-10-24 10:24:48 +11:00

DeployLab

A small native C++ Win32 FTP browser and downloader. The release is a single portable executable: dist/DeployLab.exe. Copy it to a computer and run it; no installer, .NET, Visual C++ redistributable, or accompanying configuration files are needed. The executable requests administrator privileges whenever it starts.

The supplied build connects to nas:21 as preinstall, starting at /preinstall/, using passive FTP. The computer must be able to resolve and reach nas, normally on your local network or VPN. The title bar shows DeployLab - ftp://user@host:port (Passive) for the connected server; all current FTP transports use passive mode.

Use

The app defaults to the Windows display language, matching supported regional variants and falling back to English for other languages. Supported languages are English, Simplified Chinese, Spanish, French, German, Italian, Polish, Indonesian, Turkish, Russian, Japanese, Korean, Vietnamese, Arabic, Dutch, Thai, Hindi, Ukrainian, and Tamil. Use Language in the top menu bar to choose a language manually, or Automatic (Windows language) to follow Windows again. Menus, toolbar labels, columns, login fields, common dialogs, help and transfer states update immediately. Arabic labels and messages use right-to-left reading while paths and editable content retain their direction. The selection is remembered on the next launch. File names, server metadata and typed input retain their original text. Technical messages without a catalogue entry fall back to English.

  1. Run the executable. It connects and lists /preinstall/ automatically. The main window starts maximized on every launch. The address bar shows the FTP server and folder path. Edit it and press Enter (or click Refresh on the right) to navigate within the configured starting folder; Escape restores the current address.
  2. Right-click any row for Download, Edit. The Menu key or Shift+F10 opens the same menu for the focused row. Double-click a file or folder row, or press Enter, to download it or open its completed local copy. Space toggles the focused row checkbox. Folders are displayed as downloadable items; Enter and Backspace do not browse them.
  3. Click the Download button beside any file or folder to save it. A separate progress dialog opens automatically on its own Windows UI thread while the background worker downloads. Its independent message loop and timer display snapshots and calculate live statistics. The download worker starts independently of the dialog and publishes completion directly into shared state. DNS, login, FTP listing, and disk I/O run on the background job worker; four additional workers per download transfer the file chunks. The main UI submits the selected items and displays lightweight status, keeping both windows responsive without synchronous calls between them. Each item row has a Progress bar and a Average speed column. Folder bars cover the whole item, including skipped files, and remain at 99% until extraction finishes. Finished rows retain 100% and their final average speed. Average speed is total bytes received divided by elapsed job time, including all files in a folder and the gaps between them; queued waiting time is excluded. There is no bottom progress bar or connection-statistics panel. See DOWNLOAD-DESIGN.md for the architecture. The row changes to Downloading; clicking it brings the dialog back. Hide or the window close button keeps an active download running, while Cancel download stops it. Successful downloads automatically close their dialogs, including hidden dialogs. Failed and cancelled dialogs remain available to inspect their result. Each selected item gets its own job, cancellation flag, statistics, configuration snapshot, and progress dialog with a Windows taskbar entry. At most two jobs run at once; the rest wait in a FIFO queue and start automatically as workers finish. Waiting rows and dialogs show Queued, and queued jobs can be cancelled without downloading anything. The main window stays available for browsing, refreshing, changing the destination, and starting other downloads. Cancelling one dialog affects only its own item. Dialog titles include the item name.
  4. Each row's Download arrow menu offers Download & Open to open the downloaded file or folder after successful completion.
  5. Choose Edit from an item's context menu to edit its Name and Category. Name changes rename the FTP file or folder and update its metadata. Unknown metadata fields are preserved.
  6. For folders, Calculate size updates the size stored in metadata; Save persists it. Compress folder and Delete item remain available in the editor.
  7. Press F2 on a focused row or use Rename in its context menu to rename it. Delete... in that menu confirms before deleting the FTP item and its metadata. Rename and delete wait until downloads finish. Failed metadata publication rolls back the FTP rename.
  8. Edit Download to below the file table, or click Browse... to choose a folder. The same destination is available in Options. Downloads replace existing files by default, publishing replacements only after successful transfer. Destination changes apply to newly submitted downloads; existing downloads keep their original destination. Checked-download controls will return in a later update.

Downloaded files and folders use the current creation and modified dates. Extracted files and archive folders also receive current dates after extraction completes.

A Delete button beside Download in the same column removes that item's local downloaded copy after confirmation. It becomes available once the download completes and no downloads are active. Clicking a progress bar does not open a connection dialog.

While a row is downloading, its button shows Stop downloading. Clicking it cancels only that item; other active and queued downloads continue.

After a successful download, the row shows Open Directory for folders and extracted archives or Open File for files. Delete Downloaded Files, beside Stop All, removes files downloaded during the current app session, including files from dismissed downloads. Cleanup asks for confirmation and preserves unrelated files and the download destination. Stop active downloads before cleanup. Download records are kept in memory and reset when the app closes.

The default destination is the user's configured Downloads folder on Vista and later, or %USERPROFILE%\Downloads on XP. The destination is created when downloading. Language and sorting preferences are saved in the server<65>s deploylab.json and shared by clients using that server. The download destination remains session-only. Older local language preferences are read only as a startup fallback until the server configuration loads; new selections are saved to deploylab.json.

Windows Vista and later display a User Account Control prompt every time the app starts. A user must approve the prompt and have administrator credentials. Windows XP has no UAC prompt and runs the app with the privileges of the current account.

FTP directories whose names begin with @, such as @Recycle, are hidden from every folder listing and recursive download.

At launch, the app downloads /preinstall/deploylab.json as UTF-8 for app metadata and shared interface settings. App name values inside apps match file or folder names case-insensitively. The JSON file is limited to 256 KB. Legacy postAction fields are ignored and preserved as unknown metadata; the application has no post-install action support.

The root is an object with an apps array and a settings object. For example:

{
  "apps": [
    { "name": "Example App", "category": "Utilities" }
  ],
  "settings": {
    "language": "auto",
    "sortColumn": "name",
    "sortDescending": false,
    "dateFormat": "system"
  }
}

Legacy top-level arrays remain readable and are migrated to this structure on the next successful save. Existing app metadata, unknown root fields and unknown settings are preserved. language accepts auto or en, zh-CN, es, fr, de, it, pl, id, tr, ru, ja, ko, vi, ar, nl, th, hi, uk, ta. sortColumn accepts name, category, progress, speed, type, size, or updated; sortDescending is a boolean. Missing or invalid settings default to the Windows display language and ascending name order.

Options contains language, sort column, sort direction, and the Updated date format. The Language menu and column header clicks also save these settings. Saves run in the background and merge with the latest server configuration; failures are reported without changing app entries. The server<65>s saved settings apply when its configuration is loaded. A valid deploylab.json and FTP write/rename permissions are required to save preferences.

The Updated column shows each item's FTP last-modified timestamp, including the archive timestamp for compressed apps. Exact dates display in local time. Options offers the system short date, YYYY-MM-DD, DD/MM/YYYY, MM/DD/YYYY, DD MMM YYYY, YYYY-MM-DD HH:mm, and relative time such as 12 days ago or 8 months ago. Relative text refreshes every minute, with completed calendar months/years and correct leap-day/month-end handling. Unknown timestamps display a dash; future timestamps say In the future. Clicking Updated sorts by the actual timestamp, newest first initially, regardless of display format.

The shared settings.dateFormat accepts system (default), yyyy-MM-dd, dd/MM/yyyy, MM/dd/yyyy, dd MMM yyyy, yyyy-MM-dd HH:mm, or relative. Numeric custom formats use Gregorian dates; system dates and month names follow Windows regional settings. Saving the format updates existing rows immediately and preserves app metadata.

Editing preserves other items and unknown fields. Saving checks the server configuration against the loaded version and refuses stale edits; return to the root folder and Refresh to reload it. Uploads use a temporary file before renaming. Servers that cannot overwrite by rename use a temporary backup of the original, restored if publication fails. FTP does not provide transactional locking against simultaneous writers. The account needs upload, rename, and delete permissions. Failed saves leave the edited JSON available for retry.

Existing files are replaced after a successful transfer. A failure stops that individual download and reports the error in its dialog; other download jobs continue and completed files remain. Duplicate or overlapping active destination paths reopen the existing download dialog instead of starting competing writes. Temporary files are removed on ordinary cancellation or failure. Force-terminating the process or losing power can leave pid*.tmp files in the destination.

Files of at least four bytes download through four independent passive FTP connections. Each connection sets binary mode, verifies the remote size, sends a REST byte offset, and writes its assigned range into a temporary file. The progress bar aggregates all four ranges. Files smaller than four bytes use one connection. Servers that explicitly reject REST use the original single-connection transfer. A failed or cancelled chunk discards the temporary file and keeps an existing destination unchanged.

The download status panel shows active FTP control connections versus the current target, live speed for each connection, combined speed for the current file, total live download speed, bytes received across the batch, and average speeds for the current file and the batch. Live speeds are sampled every half second; closed connections show zero speed. Per-file counters reset for each file, while batch counters include all received data (including retried transfers). After completion or cancellation, live speeds return to zero and the received total and averages remain visible. The file average freezes once that file completes. Within each job one file downloads at a time, so file and total live speed generally match; four chunks run concurrently within that file. Independent jobs can transfer different files at the same time, and each dialog shows only its own job statistics.

The dialog progress bar shows the current file; row progress bars cover the whole item. Progress and Average speed columns sort numerically, starting largest first. Cancellation is cooperative: the window stays responsive while waiting for a synchronous network call to finish. Network timeouts are configured to 15 seconds, but Windows name resolution and FTP behavior can take longer.

Compatibility

  • 32-bit x86 executable, targeting Windows XP and later (including 64-bit Windows through its x86 compatibility support).
  • PE operating-system and GUI subsystem versions are both 5.01.
  • No C/C++ runtime is linked. Small x86 compiler helpers are included in runtime.cpp.
  • Imports only Windows components: KERNEL32, USER32, GDI32, COMCTL32, WININET, WS2_32, SHELL32, and ole32.
  • Vista's Downloads-folder API is resolved at runtime, with an XP fallback.
  • Classic Windows path limits apply. Local directories are limited to 247 characters, full file/remote paths to 259, and recursion to 32 levels. Unsupported Windows filenames and local junctions/symbolic links are rejected.
  • Filename encoding is limited by the FTP server and the installed Windows WinINet version. ASCII filenames provide the broadest XP compatibility.

Built and tested on the current Windows machine, including browsing the real NAS. An actual Windows XP machine/VM has not been available for runtime verification. The XP target and imports have been checked, but an XP smoke test is still recommended before distributing broadly.

Chunked downloads use the Windows Winsock IPv4 transport to send TYPE, SIZE, PASV, REST, and RETR directly. Browsing and configuration use Microsoft's built-in WinINet FTP APIs.

Credentials

The requested login is compiled into the executable from config.h. Anyone with a copy of the binary can recover it, and plain FTP sends credentials and files unencrypted. Treat the binary as containing the FTP password; distribute it only to intended users. The editor uploads deploylab.json and removes its temporary upload/backup files. Restrict write access to the FTP area to trusted users because downloaded programs run with the application's privileges when opened.

config.h is excluded by .gitignore; config.example.h contains placeholders. The real password is not repeated in this README. Rebuild to change the server, login, or starting folder.

Build

On a modern Windows development PC, install Microsoft Visual Studio C++ build tools and the Windows SDK. The compiler is only needed to build, not to run the app.

# If config.h does not already exist:
Copy-Item config.example.h config.h
# Edit config.h with the intended connection settings.
.\build.ps1

The script discovers Visual Studio and the SDK, compiles resources and C++ for x86, and links with /NODEFAULTLIB, /ENTRY:AppEntry, and /SUBSYSTEM:WINDOWS,5.01. It writes dist/DeployLab.exe and build inspection reports in build/.

Tests

The local integration suite uses the production FTP/download implementation against a loopback-only test server. Python and pyftpdlib are test tools only; neither is needed by the executable.

python -m pip install --user pyftpdlib
.\build.ps1 -Test
python tests/run.py
.\build.ps1

Tests cover item JSON validation and preservation, recursive folder-size calculation, four-connection REST transfers with uneven boundaries and SHA-256 verification, unsupported-REST fallback, chunk cancellation and failed replacement preservation, staged configuration upload, stale-edit rejection, configuration download/parsing, recursive binary downloads, nested/empty folders, zero-byte files, SHA-256 verification, existing-file skip/replacement, cancellation cleanup, truncated transfers, missing folders, filename validation, and 64-bit size formatting. Test fixtures and downloaded data stay inside build/.

Compressed folders

The edit dialog's Compress folder button downloads the complete folder into a temporary local workspace, including empty folders and hidden entries. It streams a USTAR archive through embedded Zstandard 1.5.7 at level 3 with a checksum, uploads and verifies the archive, updates deploylab.json, then removes the original remote folder. Failed configuration updates restore the original folder. Compression and FTP work run on the background worker.

The item keeps its original name, category and custom properties. Its size stores the original contents size; archive records format: "tar.zst", file: "folder.tar.zst" and compressed size. Listings show the original logical folder. Downloading automatically verifies and extracts it before publishing files. Existing-file skip/replace behavior still applies.

Temporary disk space is needed for the downloaded contents and compressed archive; downloads need space for both the archive and staged extracted contents. Classic Windows path limits also apply to archive members. Links and unsafe archive paths are rejected.

Zstandard is embedded under its BSD license, included in vendor/zstd/LICENSE and the executable resources. Upstream: https://github.com/facebook/zstd/tree/v1.5.7 . No external archiver is required.

Upload by drag and drop

Drop files or folders onto the FTP file list to upload into the directory currently shown. For each folder, choose Yes to upload a .tar.zst archive or No to upload its existing folder structure, including empty folders. Cancel dismisses the entire drop. Compressed uploads add or update their entry in deploylab.json, preserving existing category and custom properties. The entry stores the original folder size plus archive format, filename and compressed size, so DeployLab displays it as an archived folder and extracts it on download. If metadata publication fails, the new archive is removed.

Uploads run on a background worker; the main Cancel button stops them. Each top-level item is staged and renamed after transfer, and the listing refreshes afterward. Existing remote names are rejected; rename the local item to upload it separately. Earlier completed items remain if a later item fails or is cancelled. Links, paths beyond classic Windows limits, reserved top-level @ names and deploylab.json are rejected. The FTP account needs upload, directory creation, rename and delete permissions.

The item editor includes Delete.... Confirm to permanently delete the selected FTP file, folder and all contents, or compressed archive, and remove its deploylab.json entry. Deletion waits until downloads finish and runs on a background worker. Metadata publication failures restore the staged item; cleanup failures report the remaining FTP staging path.

The browser hides the final file extension, similar to Windows Explorer. F2 and inline Rename preserve that extension. The Name field in Edit shows the full filename and can change its extension. Folder names and dotfiles such as .env remain unchanged.

The menu bar includes File, Edit, View, Tools, and Help. Basic actions include downloading the selected item, opening the download folder, editing/renaming/deleting, selecting all checkboxes, refreshing, toggling extension visibility, settings, cancelling the current browser operation, quick help, and About. Future upload selection, checked downloads and queue controls are disabled placeholders. Drag-and-drop uploads remain available.

Dropped uploads appear immediately as temporary browser rows labelled Uploading. File sizes are shown immediately; folder sizes are measured in the background, including empty and nested folders. The Progress column shows aggregate upload bytes, and average upload speed appears alongside it. Compression and publication have their own status labels. Row actions and context menus are disabled during upload; completion refreshes the rows from FTP.

Compressed uploads show Compressing N% based on original file bytes processed by Zstandard, capped at 99% until archive finalisation. The bar then resets to Uploading 0% and tracks compressed transfer bytes.

Compressed downloads reset the row and dialog bars to Extracting N% after transfer, based on extracted file bytes and the original size in archive metadata. The archive is verified before extraction. Extraction stays below 100% until the final files are saved; there is no separate publishing stage.

A toolbar below the menus provides Download Selected, Options, and Refresh. Download Selected downloads highlighted rows, or checked items when no rows are highlighted. Drag table column headers to reorder them. The address bar is removed; Refresh reloads the configured root directory. Folder navigation by double-click and keyboard is disabled.

Compressed downloads save a visible <archive>.part-<id> file directly in the download directory. The complete archive is validated before extraction, then files are extracted directly into their final folder without a separate publishing/moving stage. Each file uses a small temporary sibling until its contents are written; completed files remain if extraction is cancelled. The temporary archive is removed afterward.

Downloads start without automatically opening a dialog. Click a row's Progress bar to open its download details, including completed results. Repeated Download clicks on an active destination do not open its dialog or start a duplicate transfer.

Stop All, beside Download Selected, cancels active and queued downloads, closes their dialogs, clears checkboxes and removes their progress history. Temporary files are cleaned up cooperatively by their workers. Previously completed files and the FTP source items are preserved. The table order is Name, Category, Progress, Average Speed, Type, Size, Download, Post Action. Use the context menu or Edit menu to edit items.

At launch, the app automatically tries the compiled connection defaults. If the connection fails, the centered FTP login form appears in the main window with those defaults filled in. Change the server hostname/IP, port, credentials, or remote folder and click Connect. Failed connections keep the login form available for retry. Connection settings are kept only for the current app session.