Skip to content

http: rewrite CHTTPDownloadThread on top of wxWebRequest - #462

Merged
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:http-wxwebrequest
Apr 23, 2026
Merged

http: rewrite CHTTPDownloadThread on top of wxWebRequest#462
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:http-wxwebrequest

Conversation

@got3nks

@got3nks got3nks commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes upstream PR #455's intermittent startup SIGSEGV on Linux by removing the entire wxHTTP/wxSocket-based HTTP download path and replacing it with an event-driven wxWebRequest-based implementation on the main thread. No worker thread, no wxEpollDispatcher on the download path, no recursive redirect handler, HTTPS transparent, no nested event loops — therefore no reentrancy surface.

Stock origin/master reproduces PR #455's crash at ~1-in-27 iterations of amuled in a tight startup loop. This branch cleared 115+ iterations clean on Linux/ARM64 without a single SIGSEGV.

Platform-specific follow-ups included in the same PR because they surfaced the moment the rewrite ran on each platform:

  • macOS: Info.plist opt-out of App Transport Security, so plaintext http:// downloads still work (wxWebRequest's macOS backend is NSURLSession, which enforces ATS).
  • macOS: std::_Exit(0) at the end of CamuleApp::OnExit to side-step a wx 3.3.2 upstream bug in ~wxWebSessionURLSession that otherwise crashes the process at teardown.
  • Windows: CMake install ships an MSYS2 CA bundle next to .exe; CamuleApp::OnInit points CURL_CA_BUNDLE at it on startup. Without this, libcurl on MSYS2 builds fails every HTTPS request with error 77 on end-user machines.
  • wxGTK: progress bar safety — clamp wxWebRequest::GetBytesExpectedToReceive() at event source and in the dialog, so a -1 (unknown Content-Length) never trips wxGauge::SetRange(-1) and the ensuing GTK assertion.

wx 3.2.0 is the floor (merged via #459), so wxWebRequest is always available in wxBase_net. No new build dependency.


Why the upstream crash happens

CHTTPDownloadThread::GetInputStream handles 301/302 redirects by recursively reopening with a fresh wxHTTP, tearing down the previous one with Destroy(). Since SourceForge and several other defaults now 301 to https://, and wxHTTP does not speak HTTPS, the recursion throws. Meanwhile the socket fd of the previous wxHTTP is still registered with wxEpollDispatcher, its wxSocketImpl still on wxPendingDelete. An epoll event fires on the fd between Destroy() and the pending-delete run → handler invoked on a socket mid-teardown → segfault.

libwx_baseu_net.so [handler on dead socket]
wxEpollDispatcher::Dispatch(int)
wxConsoleEventLoop::DispatchTimeout(unsigned long)

The fault is inside libwx_baseu_net — not fixable from aMule code paths alone. Avoiding the code path is the only reliable remedy.

Why wxWebRequest rather than libcurl-direct (PR #455's approach)

PR #455's libcurl backend is a good fix. Three reasons we reached for wxWebRequest instead:

Why not a synchronous wrapper (nested wxEventLoop)

wxWebRequest in wx 3.2 is async-only; wxWebRequestSync lands in 3.3. A "sync wrapper" done by running a nested wxEventLoop while waiting for wxEVT_WEBREQUEST_STATE would reintroduce a classic reentrancy surface (modal dialogs running during the wait, menu dispatch during the wait, paint events mid-wait). We explicitly avoid this.

Instead, the class is event-driven on the main thread, and the 6 call sites of CHTTPDownloadThread already used an async-completion contract via wxEVT_CORE_FINISHED_HTTP_DOWNLOAD + CamuleApp::OnFinishedHTTPDownload. We preserve that dispatcher verbatim, so nothing changes for callers.

Once wx 3.3 / wxWebRequestSync is a reasonable distro floor, the entire class can collapse into a synchronous call on a worker thread with no wxSocket anywhere, and the current event-driven plumbing becomes a historical footnote. Create() / Run() / Stop() / OnExit() / Entry() are kept as stubs so the 6 call sites already pre-declare the eventual shape.

Reentrancy proof

  • No nested event loops anywhere on the fix path. No ProcessEvents, no wxYield.
  • No ShowModal — the progress dialog is Show(true) (modeless) as it always has been.
  • No blocking wait on the request. Start() returns immediately; state transitions arrive asynchronously.
  • Cancellation (Cancel button, StopAll) calls request.Cancel() fire-and-forget → returns → State_Cancelled event arrives later on the main loop.
  • OnFinishedHTTPDownload dispatcher unchanged — ServerList::AutoDownloadFinishedAutoUpdate retry chain across addresses.dat URLs continues to work one-iteration-per-main-loop-turn.
  • this->Destroy() (via CallAfter) runs only after State_Completed / Failed / Cancelled, so wxWebRequest's internal socket is already torn down by the time our handler is deleted.

What changed

File Change
src/HTTPDownload.{h,cpp} Class becomes wxEvtHandler; ~110-line recursive GetInputStream redirect handler deleted; Entry() / worker-thread loop deleted; OnStateEvent handles Active / Completed / Failed / Cancelled / Unauthorized; FinishAndDestroy centralises terminal-state cleanup (post shutdown to dialog, post finished to theApp, erase registry, CallAfter(delete this)); dialog UpdateGauge made defensive against -1 totals.
src/CMakeLists.txt (APPLE branch) POST_BUILD plutil -replace NSAppTransportSecurity to inject NSAllowsArbitraryLoads=true into the CMake-generated Info.plist.
src/CMakeLists.txt (WIN32+MINGW branch) Install step ships ca-bundle.crt next to the bundled DLLs so libcurl HTTPS works from a portable install — builds on top of the existing file(GET_RUNTIME_DEPENDENCIES) DLL-bundling block.
src/amule.cpp (APPLE guard) std::_Exit(0) at the end of CamuleApp::OnExit to bypass atexit (wx 3.3.2 macOS dtor bug — see below).
src/amule.cpp (Windows guard) CamuleApp::OnInit points CURL_CA_BUNDLE at the bundled ca-bundle.crt resolved from wxStandardPaths::GetExecutablePath when the user has not set one explicitly.
6 call sites (version check, server.met manual/auto, IP filter, nodes.dat, GeoIP) Zero changes.

Net: ~50 LOC added, ~260 LOC removed in HTTPDownload.cpp, one 8-line Info.plist injection, ~15 lines macOS exit guard + Windows CA-bundle hook, ~20 lines CMake install for the CA bundle. No CMake build-flag changes beyond the post-build plist injection. No new dep.

Proxy

HTTP proxy support via wxWebProxy::FromURL(...) on wx 3.3+, guarded by wxCHECK_VERSION(3,3,0) since wxWebProxy / SetProxy do not exist in 3.2. On wx 3.2 the libcurl backend still honours the standard http_proxy / https_proxy / all_proxy env vars automatically.

Not a regression vs. master: verified by reading wx 3.2.4 source — wxHTTP::SetProxyMode(bool) only flips a boolean and has no URL to route through unless wxURL::SetProxy / SetDefaultProxy is called first, which aMule never calls (git grep of entire upstream tree returns zero hits). The legacy Proxy-pref → HTTP plumbing was non-functional. SOCKS is out of scope on both paths; wx 3.3's wxWebProxy is HTTP-only and the legacy path didn't handle it either.

macOS: ATS opt-out

NSURLSession enforces App Transport Security, blocking plaintext http:// by default on 10.11+. The legacy wxSocket path sidestepped ATS because it was raw BSD sockets. First download after the rewrite reproduced:

HTTPDownload.cpp: HTTP download failed for
  http://upd.emule-security.org/server.met: The resource could not
  be loaded because the App Transport Security policy requires the
  use of a secure connection.

plutil -replace NSAppTransportSecurity -json '{"NSAllowsArbitraryLoads": true}' Info.plist as a POST_BUILD on the amule target restores the legacy behaviour. Not a new policy choice — simply keeping parity with master.

macOS: exit-time crash workaround

After the rewrite, Cmd+Q (or any shutdown after an HTTP download) aborts:

*** error for object 0x...: pointer being freed was not allocated
-[NSApplication terminate:] + 2004
exit + 44
__cxa_finalize_ranges
wxWebSession::~wxWebSession()
wxWebSessionURLSession::~wxWebSessionURLSession()

Root cause is an upstream wxWidgets 3.3.2 bug. Reading webrequest_urlsession.mm:508-515:

wxWebSessionURLSession::~wxWebSessionURLSession()
{
    [m_session release];
    [m_delegate release];
    ...
}

Per Apple's own docs, NSURLSession holds a strong reference to its delegate; -release on the session without first calling -invalidateAndCancel triggers the session's dealloc to drop the delegate ref — and the next line releases an already-freed delegate. Double-free. Fires in wx's module cleanup, in atexit's __cxa_finalize, and anywhere else the destructor runs.

Since aMule's orderly shutdown (CamuleApp::OnExit) has already saved prefs, credits, known files, shared files, server list, partfile state, joined upload/download/disk I/O/asio threads, and logged "aMule shutdown completed." before we reach the end of the function, nothing aMule-owned remains to clean up. std::_Exit(0) bypasses atexit + static destructors + wx's module cleanup, so the buggy dtor never fires and the process terminates cleanly.

Scope of the skip on macOS:

  • AMULE_APP_BASE::OnExit() — default wxApp::OnExit, essentially a no-op.
  • ~CamuleApp / ~CamuleAppCommon — only meaningful action is delete m_singleInstance, which drops the muleLock fcntl advisory lock. The kernel releases the lock on process exit anyway, so the next launch is unaffected.
  • wx module cleanup — the point; WebRequestModule::OnExit is where the buggy dtor would fire.
  • stdio buffer flush — logs have already been written line-by-line.
  • atexit() handlers — grep atexit src/ returns zero hits; aMule registers none.

Linux and Windows continue through wx's normal cleanup; the _Exit block is #if defined(__APPLE__) only and can be removed the day wx ships a fix in a version we depend on. The upstream fix is straightforward (call -invalidateAndCancel before release).

Windows: portable install with bundled CA certs

On MSYS2 builds (MINGW64, CLANGARM64), wxWebRequest is backed by libcurl built against OpenSSL. MSYS2's libcurl is compiled with an absolute --with-ca-bundle= path — e.g. /clangarm64/etc/ssl/certs/ca-bundle.crt — that exists on the developer's machine but not on any end-user box. Result: every HTTPS request fails with

libcurl error 77: Problem with the SSL CA cert (path? access rights?)
Failed to download the version check file

This affects the default version-check URL (http://amule.sourceforge.net/lastversion → 301 HTTPS), and any server that redirects HTTP→HTTPS.

Two-part fix so cmake --install produces a self-contained portable directory that works on a fresh Windows machine:

  1. CMake install-time bundling (src/CMakeLists.txt, in the existing WIN32 AND MINGW block that already uses file(GET_RUNTIME_DEPENDENCIES) to ship MSYS2 DLLs): find_file the MSYS2 CA bundle (/clangarm64/etc/ssl/cert.pem or /mingw64/etc/ssl/certs/ca-bundle.crt, as shipped by mingw-w64-*-ca-certificates), install(FILES ... RENAME ca-bundle.crt) into bin/ alongside the executables.

  2. Runtime lookup (src/amule.cpp, __WINDOWS__-guarded, at the top of CamuleApp::OnInit): if CURL_CA_BUNDLE is not set, look for ca-bundle.crt next to the running executable via wxStandardPaths::GetExecutablePath + wxFileName::SetFullName, and set the env var via wxSetEnv. Users who want to point at their own bundle can still set the env var before launch.

Result: cmake --install build --prefix /path/to/portable produces a fully self-contained tree (GUI + daemon + CLI + 34 DLLs + skins + ca-bundle.crt) that runs on any Windows ARM64 or x86_64 machine without MSYS2, with HTTPS working out of the box.

Dialog / thread lifetime

CHTTPDownloadDialog holds a raw pointer to its owning CHTTPDownloadThread. On successful completion, FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app, then CallAfters its own delete. Processing on the main loop:

  1. Dialog's OnShutdown runs → Destroy() queues the dialog on wxPendingDelete.
  2. CallAfter lambda runs → thread deleted.
  3. Idle → DeletePendingObjects~CHTTPDownloadDialogStopDownload() → dereferences m_owneruse-after-free.

Fix: OnShutdown nulls m_owner immediately, so the subsequent dtor sees m_owner == NULL and skips the whole StopDownload dance. The reverse direction (dialog closes first, cancels download) was already safe via DetachCompanion() on the thread side.

Progress bar safety (wxGTK)

wxWebRequest::GetBytesExpectedToReceive() returns wxInvalidOffset (= −1) when the server omits Content-Length, and can also return 0 early in the state machine before headers have been parsed. Forwarding either value unchanged to CHTTPDownloadDialog::UpdateGauge ended up in wxGauge::SetRange(-1), which puts m_rangeMax into an invalid state and trips an assertion on the next repaint on wxGTK:

./src/gtk/gauge.cpp(90): assert "0 <= m_gaugePos && m_gaugePos <= m_rangeMax"
  failed in DoSetGauge(): invalid gauge position in DoSetGauge()

Reproducible on the Ubuntu IP-filter update dialog. Fixed in two places:

  • OnStateEvent clamps GetBytesExpectedToReceive() to 0 before posting the progress event, so "unknown total" never reaches the dialog as a negative value.
  • UpdateGauge only touches the gauge when total > 0, and clamps current to [0, total] before SetValue, so out-of-range values can never reach the widget regardless of what the caller passes.

Follow-up / future work

  • When wx 3.3 becomes a reasonable distro floor, the stub Create() / Run() / Stop() / OnExit() / Entry() can be turned into a real synchronous implementation via wxWebRequestSync, simplifying the class.
  • Report the macOS ~wxWebSessionURLSession delegate double-release upstream to wx; drop the _Exit workaround when it lands.
  • Optionally bridge aMule's Proxy prefs to http_proxy env vars on wx 3.2 via wxSetEnv, so GUI-configured proxy works on 3.2 builds too (out of scope here — legacy path never honoured the pref either).

Fixes the intermittent startup SIGSEGV tracked in upstream PR amule-project#455.
The crash happens on Linux when a startup HTTP download redirects to
https://, which wxHTTP cannot speak: wxHTTP::Destroy() schedules the
current handler for deferred deletion, the redirect handler throws,
and before the deferred delete runs an epoll event fires on the
still-registered fd — invoking a method on a socket mid-teardown.
Reproducible on stock origin/master (~1-in-27 runs of amuled in a
tight startup loop); the crash-hunt.sh script from PR amule-project#455 passes
>115 iterations clean on Linux/ARM64 with this rewrite. The fault
is inside libwx_baseu_net so it cannot be fixed inside aMule's code
paths — the only reliable remedy is to avoid the wxSocket-based HTTP
path entirely.

The fix is to stop doing HTTP on a worker thread with wxSocket under
the hood. CHTTPDownloadThread now:

* Inherits wxEvtHandler instead of CMuleThread. No worker thread, no
  wxSocket, no wxEpollDispatcher on the download path.

* Owns a wxWebRequest from wxWebSession::GetDefault(), kicks it off
  from the constructor on the main thread, binds wxEVT_WEBREQUEST_STATE
  on the same handler, and lets state transitions drive everything:
  Active → progress event to the optional dialog, Completed → rename
  wx's storage temp file into the caller-supplied path, Failed /
  Cancelled → clean up, Unauthorized → treat as failure. On every
  terminal state the object posts wxEVT_CORE_FINISHED_HTTP_DOWNLOAD
  to the existing dispatcher (CamuleApp::OnFinishedHTTPDownload,
  unchanged) and CallAfter()s its own destruction, so wxWebRequest's
  internal socket is already torn down by the time the handler dies.

* Keeps Create() / Run() / Stop() / OnExit() / Entry() as stubs, so
  the six call sites (new + Create + Run) compile unchanged. When
  wx 3.3's wxWebRequestSync becomes a reasonable floor on distro
  defaults those stubs can be made real and the event-driven
  plumbing collapsed into a synchronous call on a worker thread with
  no wxSocket anywhere.

Reentrancy: no nested event loops anywhere on the fix path. No
ShowModal (the existing progress dialog has always been modeless).
No blocking wait on the request. Cancellation via the dialog's
Cancel button or the app-wide StopAll() is fire-and-forget — it
calls Cancel() on the request, returns immediately, and State_Cancelled
arrives later on the main loop. Avoids the class of bug that would
come from a "sync wrapper = nested wxEventLoop" trick on wx 3.2.

Redirects (including HTTP→HTTPS) are handled by wxWebRequest
transparently, which removes the ~110-line recursive GetInputStream
redirect handler — the actual seat of the upstream race.

Proxy: a static ApplyProxyToDefaultSession() helper maps the existing
CProxyData prefs onto wxWebSession::SetProxy, applied from the ctor.
wxWebProxy / SetProxy are wx 3.3+ only, so the body is guarded with
wxCHECK_VERSION(3,3,0); on wx 3.2 the libcurl backend still honours
http_proxy / https_proxy / all_proxy env vars, so env-driven proxy
keeps working. The legacy wxHTTP path never honoured aMule's Proxy
prefs either (it only called SetProxyMode(bool) with no URL ever
registered via wxURL::SetProxy / SetDefaultProxy anywhere in the
codebase), so this is not a regression. SOCKS is intentionally
skipped even on 3.3+ — wxWebProxy is HTTP-only. A libcurl-direct
backend would be needed if SOCKS support matters, out of scope here.

macOS App Transport Security opt-out
------------------------------------

wxWebRequest on macOS is backed by NSURLSession, which enforces ATS
and blocks plaintext http:// by default. The legacy wxHTTP/wxSocket
path bypassed ATS because it used raw BSD sockets; the rewrite
inherits NSURLSession's policy instead. Without the opt-out every
startup download to an http:// URL fails at the OS level —
reproducible on the first server.met update:

    HTTPDownload.cpp: HTTP download failed for
      http://upd.emule-security.org/server.met: The resource could
      not be loaded because the App Transport Security policy
      requires the use of a secure connection.

Inject NSAppTransportSecurity:NSAllowsArbitraryLoads=true via plutil
as a POST_BUILD step on the `amule` target in src/CMakeLists.txt.
Matches the legacy path's behaviour, not a new policy choice.

macOS exit-time crash work-around
---------------------------------

wx 3.3.2 has a bug in wxWebSessionURLSession::~wxWebSessionURLSession:
it releases the NSURLSession and the delegate separately without
first calling -invalidateAndCancel. NSURLSession retains its delegate
strongly, so the session's dealloc already drops the delegate ref —
wx's subsequent release hits a freed object and the process aborts
with "pointer being freed was not allocated". This fires in wx module
cleanup / atexit / __cxa_finalize on any Mac build after any HTTP
download (version check, server.met, …).

By the end of CamuleApp::OnExit we have saved state, joined threads,
and flushed logs — nothing aMule-owned remains to clean up. std::_Exit
bypasses atexit and static destructors, so the buggy wx dtor never
runs and the process terminates cleanly. Linux / Windows continue
through wx's normal cleanup; this macOS-only block can be dropped
once the upstream wx fix lands in a release we depend on.

Windows portable install: CA bundle for libcurl HTTPS
-----------------------------------------------------

wxWebRequest on MSYS2 (MINGW64 / CLANGARM64) is backed by libcurl
built with OpenSSL. MSYS2 libcurl is compiled with an absolute
--with-ca-bundle= path (e.g. /clangarm64/etc/ssl/certs/ca-bundle.crt)
that only exists on the developer's machine. On an end-user box the
CA bundle isn't there and every HTTPS request (including the HTTP→
HTTPS redirect that hits the default startup URLs) fails with
"libcurl error 77: Problem with the SSL CA cert".

Two-part fix so portable zips work everywhere:

* src/CMakeLists.txt (WIN32 AND MINGW install block): ship the MSYS2
  CA bundle alongside the bundled DLLs as bin/ca-bundle.crt.
* src/amule.cpp (CamuleApp::OnInit, __WINDOWS__-guarded): if
  CURL_CA_BUNDLE is not already set in the environment, point it at
  the bundled file resolved from wxStandardPaths::GetExecutablePath.

Users running from a portable directory now get working HTTPS without
any shell env setup; users who want to point at their own bundle
still can by setting CURL_CA_BUNDLE before launch.

Dialog / thread lifetime fix
----------------------------

CHTTPDownloadDialog holds a raw pointer (m_owner) to the owning
CHTTPDownloadThread. On successful download the ordered flow is:

  FinishAndDestroy posts wxEVT_HTTP_SHUTDOWN to the dialog, posts
  wxEVT_CORE_FINISHED_HTTP_DOWNLOAD to the app dispatcher, then
  CallAfter()s `delete this`.
  Main loop: dialog OnShutdown → Destroy() (queues the dialog in
  wxPendingDelete), then app dispatcher runs, then CallAfter runs
  and deletes the thread. Later, idle → DeletePendingObjects runs
  the dialog dtor — which used to dereference the now-freed m_owner
  via StopDownload → ~vtable lookup → crash.

Null the dialog's m_owner inside OnShutdown, so its dtor (which runs
later from wxPendingDelete) no longer touches the already-deleted
thread. Equivalent cleanup on the reverse direction (dialog closed
before download completes) was already handled via
DetachCompanion + Stop in StopDownload.

Progress bar safety (wxGTK)
---------------------------

wxWebRequest::GetBytesExpectedToReceive() returns wxInvalidOffset (-1)
before Content-Length is known (or indefinitely if the server never
sends it). Forwarding -1 into CHTTPDownloadDialog::UpdateGauge ended
up calling wxGauge::SetRange(-1), which trips an assertion on the
next repaint on wxGTK (./src/gtk/gauge.cpp:90, reproducible on IP
filter updates on Ubuntu). Clamp the expected size at the event
source, and make UpdateGauge only touch the gauge when the total is
known and current <= total, so out-of-range values can never reach
the widget.

Scope
-----

wxWebRequest is in wxBase_net since wx 3.2.0 (our floor since PR amule-project#459),
so no new build dependency beyond install-time packaging of an MSYS2
CA bundle on Windows.

* src/HTTPDownload.{h,cpp} — class becomes wxEvtHandler, legacy
  wxThread-style method stubs with a wx 3.3 / wxWebRequestSync TODO,
  DetachCompanion() on the owning side + m_owner nulling on the
  dialog side to make the handshake safe from both directions,
  UpdateGauge clamped.
* src/amule.cpp — macOS-only std::_Exit at end of OnExit;
  Windows-only CURL_CA_BUNDLE fallback to the bundled ca-bundle.crt.
* src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS
  opt-out into the CMake-generated Info.plist; Windows-only install
  of the MSYS2 CA bundle alongside the bundled DLLs.
* Six call sites (version check, server.met, auto-update, IP filter,
  nodes.dat, GeoIP) compile unchanged.
@mrjimenez
mrjimenez merged commit 86c4769 into amule-project:master Apr 23, 2026
5 checks passed
got3nks added a commit to got3nks/amule that referenced this pull request Apr 29, 2026
aMule's CMakeLists.txt now hard-requires wxWebRequest (via the HTTP
download path that landed in PR amule-project#462). On Linux, the backend is
libcurl; without --with-libcurl at wx configure time, wxUSE_WEBREQUEST
ends up 0 and the aMule cmake step fails:

    wxWidgets was found but wxUSE_WEBREQUEST is 0 in this build.

Add libcurl4-openssl-dev to the apt install + --with-libcurl to the
wx configure flags.
got3nks added a commit to got3nks/amule that referenced this pull request May 3, 2026
… Releases API

`amule.cpp:636` was hitting `http://amule.sourceforge.net/lastversion`,
a plain-text `MAJOR.MINOR.UPDATE` file unmaintained since the project
moved to GitHub years ago.  Repoint the request at
`https://api.github.com/repos/amule-project/amule/releases/latest`,
which returns JSON describing the most recent non-prerelease,
non-draft Release.

Pairs with the release.yml flow added in amule-project#520: once a stable tag is
published on GitHub, every aMule installation with version-check
enabled picks it up automatically on next startup, with no
maintainer step beyond un-drafting the Release.  `/releases/latest`
excludes pre-releases by design, so users on 2.3.3 stable won't be
prompted to upgrade when we tag `3.0.0-beta` / `3.0.0-rc1` —
only when 3.0.0 stable is published.

Parser changes in `CheckNewVersion()`:

 - Concatenate all lines of the downloaded file before regex-matching
   (the JSON body is pretty-printed across many lines).
 - Extract `tag_name` via `wxRegEx` — simpler than dragging in a
   full JSON parser for one well-known field.
 - Strip optional `v` prefix and any pre-release / build-metadata
   suffix (`-beta`, `-rc1`, `+build42`) before the integer
   comparison.
 - Treat tags with fewer than three components (e.g. `3.1`) as
   missing-field-= 0 rather than erroring out.
 - Clean up the temp file in early-error paths too (the original
   only removed it on the success path).

HTTPS works without transport changes — wxWebRequest support landed
in amule-project#462.  The `s_NewVersionCheck` pref still controls whether the
request fires at all.
mrjimenez pushed a commit that referenced this pull request May 3, 2026
… Releases API

`amule.cpp:636` was hitting `http://amule.sourceforge.net/lastversion`,
a plain-text `MAJOR.MINOR.UPDATE` file unmaintained since the project
moved to GitHub years ago.  Repoint the request at
`https://api.github.com/repos/amule-project/amule/releases/latest`,
which returns JSON describing the most recent non-prerelease,
non-draft Release.

Pairs with the release.yml flow added in #520: once a stable tag is
published on GitHub, every aMule installation with version-check
enabled picks it up automatically on next startup, with no
maintainer step beyond un-drafting the Release.  `/releases/latest`
excludes pre-releases by design, so users on 2.3.3 stable won't be
prompted to upgrade when we tag `3.0.0-beta` / `3.0.0-rc1` —
only when 3.0.0 stable is published.

Parser changes in `CheckNewVersion()`:

 - Concatenate all lines of the downloaded file before regex-matching
   (the JSON body is pretty-printed across many lines).
 - Extract `tag_name` via `wxRegEx` — simpler than dragging in a
   full JSON parser for one well-known field.
 - Strip optional `v` prefix and any pre-release / build-metadata
   suffix (`-beta`, `-rc1`, `+build42`) before the integer
   comparison.
 - Treat tags with fewer than three components (e.g. `3.1`) as
   missing-field-= 0 rather than erroring out.
 - Clean up the temp file in early-error paths too (the original
   only removed it on the success path).

HTTPS works without transport changes — wxWebRequest support landed
in #462.  The `s_NewVersionCheck` pref still controls whether the
request fires at all.
@got3nks
got3nks deleted the http-wxwebrequest branch May 3, 2026 15:19
ngosang pushed a commit to ngosang/amule that referenced this pull request Jul 13, 2026
… parity (amule-project#434) (amule-project#462)

aMule already publishes notes to Kad (STORENOTES) and answers other clients'
note lookups, but nothing triggered a retrieval and the results were never
surfaced. This wires the read side end to end and fixes two rating-encoding
divergences from eMule 0.70b. Scope is the download side; shared-file parity
is a planned follow-up.

Core:
- CKnownFile::RequestKadNoteSearch() fires an on-demand NOTES lookup, guarded
  to files in the shared list / download queue. A per-file running flag is set
  on start and cleared in ~CSearch; both edges, and each note arrival, call
  MarkECChanged() so the partfile is re-emitted in the next incremental EC
  update - this is how amulegui / amuleapi learn the lookup started, stream in
  results live, and see it finish (GET_UPDATE otherwise skips an unchanged
  partfile). When the lookup can't start, the specific reason (Kad down,
  a search already using this hash, file not shared/queued, ...) is logged at
  notice level so it is visible in release builds.
- Kad search-result rating: decode raw 0-5 for Kad keyword hits instead of the
  ed2k server-packed (x&0xF)/3 formula. (Kad keyword results carry no rating
  tag in practice - ratings live in NOTES - but the decode is now correct if
  one is present. ed2k results are unchanged.)
- ed2k client-write: send the rating packed (x51) to a peer client, raw to a
  server, matching eMule (remote clients previously decoded aMule's as 0).

GUI (monolithic + amulegui):
- "Get from Kad" button in the download comments dialog; retrieved notes merge
  into the existing comments list. A timer auto-refreshes while the lookup runs
  and stops when the daemon reports it finished (60s safety cap). "Show all
  comments" is enabled when Kad is connected (not only when source comments
  already exist), so the button is reachable for a file with no comments yet.
- amulegui triggers over EC (EC_OP_SHARED_FILE_SEARCH_KAD_NOTES); notes ride
  the existing partfile-comments channel; the running flag is surfaced via a
  new EC_TAG_PARTFILE_KAD_COMMENT_SEARCHING so the remote dialog is stateful.

REST (amuleapi):
- POST /downloads/{hash}/comments triggers the lookup (202 Accepted).
- kad_search_running exposed on the download object (list, detail, GET
  .../comments) and on the download_added/_updated SSE event, so clients can
  watch the start -> finish edge.
- New comments_updated SSE event carries a download's full comment list
  (retrieved Kad notes + ed2k source comments) whenever it changes, so web
  clients get live comments without polling.
- curl tests (04, 22) + REFERENCE.md / EVENTS.md updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants