Skip to content

cmake: bump MIN_WX_VERSION to 3.2.0 and retire legacy wx.cmake - #459

Merged
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:wx-3.2-bump
Apr 22, 2026
Merged

cmake: bump MIN_WX_VERSION to 3.2.0 and retire legacy wx.cmake#459
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:wx-3.2-bump

Conversation

@got3nks

@got3nks got3nks commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Raises the minimum wxWidgets version from 2.8.12 to 3.2.0 and replaces the 311-line custom cmake/wx.cmake wrapper with an ~90-line thin shim over stock find_package(wxWidgets). Also removes one wx-2.x-era reference (wxCHECK_VERSION(2, 8, 4) in a commented-out block) and one wx 3.3 deprecation (wxTextDataObject::GetTextLength()) at the same time.

First step of the wx-3.2+ migration plan — unblocks subsequent cleanups (retiring the macOS Carbon API use, modernising boost::asio, refactoring the HTTP path onto wxWebRequest) which will each ship as their own PR.

Why 3.2 is safe as a floor

Every current-stable distribution already ships wxWidgets 3.2:

Distro wx version
Ubuntu 24.04 LTS 3.2.4
Ubuntu 25.10 3.2.8
Debian 13 3.2.8
Debian sid 3.2.9
Fedora 42/43 3.2.8
Fedora rawhide 3.2.9
Arch Linux 3.2.10
openSUSE Tumbleweed 3.2.8
MSYS2 MinGW64 3.2.x
Homebrew (macOS) 3.3.2

No mainstream distro ships 3.3 yet (released 2025-06-06, ~10 months ago), so this intentionally does not require 3.3.

On top of that, configure.ac has already been hard-failing for wx < 3.2 since 2024-09 (commit be22aea36a added the AS_IF([test "$WX_VERSION_MAJOR""$WX_VERSION_MINOR" -lt 32], AC_MSG_ERROR(...)) gate right below the old WX_CONFIG_CHECK([2.8.12]) line). The 2.8.12 floor was already nominal on the autotools side; this PR aligns the CMake path and error messages with that reality.

Changes

CMakeLists.txt + configure.ac

MIN_WX_VERSION and WX_CONFIG_CHECK both bumped to 3.2.0; the "2.8.12 or above" text in the configure.ac error message updated to match.

cmake/wx.cmake — 311 lines → ~90 lines

Replaced with a thin shim over stock find_package(wxWidgets) that still exposes wxWidgets::{BASE,CORE,NET,ADV} as INTERFACE IMPORTED targets, so existing target_link_libraries(... PRIVATE wxWidgets::CORE) call sites across src/ don't change.

Gone:

  • The WIN32 AND NOT MINGW MSVC-prebuilt-subfolder detection (WX_BASE / WX_CORE / WX_ADV / WX_NET user-supplied variables). This was the layout convention from the platforms/Windows/MSVC12/README era; the MSVC build path has bitrotted since.
  • The manual -l<name> parser that hand-walked wxWidgets_LIBRARIES, find_library'd each piece, and pruned duplicates. Modern CMake's FindwxWidgets already returns proper lists; the hand-parsing was working around an older version.
  • The CHECK_CXX_SYMBOL_EXISTS(wxUSE_UNICODE ...) probe. wx 2.8 shipped both unicode and non-unicode builds; from wx 3.0 onward unicode is mandatory, so the probe is always true.
  • The wx 3.1.2 ADV-merged-into-CORE detection. With the minimum at 3.2.0, ADV is always merged, so adv is not requested from find_package (but wxWidgets::ADV is still created when wx_NEED_ADV is set, so existing generator expressions in src/CMakeLists.txt keep resolving).

Kept: the MinGW -DUNICODE / -D_UNICODE propagation originally added in #457. MSYS2's wx-config points at the unicode wx build but doesn't emit those defines, and stock FindwxWidgets just forwards wx-config's output, so the shim still has to set them explicitly on MinGW (otherwise <wx/msw/winundef.h> lands in a mixed ANSI/UNICODE state and the build fails with LoadBitmapA/LPCSTR/LPCTSTR type mismatches).

src/MuleTextCtrl.cpp:100

-                canpaste = (data.GetTextLength() > 0);
+                canpaste = !data.GetText().IsEmpty();

wxTextDataObject::GetTextLength() is [[deprecated]] in wx 3.3 with the message "Don't call nor override this function". GetText().IsEmpty() has the same semantics and is supported on every wx version we accept.

src/utils/fileview/Print.h

Dropped the commented-out #if wxCHECK_VERSION(2, 8, 4) block — always-true with the new floor, and the wrapping #if / #else / #endif were already commented out anyway so it was just stale noise.

Every current-stable Linux distro ships wx 3.2: Ubuntu 24.04 LTS
3.2.4, Debian 13 3.2.8, Fedora 42/43 3.2.8, Arch 3.2.10, openSUSE
Tumbleweed 3.2.8.  configure.ac has also enforced wx >= 3.2 for a
while (the AS_IF on "$WX_VERSION_MAJOR""$WX_VERSION_MINOR" -lt 32
next to WX_CONFIG_CHECK), so the 2.8.12 floor in CMakeLists.txt and
in the WX_CONFIG_CHECK line has been nominal.  Raising it to 3.2.0
unblocks retiring the custom cmake/wx.cmake wrapper.

* CMakeLists.txt: MIN_WX_VERSION 2.8.12 -> 3.2.0.
* configure.ac: WX_CONFIG_CHECK and the error-message comment bumped
  to 3.2.0.
* cmake/wx.cmake: 311-line custom wrapper replaced with an ~90-line
  thin shim over stock find_package(wxWidgets).  The shim still
  exposes wxWidgets::{BASE,CORE,NET,ADV} as INTERFACE IMPORTED
  targets so existing target_link_libraries(... wxWidgets::CORE)
  call sites across src/ don't change.  What's gone:
  - the WIN32 AND NOT MINGW MSVC-prebuilt-subfolder detection
    (WX_BASE / WX_CORE / WX_ADV / WX_NET user vars);
  - the manual wxWidgets_LIBRARIES -l parser that hand-rolled what
    stock CMake's FindwxWidgets module already does;
  - the wx 3.1.2 ADV-merged-into-CORE detection: since the minimum
    is now 3.2.0, ADV is always merged, so 'adv' is not requested
    from find_package (but wxWidgets::ADV is still created when
    wx_NEED_ADV is set so existing generator expressions in
    src/CMakeLists.txt keep resolving);
  - the wxUSE_UNICODE CHECK_CXX_SYMBOL_EXISTS probe: wx 3.0+ is
    unicode-only, so the probe is always true.

  What's kept: the MinGW -DUNICODE / -D_UNICODE propagation
  originally added in PR amule-project#457.  MSYS2's wx-config points at the
  unicode wx build but does not emit those defines, and stock
  FindwxWidgets just forwards wx-config's output, so the shim
  still has to set them explicitly on MinGW; without this the
  MinGW build fails with LoadBitmapA/LPCSTR/LPCTSTR mismatches.
* src/MuleTextCtrl.cpp:100: data.GetTextLength() > 0 becomes
  !data.GetText().IsEmpty().  wxTextDataObject::GetTextLength() is
  [[deprecated]] in wx 3.3 ("Don't call nor override this
  function"); GetText().IsEmpty() has the same semantics and is
  supported on every wx version we care about.
* src/utils/fileview/Print.h: drop the now-always-true commented
  #if wxCHECK_VERSION(2, 8, 4) dead block.
@Vollstrecker

Copy link
Copy Markdown
Collaborator

Seems a good starting point. I'm not sure if there's another way of building wx, but afaik wx ships ships a useable config, so why not just using this? That would shrink the file to ~30 lines.

And if there's only cmake to get wx in, we maybe should think about retireing autotools at all, so it won't ever get out-of-sync again.

@got3nks

got3nks commented Apr 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — on the "~30 lines with wx's config" thought: I looked at it specifically, and unfortunately distro packaging isn't uniform here.

Distro Ships wxWidgetsConfig.cmake? How wx is packaged
Debian 13 libwxgtk3.2-dev No (wx-config only) autotools
Ubuntu 24.04 / 25.10 libwxgtk3.2-dev No autotools
Fedora wxGTK-devel No autotools
Homebrew wxwidgets 3.3.2 No autotools
Arch wxwidgets-gtk3 Yes CMake
MSYS2 mingw-w64-x86_64-wxwidgets3.2-msw Yes CMake

wx ships both an autotools and a CMake build system. The CMake config files (wxWidgetsConfig.cmake + targets) only get installed when wx is built via its CMake build. Most major distros — including Debian, Ubuntu, Fedora, and Homebrew — still build wx via autotools, so they ship wx-config only and no CMake config. This isn't version-specific (the same is true for wx 3.3 when built via autotools).

So find_package(wxWidgets CONFIG REQUIRED) would fail on Debian, Ubuntu, Fedora, and Homebrew — i.e. most users. The stock FindwxWidgets.cmake module path (which wraps wx-config under the hood) still has to be there.

A hybrid could work as a follow-up — try CONFIG first, fall back to MODULE — happy to do that once distros start shipping the CMake config more uniformly.

On retiring autotools: no strong opinion, but if it's on the cards I can look at it separately — it's a bigger cleanup touching configure.ac, Makefile.am files, autogen.sh, the Debian debian/rules, and the autotools CI job.

@Vollstrecker

Copy link
Copy Markdown
Collaborator

Thanks — on the "~30 lines with wx's config" thought: I looked at it specifically, and unfortunately distro packaging isn't uniform here.

I hate it when they do this.

On retiring autotools: no strong opinion, but if it's on the cards I can look at it separately — it's a bigger cleanup touching configure.ac, Makefile.am files, autogen.sh, the Debian debian/rules, and the autotools CI job.

For me it's always on cards, as it's a pita to dig through that stuff. Touching isn't the right word, I would say deleting, but I would first want to hear more oppinions an that. For the debian/ dir, there's a lot of old stuff I didn't maintain for a long time. I need to check if it is useful for cpack, otherwise I would delete it completely.

@got3nks

got3nks commented Apr 22, 2026

Copy link
Copy Markdown
Contributor Author

On debian/ + cpack: checked and confirmed it's safe to delete.

  • Debian doesn't consume it. The Debian source package is Format: 3.0 (quilt) and overlays its own .debian.tar.xz on top of the upstream tarball — our debian/ gets thrown away during dpkg-buildpackage. Debian maintains the real packaging at https://salsa.debian.org/debian/amule.git (Sandro Tosi, last changelog entry 1:2.3.3-4 on 2026-04-14, debhelper-compat=13, current deps). Ubuntu syncs from Debian, same pipeline.
  • Upstream's debian/ is 5 years stale. Top of debian/changelog is 1:2.3.3-1+git-20210424, debian/compat = 10, still references plasmamule and amule-skin-{gnome,kde4,tango,...} packages that don't exist.
  • No CPack wiring in the tree. grep -rn CPACK cmake/ CMakeLists.txt is empty. Would need a from-scratch CPack setup to leverage debian/, and even then CPack DEB's inputs are a small subset (binary description + runtime deps) — much less than what's there.
  • Only internal consumer is build_autotools in ccpp.ymlmk-build-deps -i reads debian/control for apt. If autotools retires, that reference retires with it.

So: delete debian/ + configure.ac + Makefile.ams + autogen.sh + build_autotools job — one coherent cleanup.

@mrjimenez
mrjimenez merged commit f3577b9 into amule-project:master Apr 22, 2026
8 of 9 checks passed
@Vollstrecker

Copy link
Copy Markdown
Collaborator
* **Debian doesn't consume it.**

I know, as I was in contact with Sandro when adding this, it was clear that debian won't use it.

* **Upstream's `debian/` is 5 years stale.** Top of `debian/changelog` is `1:2.3.3-1+git-20210424`, `debian/compat = 10`, still references `plasmamule` and `amule-skin-{gnome,kde4,tango,...}` packages that don't exist.

That's what I meant with "haven't maintained it a long time". In fact with the switch to qt6/plasma6 plasmamule wasn't useable the old way, that's why I started the cmake-stuff, to get that working again (doesn't work, can be also dropped).

* **No CPack wiring in the tree.** `grep -rn CPACK cmake/ CMakeLists.txt` is empty. Would need a from-scratch CPack setup to leverage `debian/`, and even then CPack DEB's inputs are a small subset (binary description + runtime deps) — much less than what's there.

The question wasn't if it used in cpack, as I know that it isn't atm., the question is, when cpack creates .deb can it be useful for that (in the future).

So: delete debian/ + configure.ac + Makefile.ams + autogen.sh + build_autotools job — one coherent cleanup.

plus plasmamule + maybe some skins that don't work + xas as xchat is also dead +

got3nks added a commit to got3nks/amule that referenced this pull request Apr 23, 2026
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.

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.

Scope
-----

No CMake build-flag changes. wxWebRequest is in wxBase_net since
wx 3.2.0 (our floor since PR amule-project#459).

* 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.
* src/amule.cpp — macOS-only std::_Exit at end of OnExit.
* src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS
  opt-out into the CMake-generated Info.plist.
* Six call sites (version check, server.met, auto-update, IP filter,
  nodes.dat, GeoIP) compile unchanged.
got3nks added a commit to got3nks/amule that referenced this pull request Apr 23, 2026
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.

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.

Scope
-----

No CMake build-flag changes. wxWebRequest is in wxBase_net since
wx 3.2.0 (our floor since PR amule-project#459).

* 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.
* src/amule.cpp — macOS-only std::_Exit at end of OnExit.
* src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS
  opt-out into the CMake-generated Info.plist.
* Six call sites (version check, server.met, auto-update, IP filter,
  nodes.dat, GeoIP) compile unchanged.
got3nks added a commit to got3nks/amule that referenced this pull request Apr 23, 2026
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.

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.

Scope
-----

No CMake build-flag changes. wxWebRequest is in wxBase_net since
wx 3.2.0 (our floor since PR amule-project#459).

* 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.
* src/amule.cpp — macOS-only std::_Exit at end of OnExit.
* src/CMakeLists.txt — macOS-only POST_BUILD plutil to inject ATS
  opt-out into the CMake-generated Info.plist.
* Six call sites (version check, server.met, auto-update, IP filter,
  nodes.dat, GeoIP) compile unchanged.
got3nks added a commit to got3nks/amule that referenced this pull request Apr 23, 2026
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 pushed a commit that referenced this pull request Apr 23, 2026
Fixes the intermittent startup SIGSEGV tracked in upstream PR #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 #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 #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.
@barracuda156

barracuda156 commented Apr 30, 2026

Copy link
Copy Markdown

This breaks Amule with native GUI (wxCocoa or Carbon) for all macOS version < 10.12 (and breaks it completely for these versions with MacPorts, which does not have wxGTK 3.2 port).

Is it really justified?

@got3nks

got3nks commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

MacOS 10.12 was released in 2016, we’re talking about nearly 10-year-old software. In my opinion, users on such an outdated OS should be comfortable running an older version of aMule (i.e., 2.3.3).

@got3nks
got3nks deleted the wx-3.2-bump branch May 3, 2026 15:20
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.

4 participants