Compare commits
122 Commits
3.49.18
...
zz-pidreus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d22b59809 | ||
|
|
65940365ee | ||
|
|
95c03624dd | ||
|
|
bfb9d4611d | ||
|
|
de484e0670 | ||
|
|
b6d15c7bc8 | ||
|
|
db22036173 | ||
|
|
50f151e11b | ||
|
|
729f823db9 | ||
|
|
01274a93f5 | ||
|
|
bd765e4ed6 | ||
|
|
17e3d241fa | ||
|
|
ee20bfbb2b | ||
|
|
bde9eadb05 | ||
|
|
88f0a0174d | ||
|
|
696553b0f9 | ||
|
|
99d494bd39 | ||
|
|
53eace3cfe | ||
|
|
1dcfcf491f | ||
|
|
14e9108049 | ||
|
|
5c068a3c65 | ||
|
|
e9336b4d87 | ||
|
|
11c357e73a | ||
|
|
94d2d79217 | ||
|
|
52ac9ee142 | ||
|
|
d432772e1e | ||
|
|
997160c98f | ||
|
|
c3a5deef57 | ||
|
|
b33ef757e5 | ||
|
|
9bb989053d | ||
|
|
c27d55c345 | ||
|
|
40e94df2cc | ||
|
|
c71f37189a | ||
|
|
cb91ecaedc | ||
|
|
faa6964d18 | ||
|
|
fce40a5511 | ||
|
|
0f3dbcded4 | ||
|
|
04c75c430a | ||
|
|
0e0072ec03 | ||
|
|
ceab9c834b | ||
|
|
082d87e284 | ||
|
|
58e8aec88f | ||
|
|
ce66c2391a | ||
|
|
49e93b171b | ||
|
|
3ea7a773fa | ||
|
|
5175cf986f | ||
|
|
d25dd436ee | ||
|
|
0fe1e2ba34 | ||
|
|
9bc300296f | ||
|
|
a6315d04a3 | ||
|
|
893b542953 | ||
|
|
3232f81373 | ||
|
|
4a5854f31d | ||
|
|
df0fbd4711 | ||
|
|
77d03052a6 | ||
|
|
628fc70727 | ||
|
|
7ff60e9de7 | ||
|
|
4588831dcd | ||
|
|
adbc46a5b8 | ||
|
|
249b96b975 | ||
|
|
d8402d7ad2 | ||
|
|
b268caa220 | ||
|
|
fb59dd6aba | ||
|
|
ef100363a7 | ||
|
|
c3e1a202c1 | ||
|
|
f55f68c94e | ||
|
|
749de004c7 | ||
|
|
09e3e1f62b | ||
|
|
4364e6c93e | ||
|
|
35319c05be | ||
|
|
e4fbe856e5 | ||
|
|
4f49eec86b | ||
|
|
f8ed3e771f | ||
|
|
4231a141d3 | ||
|
|
314ee45350 | ||
|
|
54d93200a4 | ||
|
|
6e98e33495 | ||
|
|
67c4668a3b | ||
|
|
e6015e29bb | ||
|
|
ba53bca32e | ||
|
|
5b59cdb159 | ||
|
|
c520d81f6b | ||
|
|
0748fa44e9 | ||
|
|
0ced83ccfc | ||
|
|
171ffb0754 | ||
|
|
e12fe63246 | ||
|
|
ff266a0e61 | ||
|
|
dfc0efff3a | ||
|
|
a0622bcd04 | ||
|
|
f5fbd1a82e | ||
|
|
496e85a3ac | ||
|
|
d4b77e53dc | ||
|
|
507f3565e8 | ||
|
|
4df1434b56 | ||
|
|
d85d91f8c6 | ||
|
|
b16ffc7be2 | ||
|
|
defd6735f3 | ||
|
|
178f9156e5 | ||
|
|
ee5db0a9e6 | ||
|
|
edeada647c | ||
|
|
562f6790ec | ||
|
|
cbb2257847 | ||
|
|
0e95c58865 | ||
|
|
8184b45696 | ||
|
|
62ca6e087a | ||
|
|
7c6e5c3018 | ||
|
|
1878595043 | ||
|
|
057648e335 | ||
|
|
7a7206d7f1 | ||
|
|
e4df3b387c | ||
|
|
58ddc5a6b1 | ||
|
|
fb01ad6f65 | ||
|
|
1a211555d0 | ||
|
|
f2abfc36ef | ||
|
|
b2cd2a51d2 | ||
|
|
562cd1c867 | ||
|
|
a2a20ef684 | ||
|
|
bfe0a4a7f7 | ||
|
|
03c30e17ce | ||
|
|
1a45e0237b | ||
|
|
2a44dc1728 | ||
|
|
23e182281c |
3
.gitattributes
vendored
@@ -6,6 +6,3 @@
|
||||
# bash die on $'\r' on every line of them.
|
||||
*.test text eol=lf
|
||||
*.sh text eol=lf
|
||||
|
||||
# Scoped to this file so a union can never touch build logic.
|
||||
tests/tests-list.mk merge=union
|
||||
|
||||
2
.github/workflows/appstream-network.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Install appstreamcli
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends appstream
|
||||
appstreamcli --version
|
||||
|
||||
|
||||
255
.github/workflows/ci.yml
vendored
@@ -38,10 +38,11 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential clang autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev \
|
||||
appstream appstream-util
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -79,6 +80,27 @@ jobs:
|
||||
- name: Build
|
||||
run: make -j"$(nproc)"
|
||||
|
||||
# FTP_PASV is 1 in every build, so #if !FTP_PASV never reaches a compiler
|
||||
# and rots unnoticed (#1091). Compiling is all this proves; nothing runs it.
|
||||
- name: Compile the FTP active-mode branch
|
||||
run: |
|
||||
set -euo pipefail
|
||||
objs="src/libhttrack_la-htsftp.lo src/libhttrack_la-htsftp.o src/.libs/libhttrack_la-htsftp.o"
|
||||
# accept() marks active-mode code: absent from the object Build made,
|
||||
# present below. One-sided, it passes on a file that ignored the define.
|
||||
syms=$(nm --undefined-only src/.libs/libhttrack_la-htsftp.o)
|
||||
if grep -qw accept <<<"$syms"; then
|
||||
echo "::error::accept() is reachable with FTP_PASV=1; the marker no longer discriminates"
|
||||
exit 1
|
||||
fi
|
||||
rm -f $objs
|
||||
# Through CC: overriding CPPFLAGS would drop configure's
|
||||
# -D_FORTIFY_SOURCE and the object-size warnings that come with it.
|
||||
make -C src libhttrack_la-htsftp.lo CC="$CC -DFTP_PASV=0 -Werror"
|
||||
syms=$(nm --undefined-only src/.libs/libhttrack_la-htsftp.o)
|
||||
grep -qw accept <<<"$syms" || { echo "::error::-DFTP_PASV=0 built no active-mode code"; exit 1; }
|
||||
rm -f $objs
|
||||
|
||||
# A backstop only: tests/test-timeout.sh bounds each test, and a healthy run
|
||||
# is a minute here, two on macOS. Without it a stall ran to the job's 6h default.
|
||||
- name: Test
|
||||
@@ -107,7 +129,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -137,6 +159,108 @@ jobs:
|
||||
if: failure()
|
||||
run: cat tests/test-suite.log 2>/dev/null || true
|
||||
|
||||
# Reproduces the buildds that refuse TEST-NET-1 instead of dropping it, where
|
||||
# every fixture needing a stalled connect must skip rather than fail (#1108).
|
||||
hostile-network:
|
||||
name: build (network refuses TEST-NET-1)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev iproute2
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
set -euo pipefail
|
||||
autoreconf -fi
|
||||
./configure
|
||||
|
||||
- name: Build
|
||||
run: make -j"$(nproc)"
|
||||
|
||||
- name: Test on a network that refuses TEST-NET-1
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
set -euo pipefail
|
||||
jobs=$(( $(nproc) * 2 )); [ "$jobs" -le 16 ] || jobs=16
|
||||
tools/hostile-net.sh make check -j"$jobs"
|
||||
# A refusal landing before connect() returns skips on the older probe
|
||||
# instead, leaving the settle this leg exists to guard unrun.
|
||||
cat tests/246_engine-connect-stop.log
|
||||
grep -q 'within 500 ms' tests/246_engine-connect-stop.log
|
||||
|
||||
- name: Print the test log on failure
|
||||
if: failure()
|
||||
run: cat tests/test-suite.log 2>/dev/null || true
|
||||
|
||||
# The Ubuntu buildds run a development release every runner above is well behind.
|
||||
# That gap is what shipped 3.49.18 broken on every Ubuntu architecture but
|
||||
# riscv64, when 25.10 made uutils the default coreutils (#1042).
|
||||
ubuntu-devel:
|
||||
name: build (Ubuntu devel, uutils coreutils)
|
||||
runs-on: ubuntu-24.04
|
||||
container: ubuntu:devel
|
||||
timeout-minutes: 40
|
||||
steps:
|
||||
# Before checkout: the container has no git, so submodules would be lost.
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev \
|
||||
git ca-certificates python3 procps
|
||||
|
||||
- name: Assert the coreutils dispatch on argv[0]
|
||||
# The property #1042 broke on, not the version banner, which uutils has
|
||||
# already reworded once. Without it the job quietly becomes a slower copy
|
||||
# of the one above.
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ln -s "$(command -v sleep)" /tmp/not-a-coreutil
|
||||
if /tmp/not-a-coreutil 0; then
|
||||
echo "::error::coreutils here take any argv[0]; this image no longer guards uutils" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
set -euo pipefail
|
||||
autoreconf -fi
|
||||
./configure
|
||||
# A renamed dev package would disable a codec or TLS here, and the tests
|
||||
# that cover them would SKIP rather than fail.
|
||||
grep -q "define HTS_USEBROTLI 1" config.h
|
||||
grep -q "define HTS_USEZSTD 1" config.h
|
||||
grep -q "define HTS_USEOPENSSL 1" config.h
|
||||
|
||||
- name: Build
|
||||
run: make -j"$(nproc)"
|
||||
|
||||
- name: Test
|
||||
timeout-minutes: 25
|
||||
run: |
|
||||
set -euo pipefail
|
||||
jobs=$(( $(nproc) * 2 )); [ "$jobs" -le 16 ] || jobs=16
|
||||
make check -j"$jobs"
|
||||
|
||||
- name: Print the test log on failure
|
||||
if: failure()
|
||||
run: cat tests/test-suite.log 2>/dev/null || true
|
||||
|
||||
# Portability: build and test on macOS (Darwin/clang) on a native runner --
|
||||
# no VM. The tree has no __APPLE__ branches, so Darwin exercises the
|
||||
# generic-Unix path on a second libc and kernel. brew's openssl@3 is keg-only,
|
||||
@@ -231,7 +355,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -340,7 +464,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential gcc-multilib autoconf automake libtool \
|
||||
autoconf-archive zlib1g-dev:i386 libssl-dev:i386 \
|
||||
@@ -383,7 +507,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -438,7 +562,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential clang autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev
|
||||
@@ -487,7 +611,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential clang autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -510,6 +634,33 @@ jobs:
|
||||
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||
run: bash fuzz/run-fuzzers.sh fuzz check
|
||||
|
||||
# Catches #1030: the job above links ASan, whose runtime already defines the
|
||||
# sancov hooks the instrumentation calls, hiding a program left without one.
|
||||
fuzz-no-sanitizer:
|
||||
name: build (fuzzers, no sanitizer runtime, clang)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential clang autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
|
||||
- name: Configure (fuzzers, static)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
autoreconf -fi
|
||||
./configure CC=clang --enable-fuzzers --disable-shared
|
||||
|
||||
- name: Build
|
||||
run: make -j"$(nproc)"
|
||||
|
||||
# Optional-dependency build: compile and test with HTTPS/OpenSSL disabled --
|
||||
# the configuration users on minimal systems build, and one libssl is not even
|
||||
# installed here so configure cannot silently re-enable it. The matrix above
|
||||
@@ -526,7 +677,7 @@ jobs:
|
||||
- name: Install build dependencies (no libssl)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive zlib1g-dev
|
||||
|
||||
@@ -549,6 +700,47 @@ jobs:
|
||||
if: failure()
|
||||
run: cat tests/test-suite.log 2>/dev/null || true
|
||||
|
||||
# The cell of #1139: a libc exporting getrandom() without shipping
|
||||
# <sys/random.h>. Nothing else reaches it, and it is a hard build break when
|
||||
# the call is not gated on the header. It also runs the /dev/urandom fallback.
|
||||
no-getrandom:
|
||||
name: build (getrandom header hidden)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
|
||||
- name: Configure (sys/random.h hidden from configure)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
autoreconf -fi
|
||||
./configure ac_cv_header_sys_random_h=no
|
||||
# A libc without getrandom() at all would pass this job vacuously.
|
||||
grep -q "define HAVE_GETRANDOM 1" config.h
|
||||
! grep -q "define HAVE_SYS_RANDOM_H" config.h
|
||||
|
||||
- name: Build
|
||||
run: make -j"$(nproc)"
|
||||
|
||||
- name: Test
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
jobs=$(( $(nproc) * 2 )); [ "$jobs" -le 16 ] || jobs=16
|
||||
make check -j"$jobs"
|
||||
|
||||
- name: Print the test log on failure
|
||||
if: failure()
|
||||
run: cat tests/test-suite.log 2>/dev/null || true
|
||||
|
||||
# OpenSSL 4.0 removes the API 3.x deprecated. Building with that set hidden
|
||||
# keeps the engine ready for it: a deprecated call fails here rather than the
|
||||
# day a distro moves. Compile-only, since the headers are what is under test.
|
||||
@@ -563,7 +755,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -642,7 +834,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -686,6 +878,7 @@ jobs:
|
||||
fi
|
||||
|
||||
lint:
|
||||
# The name is a required status check on master; renaming it blocks every PR.
|
||||
name: lint (shellcheck, shfmt)
|
||||
runs-on: ubuntu-24.04
|
||||
# Every tracked shell script; the globs expand at run time. Kept here so the
|
||||
@@ -702,22 +895,25 @@ jobs:
|
||||
src/webhttrack.in
|
||||
tests/*.sh
|
||||
tests/*.test
|
||||
tools/macos-app.sh
|
||||
tools/macos-bundle.sh
|
||||
tools/macos-release.sh
|
||||
tools/mkdeb.sh
|
||||
tools/*.sh
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Install linters
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
# noble ships shfmt 3.8.0 (universe), matching the pinned local dev
|
||||
# version; use it rather than fetching a release binary from github.com.
|
||||
sudo apt-get install -y --no-install-recommends shellcheck shfmt appstream
|
||||
sudo apt-get install -y --no-install-recommends shellcheck shfmt appstream \
|
||||
appstream-util
|
||||
# Pinned to the local dev versions: black's stable style moves across
|
||||
# majors, so the runner image's build would reformat on its own schedule.
|
||||
pip install --quiet black==25.1.0 flake8==7.1.1
|
||||
shfmt --version
|
||||
appstreamcli --version
|
||||
black --version
|
||||
flake8 --version
|
||||
|
||||
- name: shellcheck
|
||||
run: shellcheck $SHELL_SCRIPTS
|
||||
@@ -725,6 +921,27 @@ jobs:
|
||||
- name: shfmt
|
||||
run: shfmt -d -i 4 $SHELL_SCRIPTS
|
||||
|
||||
# Read the list rather than hand-keeping one: a .py added in a new
|
||||
# directory then gets checked without anyone widening a glob.
|
||||
- name: List the tracked Python files
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mapfile -t py < <(git ls-files '*.py')
|
||||
test "${#py[@]}" -gt 0 # flake8 given no paths exits 0, checking nothing
|
||||
echo "${#py[@]} tracked Python files"
|
||||
echo "PYTHON_FILES=${py[*]}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: black
|
||||
run: black --check --diff $PYTHON_FILES
|
||||
|
||||
- name: flake8
|
||||
run: flake8 $PYTHON_FILES
|
||||
|
||||
# A misnamed test falls outside the glob "make check" runs, so it goes
|
||||
# missing without failing anything.
|
||||
- name: test file names
|
||||
run: bash tests/check-test-names.sh
|
||||
|
||||
# MSBuild rejects a malformed .vcxproj with a bare MSB4025 and no build, so
|
||||
# catch it here in seconds rather than on a Windows runner minutes in.
|
||||
- name: XML well-formedness (MSBuild project files)
|
||||
@@ -742,6 +959,8 @@ jobs:
|
||||
set -euo pipefail
|
||||
appstreamcli validate --no-net --explain \
|
||||
html/server/div/com.httrack.WebHTTrack.metainfo.xml
|
||||
appstream-util validate-relax --nonet \
|
||||
html/server/div/com.httrack.WebHTTrack.metainfo.xml
|
||||
|
||||
# Check clang-format on CHANGED LINES ONLY. The engine predates clang-format
|
||||
# (it was shaped by an old Visual Studio formatter) and does not round-trip,
|
||||
@@ -763,7 +982,7 @@ jobs:
|
||||
| sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc >/dev/null
|
||||
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main" \
|
||||
| sudo tee /etc/apt/sources.list.d/llvm-19.list >/dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends clang-format-19
|
||||
# The clang-format-19 package ships the git-clang-format driver;
|
||||
# expose it unsuffixed so "git clang-format" finds it.
|
||||
|
||||
6
.github/workflows/codeql.yml
vendored
@@ -33,12 +33,12 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev
|
||||
|
||||
- uses: github/codeql-action/init@v4.37.4
|
||||
- uses: github/codeql-action/init@v4.37.6
|
||||
with:
|
||||
languages: c-cpp
|
||||
build-mode: manual
|
||||
@@ -67,6 +67,6 @@ jobs:
|
||||
./configure
|
||||
make -j"$(nproc)"
|
||||
|
||||
- uses: github/codeql-action/analyze@v4.37.4
|
||||
- uses: github/codeql-action/analyze@v4.37.6
|
||||
with:
|
||||
category: "/language:c-cpp"
|
||||
|
||||
78
.github/workflows/emulated-suite.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
# cross-arch.yml stops at compile for the ports architectures, so the Debian
|
||||
# buildd is the first machine to run the suite on one, and it reports by failing
|
||||
# the package build (#1146, #1148). This runs it under qemu-user, where a process
|
||||
# is not the shape a native one is and every step costs far more.
|
||||
name: Emulated suite
|
||||
|
||||
on:
|
||||
# The harness is where a native-shape assumption lands, so gate on it rather
|
||||
# than on every PR: this leg is slow and is not a required check.
|
||||
pull_request:
|
||||
paths:
|
||||
- "tests/**"
|
||||
- "tools/emulated-suite.sh"
|
||||
- ".github/workflows/emulated-suite.yml"
|
||||
push:
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: "41 5 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
# Least privilege: the workflow only needs to read the repo.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: emulated-suite-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
emulated:
|
||||
name: emulated suite (${{ matrix.arch }})
|
||||
runs-on: ubuntu-24.04
|
||||
# The emulated apt, configure and build take the bulk of it, well under the
|
||||
# 6h ceiling: a wedge should not run that long.
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Emulation is what is under test, not parisc: the hppa that reported
|
||||
# #1146 lives in debian-ports, with no container image and a far more
|
||||
# fragile archive. s390x is in main, and big-endian, which nothing else
|
||||
# here covers.
|
||||
arch: [s390x]
|
||||
steps:
|
||||
- name: Register the binfmt handlers
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
# Registered with the F flag, so the kernel holds the interpreter open
|
||||
# and the container needs no copy of it.
|
||||
sudo apt-get install -y --no-install-recommends qemu-user-static binfmt-support
|
||||
test -e "/proc/sys/fs/binfmt_misc/qemu-${{ matrix.arch }}" ||
|
||||
{ ls /proc/sys/fs/binfmt_misc; exit 1; }
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build and run the suite emulated
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# The build dir is a mounted volume, so the suite log outlives the
|
||||
# container: a failure here has no detail otherwise.
|
||||
mkdir -p "$RUNNER_TEMP/emul-bld"
|
||||
docker run --rm --platform "linux/${{ matrix.arch }}" \
|
||||
-v "$GITHUB_WORKSPACE:$GITHUB_WORKSPACE" -w "$GITHUB_WORKSPACE" \
|
||||
-v "$RUNNER_TEMP/emul-bld:/bld" \
|
||||
-e "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \
|
||||
-e "EMULATED_ARCH=${{ matrix.arch }}" \
|
||||
debian:sid bash ./tools/emulated-suite.sh
|
||||
|
||||
- name: Collect the suite log
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: emulated-suite-${{ matrix.arch }}-log
|
||||
path: ${{ runner.temp }}/emul-bld/tests/test-suite.log
|
||||
if-no-files-found: warn
|
||||
106
.github/workflows/httraqt.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
# Build HTTraQt, the only consumer of our installed headers, against this branch.
|
||||
name: HTTraQt
|
||||
|
||||
# Filtered, so it must never join the required set: a check that a path filter
|
||||
# skips stays "Expected" forever and blocks the merge.
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths: ['src/**', 'm4/**', 'configure.ac', '.github/workflows/httraqt.yml']
|
||||
pull_request:
|
||||
paths: ['src/**', 'm4/**', 'configure.ac', '.github/workflows/httraqt.yml']
|
||||
schedule:
|
||||
- cron: '37 05 * * 1' # upstream is pinned; this catches drift in Qt and the runner image
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: httraqt-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# Unofficial mirror of the SourceForge tree, so no PR depends on a third-party host.
|
||||
HTTRAQT_REPO: https://github.com/xroche/httraqt-ci-mirror.git
|
||||
HTTRAQT_REF: a1175565c61ab9afd7bda2dc96ed1182c2d44ec3 # 1.4.11 + cmake patch, upstream HEAD since 2023-01-06
|
||||
PREFIX: /usr/local
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build HTTraQt against this branch
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev \
|
||||
cmake qt6-base-dev qt6-multimedia-dev
|
||||
|
||||
- name: Build and install libhttrack
|
||||
run: |
|
||||
set -euo pipefail
|
||||
autoreconf -fi
|
||||
bld="$RUNNER_TEMP/bld"
|
||||
mkdir -p "$bld"
|
||||
(cd "$bld" && "$GITHUB_WORKSPACE/configure" --prefix="$PREFIX" --disable-static)
|
||||
make -C "$bld" -j"$(nproc)"
|
||||
sudo make -C "$bld" install
|
||||
sudo ldconfig
|
||||
|
||||
- name: Assert no distribution libhttrack can shadow ours
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# FindHttrack.cmake hardcodes /usr/include/httrack first, so a stray libhttrack-dev would test Debian's headers.
|
||||
for stray in /usr/include/httrack /usr/lib/libhttrack.so /usr/lib/*/libhttrack.so; do
|
||||
if [ -e "$stray" ]; then
|
||||
echo "::error::$stray exists; HTTraQt would build against it, not this branch"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
test -f "$PREFIX/include/httrack/htsglobal.h"
|
||||
|
||||
- name: Fetch HTTraQt
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Fetching the SHA itself needs no pin check afterwards, and keeps the
|
||||
# tree out of the workspace so our own build cannot pick it up.
|
||||
rm -rf "$RUNNER_TEMP/httraqt"
|
||||
git init -q "$RUNNER_TEMP/httraqt"
|
||||
ok=
|
||||
for attempt in 1 2 3; do
|
||||
if git -C "$RUNNER_TEMP/httraqt" fetch -q --depth 1 "$HTTRAQT_REPO" "$HTTRAQT_REF"; then ok=1; break; fi
|
||||
echo "fetch failed (attempt $attempt)"
|
||||
[ "$attempt" -eq 3 ] || sleep $((attempt * 15))
|
||||
done
|
||||
[ -n "$ok" ] || { echo "::error::cannot reach $HTTRAQT_REPO"; exit 1; }
|
||||
git -C "$RUNNER_TEMP/httraqt" checkout -q --detach FETCH_HEAD
|
||||
|
||||
- name: Configure HTTraQt
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# CMAKE_MINIMUM_REQUIRED is 2.8 upstream, which CMake 4 rejects outright.
|
||||
# The redirect is load-bearing: a bare MESSAGE() is NOTICE mode, which goes to stderr.
|
||||
cmake -S "$RUNNER_TEMP/httraqt" -B "$RUNNER_TEMP/httraqt-bld" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
2>&1 | tee "$RUNNER_TEMP/cmake.log"
|
||||
# A configure that resolved elsewhere would compile someone else's headers.
|
||||
grep -q "httrack header directory found: $PREFIX/include/httrack/" "$RUNNER_TEMP/cmake.log"
|
||||
|
||||
- name: Build HTTraQt
|
||||
run: cmake --build "$RUNNER_TEMP/httraqt-bld" -j"$(nproc)"
|
||||
|
||||
- name: Assert it linked against the libhttrack we just built
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Not bin/: CMAKE_RUNTIME_OUTPUT_DIRECTORY is set after ADD_EXECUTABLE, too late.
|
||||
bin="$RUNNER_TEMP/httraqt-bld/httraqt"
|
||||
ldd "$bin" | grep -E "libhttrack\.so.* => $PREFIX/lib/" \
|
||||
|| { ldd "$bin"; exit 1; }
|
||||
12
.github/workflows/screenshots.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update
|
||||
sudo apt-get update || true # a third-party source in the runner image can 403 (#1097)
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential autoconf automake libtool autoconf-archive \
|
||||
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev
|
||||
@@ -52,6 +52,15 @@ jobs:
|
||||
pip install --quiet playwright
|
||||
playwright install --with-deps chromium
|
||||
|
||||
# The address is on screen in the wizard panes and names the mirror folder,
|
||||
# so the crawl needs a presentable host rather than 127.0.0.1 and a random
|
||||
# port. example.com is reserved for documentation (RFC 2606).
|
||||
- name: Let the crawl address a named site on port 80
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "127.0.0.1 www.example.com" | sudo tee -a /etc/hosts
|
||||
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80 # bind 80 unprivileged
|
||||
|
||||
- name: Capture
|
||||
env:
|
||||
LANGN: ${{ inputs.lang || '1' }}
|
||||
@@ -61,6 +70,7 @@ jobs:
|
||||
# --root is the dist root; the source tree already holds every piece
|
||||
# htsserver reads, so this needs no install.
|
||||
python3 tools/screenshot-walk.py --htsserver src/htsserver --root . \
|
||||
--site-host www.example.com --site-port 80 \
|
||||
--lang "$LANGN" --scale "$SCALE" --out shots
|
||||
|
||||
- name: Upload the screens
|
||||
|
||||
115
.github/workflows/windows-build.yml
vendored
@@ -14,6 +14,9 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# The suite watchdog reports as a commit status, the only channel that
|
||||
# outlives the runner it is reporting on (#795).
|
||||
statuses: write
|
||||
|
||||
# Cancel superseded runs on the same branch or PR.
|
||||
concurrency:
|
||||
@@ -28,6 +31,11 @@ jobs:
|
||||
matrix:
|
||||
platform: [x64, Win32]
|
||||
configuration: [Release]
|
||||
# Windows runs from other branches starve this one's runner (#1213), so cap
|
||||
# them repo-wide at one per platform. queue: max: the default cancels a waiter.
|
||||
concurrency:
|
||||
group: windows-build-${{ matrix.platform }}
|
||||
queue: max
|
||||
# Redirect vcpkg's default `files` binary cache into the workspace so
|
||||
# actions/cache can persist it. vcpkg builds openssl/brotli/zlib/zstd from
|
||||
# source otherwise, several minutes every run.
|
||||
@@ -37,6 +45,9 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive # coucal lives in src/coucal
|
||||
# Or the job token stays in .git/config, where every test the suite
|
||||
# runs can read it.
|
||||
persist-credentials: false
|
||||
|
||||
# Located through vswhere rather than microsoft/setup-msbuild: the repo
|
||||
# only allows GitHub-owned actions.
|
||||
@@ -61,17 +72,30 @@ jobs:
|
||||
run: New-Item -ItemType Directory -Force -Path $env:VCPKG_DEFAULT_BINARY_CACHE | Out-Null
|
||||
|
||||
# x-gha is gone (vcpkg-tool #1662 dropped it after GitHub changed the cache
|
||||
# API), so cache the binary archives directly. Keyed on the manifest, which
|
||||
# carries the builtin-baseline, so a Dependabot bump busts it; restore-keys
|
||||
# still seeds the unchanged ports' archives, so only the bumped one rebuilds.
|
||||
- name: Cache vcpkg binary archives
|
||||
uses: actions/cache@v6
|
||||
# API), so cache the binary archives directly. An exact key hit skips the save,
|
||||
# so a cache orphaned by a toolchain bump pins itself, and naming every input
|
||||
# vcpkg's ABI hash folds in is a list that can come up short: key on the run
|
||||
# id instead, and restore by prefix.
|
||||
- name: Restore the vcpkg binary archives
|
||||
uses: actions/cache/restore@v6
|
||||
with:
|
||||
path: ${{ github.workspace }}\vcpkg_cache
|
||||
key: vcpkg-${{ matrix.platform }}-${{ hashFiles('src/vcpkg.json') }}
|
||||
key: vcpkg-${{ matrix.platform }}-${{ hashFiles('src/vcpkg.json') }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
restore-keys: |
|
||||
vcpkg-${{ matrix.platform }}-${{ hashFiles('src/vcpkg.json') }}-
|
||||
vcpkg-${{ matrix.platform }}-
|
||||
|
||||
# Counted before the build, or vcpkg's own writes during it make a cold cache
|
||||
# look warm.
|
||||
- name: Count the restored vcpkg archives
|
||||
shell: pwsh
|
||||
run: |
|
||||
$a = @(Get-ChildItem -Recurse -File -Filter *.zip `
|
||||
$env:VCPKG_DEFAULT_BINARY_CACHE -EA SilentlyContinue)
|
||||
Write-Host "restored $($a.Count) archive(s)"
|
||||
$a.FullName | Out-File -FilePath vcpkg-restored.txt
|
||||
"VCPKG_ARCHIVES_BEFORE=$($a.Count)" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
|
||||
# The runner image's vcpkg checkout is pinned to some commit; our manifest's
|
||||
# builtin-baseline is usually newer, so `git show <baseline>:versions/...`
|
||||
# fails until that commit is local. Fetch exactly the pinned baseline (read
|
||||
@@ -101,6 +125,50 @@ jobs:
|
||||
if ($LASTEXITCODE -ne 0) { throw "$proj failed" }
|
||||
}
|
||||
|
||||
# A stale cache and a warm one both restore and rebuild; only archives
|
||||
# restored and unmatched prove staleness.
|
||||
- name: Check whether vcpkg could use the restored archives
|
||||
id: vcpkg
|
||||
if: ${{ !cancelled() }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
$before = [int]$env:VCPKG_ARCHIVES_BEFORE
|
||||
$stale = $false
|
||||
# The log is missing when the build never reached MSBuild.
|
||||
if (Test-Path msbuild-libhttrack.log) {
|
||||
$m = Select-String -Path msbuild-libhttrack.log -Pattern 'Restored (\d+) package' |
|
||||
Select-Object -First 1
|
||||
if ($m) {
|
||||
$restored = [int]$m.Matches[0].Groups[1].Value
|
||||
Write-Host "vcpkg restored $restored of $before archive(s)"
|
||||
$stale = ($restored -eq 0 -and $before -gt 0)
|
||||
} else { Write-Host "no vcpkg restore line in the build log" }
|
||||
} else { Write-Host "no build log" }
|
||||
if ($stale) {
|
||||
Write-Host "::warning::stale vcpkg cache: $before archives, none matching this toolchain; every port rebuilt from source"
|
||||
# Provably dead: drop them rather than carry every past toolchain
|
||||
# generation in the saved entry forever. -LiteralPath because a piped
|
||||
# string binds -Path, which globs on a name carrying [ ].
|
||||
Get-Content vcpkg-restored.txt -EA SilentlyContinue | Where-Object { $_ } |
|
||||
ForEach-Object { Remove-Item -LiteralPath $_ -Force -EA SilentlyContinue }
|
||||
}
|
||||
$after = @(Get-ChildItem -Recurse -File -Filter *.zip `
|
||||
$env:VCPKG_DEFAULT_BINARY_CACHE -EA SilentlyContinue).Count
|
||||
Write-Host "$after archive(s) to cache"
|
||||
# Pruning can leave the count unmoved while replacing every archive, so the
|
||||
# stale case saves on its own account rather than on growth.
|
||||
"save=$($stale -or ($after -gt $before))".ToLower() |
|
||||
Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
||||
|
||||
# Only when the archive set changed, so a warm run adds no entry and the
|
||||
# eviction budget stays for the runs that rebuilt something.
|
||||
- name: Save the vcpkg binary archives
|
||||
if: ${{ !cancelled() && steps.vcpkg.outputs.save == 'true' }}
|
||||
uses: actions/cache/save@v6
|
||||
with:
|
||||
path: ${{ github.workspace }}\vcpkg_cache
|
||||
key: vcpkg-${{ matrix.platform }}-${{ hashFiles('src/vcpkg.json') }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
|
||||
# WinHTTrack links src\$(Platform)\$(Configuration)\libhttrack.lib, so the
|
||||
# import lib and the DLL both have to land there under that exact name.
|
||||
- name: Check the binaries landed where they are expected
|
||||
@@ -161,6 +229,33 @@ jobs:
|
||||
throw "CRT mismatch across the boundary: libhttrack.dll=$a httrack.exe=$b"
|
||||
}
|
||||
|
||||
# The DLL build only proves the engine's own fixed include order, the one
|
||||
# order a consumer need not use, and 205/206/207/269 all gate on an automake
|
||||
# environment this job has not (#1153).
|
||||
- name: Compile the installed headers standalone and pairwise
|
||||
shell: pwsh
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
$vs = & $vswhere -latest -products * -property installationPath | Select-Object -First 1
|
||||
if (-not $vs) { throw "Visual Studio not found" }
|
||||
Import-Module "$vs\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
$arch = if ("${{ matrix.platform }}" -eq "x64") { "x64" } else { "x86" }
|
||||
Enter-VsDevShell -VsInstallPath $vs -SkipAutomaticLocation `
|
||||
-DevCmdArguments "-arch=$arch -host_arch=x64"
|
||||
# htsbasenet.h reaches for <openssl/ssl.h> unless a consumer turns it
|
||||
# off, so the sweep needs what vcpkg installed for the build. Searched,
|
||||
# not spelled: the install root nests the triplet twice.
|
||||
$dirs = @(Get-ChildItem -Path "src\vcpkg_installed" -Recurse -Depth 3 `
|
||||
-Directory -Filter include -ErrorAction SilentlyContinue)
|
||||
if (-not ($dirs | Where-Object { Test-Path (Join-Path $_.FullName "openssl\ssl.h") })) {
|
||||
throw "no vcpkg include directory under src\vcpkg_installed holds openssl/ssl.h"
|
||||
}
|
||||
$inc = @($dirs | ForEach-Object { "-I" + $_.FullName })
|
||||
bash tests/install-headers-sweep.sh --backend cl --srcdir . -- @inc
|
||||
if ($LASTEXITCODE -ne 0) { throw "the installed headers do not compile standalone and pairwise" }
|
||||
|
||||
# The engine ships ~90 tests and none had ever run on Windows: "make check"
|
||||
# is Linux/macOS only. These are the offline ones, driven from Git Bash
|
||||
# against the native httrack.exe. They subsume the self-tests this step
|
||||
@@ -171,6 +266,14 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: tests
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
# Through the environment, never argv, which the process list exposes.
|
||||
WATCHDOG_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
WATCHDOG_REPO: ${{ github.repository }}
|
||||
# github.sha here is the PR's merge commit, so statuses posted against it stay out of the PR's checks UI.
|
||||
WATCHDOG_SHA: ${{ github.sha }}
|
||||
WATCHDOG_CONTEXT: windows-suite (${{ matrix.platform }}, ${{ matrix.configuration }})
|
||||
WATCHDOG_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
bash ./ci-windows-suite.sh \
|
||||
"$(cygpath -u "$GITHUB_WORKSPACE")/src/${{ matrix.platform }}/${{ matrix.configuration }}"
|
||||
|
||||
364
.github/workflows/zz-pidreuse-probe.yml
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
# Scratch probe: does taskkill /F /T follow a recorded parent PID whose owner is
|
||||
# long dead? Not part of the build gate; branch-scoped, delete when answered.
|
||||
name: zz pid-reuse probe
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [zz-pidreuse-probe]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
probe:
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Probe
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
$sum = $env:GITHUB_STEP_SUMMARY
|
||||
|
||||
function Say([string]$m) {
|
||||
Write-Host $m
|
||||
if ($sum) { try { Add-Content -LiteralPath $sum -Value $m -ErrorAction SilentlyContinue } catch { } }
|
||||
}
|
||||
|
||||
Say "# PID-reuse probe on $env:COMPUTERNAME $(Get-Date -Format o)"
|
||||
Say ""
|
||||
Say '```'
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Part 1: is the hazard even present?
|
||||
# ---------------------------------------------------------------
|
||||
Say "== PART 1: recorded-parent state of the runner's own processes =="
|
||||
|
||||
$snap = @(Get-CimInstance Win32_Process)
|
||||
$byPid = @{}
|
||||
foreach ($p in $snap) { $byPid[[int]$p.ProcessId] = $p }
|
||||
Say ("snapshot: {0} live processes" -f $snap.Count)
|
||||
|
||||
function Stamp($d) { if ($null -eq $d) { return '?' } else { return ([datetime]$d).ToString('HH:mm:ss.fff') } }
|
||||
|
||||
function Classify([int]$q) {
|
||||
$c = $byPid[$q]
|
||||
if ($null -eq $c) { return 'NOPROC' }
|
||||
$par = $byPid[[int]$c.ParentProcessId]
|
||||
if ($null -eq $par) { return 'DEAD-PARENT' }
|
||||
if ($null -eq $par.CreationDate -or $null -eq $c.CreationDate) { return 'OK' }
|
||||
if ($par.CreationDate -gt $c.CreationDate) { return 'RECYCLED-PARENT' }
|
||||
return 'OK'
|
||||
}
|
||||
|
||||
function Chain([int]$q) {
|
||||
$out = @()
|
||||
$seen = @{}
|
||||
$cur = $q
|
||||
for ($i = 0; $i -lt 24; $i++) {
|
||||
$c = $byPid[$cur]
|
||||
if ($null -eq $c) { $out += ("{0}=<no such process>" -f $cur); break }
|
||||
$out += ("{0}/{1}@{2}" -f $c.ProcessId, $c.Name, (Stamp $c.CreationDate))
|
||||
if ($seen.ContainsKey($cur)) { $out += '<cycle>'; break }
|
||||
$seen[$cur] = $true
|
||||
$st = Classify $cur
|
||||
if ($st -ne 'OK') { $out += ("<<{0}>>" -f $st); break }
|
||||
$cur = [int]$c.ParentProcessId
|
||||
if ($cur -eq 0) { break }
|
||||
}
|
||||
return ($out -join ' <- ')
|
||||
}
|
||||
|
||||
$interesting = @($snap | Where-Object {
|
||||
$_.Name -like 'Runner.*' -or $_.Name -like 'Agent.*' -or $_.Name -like 'hosted-*' -or
|
||||
$_.Name -eq 'bash.exe' -or $_.Name -eq 'pwsh.exe' -or
|
||||
$_.Name -eq 'powershell.exe' -or $_.Name -eq 'dotnet.exe' -or
|
||||
$_.Name -eq 'node.exe' -or $_.Name -eq 'cmd.exe' -or
|
||||
$_.Name -eq 'conhost.exe' -or $_.Name -eq 'sh.exe'
|
||||
} | Sort-Object Name, ProcessId)
|
||||
|
||||
$armed = 0
|
||||
foreach ($p in $interesting) {
|
||||
$st = Classify ([int]$p.ProcessId)
|
||||
Say (" {0,-24} pid={1,-7} ppid={2,-7} {3,-16} {4}" -f $p.Name, $p.ProcessId, $p.ParentProcessId, $st, (Chain ([int]$p.ProcessId)))
|
||||
if ($st -ne 'OK' -and ($p.Name -like 'Runner.*' -or $p.Name -like 'Agent.*' -or $p.Name -like 'hosted-*')) { $armed++ }
|
||||
}
|
||||
|
||||
$dead = 0; $rec = 0; $ok = 0
|
||||
$boxOrphan = @{}
|
||||
foreach ($p in $snap) {
|
||||
$st = Classify ([int]$p.ProcessId)
|
||||
if ($st -eq 'DEAD-PARENT') {
|
||||
$dead++
|
||||
$k = [int]$p.ParentProcessId
|
||||
if (-not $boxOrphan.ContainsKey($k)) { $boxOrphan[$k] = @() }
|
||||
$boxOrphan[$k] += ("{0}/{1}" -f $p.ProcessId, $p.Name)
|
||||
} elseif ($st -eq 'RECYCLED-PARENT') { $rec++ } else { $ok++ }
|
||||
}
|
||||
Say ""
|
||||
Say ("box-wide: {0} processes, {1} with a DEAD recorded parent, {2} with a RECYCLED one, {3} sound" -f $snap.Count, $dead, $rec, $ok)
|
||||
foreach ($k in ($boxOrphan.Keys | Sort-Object)) {
|
||||
Say (" dead ppid {0,-7} still claimed as parent by: {1}" -f $k, ($boxOrphan[$k] -join ', '))
|
||||
}
|
||||
Say ""
|
||||
if ($armed -gt 0) {
|
||||
Say "PART 1 VERDICT: HAZARD PRESENT - $armed runner-agent process(es) record a parent PID that is dead or already recycled."
|
||||
} elseif ($dead -gt 0) {
|
||||
Say "PART 1 VERDICT: no Runner.*/Agent.* process has a stale recorded parent, but $dead other live processes do."
|
||||
} else {
|
||||
Say "PART 1 VERDICT: NOT ARMED - every live process's recorded parent is alive and older."
|
||||
}
|
||||
Say ""
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Part 2. Five sets of freed PIDs, released at the same instant, so
|
||||
# that a null on the set the theory needs can be told apart from a
|
||||
# probe that never recycles anything:
|
||||
# A plain ping, killed, .NET handle disposed (baseline: does reuse happen?)
|
||||
# C plain ping, killed, handle deliberately HELD (does a handle pin the PID?)
|
||||
# D cmd orphan-maker, child killed (is the construction sound?)
|
||||
# B cmd orphan-maker, ping child left ALIVE (the question)
|
||||
# E cmd orphan-maker, wscript child left ALIVE (same, with a child that
|
||||
# attaches to no console)
|
||||
# Every process inherits this shell's console, so no console host survives
|
||||
# to hold a handle on the dead cmd. Run 2 created 500 of those.
|
||||
# ---------------------------------------------------------------
|
||||
Say "== PART 2: can a dead parent's PID be handed out again, and does taskkill /T follow it? =="
|
||||
|
||||
$vbs = Join-Path $env:TEMP 'zzsleep.vbs'
|
||||
Set-Content -LiteralPath $vbs -Value 'WScript.Sleep 600000' -Encoding ascii
|
||||
|
||||
function New-Ping {
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.FileName = "$env:SystemRoot\System32\PING.EXE"
|
||||
$psi.Arguments = '-n 600 127.0.0.1'
|
||||
$psi.UseShellExecute = $false
|
||||
$psi.CreateNoWindow = $false
|
||||
$psi.RedirectStandardOutput = $true
|
||||
$psi.RedirectStandardError = $true
|
||||
return [System.Diagnostics.Process]::Start($psi)
|
||||
}
|
||||
|
||||
function New-Orphan([string]$childArgs) {
|
||||
# cmd exits at once, leaving the child alive with cmd's now-free PID recorded as parent.
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.FileName = "$env:ComSpec"
|
||||
$psi.Arguments = $childArgs
|
||||
$psi.UseShellExecute = $false
|
||||
$psi.CreateNoWindow = $false
|
||||
return [System.Diagnostics.Process]::Start($psi)
|
||||
}
|
||||
|
||||
function Get-Proc([int]$q) {
|
||||
return (Get-CimInstance Win32_Process -Filter "ProcessId = $q" -ErrorAction SilentlyContinue | Select-Object -First 1)
|
||||
}
|
||||
|
||||
# --- control: a tree kill of a LIVE parent must kill its child, or the probe is blind.
|
||||
Say ""
|
||||
Say "-- control: taskkill /F /T on a live cmd whose child ping is genuine"
|
||||
$ctl = New-Orphan '/c ping -n 600 127.0.0.1 > NUL'
|
||||
Start-Sleep -Milliseconds 1500
|
||||
$ctlKid = @(Get-CimInstance Win32_Process -Filter "ParentProcessId = $($ctl.Id)")
|
||||
if ($ctlKid.Count -eq 0) {
|
||||
Say " control INCONCLUSIVE: no child found under cmd $($ctl.Id)"
|
||||
try { $ctl.Kill() } catch { }
|
||||
} else {
|
||||
$kp = [int]$ctlKid[0].ProcessId
|
||||
Say (" cmd {0} -> child {1}/{2}" -f $ctl.Id, $kp, $ctlKid[0].Name)
|
||||
$co = (& taskkill.exe /F /T /PID $ctl.Id 2>&1 | Out-String).Trim()
|
||||
Say (" taskkill: " + ($co -replace "`r?`n", ' | '))
|
||||
Start-Sleep -Milliseconds 800
|
||||
$still = Get-Proc $kp
|
||||
Say (" control result: child {0} {1}" -f $kp, $(if ($null -eq $still) { 'GONE - the probe can observe a tree kill' } else { 'STILL ALIVE - probe is blind, part 2 is inconclusive' }))
|
||||
}
|
||||
try { $ctl.Dispose() } catch { }
|
||||
|
||||
$conBefore = @(Get-Process conhost -ErrorAction SilentlyContinue).Count
|
||||
|
||||
# --- build the sets.
|
||||
$NB = 300; $ND = 200; $NE = 200; $NA = 200; $NC = 200
|
||||
Say ""
|
||||
Say "-- building sets: B=$NB (ping child alive) E=$NE (wscript child alive) D=$ND (child killed) A=$NA (baseline) C=$NC (handle held)"
|
||||
|
||||
$bCmd = New-Object 'System.Collections.Generic.List[System.Diagnostics.Process]'
|
||||
for ($i = 0; $i -lt $NB; $i++) { try { $bCmd.Add((New-Orphan '/c start /b ping -n 600 127.0.0.1 > NUL')) } catch { } }
|
||||
$eCmd = New-Object 'System.Collections.Generic.List[System.Diagnostics.Process]'
|
||||
for ($i = 0; $i -lt $NE; $i++) { try { $eCmd.Add((New-Orphan ('/c start /b wscript.exe //B //Nologo "' + $vbs + '"'))) } catch { } }
|
||||
$dCmd = New-Object 'System.Collections.Generic.List[System.Diagnostics.Process]'
|
||||
for ($i = 0; $i -lt $ND; $i++) { try { $dCmd.Add((New-Orphan '/c start /b ping -n 600 127.0.0.1 > NUL')) } catch { } }
|
||||
$aProc = New-Object 'System.Collections.Generic.List[System.Diagnostics.Process]'
|
||||
for ($i = 0; $i -lt $NA; $i++) { try { $aProc.Add((New-Ping)) } catch { } }
|
||||
$pinned = New-Object 'System.Collections.Generic.List[System.Diagnostics.Process]'
|
||||
for ($i = 0; $i -lt $NC; $i++) { try { $pinned.Add((New-Ping)) } catch { } }
|
||||
|
||||
$bPid = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
foreach ($p in $bCmd) { [void]$bPid.Add([int]$p.Id) }
|
||||
$ePid = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
foreach ($p in $eCmd) { [void]$ePid.Add([int]$p.Id) }
|
||||
$dPid = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
foreach ($p in $dCmd) { [void]$dPid.Add([int]$p.Id) }
|
||||
$aPid = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
foreach ($p in $aProc) { [void]$aPid.Add([int]$p.Id) }
|
||||
$cPid = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
foreach ($p in $pinned) { [void]$cPid.Add([int]$p.Id) }
|
||||
Say (" distinct PIDs held: B={0} E={1} D={2} A={3} C={4}" -f $bPid.Count, $ePid.Count, $dPid.Count, $aPid.Count, $cPid.Count)
|
||||
if ($bPid.Count -eq 0 -or $aPid.Count -eq 0) { Say " set construction failed, aborting"; Say '```'; exit 1 }
|
||||
|
||||
Start-Sleep -Seconds 3
|
||||
|
||||
# Map the surviving children back to the cmd that made them.
|
||||
$orphan = @{}
|
||||
$dKids = New-Object 'System.Collections.Generic.List[int]'
|
||||
$nB = 0; $nE = 0
|
||||
foreach ($p in @(Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'ping.exe' -or $_.Name -eq 'wscript.exe' })) {
|
||||
$pp = [int]$p.ParentProcessId
|
||||
if ($bPid.Contains($pp) -or $ePid.Contains($pp)) {
|
||||
if (-not $orphan.ContainsKey($pp)) { $orphan[$pp] = @() }
|
||||
$orphan[$pp] += [pscustomobject]@{ Pid = [int]$p.ProcessId; Created = $p.CreationDate }
|
||||
if ($bPid.Contains($pp)) { $nB++ } else { $nE++ }
|
||||
} elseif ($dPid.Contains($pp)) {
|
||||
$dKids.Add([int]$p.ProcessId)
|
||||
}
|
||||
}
|
||||
Say (" surviving orphan children: {0} under B parents, {1} under E parents, {2} under D parents (to be killed)" -f $nB, $nE, $dKids.Count)
|
||||
|
||||
# Free every set at the same instant. Only C keeps its .NET handle open.
|
||||
foreach ($p in $bCmd) { try { [void]$p.WaitForExit(2000) } catch { }; try { $p.Dispose() } catch { } }
|
||||
foreach ($p in $eCmd) { try { [void]$p.WaitForExit(2000) } catch { }; try { $p.Dispose() } catch { } }
|
||||
foreach ($p in $dCmd) { try { [void]$p.WaitForExit(2000) } catch { }; try { $p.Dispose() } catch { } }
|
||||
foreach ($p in $aProc) { try { $p.Kill() } catch { } }
|
||||
foreach ($p in $pinned) { try { $p.Kill() } catch { } }
|
||||
Start-Sleep -Milliseconds 1000
|
||||
foreach ($p in $aProc) { try { $p.Dispose() } catch { } }
|
||||
$aProc.Clear(); $bCmd.Clear(); $eCmd.Clear(); $dCmd.Clear()
|
||||
if ($dKids.Count -gt 0) { Stop-Process -Id $dKids.ToArray() -Force -ErrorAction SilentlyContinue }
|
||||
[GC]::Collect(); [GC]::WaitForPendingFinalizers(); [GC]::Collect()
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
$liveNow = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
foreach ($p in @(Get-CimInstance Win32_Process)) { [void]$liveNow.Add([int]$p.ProcessId) }
|
||||
function CountLive($s) { $n = 0; foreach ($x in $s) { if ($liveNow.Contains($x)) { $n++ } }; return $n }
|
||||
Say (" set members still live (must be 0): B={0} E={1} D={2} A={3} C={4}" -f (CountLive $bPid), (CountLive $ePid), (CountLive $dPid), (CountLive $aPid), (CountLive $cPid))
|
||||
$conAfter = @(Get-Process conhost -ErrorAction SilentlyContinue).Count
|
||||
Say (" conhost.exe: {0} before, {1} after (a surviving console host would hold a handle on its dead creator and pin its PID)" -f $conBefore, $conAfter)
|
||||
$kidsAlive = 0
|
||||
foreach ($k in $orphan.Keys) { foreach ($o in $orphan[$k]) { if ($liveNow.Contains($o.Pid)) { $kidsAlive++ } } }
|
||||
Say (" orphan children still alive under a dead B/E parent: {0}" -f $kidsAlive)
|
||||
|
||||
# --- hunt.
|
||||
$hitA = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
$hitB = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
$hitC = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
$hitD = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
$hitE = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
$boxHits = 0
|
||||
$spawns = 0; $tested = 0; $followed = 0; $survived = 0
|
||||
$KILLCAP = 25
|
||||
$distinct = New-Object 'System.Collections.Generic.HashSet[int]'
|
||||
$window = New-Object 'System.Collections.Generic.Queue[System.Diagnostics.Process]'
|
||||
$minPid = [int]::MaxValue; $maxPid = 0
|
||||
|
||||
function Test-Hit([int]$np) {
|
||||
if ($script:aPid.Contains($np)) { [void]$script:hitA.Add($np) }
|
||||
if ($script:dPid.Contains($np)) { [void]$script:hitD.Add($np) }
|
||||
if ($script:cPid.Contains($np)) {
|
||||
if ($script:hitC.Add($np) -and $script:hitC.Count -le 5) { Say ("!! set C hit: PID $np was handed out although an open process handle on it is still held") }
|
||||
}
|
||||
if ($script:boxOrphan.ContainsKey($np)) {
|
||||
$script:boxHits++
|
||||
Say ""
|
||||
Say ("!! REAL COLLISION: our spawn got PID $np, which live box process(es) still record as their parent: $($script:boxOrphan[$np] -join ', ')")
|
||||
Say " (not tree-killing this one: it would reap a process we did not start)"
|
||||
Say ""
|
||||
}
|
||||
$isB = $script:bPid.Contains($np)
|
||||
$isE = $script:ePid.Contains($np)
|
||||
if (-not ($isB -or $isE)) { return $false }
|
||||
if ($isB) { [void]$script:hitB.Add($np) } else { [void]$script:hitE.Add($np) }
|
||||
if ($script:tested -ge $script:KILLCAP) { return $false }
|
||||
|
||||
$live = @()
|
||||
foreach ($o in $script:orphan[$np]) {
|
||||
$cur = Get-Proc $o.Pid
|
||||
if ($null -ne $cur -and $cur.CreationDate -eq $o.Created) { $live += $o }
|
||||
}
|
||||
if ($live.Count -eq 0) { return $false }
|
||||
|
||||
$script:tested++
|
||||
Say ""
|
||||
Say ("### HIT #$($script:tested) on set $(if ($isB) { 'B' } else { 'E' }): spawn #$($script:spawns) at t=$([int]$script:sw.Elapsed.TotalSeconds)s was handed PID $np, the PID of a dead parent whose child is still running")
|
||||
foreach ($k in @(Get-CimInstance Win32_Process -Filter "ParentProcessId = $np")) {
|
||||
Say (" the process table calls this a child of $np : {0}/{1} created {2}" -f $k.ProcessId, $k.Name, $k.CreationDate)
|
||||
}
|
||||
foreach ($o in $live) { Say (" stale-PPID orphan {0} is alive before the kill" -f $o.Pid) }
|
||||
Say (" issuing: taskkill /F /T /PID $np")
|
||||
$out = (& taskkill.exe /F /T /PID $np 2>&1 | Out-String).Trim()
|
||||
Say (" taskkill said: " + ($out -replace "`r?`n", ' | '))
|
||||
Start-Sleep -Milliseconds 1000
|
||||
$anyDied = $false
|
||||
foreach ($o in $live) {
|
||||
$cur = Get-Proc $o.Pid
|
||||
$alive = ($null -ne $cur -and $cur.CreationDate -eq $o.Created)
|
||||
if (-not $alive) { $anyDied = $true }
|
||||
Say (" stale-PPID orphan {0}: alive after the kill = {1}" -f $o.Pid, $alive)
|
||||
}
|
||||
if (-not $anyDied) { $script:survived++ }
|
||||
if ($anyDied) {
|
||||
$script:followed++
|
||||
Say " >>> THE TREE WALK FOLLOWED A STALE PPID. taskkill /T does not validate that the recorded parent is the real one."
|
||||
} else {
|
||||
Say " >>> the orphan survived: taskkill /T did not follow the stale PPID."
|
||||
}
|
||||
Say ""
|
||||
return $false
|
||||
}
|
||||
|
||||
$sw = [Diagnostics.Stopwatch]::StartNew()
|
||||
Say ""
|
||||
Say "-- hunting: rolling window of 200 live spawns, 300s cap"
|
||||
$done = $false
|
||||
while (-not $done -and $sw.Elapsed.TotalSeconds -lt 300) {
|
||||
try { $np = (New-Ping) } catch { Start-Sleep -Milliseconds 50; continue }
|
||||
$window.Enqueue($np)
|
||||
$spawns++
|
||||
$id = [int]$np.Id
|
||||
[void]$distinct.Add($id)
|
||||
if ($id -lt $minPid) { $minPid = $id }
|
||||
if ($id -gt $maxPid) { $maxPid = $id }
|
||||
try { $done = Test-Hit $id } catch { Say ("Test-Hit failed on $id : " + $_.Exception.Message) }
|
||||
while ($window.Count -gt 200) {
|
||||
$old = $window.Dequeue()
|
||||
try { $old.Kill() } catch { }
|
||||
try { $old.Dispose() } catch { }
|
||||
}
|
||||
if (($spawns % 250) -eq 0) { Write-Host (" ... spawn $spawns, distinct $($distinct.Count), A=$($hitA.Count) B=$($hitB.Count) C=$($hitC.Count) D=$($hitD.Count) E=$($hitE.Count), t=$([int]$sw.Elapsed.TotalSeconds)s") }
|
||||
}
|
||||
|
||||
Say ""
|
||||
Say "== RESULT =="
|
||||
Say ("spawns {0}, distinct PIDs {1}, range {2}..{3}, {4:N0}s" -f $spawns, $distinct.Count, $minPid, $maxPid, $sw.Elapsed.TotalSeconds)
|
||||
Say ("distinct set PIDs reissued, by set:")
|
||||
Say (" A killed, handle disposed, no child : {0} of {1}" -f $hitA.Count, $aPid.Count)
|
||||
Say (" C killed, handle still OPEN : {0} of {1}" -f $hitC.Count, $cPid.Count)
|
||||
Say (" D dead orphan-maker, child killed : {0} of {1}" -f $hitD.Count, $dPid.Count)
|
||||
Say (" B dead orphan-maker, ping child ALIVE : {0} of {1}" -f $hitB.Count, $bPid.Count)
|
||||
Say (" E dead orphan-maker, wscript ALIVE : {0} of {1}" -f $hitE.Count, $ePid.Count)
|
||||
Say ("tree kills issued on a reissued stale-parent PID: {0}; reached the orphan: {1}; orphan survived: {2}" -f $tested, $followed, $survived)
|
||||
Say ("collisions with a dead PPID recorded by a process we did NOT start: {0}" -f $boxHits)
|
||||
if ($tested -gt 0 -and $followed -eq 0) {
|
||||
Say "INTERPRETATION: a dead parent's PID IS reissued while a live process still records it as its parent, in $tested measured cases, and taskkill /F /T left that process alone every time. The tree walk does not follow a stale PPID: it skips a claimed child that predates the process now holding the PID."
|
||||
} elseif ($hitA.Count -eq 0 -and $hitB.Count -eq 0 -and $hitE.Count -eq 0) {
|
||||
Say "INTERPRETATION: nothing came back, so this run recycled nothing and says nothing either way."
|
||||
} elseif ($tested -eq 0) {
|
||||
Say "INTERPRETATION: PIDs recycle (A>0) but no PID with a live process recording it as parent ever came back. On this kernel such a PID stays reserved, so a tree kill cannot land on one and the stale-PPID mechanism cannot fire."
|
||||
} elseif ($followed -gt 0) {
|
||||
Say "INTERPRETATION: a dead parent's PID IS reissued while a live process records it, and taskkill /F /T reaped that process. The mechanism is real on this image."
|
||||
} else {
|
||||
Say "INTERPRETATION: the PID was reissued, but taskkill /F /T left the stale-PPID orphan alone: the tree walk validates parentage."
|
||||
}
|
||||
Say '```'
|
||||
|
||||
while ($window.Count -gt 0) { $o = $window.Dequeue(); try { $o.Kill() } catch { } }
|
||||
try { Get-Process ping, wscript -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue } catch { }
|
||||
Write-Host "probe done"
|
||||
18
AGENTS.md
@@ -19,8 +19,12 @@ the operational checklist: toolchain, invariants, and how to ship a change.
|
||||
(`request_queue_size`) so macOS/BSD don't drop connections under a parallel
|
||||
`-c16` bigcrawl the way Python's default backlog of 5 did.
|
||||
Or run `sh build.sh` to do bootstrap + configure + make in one shot.
|
||||
- A `tests/NN_*.test` runs only if listed in `tests/tests-list.mk`; an
|
||||
unregistered file is silently skipped.
|
||||
- `configure` globs `TESTS` from `tests/[0-9]*_*.test`, so a new test needs no
|
||||
registration, but an existing build dir keeps the list it was configured with:
|
||||
`231_test-names.test` goes red until you reconfigure. It also compares the glob
|
||||
against `git ls-files`, so a test you forgot to `git add` fails there instead of
|
||||
quietly shrinking CI's suite. Name one outside the pattern and it never runs;
|
||||
`tests/check-test-names.sh` (also a CI lint) rejects that.
|
||||
- `make check` prepends the build's `src/` to `PATH`, but a hand-run `.test` does
|
||||
not — an installed `/usr/bin/httrack` then shadows your build. Run via `make
|
||||
check`, or `PATH="<bld>/src:$PATH"` for a manual run.
|
||||
@@ -38,6 +42,16 @@ the operational checklist: toolchain, invariants, and how to ship a change.
|
||||
match that is not on the last line is exposed. Capture the reply, assert the
|
||||
status line it must carry (an empty, truncated or redirected one is
|
||||
marker-free too), then match with a here-string: `grep -q M <<<"$reply"`.
|
||||
`head -c N` and `head -n N` end the same way, and there the reds are
|
||||
platform-specific: GNU `tail | head -c 3` survives, the uutils coreutils leg
|
||||
turns the same line into exit 141 with no output at all. Let the reader seek
|
||||
instead: `od -An -c -j <skip> -N <len> file`.
|
||||
- Never assert a fault by signal *number*: SIGBUS is 7 on x86, arm, powerpc and
|
||||
s390x but 10 on hppa, alpha, mips and sparc. Map it with `kill -l "$n"`.
|
||||
- A fixture that needs a host to stay silent must settle (500ms), re-probe every
|
||||
socket, and SKIP when one answered: the powerpc and ppc64 buildds refuse
|
||||
TEST-NET-1 milliseconds after `connect()` where runners drop it.
|
||||
`tools/hostile-net.sh` runs a command on such a network.
|
||||
|
||||
## Hard invariants
|
||||
- **Generated autotools files are NOT in git.** `configure`, every
|
||||
|
||||
37
configure.ac
@@ -1,6 +1,6 @@
|
||||
AC_PREREQ([2.71])
|
||||
|
||||
AC_INIT([httrack], [3.49.18], [roche+packaging@httrack.com], [httrack], [http://www.httrack.com/])
|
||||
AC_INIT([httrack], [3.49.21], [roche+packaging@httrack.com], [httrack], [http://www.httrack.com/])
|
||||
AC_COPYRIGHT([
|
||||
HTTrack Website Copier, Offline Browser for Windows and Unix
|
||||
Copyright (C) 1998-2015 Xavier Roche and other contributors
|
||||
@@ -29,6 +29,14 @@ AC_CONFIG_SRCDIR(src/httrack.c)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
# 3:14:0: revision-only bump. httrackp gained a tail field (host_alias) and #1202
|
||||
# added an export (hts_host_alias_rule_ok); no layout moved, nothing went away.
|
||||
# 3:13:0: revision-only bump. htsstrings.h's StringRoomTotal grew saturation in
|
||||
# its macro body; no installed struct moved and no export came or went.
|
||||
# 3:12:0: revision-only bump. httrackp gained a tail field (singlefile_state) and
|
||||
# htsopt.h added volatile qualifiers; no layout moved, nothing went away.
|
||||
# 3:11:0: revision-only bump. #1001 moved SOCaddr_inetntoa_ out of htsnet.h, adding
|
||||
# an export where callers had an inline; nothing changed or went away.
|
||||
# 3:10:0: revision-only bump; only the version macro moved, the engine is untouched.
|
||||
# 3:9:0: revision-only bump. #991 and #1005 each added an export
|
||||
# (hts_set_thread_hooks, escape_control_url); nothing changed or went away.
|
||||
@@ -38,7 +46,7 @@ AM_INIT_AUTOMAKE([subdir-objects])
|
||||
# moves nothing). Soname stays .so.3: HTTrackQt is the only consumer of the installed
|
||||
# headers, so a libhttrack4 rename isn't worth it.
|
||||
# (3:0:0 was the htsblk mime-buffer widening, the ABI break that moved .so.2 -> .so.3.)
|
||||
VERSION_INFO="3:10:0"
|
||||
VERSION_INFO="3:14:0"
|
||||
AM_MAINTAINER_MODE
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
@@ -393,7 +401,10 @@ AC_CHECK_TYPE(sa_family_t, [], [AC_DEFINE([sa_family_t], [uint16_t], [sa_family_
|
||||
AX_CHECK_ALIGNED_ACCESS_REQUIRED
|
||||
|
||||
# check for various headers
|
||||
AC_CHECK_HEADERS([execinfo.h sys/ioctl.h])
|
||||
AC_CHECK_HEADERS([execinfo.h sys/ioctl.h sys/random.h])
|
||||
|
||||
## CSPRNG for the --single-file mark; /dev/urandom is the fallback
|
||||
AC_CHECK_FUNCS([getrandom])
|
||||
|
||||
### zlib (mandatory)
|
||||
CHECK_ZLIB()
|
||||
@@ -548,7 +559,7 @@ AC_DEFINE(HAVE_STRNLEN, 1,[Check for strnlen])], AC_MSG_RESULT([not found]))
|
||||
## Online unit tests
|
||||
AC_MSG_CHECKING(whether to enable online unit tests)
|
||||
AC_ARG_ENABLE([online-unit-tests],
|
||||
[AS_HELP_STRING([--enable-online-unit-tests=@<:@yes/no/auto@:>@],[Enable online-unit-tests @<:@default=yes@:>@])],
|
||||
[AS_HELP_STRING([--enable-online-unit-tests=@<:@yes/no/auto@:>@],[Enable online-unit-tests @<:@default=no@:>@])],
|
||||
[
|
||||
case "${enableval}" in
|
||||
no|yes|auto)
|
||||
@@ -590,6 +601,10 @@ if test x"$fuzzers" = x"yes"; then
|
||||
AX_CHECK_COMPILE_FLAG([-fsanitize=fuzzer-no-link],
|
||||
[DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fsanitize=fuzzer-no-link"],
|
||||
[AC_MSG_ERROR([--enable-fuzzers requires libFuzzer support (clang)])])
|
||||
# Ordinary programs are instrumented too, so they need that runtime at link.
|
||||
AX_CHECK_LINK_FLAG([-fsanitize=fuzzer-no-link],
|
||||
[DEFAULT_LDFLAGS="$DEFAULT_LDFLAGS -fsanitize=fuzzer-no-link"],
|
||||
[AC_MSG_ERROR([--enable-fuzzers requires libFuzzer support (clang)])])
|
||||
# clang's static sanitizer runtimes clash with -Wl,--no-undefined on the .so.
|
||||
if test x"$enable_shared" != x"no"; then
|
||||
AC_MSG_ERROR([--enable-fuzzers requires --disable-shared])
|
||||
@@ -597,6 +612,20 @@ if test x"$fuzzers" = x"yes"; then
|
||||
fi
|
||||
AM_CONDITIONAL([FUZZERS], [test x"$fuzzers" = x"yes"])
|
||||
|
||||
# tests/Makefile.am takes its TESTS from here instead of a checked-in list,
|
||||
# which made every pair of test-adding PRs conflict on the forge (#844).
|
||||
AC_MSG_CHECKING([for test scripts])
|
||||
TESTS_LIST=
|
||||
hts_ntests=0
|
||||
for hts_t in "$srcdir"/tests/[[0-9]]*_*.test; do
|
||||
test -f "$hts_t" || continue
|
||||
TESTS_LIST="$TESTS_LIST `echo "$hts_t" | sed 's|.*/||'`"
|
||||
hts_ntests=`expr $hts_ntests + 1`
|
||||
done
|
||||
AC_SUBST([TESTS_LIST])
|
||||
AC_MSG_RESULT([$hts_ntests])
|
||||
test "$hts_ntests" -gt 0 || AC_MSG_ERROR([no test script matched tests/[[0-9]]*_*.test])
|
||||
|
||||
# Final output
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
|
||||
29
debian/changelog
vendored
@@ -1,3 +1,32 @@
|
||||
httrack (3.49.21-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release: a site answering to several hostnames can now be
|
||||
mirrored once rather than once per name, plus fixes to mirror
|
||||
interruption, to an update pass that purged files it had never reached,
|
||||
and to several buffer overflows; full list in history.txt.
|
||||
|
||||
-- Xavier Roche <xavier@debian.org> Wed, 12 Aug 2026 15:05:15 +0200
|
||||
|
||||
httrack (3.49.20-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release: an off-site link that looked like a file could pull
|
||||
in the whole foreign site, and a mirror asked to stop while receiving over
|
||||
FTP hung; full list in history.txt. The documentation was also refreshed.
|
||||
* debian/copyright: record the League Spartan webfont the documentation now
|
||||
bundles (OFL-1.1).
|
||||
|
||||
-- Xavier Roche <xavier@debian.org> Mon, 10 Aug 2026 21:08:40 +0200
|
||||
|
||||
httrack (3.49.19-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release: mostly FTP fixes, plus stack-overflow, mirror
|
||||
interruption and --update data-loss fixes; full list in history.txt. Two
|
||||
of them matter to the build: the test suite no longer assumes GNU
|
||||
coreutils, and the AppStream metainfo no longer declares a non-stock icon
|
||||
name that appstream-util rejects.
|
||||
|
||||
-- Xavier Roche <xavier@debian.org> Sat, 08 Aug 2026 21:29:56 +0200
|
||||
|
||||
httrack (3.49.18-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release, fixing the 3.49.17-1 build failures on armhf,
|
||||
|
||||
95
debian/copyright
vendored
@@ -44,6 +44,13 @@ License: FSFAP
|
||||
permitted in any medium without royalty provided the copyright notice and
|
||||
this notice are preserved. This file is offered as-is, without any warranty.
|
||||
|
||||
Files: html/fonts/*
|
||||
Copyright: 2020 The League Spartan Project Authors
|
||||
License: OFL-1.1
|
||||
Comment:
|
||||
The woff2 is the latin subset at weight 700, as served by Google Fonts.
|
||||
Upstream declares no Reserved Font Name, so the subset keeps the family name.
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2002-2026 Xavier Roche <xavier@debian.org>
|
||||
License: GPL-3+
|
||||
@@ -106,3 +113,91 @@ License: BSD-3-clause
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
License: OFL-1.1
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
.
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
.
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
.
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
.
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
.
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
.
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
.
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
.
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
.
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
.
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
.
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
.
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
.
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
.
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
.
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
@@ -58,7 +58,9 @@ EXTRA_DIST = README.md run-fuzzers.sh \
|
||||
corpus/singlefile/img-src.html corpus/singlefile/link-rel.html \
|
||||
corpus/singlefile/style-block.html corpus/singlefile/style-attr.html \
|
||||
corpus/singlefile/srcset.html corpus/singlefile/rawtext.html \
|
||||
corpus/singlefile/malformed.html corpus/singlefile/many-attrs.html \
|
||||
corpus/singlefile/malformed.html \
|
||||
corpus/singlefile/mark-at-eof.html corpus/singlefile/mark-only.html \
|
||||
corpus/singlefile/mark-degenerate.html corpus/singlefile/traversal.html \
|
||||
corpus/sitemap/urlset.xml corpus/sitemap/sitemapindex.xml \
|
||||
corpus/sitemap/truncated.xml corpus/sitemap/urlset.xml.gz \
|
||||
corpus/arc/roundtrip.arc corpus/arc/truncated.arc \
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<img src="a.png">
|
||||
<img src=big.png alt=over-cap>
|
||||
<img src="../escape.png"><img src="/abs.png"><img src="data:,x">
|
||||
<img src="a.png">
|
||||
<img src=big.png alt=over-cap>
|
||||
<img src="../escape.png"><img src="/abs.png">
|
||||
<img src="data:,x">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<link rel="stylesheet" href="s.css">
|
||||
<link rel=icon href=a.png>
|
||||
<link rel="next" href="p2.html">
|
||||
<link rel="preload" href="j.js">
|
||||
<link rel="stylesheet" href="s.css">
|
||||
<link rel="icon" href="a.png">
|
||||
<link rel="canonical" href="other.html">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<img src="unterminated.png
|
||||
<div style="background:url(a.png">
|
||||
<style>@import url(
|
||||
<!-- unterminated comment
|
||||
<a href=
|
||||
<p a=1 b=2 c=3 d=4 e=5 f=6 g=7 h=8 i=9 j=10 title="> <img src=a.png>">x</p>
|
||||
<!--><img src="a.png">
|
||||
<img src="a.png" >
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<img src="a.png" a0="v" a1="v" a2="v" a3="v" a4="v" a5="v" a6="v" a7="v" a8="v" a9="v" a10="v" a11="v" a12="v" a13="v" a14="v" a15="v" a16="v" a17="v" a18="v" a19="v" a20="v" a21="v" a22="v" a23="v" a24="v" a25="v" a26="v" a27="v" a28="v" a29="v" a30="v" a31="v" a32="v" a33="v" a34="v" a35="v" a36="v" a37="v" a38="v" a39="v" a40="v" a41="v" a42="v" a43="v" a44="v" a45="v" a46="v" a47="v" a48="v" a49="v" a50="v" a51="v" a52="v" a53="v" a54="v" a55="v" a56="v" a57="v" a58="v" a59="v" a60="v" a61="v" a62="v" a63="v" a64="v" a65="v" a66="v" a67="v" a68="v" a69="v">
|
||||
1
fuzz/corpus/singlefile/mark-at-eof.html
Normal file
@@ -0,0 +1 @@
|
||||
a.png
|
||||
5
fuzz/corpus/singlefile/mark-degenerate.html
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
==
|
||||
""
|
||||
a.png#!htsinlin
|
||||
#!htsinlin
|
||||
1
fuzz/corpus/singlefile/mark-only.html
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<script>var s="</scripting>"; if(a</b) x=1;</script>
|
||||
<script src="j.js"></script>
|
||||
<textarea></textareas></textarea>
|
||||
<title></titles></title>
|
||||
<script src="j.js"></script>
|
||||
<script>var u="a.png";</script>
|
||||
<textarea>a.png</textarea>
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
<img srcset="a.png 1x, big.png 2x, a.png 100w">
|
||||
<source srcset="a.png,, a.png 2x," src="a.png">
|
||||
<img srcset="a.png 1x, big.png 2x, a.png 480w">
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<div style="background:url(a.png);list-style:url('a.png')"></div>
|
||||
<p style='background:url("a.png")'>x</p>
|
||||
<div style="background:url(a.png)"></div>
|
||||
<div style='background:url("a.png")'></div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<style>@import "s.css";
|
||||
@import url(sub/b.css);
|
||||
div{background:url(a.png)}
|
||||
/* url(a.png) */ p:after{content:"url(a.png)"}
|
||||
<style>@import "s.css";
|
||||
@import url(sub/b.css);
|
||||
div{background:url(a.png)}
|
||||
</style>
|
||||
|
||||
4
fuzz/corpus/singlefile/traversal.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<img src="../../../../etc/passwd">
|
||||
<img src="a.png#frag?q=1">
|
||||
<img src="sub/../a.png">
|
||||
<img src="./a.png">
|
||||
@@ -60,6 +60,10 @@ static void sf_cleanup(void) {
|
||||
(void) remove(sf_root);
|
||||
}
|
||||
|
||||
/* One httrackp for the whole run: the mark secret lives on it, so a fresh one
|
||||
per input would make every mark in the corpus unrecognisable. */
|
||||
static httrackp *sf_opt = NULL;
|
||||
|
||||
/* A missing asset would silently reduce the target to its parser half. */
|
||||
static void sf_write(const char *name, const char *data, size_t len) {
|
||||
char path[700];
|
||||
@@ -76,6 +80,48 @@ static void sf_text(const char *name, const char *data) {
|
||||
sf_write(name, data, strlen(data));
|
||||
}
|
||||
|
||||
/* Append ref plus a real mark for it. The secret is drawn per httrackp, so a
|
||||
fixture cannot spell one; every mark the target sees is built here. */
|
||||
static void sf_marked(String *out, const char *ref, char cls) {
|
||||
char mark[SINGLEFILE_MARK_MAX];
|
||||
|
||||
StringCat(*out, ref);
|
||||
StringCat(*out,
|
||||
singlefile_mark(sf_opt, mark, sizeof(mark), cls, strlen(ref)));
|
||||
}
|
||||
|
||||
/* \001<ref>\002 in an input becomes <ref> plus its mark, which is the only
|
||||
way a corpus file can reach the mark parser at all. */
|
||||
static void sf_expand_input(const char *in, size_t len, String *out) {
|
||||
size_t i, start = 0;
|
||||
|
||||
StringClear(*out);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (in[i] == '\001') {
|
||||
start = StringLength(*out);
|
||||
} else if (in[i] == '\002') {
|
||||
char mark[SINGLEFILE_MARK_MAX];
|
||||
|
||||
StringCat(*out, singlefile_mark(sf_opt, mark, sizeof(mark),
|
||||
SINGLEFILE_CLASS_ANY,
|
||||
StringLength(*out) - start));
|
||||
} else {
|
||||
StringAddchar(*out, in[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sf_css(const char *name, const char *pre, const char *ref,
|
||||
const char *post) {
|
||||
String body = STRING_EMPTY;
|
||||
|
||||
StringCopy(body, pre);
|
||||
sf_marked(&body, ref, SINGLEFILE_CLASS_ANY);
|
||||
StringCat(body, post);
|
||||
sf_write(name, StringBuff(body), StringLength(body));
|
||||
StringFree(body);
|
||||
}
|
||||
|
||||
static void sf_init(void) {
|
||||
static const char png[] = "\x89PNG\r\n\x1a\n";
|
||||
static const char big[4096] = "\x89PNG";
|
||||
@@ -83,6 +129,9 @@ static void sf_init(void) {
|
||||
char path[700];
|
||||
|
||||
hts_init();
|
||||
sf_opt = hts_create_opt();
|
||||
sf_opt->log = sf_opt->errlog = NULL;
|
||||
sf_opt->single_file_max_size = FUZZ_SF_CAP;
|
||||
snprintf(sf_root, sizeof(sf_root), "%s/httrack-fuzz-sf-XXXXXX",
|
||||
tmp != NULL && tmp[0] != '\0' ? tmp : "/tmp");
|
||||
if (mkdtemp(sf_root) == NULL)
|
||||
@@ -95,37 +144,50 @@ static void sf_init(void) {
|
||||
sf_write("a.png", png, sizeof(png) - 1);
|
||||
sf_write("big.png", big, sizeof(big));
|
||||
sf_text("j.js", "var x=1;\n");
|
||||
/* @import plus a url(), so an inlined stylesheet recurses and its own
|
||||
relative reference is rebased. */
|
||||
sf_text("s.css", "@import url(sub/b.css);\ndiv{background:url(a.png)}\n");
|
||||
sf_text("sub/b.css", "p{background:url(../a.png)}\n");
|
||||
/* Marked, so an inlined stylesheet recurses into its own marks and its
|
||||
un-inlinable reference is rebased; unmarked assets leave the target as a
|
||||
bare scan that reaches nothing. */
|
||||
{
|
||||
String css = STRING_EMPTY;
|
||||
|
||||
StringCopy(css, "@import url(");
|
||||
sf_marked(&css, "sub/b.css", SINGLEFILE_CLASS_CSS);
|
||||
StringCat(css, ");\ndiv{background:url(");
|
||||
sf_marked(&css, "a.png", SINGLEFILE_CLASS_ANY);
|
||||
StringCat(css, ")}\np{background:url(");
|
||||
sf_marked(&css, "big.png", SINGLEFILE_CLASS_ANY);
|
||||
StringCat(css, ")}\n");
|
||||
sf_write("s.css", StringBuff(css), StringLength(css));
|
||||
StringFree(css);
|
||||
}
|
||||
sf_css("sub/b.css", "p{background:url(", "../a.png", ")}\n");
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
static int inited = 0;
|
||||
|
||||
String out = STRING_EMPTY;
|
||||
httrackp *opt;
|
||||
/* Exact-length, unterminated: the rewriter is span-based, so ASan bounds a
|
||||
read past html_len instead of it landing on a terminator. */
|
||||
char *html = malloct(size != 0 ? size : 1);
|
||||
String in = STRING_EMPTY;
|
||||
char *html;
|
||||
size_t html_len;
|
||||
|
||||
if (!inited) {
|
||||
sf_init();
|
||||
inited = 1;
|
||||
}
|
||||
memcpy(html, data, size);
|
||||
|
||||
opt = hts_create_opt();
|
||||
opt->log = opt->errlog = NULL;
|
||||
opt->single_file_max_size = FUZZ_SF_CAP;
|
||||
sf_expand_input((const char *) data, size, &in);
|
||||
html_len = StringLength(in);
|
||||
/* Exact-length, unterminated: the rewriter is span-based, so ASan bounds a
|
||||
read past html_len instead of it landing on a terminator. */
|
||||
html = malloct(html_len != 0 ? html_len : 1);
|
||||
memcpy(html, StringBuff(in), html_len);
|
||||
StringFree(in);
|
||||
|
||||
StringClear(out);
|
||||
(void) singlefile_rewrite_html(opt, sf_root, sf_page, html, size,
|
||||
(void) singlefile_rewrite_html(sf_opt, sf_root, sf_page, html, html_len,
|
||||
SINGLEFILE_MAX_PAGE_SIZE, &out);
|
||||
|
||||
StringFree(out);
|
||||
freet(html);
|
||||
hts_free_opt(opt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
53
history.txt
@@ -4,6 +4,59 @@ HTTrack Website Copier release history:
|
||||
|
||||
This file lists all changes and fixes that have been made for HTTrack
|
||||
|
||||
3.49-21
|
||||
+ New: --host-alias (-%C) folds the several hostnames of one site onto a single canonical host, so a site that answers under more than one hostname is fetched once instead of once per name (#16)
|
||||
+ New: the WebHTTrack wizard can set the host-alias rules and the guide documents them for every front end; a malformed rule is refused where it can still be corrected, through a newly exported hts_host_alias_rule_ok() (#1161, #1172, #1192)
|
||||
+ Fixed: any signal caught during a mirror killed every transfer in flight, so a burst of them silently truncated the mirror; a stop now ends an HTTP transfer as the user's stop rather than a fabricated receive error (#1110)
|
||||
+ Fixed: stopping an FTP transfer against a silent server waited out --timeout twice, once on the data socket and again on a QUIT reply nobody was going to send (#1096)
|
||||
+ Fixed: a crawl that ended early ran --update's purge against the files it had never reached, deleting good ones (#1109)
|
||||
+ Fixed: a long URL overflowed the duplicate-detection key buffer (#1160)
|
||||
+ Fixed: a long command line, a large doit.log or a large .httrackrc aborted the engine while it rebuilt its arguments (#1189, #1198)
|
||||
+ Fixed: a scan rule beginning with a dash ended the whole run (#1179)
|
||||
+ Fixed: --index=0 and --noindex enabled the index rather than disabling it, and --purge-old=1 killed the run; an option that takes no value now refuses one instead of silently dropping it (#1195, #1200)
|
||||
+ Fixed: reloading a WebHTTrack project lost every zero and every cleared field, so a rate limit of 0 came back as 100 KB/s and a cleared user-agent refilled itself (#1177, #1171, #1186)
|
||||
+ Fixed: the wizard kept only the last of several --strip-query rules, and its domain answers matched beyond the domain they named (#1168, #1119)
|
||||
+ Fixed: the offer to resume an interrupted mirror printed the program path instead of the command line it would rerun (#1204)
|
||||
+ Fixed: the build failed where getrandom() has no declaration, and the installed headers did not compile under some include orders (#1139, #1150, #1151)
|
||||
+ Changed: the documentation and the WebHTTrack pages compute their copyright year instead of carrying a fixed one (#1163, #1165)
|
||||
+ Changed: refreshed the guide screenshots for all three interfaces, and repaired the documentation's dark mode and its search-target tabs (#1164, #1158)
|
||||
+ Changed: multiple internal hardening, build, test and CI improvements
|
||||
|
||||
3.49-20
|
||||
+ Fixed: a link to another host whose name looked like a file was scanned as a page, so one link could mirror the whole foreign site on the referring page's depth budget (#121)
|
||||
+ Fixed: a size-bounded filter built both of its filter URLs wrong when the path had no leading slash (#1095)
|
||||
+ Fixed: a mirror told to quit while an FTP file was being received announced the exit and then waited forever on that slot (#1106)
|
||||
+ Fixed: stopping an FTP transfer closed its data socket twice, so a threaded crawl could lose another worker's connection (#1090)
|
||||
+ Fixed: WebHTTrack's welcome page opened its language menu blank instead of marking the language in use (#1084)
|
||||
+ Fixed: two suite tests failed on the hppa, powerpc and ppc64 Debian buildds, holding the package back on those ports (#1108)
|
||||
+ Fixed: a growable string could wrap its capacity to zero instead of saturating (#1062)
|
||||
+ Changed: the macOS disk image carries its architecture in its name, so an Intel user can tell before downloading that the build is for Apple Silicon (#1083)
|
||||
+ Changed: refreshed the GUI guide, its screenshots, the documentation typography and the footer contrast
|
||||
+ Changed: settled on one spelling of the product name across the docs and the site
|
||||
+ Changed: multiple internal hardening, build, test and CI improvements
|
||||
|
||||
3.49-19
|
||||
+ Fixed: an over-long user name in an ftp:// URL was truncated at the credential boundary, so the login went to a different account (#1032)
|
||||
+ Fixed: an over-long FTP path or host name aborted the process instead of failing that one link (#1019)
|
||||
+ Fixed: an FTP crawl ignored --timeout and --max-time, so a server that went quiet held a slot for five minutes (#1039)
|
||||
+ Fixed: an FTP crawl of a dead host took two minutes to give up, and no signal could shorten it (#1059)
|
||||
+ Fixed: the engine released the transfer slots while an FTP thread was still writing to one, crashing a mirror that hit its size or time limit (#1051)
|
||||
+ Fixed: Ctrl-C did not shorten a connection or a name lookup already in flight, so ending a crawl against an unreachable host took minutes (#1073)
|
||||
+ Fixed: a crawl that gave up mid-loop, on the link limit or an abort, lost its WARC archive and its change report, and a WARC written past teardown was stranded in a .tmp file (#1061, #1060)
|
||||
+ Fixed: a crawl that rolled back for want of data still replaced the previous WARC archive, stranding its temporary and never writing its index; a stale or unwritable index is now reported rather than left silently in place (#1053)
|
||||
+ Fixed: a resume the server answered with an unusable Content-Range deleted the partial file and never fetched it again; the restart no longer spends the retry budget, and is latched to one per link (#581, #1052)
|
||||
+ Fixed: a long argument to -n or -O smashed the stack through an unbounded panic message, and the growable arrays could wrap their capacity to zero (#1040)
|
||||
+ Fixed: --single-file could write a reference mark longer than it reads back, leaving it in the delivered page as visible text (#1054, #1069)
|
||||
+ Fixed: on a terminal with no input left, a crawl toggled between the spinner and the full panel on every pass (#1072)
|
||||
+ Fixed: -%v1 ended each line with a carriage return, so the lines overwrote each other and a redirected log was unreadable; -%v2 now paints its panel only on a terminal
|
||||
+ Fixed: the installed socket headers did not compile under -std=c99 without _POSIX_C_SOURCE (#1001)
|
||||
+ Fixed: the 16 px application icon rendered every stem as grey (#938)
|
||||
+ Fixed: the desktop files installed outside the data directory, and the AppStream metainfo declared an icon name that is not a stock one
|
||||
+ Fixed: the build failed on distributions whose coreutils are not GNU's (#1042)
|
||||
+ Changed: --single-file drops its own HTML and CSS scanner and reuses the crawler's parser, so it follows the same references the mirror does (#749)
|
||||
+ Changed: updated Turkish translation, and repaired two mangled Polish strings (#306)
|
||||
+ Changed: multiple internal hardening, build, test and CI improvements
|
||||
|
||||
3.49-18
|
||||
+ Fixed: the 3.49.17 package build failed on four architectures: the test suite's mmap interposer did not compile on armhf, powerpc or hppa, and the alternate-stack test rejected loong64's shorter backtrace (#1023)
|
||||
+ Fixed: the test suite failed on a host with no ps command, such as a Fedora build root; its hang diagnostics now read /proc directly (#1021)
|
||||
|
||||
@@ -4,6 +4,7 @@ HelpHtmldir = $(htmldir)
|
||||
HelpHtmlimgdir = $(HelpHtmldir)/img
|
||||
HelpHtmldivdir = $(HelpHtmldir)/div
|
||||
HelpHtmlimagesdir = $(HelpHtmldir)/images
|
||||
HelpHtmlfontsdir = $(HelpHtmldir)/fonts
|
||||
HelpHtmlTxtdir = $(HelpHtmldir)
|
||||
WebHtmldir = $(HelpHtmldir)/server
|
||||
WebHtmlimagesdir = $(HelpHtmldir)/server/images
|
||||
@@ -15,7 +16,7 @@ WebIcon64x64dir = $(datadir)/icons/hicolor/64x64/apps
|
||||
WebIcon128x128dir = $(datadir)/icons/hicolor/128x128/apps
|
||||
WebIcon256x256dir = $(datadir)/icons/hicolor/256x256/apps
|
||||
WebIconScalabledir = $(datadir)/icons/hicolor/scalable/apps
|
||||
VFolderEntrydir = $(prefix)/share/applications
|
||||
VFolderEntrydir = $(datadir)/applications
|
||||
MetaInfodir = $(datadir)/metainfo
|
||||
|
||||
# Wildcards are globbed against $(srcdir): a bare "*.html" is resolved against
|
||||
@@ -27,6 +28,8 @@ HelpHtml_DATA = $(srcdir)/*.html $(srcdir)/*.js $(srcdir)/*.css
|
||||
HelpHtmldiv_DATA = div/search.sh
|
||||
HelpHtmlimg_DATA = $(srcdir)/img/*
|
||||
HelpHtmlimages_DATA = $(srcdir)/images/*
|
||||
# doc.css reaches the woff2 by a relative URL, so it installs beside the pages.
|
||||
HelpHtmlfonts_DATA = $(srcdir)/fonts/*
|
||||
HelpHtmlTxt_DATA = ../greetings.txt ../history.txt ../license.txt
|
||||
WebHtml_DATA = $(srcdir)/server/*.html $(srcdir)/server/*.js $(srcdir)/server/*.css
|
||||
WebHtmlimages_DATA = $(srcdir)/server/images/*
|
||||
@@ -45,6 +48,7 @@ VFolderEntry_DATA = $(srcdir)/server/div/*.desktop
|
||||
MetaInfo_DATA = $(srcdir)/server/div/*.metainfo.xml
|
||||
|
||||
EXTRA_DIST = $(HelpHtml_DATA) $(HelpHtmlimg_DATA) $(HelpHtmlimages_DATA) \
|
||||
$(HelpHtmlfonts_DATA) \
|
||||
$(HelpHtmldiv_DATA) $(WebHtml_DATA) $(WebHtmlimages_DATA) \
|
||||
$(WebPixmap_DATA) $(WebIcon16x16_DATA) $(WebIcon32x32_DATA) $(WebIcon48x48_DATA) \
|
||||
$(WebIcon64x64_DATA) $(WebIcon128x128_DATA) $(WebIcon256x256_DATA) \
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -534,9 +536,14 @@ And then, put the email address in your pages through:
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -75,8 +77,8 @@ The main advantages of this cache are:
|
||||
|
||||
<ul>
|
||||
<li>One single file for a complete website cache archive</li>
|
||||
<li>Standard <a href="http://www.pkware.com/products/enterprise/white_papers/appnote.txt" target="_new">ZIP</a> format, that can be easily reused on most platforms and languages</li>
|
||||
<li>Compressed data with the efficient and opened <a href="http://www.gzip.org/zlib/" target="_new">zlib</a> format</li>
|
||||
<li>Standard <a href="http://www.pkware.com/products/enterprise/white_papers/appnote.txt" target="_blank">ZIP</a> format, that can be easily reused on most platforms and languages</li>
|
||||
<li>Compressed data with the efficient and opened <a href="http://www.gzip.org/zlib/" target="_blank">zlib</a> format</li>
|
||||
</ul>
|
||||
|
||||
The cache is made of ZIP files entries ; with one ZIP file entry per fetched URL (successfully or not - errors are also stored).<br />
|
||||
@@ -84,7 +86,7 @@ For each entry:
|
||||
<ul>
|
||||
<li>The ZIP file name is the original URL [<small><a href="#orig">see notes below</a></small>]</li>
|
||||
<li>The ZIP file contents, <b>if available</b>, is the original (compressed, using the deflate algorythm) data</li>
|
||||
<li>The ZIP file extra field (in the local file header) contains a list of meta-fields, very similar to the <a href="http://www.ietf.org/rfc/rfc2616.txt?number=2616" target="new_">HTTP</a> headers fields. See also <a href="http://www.ietf.org/rfc/rfc2396.txt?number=2396" target="new_">RFC</a>.</li><br />
|
||||
<li>The ZIP file extra field (in the local file header) contains a list of meta-fields, very similar to the <a href="http://www.ietf.org/rfc/rfc2616.txt?number=2616" target="_blank">HTTP</a> headers fields. See also <a href="http://www.ietf.org/rfc/rfc2396.txt?number=2396" target="_blank">RFC</a>.</li><br />
|
||||
<li>The ZIP file timestamp follows the "Last-Modified-Since" field given for this URL, if any</li>
|
||||
</ul>
|
||||
|
||||
@@ -230,9 +232,14 @@ Libraries should generally handle this peculiar format, however.
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -155,6 +157,8 @@ rather than on <tt>url</tt> when a mirror is known to carry legacy-charset URLs.
|
||||
|
||||
<h3 id="example">Example</h3>
|
||||
|
||||
One captured run; <tt>generator</tt> and <tt>date</tt> will read differently in yours.
|
||||
|
||||
<pre>
|
||||
{
|
||||
"schema": 1,
|
||||
@@ -209,9 +213,14 @@ scope: both change what a mirror directory contains.</li>
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -117,6 +119,7 @@ the index" problems disappear.</p>
|
||||
<tr class="head"><td><b>Option</b></td><td><b>What it controls</b></td></tr>
|
||||
<tr><td><tt>--depth (-r)</tt></td><td>Maximum link depth. <b>The start page is level 1</b>, so one level of links out is <tt>-r2</tt>, not <tt>-r1</tt>.</td></tr>
|
||||
<tr><td><tt>--stay-on-same-address (-a), --stay-on-same-domain (-d), --stay-on-same-tld (-l), --go-everywhere (-e)</tt></td><td>How far off the starting host the crawl may travel: same address (host), same principal domain, same top-level domain (for example .com), or everywhere. The default keeps you on the starting host.</td></tr>
|
||||
<tr><td><tt>--host-alias (-%C)</tt></td><td>Declares that several hostnames serve the same site, for example <tt>--host-alias "www2.example.com,m.example.com=example.com"</tt>. Those names then count as the starting host: their links travel under the same-address rules, are fetched from the canonical host, and are saved once rather than once per name. The left side takes filter-style globs and comma-separated names, the right side one literal host, port included. Either side may name a scheme (<tt>legacy.example.com=https://example.com</tt> reaches a host that only answers over HTTPS, and <tt>https://www.example.com=ftp://files.example.com</tt> matches only the HTTPS form). A path is refused: this maps hosts, not URLs.</td></tr>
|
||||
<tr><td><tt>--can-go-down (-D), --can-go-up (-U), --stay-on-same-dir (-S), --can-go-up-and-down (-B)</tt></td><td>Directory travel: down into subdirectories only, up to parent directories only, stay in the same directory, or both up and down.</td></tr>
|
||||
<tr><td><tt>--near (-n)</tt></td><td>Also fetch non-HTML files "near" a followed link, such as an image linked from a page you kept but hosted elsewhere.</td></tr>
|
||||
<tr><td><tt>--ext-depth (-%e)</tt></td><td>How many levels of external links to follow once the crawl leaves your scope (default 0).</td></tr>
|
||||
@@ -146,7 +149,9 @@ usually not a scope option at all: it is an off-host redirect. A start URL of
|
||||
<tt>http://example.com/</tt> that redirects to <tt>https://www.example.com/</tt>
|
||||
lands you on a different host, and same-host scope stops the crawl there. Start
|
||||
from the final URL, or add a filter that re-admits the real host (see
|
||||
<a href="#filters">Filters</a>). The log will show the redirect.</p>
|
||||
<a href="#filters">Filters</a>). The log will show the redirect. When both names
|
||||
really are one site, <tt>--host-alias</tt> says so once and the mirror stops
|
||||
depending on which one you started from.</p>
|
||||
|
||||
<p><tt>-n</tt> is the fix for pages that render locally without their images or
|
||||
stylesheets: it lets HTTrack pull in requisites that sit just outside scope. Note
|
||||
@@ -503,9 +508,14 @@ all disabled.</small></p>
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -206,9 +208,14 @@ roche at httrack dot com (Xavier ROCHE)<br>
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -103,9 +105,14 @@ compared to the previous mirror. This page describes that file.
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
48
html/doc.css
@@ -32,6 +32,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Additions stay out of the palette block above: it is kept byte-identical with the website's. */
|
||||
:root {
|
||||
--body-font: system-ui, -apple-system, "Segoe UI", Roboto, "Trebuchet MS", Verdana, sans-serif;
|
||||
}
|
||||
|
||||
/* Relative URL: the docs are read from an installed directory too. */
|
||||
@font-face {
|
||||
font-family: "League Spartan";
|
||||
src: url(fonts/league-spartan-700-latin.woff2) format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
/* The plain declaration before each var() is what IE11 gets: it drops the
|
||||
@@ -44,6 +58,7 @@ body {
|
||||
color: #111;
|
||||
color: var(--ink);
|
||||
font: 1em/1.6 "Trebuchet MS", Verdana, system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
font-family: var(--body-font);
|
||||
}
|
||||
|
||||
/* ---- page frame ---- */
|
||||
@@ -72,7 +87,6 @@ body {
|
||||
/* A one-line label has no use for the body's prose leading. */
|
||||
line-height: 1;
|
||||
padding: .3rem .4rem;
|
||||
margin-top: .4rem;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
@@ -106,11 +120,18 @@ body {
|
||||
.wrap.solo { display: block; }
|
||||
}
|
||||
|
||||
/* Colour inherited: nothing lighter than --ink reaches 4.5:1 on the --field
|
||||
this sits on, and at this size it is small text. */
|
||||
/* Mirrors site.css's .sitefooter: flush on the panel's black edge, chicklet at the far end. */
|
||||
footer {
|
||||
max-width: 68rem;
|
||||
margin: .5rem auto 0;
|
||||
color: #eef;
|
||||
margin: 0 auto;
|
||||
font-size: .8em;
|
||||
line-height: 1.3;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -121,6 +142,9 @@ footer {
|
||||
|
||||
h2, h3 { scroll-margin-top: 1rem; }
|
||||
|
||||
/* Top two levels only: a display face reads worse than the system font over prose. */
|
||||
h1, h2 { font-family: "League Spartan", var(--body-font); font-weight: 700; }
|
||||
|
||||
h1 { font-size: 1.9em; margin: 0 0 .3em; line-height: 1.2; }
|
||||
h2 { font-size: 1.45em; margin: 2.5em 0 .5em; padding-bottom: .2em; border-bottom: 2px solid var(--rule); }
|
||||
h3 { font-size: 1.15em; margin: 2em 0 .4em; }
|
||||
@@ -342,8 +366,26 @@ dialog#zoom::backdrop { background: rgba(0, 0, 0, .8); }
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Some browsers still apply prefers-color-scheme: dark when printing, so the ink
|
||||
comes out #e6e6ee on white paper. Reset the palette, not each rule reading it. */
|
||||
@media print {
|
||||
:root {
|
||||
--field: #77b;
|
||||
--panel: #ccd;
|
||||
--accent: #99c;
|
||||
--ink: #111;
|
||||
--ink-soft: #444;
|
||||
--rule: #aab;
|
||||
--link: #00c;
|
||||
--link-hover: #c00;
|
||||
--code-bg: #e8e8f2;
|
||||
--chip: #dde;
|
||||
--shadow: rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
body { background: #fff; }
|
||||
.toc, .filter { display: none; }
|
||||
.wrap { display: block; border: 0; }
|
||||
/* Not a token, same paper: the dark scheme inverts the wordmark to white. */
|
||||
.masthead img { filter: none; }
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -318,7 +320,7 @@ However, be sure to always download HTTrack from a <b>trusted source</b> (prefer
|
||||
|
||||
<br><br><a NAME="QG0c">Q: <strong>This software is 'free', but I bought it from an authorized reseller . What's going on?</strong></a><br>
|
||||
A: <em>
|
||||
HTTrack is free (free as in 'freedom') as it is covered by the <a href="http://www.gnu.org/licenses/gpl.txt" target="_new">GNU General Public License (GPL)</a>.
|
||||
HTTrack is free (free as in 'freedom') as it is covered by the <a href="http://www.gnu.org/licenses/gpl.txt" target="_blank">GNU General Public License (GPL)</a>.
|
||||
You can freely download it, without paying any fees, copy it to your friends, and modify it if you respect the license.
|
||||
There are NO official/authorized resellers, because HTTrack is <b>NOT</b> a commercial product.
|
||||
But you can be charged for duplication fees, or any other services (example: software CDroms or shareware collections, or fees for maintenance),
|
||||
@@ -881,9 +883,14 @@ A: <em>Feel free to <a href="contact.html">contact us</a>!
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -174,7 +176,7 @@ Action options:
|
||||
|
||||
Proxy options:
|
||||
P proxy use (-P proxy:port or -P user:pass@proxy:port) (--proxy <param>)
|
||||
%f *use proxy for ftp (f0 don't use) (--httpproxy-ftp[=N])
|
||||
%f *use proxy for ftp (f0 don't use) (--httpproxy-ftp[=0])
|
||||
|
||||
Limits options:
|
||||
rN set the mirror depth to N (* r9999) (--depth[=N])
|
||||
@@ -195,7 +197,7 @@ Flow control:
|
||||
HN host is abandoned if: 0=never, 1=timeout, 2=slow, 3=timeout or slow (--host-control[=N])
|
||||
|
||||
Links options:
|
||||
%P *extended parsing, attempt to parse all links, even in unknown tags or Javascript (%P0 don't use) (--extended-parsing[=N])
|
||||
%P *extended parsing, attempt to parse all links, even in unknown tags or Javascript (%P0 don't use) (--extended-parsing[=0])
|
||||
n get non-html files 'near' an html file (ex: an image located outside) (--near)
|
||||
t test all URLs (even forbidden ones) (--test)
|
||||
%L <file> add all URL located in this text file (one URL per line) (--list <param>)
|
||||
@@ -209,7 +211,7 @@ Build options:
|
||||
%x do not include any password for external password protected websites (%x0 include) (--no-passwords)
|
||||
%q *include query string for local files (useless, for information purpose only) (%q0 don't include) (--include-query-string)
|
||||
o *save the server's error pages (404..) (o0 discard them) (--generate-errors)
|
||||
X *purge old files after update (X0 keep delete) (--purge-old[=N])
|
||||
X *purge old files after update (X0 keep delete) (--purge-old[=0])
|
||||
|
||||
Spider options:
|
||||
bN accept cookies in cookies.txt (0=do not accept,* 1=accept) (--cookies[=N])
|
||||
@@ -2669,9 +2671,14 @@ for only 2 simultaneous sesions.
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:top -->
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -416,9 +418,14 @@ See also: The <a href="faq.html#VF1">FAQ</a><br>
|
||||
</div>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged image"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- /doc-chrome:bottom -->
|
||||
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
93
html/fonts/OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The League Spartan Project Authors (https://github.com/theleagueof/league-spartan)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
html/fonts/league-spartan-700-latin.woff2
Normal file
@@ -13,12 +13,14 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap">
|
||||
|
||||
@@ -58,6 +60,9 @@
|
||||
|
||||
<main id="main">
|
||||
|
||||
<!-- Section ids are link API: the redirect stubs and the Android app's contextual help
|
||||
hardcode them, and an app release outlives its engine pin. -->
|
||||
|
||||
<h1>The HTTrack interface, step by step</h1>
|
||||
|
||||
<p class="lede">HTTrack copies a website to your disk so you can read it offline. The same
|
||||
@@ -88,7 +93,7 @@ itself down once those pings stop.</p>
|
||||
<p>Each option also lists its command-line equivalent, so anything you set here can later be
|
||||
scripted. The <a href="cmdguide.html">command-line guide</a> covers that side.</p>
|
||||
|
||||
<div class="note" data-for="droid">
|
||||
<div class="note" data-for="droid" id="droid-first-launch">
|
||||
<p><b>First launch.</b> Android asks for permission to store mirrors on your device. Without
|
||||
it the app cannot save anything, so tap <b>Allow</b>.</p>
|
||||
<figure>
|
||||
@@ -117,13 +122,13 @@ data-for="droid">Tap <b>Next</b></span> to create a project, or open one you alr
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-start.png" alt="WinHTTrack welcome pane" width="1040" height="744">
|
||||
<img data-for="web" src="img/guide-web-start.png" alt="WebHTTrack welcome page" width="1024" height="388">
|
||||
<img data-for="web" src="img/guide-web-start.png" alt="WebHTTrack welcome page" width="1024" height="387">
|
||||
<img data-for="droid" src="img/guide-droid-start.png" alt="HTTrack for Android welcome screen" width="600" height="1300">
|
||||
<figcaption>The welcome screen.</figcaption>
|
||||
</figure>
|
||||
|
||||
<p class="note" data-for="web">The language dropdown starts blank on purpose: its first entry
|
||||
means "leave the interface as it is". Pick a language only if you want to change it.</p>
|
||||
<p class="note" data-for="web">The dropdown shows the language the interface is already in.
|
||||
Picking another one reloads the page in it.</p>
|
||||
|
||||
<p class="note" data-for="web">On macOS, <code>brew install httrack</code> installs WebHTTrack
|
||||
alongside the command line tool. Open <b>HTTrack.app</b> if you have it, or run
|
||||
@@ -138,7 +143,7 @@ together in the list.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-project-name.png" alt="WinHTTrack project name, category and base path" width="1040" height="744">
|
||||
<img data-for="web" src="img/guide-web-project-name.png" alt="WebHTTrack project name, category and base path" width="1024" height="346">
|
||||
<img data-for="web" src="img/guide-web-project-name.png" alt="WebHTTrack project name, category and base path" width="1024" height="345">
|
||||
<img data-for="droid" src="img/guide-droid-project-name.png" alt="Android project name, category and base path" width="600" height="1300">
|
||||
<figcaption>Project name, category, and where the files go.</figcaption>
|
||||
</figure>
|
||||
@@ -157,7 +162,7 @@ keep their links to each other.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-project-setup.png" alt="WinHTTrack action and web addresses" width="1040" height="744">
|
||||
<img data-for="web" src="img/guide-web-project-setup.png" alt="WebHTTrack action and web addresses" width="1024" height="496">
|
||||
<img data-for="web" src="img/guide-web-project-setup.png" alt="WebHTTrack action and web addresses" width="1024" height="495">
|
||||
<img data-for="droid" src="img/guide-droid-project-setup.png" alt="Android web address and action" width="600" height="1300">
|
||||
<figcaption>The addresses to copy, and what to do with them.</figcaption>
|
||||
</figure>
|
||||
@@ -212,7 +217,7 @@ port, you point your browser at it as a proxy and click the link or the submit b
|
||||
usual, and the address the browser asks for is added to the project.</p>
|
||||
|
||||
<figure data-for="web">
|
||||
<img data-for="web" src="img/guide-web-add-url.png" alt="WebHTTrack add-a-URL dialog" width="1024" height="634">
|
||||
<img data-for="web" src="img/guide-web-add-url.png" alt="WebHTTrack add-a-URL dialog" width="1024" height="600">
|
||||
<figcaption>Adding a single address, with an optional login.</figcaption>
|
||||
</figure>
|
||||
|
||||
@@ -241,7 +246,7 @@ mirror straight away.</p>
|
||||
|
||||
<figure data-for="win web">
|
||||
<img data-for="win" src="img/guide-win-ready.png" alt="WinHTTrack connection settings before starting" width="1040" height="744">
|
||||
<img data-for="web" src="img/guide-web-ready.png" alt="WebHTTrack ready to start" width="1024" height="508">
|
||||
<img data-for="web" src="img/guide-web-ready.png" alt="WebHTTrack ready to start" width="1024" height="489">
|
||||
<figcaption>The last screen before the mirror runs.</figcaption>
|
||||
</figure>
|
||||
|
||||
@@ -274,7 +279,7 @@ mirrored pages are rewritten to point at each other, so it browses offline in an
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-finished.png" alt="WinHTTrack mirror complete" width="1040" height="744">
|
||||
<img data-for="web" src="img/guide-web-finished.png" alt="WebHTTrack mirror complete" width="1024" height="487">
|
||||
<img data-for="web" src="img/guide-web-finished.png" alt="WebHTTrack mirror complete" width="1024" height="486">
|
||||
<img data-for="droid" src="img/guide-droid-finished.png" alt="Android mirror complete" width="600" height="1300">
|
||||
<figcaption>Done.</figcaption>
|
||||
</figure>
|
||||
@@ -287,7 +292,7 @@ usual causes.</p>
|
||||
<p data-for="win web">The files sit under the base path you chose, in a folder named after the
|
||||
project. Opening <code>index.html</code> there browses the mirror without HTTrack.</p>
|
||||
|
||||
<p data-for="droid">Mirrors are written to
|
||||
<p data-for="droid" id="droid-storage-location">Mirrors are written to
|
||||
<code>/storage/emulated/0/HTTrack/Websites</code>, in a folder named after the project. That is
|
||||
shared storage, so a file manager or a USB cable can reach it, and opening
|
||||
<code>index.html</code> there browses the mirror without the app.</p>
|
||||
@@ -438,7 +443,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-flow-control.png" alt="WinHTTrack Flow Control tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-flow-control.png" alt="WebHTTrack Flow Control tab" width="1024" height="910">
|
||||
<img data-for="web" src="img/guide-web-opt-flow-control.png" alt="WebHTTrack Flow Control tab" width="1024" height="862">
|
||||
<img data-for="droid" src="img/guide-droid-opt-flow-control.png" alt="Android Flow Control tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -481,7 +486,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-links.png" alt="WinHTTrack Links tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-links.png" alt="WebHTTrack Links tab" width="1024" height="781">
|
||||
<img data-for="web" src="img/guide-web-opt-links.png" alt="WebHTTrack Links tab" width="1024" height="749">
|
||||
<img data-for="droid" src="img/guide-droid-opt-links.png" alt="Android Links tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -518,7 +523,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-build.png" alt="WinHTTrack Build tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-build.png" alt="WebHTTrack Build tab" width="1024" height="986">
|
||||
<img data-for="web" src="img/guide-web-opt-build.png" alt="WebHTTrack Build tab" width="1024" height="976">
|
||||
<img data-for="droid" src="img/guide-droid-opt-build.png" alt="Android Build tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -584,7 +589,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-browser-id.png" alt="WinHTTrack Browser ID tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-browser-id.png" alt="WebHTTrack Browser ID tab" width="1024" height="706">
|
||||
<img data-for="web" src="img/guide-web-opt-browser-id.png" alt="WebHTTrack Browser ID tab" width="1024" height="654">
|
||||
<img data-for="droid" src="img/guide-droid-opt-browser-id.png" alt="Android Browser ID tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -624,7 +629,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-spider.png" alt="WinHTTrack Spider tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-spider.png" alt="WebHTTrack Spider tab" width="1024" height="1557">
|
||||
<img data-for="web" src="img/guide-web-opt-spider.png" alt="WebHTTrack Spider tab" width="1024" height="1770">
|
||||
<img data-for="droid" src="img/guide-droid-opt-spider.png" alt="Android Spider tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -698,11 +703,23 @@ bottom are the ones that keep you welcome.</p>
|
||||
mirror something that needs you logged in, by exporting the session from your browser.</p>
|
||||
</div>
|
||||
|
||||
<div class="opt" data-for="win web">
|
||||
<div class="opt" data-for="win web droid">
|
||||
<div class="opt-head"><span class="opt-name">Strip query keys</span><span class="cli">-%g, --strip-query</span></div>
|
||||
<p>Comma-separated query keys to drop when naming saved files, such as
|
||||
<code>sid,utm_source</code>. Keeps one page from being saved several times under tracking
|
||||
parameters that change nothing.</p>
|
||||
parameters that change nothing. You can scope a rule to the addresses it covers, for example
|
||||
<code>shop.example.com/*=sid</code>.</p>
|
||||
<p data-for="web">The box takes one rule per line. Only the last matching rule applies, so keep
|
||||
the keys for one scope together on a line rather than on lines of their own.</p>
|
||||
</div>
|
||||
|
||||
<div class="opt" data-for="win web droid">
|
||||
<div class="opt-head"><span class="opt-name">Host aliases</span><span class="cli">-%C, --host-alias</span></div>
|
||||
<p>Names the other hostnames one site answers to, one rule per line, written
|
||||
<code>www2.example.com,m.example.com=example.com</code>. They fold onto that host: their links
|
||||
travel under the same rules and each page is saved once instead of once per name. Either side may
|
||||
carry a scheme, as in <code>legacy.example.com=https://example.com</code>. A trailing slash is
|
||||
ignored, but a path is refused: this maps hosts, not addresses.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -711,7 +728,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-proxy.png" alt="WinHTTrack Proxy tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-proxy.png" alt="WebHTTrack Proxy tab" width="1024" height="762">
|
||||
<img data-for="web" src="img/guide-web-opt-proxy.png" alt="WebHTTrack Proxy tab" width="1024" height="760">
|
||||
<img data-for="droid" src="img/guide-droid-opt-proxy.png" alt="Android Proxy tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -748,7 +765,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-log-index-cache.png" alt="WinHTTrack Log, Index, Cache tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-log-index-cache.png" alt="WebHTTrack Log, Index, Cache tab" width="1024" height="1278">
|
||||
<img data-for="web" src="img/guide-web-opt-log-index-cache.png" alt="WebHTTrack Log, Index, Cache tab" width="1024" height="1266">
|
||||
<img data-for="droid" src="img/guide-droid-opt-log-index-cache.png" alt="Android Log, Index, Cache tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -847,7 +864,7 @@ bottom are the ones that keep you welcome.</p>
|
||||
|
||||
<figure>
|
||||
<img data-for="win" src="img/guide-win-opt-experts-only.png" alt="WinHTTrack Experts Only tab" width="524" height="448">
|
||||
<img data-for="web" src="img/guide-web-opt-experts-only.png" alt="WebHTTrack Experts Only tab" width="1024" height="1088">
|
||||
<img data-for="web" src="img/guide-web-opt-experts-only.png" alt="WebHTTrack Experts Only tab" width="1024" height="1085">
|
||||
<img data-for="droid" src="img/guide-droid-opt-experts-only.png" alt="Android Experts Only tab" width="600" height="1300">
|
||||
</figure>
|
||||
|
||||
@@ -910,7 +927,12 @@ are how you widen a crawl deliberately.</p>
|
||||
|
||||
<dialog id="zoom" aria-label="Enlarged screenshot"><img src="" alt=""></dialog>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -76,7 +76,7 @@ offline browser : copy websites to a local directory</p>
|
||||
[ <b>-w, --mirror</b> ] [ <b>-W, --mirror-wizard</b> ] [
|
||||
<b>-g, --get-files</b> ] [ <b>-i, --continue</b> ] [ <b>-Y,
|
||||
--mirrorlinks</b> ] [ <b>-P, --proxy</b> ] [ <b>-%f,
|
||||
--httpproxy-ftp[=N]</b> ] [ <b>-%b, --bind</b> ] [ <b>-rN,
|
||||
--httpproxy-ftp[=0]</b> ] [ <b>-%b, --bind</b> ] [ <b>-rN,
|
||||
--depth[=N]</b> ] [ <b>-%eN, --ext-depth[=N]</b> ] [ <b>-mN,
|
||||
--max-files[=N]</b> ] [ <b>-MN, --max-size[=N]</b> ] [
|
||||
<b>-EN, --max-time[=N]</b> ] [ <b>-AN, --max-rate[=N]</b> ]
|
||||
@@ -85,48 +85,50 @@ offline browser : copy websites to a local directory</p>
|
||||
--sockets[=N]</b> ] [ <b>-TN, --timeout[=N]</b> ] [ <b>-RN,
|
||||
--retries[=N]</b> ] [ <b>-JN, --min-rate[=N]</b> ] [ <b>-HN,
|
||||
--host-control[=N]</b> ] [ <b>-%P,
|
||||
--extended-parsing[=N]</b> ] [ <b>-n, --near</b> ] [ <b>-t,
|
||||
--extended-parsing[=0]</b> ] [ <b>-n, --near</b> ] [ <b>-t,
|
||||
--test</b> ] [ <b>-%L, --list</b> ] [ <b>-%S, --urllist</b>
|
||||
] [ <b>-%m, --sitemap</b> ] [ <b>-NN, --structure[=N]</b> ]
|
||||
[ <b>-%N, --delayed-type-check</b> ] [ <b>-%D,
|
||||
--cached-delayed-type-check</b> ] [ <b>-%M, --mime-html</b>
|
||||
] [ <b>-%Z, --single-file</b> ] [ <b>-LN,
|
||||
--long-names[=N]</b> ] [ <b>-KN, --keep-links[=N]</b> ] [
|
||||
<b>-x, --replace-external</b> ] [ <b>-%x,
|
||||
--disable-passwords</b> ] [ <b>-%q,
|
||||
--include-query-string</b> ] [ <b>-%g, --strip-query</b> ] [
|
||||
<b>-o, --generate-errors</b> ] [ <b>-X, --purge-old[=N]</b>
|
||||
] [ <b>-%p, --preserve</b> ] [ <b>-%T, --utf8-conversion</b>
|
||||
[ <b>-%N, --delayed-type-check[=N]</b> ] [ <b>-%D,
|
||||
--cached-delayed-type-check[=0]</b> ] [ <b>-%M,
|
||||
--mime-html[=0]</b> ] [ <b>-%Z, --single-file[=0]</b> ] [
|
||||
<b>-LN, --long-names[=N]</b> ] [ <b>-KN,
|
||||
--keep-links[=N]</b> ] [ <b>-x, --replace-external</b> ] [
|
||||
<b>-%x, --disable-passwords[=0]</b> ] [ <b>-%q,
|
||||
--include-query-string[=0]</b> ] [ <b>-%g, --strip-query</b>
|
||||
] [ <b>-%C, --host-alias</b> ] [ <b>-o,
|
||||
--generate-errors[=N]</b> ] [ <b>-X, --purge-old[=0]</b> ] [
|
||||
<b>-%p, --preserve</b> ] [ <b>-%T, --utf8-conversion[=0]</b>
|
||||
] [ <b>-bN, --cookies[=N]</b> ] [ <b>-%K, --cookies-file</b>
|
||||
] [ <b>-%Y, --why</b> ] [ <b>-u, --check-type[=N]</b> ] [
|
||||
<b>-j, --parse-java[=N]</b> ] [ <b>-sN, --robots[=N]</b> ] [
|
||||
<b>-%h, --http-10</b> ] [ <b>-%k, --keep-alive</b> ] [
|
||||
<b>-%z, --disable-compression</b> ] [ <b>-%B, --tolerant</b>
|
||||
] [ <b>-%s, --updatehack</b> ] [ <b>-%u, --urlhack</b> ] [
|
||||
<b>-%A, --assume</b> ] [ <b>-@iN, --protocol[=N]</b> ] [
|
||||
<b>-%w, --disable-module</b> ] [ <b>-F, --user-agent</b> ] [
|
||||
<b>-%R, --referer</b> ] [ <b>-%E, --from</b> ] [ <b>-%F,
|
||||
--footer</b> ] [ <b>-%l, --language</b> ] [ <b>-%a,
|
||||
--accept</b> ] [ <b>-%X, --headers</b> ] [ <b>-C,
|
||||
--cache[=N]</b> ] [ <b>-k, --store-all-in-cache</b> ] [
|
||||
<b>-%r, --warc</b> ] [ <b>-%d, --changes</b> ] [ <b>-%n,
|
||||
--do-not-recatch</b> ] [ <b>-%v, --display</b> ] [ <b>-Q,
|
||||
--do-not-log</b> ] [ <b>-q, --quiet</b> ] [ <b>-z,
|
||||
--extra-log</b> ] [ <b>-Z, --debug-log</b> ] [ <b>-v,
|
||||
--verbose</b> ] [ <b>-f, --file-log</b> ] [ <b>-f2,
|
||||
--single-log</b> ] [ <b>-I, --index</b> ] [ <b>-%i,
|
||||
--build-top-index</b> ] [ <b>-%I, --search-index</b> ] [
|
||||
<b>-pN, --priority[=N]</b> ] [ <b>-S, --stay-on-same-dir</b>
|
||||
] [ <b>-D, --can-go-down</b> ] [ <b>-U, --can-go-up</b> ] [
|
||||
<b>-%h, --http-10[=0]</b> ] [ <b>-%k, --keep-alive[=0]</b> ]
|
||||
[ <b>-%z, --disable-compression[=0]</b> ] [ <b>-%B,
|
||||
--tolerant[=0]</b> ] [ <b>-%s, --updatehack[=0]</b> ] [
|
||||
<b>-%u, --urlhack[=0]</b> ] [ <b>-%A, --assume</b> ] [
|
||||
<b>-@iN, --protocol[=N]</b> ] [ <b>-%w, --disable-module</b>
|
||||
] [ <b>-F, --user-agent</b> ] [ <b>-%R, --referer</b> ] [
|
||||
<b>-%E, --from</b> ] [ <b>-%F, --footer</b> ] [ <b>-%l,
|
||||
--language</b> ] [ <b>-%a, --accept</b> ] [ <b>-%X,
|
||||
--headers</b> ] [ <b>-C, --cache[=N]</b> ] [ <b>-k,
|
||||
--store-all-in-cache</b> ] [ <b>-%r, --warc</b> ] [ <b>-%d,
|
||||
--changes[=0]</b> ] [ <b>-%n, --do-not-recatch[=0]</b> ] [
|
||||
<b>-%v, --display[=N]</b> ] [ <b>-Q, --do-not-log</b> ] [
|
||||
<b>-q, --quiet</b> ] [ <b>-z, --extra-log</b> ] [ <b>-Z,
|
||||
--debug-log</b> ] [ <b>-v, --verbose</b> ] [ <b>-f,
|
||||
--file-log</b> ] [ <b>-f2, --single-log</b> ] [ <b>-I,
|
||||
--index[=0]</b> ] [ <b>-%i, --build-top-index[=0]</b> ] [
|
||||
<b>-%I, --search-index[=N]</b> ] [ <b>-pN,
|
||||
--priority[=N]</b> ] [ <b>-S, --stay-on-same-dir</b> ] [
|
||||
<b>-D, --can-go-down</b> ] [ <b>-U, --can-go-up</b> ] [
|
||||
<b>-B, --can-go-up-and-down</b> ] [ <b>-a,
|
||||
--stay-on-same-address</b> ] [ <b>-d,
|
||||
--stay-on-same-domain</b> ] [ <b>-l, --stay-on-same-tld</b>
|
||||
] [ <b>-e, --go-everywhere</b> ] [ <b>-%H,
|
||||
--debug-headers</b> ] [ <b>-%!,
|
||||
--disable-security-limits</b> ] [ <b>-V, --userdef-cmd</b> ]
|
||||
[ <b>-%W, --callback</b> ] [ <b>-y,
|
||||
--background-on-suspend</b> ] [ <b>-K, --keep-links[=N]</b>
|
||||
]</p>
|
||||
--disable-security-limits[=0]</b> ] [ <b>-V,
|
||||
--userdef-cmd</b> ] [ <b>-%W, --callback</b> ] [ <b>-y,
|
||||
--background-on-suspend[=0]</b> ] [ <b>-K,
|
||||
--keep-links[=N]</b> ]</p>
|
||||
|
||||
<h2>DESCRIPTION
|
||||
<a name="DESCRIPTION"></a>
|
||||
@@ -297,7 +299,7 @@ httrack --continue</b></p>
|
||||
|
||||
|
||||
<p>*use proxy for ftp (f0 don’t use)
|
||||
(--httpproxy-ftp[=N])</p> </td></tr>
|
||||
(--httpproxy-ftp[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -529,7 +531,7 @@ errors (*R1) (--retries[=N])</p></td></tr>
|
||||
|
||||
<p style="margin-top: 1em">*extended parsing, attempt to
|
||||
parse all links, even in unknown tags or Javascript (%P0
|
||||
don’t use) (--extended-parsing[=N])</p></td></tr>
|
||||
don’t use) (--extended-parsing[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -635,7 +637,7 @@ structure, 1+: see below) (--structure[=N])</p></td></tr>
|
||||
make any link test but wait for files download to start
|
||||
instead (experimental) (%N0 don’t use, %N1 use for
|
||||
unknown extensions, * %N2 always use)
|
||||
(--delayed-type-check)</p> </td></tr>
|
||||
(--delayed-type-check[=N])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -648,7 +650,7 @@ unknown extensions, * %N2 always use)
|
||||
|
||||
<p>cached delayed type check, don’t wait for remote
|
||||
type during updates, to speedup them (%D0 wait, * %D1
|
||||
don’t wait) (--cached-delayed-type-check)</p></td></tr>
|
||||
don’t wait) (--cached-delayed-type-check[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -660,7 +662,7 @@ don’t wait) (--cached-delayed-type-check)</p></td></tr>
|
||||
|
||||
|
||||
<p>generate a RFC MIME-encapsulated full-archive (.mht)
|
||||
(--mime-html)</p> </td></tr>
|
||||
(--mime-html[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -678,7 +680,7 @@ between pages stay relative; audio and video stay links);
|
||||
--single-file-max-size N caps each asset (default 10485760
|
||||
bytes). %M is the better container where a Chromium-family
|
||||
browser is a given: one archive, no base64 tax on text, a
|
||||
shared asset stored once (--single-file)</p></td></tr>
|
||||
shared asset stored once (--single-file[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -740,7 +742,8 @@ absolute URI links, K5 transparent proxy link)
|
||||
|
||||
|
||||
<p>do not include any password for external password
|
||||
protected websites (%x0 include) (--disable-passwords)</p></td></tr>
|
||||
protected websites (%x0 include)
|
||||
(--disable-passwords[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -753,7 +756,7 @@ protected websites (%x0 include) (--disable-passwords)</p></td></tr>
|
||||
|
||||
<p>*include query string for local files (useless, for
|
||||
information purpose only) (%q0 don’t include)
|
||||
(--include-query-string)</p> </td></tr>
|
||||
(--include-query-string[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -772,13 +775,26 @@ information purpose only) (%q0 don’t include)
|
||||
<td width="4%">
|
||||
|
||||
|
||||
<p>-%C</p></td>
|
||||
<td width="5%"></td>
|
||||
<td width="82%">
|
||||
|
||||
|
||||
<p>fold other hostnames of one site onto it
|
||||
([scheme://]alias[,...]=[scheme://]host) (--host-alias
|
||||
<param>)</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
|
||||
|
||||
<p>-o</p></td>
|
||||
<td width="5%"></td>
|
||||
<td width="82%">
|
||||
|
||||
|
||||
<p>*save the server’s error pages (404..) (o0 discard
|
||||
them) (--generate-errors)</p></td></tr>
|
||||
them) (--generate-errors[=N])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -790,7 +806,7 @@ them) (--generate-errors)</p></td></tr>
|
||||
|
||||
|
||||
<p>*purge old files after update (X0 keep delete)
|
||||
(--purge-old[=N])</p> </td></tr>
|
||||
(--purge-old[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -813,7 +829,7 @@ them) (--generate-errors)</p></td></tr>
|
||||
<td width="82%">
|
||||
|
||||
|
||||
<p>links conversion to UTF-8 (--utf8-conversion)</p></td></tr>
|
||||
<p>links conversion to UTF-8 (--utf8-conversion[=0])</p></td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Spider options:
|
||||
@@ -909,7 +925,7 @@ rules)) (--robots[=N])</p></td></tr>
|
||||
|
||||
|
||||
<p>force HTTP/1.0 requests (reduce update features, only
|
||||
for old servers or proxies) (--http-10)</p></td></tr>
|
||||
for old servers or proxies) (--http-10[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -922,7 +938,7 @@ for old servers or proxies) (--http-10)</p></td></tr>
|
||||
|
||||
<p>use keep-alive if possible, greately reducing latency
|
||||
for small files and test requests (%k0 don’t use)
|
||||
(--keep-alive)</p> </td></tr>
|
||||
(--keep-alive[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -934,7 +950,7 @@ for small files and test requests (%k0 don’t use)
|
||||
|
||||
|
||||
<p>do not request compressed content (%z0 request)
|
||||
(--disable-compression)</p> </td></tr>
|
||||
(--disable-compression[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -946,7 +962,7 @@ for small files and test requests (%k0 don’t use)
|
||||
|
||||
|
||||
<p>tolerant requests (accept bogus responses on some
|
||||
servers, but not standard!) (--tolerant)</p></td></tr>
|
||||
servers, but not standard!) (--tolerant[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -959,7 +975,7 @@ servers, but not standard!) (--tolerant)</p></td></tr>
|
||||
|
||||
<p>update hacks: various hacks to limit re-transfers when
|
||||
updating (identical size, bogus response..)
|
||||
(--updatehack)</p> </td></tr>
|
||||
(--updatehack[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -971,7 +987,7 @@ updating (identical size, bogus response..)
|
||||
|
||||
|
||||
<p>url hacks: various hacks to limit duplicate URLs (strip
|
||||
//, www.foo.com==foo.com..) (--urlhack)</p></td></tr>
|
||||
//, www.foo.com==foo.com..) (--urlhack[=0])</p></td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%;">opt out of one url-hack part:
|
||||
@@ -1186,7 +1202,7 @@ past N bytes, --warc-cdx also writes a sorted CDXJ index,
|
||||
|
||||
<p>write hts-changes.json listing what this crawl left new,
|
||||
changed, unchanged and gone compared to the previous mirror
|
||||
(--changes)</p> </td></tr>
|
||||
(--changes[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -1198,7 +1214,7 @@ changed, unchanged and gone compared to the previous mirror
|
||||
|
||||
|
||||
<p>do not re-download locally erased files
|
||||
(--do-not-recatch)</p> </td></tr>
|
||||
(--do-not-recatch[=0])</p> </td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -1210,7 +1226,7 @@ changed, unchanged and gone compared to the previous mirror
|
||||
|
||||
|
||||
<p>display on screen filenames downloaded (in realtime) - *
|
||||
%v1 short version - %v2 full animation (--display)</p></td></tr>
|
||||
%v1 short version - %v2 full animation (--display[=N])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -1298,7 +1314,7 @@ changed, unchanged and gone compared to the previous mirror
|
||||
<td width="82%">
|
||||
|
||||
|
||||
<p>*make an index (I0 don’t make) (--index)</p></td></tr>
|
||||
<p>*make an index (I0 don’t make) (--index[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -1310,7 +1326,7 @@ changed, unchanged and gone compared to the previous mirror
|
||||
|
||||
|
||||
<p>make a top index for a project folder (* %i0 don’t
|
||||
make) (--build-top-index)</p></td></tr>
|
||||
make) (--build-top-index[=0])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="4%">
|
||||
@@ -1322,7 +1338,7 @@ make) (--build-top-index)</p></td></tr>
|
||||
|
||||
|
||||
<p>make an searchable index for this mirror (* %I0
|
||||
don’t make) (--search-index)</p></td></tr>
|
||||
don’t make) (--search-index[=N])</p></td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Expert options:
|
||||
@@ -1627,7 +1643,7 @@ with -#test=NAME [args])</p></td></tr>
|
||||
<td width="82%">
|
||||
|
||||
|
||||
<p>wait time (--advanced-wait)</p></td></tr>
|
||||
<p>wait time (--advanced-wait[=N])</p></td></tr>
|
||||
<tr valign="top" align="left">
|
||||
<td width="9%"></td>
|
||||
<td width="8%">
|
||||
@@ -1661,7 +1677,7 @@ with -#test=NAME [args])</p></td></tr>
|
||||
|
||||
<p style="margin-top: 1em">bypass built-in security limits
|
||||
aimed to avoid bandwidth abuses (bandwidth, simultaneous
|
||||
connections) (--disable-security-limits)</p></td></tr>
|
||||
connections) (--disable-security-limits[=0])</p></td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin-left:18%;">IMPORTANT NOTE: DANGEROUS
|
||||
@@ -1711,7 +1727,7 @@ myfoo.so[,myparameters]) (--callback <param>)</p></td></tr>
|
||||
|
||||
|
||||
<p>go to background when suspended (y0 don’t)
|
||||
(--background-on-suspend)</p> </td></tr>
|
||||
(--background-on-suspend[=0])</p> </td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Details: Option N
|
||||
|
||||
BIN
html/images/esp_chicklet.png
Normal file
|
After Width: | Height: | Size: 842 B |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 13 KiB |
@@ -10,12 +10,14 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- doc-chrome:masthead -->
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="masthead">
|
||||
<img src="images/wordmark.svg" width="400" height="36" alt="HTTrack Website Copier">
|
||||
<div class="tagline">Open Source offline browser</div>
|
||||
<div class="tagline">Free software offline browser</div>
|
||||
</header>
|
||||
<!-- /doc-chrome:masthead -->
|
||||
|
||||
<div class="wrap solo">
|
||||
<main id="main">
|
||||
@@ -108,7 +110,12 @@ not own.</p>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</footer>
|
||||
<!-- doc-chrome:footer -->
|
||||
<footer>
|
||||
<small>© 1998-2026 Xavier Roche & other contributors - Web Design: Leto Kauler.</small>
|
||||
<a href="https://endsoftwarepatents.org/innovating-without-patents/" target="_blank" rel="noopener" title="This site is innovating without patents!"><img src="images/esp_chicklet.png" width="91" height="17" alt="End Software Patents"></a>
|
||||
</footer>
|
||||
<!-- /doc-chrome:footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||