Skip to content

feat: download .wpress backups from cloud storage URLs - #52

Merged
developeritsme merged 18 commits into
masterfrom
feat/cloud-storage-open
Aug 14, 2026
Merged

feat: download .wpress backups from cloud storage URLs#52
developeritsme merged 18 commits into
masterfrom
feat/cloud-storage-open

Conversation

@dugyen

@dugyen dugyen commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the ability to open a .wpress backup directly from a cloud share link or any direct HTTPS URL — no manual download step.

  • CloudDownloader (src/clouddownloader.{h,cpp}): streams a .wpress file from a remote HTTPS URL into a QTemporaryFile via QNetworkAccessManager. Simple providers are rewritten into direct-download URLs; the rest use a multi-step API flow. This is the only file in the app that touches QNetworkAccessManager.
  • Drop overlay (src/dropoverlay.{h,cpp}): accepts remote URLs alongside local .wpress files. Two-path drag handling — text/uri-list primary, text/plain fallback — so cloud web-UI drags work and Mega #KEY fragments survive (text/uri-list strips fragments per RFC 2483).
  • URL dialog (src/urlopendialog.{h,cpp}): File → Open from URL… (⌘⇧O / Ctrl+Shift+O). Validates the URL before enabling OK.
  • MainWindow wiring: urlDroppedopenBackupFromUrl, progress bar with indeterminate mode when Content-Length is unknown, re-entrancy guard, temp-file cleanup on clear/re-download.
  • Mega AES-128-CTR (src/megaaes.{c,h}, tiny-aes-mega target): a second vendored tiny-AES-c instance. tiny-AES-c bakes its key size in at compile time, so AES-128 (Mega) cannot share the app's AES-256 instance; symbols are prefixed (mega_AES_*) via compile definitions to avoid link clashes. CTR is driven from the ECB primitive in megaaes.c.
  • CI fix: macdeployqt cleanup was stripping the entire PlugIns/tls directory, so every HTTPS request failed with "TLS initialization failed". libqsecuretransportbackend.dylib is now kept in both ci.yml and release.yml.

Supported providers

Provider How it's handled
Google Drive /file/d/{ID}/view and /open?id={ID}drive.usercontent.google.com/download?id=…&export=download&confirm=t
Dropbox dl=0dl=1
pCloud Two-step: GET api.pcloud.com/getpublinkdownload?code=… → CDN host + path
Mega Two-step: API call for CDN URL + on-the-fly AES-128-CTR decryption (key from URL fragment)
WeTransfer Multi-step: resolve we.tl short link → POST /api/v4/transfers/{id}/downloaddirect_link
DigitalOcean Spaces / any direct HTTPS link Pass-through (pre-signed or public URLs work as-is)

Not supported (investigated and deliberately excluded):

  • OneDrive — personal accounts are migrated to SharePoint Online (migratedtospo=true). Every server-side endpoint (anonymous shares API, badge-token flow, legacy authkey) returns 401/404; the file only unlocks after a JavaScript guest-session handshake that plain HTTP can't perform. Verified against live links.
  • Box — the /shared/static/{code} direct-download endpoint now returns 404.
  • Amazon S3 / Google Cloud Storage / Azure Blob — de-advertised. They were only ever pass-through, so their URLs still work via the "Any direct HTTPS link" path; they're just no longer listed as first-class providers.

Security notes

  • The Mega decryption key lives in the URL fragment and is never transmitted — only the public file node ID goes to Mega's API.
  • Outbound HTTPS happens only when the user explicitly supplies a URL (drag-and-drop or File → Open from URL). No telemetry, no background connections.
  • Downloads land in a QTemporaryFile in the system temp dir, removed on clear/re-download/abort. The path is never written to QSettings.
  • FTP is rejected — Qt 6 removed FTP from QNetworkAccessManager.
  • Responses are probed for HTML interstitials (e.g. Google Drive's virus-scan page) so an error page is never mistaken for an archive.

Tests

25 new tests in tst_clouddownloader.cpp (a 7th test class, wired into tst_main.cpp): isRemoteUrl, per-provider normalizeUrl, parseMegaUrl key derivation + legacy format + edge cases, and AES-128-CTR correctness (NIST vector, plus chunked-vs-single-shot equivalence to prove the keystream carries across chunk boundaries).

Test plan

  • ctest --output-on-failure — all tests pass across 7 classes
  • clang-format --dry-run --Werror (v18, matching CI) — no violations
  • Google Drive share link → downloads
  • pCloud share link → downloads
  • WeTransfer link (we.tl/…) → downloads
  • Dropbox share link → downloads with dl=1
  • Mega link → two-step download + AES decryption produces a valid .wpress
  • Drag a local .wpress file — still works as before
  • Paste an invalid URL → OK button stays disabled
  • Cancel mid-download → temp file is cleaned up
  • Drop a second URL while downloading → ignored (re-entrancy guard)

🤖 Generated with Claude Code

@github-actions github-actions Bot added build Build system/project files ui User interface docs Documentation labels May 19, 2026
@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Build Artifacts

Platform Download
Linux (x86_64) Traktor-linux-x86_64
macOS (Apple Silicon) Traktor-macOS
Windows (x64) Traktor-windows-x64

Built from 29337f4. Artifacts expire after 90 days.

@developeritsme

developeritsme commented May 19, 2026

Copy link
Copy Markdown
Contributor

Code Review

Thanks for the PR - the feature is well-organized and the signal/slot wiring is clean. However, all 4 substantive CI checks are failing (Linux/macOS/Windows builds + Code Style), and there are correctness issues that need to be addressed before this can merge. I also checked the branch out and reproduced the failures locally.

Blockers

1. urlopendialog.cpp does not compile - smart quotes used as string delimiters

Lines 40-43 (the hintLabel "Tip:" block) use Unicode curly quotes (U+201C / U+201D) instead of ASCII " to delimit the string passed to tr(). This fails on every platform:

src/urlopendialog.cpp:40:12: error: unexpected character <U+201C>

Every other string in the file uses straight quotes correctly. Fix is mechanical: replace the curly-quote delimiters with " and escape the inner quotes.

2. clang-format violations (Code Style check)

The project's formatter was not run. clang-format --dry-run --Werror flags:

  • src/clouddownloader.cpp - lines 15-17, 32-33, 247, 295-297, 335-337, 359, 399, 401, 442
  • src/dropoverlay.cpp - lines 45-46
  • src/mainwindow.h - line 55
  • src/urlopendialog.cpp - lines 23-28

Fix per CLAUDE.md:

find src tests \( -name '*.cpp' -o -name '*.h' \) ! -name 'moc_*' | xargs clang-format -i

3. PR description contradicts the implementation

The description says "Note on Mega: ... not supported in this PR." but the branch HEAD (feat: add Mega (mega.nz) cloud storage support) fully implements it - parseMegaUrl, downloadMega, AES-128-CTR decryption. CLAUDE.md and the dialog also advertise Mega. Please update the description so reviewers can evaluate the (now in-scope) Mega code and its security claims.

Correctness

4. Mega AES key is likely used without un-mangling. Mega file links embed a 256-bit key; the real 128-bit AES key is the XOR of the two 16-byte halves (key[0..15] ^ key[16..31]), with bytes 16-23 as the nonce. The code uses keyBytes.left(16) directly as the AES key, which should decrypt to garbage. Please verify against the Mega protocol - Mega support should not ship without a round-trip test using a known key/ciphertext vector.

5. Mega multi-part (g as array) uses only the first segment and assumes redirects fill the rest. If that branch is taken, the temp file will be silently truncated/corrupt with no error. Either reassemble all parts or emit failed() explicitly.

6. FTP is claimed but not supported in Qt 6. isRemoteUrl() accepts ftp/ftps and the description/dialog advertise FTP "via Qt's built-in FTP handler", but the FTP backend was removed from QNetworkAccessManager in Qt 6 (the project targets Qt 6.8). ftp:// URLs will error out. Drop the FTP claim/scheme, or back it with a real implementation.

Bugs

7. Temp files leak. QTemporaryFile is created with setAutoRemove(false), and disk cleanup happens only via m_pendingTempFile, which is set only on onDownloadFinished() (success). As a result:

  • Failed downloads leave the temp file on disk (not removed in onFinished()/onDownloadFailed()).
  • abort() doesn't remove the temp file.
  • Repeated downloads: startCdnDownload() does delete m_tempFile, which frees the object but leaves the file on disk (autoRemove is off) - each download leaks the previous one.

Centralize cleanup: remove the temp file in abort(), in the failure path of onFinished(), and before creating a new one.

8. EVP_DecryptUpdate return value ignored in onReadyRead() - a decrypt failure on a chunk silently writes garbage. Check the return and emit failed().

9. No content validation of the downloaded file. Google Drive's confirm=t trick is fragile - for larger files Drive often returns an HTML interstitial instead of the file. The bytes are handed straight to openBackupFile() with no content-type or magic-byte check, so the user sees a confusing parse error instead of "this link isn't a direct download."

Test coverage

No tests added. isRemoteUrl(), normalizeUrl(), extractGoogleDriveId(), and parseMegaUrl() are pure/static functions and trivial to unit-test - exactly the logic that breaks silently (see items 4-6). The project has 97 tests and CLAUDE.md emphasizes coverage; please add tst_ cases for per-provider URL normalization and Mega URL/key parsing (new + legacy formats + invalid input).

Code quality

  • Dead code in parseMegaUrl() - a six-line comment block explains an abandoned design and ends with (void)fileId;. fileId is parsed, discarded, then re-parsed in downloadMega(). Remove the parsing and the comment block.
  • Indeterminate progress - progress(-1) is rendered as setValue(0), which looks like a stuck bar. Use setRange(0,0) for a true busy indicator.
  • Re-entrancy - if a second URL is dropped mid-download, openBackupFromUrl() updates the UI to "Downloading..." but CloudDownloader::download() returns early silently, so UI and downloader state diverge. Guard at the MainWindow level too.

Security

The user-initiated-only network model is reasonable, and the CLAUDE.md security note was honestly updated. Two points:

  • This PR fundamentally changes the app's "zero network requests, fully offline" posture. That's a product decision worth an explicit sign-off, not just a doc edit.
  • The "key never leaves the client" claim for Mega is accurate for the decryption key, but the file ID is necessarily sent to g.api.mega.co.nz - state that precisely.

Recommendation

Request changes. Fix the build (item 1), formatting (item 2), and description (item 3) blockers, plus the temp-file leaks (item 7) and tests (item 9). For Mega: given the unverified key handling (item 4) and multi-part gap (item 5), I'd suggest splitting Mega into a separate follow-up PR with proper test vectors, and landing the simpler, well-understood provider normalization (Drive/Dropbox/OneDrive/Box/pCloud/S3) first - that part looks solid.

@github-actions github-actions Bot added the test Test suite changes label May 19, 2026
@dugyen
dugyen force-pushed the feat/cloud-storage-open branch from 6784c3a to 8c3119c Compare May 19, 2026 15:36
@dugyen dugyen changed the title feat: open .wpress backups directly from cloud storage URLs feat: download .wpress backups from cloud storage URLs May 19, 2026
@github-actions github-actions Bot added the ci CI/CD workflows label May 20, 2026
@developeritsme

Copy link
Copy Markdown
Contributor

Could you split 7959b59 (the GitHub Actions / Node.js 24 bump) into its own ci: PR? It's unrelated to the cloud-storage feature, and actions/upload-artifact v4 → v7 in particular is a big enough jump that it should be reviewable and revertable on its own.

@dugyen
dugyen force-pushed the feat/cloud-storage-open branch from f9a3e18 to b38af16 Compare May 20, 2026 13:59
@developeritsme developeritsme removed the ci CI/CD workflows label May 20, 2026
Ugyen Dorji and others added 4 commits July 8, 2026 13:55
Add CloudDownloader (src/clouddownloader.{h,cpp}) which downloads a
.wpress file from a remote HTTPS URL to a temp file. Normalizes share
links from Google Drive, Dropbox, OneDrive, Box, and pCloud into direct
download URLs. Mega (mega.nz) is handled via a two-step flow: parse the
AES-128-CTR key from the URL fragment (never sent to any server), call
the Mega public API for a CDN URL, then decrypt the stream on-the-fly
with OpenSSL EVP_aes_128_ctr().

Drop overlay (dropoverlay.{h,cpp}): accept remote URLs in addition to
local .wpress files. Two-path drag handling — text/uri-list primary,
text/plain fallback — so cloud web-UI drags work and Mega #KEY fragments
are preserved (text/uri-list strips fragments per RFC 2483).

URL dialog (urlopendialog.{h,cpp}): File → Open from URL (Ctrl+Shift+O)
lets the user paste any cloud share link or direct URL.

MainWindow wiring: urlDropped signal → openBackupFromUrl slot, download
progress bar with indeterminate mode (setRange(0,0)) when Content-Length
is unknown, re-entrancy guard (m_downloading flag), temp file cleanup on
clear/re-download.

Tests: 24 new test cases in tst_clouddownloader.cpp covering isRemoteUrl,
normalizeUrl (per provider), and parseMegaUrl (key derivation, legacy
format, edge cases). All 121 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The .ui file already defines a File menu (menu_File). The previous code
created a second File menu via menuBar()->addMenu("&File"), which on
macOS gets silently merged/hidden — making "Open from URL…" invisible.

Fix: insert the action into ui->menu_File (after "Open backup", before
"Clear file") instead of creating a duplicate menu.

Also update the drop zone hint text from "Drop .wpress file here" to
"Drop .wpress file or cloud link here" to hint at URL drop support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add an "Open URL..." button in the bottom button row, between "Open
backup..." and "Clear", so the cloud-storage feature is immediately
discoverable without needing the File menu or keyboard shortcut.

The button is wired to openFromUrl() via the .ui file and is disabled
during downloads (same as the other buttons).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…OpenSSL

master (#57) replaced OpenSSL with vendored tiny-AES-c for the .wpress
AES-256-CBC path, so CloudDownloader's Mega AES-128-CTR decryption was the
last remaining OpenSSL user. Port it to tiny-AES-c too, dropping the OpenSSL
dependency entirely.

tiny-AES-c fixes its key size at compile time and the app already compiles it
as AES-256 (for .wpress), so AES-128 needs its own translation unit that does
not inherit AES256=1. Add a second aes.c compilation as the tiny-aes-mega
target with AES-128 and prefixed symbols (mega_AES_*) so it does not clash
with the AES-256 instance at link time.

CTR streaming lives in the new src/megaaes.{c,h}, built on tiny-AES-c's ECB
primitive with a big-endian counter matching the previous EVP_aes_128_ctr().
Unlike tiny-AES-c's own AES_CTR_xcrypt_buffer, this keeps the keystream
aligned across arbitrary-sized network chunks. CloudDownloader now writes
through a single writeChunk() helper (which also decrypts the final flushed
bytes, previously written undecrypted), and the now-unreachable decrypt-failure
path is removed.

Tests: NIST SP 800-38A CTR-AES128 vector plus a chunk-alignment test proving
cross-chunk keystream continuity. 120 tests pass.
@developeritsme
developeritsme force-pushed the feat/cloud-storage-open branch from 4341813 to adec5a1 Compare July 8, 2026 12:48
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds remote .wpress backup opening from HTTPS URLs and supported cloud share links. The main changes are:

  • A new CloudDownloader streams downloads to temporary files.
  • Provider handling for Google Drive, Dropbox, pCloud, Mega, WeTransfer, and direct HTTPS links.
  • Mega download support with AES-128-CTR decryption.
  • Drag-and-drop and File menu UI paths for opening backup URLs.
  • Progress, cancel, busy-state, and temp-file cleanup wiring in MainWindow.
  • Tests for URL handling, provider normalization, Mega parsing, AES-CTR behavior, and abort lifecycle.
  • macOS CI and release packaging updates to retain the TLS plugin needed for HTTPS.

Confidence Score: 4/5

Safe to merge after the stale contributor-guide provider notes are corrected.

The changed download and UI paths are covered by focused tests. Previously reported blocking issues are addressed in the current code. The remaining accepted issue is non-blocking documentation drift.

Files Needing Attention: CLAUDE.md

T-Rex T-Rex Logs

What T-Rex did

  • The general-contract-validation run confirmed that cmake, ctest, ninja, qmake6, qt-cmake, and clang-format were unavailable, while /usr/bin/make and /usr/bin/g++ existed.
  • A CMake configure attempt was blocked by cmake not found, preventing the build configuration step from proceeding.
  • An optional clang-format check was attempted but blocked by clang-format not found.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
.github/workflows/ci.yml Keeps the macOS SecureTransport TLS plugin while continuing to strip unused Qt plugins.
.github/workflows/release.yml Mirrors the CI packaging fix so release bundles retain HTTPS TLS support.
CLAUDE.md Documents the new cloud download flow, but still lists stale HTTP, OneDrive, and Box support details.
src/clouddownloader.cpp Implements HTTPS-only provider normalization, multi-step cloud download flows, temp-file streaming, and Mega AES-CTR decryption.
src/dropoverlay.cpp Extends drag-and-drop handling to accept remote HTTPS URLs and preserve Mega URL fragments from text/plain.
src/mainwindow.cpp Wires URL downloads into the main UI with progress, cancellation, temp-file ownership, and busy-state guards.
src/megaaes.c Implements chunk-safe AES-128-CTR streaming on top of the vendored tiny-AES ECB primitive.
tests/tst_clouddownloader.cpp Adds coverage for URL acceptance, provider normalization, Mega key derivation, AES-CTR behavior, and abort lifecycle.

Sequence Diagram

sequenceDiagram
actor User
participant UI as DropOverlay / UrlOpenDialog
participant MW as MainWindow
participant CD as CloudDownloader
participant API as Provider API
participant CDN as HTTPS CDN
participant TMP as QTemporaryFile

User->>UI: Drop URL or paste Open from URL
UI->>MW: urlDropped(url) / accepted url()
MW->>CD: download(url)
alt Direct / Google Drive / Dropbox
    CD->>CD: normalizeUrl(url)
else pCloud / WeTransfer / Mega
    CD->>API: Resolve share link
    API-->>CD: HTTPS direct download URL
    opt Mega
        CD->>CD: Parse fragment key and init AES-128-CTR
    end
end
CD->>CDN: GET resolved HTTPS URL
CDN-->>CD: download chunks + progress
opt Mega
    CD->>CD: Decrypt chunk in AES-128-CTR
end
CD->>TMP: Stream bytes to temp file
CD-->>MW: finished(tempFilePath, suggestedName)
MW->>MW: replace loaded backup and remove old temp file
Loading

Reviews (12): Last reviewed commit: "fix: make the Open-from-URL provider tex..." | Re-trigger Greptile

Comment thread src/mainwindow.cpp Outdated
Ugyen Dorji and others added 2 commits July 13, 2026 16:36
- pCloud: resolve the real CDN URL via the getpublinkdownload API
  (two-step flow) instead of the dead /publink/code endpoint
- Google Drive: use drive.usercontent.google.com/download; the old
  drive.google.com/uc endpoint now returns 403
- Remove OneDrive: SharePoint-migrated shares need a JS guest-session
  handshake plain HTTP can't perform (badge-token + shares API both 401/404)
- Remove Box: the /shared/static/ direct-download path returns 404
- Drop-overlay text: "Drop .wpress file here" (cloud links aren't drag-droppable)
- Update the Open-from-URL provider list/tips and tests to match

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
macdeployqt cleanup was stripping the whole PlugIns/tls directory,
including libqsecuretransportbackend.dylib. Without it CloudDownloader
fails every HTTPS request with "TLS initialization failed". Keep the
securetransport backend (and the tls dir) in ci.yml and release.yml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the ci CI/CD workflows label Jul 13, 2026
Comment thread src/clouddownloader.cpp Outdated
Comment thread src/clouddownloader.cpp
Ugyen Dorji and others added 3 commits July 13, 2026 18:41
Resolve WeTransfer share links to a direct CDN download via the public
transfers API (no auth, CSRF token, or session cookie required):
- we.tl short links: resolve the redirect to wetransfer.com/downloads/{id}/{hash}
- POST {security_hash, intent} to /api/v4/transfers/{id}/download for a direct_link
- stream the direct_link like any other HTTPS download (served plaintext,
  so no tiny-AES-c decryption — that path stays Mega-only)

Handles both 2-part links and 3-part email links (with recipient_id),
takes the real filename from the CDN URL, and reports friendly errors
for expired or removed transfers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The WeTransfer changes tripped the cpp-linter clang-format check:
- clouddownloader.cpp: the apiUrl construction fits on one line (<=120)
- urlopendialog.cpp: adding "WeTransfer" pushed the provider string past
  the 120-column limit, so the QLabel(tr(...)) block reflows

Formatted with clang-format 18.1.8 to match the CI version exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…iders

These were pass-through providers with no dedicated code path — they only
appeared in the Open-from-URL provider list and a comment. Removing them
de-advertises the three; it does not block their URLs, which still download
via the "Any direct HTTPS link" catch-all.

- urlopendialog: drop all three from the supported list, and remove the
  "For Amazon S3, use a pre-signed URL" tip
- clouddownloader: trim the pass-through comment accordingly
- tests: generalize testNormalize_s3_unchanged -> testNormalize_directUrl_unchanged
  so pre-signed pass-through stays covered without naming a dropped provider

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dugyen dugyen self-assigned this Jul 16, 2026
…ed download

Addresses two review findings on PR #52:

- isRemoteUrl() now accepts https:// only. Plain http:// let a network
  attacker tamper with backup bytes in transit, contradicting the
  HTTPS-only security posture. Flip testIsRemoteUrl_http accordingly.
- setDownloadingState(false) unconditionally enabled the extract button;
  after a failed download from the empty state that left "Extract" clickable
  with no backup loaded. Gate it on !backupFilename.isEmpty(), matching the
  clearButton line beside it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/clouddownloader.cpp Outdated
Ugyen Dorji and others added 4 commits July 29, 2026 16:56
writeChunk() discarded QTemporaryFile::write()'s return value, so a full
or read-only temp volume produced a truncated .wpress that reached
finished() as success — the user then saw a confusing extraction/corruption
error instead of the real cause. Track a short write and fail explicitly in
onFinished(), cleaning up the partial file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses review findings on PR #52:

- abort() no longer segfaults. QNetworkReply::abort() emits finished()
  synchronously and our slots null the member in their m_aborted branch, so
  the old abort()/deleteLater() dereferenced a dangling member. A killReply()
  helper disconnects and clears the member before aborting. Add an in-flight
  abort() regression test (QTcpServer that never completes the handshake).
- Enforce HTTPS on API-resolved CDN URLs too: a pCloud/Mega/WeTransfer
  direct_link of http:// would defeat the HTTPS-only guarantee.
- pcloud host match uses an exact/dot boundary (was endsWith("pcloud.link"),
  which matched evilpcloud.link).
- Prefer the Content-Disposition filename so Google Drive downloads keep the
  real name instead of "view.wpress" (basename only, no path smuggling).
- 30s transfer timeout on every request so a stalled connection can't wedge.
- Drop dead m_isPCloud / m_isWeTransfer members.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses review findings on PR #52:

- Downloads and extractions can no longer run concurrently. A new isBusy()
  (download OR extraction) gates every entry point, and openUrlButton is
  disabled during extraction, so the two flows can't fight over the progress
  bar or re-enable each other's buttons.
- Clear File now aborts an in-flight download instead of leaving it running
  to repopulate the file the user just cleared; actionClearFile is disabled
  while downloading.
- A failed download restores the previously-open file's label instead of
  blanking it, so Extract no longer stays enabled pointing at a hidden file.
- dropEvent() prefers the text/plain URL when it preserves a fragment the
  text/uri-list one dropped (RFC 2483), so Mega #KEY drags resolve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes review item 5. While a download runs, the Clear button is
swapped for a Cancel button that calls CloudDownloader::abort() (now
crash-safe via killReply) and restores the prior UI state — giving the
user an immediate way out of a live transfer rather than waiting for the
30s transfer timeout or closing the window. The button is hidden on every
non-downloading state path (finish, fail, cancel, clear).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/mainwindow.cpp Outdated
Ugyen Dorji and others added 2 commits August 12, 2026 16:39
Mega's API returns its CDN download URL as plain http:// by default, which
startCdnDownload()'s HTTPS-only guard (added for the provider-URL review
finding) now rightly refuses — so Mega downloads regressed. Add "ssl":2 to
the API request so Mega hands back an https:// URL. The payload is already
AES-128-CTR encrypted; this keeps the transport encrypted too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cceeds

Opening a second URL deleted the current backup's temp file up front, so a
cancelled/failed second download left Extract enabled pointing at a deleted
file (review finding on openBackupFromUrl).

- openBackupFromUrl no longer deletes the previous temp file at download start
- onDownloadFinished removes it only once the new download succeeds
- CloudDownloader::onFinished now relinquishes its QTemporaryFile on success
  (releases the object, keeps the file) so a later download() can't delete the
  file the caller now owns

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dugyen

dugyen commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@developeritsme Thanks for the thorough review 🙏 — I've addressed all of it and re-requested your review. Each fix is its own commit for easy walking (f030433315253d).

Blocking

  • 1 — abort() SIGSEGV: killReply() disconnects + clears the member before abort() (your verified mechanism). Added an in-flight abort() regression test with a QTcpServer fixture.
  • 2 — Clear File: now aborts an in-flight download; actionClearFile disabled while downloading.
  • 3 — Stale backupFilename: a failed download restores the previous file's label instead of blanking it.
  • 4 — Concurrency: downloads and extractions are gated on a single isBusy(); openUrlButton is disabled during extraction.
  • 5 — Cancel/timeout: 30s setTransferTimeout on all requests, plus a Cancel button shown during downloads.

Should fix

  • 6dropEvent() prefers the fragment-preserving text/plain URL so Mega #KEY drags resolve.
  • 7startCdnDownload() rejects non-HTTPS provider-returned URLs.
  • 8 — pCloud host match now uses an exact/.pcloud.link boundary.
  • 9 — prefers the Content-Disposition filename (no more view.wpress).
  • 10 — dropped the dead m_isPCloud / m_isWeTransfer state.

Two follow-ups also fixed:

  • Mega regressed on the item-7 HTTPS check — its CDN URL is http:// by default, so I now request ssl:2 to get an https:// URL.
  • A temp-file lifecycle bug (flagged separately): opening a second URL deleted the loaded backup before the replacement succeeded — now preserved until the new download lands.

The two Notes (routing through the planned /url/resolve endpoint, and a download size cap) I've left for a follow-up PR rather than growing this one. Ready when you are!

After a successful cloud download the drop zone shows the friendly name
(e.g. mysite.wpress) while backupFilename holds the temp path. A later
cancelled or failed download restored QFileInfo(backupFilename).fileName(),
degrading the label to the temp name (traktor-a1b2c3.wpress). Cache the
display name in m_loadedDisplayName and restore that instead.

Addresses a minor item from the PR #52 round-2 review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dugyen

dugyen commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@developeritsme Thanks again for the round-2 pass 🙏 — small delta since then, re-requesting your review.

Changed since 315253dbd630b6 (1 commit):

  • Remaining item 1 (label degrades on cancel/fail): fixed. The friendly download name is now cached in m_loadedDisplayName and restored on cancel/failure, so the drop zone no longer falls back to the traktor-a1b2c3.wpress temp basename. Set on both local open and download success; cleared on Clear File.

The other two "Remaining items" I've split into follow-ups rather than growing this PR:

  • Size cap — done right (confirm-don't-block, per-provider size sources, free-disk-space guard) it touches every provider flow and needs a threshold decision, so it's its own change.
  • /url/resolve routing — the architectural direction you flagged; tracked against .claude/plan-url-resolve-endpoint.md.

That leaves only the two manual-QA boxes from your review as the merge gate:

  • Mega end-to-end — since you noted ssl:2 was untested against the live API: I've verified it returns an https:// CDN URL that serves the file, and it's in the current build — worth one real download to tick the box.
  • Local .wpress drag — one manual pass to confirm dropEvent still handles local files.

Appreciate the thorough reviews — this is in good shape now.

The description/"Supported providers" label used color: palette(mid), a
3D-shadow grey that sits almost on top of the window background in dark
mode, so the text was nearly invisible. Use the PlaceholderText palette
role instead — muted but legible in both light and dark themes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dugyen

dugyen commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@developeritsme One more small fix since my last note — re-requesting your review.

New since bd630b68917ea0 (1 commit):

  • Dark-mode readability: the description / "Supported providers" text in the Open-from-URL dialog used color: palette(mid) — a 3D-shadow grey that's nearly invisible on a dark background. Switched to the PlaceholderText palette role, which stays muted but legible in both light and dark themes.

Everything else is unchanged from my previous summary:

  • All round-1 (10) and round-2 code items addressed.
  • Size cap and /url/resolve routing are tracked as separate follow-up PRs (not part of this one — the latter is being worked in its own branch).

Merge gate is still just the two manual-QA boxes from your review — Mega end-to-end and local .wpress drag. Thanks again for the reviews 🙏

@developeritsme
developeritsme merged commit ea8cc0f into master Aug 14, 2026
8 checks passed
@developeritsme
developeritsme deleted the feat/cloud-storage-open branch August 14, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Build system/project files ci CI/CD workflows docs Documentation test Test suite changes ui User interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants