mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-07-28 14:47:22 -07:00
fix(update): repair master->main tracking in self-updater
Old clones made before the default-branch rename sit on a local `master` whose upstream still points at the deleted refs/heads/master, so both a bare `git pull` and `_run_upgrade()`'s `git checkout main` failed on stale clones. The updater now fetches origin first, checks out main via `git checkout -B main origin/main`, repairs the upstream to origin/main, and pulls explicitly with `git pull origin main` so it never consults the dangling branch.*.merge config. Existing dirty-branch and detached-HEAD guards are unchanged. Adds tests covering the renamed-clone migration and the fetch-failure bail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
00dcd74628
commit
6c7b31dbb7
@@ -937,6 +937,9 @@ Interactive menu for downloading and managing wordlists from Weakpass.com via Bi
|
||||
-------------------------------------------------------------------
|
||||
### Version History
|
||||
|
||||
Version 2.10.7
|
||||
- Auto-upgrade (`hate_crack --update` / the in-menu upgrade) now survives the historical `master` → `main` default-branch rename. Old clones made before the rename sit on a local `master` whose upstream (`branch.master.merge`) still points at the now-deleted `refs/heads/master`, so a bare `git pull` failed with "Your configuration specifies to merge with the ref 'refs/heads/master' from the remote, but no such ref was fetched" — and `_run_upgrade()`'s `git checkout main` also failed on stale clones that had never fetched `origin/main`. The updater now fetches from `origin` *before* switching branches, checks out `main` via `git checkout -B main origin/main` (creating/resetting it from the remote regardless of local state), repairs the upstream with `git branch --set-upstream-to=origin/main main` so future manual `git pull`s work too, and pulls explicitly with `git pull origin main` so it never consults the dangling `branch.*.merge` config. Existing safety guards (dirty-branch bail, detached-HEAD skip) are unchanged
|
||||
|
||||
Version 2.10.6
|
||||
- Fixed the Hashview integration calling API routes that don't exist in Hashview (verified against v0.8.3-dev), which 404'd as soon as a customer ID was entered ("Could not list hashfiles"). The customer→hashfile listing relied on a phantom `GET /v1/hashfiles` list-all route; it now enumerates via the real `GET /v1/hashfiles/hash_type/<type>` endpoint where available — the download flow sweeps common hashcat modes to display a customer's uploaded hashfiles. That listing route only exists on Hashview builds from 2026-06-08+ (the `v0.8.3-dev` branch); on `main`/older servers there is no hashfile-listing API at all, so the flow now degrades gracefully to entering the hashfile ID directly (looked up in the Hashview web UI) and resolving its type via `GET /v1/getHashType/<id>`. Additional client-side route fixes: hashfile hash-type lookup now uses `GET /v1/getHashType/<id>`; "left" (uncracked) hash download uses `GET /v1/hashfiles/<id>`; `delete_job` uses `DELETE /v1/jobs/<id>`; `start_job` uses `POST`. Hashview exposes no stop-job route, so `stop_job` now raises with guidance to use `delete_job`; and no bulk cracked-hash export exists, so the best-effort "found" merge degrades gracefully
|
||||
- The Hashview CLI now honours `HASHVIEW_URL` / `HASHVIEW_API_KEY` environment variables as overrides for the `config.json` values, so the client can be pointed at a different Hashview instance (e.g. a local dev stack) without editing the persisted config
|
||||
|
||||
+41
-4
@@ -986,10 +986,33 @@ def _run_upgrade():
|
||||
raise SystemExit(1)
|
||||
repo_root = git_root_result.stdout.strip()
|
||||
|
||||
# Fetch first so origin/main is present even on a stale clone that has
|
||||
# never been fetched since the default branch was renamed master -> main.
|
||||
# Without this, `git checkout main` on a master-only clone fails because
|
||||
# there's no origin/main ref to auto-create a tracking branch from.
|
||||
fetch_result = subprocess.run(
|
||||
["git", "fetch", "--tags", "origin"],
|
||||
cwd=repo_root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if fetch_result.returncode != 0:
|
||||
print(
|
||||
f"\n Failed to fetch from origin:\n {fetch_result.stderr.strip()}\n"
|
||||
"\n Upgrade manually: git fetch --tags && git checkout main && git pull origin main && make install\n"
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
# Release tags live on main-side merge commits, so pulling on `dev` or
|
||||
# any feature branch won't move HEAD onto the new tag — setuptools-scm
|
||||
# then regenerates the version as e.g. 2.10.0.postN.devM and the update
|
||||
# checker re-fires on next start, looping forever. Switch to main first.
|
||||
#
|
||||
# Old clones made before the default branch was renamed master -> main
|
||||
# sit on a local `master` whose upstream (branch.master.merge) still
|
||||
# points at the now-deleted refs/heads/master. A bare `git pull` then
|
||||
# fails with "no such ref was fetched". We migrate such clones to a
|
||||
# local `main` tracking origin/main so future manual pulls also work.
|
||||
branch_result = subprocess.run(
|
||||
["git", "symbolic-ref", "--short", "HEAD"],
|
||||
cwd=repo_root,
|
||||
@@ -1009,13 +1032,16 @@ def _run_upgrade():
|
||||
print(
|
||||
f"\n Cannot auto-upgrade: uncommitted changes on '{branch}'."
|
||||
"\n Commit or stash them, then re-run."
|
||||
"\n Or upgrade manually: git checkout main && git pull && make install\n"
|
||||
"\n Or upgrade manually: git checkout main && git pull origin main && make install\n"
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
print(f"\n Switching from '{branch}' to 'main' to pick up the release tag...")
|
||||
checkout = subprocess.run(
|
||||
["git", "checkout", "main"],
|
||||
# -B creates/resets a local `main` pointing at origin/main so this
|
||||
# works whether or not a local `main` already exists (e.g. a stale
|
||||
# master-only clone that has never had a main branch).
|
||||
["git", "checkout", "-B", "main", "origin/main"],
|
||||
cwd=repo_root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
@@ -1023,20 +1049,31 @@ def _run_upgrade():
|
||||
if checkout.returncode != 0:
|
||||
print(
|
||||
f"\n Failed to switch to main:\n {checkout.stderr.strip()}\n"
|
||||
"\n Upgrade manually: git checkout main && git pull && make install\n"
|
||||
"\n Upgrade manually: git checkout main && git pull origin main && make install\n"
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
# Repair the upstream so a later manual `git pull` consults
|
||||
# origin/main rather than a dangling branch.master.merge ref.
|
||||
subprocess.run(
|
||||
["git", "branch", "--set-upstream-to=origin/main", "main"],
|
||||
cwd=repo_root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
import shutil
|
||||
|
||||
uv = shutil.which("uv") or os.path.expanduser("~/.local/bin/uv")
|
||||
|
||||
result = subprocess.run(
|
||||
# `git pull origin main` is explicit so it never consults the possibly
|
||||
# broken branch.<current>.merge config on a renamed clone.
|
||||
# git fetch --tags ensures new release tags are visible to setuptools-scm.
|
||||
# make install handles system deps and the CLI shim.
|
||||
# uv sync --reinstall-package forces setuptools-scm to regenerate the
|
||||
# version from the new tag so the version number updates correctly.
|
||||
f"git pull && git fetch --tags && make install && {uv} sync --reinstall-package hate_crack",
|
||||
f"git pull origin main && git fetch --tags && make install && {uv} sync --reinstall-package hate_crack",
|
||||
shell=True,
|
||||
cwd=repo_root,
|
||||
)
|
||||
|
||||
+139
-29
@@ -143,6 +143,9 @@ class TestCheckForUpdates:
|
||||
git_root_proc.returncode = 0
|
||||
git_root_proc.stdout = "/fake/repo\n"
|
||||
|
||||
fetch_proc = MagicMock()
|
||||
fetch_proc.returncode = 0
|
||||
|
||||
branch_proc = MagicMock()
|
||||
branch_proc.returncode = 0
|
||||
branch_proc.stdout = "main\n"
|
||||
@@ -153,16 +156,17 @@ class TestCheckForUpdates:
|
||||
with patch.object(hc_module, "requests") as mock_requests, patch.object(
|
||||
hc_module, "REQUESTS_AVAILABLE", True
|
||||
), patch("builtins.input", return_value="y"), patch(
|
||||
"subprocess.run", side_effect=[git_root_proc, branch_proc, make_proc]
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, make_proc],
|
||||
) as mock_run, pytest.raises(SystemExit):
|
||||
mock_requests.get.return_value = mock_resp
|
||||
hc_module.check_for_updates()
|
||||
|
||||
assert mock_run.call_count == 3
|
||||
make_cmd = mock_run.call_args_list[2][0][0]
|
||||
assert "git pull" in make_cmd
|
||||
assert mock_run.call_count == 4
|
||||
make_cmd = mock_run.call_args_list[3][0][0]
|
||||
assert "git pull origin main" in make_cmd
|
||||
assert "make install" in make_cmd
|
||||
assert mock_run.call_args_list[2][1]["cwd"] == "/fake/repo"
|
||||
assert mock_run.call_args_list[3][1]["cwd"] == "/fake/repo"
|
||||
output = capsys.readouterr().out
|
||||
assert "Upgrade complete" in output
|
||||
|
||||
@@ -175,6 +179,9 @@ class TestCheckForUpdates:
|
||||
git_root_proc.returncode = 0
|
||||
git_root_proc.stdout = "/fake/repo\n"
|
||||
|
||||
fetch_proc = MagicMock()
|
||||
fetch_proc.returncode = 0
|
||||
|
||||
branch_proc = MagicMock()
|
||||
branch_proc.returncode = 0
|
||||
branch_proc.stdout = "main\n"
|
||||
@@ -185,7 +192,8 @@ class TestCheckForUpdates:
|
||||
with patch.object(hc_module, "requests") as mock_requests, patch.object(
|
||||
hc_module, "REQUESTS_AVAILABLE", True
|
||||
), patch("builtins.input", return_value="y"), patch(
|
||||
"subprocess.run", side_effect=[git_root_proc, branch_proc, make_proc]
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, make_proc],
|
||||
), patch("shutil.which", return_value="/usr/local/bin/uv"), patch(
|
||||
"os.path.isfile", return_value=True
|
||||
), pytest.raises(
|
||||
@@ -228,6 +236,9 @@ class TestRunUpgrade:
|
||||
git_root_proc.returncode = 0
|
||||
git_root_proc.stdout = "/fake/repo\n"
|
||||
|
||||
fetch_proc = MagicMock()
|
||||
fetch_proc.returncode = 0
|
||||
|
||||
branch_proc = MagicMock()
|
||||
branch_proc.returncode = 0
|
||||
branch_proc.stdout = "main\n"
|
||||
@@ -236,16 +247,19 @@ class TestRunUpgrade:
|
||||
make_proc.returncode = 0
|
||||
|
||||
with patch(
|
||||
"subprocess.run", side_effect=[git_root_proc, branch_proc, make_proc]
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, make_proc],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 0
|
||||
assert mock_run.call_count == 3
|
||||
make_cmd = mock_run.call_args_list[2][0][0]
|
||||
assert "git pull" in make_cmd
|
||||
assert mock_run.call_count == 4
|
||||
# The fetch happens before the branch inspection.
|
||||
assert mock_run.call_args_list[1][0][0] == ["git", "fetch", "--tags", "origin"]
|
||||
make_cmd = mock_run.call_args_list[3][0][0]
|
||||
assert "git pull origin main" in make_cmd
|
||||
assert "make install" in make_cmd
|
||||
assert mock_run.call_args_list[2][1]["cwd"] == "/fake/repo"
|
||||
assert mock_run.call_args_list[3][1]["cwd"] == "/fake/repo"
|
||||
output = capsys.readouterr().out
|
||||
assert "Upgrade complete" in output
|
||||
|
||||
@@ -254,6 +268,9 @@ class TestRunUpgrade:
|
||||
git_root_proc.returncode = 0
|
||||
git_root_proc.stdout = "/fake/repo\n"
|
||||
|
||||
fetch_proc = MagicMock()
|
||||
fetch_proc.returncode = 0
|
||||
|
||||
branch_proc = MagicMock()
|
||||
branch_proc.returncode = 0
|
||||
branch_proc.stdout = "main\n"
|
||||
@@ -262,7 +279,8 @@ class TestRunUpgrade:
|
||||
make_proc.returncode = 1
|
||||
|
||||
with patch(
|
||||
"subprocess.run", side_effect=[git_root_proc, branch_proc, make_proc]
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, make_proc],
|
||||
), pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
@@ -308,59 +326,143 @@ class TestRunUpgrade:
|
||||
self, hc_module, capsys
|
||||
):
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
branch_proc = MagicMock(returncode=0, stdout="dev\n")
|
||||
status_proc = MagicMock(returncode=0, stdout="")
|
||||
checkout_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
upstream_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
make_proc = MagicMock(returncode=0)
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[
|
||||
git_root_proc,
|
||||
fetch_proc,
|
||||
branch_proc,
|
||||
status_proc,
|
||||
checkout_proc,
|
||||
upstream_proc,
|
||||
make_proc,
|
||||
],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 0
|
||||
assert mock_run.call_count == 5
|
||||
# Third call is the checkout to main.
|
||||
checkout_call = mock_run.call_args_list[3]
|
||||
assert checkout_call[0][0] == ["git", "checkout", "main"]
|
||||
assert mock_run.call_count == 7
|
||||
# Fetch happens before the checkout.
|
||||
assert mock_run.call_args_list[1][0][0] == ["git", "fetch", "--tags", "origin"]
|
||||
# The checkout creates/resets main from origin/main.
|
||||
checkout_call = mock_run.call_args_list[4]
|
||||
assert checkout_call[0][0] == ["git", "checkout", "-B", "main", "origin/main"]
|
||||
# Upstream is repaired to origin/main.
|
||||
assert mock_run.call_args_list[5][0][0] == [
|
||||
"git",
|
||||
"branch",
|
||||
"--set-upstream-to=origin/main",
|
||||
"main",
|
||||
]
|
||||
# Final call is the shell upgrade.
|
||||
upgrade_cmd = mock_run.call_args_list[4][0][0]
|
||||
assert "git pull" in upgrade_cmd
|
||||
upgrade_cmd = mock_run.call_args_list[6][0][0]
|
||||
assert "git pull origin main" in upgrade_cmd
|
||||
assert "git fetch --tags" in upgrade_cmd
|
||||
output = capsys.readouterr().out
|
||||
assert "Switching from 'dev' to 'main'" in output
|
||||
|
||||
def test_run_upgrade_migrates_master_only_renamed_clone(
|
||||
self, hc_module, capsys
|
||||
):
|
||||
"""An old clone still sitting on `master` (default branch renamed
|
||||
master -> main upstream) must be migrated: fetch BEFORE checkout,
|
||||
checkout/create `main` from origin/main, set upstream to origin/main,
|
||||
and the final pull must be `git pull origin main` (never bare)."""
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
branch_proc = MagicMock(returncode=0, stdout="master\n")
|
||||
status_proc = MagicMock(returncode=0, stdout="")
|
||||
checkout_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
upstream_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
make_proc = MagicMock(returncode=0)
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[
|
||||
git_root_proc,
|
||||
fetch_proc,
|
||||
branch_proc,
|
||||
status_proc,
|
||||
checkout_proc,
|
||||
upstream_proc,
|
||||
make_proc,
|
||||
],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 0
|
||||
calls = [c[0][0] for c in mock_run.call_args_list]
|
||||
|
||||
# A fetch must occur before any checkout.
|
||||
fetch_idx = calls.index(["git", "fetch", "--tags", "origin"])
|
||||
checkout_idx = calls.index(["git", "checkout", "-B", "main", "origin/main"])
|
||||
assert fetch_idx < checkout_idx
|
||||
|
||||
# Upstream repaired to origin/main after the checkout.
|
||||
upstream_idx = calls.index(
|
||||
["git", "branch", "--set-upstream-to=origin/main", "main"]
|
||||
)
|
||||
assert checkout_idx < upstream_idx
|
||||
|
||||
# Final pull is explicit, not a bare `git pull`.
|
||||
upgrade_cmd = mock_run.call_args_list[-1][0][0]
|
||||
assert "git pull origin main" in upgrade_cmd
|
||||
assert "make install" in upgrade_cmd
|
||||
|
||||
output = capsys.readouterr().out
|
||||
assert "Switching from 'master' to 'main'" in output
|
||||
|
||||
def test_run_upgrade_bails_when_fetch_fails(self, hc_module, capsys):
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(
|
||||
returncode=1, stdout="", stderr="fatal: unable to access origin\n"
|
||||
)
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, fetch_proc],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 1
|
||||
# No branch inspection / checkout after the fetch failure.
|
||||
assert mock_run.call_count == 2
|
||||
output = capsys.readouterr().out
|
||||
assert "Failed to fetch from origin" in output
|
||||
|
||||
def test_run_upgrade_bails_when_non_main_branch_is_dirty(
|
||||
self, hc_module, capsys
|
||||
):
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
branch_proc = MagicMock(returncode=0, stdout="feat/x\n")
|
||||
status_proc = MagicMock(returncode=0, stdout=" M hate_crack/main.py\n")
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, branch_proc, status_proc],
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, status_proc],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 1
|
||||
# No checkout, no upgrade command should fire after the bail.
|
||||
assert mock_run.call_count == 3
|
||||
assert mock_run.call_count == 4
|
||||
all_call_args = [c[0][0] for c in mock_run.call_args_list]
|
||||
assert ["git", "checkout", "main"] not in all_call_args
|
||||
assert ["git", "checkout", "-B", "main", "origin/main"] not in all_call_args
|
||||
output = capsys.readouterr().out
|
||||
assert "uncommitted changes" in output
|
||||
assert "feat/x" in output
|
||||
|
||||
def test_run_upgrade_bails_when_checkout_main_fails(self, hc_module, capsys):
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
branch_proc = MagicMock(returncode=0, stdout="dev\n")
|
||||
status_proc = MagicMock(returncode=0, stdout="")
|
||||
checkout_proc = MagicMock(
|
||||
@@ -371,7 +473,13 @@ class TestRunUpgrade:
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, branch_proc, status_proc, checkout_proc],
|
||||
side_effect=[
|
||||
git_root_proc,
|
||||
fetch_proc,
|
||||
branch_proc,
|
||||
status_proc,
|
||||
checkout_proc,
|
||||
],
|
||||
), pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
@@ -384,20 +492,21 @@ class TestRunUpgrade:
|
||||
self, hc_module, capsys
|
||||
):
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
branch_proc = MagicMock(returncode=0, stdout="main\n")
|
||||
make_proc = MagicMock(returncode=0)
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, branch_proc, make_proc],
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, make_proc],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 0
|
||||
# Only rev-parse + symbolic-ref + upgrade shell. No checkout.
|
||||
assert mock_run.call_count == 3
|
||||
# rev-parse + fetch + symbolic-ref + upgrade shell. No checkout.
|
||||
assert mock_run.call_count == 4
|
||||
all_call_args = [c[0][0] for c in mock_run.call_args_list]
|
||||
assert ["git", "checkout", "main"] not in all_call_args
|
||||
assert ["git", "checkout", "-B", "main", "origin/main"] not in all_call_args
|
||||
output = capsys.readouterr().out
|
||||
assert "Switching from" not in output
|
||||
|
||||
@@ -406,16 +515,17 @@ class TestRunUpgrade:
|
||||
attempt a branch switch (there's nothing to switch from) — let
|
||||
the existing upgrade flow proceed."""
|
||||
git_root_proc = MagicMock(returncode=0, stdout="/fake/repo\n")
|
||||
fetch_proc = MagicMock(returncode=0, stdout="", stderr="")
|
||||
branch_proc = MagicMock(returncode=1, stdout="", stderr="fatal: ref HEAD is not a symbolic ref\n")
|
||||
make_proc = MagicMock(returncode=0)
|
||||
|
||||
with patch(
|
||||
"subprocess.run",
|
||||
side_effect=[git_root_proc, branch_proc, make_proc],
|
||||
side_effect=[git_root_proc, fetch_proc, branch_proc, make_proc],
|
||||
) as mock_run, pytest.raises(SystemExit) as exc:
|
||||
hc_module._run_upgrade()
|
||||
|
||||
assert exc.value.code == 0
|
||||
assert mock_run.call_count == 3
|
||||
assert mock_run.call_count == 4
|
||||
all_call_args = [c[0][0] for c in mock_run.call_args_list]
|
||||
assert ["git", "checkout", "main"] not in all_call_args
|
||||
assert ["git", "checkout", "-B", "main", "origin/main"] not in all_call_args
|
||||
|
||||
Reference in New Issue
Block a user