Compare commits

..

No commits in common. "master" and "v2.2.1" have entirely different histories.

1539 changed files with 47727 additions and 82162 deletions

View file

@ -1,7 +0,0 @@
9766c534bddad8e82e6d19f9bad5cf70b9887f9a
92ce77ec0ec703c08a659419087a373f76e711f7
2d53efc945c7747be1755d0b66557a86bdc12cbd
602137b65129b817811b80975a369ebde3270c6d
4eb26ae37e1f4c82a45961517ffeb54c20200408
e59adce848a9e10ee5775254045cbbd915236b8b
9e0a64108d62236ab07b3f8d10e8c78269b8e1d1

View file

@ -15,5 +15,5 @@ indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,tpl}]
[*.php]
indent_size = 4

View file

@ -1,11 +1,10 @@
# Common params
TP_HOST=example.com
TP_PORT=80
APP_ENV=production
APP_CRON_ENABLED=true
APP_DEMO_MODE=false
APP_NAME=TorrentPier
APP_ENV=local
APP_DEBUG=false
# Database credentials
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=torrentpier

4
.github/FUNDING.yml vendored
View file

@ -1,4 +0,0 @@
# These are supported funding model platforms
github: torrentpier
open_collective: torrentpier

View file

@ -1,62 +0,0 @@
name: Bug Report
description: File a bug report
title: "[Bug]"
labels: [Bug]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
The more detailed this bug report is, the faster it can be reviewed and fixed.
- type: input
id: version-torrentpier
attributes:
label: TorrentPier Version
description: TorrentPier version your using?
placeholder: 2.4.0
validations:
required: true
- type: input
id: version-php-os
attributes:
label: PHP & Platform
description: Exact PHP and Platform (OS) versions your using.
placeholder: 8.2.2 - Ubuntu 22.04 x64
validations:
required: true
- type: checkboxes
id: requirements
attributes:
label: Have you done this?
options:
- label: I am willing to share my stack trace and logs
required: true
- label: I can suggest a fix as a Pull Request
required: false
- type: textarea
id: expectation
attributes:
label: Expectation
description: Write what you expect to (correctly) happen.
placeholder: When I do this, I expect to this to happen.
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Write what (incorrectly) happens instead.
placeholder: Instead, when I do this, I receive that.
validations:
required: true
- type: textarea
id: logs
attributes:
label: Stack trace & logs
description: |
If you have a stack trace, you can copy it here. You may hide sensitive information.
Including a stack trace when reporting an error 500 is required.
placeholder: This is automatically formatted into code, no need for backticks.
render: shell
validations:
required: false

View file

@ -1,7 +0,0 @@
---
name: Feature / Enhancement request
about: Suggest an idea for TorrentPier
title: "[Feature]"
labels: [Feature, Enhancement]
assignees: ''
---

View file

@ -1,16 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "composer"
directory: "/"
versioning-strategy: increase-if-necessary
commit-message:
prefix: "Composer"
include: "scope"
schedule:
interval: "daily"

View file

@ -1,80 +0,0 @@
name: Continuous Deployment
on:
push:
tags:
- "v*.*.*"
jobs:
generate-changelog:
name: Generate changelog
runs-on: ubuntu-22.04
outputs:
release_body: ${{ steps.git-cliff.outputs.content }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate a changelog
uses: orhun/git-cliff-action@v4
id: git-cliff
with:
config: cliff.toml
args: -vv --latest --no-exec --github-repo ${{ github.repository }}
- name: Print the changelog
run: cat "${{ steps.git-cliff.outputs.changelog }}"
release:
name: Create release
needs: [ generate-changelog ]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set the release version
shell: bash
run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Install Composer dependencies
run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader
- name: Cleanup
run: php _cleanup.php && rm _cleanup.php
- name: Create archive
id: create-zip
run: |
ZIP_NAME="torrentpier-v${{ env.RELEASE_VERSION }}.zip"
zip -r "$ZIP_NAME" . -x ".git/*"
echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT
- name: Publish to GitHub
if: ${{ !contains(github.ref, '-') }}
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.create-zip.outputs.ZIP_NAME }}
overwrite: true
tag: ${{ github.ref }}
release_name: "v${{ env.RELEASE_VERSION }}"
body: "${{ needs.generate-changelog.outputs.release_body }}"
- name: Publish to GitHub (pre-release)
if: ${{ contains(github.ref, '-') }}
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.create-zip.outputs.ZIP_NAME }}
overwrite: true
tag: ${{ github.ref }}
release_name: "v${{ env.RELEASE_VERSION }}"
body: "${{ needs.generate-changelog.outputs.release_body }}"
prerelease: true

View file

@ -1,45 +0,0 @@
name: Continuous Integration
on:
push:
branches:
- master
jobs:
nightly:
name: Nightly builds 📦
runs-on: ubuntu-22.04
steps:
- name: Checkout code 🗳
uses: actions/checkout@v4
- name: Setup PHP 🔩
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Install Composer dependencies 🪚
run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader
- name: Get commit hash 🔗
id: get-commit-hash
run: |
COMMIT_HASH=$(git rev-parse --short HEAD)
echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_OUTPUT
- name: Cleanup
run: php _cleanup.php && rm _cleanup.php
- name: Create archive 🗞
id: create-zip
run: |
ZIP_NAME="torrentpier-${{ steps.get-commit-hash.outputs.COMMIT_HASH }}.zip"
zip -r "$ZIP_NAME" . -x ".git/*"
echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT
- name: Upload Archive 📤
uses: actions/upload-artifact@v4
with:
name: TorrentPier-master
path: ${{ steps.create-zip.outputs.ZIP_NAME }}

View file

@ -1,57 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# PHPMD is a spin-off project of PHP Depend and
# aims to be a PHP equivalent of the well known Java tool PMD.
# What PHPMD does is: It takes a given PHP source code base
# and look for several potential problems within that source.
# These problems can be things like:
# Possible bugs
# Suboptimal code
# Overcomplicated expressions
# Unused parameters, methods, properties
# More details at https://phpmd.org/
name: PHPMD
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '40 0 * * 3'
permissions:
contents: read
jobs:
PHPMD:
name: Run PHPMD scanning
runs-on: ubuntu-latest
permissions:
contents: read # for checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@aa1fe473f9c687b6fb896056d771232c0bc41161
with:
coverage: none
tools: phpmd
- name: Run PHPMD
run: phpmd . sarif codesize --reportfile phpmd-results.sarif
continue-on-error: true
- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: phpmd-results.sarif
wait-for-processing: true

View file

@ -1,41 +0,0 @@
name: Changelog generation
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
changelog:
name: Changelog generation
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: master
token: ${{ secrets.REPO_TOKEN }}
- name: Generate a changelog
uses: orhun/git-cliff-action@v4
id: git-cliff
with:
config: cliff.toml
args: v2.4.6-alpha.4.. --verbose
env:
OUTPUT: CHANGELOG.md
GITHUB_REPO: ${{ github.repository }}
- name: Print the changelog
run: cat "${{ steps.git-cliff.outputs.changelog }}"
- name: Commit changelog
run: |
git checkout master
git config --local user.name 'belomaxorka'
git config --local user.email 'roman25052006.kelesh@gmail.com'
set +e
git add CHANGELOG.md
git commit -m "changelog: Update CHANGELOG.md 📖"
git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git master

28
.gitignore vendored
View file

@ -1,40 +1,36 @@
### IDE ###
.idea
.vscode
### TorrentPier ###
*.log
install.php_*
composer-setup.php
.env
.idea
.php_cs.cache
bower_components
composer.phar
composer.lock
configs/local.php
data/avatars
data/uploads
data/torrent_files
internal_data/ajax_html
internal_data/atom
internal_data/cache
internal_data/log
internal_data/updater.json
sitemap
internal_data/sitemap
internal_data/triggers
library/config.local.php
node_modules
vendor
yarn.lock
### Archives ###
*.phar
*.log
*.zip
*.rar
*.tar
*.gz
*.zip
*.7z
*.torrent
*.pak
### Windows ###
Thumbs.db
Desktop.ini
$RECYCLE.BIN/
*.lnk
*.bat
### OSX ###
.DS_Store

View file

@ -14,5 +14,5 @@ RedirectMatch 404 /\\.git(/|$)
## deny access to system files
<FilesMatch "\.(.*sql|tpl|db|inc|log|md|env)|(config|common).php$">
Require all denied
deny from all
</FilesMatch>

View file

@ -1,113 +1,313 @@
[![TorrentPier](https://raw.githubusercontent.com/torrentpier/.github/refs/heads/main/versions/Cattle.png)](https://github.com/torrentpier)
# Change Log
# 📖 Change Log
## [v2.2.1](https://github.com/torrentpier/torrentpier/tree/v2.2.1) (2017-06-16)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.2.0...v2.2.1)
## [v2.8.3](https://github.com/torrentpier/torrentpier/compare/v2.8.2..v2.8.3) (2025-07-03)
**Merged pull requests:**
### 🚀 Features
- Partial renewal of the Ukrainian language from our toloka.to friends [\#391](https://github.com/torrentpier/torrentpier/pull/391) ([Exile37](https://github.com/Exile37))
- Create CODE\_OF\_CONDUCT.md [\#390](https://github.com/torrentpier/torrentpier/pull/390) ([Exile37](https://github.com/Exile37))
- Fix default users language in dump [\#389](https://github.com/torrentpier/torrentpier/pull/389) ([Exile37](https://github.com/Exile37))
- Tracker search forum list simplification [\#388](https://github.com/torrentpier/torrentpier/pull/388) ([Exile37](https://github.com/Exile37))
- Fix some notices in admin panel reported by BugSnag [\#387](https://github.com/torrentpier/torrentpier/pull/387) ([Exile37](https://github.com/Exile37))
- Fixed SQL. Remove limit from update [\#368](https://github.com/torrentpier/torrentpier/pull/368) ([VasyOk](https://github.com/VasyOk))
- *(lang)* Added `RTL` languages support ([#2031](https://github.com/torrentpier/torrentpier/pull/2031)) - ([fd46d3d](https://github.com/torrentpier/torrentpier/commit/fd46d3d04ad3ab1453256b2ab620508e2ba33586))
- *(updater)* Added exceptions logging ([#2026](https://github.com/torrentpier/torrentpier/pull/2026)) - ([51f2c70](https://github.com/torrentpier/torrentpier/commit/51f2c70d81b910012cdecd111b5b92c1dfd0d6f6))
## [v2.2.0](https://github.com/torrentpier/torrentpier/tree/v2.2.0) (2017-06-12)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.1.5...v2.2.0)
### 🚜 Refactor
- *(TorrentFileList)* Reduce duplication in root directory unset logic ([#2027](https://github.com/torrentpier/torrentpier/pull/2027)) - ([d4d8210](https://github.com/torrentpier/torrentpier/commit/d4d82101dd67c9f4cd86e0f6f909495696974354))
## [v2.8.2](https://github.com/torrentpier/torrentpier/compare/v2.8.1..v2.8.2) (2025-06-30)
### 🐛 Bug Fixes
- *(TorrentFileList)* Avoid `array_merge` reindexing for numeric folder names ([#2014](https://github.com/torrentpier/torrentpier/pull/2014)) - ([915e1d8](https://github.com/torrentpier/torrentpier/commit/915e1d817c61d2a4f0691b24ec1bc6577a9cd44b))
### 🚜 Refactor
- Use `DEFAULT_CHARSET` constant instead of hardcoded string ([#2011](https://github.com/torrentpier/torrentpier/pull/2011)) - ([7ac3359](https://github.com/torrentpier/torrentpier/commit/7ac335974baa44a8575bebb71ae2fbc0902d10e7))
## [v2.8.1](https://github.com/torrentpier/torrentpier/compare/v2.8.0..v2.8.1) (2025-06-24)
### 🐛 Bug Fixes
- *(filelist)* `Undefined property: FileTree::$length` when v2 torrent only ([#2004](https://github.com/torrentpier/torrentpier/pull/2004)) - ([7f4cc9d](https://github.com/torrentpier/torrentpier/commit/7f4cc9d3b9a5b87100f710cc60f636d6e7d5a34e))
- *(ip-api)* Add error handling and logging for freeipapi.com requests ([#2006](https://github.com/torrentpier/torrentpier/pull/2006)) - ([f1d6e74](https://github.com/torrentpier/torrentpier/commit/f1d6e74e5d4c74b6e12e9e742f60f62e71783d11))
## [v2.8.0](https://github.com/torrentpier/torrentpier/compare/v2.7.0..v2.8.0) (2025-06-21)
### 🐛 Bug Fixes
- *(template)* Handle L_ variables in template vars when not found in lang vars ([#1998](https://github.com/torrentpier/torrentpier/pull/1998)) - ([c6076c2](https://github.com/torrentpier/torrentpier/commit/c6076c2c278e9a423f3862670236b75bddeadd87))
## [v2.7.0](https://github.com/torrentpier/torrentpier/compare/v2.6.0..v2.7.0) (2025-06-21)
### 🚀 Features
- *(database)* Add visual markers for Nette Explorer queries in debug panel ([#1965](https://github.com/torrentpier/torrentpier/pull/1965)) - ([2fd3067](https://github.com/torrentpier/torrentpier/commit/2fd306704f21febee7d53f4b4531601ce0cb81ce))
- *(language)* Add new language variable for migration file and enhance template fallback logic ([#1984](https://github.com/torrentpier/torrentpier/pull/1984)) - ([a33574c](https://github.com/torrentpier/torrentpier/commit/a33574c28f2eb6267a74fa6c9d97fea86527157a))
- *(migrations)* Implement Phinx database migration system ([#1976](https://github.com/torrentpier/torrentpier/pull/1976)) - ([fbde8cd](https://github.com/torrentpier/torrentpier/commit/fbde8cd421c9048afe70ddb41d0a9ed26d3fbef5))
- *(test)* [**breaking**] Add comprehensive testing infrastructure with Pest PHP ([#1979](https://github.com/torrentpier/torrentpier/pull/1979)) - ([cc9d412](https://github.com/torrentpier/torrentpier/commit/cc9d412522938a023bd2b8eb880c4d2dd307c82a))
- [**breaking**] Implement Language singleton with shorthand functions ([#1966](https://github.com/torrentpier/torrentpier/pull/1966)) - ([49717d3](https://github.com/torrentpier/torrentpier/commit/49717d3a687b95885fe9773f2597354aed4b2b60))
### 🐛 Bug Fixes
- *(database)* Update affected rows tracking in Database class ([#1980](https://github.com/torrentpier/torrentpier/pull/1980)) - ([4f9cc9f](https://github.com/torrentpier/torrentpier/commit/4f9cc9fe0f7f4a85c90001a3f5514efdf04836da))
### 🚜 Refactor
- *(database)* Enhance error logging and various fixes ([#1978](https://github.com/torrentpier/torrentpier/pull/1978)) - ([7aed6bc](https://github.com/torrentpier/torrentpier/commit/7aed6bc7d89f4ed31e7ed6c6eeecc6e08d348c24))
- *(database)* Rename DB to Database and extract debug functionality ([#1964](https://github.com/torrentpier/torrentpier/pull/1964)) - ([6c0219d](https://github.com/torrentpier/torrentpier/commit/6c0219d53c7544b7d8a6374c0d0848945d32ae17))
- *(stats)* Improve database row fetching in tr_stats.php ([#1985](https://github.com/torrentpier/torrentpier/pull/1985)) - ([728116d](https://github.com/torrentpier/torrentpier/commit/728116d6dc9cf4476cce572ced5e8a7ef529ead8))
### ⚙️ Miscellaneous
- Update minimum `PHP` requirement to `8.2` ([#1987](https://github.com/torrentpier/torrentpier/pull/1987)) - ([9b322c7](https://github.com/torrentpier/torrentpier/commit/9b322c7093a634669e9f17a32ac42500f44f2496))
- Removed useless `composer update` from workflows & installer ([#1986](https://github.com/torrentpier/torrentpier/pull/1986)) - ([423424e](https://github.com/torrentpier/torrentpier/commit/423424e9478e0772957014fb30f5e84158067af7))
- Added --no-dev composer flag for some workflows ([#1982](https://github.com/torrentpier/torrentpier/pull/1982)) - ([e9a9e09](https://github.com/torrentpier/torrentpier/commit/e9a9e095768ba68aa5d5058a3e152ffaec916117))
- Added `--no-dev` composer flag for some workflows ([#1981](https://github.com/torrentpier/torrentpier/pull/1981)) - ([e8cba5d](https://github.com/torrentpier/torrentpier/commit/e8cba5dd3fc83b616f83c24991f79dc7258c5df3))
## [v2.6.0](https://github.com/torrentpier/torrentpier/compare/v2.5.0..v2.6.0) (2025-06-18)
### 🚀 Features
- [**breaking**] Implement unified cache system with Nette Caching ([#1963](https://github.com/torrentpier/torrentpier/pull/1963)) - ([07a06a3](https://github.com/torrentpier/torrentpier/commit/07a06a33cd97b37f68b533a87cdb5f7578f2c86f))
- Replace legacy database layer with Nette Database implementation ([#1961](https://github.com/torrentpier/torrentpier/pull/1961)) - ([f50b914](https://github.com/torrentpier/torrentpier/commit/f50b914cc18f777d92002baf2c812a635d5eed4b))
### 🐛 Bug Fixes
- *(User)* Add null and array checks before session data operations ([#1962](https://github.com/torrentpier/torrentpier/pull/1962)) - ([e458109](https://github.com/torrentpier/torrentpier/commit/e458109eefc54d86a78a1ddb3954581524852516))
## [v2.5.0](https://github.com/torrentpier/torrentpier/compare/v2.4.6-alpha.4..v2.5.0) (2025-06-18)
### 🚀 Features
- [**breaking**] Implement centralized Config class to replace global $bb_cfg array ([#1953](https://github.com/torrentpier/torrentpier/pull/1953)) - ([bf9100f](https://github.com/torrentpier/torrentpier/commit/bf9100fbfa74768edb01c62636198a44739d9923))
### 🐛 Bug Fixes
- *(installer)* Strip protocol from TP_HOST to keep only hostname ([#1952](https://github.com/torrentpier/torrentpier/pull/1952)) - ([81bf67c](https://github.com/torrentpier/torrentpier/commit/81bf67c2be85d49e988b7802ca7e9738ff580031))
- *(sql)* Resolve only_full_group_by compatibility issues in tracker cleanup ([#1951](https://github.com/torrentpier/torrentpier/pull/1951)) - ([37a0675](https://github.com/torrentpier/torrentpier/commit/37a0675adfb02014e7068f4aa82301e29f39eab6))
### 📦 Dependencies
- *(deps)* Bump filp/whoops from 2.18.2 to 2.18.3 ([#1948](https://github.com/torrentpier/torrentpier/pull/1948)) - ([b477680](https://github.com/torrentpier/torrentpier/commit/b4776804a408217229caa327c79849cf13ce2aa5))
### 🚜 Refactor
- *(censor)* [**breaking**] Migrate Censor class to singleton pattern ([#1954](https://github.com/torrentpier/torrentpier/pull/1954)) - ([74a564d](https://github.com/torrentpier/torrentpier/commit/74a564d7954c6f8745ebcffdcd9c8997e371d47a))
- *(config)* [**breaking**] Encapsulate global $bb_cfg array in Config class ([#1950](https://github.com/torrentpier/torrentpier/pull/1950)) - ([5842994](https://github.com/torrentpier/torrentpier/commit/5842994782dfa62788f8427c55045abdbfb5b8e9))
### 📚 Documentation
- Add Select class migration guide ([#1960](https://github.com/torrentpier/torrentpier/pull/1960)) - ([86abafb](https://github.com/torrentpier/torrentpier/commit/86abafb11469d14a746d12725b15cf6b7015ec44))
### ⚙️ Miscellaneous
- *(_release.php)* Finally! Removed some useless params ([#1947](https://github.com/torrentpier/torrentpier/pull/1947)) - ([9c7d270](https://github.com/torrentpier/torrentpier/commit/9c7d270598c0153fb82f4b7ad96f5b59399b2159))
- *(cliff)* Add conventional commit prefix to changelog message ([#1957](https://github.com/torrentpier/torrentpier/pull/1957)) - ([b1b2618](https://github.com/torrentpier/torrentpier/commit/b1b26187579f6981165d85c316a3c5b7199ce2ee))
**Merged pull requests:**
- Release 2.2.0 ☘️ [\#328](https://github.com/torrentpier/torrentpier/pull/328) ([Exile37](https://github.com/Exile37))
- Release preparation. Crowdin language pack update [\#322](https://github.com/torrentpier/torrentpier/pull/322) ([Exile37](https://github.com/Exile37))
- TorrentPier Aurochs release preparation [\#321](https://github.com/torrentpier/torrentpier/pull/321) ([Exile37](https://github.com/Exile37))
- Release preparation. Small bugfixes and readme translation [\#318](https://github.com/torrentpier/torrentpier/pull/318) ([Exile37](https://github.com/Exile37))
- Crowdin language pack update [\#314](https://github.com/torrentpier/torrentpier/pull/314) ([Exile37](https://github.com/Exile37))
- IP storage and attachment system bugfix. PHP 5.6+ [\#313](https://github.com/torrentpier/torrentpier/pull/313) ([Exile37](https://github.com/Exile37))
- Bootstrap update & beginning of the develop branch partial merge [\#303](https://github.com/torrentpier/torrentpier/pull/303) ([Exile37](https://github.com/Exile37))
- Fix avatars display bug [\#302](https://github.com/torrentpier/torrentpier/pull/302) ([Exile37](https://github.com/Exile37))
- Cron subsystem rework. Environments [\#301](https://github.com/torrentpier/torrentpier/pull/301) ([Exile37](https://github.com/Exile37))
- New logotype, favicon and css split & reformat [\#293](https://github.com/torrentpier/torrentpier/pull/293) ([Exile37](https://github.com/Exile37))
- Whoops error handler for debug users [\#291](https://github.com/torrentpier/torrentpier/pull/291) ([Exile37](https://github.com/Exile37))
- Replace sitemap to the new external component [\#252](https://github.com/torrentpier/torrentpier/pull/252) ([Exile37](https://github.com/Exile37))
- Crowdin language pack update. Removed some languages [\#250](https://github.com/torrentpier/torrentpier/pull/250) ([Exile37](https://github.com/Exile37))
- IP detect subsystem replace. Trash cleanup. Defines [\#249](https://github.com/torrentpier/torrentpier/pull/249) ([Exile37](https://github.com/Exile37))
- Old ads module removal [\#244](https://github.com/torrentpier/torrentpier/pull/244) ([Exile37](https://github.com/Exile37))
- External bencode library and some other changes [\#243](https://github.com/torrentpier/torrentpier/pull/243) ([Exile37](https://github.com/Exile37))
- Added new logo to readme [\#242](https://github.com/torrentpier/torrentpier/pull/242) ([VasyOk](https://github.com/VasyOk))
- Bugsnag integration and some bugfixes in for cycles [\#239](https://github.com/torrentpier/torrentpier/pull/239) ([Exile37](https://github.com/Exile37))
- Bug with variables replacement and Crowdin localization fix [\#238](https://github.com/torrentpier/torrentpier/pull/238) ([Exile37](https://github.com/Exile37))
- PSR-4 compatible legacy code autoloading [\#237](https://github.com/torrentpier/torrentpier/pull/237) ([Exile37](https://github.com/Exile37))
- UFT-8 autocorrection removal from standart package [\#236](https://github.com/torrentpier/torrentpier/pull/236) ([Exile37](https://github.com/Exile37))
- New localization strings and full Crowdin language pack update [\#235](https://github.com/torrentpier/torrentpier/pull/235) ([Exile37](https://github.com/Exile37))
- Replace own emailer to SwiftMailer [\#234](https://github.com/torrentpier/torrentpier/pull/234) ([Exile37](https://github.com/Exile37))
- Force email charset and Crowdin language pack update [\#232](https://github.com/torrentpier/torrentpier/pull/232) ([Exile37](https://github.com/Exile37))
- Crowdin language pack update [\#231](https://github.com/torrentpier/torrentpier/pull/231) ([Exile37](https://github.com/Exile37))
- Static code analyzer inspection, part 2 [\#230](https://github.com/torrentpier/torrentpier/pull/230) ([Exile37](https://github.com/Exile37))
- Static code analyzer cherry picked from \#228 [\#229](https://github.com/torrentpier/torrentpier/pull/229) ([VasyOk](https://github.com/VasyOk))
- Fix compare php version. [\#226](https://github.com/torrentpier/torrentpier/pull/226) ([VasyOk](https://github.com/VasyOk))
- Fixed compare version PHP [\#225](https://github.com/torrentpier/torrentpier/pull/225) ([VasyOk](https://github.com/VasyOk))
- Deprecated each\(\) function in php 7.2 [\#211](https://github.com/torrentpier/torrentpier/pull/211) ([Exile37](https://github.com/Exile37))
- Performance refactoring. Remove test code. Fix path in config [\#208](https://github.com/torrentpier/torrentpier/pull/208) ([VasyOk](https://github.com/VasyOk))
- Fix many notices in admin\_attach\_cp.php [\#183](https://github.com/torrentpier/torrentpier/pull/183) ([Exile37](https://github.com/Exile37))
- Add check lang [\#178](https://github.com/torrentpier/torrentpier/pull/178) ([VasyOk](https://github.com/VasyOk))
- Remove order from sql [\#177](https://github.com/torrentpier/torrentpier/pull/177) ([VasyOk](https://github.com/VasyOk))
- Fix path to viewtorrent.php [\#176](https://github.com/torrentpier/torrentpier/pull/176) ([VasyOk](https://github.com/VasyOk))
- New Crowdin translations [\#168](https://github.com/torrentpier/torrentpier/pull/168) ([Exile37](https://github.com/Exile37))
- Localization trash cleanup [\#167](https://github.com/torrentpier/torrentpier/pull/167) ([Exile37](https://github.com/Exile37))
- New Crowdin translations \(develop\) [\#165](https://github.com/torrentpier/torrentpier/pull/165) ([Exile37](https://github.com/Exile37))
- New Crowdin translations \(master\) [\#164](https://github.com/torrentpier/torrentpier/pull/164) ([Exile37](https://github.com/Exile37))
- Crowdin localization integration prepare and stopwords removal [\#163](https://github.com/torrentpier/torrentpier/pull/163) ([Exile37](https://github.com/Exile37))
- Crowdin localization integration [\#162](https://github.com/torrentpier/torrentpier/pull/162) ([Exile37](https://github.com/Exile37))
- New Crowdin translations \(develop\) [\#161](https://github.com/torrentpier/torrentpier/pull/161) ([Exile37](https://github.com/Exile37))
- \#157. Fix Error in GET /bt/announce.php [\#159](https://github.com/torrentpier/torrentpier/pull/159) ([VasyOk](https://github.com/VasyOk))
- Added check composer install [\#148](https://github.com/torrentpier/torrentpier/pull/148) ([VasyOk](https://github.com/VasyOk))
- Fix operators [\#147](https://github.com/torrentpier/torrentpier/pull/147) ([VasyOk](https://github.com/VasyOk))
- \#144 Files should not be executable [\#145](https://github.com/torrentpier/torrentpier/pull/145) ([VasyOk](https://github.com/VasyOk))
- Change paths to absolute pathname [\#143](https://github.com/torrentpier/torrentpier/pull/143) ([VasyOk](https://github.com/VasyOk))
- Redundant pagination, mysql 5.7+ issue, release template option [\#141](https://github.com/torrentpier/torrentpier/pull/141) ([Exile37](https://github.com/Exile37))
- Transfer announce to the php7-optimized database layer [\#140](https://github.com/torrentpier/torrentpier/pull/140) ([Exile37](https://github.com/Exile37))
- Cleanup repository from old deprecated scripts and server configs [\#139](https://github.com/torrentpier/torrentpier/pull/139) ([Exile37](https://github.com/Exile37))
- Torrent ajax file list fixes and small reformat [\#138](https://github.com/torrentpier/torrentpier/pull/138) ([Exile37](https://github.com/Exile37))
- Codacy / Scrutinizer / Code Climate / Coveralls integration, Slack hook to Travis CI [\#137](https://github.com/torrentpier/torrentpier/pull/137) ([Exile37](https://github.com/Exile37))
- Add a Codacy badge to README.md [\#136](https://github.com/torrentpier/torrentpier/pull/136) ([codacy-badger](https://github.com/codacy-badger))
- Replace Sphinx API to the composer version [\#135](https://github.com/torrentpier/torrentpier/pull/135) ([Exile37](https://github.com/Exile37))
- Incorrect case close operators \(develop\) [\#134](https://github.com/torrentpier/torrentpier/pull/134) ([Exile37](https://github.com/Exile37))
- Incorrect case close operators \(master\) [\#133](https://github.com/torrentpier/torrentpier/pull/133) ([Exile37](https://github.com/Exile37))
- Composer init, editor config, some cleanup and much more [\#132](https://github.com/torrentpier/torrentpier/pull/132) ([Exile37](https://github.com/Exile37))
- Remove eval from admin\_attachments and emailer [\#129](https://github.com/torrentpier/torrentpier/pull/129) ([VasyOk](https://github.com/VasyOk))
- Fix sql group [\#128](https://github.com/torrentpier/torrentpier/pull/128) ([VasyOk](https://github.com/VasyOk))
- Remove Zend [\#127](https://github.com/torrentpier/torrentpier/pull/127) ([VasyOk](https://github.com/VasyOk))
- Small fix to the upgrade schema [\#126](https://github.com/torrentpier/torrentpier/pull/126) ([Exile37](https://github.com/Exile37))
- Fixed id sqllog table and name select db [\#125](https://github.com/torrentpier/torrentpier/pull/125) ([VasyOk](https://github.com/VasyOk))
- New external service for look up IP address [\#122](https://github.com/torrentpier/torrentpier/pull/122) ([Exile37](https://github.com/Exile37))
- New branding and copyright [\#121](https://github.com/torrentpier/torrentpier/pull/121) ([Exile37](https://github.com/Exile37))
- Poster birthday with no birthday date fix [\#120](https://github.com/torrentpier/torrentpier/pull/120) ([Exile37](https://github.com/Exile37))
- Tidy deprecated option merge-spans remove [\#119](https://github.com/torrentpier/torrentpier/pull/119) ([Exile37](https://github.com/Exile37))
- Db logging [\#118](https://github.com/torrentpier/torrentpier/pull/118) ([leroy0](https://github.com/leroy0))
- CircleCi, CodeCoverage and composer dependencies [\#117](https://github.com/torrentpier/torrentpier/pull/117) ([Exile37](https://github.com/Exile37))
- Db exceptions, query with binding [\#116](https://github.com/torrentpier/torrentpier/pull/116) ([leroy0](https://github.com/leroy0))
- PHP 7+ requirements, Travis and other small fixes [\#115](https://github.com/torrentpier/torrentpier/pull/115) ([Exile37](https://github.com/Exile37))
- New compatible with php7 classes: Db, Config [\#114](https://github.com/torrentpier/torrentpier/pull/114) ([Exile37](https://github.com/Exile37))
- Refactoring posting\_attachments [\#112](https://github.com/torrentpier/torrentpier/pull/112) ([VasyOk](https://github.com/VasyOk))
- Update the current year in the license text [\#110](https://github.com/torrentpier/torrentpier/pull/110) ([Exile37](https://github.com/Exile37))
- Reformat master branch to PSR-2 and MIT license [\#109](https://github.com/torrentpier/torrentpier/pull/109) ([Exile37](https://github.com/Exile37))
- Master branch up to php 7 compatibility [\#107](https://github.com/torrentpier/torrentpier/pull/107) ([VasyOk](https://github.com/VasyOk))
- Removal of unused scripts and server configs [\#105](https://github.com/torrentpier/torrentpier/pull/105) ([Exile37](https://github.com/Exile37))
- New license - MIT [\#104](https://github.com/torrentpier/torrentpier/pull/104) ([Exile37](https://github.com/Exile37))
- New coding standart: PSR-2 [\#103](https://github.com/torrentpier/torrentpier/pull/103) ([Exile37](https://github.com/Exile37))
- Improvements in code and work cache [\#101](https://github.com/torrentpier/torrentpier/pull/101) ([VasyOk](https://github.com/VasyOk))
- Migration to the new config subsystem [\#100](https://github.com/torrentpier/torrentpier/pull/100) ([Exile37](https://github.com/Exile37))
- php-lang-correct removed [\#99](https://github.com/torrentpier/torrentpier/pull/99) ([Exile37](https://github.com/Exile37))
- Logical operators should be avoided [\#98](https://github.com/torrentpier/torrentpier/pull/98) ([Exile37](https://github.com/Exile37))
- Migration to the new cache subsystem [\#97](https://github.com/torrentpier/torrentpier/pull/97) ([Exile37](https://github.com/Exile37))
- Rework of feed.php and some other files [\#94](https://github.com/torrentpier/torrentpier/pull/94) ([Exile37](https://github.com/Exile37))
- Refactoring Cache [\#92](https://github.com/torrentpier/torrentpier/pull/92) ([VasyOk](https://github.com/VasyOk))
- Add new tests and refactoring [\#89](https://github.com/torrentpier/torrentpier/pull/89) ([VasyOk](https://github.com/VasyOk))
- Add tests [\#88](https://github.com/torrentpier/torrentpier/pull/88) ([VasyOk](https://github.com/VasyOk))
- Some fix after removed @ [\#87](https://github.com/torrentpier/torrentpier/pull/87) ([VasyOk](https://github.com/VasyOk))
- \#77 Add monolog [\#86](https://github.com/torrentpier/torrentpier/pull/86) ([VasyOk](https://github.com/VasyOk))
- Remove at [\#85](https://github.com/torrentpier/torrentpier/pull/85) ([VasyOk](https://github.com/VasyOk))
- Переделка файла dl.php на работу с новой базой [\#83](https://github.com/torrentpier/torrentpier/pull/83) ([Exile37](https://github.com/Exile37))
- Added use profiler and in\(de\)crement methods. [\#82](https://github.com/torrentpier/torrentpier/pull/82) ([VasyOk](https://github.com/VasyOk))
- Remove response service provider [\#80](https://github.com/torrentpier/torrentpier/pull/80) ([VasyOk](https://github.com/VasyOk))
- DI usage example [\#79](https://github.com/torrentpier/torrentpier/pull/79) ([Exile37](https://github.com/Exile37))
- Added methods to simplify the work with the database [\#75](https://github.com/torrentpier/torrentpier/pull/75) ([VasyOk](https://github.com/VasyOk))
- Captcha service provider [\#72](https://github.com/torrentpier/torrentpier/pull/72) ([Exile37](https://github.com/Exile37))
- Fixed a getting value from config through method toArray [\#71](https://github.com/torrentpier/torrentpier/pull/71) ([VasyOk](https://github.com/VasyOk))
- \#69 Fixed crypt notice [\#70](https://github.com/torrentpier/torrentpier/pull/70) ([VasyOk](https://github.com/VasyOk))
- \#58 Expansion Zend Config [\#68](https://github.com/torrentpier/torrentpier/pull/68) ([VasyOk](https://github.com/VasyOk))
- change preset to prs2 [\#61](https://github.com/torrentpier/torrentpier/pull/61) ([VasyOk](https://github.com/VasyOk))
- Applied fixes from StyleCI [\#60](https://github.com/torrentpier/torrentpier/pull/60) ([Exile37](https://github.com/Exile37))
## [v2.1.5](https://github.com/torrentpier/torrentpier/tree/v2.1.5) (2015-05-23)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.1.4...v2.1.5)
**Merged pull requests:**
- Add a Gitter chat badge to README.md [\#47](https://github.com/torrentpier/torrentpier/pull/47) ([gitter-badger](https://github.com/gitter-badger))
- Фикс подтверждения пароля [\#43](https://github.com/torrentpier/torrentpier/pull/43) ([dreddred](https://github.com/dreddred))
- Fix port Ocelot [\#42](https://github.com/torrentpier/torrentpier/pull/42) ([Altairko](https://github.com/Altairko))
- Develop [\#40](https://github.com/torrentpier/torrentpier/pull/40) ([Exile37](https://github.com/Exile37))
## [v2.1.4](https://github.com/torrentpier/torrentpier/tree/v2.1.4) (2014-11-26)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.1.3...v2.1.4)
**Merged pull requests:**
- Develop [\#39](https://github.com/torrentpier/torrentpier/pull/39) ([Exile37](https://github.com/Exile37))
## [v2.1.3](https://github.com/torrentpier/torrentpier/tree/v2.1.3) (2014-10-24)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.1.2...v2.1.3)
**Merged pull requests:**
- Версия 2.1.3 ALPHA-3 [\#38](https://github.com/torrentpier/torrentpier/pull/38) ([Exile37](https://github.com/Exile37))
## [v2.1.2](https://github.com/torrentpier/torrentpier/tree/v2.1.2) (2014-10-20)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.1.1...v2.1.2)
**Merged pull requests:**
- Версия 2.1.2 ALPHA-2 [\#37](https://github.com/torrentpier/torrentpier/pull/37) ([Exile37](https://github.com/Exile37))
## [v2.1.1](https://github.com/torrentpier/torrentpier/tree/v2.1.1) (2014-09-11)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.1.0...v2.1.1)
**Merged pull requests:**
- Версия 2.1.1 ALPHA-1 [\#34](https://github.com/torrentpier/torrentpier/pull/34) ([Exile37](https://github.com/Exile37))
## [v2.1.0](https://github.com/torrentpier/torrentpier/tree/v2.1.0) (2014-09-07)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.599b...v2.1.0)
**Merged pull requests:**
- Версия 2.1 \(R600\) [\#32](https://github.com/torrentpier/torrentpier/pull/32) ([Exile37](https://github.com/Exile37))
## [v2.0.599b](https://github.com/torrentpier/torrentpier/tree/v2.0.599b) (2014-08-30)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.599...v2.0.599b)
**Merged pull requests:**
- Develop [\#31](https://github.com/torrentpier/torrentpier/pull/31) ([Exile37](https://github.com/Exile37))
- Feature/terms [\#30](https://github.com/torrentpier/torrentpier/pull/30) ([Exile37](https://github.com/Exile37))
## [v2.0.599](https://github.com/torrentpier/torrentpier/tree/v2.0.599) (2014-08-29)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.598...v2.0.599)
**Merged pull requests:**
- R599 [\#29](https://github.com/torrentpier/torrentpier/pull/29) ([Exile37](https://github.com/Exile37))
## [v2.0.598](https://github.com/torrentpier/torrentpier/tree/v2.0.598) (2014-08-27)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.597...v2.0.598)
**Merged pull requests:**
- R598 [\#28](https://github.com/torrentpier/torrentpier/pull/28) ([Exile37](https://github.com/Exile37))
## [v2.0.597](https://github.com/torrentpier/torrentpier/tree/v2.0.597) (2014-08-24)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.596...v2.0.597)
**Merged pull requests:**
- R597 [\#27](https://github.com/torrentpier/torrentpier/pull/27) ([Exile37](https://github.com/Exile37))
## [v2.0.596](https://github.com/torrentpier/torrentpier/tree/v2.0.596) (2014-08-20)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.595...v2.0.596)
**Merged pull requests:**
- Develop [\#26](https://github.com/torrentpier/torrentpier/pull/26) ([Exile37](https://github.com/Exile37))
## [v2.0.595](https://github.com/torrentpier/torrentpier/tree/v2.0.595) (2014-08-14)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.594b...v2.0.595)
**Merged pull requests:**
- Develop [\#22](https://github.com/torrentpier/torrentpier/pull/22) ([Exile37](https://github.com/Exile37))
## [v2.0.594b](https://github.com/torrentpier/torrentpier/tree/v2.0.594b) (2014-08-07)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.594...v2.0.594b)
**Merged pull requests:**
- Develop [\#17](https://github.com/torrentpier/torrentpier/pull/17) ([Exile37](https://github.com/Exile37))
- Hotfix/bbcode [\#16](https://github.com/torrentpier/torrentpier/pull/16) ([Exile37](https://github.com/Exile37))
## [v2.0.594](https://github.com/torrentpier/torrentpier/tree/v2.0.594) (2014-08-07)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.593b...v2.0.594)
**Merged pull requests:**
- Develop [\#15](https://github.com/torrentpier/torrentpier/pull/15) ([Exile37](https://github.com/Exile37))
## [v2.0.593b](https://github.com/torrentpier/torrentpier/tree/v2.0.593b) (2014-08-05)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.593...v2.0.593b)
## [v2.0.593](https://github.com/torrentpier/torrentpier/tree/v2.0.593) (2014-08-05)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.592...v2.0.593)
**Merged pull requests:**
- Develop [\#13](https://github.com/torrentpier/torrentpier/pull/13) ([Exile37](https://github.com/Exile37))
## [v2.0.592](https://github.com/torrentpier/torrentpier/tree/v2.0.592) (2014-08-01)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.591...v2.0.592)
## [v2.0.591](https://github.com/torrentpier/torrentpier/tree/v2.0.591) (2014-07-13)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.590...v2.0.591)
## [v2.0.590](https://github.com/torrentpier/torrentpier/tree/v2.0.590) (2014-06-21)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.589...v2.0.590)
## [v2.0.589](https://github.com/torrentpier/torrentpier/tree/v2.0.589) (2014-06-19)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.588...v2.0.589)
## [v2.0.588](https://github.com/torrentpier/torrentpier/tree/v2.0.588) (2014-06-17)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.587...v2.0.588)
## [v2.0.587](https://github.com/torrentpier/torrentpier/tree/v2.0.587) (2014-06-15)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.586...v2.0.587)
## [v2.0.586](https://github.com/torrentpier/torrentpier/tree/v2.0.586) (2014-06-13)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.585...v2.0.586)
## [v2.0.585](https://github.com/torrentpier/torrentpier/tree/v2.0.585) (2014-05-14)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.584...v2.0.585)
## [v2.0.584](https://github.com/torrentpier/torrentpier/tree/v2.0.584) (2014-03-07)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.583...v2.0.584)
## [v2.0.583](https://github.com/torrentpier/torrentpier/tree/v2.0.583) (2014-02-10)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.581...v2.0.583)
## [v2.0.581](https://github.com/torrentpier/torrentpier/tree/v2.0.581) (2014-02-03)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.572...v2.0.581)
## [v2.0.572](https://github.com/torrentpier/torrentpier/tree/v2.0.572) (2014-01-28)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.564...v2.0.572)
## [v2.0.564](https://github.com/torrentpier/torrentpier/tree/v2.0.564) (2014-01-20)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.560...v2.0.564)
## [v2.0.560](https://github.com/torrentpier/torrentpier/tree/v2.0.560) (2014-01-17)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.556...v2.0.560)
## [v2.0.556](https://github.com/torrentpier/torrentpier/tree/v2.0.556) (2014-01-12)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.552...v2.0.556)
## [v2.0.552](https://github.com/torrentpier/torrentpier/tree/v2.0.552) (2013-09-05)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.506...v2.0.552)
## [v2.0.506](https://github.com/torrentpier/torrentpier/tree/v2.0.506) (2013-06-23)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.500...v2.0.506)
## [v2.0.500](https://github.com/torrentpier/torrentpier/tree/v2.0.500) (2013-05-14)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.491...v2.0.500)
## [v2.0.491](https://github.com/torrentpier/torrentpier/tree/v2.0.491) (2013-01-12)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.477...v2.0.491)
## [v2.0.477](https://github.com/torrentpier/torrentpier/tree/v2.0.477) (2012-11-14)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.463...v2.0.477)
## [v2.0.463](https://github.com/torrentpier/torrentpier/tree/v2.0.463) (2012-10-16)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.456...v2.0.463)
## [v2.0.456](https://github.com/torrentpier/torrentpier/tree/v2.0.456) (2012-09-07)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.400...v2.0.456)
## [v2.0.400](https://github.com/torrentpier/torrentpier/tree/v2.0.400) (2012-04-13)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.300...v2.0.400)
## [v2.0.300](https://github.com/torrentpier/torrentpier/tree/v2.0.300) (2011-10-14)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.261...v2.0.300)
## [v2.0.261](https://github.com/torrentpier/torrentpier/tree/v2.0.261) (2011-08-28)
[Full Changelog](https://github.com/torrentpier/torrentpier/compare/v2.0.0...v2.0.261)
## [v2.0.0](https://github.com/torrentpier/torrentpier/tree/v2.0.0) (2011-08-08)
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

144
CLAUDE.md
View file

@ -1,144 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
TorrentPier is a BitTorrent tracker engine written in PHP, designed for hosting BitTorrent communities with forum functionality. The project is in active modernization, transitioning from legacy code to modern PHP practices while maintaining backward compatibility.
## Technology Stack & Architecture
- **PHP 8.2+** with modern features
- **MySQL/MariaDB/Percona** database
- **Nette Database** with backward-compatible wrapper
- **Composer** for dependency management
- **Custom BitTorrent tracker** implementation
## Key Directory Structure
- `/src/` - Modern PHP classes (PSR-4 autoloaded as `TorrentPier\`)
- `/library/` - Core application logic and legacy code
- `/admin/` - Administrative interface
- `/bt/` - BitTorrent tracker functionality (announce.php, scrape.php)
- `/styles/` - Templates, CSS, JS, images
- `/internal_data/` - Cache, logs, compiled templates
- `/install/` - Installation scripts and configuration examples
- `/migrations/` - Database migration files (Phinx)
## Entry Points & Key Files
- `index.php` - Main forum homepage
- `tracker.php` - Torrent search/browse interface
- `bt/announce.php` - BitTorrent announce endpoint
- `bt/scrape.php` - BitTorrent scrape endpoint
- `admin/index.php` - Administrative panel
- `cron.php` - Background task runner (CLI only)
- `install.php` - Installation script (CLI only)
## Development Commands
### Installation & Setup
```bash
# Automated installation (CLI)
php install.php
# Install dependencies
composer install
# Update dependencies
composer update
```
### Maintenance & Operations
```bash
# Run background maintenance tasks
php cron.php
```
### Code Quality
The project uses **StyleCI** with PSR-2 preset for code style enforcement. StyleCI configuration is in `.styleci.yml` targeting `src/` directory.
## Modern Architecture Components
### Database Layer (`/src/Database/`)
- **Nette Database** with full old SqlDb backward compatibility
- Singleton pattern accessible via `DB()` function
- Support for multiple database connections and debug functionality
- Migration path to ORM-style Explorer queries
### Cache System (`/src/Cache/`)
- **Unified caching** using Nette Caching internally
- 100% backward compatibility with existing `CACHE()` and $datastore calls
- Supports file, SQLite, memory, and Memcached storage
- Advanced features: memoization, cache dependencies
### Configuration Management
- Environment-based config with `.env` files
- Singleton `Config` class accessible via `config()` function
- Local overrides supported via `library/config.local.php`
## Configuration Files
- `.env` - Environment variables (copy from `.env.example`)
- `library/config.php` - Main application configuration
- `library/config.local.php` - Local configuration overrides
- `composer.json` - Dependencies and PSR-4 autoloading
## Development Workflow
### CI/CD Pipeline
- **GitHub Actions** for automated testing and deployment
- **StyleCI** for code style enforcement
- **Dependabot** for dependency updates
- **FTP deployment** to demo environment
### Installation Methods
1. **Automated**: `php install.php` (recommended)
2. **Composer**: `composer create-project torrentpier/torrentpier`
3. **Manual**: Git clone + `composer install` + database setup
## Database & Schema
- **Database migrations** managed via Phinx in `/migrations/` directory
- Initial schema: `20250619000001_initial_schema.php`
- Initial seed data: `20250619000002_seed_initial_data.php`
- UTF-8 (utf8mb4) character set required
- Multiple database alias support for different components
### Migration Commands
```bash
# Run all pending migrations
php vendor/bin/phinx migrate --configuration=phinx.php
# Check migration status
php vendor/bin/phinx status --configuration=phinx.php
# Mark migrations as applied (for existing installations)
php vendor/bin/phinx migrate --fake --configuration=phinx.php
```
## Legacy Compatibility Strategy
The codebase maintains 100% backward compatibility while introducing modern alternatives:
- **Database layer**: Existing old SqlDb calls work while new code can use Nette Database
- **Cache system**: All existing `CACHE()` and $datastore calls preserved while adding modern features
- **Configuration**: Legacy config access maintained alongside new singleton pattern
This approach allows gradual modernization without breaking existing functionality - critical for a mature application with existing deployments.
## Security & Performance
- **Environment-based secrets** management via `.env`
- **CDN/proxy support** (Cloudflare, Fastly)
- **Input sanitization** and CSRF protection
- **Advanced caching** with multiple storage backends
- **Rate limiting** and IP-based restrictions
## BitTorrent Tracker Features
- **BitTorrent v1 & v2** support
- **TorrServer integration** capability
- **Client ban system** for problematic torrent clients
- **Scrape support** for tracker statistics
When working with this codebase, prioritize understanding the legacy compatibility approach and modern architecture patterns. Always test both legacy and modern code paths when making changes to core systems.

View file

@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at admin@torrentpier.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at admin@torrentpier.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View file

@ -1,19 +1,19 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Update the README.md with details of changes to the interface, this includes new environment
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Code of Conduct
@ -41,7 +41,7 @@ include:
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
@ -73,7 +73,7 @@ further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at admin@torrentpier.com. All
reported by contacting the project team at admin@torrentpier.me. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
@ -89,5 +89,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2005-2025 TorrentPier
Copyright (c) 2005-2017 TorrentPier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

233
README.md
View file

@ -1,191 +1,98 @@
<p align="center"><a href="https://torrentpier.com"><img src="https://torrentpier.com/styles/default/xenforo/bull-logo.svg" width="400px" alt="TorrentPier" /></a></p>
<p align="center"><a href="https://torrentpier.me/"><img src="https://torrentpier.me/forum/styles/default/xenforo/bull-logo.svg" width="400px" /></a></p>
<p align="center">
Bull-powered BitTorrent tracker engine
<br/>
<a href="http://torrentpier.herokuapp.com/"><img src="http://torrentpier.herokuapp.com/badge.svg" alt="Slack"></a>
<a href="https://crowdin.com/project/torrentpier"><img src="https://d322cqt584bo4o.cloudfront.net/torrentpier/localized.svg" alt="Crowdin"></a>
<a href="https://scrutinizer-ci.com/g/torrentpier/torrentpier/"><img src="https://img.shields.io/scrutinizer/g/torrentpier/torrentpier.svg" alt="Scrutinizer"></a>
<a href="https://www.codacy.com/app/Exile37/torrentpier"><img src="https://img.shields.io/codacy/grade/8b79a63a6d464b81bf0a39923f42bdf5/master.svg" alt="Codacy"></a>
<br />
<a href="https://insight.sensiolabs.com/projects/1a5d5098-e0b0-45c2-816a-020dfd50acaf"><img src="https://img.shields.io/sensiolabs/i/1a5d5098-e0b0-45c2-816a-020dfd50acaf.svg" alt="SensioLabs Insight"></a>
<a href="https://travis-ci.org/torrentpier/torrentpier"><img src="https://img.shields.io/travis/torrentpier/torrentpier/master.svg" alt="Travis"></a>
<a href="https://circleci.com/gh/torrentpier/torrentpier"><img src="https://img.shields.io/circleci/project/github/torrentpier/torrentpier/master.svg" alt="CircleCI"></a>
<a href="https://codecov.io/gh/torrentpier/torrentpier"><img src="https://img.shields.io/codecov/c/github/torrentpier/torrentpier/master.svg" alt="Codecov"></a>
</p>
<p align="center">
<a href="https://github.com/torrentpier/torrentpier/blob/master/LICENSE"><img src="https://img.shields.io/github/license/torrentpier/torrentpier" alt="License"></a>
<a href="https://packagist.org/packages/torrentpier/torrentpier"><img src="https://img.shields.io/packagist/stars/torrentpier/torrentpier" alt="Stars Packagist"></a>
<a href="https://crowdin.com/project/torrentpier"><img src="https://badges.crowdin.net/torrentpier/localized.svg" alt="Crowdin"></a>
<a href="https://nightly.link/torrentpier/torrentpier/workflows/ci/master/TorrentPier-master"><img src="https://img.shields.io/badge/Nightly%20release-gray?logo=hackthebox&logoColor=fff" alt="TorrentPier nightly"></a>
<a href="https://packagist.org/packages/torrentpier/torrentpier"><img src="https://img.shields.io/packagist/dt/torrentpier/torrentpier" alt="Downloads"></a>
<a href="https://packagist.org/packages/torrentpier/torrentpier"><img src="https://img.shields.io/packagist/v/torrentpier/torrentpier" alt="Version"></a>
<a href="https://github.com/torrentpier/torrentpier/releases"><img src="https://img.shields.io/github/release-date/torrentpier/torrentpier" alt="Last release"></a>
<img src="https://img.shields.io/github/repo-size/torrentpier/torrentpier" alt="Size">
<a href="https://github.com/SamKirkland/FTP-Deploy-Action"><img src="https://img.shields.io/badge/Deployed to TorrentPier Demo with-FTP DEPLOY ACTION-%3CCOLOR%3E?color=2b9348" alt="Deployed to TorrentPier Demo with FTP Deploy Action"></a>
</p>
## About TorrentPier
## 🐂 About TorrentPier
TorrentPier — bull-powered BitTorrent tracker engine, written in php. High speed, simple modification, high load
architecture, built-in support for alternative compiled announcers (Ocelot, XBT). In addition we have very helpful
[official support forum](https://torrentpier.me/forum), where among other things it is possible to test the live
demo, get any support and download modifications for engine.
TorrentPier — bull-powered BitTorrent Public/Private tracker engine, written in PHP. High speed, simple modifications, load-balanced
architecture. In addition, we have a very helpful
[official support forum](https://torrentpier.com), where it's possible to get support and download modifications for the engine.
## Current status
## 🌈 Current status
TorrentPier is currently in active development. The goal is to remove all legacy code and rewrite existing to
modern standards. If you want to go deep on the code, check our [issues](https://github.com/torrentpier/torrentpier/issues)
and go from there. The documentation will be translated into english in the near future, currently russian is the main language of it.
TorrentPier is currently in active development. The goal is to remove all legacy code and rewrite the existing code to
modern specifications. If you want to delve deep into the code, check our [issues](https://github.com/torrentpier/torrentpier/issues)
and go from there. The documentation will be translated to English in the near future, currently Russian is the main language.
## Requirements
## ✨ Features
* Rich forum with browsing/moderation tools
* High-load capable, heavily configurable announcer
* Scrape support
* FreeLeech
* [TorrServer integration](https://github.com/YouROK/TorrServer) support
* BitTorrent v2 support
* Event-based invite system
* Bonus points
* Polling system
* PM/DM system
* Multilingual support (Russian and English are currently fully supported, with others in the future)
* Atom/RSS feeds
* ... and so MUCH MORE!
* Apache / nginx
* MySQL / MariaDB / Percona
* PHP: 5.6 / 7.0 / 7.1
* PHP Extensions: bcmath, intl, tidy (optional)
## 🖥️ Demo
## Installation
* URL: https://torrentpier.duckdns.org
* Username: `admin`
* Password: `admin`
For installation you need to follow a few simple steps:
> [!NOTE]
> Demo resets every 24 hours!
1. Unpack to the server the contents of the downloaded folder
1. Install [Composer](https://getcomposer.org/) and run `composer install` on the downloaded directory
1. Create database and import dump located at **install/sql/mysql.sql**
1. Edit database configuration settings in the configuration file or a local copy (see below)
1. Edit domain name in the configuration file or a local copy (see below)
1. Edit this files:
1. **favicon.png** (change on your own)
1. **robots.txt** (change the addresses in lines **Host** and **Sitemap** on your own)
1. **opensearch_desc.xml** (change the description and address on your own)
1. **opensearch_desc_bt.xml** (change the description and address on your own)
1. Log in to the forum with admin/admin login/password and finish setting up via admin panel
## 🔧 Requirements
## Access rights on folders and files
* Apache / nginx ([example config](install/nginx.conf)) / caddy ([example config](install/Caddyfile))
* MySQL 5.5.3 or above (including MySQL 8.0+) / MariaDB 10.0 or above / Percona
* PHP: 8.2 / 8.3 / 8.4
* PHP Extensions: mbstring, gd, bcmath, intl, tidy (optional), xml, xmlwriter
* Crontab (Recommended)
You must provide write permissions to the specified folders:
* `data/avatars`
* `data/torrent_files`
* `internal_data/ajax_html`
* `internal_data/atom`
* `internal_data/cache`
* `internal_data/log`
* `internal_data/triggers`
* `sitemap`
## 💾 Installation
The specific settings depend on the server you are using, but in general case we recommend chmod 0755 for folders,
and chmod 0644 for files in them. If you are not sure, leave it as is.
For the installation, select one of the installation variants below:
## The recommended way to run cron.php
### Quick (Clean install) 🚀
For significant tracker speed increase may be required to replace built-in cron.php by operating system daemon. For more
information about that you can read [this thread](https://torrentpier.me/forum/threads/52/) on our support forum.
Check out our [autoinstall](https://github.com/torrentpier/autoinstall) repository with detailed instructions.
## Local configuration copy
> [!NOTE]
> Thanks to [Sergei Solovev](https://github.com/SeAnSolovev) for this installation script ❤️
You can override the settings using one of these methods: configuration file **library/config.local.php** and the environment
file **.env**. Both files are created by copying the appropriate .example templates without extension. Local configuration files
should not be available for reading to anyone by setting up access rights for your web server.
### Quick (For web-panels) ☕️
## Ocelot installation
1. Select the folder where you want TorrentPier installed
```shell
cd /path/to/public_html
```
2. Download the latest version of TorrentPier
```shell
sudo git clone https://github.com/torrentpier/torrentpier.git .
```
3. After completing, execute the command below and follow the instructions
```shell
php install.php
```
4. Voila! ✨
We have built-in support for alternate compiled announcer — Ocelot. The configuration is in the file **library/config.php**,
the announcer is in the repository [torrentpier/ocelot](https://github.com/torrentpier/ocelot). You can read assembly instructions
on his repository or in [this thread](https://torrentpier.me/forum/threads/26078/) on our support forum.
### Manual 🔩
## Official documentation
1. Install [Composer](https://getcomposer.org/)
2. Run the following command to create the TorrentPier project
```shell
composer create-project torrentpier/torrentpier
```
3. [Check our system requirements](#-requirements)
4. After, run this command in the project directory to install Composer dependencies
```shell
composer install
```
5. Edit database configuration settings in the environment (`.env.example`), after, rename to `.env`
6. Create a database and run migrations to set up the schema
```shell
php vendor/bin/phinx migrate --configuration=phinx.php
```
7. Provide write permissions to the specified folders:
* `data/avatars`, `data/uploads`, `data/uploads/thumbs`
* `internal_data/atom`, `internal_data/cache`, `internal_data/log`, `internal_data/triggers`
* `sitemap`
8. Voila! ✨
Documentation for TorrentPier can be found on the [TorrentPier docs website](https://docs.torrentpier.me).
> [!TIP]
> You can automate steps 4-7 by running `php install.php` instead, which will guide you through the setup process interactively.
## Contributing
> [!IMPORTANT]
> The specific settings depend on the server you are using, but in general we recommend chmod **0755** for folders, and chmod **0644** for the files in them.
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for
submitting pull requests to us. But we are always ready to renew your pull-request for compliance with
these requirements. Just send it.
### Additional steps 👣
## Versioning
1. Edit these files:
* `favicon.png` (change to your own)
* `robots.txt` (change the addresses in lines `Host` and `Sitemap` to your own)
2. Log in to the forum using the **admin/admin** login/password, and finish setting up via admin panel. Don't forget to change your password!
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/torrentpier/torrentpier/tags).
## 🔐 Security vulnerabilities
## License
If you discover a security vulnerability within TorrentPier, please follow our [security policy](https://github.com/torrentpier/torrentpier/security/policy), so we can address it promptly.
## 🧪 Testing
TorrentPier includes a comprehensive testing suite built with **Pest PHP**. Run tests to ensure code quality and system reliability:
```shell
# Run all tests
./vendor/bin/pest
# Run with coverage
./vendor/bin/pest --coverage
```
For detailed testing documentation, see [tests/README.md](tests/README.md).
## 📌 Our recommendations
* *It's recommended to run `cron.php`.* - For significant tracker speed increase it may be required to replace the built-in cron.php with an operating system daemon.
* *Local configuration copy.* - You can override the settings using the local configuration file `library/config.local.php`.
## 💚 Contributing / Contributors
Please read our [contributing policy](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) for details, and the process for
submitting pull requests to us. But we are always ready to review your pull-request for compliance with
these requirements. Just send it!
<a href="https://github.com/torrentpier/torrentpier/graphs/contributors">
<img src="https://contrib.rocks/image?repo=torrentpier/torrentpier" alt="Contributors"/>
</a>
Made with [contrib.rocks](https://contrib.rocks).
## 💞 Sponsoring
Support this project by becoming a sponsor or a backer.
[![OpenCollective sponsors](https://opencollective.com/torrentpier/sponsors/badge.svg)](https://opencollective.com/torrentpier)
[![OpenCollective backers](https://opencollective.com/torrentpier/backers/badge.svg)](https://opencollective.com/torrentpier)
<details>
<summary>Monero</summary>
```
42zJE3FDvN8foP9QYgDrBjgtd7h2FipGCGmAcmG5VFQuRkJBGMbCvoLSmivepmAMEgik2E8MPWUzKaoYsGCtmhvL7ZN73jh
```
</details>
<details>
<summary>YooMoney</summary>
```
4100118022415720
```
</details>
## 📦 Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/torrentpier/torrentpier/tags).
## 📖 License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/torrentpier/torrentpier/blob/master/LICENSE) file for details.
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details

View file

@ -1,13 +0,0 @@
# Security Policy
## Versions
Due to the nature of our project - being open source - we have decided to patch only the latest major release (currently v2.4.x) for security vulnerabilities.
## How to disclose
Please disclose security issues by mailing [admin@torrentpier.com](mailto:admin@torrentpier.com).
## What we do
Any submitted security issue will be checked thoroughly by our development team. A fix for the issue and a transparent information on GitHub about the issue existing will be released. You can view any previously identified issues on our [GitHub Security Page](https://github.com/torrentpier/torrentpier/security/advisories). New major versions of TorrentPier will also receive a security audit to verify our efforts on providing a secure application.

File diff suppressed because it is too large Load diff

View file

@ -1,57 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!defined('BB_ROOT')) {
define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT);
}
// Check CLI mode
if (PHP_SAPI != 'cli') {
exit;
}
if (!function_exists('removeFile')) {
// Get all constants
require_once BB_ROOT . 'library/defines.php';
// Include CLI functions
require INC_DIR . '/functions_cli.php';
}
$items = [
'.github',
'.cliffignore',
'.editorconfig',
'.gitignore',
'.styleci.yml',
'_release.php',
'CHANGELOG.md',
'CLAUDE.md',
'cliff.toml',
'CODE_OF_CONDUCT.md',
'CONTRIBUTING.md',
'crowdin.yml',
'HISTORY.md',
'phpunit.xml',
'README.md',
'SECURITY.md',
'tests',
'UPGRADE_GUIDE.md'
];
foreach ($items as $item) {
$path = BB_ROOT . $item;
if (is_file($path)) {
removeFile($path);
} elseif (is_dir($path)) {
removeDir($path);
}
}

View file

@ -1,130 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT);
// Check CLI mode
if (PHP_SAPI != 'cli') {
die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode');
}
// Get all constants
require_once BB_ROOT . 'library/defines.php';
// Include CLI functions
require INC_DIR . '/functions_cli.php';
// Welcoming message
out("--- Release creation tool ---\n", 'info');
$configFile = BB_PATH . '/library/config.php';
if (!is_file($configFile)) {
out('- Config file ' . basename($configFile) . ' not found', 'error');
exit;
}
if (!is_readable($configFile)) {
out('- Config file ' . basename($configFile) . ' is not readable', 'error');
exit;
}
if (!is_writable($configFile)) {
out('- Config file ' . basename($configFile) . ' is not writable', 'error');
exit;
}
// Ask for version
fwrite(STDOUT, 'Enter version number (e.g, v2.4.0): ');
$version = trim(fgets(STDIN));
if (empty($version)) {
out("- Version cannot be empty. Please enter a valid version number", 'error');
exit;
} else {
// Add 'v' prefix if missing
if (!str_starts_with($version, 'v')) {
$version = 'v' . $version;
}
out("- Using version: $version", 'info');
}
// Ask for version emoji
fwrite(STDOUT, 'Enter version emoji: ');
$versionEmoji = trim(fgets(STDIN));
if (!empty($versionEmoji)) {
out("- Using version emoji: $versionEmoji", 'info');
}
// Ask for release date or use today's date
fwrite(STDOUT, "Enter release date (e.g. 25-05-2025), leave empty to use today's date: ");
$date = trim(fgets(STDIN));
if (empty($date)) {
$date = date('d-m-Y');
out("- Using current date: $date", 'info');
} else {
// Validate date format (dd-mm-yyyy)
$dateObj = DateTime::createFromFormat('d-m-Y', $date);
if (!$dateObj || $dateObj->format('d-m-Y') !== $date) {
out("- Invalid date format. Expected format: DD-MM-YYYY", 'error');
exit;
}
out("- Using date: $date", 'info');
}
// Read config file content
$content = file_get_contents($configFile);
// Update version
$content = preg_replace(
"/\\\$bb_cfg\['tp_version'\]\s*=\s*'[^']*';/",
"\$bb_cfg['tp_version'] = '$version';",
$content
);
// Update release date
$content = preg_replace(
"/\\\$bb_cfg\['tp_release_date'\]\s*=\s*'[^']*';/",
"\$bb_cfg['tp_release_date'] = '$date';",
$content
);
// Save updated config
$bytesWritten = file_put_contents($configFile, $content);
if ($bytesWritten === false) {
out("- Failed to write to config file", 'error');
exit;
}
if ($bytesWritten === 0) {
out("- Config file was not updated (0 bytes written)", 'error');
exit;
}
out("\n- Config file has been updated!", 'success');
// Update CHANGELOG.md
runProcess('npx git-cliff v2.4.6-alpha.4.. --config cliff.toml --tag "' . $version . '" > CHANGELOG.md');
// Git add & commit
runProcess('git add -A && git commit -m "release: ' . escapeshellarg($version) . (!empty($versionEmoji) ? (' ' . $versionEmoji) : '') . '"');
// Git tag
runProcess("git tag -a \"$version\" -m \"Release $version\"");
runProcess("git tag -v \"$version\"");
// Git push
runProcess("git push origin master");
runProcess("git push origin $version");
out("\n- Release $version has been successfully prepared, committed and pushed!", 'success');

View file

@ -1,17 +1,32 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['ATTACHMENTS']['CONTROL_PANEL'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
if (($attach_config['upload_dir'][0] == '/') || (($attach_config['upload_dir'][0] != '/') && ($attach_config['upload_dir'][1] == ':'))) {
@ -34,29 +49,29 @@ $view = (isset($_POST['search']) && $_POST['search']) ? 'attachments' : $view;
// process modes based on view
if ($view === 'username') {
$mode_types_text = [$lang['SORT_USERNAME'], $lang['SORT_ATTACHMENTS'], $lang['SORT_SIZE']];
$mode_types = ['username', 'attachments', 'filesize'];
$mode_types_text = array($lang['SORT_USERNAME'], $lang['SORT_ATTACHMENTS'], $lang['SORT_SIZE']);
$mode_types = array('username', 'attachments', 'filesize');
if (!$mode) {
$mode = 'attachments';
$sort_order = 'DESC';
}
} elseif ($view === 'attachments') {
$mode_types_text = [$lang['SORT_FILENAME'], $lang['SORT_COMMENT'], $lang['SORT_EXTENSION'], $lang['SORT_SIZE'], $lang['SORT_DOWNLOADS'], $lang['SORT_POSTTIME']];
$mode_types = ['real_filename', 'comment', 'extension', 'filesize', 'downloads', 'post_time'];
$mode_types_text = array($lang['SORT_FILENAME'], $lang['SORT_COMMENT'], $lang['SORT_EXTENSION'], $lang['SORT_SIZE'], $lang['SORT_DOWNLOADS'], $lang['SORT_POSTTIME']);
$mode_types = array('real_filename', 'comment', 'extension', 'filesize', 'downloads', 'post_time');
if (!$mode) {
$mode = 'real_filename';
$sort_order = 'ASC';
}
} elseif ($view === 'search') {
$mode_types_text = [$lang['SORT_FILENAME'], $lang['SORT_COMMENT'], $lang['SORT_EXTENSION'], $lang['SORT_SIZE'], $lang['SORT_DOWNLOADS'], $lang['SORT_POSTTIME']];
$mode_types = ['real_filename', 'comment', 'extension', 'filesize', 'downloads', 'post_time'];
$mode_types_text = array($lang['SORT_FILENAME'], $lang['SORT_COMMENT'], $lang['SORT_EXTENSION'], $lang['SORT_SIZE'], $lang['SORT_DOWNLOADS'], $lang['SORT_POSTTIME']);
$mode_types = array('real_filename', 'comment', 'extension', 'filesize', 'downloads', 'post_time');
$sort_order = 'DESC';
} else {
$view = 'stats';
$mode_types_text = [];
$mode_types_text = array();
$sort_order = 'ASC';
}
@ -69,66 +84,65 @@ $order_by = '';
if ($view === 'username') {
switch ($mode) {
case 'username':
$order_by = 'ORDER BY u.username ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY u.username ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'attachments':
$order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'filesize':
$order_by = 'ORDER BY total_size ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY total_size ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
default:
$mode = 'attachments';
$sort_order = 'DESC';
$order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
}
} elseif ($view === 'attachments') {
switch ($mode) {
case 'real_filename':
$order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
case 'filename':
$order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'comment':
$order_by = 'ORDER BY a.comment ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY a.comment ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'extension':
$order_by = 'ORDER BY a.extension ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY a.extension ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'filesize':
$order_by = 'ORDER BY a.filesize ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY a.filesize ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'downloads':
$order_by = 'ORDER BY a.download_count ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY a.download_count ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
case 'post_time':
$order_by = 'ORDER BY a.filetime ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY a.filetime ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
default:
$mode = 'a.real_filename';
$sort_order = 'ASC';
$order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
$order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page'];
break;
}
}
// Set select fields
$view_types_text = [$lang['VIEW_STATISTIC'], $lang['VIEW_SEARCH']];
$view_types = ['stats', 'search'];
$select_view = '';
$view_types_text = array($lang['VIEW_STATISTIC'], $lang['VIEW_SEARCH']);
$view_types = array('stats', 'search');
if (in_array($view, $view_types)) {
$select_view = '<select name="view">';
for ($i = 0, $iMax = count($view_types_text); $i < $iMax; $i++) {
$selected = ($view === $view_types[$i]) ? ' selected' : '';
$select_view .= '<option value="' . $view_types[$i] . '"' . $selected . '>' . $view_types_text[$i] . '</option>';
}
$select_view .= '</select>';
$select_view = '<select name="view">';
for ($i = 0, $iMax = count($view_types_text); $i < $iMax; $i++) {
$selected = ($view === $view_types[$i]) ? ' selected="selected"' : '';
$select_view .= '<option value="' . $view_types[$i] . '"' . $selected . '>' . $view_types_text[$i] . '</option>';
}
$select_view .= '</select>';
if (count($mode_types_text) > 0 && !empty($mode_types)) {
if (count($mode_types_text) > 0) {
$select_sort_mode = '<select name="mode">';
for ($i = 0, $iMax = count($mode_types_text); $i < $iMax; $i++) {
$selected = ($mode === $mode_types[$i]) ? ' selected' : '';
$selected = ($mode === $mode_types[$i]) ? ' selected="selected"' : '';
$select_sort_mode .= '<option value="' . $mode_types[$i] . '"' . $selected . '>' . $mode_types_text[$i] . '</option>';
}
$select_sort_mode .= '</select>';
@ -136,20 +150,20 @@ if (count($mode_types_text) > 0 && !empty($mode_types)) {
$select_sort_order = '<select name="order">';
if ($sort_order === 'ASC') {
$select_sort_order .= '<option value="ASC" selected>' . $lang['ASC'] . '</option><option value="DESC">' . $lang['DESC'] . '</option>';
$select_sort_order .= '<option value="ASC" selected="selected">' . $lang['ASC'] . '</option><option value="DESC">' . $lang['DESC'] . '</option>';
} else {
$select_sort_order .= '<option value="ASC">' . $lang['ASC'] . '</option><option value="DESC" selected>' . $lang['DESC'] . '</option>';
$select_sort_order .= '<option value="ASC">' . $lang['ASC'] . '</option><option value="DESC" selected="selected">' . $lang['DESC'] . '</option>';
}
$select_sort_order .= '</select>';
$submit_change = isset($_POST['submit_change']);
$delete = isset($_POST['delete']);
$delete_id_list = get_var('delete_id_list', [0]);
$delete_id_list = get_var('delete_id_list', array(0));
$confirm = isset($_POST['confirm']);
if ($confirm && count($delete_id_list) > 0) {
$attachments = [];
$attachments = array();
delete_attachment(0, $delete_id_list);
} elseif ($delete && count($delete_id_list) > 0) {
@ -160,29 +174,29 @@ if ($confirm && count($delete_id_list) > 0) {
$hidden_fields .= '<input type="hidden" name="u_id" value="' . $uid . '" />';
$hidden_fields .= '<input type="hidden" name="start" value="' . $start . '" />';
foreach ($delete_id_list as $iValue) {
$hidden_fields .= '<input type="hidden" name="delete_id_list[]" value="' . $iValue . '" />';
for ($i = 0, $iMax = count($delete_id_list); $i < $iMax; $i++) {
$hidden_fields .= '<input type="hidden" name="delete_id_list[]" value="' . $delete_id_list[$i] . '" />';
}
print_confirmation([
print_confirmation(array(
'FORM_ACTION' => 'admin_attach_cp.php',
'HIDDEN_FIELDS' => $hidden_fields,
]);
));
}
// Assign Default Template Vars
$template->assign_vars([
$template->assign_vars(array(
'S_VIEW_SELECT' => $select_view,
'S_MODE_ACTION' => 'admin_attach_cp.php?view=' . $view . '&amp;mode=' . $mode . '&amp;order=' . $sort_order . '&amp;uid=' . $uid
]);
'S_MODE_ACTION' => 'admin_attach_cp.php',
));
if ($submit_change && $view === 'attachments') {
$attach_change_list = get_var('attach_id_list', [0]);
$attach_comment_list = get_var('attach_comment_list', ['']);
$attach_download_count_list = get_var('attach_count_list', [0]);
$attach_change_list = get_var('attach_id_list', array(0));
$attach_comment_list = get_var('attach_comment_list', array(''));
$attach_download_count_list = get_var('attach_count_list', array(0));
// Generate correct Change List
$attachments = [];
$attachments = array();
for ($i = 0, $iMax = count($attach_change_list); $i < $iMax; $i++) {
$attachments['_' . $attach_change_list[$i]]['comment'] = $attach_comment_list[$i];
@ -201,7 +215,7 @@ if ($submit_change && $view === 'attachments') {
if (isset($attachments['_' . $attachrow['attach_id']])) {
if ($attachrow['comment'] != $attachments['_' . $attachrow['attach_id']]['comment'] || $attachrow['download_count'] != $attachments['_' . $attachrow['attach_id']]['download_count']) {
$sql = 'UPDATE ' . BB_ATTACHMENTS_DESC . "
SET comment = '" . DB()->escape($attachments['_' . $attachrow['attach_id']]['comment']) . "', download_count = " . (int)$attachments['_' . $attachrow['attach_id']]['download_count'] . '
SET comment = '" . attach_mod_sql_escape($attachments['_' . $attachrow['attach_id']]['comment']) . "', download_count = " . (int)$attachments['_' . $attachrow['attach_id']]['download_count'] . '
WHERE attach_id = ' . (int)$attachrow['attach_id'];
if (!DB()->sql_query($sql)) {
@ -233,7 +247,7 @@ if ($view == 'stats') {
$row = DB()->fetch_row('SELECT COUNT(DISTINCT user_id_1) AS users FROM ' . BB_ATTACHMENTS . ' WHERE post_id != 0');
$number_of_users = $row['users'];
$template->assign_vars([
$template->assign_vars(array(
'TPL_ATTACH_STATISTICS' => true,
'TOTAL_FILESIZE' => $upload_dir_size,
'ATTACH_QUOTA' => $attachment_quota,
@ -242,7 +256,7 @@ if ($view == 'stats') {
'NUMBER_OF_PMS' => $number_of_pms,
'NUMBER_OF_TOPICS' => $number_of_topics,
'NUMBER_OF_USERS' => $number_of_users,
]);
));
}
// Search
@ -282,38 +296,38 @@ if ($view === 'search') {
bb_die($lang['NO_SEARCHABLE_FORUMS']);
}
$template->assign_vars([
$template->assign_vars(array(
'TPL_ATTACH_SEARCH' => true,
'S_FORUM_OPTIONS' => $s_forums,
'S_CATEGORY_OPTIONS' => $s_categories,
'S_SORT_OPTIONS' => $select_sort_mode,
'S_SORT_ORDER' => $select_sort_order,
]);
));
}
// Username
if ($view === 'username') {
$template->assign_vars([
$template->assign_vars(array(
'TPL_ATTACH_USER' => true,
'S_MODE_SELECT' => $select_sort_mode,
'S_ORDER_SELECT' => $select_sort_order,
]);
));
$total_rows = 0;
bb_die('removed');
}
// Attachments
if ($view === 'attachments') {
$user_based = (bool)$uid;
$user_based = $uid ? true : false;
$search_based = (isset($_POST['search']) && $_POST['search']);
$hidden_fields = '';
$template->assign_vars([
$template->assign_vars(array(
'TPL_ATTACH_ATTACHMENTS' => true,
'S_MODE_SELECT' => $select_sort_mode,
'S_ORDER_SELECT' => $select_sort_order,
]);
));
$total_rows = 0;
@ -331,12 +345,12 @@ if ($view === 'attachments') {
$s_hidden = '<input type="hidden" name="u_id" value="' . (int)$uid . '" />';
$template->assign_block_vars('switch_user_based', []);
$template->assign_block_vars('switch_user_based', array());
$template->assign_vars([
$template->assign_vars(array(
'S_USER_HIDDEN' => $s_hidden,
'L_STATISTICS_FOR_USER' => sprintf($lang['STATISTICS_FOR_USER'], $username),
]);
));
$sql = 'SELECT attach_id
FROM ' . BB_ATTACHMENTS . '
@ -357,7 +371,7 @@ if ($view === 'attachments') {
$total_rows = $num_attach_ids;
$attach_id = [];
$attach_id = array();
for ($j = 0; $j < $num_attach_ids; $j++) {
$attach_id[] = (int)$attach_ids[$j]['attach_id'];
@ -384,9 +398,9 @@ if ($view === 'attachments') {
for ($i = 0, $iMax = count($attachments); $i < $iMax; $i++) {
$delete_box = '<input type="checkbox" name="delete_id_list[]" value="' . (int)$attachments[$i]['attach_id'] . '" />';
foreach ($delete_id_list as $jValue) {
if ($jValue == $attachments[$i]['attach_id']) {
$delete_box = '<input type="checkbox" name="delete_id_list[]" value="' . (int)$attachments[$i]['attach_id'] . '" checked />';
for ($j = 0, $jMax = count($delete_id_list); $j < $jMax; $j++) {
if ($delete_id_list[$j] == $attachments[$i]['attach_id']) {
$delete_box = '<input type="checkbox" name="delete_id_list[]" value="' . (int)$attachments[$i]['attach_id'] . '" checked="checked" />';
break;
}
}
@ -422,9 +436,13 @@ if ($view === 'attachments') {
$row = DB()->sql_fetchrow($result);
DB()->sql_freeresult($result);
$post_title = str_short($row['topic_title'], 30);
$post_title = $row['topic_title'];
$view_topic = BB_ROOT . POST_URL . $ids[$j]['post_id'] . '#' . $ids[$j]['post_id'];
if (strlen($post_title) > 32) {
$post_title = str_short($post_title, 30);
}
$view_topic = BB_ROOT . 'viewtopic.php?' . POST_POST_URL . '=' . $ids[$j]['post_id'] . '#' . $ids[$j]['post_id'];
$post_titles[] = '<a href="' . $view_topic . '" class="gen" target="_blank">' . $post_title . '</a>';
} else {
@ -436,22 +454,22 @@ if ($view === 'attachments') {
$hidden_field = '<input type="hidden" name="attach_id_list[]" value="' . (int)$attachments[$i]['attach_id'] . '" />';
$template->assign_block_vars('attachrow', [
'ROW_NUMBER' => $i + (@$_GET['start'] + 1),
$template->assign_block_vars('attachrow', array(
'ROW_NUMBER' => $i + ($_GET['start'] + 1),
'ROW_CLASS' => $row_class,
'FILENAME' => htmlspecialchars($attachments[$i]['real_filename']),
'COMMENT' => htmlspecialchars($attachments[$i]['comment']),
'EXTENSION' => $attachments[$i]['extension'],
'SIZE' => humn_size($attachments[$i]['filesize'], 2),
'SIZE' => round($attachments[$i]['filesize'] / 1024, 2),
'DOWNLOAD_COUNT' => $attachments[$i]['download_count'],
'POST_TIME' => bb_date($attachments[$i]['filetime']),
'POST_TITLE' => $post_titles,
'S_DELETE_BOX' => $delete_box,
'S_HIDDEN' => $hidden_field,
'U_VIEW_ATTACHMENT' => BB_ROOT . DL_URL . $attachments[$i]['attach_id'],
]);
'U_VIEW_ATTACHMENT' => BB_ROOT . DOWNLOAD_URL . $attachments[$i]['attach_id'],
));
}
}
@ -470,8 +488,8 @@ if ($view === 'attachments') {
}
// Generate Pagination
if ($do_pagination && $total_rows > config()->get('topics_per_page')) {
generate_pagination('admin_attach_cp.php?view=' . $view . '&amp;mode=' . $mode . '&amp;order=' . $sort_order . '&amp;uid=' . $uid, $total_rows, config()->get('topics_per_page'), $start);
if ($do_pagination && $total_rows > $bb_cfg['topics_per_page']) {
generate_pagination('admin_attach_cp.php?view=' . $view . '&amp;mode=' . $mode . '&amp;order=' . $sort_order . '&amp;uid=' . $uid, $total_rows, $bb_cfg['topics_per_page'], $start);
}
print_page('admin_attach_cp.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -14,7 +30,6 @@ if (!empty($setmodules)) {
$module['ATTACHMENTS']['QUOTA_LIMITS'] = $filename . '?mode=quota';
return;
}
require __DIR__ . '/pagestart.php';
$error = false;
@ -34,9 +49,10 @@ $size = request_var('size', '');
$quota_size = request_var('quota_size', '');
$pm_size = request_var('pm_size', '');
$submit = isset($_POST['submit']);
$check_upload = isset($_POST['settings']);
$check_image_cat = isset($_POST['cat_settings']);
$submit = isset($_POST['submit']) ? true : false;
$check_upload = isset($_POST['settings']) ? true : false;
$check_image_cat = isset($_POST['cat_settings']) ? true : false;
$search_imagick = isset($_POST['search_imagick']) ? true : false;
// Re-evaluate the Attachment Configuration
$sql = 'SELECT * FROM ' . BB_ATTACH_CONFIG;
@ -103,12 +119,12 @@ while ($row = DB()->sql_fetchrow($result)) {
}
$sql = 'UPDATE ' . BB_ATTACH_CONFIG . "
SET config_value = '" . DB()->escape($new_attach[$config_name]) . "'
WHERE config_name = '" . DB()->escape($config_name) . "'";
SET config_value = '" . attach_mod_sql_escape($new_attach[$config_name]) . "'
WHERE config_name = '" . attach_mod_sql_escape($config_name) . "'";
} else {
$sql = 'UPDATE ' . BB_ATTACH_CONFIG . "
SET config_value = '" . DB()->escape($new_attach[$config_name]) . "'
WHERE config_name = '" . DB()->escape($config_name) . "'";
SET config_value = '" . attach_mod_sql_escape($new_attach[$config_name]) . "'
WHERE config_name = '" . attach_mod_sql_escape($config_name) . "'";
}
if (!DB()->sql_query($sql)) {
@ -129,10 +145,46 @@ $select_size_mode = size_select('size', $size);
$select_quota_size_mode = size_select('quota_size', $quota_size);
$select_pm_size_mode = size_select('pm_size', $pm_size);
// Search Imagick
if ($search_imagick) {
$imagick = '';
if (preg_match('/convert/i', $imagick)) {
return true;
} elseif ($imagick != 'none') {
if (!preg_match('/WIN/i', PHP_OS)) {
$retval = @exec('whereis convert');
$paths = explode(' ', $retval);
if (is_array($paths)) {
for ($i = 0, $iMax = count($paths); $i < $iMax; $i++) {
$path = basename($paths[$i]);
if ($path == 'convert') {
$imagick = $paths[$i];
}
}
}
} elseif (preg_match('/WIN/i', PHP_OS)) {
$path = 'c:/imagemagick/convert.exe';
if (!@file_exists(amod_realpath($path))) {
$imagick = $path;
}
}
}
if (!@file_exists(amod_realpath(trim($imagick)))) {
$new_attach['img_imagick'] = trim($imagick);
} else {
$new_attach['img_imagick'] = '';
}
}
// Check Settings
if ($check_upload) {
// Some tests...
$attach_config = [];
$attach_config = array();
$sql = 'SELECT * FROM ' . BB_ATTACH_CONFIG;
@ -157,7 +209,7 @@ if ($check_upload) {
$error = false;
// Does the target directory exist, is it a directory and writeable
if (!@file_exists(realpath($upload_dir))) {
if (!@file_exists(amod_realpath($upload_dir))) {
$error = true;
$error_msg = sprintf($lang['DIRECTORY_DOES_NOT_EXIST'], $attach_config['upload_dir']) . '<br />';
}
@ -168,7 +220,7 @@ if ($check_upload) {
}
if (!$error) {
if (!($fp = @fopen($upload_dir . '/0_000000.000', 'wb+'))) {
if (!($fp = @fopen($upload_dir . '/0_000000.000', 'wb'))) {
$error = true;
$error_msg = sprintf($lang['DIRECTORY_NOT_WRITEABLE'], $attach_config['upload_dir']) . '<br />';
} else {
@ -207,12 +259,12 @@ if ($mode == 'manage') {
'MAX_FILESIZE_PM' => $new_attach['max_filesize_pm'],
'MAX_ATTACHMENTS' => $new_attach['max_attachments'],
'MAX_ATTACHMENTS_PM' => $new_attach['max_attachments_pm'],
'DISABLE_MOD_YES' => $new_attach['disable_mod'] !== '0' ? 'checked' : '',
'DISABLE_MOD_NO' => $new_attach['disable_mod'] === '0' ? 'checked' : '',
'PM_ATTACH_YES' => $new_attach['allow_pm_attach'] !== '0' ? 'checked' : '',
'PM_ATTACH_NO' => $new_attach['allow_pm_attach'] === '0' ? 'checked' : '',
'DISPLAY_ORDER_ASC' => $new_attach['display_order'] !== '0' ? 'checked' : '',
'DISPLAY_ORDER_DESC' => $new_attach['display_order'] === '0' ? 'checked' : '',
'DISABLE_MOD_YES' => $new_attach['disable_mod'] !== '0' ? 'checked="checked"' : '',
'DISABLE_MOD_NO' => $new_attach['disable_mod'] === '0' ? 'checked="checked"' : '',
'PM_ATTACH_YES' => $new_attach['allow_pm_attach'] !== '0' ? 'checked="checked"' : '',
'PM_ATTACH_NO' => $new_attach['allow_pm_attach'] === '0' ? 'checked="checked"' : '',
'DISPLAY_ORDER_ASC' => $new_attach['display_order'] !== '0' ? 'checked="checked"' : '',
'DISPLAY_ORDER_DESC' => $new_attach['display_order'] === '0' ? 'checked="checked"' : '',
));
}
@ -227,7 +279,7 @@ if ($mode == 'cats') {
$sql = 'SELECT group_name, cat_id FROM ' . BB_EXTENSION_GROUPS . ' WHERE cat_id > 0 ORDER BY cat_id';
$s_assigned_group_images = [];
$s_assigned_group_images = array();
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not get group names from ' . BB_EXTENSION_GROUPS);
@ -242,17 +294,20 @@ if ($mode == 'cats') {
}
}
$display_inlined_yes = ($new_attach['img_display_inlined'] != '0') ? 'checked' : '';
$display_inlined_no = ($new_attach['img_display_inlined'] == '0') ? 'checked' : '';
$display_inlined_yes = ($new_attach['img_display_inlined'] != '0') ? 'checked="checked"' : '';
$display_inlined_no = ($new_attach['img_display_inlined'] == '0') ? 'checked="checked"' : '';
$create_thumbnail_yes = ($new_attach['img_create_thumbnail'] != '0') ? 'checked' : '';
$create_thumbnail_no = ($new_attach['img_create_thumbnail'] == '0') ? 'checked' : '';
$create_thumbnail_yes = ($new_attach['img_create_thumbnail'] != '0') ? 'checked="checked"' : '';
$create_thumbnail_no = ($new_attach['img_create_thumbnail'] == '0') ? 'checked="checked"' : '';
$use_gd2_yes = ($new_attach['use_gd2'] != '0') ? 'checked="checked"' : '';
$use_gd2_no = ($new_attach['use_gd2'] == '0') ? 'checked="checked"' : '';
// Check Thumbnail Support
if (!extension_loaded('gd')) {
if (!is_imagick() && !@extension_loaded('gd')) {
$new_attach['img_create_thumbnail'] = '0';
} else {
$template->assign_block_vars('switch_thumbnail_support', []);
$template->assign_block_vars('switch_thumbnail_support', array());
}
$template->assign_vars(array(
@ -262,10 +317,13 @@ if ($mode == 'cats') {
'IMAGE_LINK_HEIGHT' => $new_attach['img_link_height'],
'IMAGE_LINK_WIDTH' => $new_attach['img_link_width'],
'IMAGE_MIN_THUMB_FILESIZE' => $new_attach['img_min_thumb_filesize'],
'IMAGE_IMAGICK_PATH' => $new_attach['img_imagick'],
'DISPLAY_INLINED_YES' => $display_inlined_yes,
'DISPLAY_INLINED_NO' => $display_inlined_no,
'CREATE_THUMBNAIL_YES' => $create_thumbnail_yes,
'CREATE_THUMBNAIL_NO' => $create_thumbnail_no,
'USE_GD2_YES' => $use_gd2_yes,
'USE_GD2_NO' => $use_gd2_no,
'S_ASSIGNED_GROUP_IMAGES' => implode(', ', $s_assigned_group_images),
'S_ATTACH_ACTION' => 'admin_attachments.php?mode=cats',
));
@ -274,7 +332,7 @@ if ($mode == 'cats') {
// Check Cat Settings
if ($check_image_cat) {
// Some tests...
$attach_config = [];
$attach_config = array();
$sql = 'SELECT * FROM ' . BB_ATTACH_CONFIG;
@ -301,12 +359,11 @@ if ($check_image_cat) {
$error = false;
// Does the target directory exist, is it a directory and writeable
if (!@file_exists(realpath($upload_dir))) {
if (!bb_mkdir($upload_dir) && !is_dir($upload_dir)) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', $upload_dir));
}
if (!@file_exists(amod_realpath($upload_dir))) {
mkdir($upload_dir, 0755);
@chmod($upload_dir, 0777);
if (!@file_exists(realpath($upload_dir))) {
if (!@file_exists(amod_realpath($upload_dir))) {
$error = true;
$error_msg = sprintf($lang['DIRECTORY_DOES_NOT_EXIST'], $upload_dir) . '<br />';
}
@ -318,7 +375,7 @@ if ($check_image_cat) {
}
if (!$error) {
if (!($fp = @fopen($upload_dir . '/0_000000.000', 'wb+'))) {
if (!($fp = @fopen($upload_dir . '/0_000000.000', 'wb'))) {
$error = true;
$error_msg = sprintf($lang['DIRECTORY_NOT_WRITEABLE'], $upload_dir) . '<br />';
} else {
@ -340,13 +397,13 @@ if ($submit && $mode == 'quota') {
$filesize_list = get_var('max_filesize_list', array(0));
$size_select_list = get_var('size_select_list', array(''));
$allowed_list = [];
$allowed_list = array();
for ($i = 0, $iMax = count($quota_change_list); $i < $iMax; $i++) {
$filesize_list[$i] = ($size_select_list[$i] == 'kb') ? round($filesize_list[$i] * 1024) : (($size_select_list[$i] == 'mb') ? round($filesize_list[$i] * 1048576) : $filesize_list[$i]);
$sql = 'UPDATE ' . BB_QUOTA_LIMITS . "
SET quota_desc = '" . DB()->escape($quota_desc_list[$i]) . "', quota_limit = " . (int)$filesize_list[$i] . '
SET quota_desc = '" . attach_mod_sql_escape($quota_desc_list[$i]) . "', quota_limit = " . (int)$filesize_list[$i] . '
WHERE quota_limit_id = ' . (int)$quota_change_list[$i];
if (!DB()->sql_query($sql)) {
@ -378,7 +435,7 @@ if ($submit && $mode == 'quota') {
$quota_desc = get_var('quota_description', '');
$filesize = get_var('add_max_filesize', 0);
$size_select = get_var('add_size_select', '');
$add = isset($_POST['add_quota_check']);
$add = isset($_POST['add_quota_check']) ? true : false;
if ($quota_desc != '' && $add) {
// check Quota Description
@ -408,7 +465,7 @@ if ($submit && $mode == 'quota') {
$filesize = ($size_select == 'kb') ? round($filesize * 1024) : (($size_select == 'mb') ? round($filesize * 1048576) : $filesize);
$sql = 'INSERT INTO ' . BB_QUOTA_LIMITS . " (quota_desc, quota_limit)
VALUES ('" . DB()->escape($quota_desc) . "', " . (int)$filesize . ')';
VALUES ('" . attach_mod_sql_escape($quota_desc) . "', " . (int)$filesize . ')';
if (!DB()->sql_query($sql)) {
bb_die('Could not add quota limit');
@ -473,7 +530,7 @@ if ($mode == 'quota' && $e_mode == 'view_quota') {
bb_die('Invalid call');
}
$template->assign_block_vars('switch_quota_limit_desc', []);
$template->assign_block_vars('switch_quota_limit_desc', array());
$sql = 'SELECT * FROM ' . BB_QUOTA_LIMITS . ' WHERE quota_limit_id = ' . (int)$quota_id . ' LIMIT 1';

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -12,16 +28,16 @@ if (!empty($setmodules)) {
$module['MODS']['CONFIGURATION'] = basename(__FILE__) . '?mode=config_mods';
return;
}
require __DIR__ . '/pagestart.php';
require INC_DIR . '/functions_selects.php';
$mode = $_GET['mode'] ?? '';
$mode = isset($_GET['mode']) ? $_GET['mode'] : '';
$return_links = [
$return_links = array(
'index' => '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'),
'config' => '<br /><br />' . sprintf($lang['CLICK_RETURN_CONFIG'], '<a href="admin_board.php?mode=config">', '</a>'),
'config_mods' => '<br /><br />' . sprintf($lang['CLICK_RETURN_CONFIG_MODS'], '<a href="admin_board.php?mode=config_mods">', '</a>')
];
);
/**
* Pull all config data
@ -35,7 +51,7 @@ if (!$result = DB()->sql_query($sql)) {
$config_value = $row['config_value'];
$default_config[$config_name] = $config_value;
$new[$config_name] = $_POST[$config_name] ?? $default_config[$config_name];
$new[$config_name] = isset($_POST[$config_name]) ? $_POST[$config_name] : $default_config[$config_name];
if (isset($_POST['submit']) && $row['config_value'] != $new[$config_name]) {
if ($config_name == 'seed_bonus_points' ||
@ -45,7 +61,7 @@ if (!$result = DB()->sql_query($sql)) {
) {
$new[$config_name] = serialize(str_replace(',', '.', $new[$config_name]));
}
bb_update_config([$config_name => $new[$config_name]]);
bb_update_config(array($config_name => $new[$config_name]));
}
}
@ -56,12 +72,11 @@ if (!$result = DB()->sql_query($sql)) {
switch ($mode) {
case 'config_mods':
$template->assign_vars([
$template->assign_vars(array(
'S_CONFIG_ACTION' => 'admin_board.php?mode=config_mods',
'CONFIG_MODS' => true,
'MAGNET_LINKS_ENABLED' => $new['magnet_links_enabled'],
'MAGNET_LINKS_FOR_GUESTS' => $new['magnet_links_for_guests'],
'GENDER' => $new['gender'],
'CALLSEED' => $new['callseed'],
'TOR_STATS' => $new['tor_stats'],
@ -75,7 +90,6 @@ switch ($mode) {
'NETWORK_NEWS_FORUM_ID' => $new['network_news_forum_id'],
'WHOIS_INFO' => $new['whois_info'],
'SHOW_MOD_INDEX' => $new['show_mod_index'],
'SHOW_BOARD_START_INDEX' => $new['show_board_start_index'],
'BIRTHDAY_ENABLED' => $new['birthday_enabled'],
'BIRTHDAY_MAX_AGE' => $new['birthday_max_age'],
'BIRTHDAY_MIN_AGE' => $new['birthday_min_age'],
@ -84,8 +98,8 @@ switch ($mode) {
'TOR_COMMENT' => $new['tor_comment'],
'SEED_BONUS_ENABLED' => $new['seed_bonus_enabled'],
'SEED_BONUS_TOR_SIZE' => $new['seed_bonus_tor_size'],
'SEED_BONUS_USER_REGDATE' => $new['seed_bonus_user_regdate']
]);
'SEED_BONUS_USER_REGDATE' => $new['seed_bonus_user_regdate'],
));
if ($new['seed_bonus_points'] && $new['seed_bonus_release']) {
$seed_bonus = unserialize($new['seed_bonus_points']);
@ -96,10 +110,10 @@ switch ($mode) {
continue;
}
$template->assign_block_vars('seed_bonus', [
$template->assign_block_vars('seed_bonus', array(
'RELEASE' => $seed_release[$i],
'POINTS' => $row
]);
'POINTS' => $row,
));
}
}
@ -112,23 +126,23 @@ switch ($mode) {
continue;
}
$template->assign_block_vars('bonus_upload', [
$template->assign_block_vars('bonus_upload', array(
'UP' => $row,
'PRICE' => $price_row[$i]
]);
'PRICE' => $price_row[$i],
));
}
}
break;
default:
$template->assign_vars([
$template->assign_vars(array(
'S_CONFIG_ACTION' => 'admin_board.php?mode=config',
'CONFIG' => true,
'SITENAME' => htmlCHR($new['sitename']),
'CONFIG_SITE_DESCRIPTION' => htmlCHR($new['site_desc']),
'DISABLE_BOARD' => (bool)$new['board_disable'],
'ALLOW_AUTOLOGIN' => (bool)$new['allow_autologin'],
'DISABLE_BOARD' => $new['board_disable'] ? true : false,
'ALLOW_AUTOLOGIN' => $new['allow_autologin'] ? true : false,
'AUTOLOGIN_TIME' => (int)$new['max_autologin_time'],
'MAX_POLL_OPTIONS' => $new['max_poll_options'],
'FLOOD_INTERVAL' => $new['flood_interval'],
@ -136,18 +150,18 @@ switch ($mode) {
'POSTS_PER_PAGE' => $new['posts_per_page'],
'HOT_TOPIC' => $new['hot_threshold'],
'DEFAULT_DATEFORMAT' => $new['default_dateformat'],
'LANG_SELECT' => \TorrentPier\Legacy\Common\Select::language($new['default_lang'], 'default_lang'),
'TIMEZONE_SELECT' => \TorrentPier\Legacy\Common\Select::timezone($new['board_timezone'], 'board_timezone'),
'LANG_SELECT' => language_select($new['default_lang'], 'default_lang'),
'TIMEZONE_SELECT' => tz_select($new['board_timezone'], 'board_timezone'),
'MAX_LOGIN_ATTEMPTS' => $new['max_login_attempts'],
'LOGIN_RESET_TIME' => $new['login_reset_time'],
'PRUNE_ENABLE' => (bool)$new['prune_enable'],
'ALLOW_BBCODE' => (bool)$new['allow_bbcode'],
'ALLOW_SMILIES' => (bool)$new['allow_smilies'],
'ALLOW_SIG' => (bool)$new['allow_sig'],
'PRUNE_ENABLE' => $new['prune_enable'] ? true : false,
'ALLOW_BBCODE' => $new['allow_bbcode'] ? true : false,
'ALLOW_SMILIES' => $new['allow_smilies'] ? true : false,
'ALLOW_SIG' => $new['allow_sig'] ? true : false,
'SIG_SIZE' => $new['max_sig_chars'],
'ALLOW_NAMECHANGE' => (bool)$new['allow_namechange'],
'SMILIES_PATH' => $new['smilies_path']
]);
'ALLOW_NAMECHANGE' => $new['allow_namechange'] ? true : false,
'SMILIES_PATH' => $new['smilies_path'],
));
break;
}

View file

@ -1,22 +1,39 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module[APP_NAME]['FORUM_CONFIG'] = basename(__FILE__);
$module['TP']['FORUM_CONFIG'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
$max_forum_name_len = 30;
$max_forum_rows = 25;
require INC_DIR . '/functions_admin_torrent.php';
$submit = isset($_POST['submit']);
$confirm = isset($_POST['confirm']);
@ -26,7 +43,7 @@ $cfg = [];
* All config names with default values
*/
$default_cfg_str = array(
'bt_announce_url' => 'https://torrentpier.duckdns.org/bt/',
'bt_announce_url' => 'http://demo.torrentpier.me/bt/',
);
$default_cfg_bool = array(
@ -77,12 +94,12 @@ $cfg = bb_get_config(BB_CONFIG, true, false);
*/
if ($submit && $confirm) {
foreach ($db_fields_bool as $field_name => $field_def_val) {
\TorrentPier\Legacy\Admin\Torrent::update_table_bool(BB_FORUMS, 'forum_id', $field_name, $field_def_val);
update_table_bool(BB_FORUMS, 'forum_id', $field_name, $field_def_val);
}
\TorrentPier\Legacy\Admin\Torrent::update_config_table(BB_CONFIG, $default_cfg_str, $cfg, 'str');
\TorrentPier\Legacy\Admin\Torrent::update_config_table(BB_CONFIG, $default_cfg_bool, $cfg, 'bool');
\TorrentPier\Legacy\Admin\Torrent::update_config_table(BB_CONFIG, $default_cfg_num, $cfg, 'num');
update_config_table(BB_CONFIG, $default_cfg_str, $cfg, 'str');
update_config_table(BB_CONFIG, $default_cfg_bool, $cfg, 'bool');
update_config_table(BB_CONFIG, $default_cfg_num, $cfg, 'num');
$datastore->update('cat_forums');
@ -90,16 +107,16 @@ if ($submit && $confirm) {
}
// Set template vars
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars($default_cfg_str, $cfg);
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars_lang($default_cfg_str);
set_tpl_vars($default_cfg_str, $cfg);
set_tpl_vars_lang($default_cfg_str);
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars_bool($default_cfg_bool, $cfg);
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars_lang($default_cfg_bool);
set_tpl_vars_bool($default_cfg_bool, $cfg);
set_tpl_vars_lang($default_cfg_bool);
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars($default_cfg_num, $cfg);
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars_lang($default_cfg_num);
set_tpl_vars($default_cfg_num, $cfg);
set_tpl_vars_lang($default_cfg_num);
\TorrentPier\Legacy\Admin\Torrent::set_tpl_vars_lang($db_fields_bool);
set_tpl_vars_lang($db_fields_bool);
// Get Forums list
$sql = 'SELECT f.*
@ -121,7 +138,7 @@ foreach ($db_fields_bool as $field_name => $field_def_val) {
foreach ($rowset as $rid => $forum) {
foreach ($db_fields_bool as $field_name => $field_def_val) {
$forum_name = $forum['forum_name'];
$selected = $forum[$field_name] ? ' selected' : '';
$selected = $forum[$field_name] ? ' selected="selected"' : '';
$forum_name = str_short($forum_name, $max_forum_name_len);

View file

@ -0,0 +1,94 @@
<?php
/**
* MIT License
*
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
if (IS_SUPER_ADMIN) {
$module['TP']['TRACKER_CONFIG'] = basename(__FILE__);
}
return;
}
require __DIR__ . '/pagestart.php';
if (!IS_SUPER_ADMIN) {
bb_die($lang['NOT_ADMIN']);
}
require INC_DIR . '/functions_admin_torrent.php';
$submit = isset($_POST['submit']);
$confirmed = isset($_POST['confirm']);
/**
* All config names with default values
*/
$default_cfg_str = array(
'off_reason' => 'Tracker is disabled',
'browser_redirect_url' => 'http://demo.torrentpier.me/',
);
$default_cfg_bool = array(
'autoclean' => 1,
'off' => 0,
'compact_mode' => 1,
'update_dlstat' => 1,
'limit_active_tor' => 0,
'limit_concurrent_ips' => 0,
'retracker' => 1,
);
$default_cfg_num = array(
'numwant' => 50,
'expire_factor' => 4,
'limit_seed_count' => 20,
'limit_leech_count' => 4,
'leech_expire_factor' => 60,
'limit_seed_ips' => 0,
'limit_leech_ips' => 0,
);
/**
* Set template vars
*/
set_tpl_vars($default_cfg_str, $bb_cfg['tracker']);
set_tpl_vars_lang($default_cfg_str);
set_tpl_vars_bool($default_cfg_bool, $bb_cfg['tracker']);
set_tpl_vars_lang($default_cfg_bool);
set_tpl_vars($default_cfg_num, $bb_cfg['tracker']);
set_tpl_vars_lang($default_cfg_num);
$template->assign_vars(array(
'IGNORE_REPORTED_IP' => $bb_cfg['ignore_reported_ip'],
'ANNOUNCE_INTERVAL' => $bb_cfg['announce_interval'],
'PASSKEY_KEY' => $bb_cfg['passkey_key'],
'GOLD_SILVER_ENABLED' => $bb_cfg['tracker']['gold_silver_enabled'],
'DISABLE_SUBMIT' => true,
'S_HIDDEN_FIELDS' => '',
'S_CONFIG_ACTION' => 'admin_bt_tracker_cfg.php',
));
print_page('admin_bt_tracker_cfg.tpl', 'admin');

View file

@ -1,24 +1,40 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
if (IS_SUPER_ADMIN) {
$module[APP_NAME]['CRON'] = basename(__FILE__) . '?mode=list';
$module['TP']['CRON'] = basename(__FILE__) . '?mode=list';
}
return;
}
$mode = $_GET['mode'] ?? '';
$mode = isset($_GET['mode']) ? $_GET['mode'] : '';
$job_id = isset($_GET['id']) ? (int)$_GET['id'] : '';
$submit = isset($_POST['submit']);
$jobs = isset($_POST['select']) ? implode(',', $_POST['select']) : '';
$cron_action = $_POST['cron_action'] ?? '';
$cron_action = isset($_POST['cron_action']) ? $_POST['cron_action'] : '';
if ($mode == 'run' && !$job_id) {
define('BB_ROOT', './../');
@ -29,23 +45,21 @@ if ($mode == 'run' && !$job_id) {
require __DIR__ . '/pagestart.php';
}
// Check for demo mode
if (IN_DEMO_MODE && ($submit || !in_array($mode, ['add', 'list']))) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
if (!IS_SUPER_ADMIN) {
bb_die($lang['ONLY_FOR_SUPER_ADMIN']);
bb_die($lang['NOT_ADMIN']);
}
$sql = DB()->fetch_rowset('SELECT * FROM ' . BB_CONFIG . " WHERE config_name = 'cron_check_interval'");
require INC_DIR . '/functions_admin_torrent.php';
require INC_DIR . '/functions_admin_cron.php';
$sql = DB()->fetch_rowset('SELECT * FROM ' . BB_CONFIG . " WHERE config_name = 'cron_enabled' OR config_name = 'cron_check_interval'");
foreach ($sql as $row) {
$config_name = $row['config_name'];
$config_value = $row['config_value'];
$default_config[$config_name] = $config_value;
$new[$config_name] = $_POST[$config_name] ?? $default_config[$config_name];
$new[$config_name] = isset($_POST[$config_name]) ? $_POST[$config_name] : $default_config[$config_name];
if (isset($_POST['submit']) && $row['config_value'] != $new[$config_name]) {
bb_update_config(array($config_name => $new[$config_name]));
@ -53,7 +67,7 @@ foreach ($sql as $row) {
}
$template->assign_vars(array(
'CRON_ENABLED' => TorrentPier\Helpers\CronHelper::isEnabled(),
'CRON_ENABLED' => $new['cron_enabled'] ? true : false,
'CRON_CHECK_INTERVAL' => $new['cron_check_interval'],
));
@ -84,7 +98,7 @@ switch ($mode) {
));
//detect cron status
if (is_file(CRON_RUNNING)) {
if (file_exists('../triggers/cron_running')) {
$template->assign_vars(array(
'CRON_RUNNING' => true,
));
@ -92,14 +106,14 @@ switch ($mode) {
break;
case 'repair':
if (is_file(CRON_RUNNING)) {
rename(CRON_RUNNING, CRON_ALLOWED);
if (file_exists('../triggers/cron_running')) {
rename('../triggers/cron_running', '../triggers/cron_allowed');
}
redirect('admin/' . basename(__FILE__) . '?mode=list');
break;
case 'run':
\TorrentPier\Legacy\Admin\Cron::run_jobs($job_id);
run_jobs($job_id);
redirect('admin/' . basename(__FILE__) . '?mode=list');
break;
@ -153,7 +167,7 @@ switch ($mode) {
$run_day[$i] = $i;
}
$schedule = [];
$schedule = array();
foreach ($lang['SCHEDULE'] as $type => $key) {
$schedule[$key] = $type;
}
@ -164,14 +178,14 @@ switch ($mode) {
'S_MODE' => 'add',
'SCHEDULE' => build_select('schedule', $schedule, 'select', null, null),
'RUN_DAY' => build_select('run_day', $run_day, 0, null, null),
'CRON_ID' => '',
'CRON_ID' => 'none',
'CRON_ACTIVE' => 1,
'CRON_TITLE' => '',
'CRON_SCRIPT' => '',
'RUN_TIME' => '',
'RUN_ORDER' => 255,
'LAST_RUN' => '1900-01-01 00:00:00',
'NEXT_RUN' => '1900-01-01 00:00:00',
'LAST_RUN' => '0000-00-00 00:00:00',
'NEXT_RUN' => '0000-00-00 00:00:00',
'RUN_INTERVAL' => '',
'LOG_ENABLED' => 0,
'LOG_FILE' => '',
@ -182,34 +196,33 @@ switch ($mode) {
break;
case 'delete':
\TorrentPier\Legacy\Admin\Cron::delete_jobs($job_id);
delete_jobs($job_id);
bb_die($lang['JOB_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_JOBS'], '<a href="admin_cron.php?mode=list">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
break;
}
if ($submit) {
$mode2 = $_POST['mode'] ?? '';
if ($mode2 == 'list') {
if ($_POST['mode'] == 'list') {
if ($cron_action == 'run' && $jobs) {
\TorrentPier\Legacy\Admin\Cron::run_jobs($jobs);
run_jobs($jobs);
} elseif ($cron_action == 'delete' && $jobs) {
\TorrentPier\Legacy\Admin\Cron::delete_jobs($jobs);
delete_jobs($jobs);
} elseif (($cron_action == 'disable' || $cron_action == 'enable') && $jobs) {
\TorrentPier\Legacy\Admin\Cron::toggle_active($jobs, $cron_action);
toggle_active($jobs, $cron_action);
}
redirect('admin/' . basename(__FILE__) . '?mode=list');
} elseif (\TorrentPier\Legacy\Admin\Cron::validate_cron_post($_POST) == 1) {
if ($mode2 == 'edit') {
\TorrentPier\Legacy\Admin\Cron::update_cron_job($_POST);
} elseif ($mode2 == 'add') {
\TorrentPier\Legacy\Admin\Cron::insert_cron_job($_POST);
} elseif (validate_cron_post($_POST) == 1) {
if ($_POST['mode'] == 'edit') {
update_cron_job($_POST);
} elseif ($_POST['mode'] == 'add') {
insert_cron_job($_POST);
} else {
bb_die("Invalid mode: $mode2");
bb_die('Mode error');
}
redirect('admin/' . basename(__FILE__) . '?mode=list');
} else {
bb_die(\TorrentPier\Legacy\Admin\Cron::validate_cron_post($_POST));
bb_die(validate_cron_post($_POST));
}
}

View file

@ -1,28 +1,45 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['USERS']['DISALLOW'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
$message = '';
if (isset($_POST['add_name'])) {
include INC_DIR . '/functions_validate.php';
$disallowed_user = isset($_POST['disallowed_user']) ? trim($_POST['disallowed_user']) : trim($_GET['disallowed_user']);
if ($disallowed_user == '') {
bb_die($lang['FIELDS_EMPTY']);
}
if (\TorrentPier\Validate::username($disallowed_user)) {
if (!validate_username($disallowed_user)) {
$message = $lang['DISALLOWED_ALREADY'];
} else {
$sql = 'INSERT INTO ' . BB_DISALLOW . " (disallow_username) VALUES('" . DB()->escape($disallowed_user) . "')";
@ -39,17 +56,15 @@ if (isset($_POST['add_name'])) {
} elseif (isset($_POST['delete_name'])) {
$disallowed_id = isset($_POST['disallowed_id']) ? (int)$_POST['disallowed_id'] : (int)$_GET['disallowed_id'];
if (!empty($disallowed_id)) {
$sql = 'DELETE FROM ' . BB_DISALLOW . " WHERE disallow_id = $disallowed_id";
$result = DB()->sql_query($sql);
if (!$result) {
bb_die('Could not removed disallowed user');
}
$message .= $lang['DISALLOWED_DELETED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_DISALLOWADMIN'], '<a href="admin_disallow.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
$sql = 'DELETE FROM ' . BB_DISALLOW . " WHERE disallow_id = $disallowed_id";
$result = DB()->sql_query($sql);
if (!$result) {
bb_die('Could not removed disallowed user');
}
$message .= $lang['DISALLOWED_DELETED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_DISALLOWADMIN'], '<a href="admin_disallow.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
}
/**
@ -78,9 +93,9 @@ if (count($disallowed) <= 0) {
$disallow_select .= '</select>';
$template->assign_vars([
$template->assign_vars(array(
'S_DISALLOW_SELECT' => $disallow_select,
'S_FORM_ACTION' => 'admin_disallow.php',
]);
));
print_page('admin_disallow.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -43,17 +59,12 @@ $mode = get_var('mode', '');
$e_mode = get_var('e_mode', '');
$error = false;
$add_forum = isset($_POST['add_forum']);
$delete_forum = isset($_POST['del_forum']);
$submit = isset($_POST['submit']);
// Check for demo mode
if (IN_DEMO_MODE && ($submit || $add_forum || $delete_forum)) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
$add_forum = isset($_POST['add_forum']) ? true : false;
$delete_forum = isset($_POST['del_forum']) ? true : false;
$submit = isset($_POST['submit']) ? true : false;
// Get Attachment Config
$attach_config = [];
$attach_config = array();
$sql = 'SELECT * FROM ' . BB_ATTACH_CONFIG;
@ -74,7 +85,7 @@ if ($submit && $mode == 'extensions') {
$group_select_list = get_var('group_select', array(0));
// Generate correct Change List
$extensions = [];
$extensions = array();
for ($i = 0, $iMax = count($extension_change_list); $i < $iMax; $i++) {
$extensions['_' . $extension_change_list[$i]]['comment'] = $extension_explain_list[$i];
@ -98,7 +109,7 @@ if ($submit && $mode == 'extensions') {
'group_id' => (int)$extensions['_' . $extension_row[$i]['ext_id']]['group_id']
);
$sql = 'UPDATE ' . BB_EXTENSIONS . ' SET ' . DB()->build_array('UPDATE', $sql_ary) . '
$sql = 'UPDATE ' . BB_EXTENSIONS . ' SET ' . attach_mod_sql_build_array('UPDATE', $sql_ary) . '
WHERE ext_id = ' . (int)$extension_row[$i]['ext_id'];
if (!DB()->sql_query($sql)) {
@ -125,7 +136,7 @@ if ($submit && $mode == 'extensions') {
$extension = get_var('add_extension', '');
$extension_explain = get_var('add_extension_explain', '');
$extension_group = get_var('add_group_select', 0);
$add = isset($_POST['add_extension_check']);
$add = isset($_POST['add_extension_check']) ? true : false;
if ($extension != '' && $add) {
$template->assign_vars(array(
@ -164,7 +175,7 @@ if ($submit && $mode == 'extensions') {
'comment' => (string)$extension_explain
);
$sql = 'INSERT INTO ' . BB_EXTENSIONS . ' ' . DB()->build_array('INSERT', $sql_ary);
$sql = 'INSERT INTO ' . BB_EXTENSIONS . ' ' . attach_mod_sql_build_array('INSERT', $sql_ary);
if (!DB()->sql_query($sql)) {
bb_die('Could not add extension');
@ -207,7 +218,7 @@ if ($mode == 'extensions') {
DB()->sql_freeresult($result);
if ($num_extension_row > 0) {
$extension_row = sort_multi_array($extension_row, 'group_id');
$extension_row = sort_multi_array($extension_row, 'group_name', 'ASC');
for ($i = 0; $i < $num_extension_row; $i++) {
if ($submit) {
@ -241,11 +252,11 @@ if ($submit && $mode == 'groups') {
$filesize_list = get_var('max_filesize_list', array(0));
$size_select_list = get_var('size_select_list', array(''));
$allowed_list = [];
$allowed_list = array();
foreach ($group_allowed_list as $iValue) {
for ($i = 0, $iMax = count($group_allowed_list); $i < $iMax; $i++) {
for ($j = 0, $jMax = count($group_change_list); $j < $jMax; $j++) {
if ($iValue == $group_change_list[$j]) {
if ($group_allowed_list[$i] == $group_change_list[$j]) {
$allowed_list[$j] = 1;
}
}
@ -265,7 +276,7 @@ if ($submit && $mode == 'groups') {
'max_filesize' => (int)$filesize_list[$i]
);
$sql = 'UPDATE ' . BB_EXTENSION_GROUPS . ' SET ' . DB()->build_array('UPDATE', $sql_ary) . '
$sql = 'UPDATE ' . BB_EXTENSION_GROUPS . ' SET ' . attach_mod_sql_build_array('UPDATE', $sql_ary) . '
WHERE group_id = ' . (int)$group_change_list[$i];
if (!DB()->sql_query($sql)) {
@ -306,7 +317,7 @@ if ($submit && $mode == 'groups') {
$size_select = get_var('add_size_select', '');
$is_allowed = isset($_POST['add_allowed']) ? 1 : 0;
$add = isset($_POST['add_extension_group_check']);
$add = isset($_POST['add_extension_group_check']) ? true : false;
if ($extension_group != '' && $add) {
// check Extension Group
@ -345,7 +356,7 @@ if ($submit && $mode == 'groups') {
'forum_permissions' => ''
);
$sql = 'INSERT INTO ' . BB_EXTENSION_GROUPS . ' ' . DB()->build_array('INSERT', $sql_ary);
$sql = 'INSERT INTO ' . BB_EXTENSION_GROUPS . ' ' . attach_mod_sql_build_array('INSERT', $sql_ary);
if (!DB()->sql_query($sql)) {
bb_die('Could not add extension group');
@ -376,7 +387,7 @@ if ($mode == 'groups') {
$template->assign_vars(array(
'TPL_ATTACH_EXTENSION_GROUPS' => true,
'ADD_GROUP_NAME' => $extension_group ?? '',
'ADD_GROUP_NAME' => isset($extension_group) ? $extension_group : '',
'MAX_FILESIZE' => $max_add_filesize,
'S_FILESIZE' => size_select('add_size_select', $size),
'S_ADD_DOWNLOAD_MODE' => download_select('add_download_mode'),
@ -409,7 +420,7 @@ if ($mode == 'groups') {
$extension_group[$i]['max_filesize'] = round($extension_group[$i]['max_filesize'] / 1024 * 100) / 100;
}
$s_allowed = ($extension_group[$i]['allow_group'] == 1) ? 'checked' : '';
$s_allowed = ($extension_group[$i]['allow_group'] == 1) ? 'checked="checked"' : '';
$template->assign_block_vars('grouprow', array(
'GROUP_ID' => $extension_group[$i]['group_id'],
@ -461,8 +472,8 @@ if ($add_forum && $e_mode == 'perm' && $group) {
$add_forums_list = get_var('entries', array(0));
$add_all_forums = false;
foreach ($add_forums_list as $iValue) {
if ($iValue == 0) {
for ($i = 0, $iMax = count($add_forums_list); $i < $iMax; $i++) {
if ($add_forums_list[$i] == 0) {
$add_all_forums = true;
}
}
@ -490,21 +501,21 @@ if ($add_forum && $e_mode == 'perm' && $group) {
DB()->sql_freeresult($result);
if (trim($row['forum_permissions']) == '') {
$auth_p = [];
$auth_p = array();
} else {
$auth_p = auth_unpack($row['forum_permissions']);
}
// Generate array for Auth_Pack, do not add doubled forums
foreach ($add_forums_list as $i => $iValue) {
for ($i = 0, $iMax = count($add_forums_list); $i < $iMax; $i++) {
if (!in_array($add_forums_list[$i], $auth_p)) {
$auth_p[] = $iValue;
$auth_p[] = $add_forums_list[$i];
}
}
$auth_bitstream = auth_pack($auth_p);
$sql = 'UPDATE ' . BB_EXTENSION_GROUPS . " SET forum_permissions = '" . DB()->escape($auth_bitstream) . "' WHERE group_id = " . (int)$group;
$sql = 'UPDATE ' . BB_EXTENSION_GROUPS . " SET forum_permissions = '" . attach_mod_sql_escape($auth_bitstream) . "' WHERE group_id = " . (int)$group;
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not update permissions #2');
@ -530,18 +541,18 @@ if ($delete_forum && $e_mode == 'perm' && $group) {
DB()->sql_freeresult($result);
$auth_p2 = auth_unpack(trim($row['forum_permissions']));
$auth_p = [];
$auth_p = array();
// Generate array for Auth_Pack, delete the chosen ones
foreach ($auth_p2 as $i => $iValue) {
for ($i = 0, $iMax = count($auth_p2); $i < $iMax; $i++) {
if (!in_array($auth_p2[$i], $delete_forums_list)) {
$auth_p[] = $iValue;
$auth_p[] = $auth_p2[$i];
}
}
$auth_bitstream = (count($auth_p) > 0) ? auth_pack($auth_p) : '';
$sql = 'UPDATE ' . BB_EXTENSION_GROUPS . " SET forum_permissions = '" . DB()->escape($auth_bitstream) . "' WHERE group_id = " . (int)$group;
$sql = 'UPDATE ' . BB_EXTENSION_GROUPS . " SET forum_permissions = '" . attach_mod_sql_escape($auth_bitstream) . "' WHERE group_id = " . (int)$group;
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not update permissions #3');
@ -565,13 +576,13 @@ if ($e_mode == 'perm' && $group) {
$group_name = $row['group_name'];
$allowed_forums = trim($row['forum_permissions']);
$forum_perm = [];
$forum_perm = array();
if ($allowed_forums == '') {
$forum_perm[0]['forum_id'] = 0;
$forum_perm[0]['forum_name'] = $lang['PERM_ALL_FORUMS'];
} else {
$forum_p = [];
$forum_p = array();
$act_id = 0;
$forum_p = auth_unpack($allowed_forums);
$sql = 'SELECT forum_id, forum_name FROM ' . BB_FORUMS . ' WHERE forum_id IN (' . implode(', ', $forum_p) . ')';
@ -619,7 +630,7 @@ if ($e_mode == 'perm' && $group) {
);
}
$empty_perm_forums = [];
$empty_perm_forums = array();
$sql = 'SELECT forum_id, forum_name FROM ' . BB_FORUMS . ' WHERE auth_attachments < ' . AUTH_ADMIN;

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -20,7 +36,7 @@ $prune_performed = false;
if (isset($_REQUEST['submit'])) {
if (!$var =& $_REQUEST['f'] or !$f_selected = get_id_ary($var)) {
bb_die($lang['SELECT_FORUM']);
bb_die('Forum not selected');
}
if (!$var =& $_REQUEST['prunedays'] or !$prunedays = abs((int)$var)) {
bb_die($lang['NOT_DAYS']);
@ -34,15 +50,15 @@ if (isset($_REQUEST['submit'])) {
$sql = 'SELECT forum_id, forum_name FROM ' . BB_FORUMS . " $where_sql";
foreach (DB()->fetch_rowset($sql) as $i => $row) {
$pruned_topics = \TorrentPier\Legacy\Admin\Common::topic_delete('prune', $row['forum_id'], $prunetime, !empty($_POST['prune_all_topic_types']));
$pruned_topics = topic_delete('prune', $row['forum_id'], $prunetime, !empty($_POST['prune_all_topic_types']));
$pruned_total += $pruned_topics;
$prune_performed = true;
$template->assign_block_vars('pruned', [
$template->assign_block_vars('pruned', array(
'ROW_CLASS' => !($i % 2) ? 'row1' : 'row2',
'FORUM_NAME' => htmlCHR($row['forum_name']),
'PRUNED_TOPICS' => $pruned_topics
]);
'PRUNED_TOPICS' => $pruned_topics,
));
}
if (!$prune_performed) {
bb_die($lang['NONE_SELECTED']);
@ -52,10 +68,10 @@ if (isset($_REQUEST['submit'])) {
}
}
$template->assign_vars([
$template->assign_vars(array(
'PRUNED_TOTAL' => $pruned_total,
'S_PRUNE_ACTION' => basename(__FILE__),
'SEL_FORUM' => get_forum_select('admin', 'f[]', null, 65, 16, '', $all_forums)
]);
'SEL_FORUM' => get_forum_select('admin', 'f[]', null, 65, 16, '', $all_forums),
));
print_page('admin_forum_prune.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -72,17 +88,10 @@ if (isset($_GET['adv'])) {
unset($adv);
}
$submit = isset($_POST['submit']);
// Check for demo mode
if (IN_DEMO_MODE && $submit) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
/**
* Start program proper
*/
if ($submit) {
if (isset($_POST['submit'])) {
$sql = '';
if (!empty($forum_id)) {
@ -123,7 +132,6 @@ if ($submit) {
}
$datastore->update('cat_forums');
CACHE('bb_cache')->rm();
bb_die($lang['FORUM_AUTH_UPDATED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_FORUMAUTH'], '<a href="' . 'admin_forumauth.php' . '">', '</a>'));
}
@ -134,11 +142,11 @@ $forum_rows = DB()->fetch_rowset('SELECT * FROM ' . BB_FORUMS . " $forum_sql");
if (empty($forum_id)) {
// Output the selection table if no forum id was specified
$template->assign_vars([
$template->assign_vars(array(
'TPL_AUTH_SELECT_FORUM' => true,
'S_AUTH_ACTION' => 'admin_forumauth.php',
'S_AUTH_SELECT' => get_forum_select('admin', 'f', null, 80),
]);
));
} else {
// Output the authorisation details if an id was specified
$forum_name = reset($forum_rows)['forum_name'];
@ -173,16 +181,16 @@ if (empty($forum_id)) {
$simple_auth = '<select name="simpleauth">';
for ($j = 0, $jMax = count($simple_auth_types); $j < $jMax; $j++) {
$selected = ($matched_type == $j) ? ' selected' : '';
$selected = ($matched_type == $j) ? ' selected="selected"' : '';
$simple_auth .= '<option value="' . $j . '"' . $selected . '>' . $simple_auth_types[$j] . '</option>';
}
$simple_auth .= '</select>';
$template->assign_block_vars('forum_auth', [
$template->assign_block_vars('forum_auth', array(
'CELL_TITLE' => $lang['SIMPLE_MODE'],
'S_AUTH_LEVELS_SELECT' => $simple_auth,
]);
));
$s_column_span++;
} else {
@ -191,38 +199,37 @@ if (empty($forum_id)) {
$custom_auth[$j] = '&nbsp;<select name="' . $forum_auth_fields[$j] . '">';
for ($k = 0, $kMax = count($forum_auth_levels); $k < $kMax; $k++) {
$selected = ($forum_rows[0][$forum_auth_fields[$j]] == $forum_auth_const[$k]) ? ' selected' : '';
$selected = ($forum_rows[0][$forum_auth_fields[$j]] == $forum_auth_const[$k]) ? ' selected="selected"' : '';
$custom_auth[$j] .= '<option value="' . $forum_auth_const[$k] . '"' . $selected . '>' . $lang['FORUM_' . strtoupper($forum_auth_levels[$k])] . '</OPTION>';
}
$custom_auth[$j] .= '</select>&nbsp;';
$cell_title = $field_names[$forum_auth_fields[$j]];
$template->assign_block_vars('forum_auth', [
$template->assign_block_vars('forum_auth', array(
'CELL_TITLE' => $cell_title,
'S_AUTH_LEVELS_SELECT' => $custom_auth[$j],
]);
));
$s_column_span++;
}
}
$adv_mode = empty($adv) ? '1' : '0';
$switch_mode = "admin_forumauth.php?" . POST_FORUM_URL . "=$forum_id&amp;adv=$adv_mode";
$switch_mode = "admin_forumauth.php?f=$forum_id&amp;adv=$adv_mode";
$switch_mode_text = empty($adv) ? $lang['ADVANCED_MODE'] : $lang['SIMPLE_MODE'];
$u_switch_mode = '<a href="' . $switch_mode . '">' . $switch_mode_text . '</a>';
$s_hidden_fields = '<input type="hidden" name="' . POST_FORUM_URL . '" value="' . $forum_id . '">';
$template->assign_vars([
$template->assign_vars(array(
'TPL_EDIT_FORUM_AUTH' => true,
'FORUM_NAME' => htmlCHR($forum_name),
'U_VIEWFORUM' => BB_ROOT . FORUM_URL . $forum_id,
'U_SWITCH_MODE' => $u_switch_mode,
'S_FORUMAUTH_ACTION' => 'admin_forumauth.php',
'S_COLUMN_SPAN' => $s_column_span,
'S_HIDDEN_FIELDS' => $s_hidden_fields,
]);
));
}
print_page('admin_forumauth.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -80,17 +96,10 @@ if (isset($_GET['adv'])) {
unset($adv);
}
$submit = isset($_POST['submit']);
// Check for demo mode
if (IN_DEMO_MODE && $submit) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
/**
* Start program proper
*/
if ($submit) {
//
// Start program proper
//
if (isset($_POST['submit'])) {
$sql = '';
if (!empty($forum_id)) {
@ -153,7 +162,6 @@ if ($submit) {
}
$datastore->update('cat_forums');
CACHE('bb_cache')->rm();
bb_die($lang['FORUM_AUTH_UPDATED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_FORUMAUTH'], '<a href="admin_forumauth_list.php">', '</a>'));
} // End of submit
@ -300,7 +308,7 @@ if (empty($forum_id) && empty($cat_id)) {
$custom_auth[$j] = '<select name="' . $forum_auth_fields[$j] . '">';
for ($k = 0, $kMax = count($forum_auth_levels); $k < $kMax; $k++) {
$selected = (!empty($forum_rows) && $forum_rows[0][$forum_auth_fields[$j]] == $forum_auth_const[$k]) ? ' selected' : '';
$selected = (!empty($forum_rows) && $forum_rows[0][$forum_auth_fields[$j]] == $forum_auth_const[$k]) ? ' selected="selected"' : '';
$custom_auth[$j] .= '<option value="' . $forum_auth_const[$k] . '"' . $selected . '>' . $lang['FORUM_' . $forum_auth_levels[$k]] . '</option>';
}
$custom_auth[$j] .= '</select>';

View file

@ -1,18 +1,34 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['FORUMS']['MANAGE'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
require INC_DIR . '/functions_group.php';
array_deep($_POST, 'trim');
@ -46,18 +62,13 @@ $forumname = '';
if (isset($_REQUEST['addforum']) || isset($_REQUEST['addcategory'])) {
$mode = isset($_REQUEST['addforum']) ? 'addforum' : 'addcat';
if (isset($_POST['addforum'], $_POST['forumname']) && $mode == 'addforum' && is_array($_POST['addforum'])) {
if ($mode == 'addforum' && isset($_POST['addforum']) && isset($_POST['forumname']) && is_array($_POST['addforum'])) {
$req_cat_id = array_keys($_POST['addforum']);
$cat_id = reset($req_cat_id);
$forumname = stripslashes($_POST['forumname'][$cat_id]);
}
}
// Check for demo mode
if (IN_DEMO_MODE && in_array($mode, ['deletecat', 'deleteforum'])) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
$show_main_page = false;
if ($mode) {
@ -115,14 +126,14 @@ if ($mode) {
if ($parent = get_forum_data($forum_parent)) {
$cat_id = $parent['cat_id'];
}
} elseif (isset($_REQUEST[POST_CAT_URL])) {
$cat_id = (int)$_REQUEST[POST_CAT_URL];
} elseif (isset($_REQUEST['c'])) {
$cat_id = (int)$_REQUEST['c'];
}
$catlist = get_list('category', $cat_id, true);
$forumlocked = $forumunlocked = '';
$forumstatus == FORUM_LOCKED ? $forumlocked = 'selected' : $forumunlocked = 'selected';
$forumstatus == FORUM_LOCKED ? $forumlocked = 'selected="selected"' : $forumunlocked = 'selected="selected"';
$statuslist = '<option value="' . FORUM_UNLOCKED . '" ' . $forumunlocked . '>' . $lang['STATUS_UNLOCKED'] . '</option>\n';
$statuslist .= '<option value="' . FORUM_LOCKED . '" ' . $forumlocked . '>' . $lang['STATUS_LOCKED'] . '</option>\n';
@ -223,7 +234,7 @@ if ($mode) {
$datastore->update('cat_forums');
CACHE('bb_cache')->rm();
bb_die($lang['FORUMS_UPDATED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_FORUMADMIN'], '<a href="admin_forums.php?' . POST_CAT_URL . '=' . $cat_id . '">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
bb_die($lang['FORUMS_UPDATED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_FORUMADMIN'], '<a href="admin_forums.php?c=' . $cat_id . '">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
break;
@ -312,7 +323,7 @@ if ($mode) {
$message = $lang['FORUMS_UPDATED'] . '<br /><br />';
$message .= $fix ? "$fix<br /><br />" : '';
$message .= sprintf($lang['CLICK_RETURN_FORUMADMIN'], '<a href="admin_forums.php?' . POST_CAT_URL . '=' . $cat_id . '">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
$message .= sprintf($lang['CLICK_RETURN_FORUMADMIN'], '<a href="admin_forums.php?c=' . $cat_id . '">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
break;
@ -322,7 +333,7 @@ if ($mode) {
// Create a category in the DB
//
if (!$new_cat_title = trim($_POST['categoryname'])) {
bb_die($lang['CATEGORY_NAME_EMPTY']);
bb_die('Category name is empty');
}
check_name_dup('cat', $new_cat_title);
@ -347,12 +358,12 @@ if ($mode) {
//
// Show form to edit a category
//
$cat_id = (int)$_GET[POST_CAT_URL];
$cat_id = (int)$_GET['c'];
$cat_info = get_info('category', $cat_id);
$hidden_fields = array(
'mode' => 'modcat',
POST_CAT_URL => $cat_id,
'c' => $cat_id,
);
$template->assign_vars(array(
@ -370,10 +381,10 @@ if ($mode) {
// Modify a category in the DB
//
if (!$new_cat_title = trim($_POST['cat_title'])) {
bb_die($lang['CATEGORY_NAME_EMPTY']);
bb_die('Category name is empty');
}
$cat_id = (int)$_POST[POST_CAT_URL];
$cat_id = (int)$_POST['c'];
$row = get_info('category', $cat_id);
$cur_cat_title = $row['cat_title'];
@ -401,7 +412,7 @@ if ($mode) {
//
// Show form to delete a forum
//
$forum_id = (int)$_GET[POST_FORUM_URL];
$forum_id = (int)$_GET['f'];
$move_to_options = '<option value="-1">' . $lang['DELETE_ALL_POSTS'] . '</option>';
$move_to_options .= sf_get_list('forum', $forum_id, 0);
@ -437,8 +448,7 @@ if ($mode) {
if ($to_id == -1) {
// Delete everything from forum
\TorrentPier\Legacy\Admin\Common::topic_delete('prune', $from_id, 0, true);
$datastore->update('stats');
topic_delete('prune', $from_id, 0, true);
} else {
// Move all posts
$sql = 'SELECT * FROM ' . BB_FORUMS . " WHERE forum_id IN($from_id, $to_id)";
@ -467,7 +477,7 @@ if ($mode) {
$start_id += $per_cycle;
}
\TorrentPier\Legacy\Admin\Common::sync('forum', $to_id);
sync('forum', $to_id);
}
DB()->query('DELETE FROM ' . BB_FORUMS . " WHERE forum_id = $from_id");
@ -476,7 +486,7 @@ if ($mode) {
$cat_forums = get_cat_forums();
fix_orphan_sf();
\TorrentPier\Legacy\Group::update_user_level('all');
update_user_level('all');
$datastore->update('cat_forums');
CACHE('bb_cache')->rm();
@ -486,7 +496,7 @@ if ($mode) {
case 'deletecat':
// Show form to delete a category
$cat_id = (int)$_GET[POST_CAT_URL];
$cat_id = (int)$_GET['c'];
$catinfo = get_info('category', $cat_id);
$categories_count = $catinfo['number'];
@ -523,11 +533,7 @@ if ($mode) {
case 'movedelcat':
// Move or delete a category in the DB
$from_id = (int)$_POST['from_id'];
$to_id = (int)$_POST['to_id'] ?? -1;
if ($to_id === -1) {
bb_die($lang['NOWHERE_TO_MOVE']);
}
$to_id = (int)$_POST['to_id'];
if ($from_id == $to_id || !cat_exists($from_id) || !cat_exists($to_id)) {
bb_die('Bad input');
@ -570,17 +576,15 @@ if ($mode) {
$move_down_forum_id = false;
$forums = $cat_forums[$cat_id]['f_ord'];
$forum_order = $forum_info['forum_order'];
$prev_forum = $forums[$forum_order - 10] ?? false;
$next_forum = $forums[$forum_order + 10] ?? false;
$prev_forum = isset($forums[$forum_order - 10]) ? $forums[$forum_order - 10] : false;
$next_forum = isset($forums[$forum_order + 10]) ? $forums[$forum_order + 10] : false;
// move selected forum ($forum_id) UP
if ($move < 0 && $prev_forum) {
if ($forum_info['forum_parent'] && $prev_forum['forum_parent'] != $forum_info['forum_parent']) {
$show_main_page = true;
break;
}
if ($move_down_forum_id = get_prev_root_forum_id($forums, $forum_order)) {
} elseif ($move_down_forum_id = get_prev_root_forum_id($forums, $forum_order)) {
$move_up_forum_id = $forum_id;
$move_down_ord_val = (get_sf_count($forum_id) + 1) * 10;
$move_up_ord_val = ((get_sf_count($move_down_forum_id) + 1) * 10) + $move_down_ord_val;
@ -591,9 +595,7 @@ if ($mode) {
if ($forum_info['forum_parent'] && $next_forum['forum_parent'] != $forum_info['forum_parent']) {
$show_main_page = true;
break;
}
if ($move_up_forum_id = get_next_root_forum_id($forums, $forum_order)) {
} elseif ($move_up_forum_id = get_next_root_forum_id($forums, $forum_order)) {
$move_down_forum_id = $forum_id;
$move_down_forum_order = $forum_order;
$move_down_ord_val = (get_sf_count($move_up_forum_id) + 1) * 10;
@ -634,7 +636,7 @@ if ($mode) {
case 'cat_order':
$move = (int)$_GET['move'];
$cat_id = (int)$_GET[POST_CAT_URL];
$cat_id = (int)$_GET['c'];
DB()->query('
UPDATE ' . BB_CATEGORIES . " SET
@ -650,7 +652,7 @@ if ($mode) {
break;
case 'forum_sync':
\TorrentPier\Legacy\Admin\Common::sync('forum', (int)$_GET[POST_FORUM_URL]);
sync('forum', (int)$_GET['f']);
$datastore->update('cat_forums');
CACHE('bb_cache')->rm();
@ -659,6 +661,7 @@ if ($mode) {
default:
bb_die($lang['NO_MODE']);
break;
}
}
@ -681,7 +684,7 @@ if (!$mode || $show_main_page) {
$where_cat_sql = $req_cat_id = '';
if ($c =& $_REQUEST[POST_CAT_URL]) {
if ($c =& $_REQUEST['c']) {
if ($c !== 'all') {
$req_cat_id = (int)$c;
$where_cat_sql = "WHERE cat_id = $req_cat_id";
@ -702,14 +705,14 @@ if (!$mode || $show_main_page) {
}
// Okay, let's build the index
$gen_cat = [];
$gen_cat = array();
$bgr_class_1 = 'prow1';
$bgr_class_2 = 'prow2';
$bgr_class_over = 'prow3';
$template->assign_vars(array(
'U_ALL_FORUMS' => 'admin_forums.php?' . POST_CAT_URL . '=all',
'U_ALL_FORUMS' => 'admin_forums.php?c=all',
'FORUMS_COUNT' => $total_forums,
));
@ -723,12 +726,12 @@ if (!$mode || $show_main_page) {
'CAT_ID' => $cat_id,
'CAT_DESC' => htmlCHR($category_rows[$i]['cat_title']),
'U_CAT_EDIT' => "admin_forums.php?mode=editcat&amp;" . POST_CAT_URL . "=$cat_id",
'U_CAT_DELETE' => "admin_forums.php?mode=deletecat&amp;" . POST_CAT_URL . "=$cat_id",
'U_CAT_MOVE_UP' => "admin_forums.php?mode=cat_order&amp;move=-15&amp;" . POST_CAT_URL . "=$cat_id",
'U_CAT_MOVE_DOWN' => "admin_forums.php?mode=cat_order&amp;move=15&amp;" . POST_CAT_URL . "=$cat_id",
'U_VIEWCAT' => "admin_forums.php?" . POST_CAT_URL . "=$cat_id",
'U_CREATE_FORUM' => "admin_forums.php?mode=addforum&amp;" . POST_CAT_URL . "=$cat_id",
'U_CAT_EDIT' => "admin_forums.php?mode=editcat&amp;c=$cat_id",
'U_CAT_DELETE' => "admin_forums.php?mode=deletecat&amp;c=$cat_id",
'U_CAT_MOVE_UP' => "admin_forums.php?mode=cat_order&amp;move=-15&amp;c=$cat_id",
'U_CAT_MOVE_DOWN' => "admin_forums.php?mode=cat_order&amp;move=15&amp;c=$cat_id",
'U_VIEWCAT' => "admin_forums.php?c=$cat_id",
'U_CREATE_FORUM' => "admin_forums.php?mode=addforum&amp;c=$cat_id",
));
for ($j = 0; $j < $total_forums; $j++) {
@ -743,7 +746,7 @@ if (!$mode || $show_main_page) {
'FORUM_DESC' => htmlCHR($forum_rows[$j]['forum_desc']),
'NUM_TOPICS' => $forum_rows[$j]['forum_topics'],
'NUM_POSTS' => $forum_rows[$j]['forum_posts'],
'PRUNE_DAYS' => !empty($forum_rows[$j]['prune_days']) ? delta_time((TIMENOW - 86400 * $forum_rows[$j]['prune_days']), TIMENOW, 'days') : $lang['DISABLED'],
'PRUNE_DAYS' => $forum_rows[$j]['prune_days'] ?: '-',
'ORDER' => $forum_rows[$j]['forum_order'],
'FORUM_ID' => $forum_rows[$j]['forum_id'],
@ -753,19 +756,19 @@ if (!$mode || $show_main_page) {
'FORUM_PARENT' => $forum_rows[$j]['forum_parent'],
'SF_PAD' => $forum_rows[$j]['forum_parent'] ? ' style="padding-left: 20px;" ' : '',
'FORUM_NAME_CLASS' => $forum_rows[$j]['forum_parent'] ? 'genmed' : 'gen',
'ADD_SUB_HREF' => !$forum_rows[$j]['forum_parent'] ? "admin_forums.php?mode=addforum&amp;forum_parent={$forum_rows[$j]['forum_id']}" : '',
'U_VIEWFORUM' => BB_ROOT . FORUM_URL . $forum_id,
'U_FORUM_EDIT' => "admin_forums.php?mode=editforum&amp;" . POST_FORUM_URL . "=$forum_id",
'U_FORUM_PERM' => "admin_forumauth.php?" . POST_FORUM_URL . "=$forum_id",
'U_FORUM_DELETE' => "admin_forums.php?mode=deleteforum&amp;" . POST_FORUM_URL . "=$forum_id",
'U_FORUM_MOVE_UP' => "admin_forums.php?mode=forum_order&amp;move=-15&amp;" . POST_FORUM_URL . "=$forum_id&amp;" . POST_CAT_URL . "=$req_cat_id",
'U_FORUM_MOVE_DOWN' => "admin_forums.php?mode=forum_order&amp;move=15&amp;" . POST_FORUM_URL . "=$forum_id&amp;" . POST_CAT_URL . "=$req_cat_id",
'U_FORUM_RESYNC' => "admin_forums.php?mode=forum_sync&amp;" . POST_FORUM_URL . "=$forum_id",
'ADD_SUB_HREF' => "admin_forums.php?mode=addforum&amp;forum_parent={$forum_rows[$j]['forum_id']}",
'U_VIEWFORUM' => BB_ROOT . "viewforum.php?f=$forum_id",
'U_FORUM_EDIT' => "admin_forums.php?mode=editforum&amp;f=$forum_id",
'U_FORUM_PERM' => "admin_forumauth.php?f=$forum_id",
'U_FORUM_DELETE' => "admin_forums.php?mode=deleteforum&amp;f=$forum_id",
'U_FORUM_MOVE_UP' => "admin_forums.php?mode=forum_order&amp;move=-15&amp;f=$forum_id&amp;c=$req_cat_id",
'U_FORUM_MOVE_DOWN' => "admin_forums.php?mode=forum_order&amp;move=15&amp;f=$forum_id&amp;c=$req_cat_id",
'U_FORUM_RESYNC' => "admin_forums.php?mode=forum_sync&amp;f=$forum_id",
));
}
}
}
}
}// if ... forumid == catid
} // for ... forums
} // for ... categories
}// if ... total_categories
}
print_page('admin_forums.tpl', 'admin');
@ -777,8 +780,6 @@ print_page('admin_forums.tpl', 'admin');
*/
function get_info($mode, $id)
{
$table = null;
$idfield = null;
switch ($mode) {
case 'category':
$table = BB_CATEGORIES;
@ -824,10 +825,6 @@ function get_info($mode, $id)
*/
function get_list($mode, $id, $select)
{
$table = null;
$idfield = null;
$order = null;
$namefield = null;
switch ($mode) {
case 'category':
$table = BB_CATEGORIES;
@ -863,9 +860,9 @@ function get_list($mode, $id, $select)
while ($row = DB()->sql_fetchrow($result)) {
$s = '';
if ($row[$idfield] == $id) {
$s = ' selected';
$s = ' selected="selected"';
}
$catlist .= '<option value="' . $row[$idfield] . '"' . $s . '>&nbsp;' . str_short(htmlCHR($row[$namefield]), 60) . '</option>\n';
$catlist .= '<option value="' . $row[$idfield] . '"' . $s . '>&nbsp;' . htmlCHR(str_short($row[$namefield], 60)) . '</option>\n';
}
return $catlist;
@ -877,10 +874,6 @@ function get_list($mode, $id, $select)
*/
function renumber_order($mode, $cat = 0)
{
$table = null;
$catfield = null;
$orderfield = null;
$idfield = null;
switch ($mode) {
case 'category':
$table = BB_CATEGORIES;
@ -932,7 +925,7 @@ function renumber_order($mode, $cat = 0)
*/
function get_cat_forums($cat_id = false)
{
$forums = [];
$forums = array();
$where_sql = '';
if ($cat_id = (int)$cat_id) {
@ -1028,7 +1021,7 @@ function get_orphan_sf()
global $cat_forums;
$last_root = 0;
$bad_sf_ary = [];
$bad_sf_ary = array();
foreach ($cat_forums as $cid => $c) {
foreach ($c['f'] as $fid => $f) {
@ -1102,7 +1095,7 @@ function sf_get_list($mode, $exclude = 0, $select = 0)
$selected = ($fid == $select) ? HTML_SELECTED : '';
$disabled = ($fid == $exclude && !$forum_parent) ? HTML_DISABLED : '';
$style = $disabled ? ' style="color: gray" ' : (($fid == $exclude) ? ' style="color: darkred" ' : '');
$opt .= '<option value="' . $fid . '" ' . $selected . $disabled . $style . '>' . ($f['forum_parent'] ? HTML_SF_SPACER : '') . str_short(htmlCHR($f['forum_name']), 60) . "&nbsp;</option>\n";
$opt .= '<option value="' . $fid . '" ' . $selected . $disabled . $style . '>' . ($f['forum_parent'] ? HTML_SF_SPACER : '') . htmlCHR(str_short($f['forum_name'], 60)) . "&nbsp;</option>\n";
}
$opt .= '</optgroup>';

View file

@ -1,48 +1,64 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['GROUPS']['MANAGE'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
require INC_DIR . '/functions_group.php';
$group_id = isset($_REQUEST[POST_GROUPS_URL]) ? (int)$_REQUEST[POST_GROUPS_URL] : 0;
$mode = isset($_REQUEST['mode']) ? (string)$_REQUEST['mode'] : '';
attachment_quota_settings('group', $mode, isset($_POST['group_update']));
attachment_quota_settings('group', isset($_POST['group_update']), $mode);
if (!empty($_POST['edit']) || !empty($_POST['new'])) {
if (!empty($_POST['edit'])) {
if (!$row = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$row = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
$group_info = [
$group_info = array(
'group_name' => $row['group_name'],
'group_description' => $row['group_description'],
'group_moderator' => $row['group_moderator'],
'group_mod_name' => $row['moderator_name'],
'group_type' => $row['group_type'],
'release_group' => $row['release_group']
];
'release_group' => $row['release_group'],
);
$mode = 'editgroup';
$template->assign_block_vars('group_edit', []);
} elseif (!empty($_POST['new'])) {
$group_info = [
$group_info = array(
'group_name' => '',
'group_description' => '',
'group_moderator' => '',
'group_mod_name' => '',
'group_type' => GROUP_OPEN,
'release_group' => 0
];
'release_group' => 0,
);
$mode = 'newgroup';
}
@ -52,7 +68,7 @@ if (!empty($_POST['edit']) || !empty($_POST['new'])) {
<input type="hidden" name="' . POST_GROUPS_URL . '" value="' . $group_id . '" />
';
$template->assign_vars([
$template->assign_vars(array(
'TPL_EDIT_GROUP' => true,
'GROUP_NAME' => stripslashes(htmlspecialchars($group_info['group_name'])),
@ -66,17 +82,17 @@ if (!empty($_POST['edit']) || !empty($_POST['new'])) {
'S_GROUP_OPEN_CHECKED' => ($group_info['group_type'] == GROUP_OPEN) ? HTML_CHECKED : '',
'S_GROUP_CLOSED_CHECKED' => ($group_info['group_type'] == GROUP_CLOSED) ? HTML_CHECKED : '',
'S_GROUP_HIDDEN_CHECKED' => ($group_info['group_type'] == GROUP_HIDDEN) ? HTML_CHECKED : '',
'RELEASE_GROUP' => (bool)$group_info['release_group'],
'RELEASE_GROUP' => $group_info['release_group'] ? true : false,
'S_GROUP_ACTION' => 'admin_groups.php',
'S_HIDDEN_FIELDS' => $s_hidden_fields
]);
'S_HIDDEN_FIELDS' => $s_hidden_fields,
));
} elseif (!empty($_POST['group_update'])) {
if (!empty($_POST['group_delete'])) {
if (!$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$group_info = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
// Delete Group
\TorrentPier\Legacy\Group::delete_group($group_id);
delete_group($group_id);
$message = $lang['DELETED_GROUP'] . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_GROUPSADMIN'], '<a href="admin_groups.php">', '</a>') . '<br /><br />';
@ -88,7 +104,7 @@ if (!empty($_POST['edit']) || !empty($_POST['new'])) {
$release_group = isset($_POST['release_group']) ? (int)$_POST['release_group'] : 0;
$group_name = isset($_POST['group_name']) ? trim($_POST['group_name']) : '';
$group_desc = isset($_POST['group_description']) ? trim($_POST['group_description']) : '';
$group_moderator = $_POST['username'] ?? '';
$group_moderator = isset($_POST['username']) ? $_POST['username'] : '';
if ($group_name === '') {
bb_die($lang['NO_GROUP_NAME']);
@ -101,28 +117,28 @@ if (!empty($_POST['edit']) || !empty($_POST['new'])) {
bb_die($lang['NO_GROUP_MODERATOR']);
}
$sql_ary = [
$sql_ary = array(
'group_type' => (int)$group_type,
'release_group' => (int)$release_group,
'group_name' => (string)$group_name,
'group_description' => (string)$group_desc,
'group_moderator' => (int)$group_moderator,
'group_single_user' => 0,
];
);
if ($mode == 'editgroup') {
if (!$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$group_info = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
if ($group_info['group_moderator'] != $group_moderator) {
// Create user_group for new group's moderator
\TorrentPier\Legacy\Group::add_user_into_group($group_id, $group_moderator);
add_user_into_group($group_id, $group_moderator);
$sql_ary['mod_time'] = TIMENOW;
// Delete old moderator's user_group
if (isset($_POST['delete_old_moderator'])) {
\TorrentPier\Legacy\Group::delete_user_group($group_id, $group_info['group_moderator']);
delete_user_group($group_id, $group_info['group_moderator']);
}
}
@ -145,7 +161,7 @@ if (!empty($_POST['edit']) || !empty($_POST['new'])) {
$new_group_id = DB()->sql_nextid();
// Create user_group for group's moderator
\TorrentPier\Legacy\Group::add_user_into_group($new_group_id, $group_moderator);
add_user_into_group($new_group_id, $group_moderator);
$message = $lang['ADDED_NEW_GROUP'] . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_GROUPSADMIN'], '<a href="admin_groups.php">', '</a>') . '<br /><br />';
@ -157,12 +173,12 @@ if (!empty($_POST['edit']) || !empty($_POST['new'])) {
}
}
} else {
$template->assign_vars([
$template->assign_vars(array(
'TPL_GROUP_SELECT' => true,
'S_GROUP_ACTION' => 'admin_groups.php',
'S_GROUP_SELECT' => stripslashes(get_select('groups')),
]);
));
}
print_page('admin_groups.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -14,10 +30,10 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php';
$datastore->enqueue([
$datastore->enqueue(array(
'moderators',
'cat_forums',
]);
));
$log_action->init();
@ -36,9 +52,9 @@ $url = basename(__FILE__);
// Key names
$type_key = 'type';
$forum_key = POST_FORUM_URL;
$topic_key = POST_TOPIC_URL;
$user_key = POST_USERS_URL;
$forum_key = 'f';
$topic_key = 't';
$user_key = 'u';
$datetime_key = 'dt'; // value should be strtotime() time ("2006-06-25" etc.)
$daysback_key = 'db';
$sort_key = 'sort';
@ -151,7 +167,7 @@ if ($var =& $_REQUEST[$daysback_key] && $var != $def_days) {
$url = url_arg($url, $daysback_key, $daysback_val);
}
if ($var =& $_REQUEST[$datetime_key] && $var != $def_datetime) {
$tz = TIMENOW + (3600 * config()->get('board_timezone'));
$tz = TIMENOW + (3600 * $bb_cfg['board_timezone']);
if (($tmp_timestamp = strtotime($var, $tz)) > 0) {
$datetime_val = $tmp_timestamp;
$url = url_arg($url, $datetime_key, date($dt_format, $datetime_val));
@ -205,7 +221,7 @@ if ($log_count == $per_page + 1) {
generate_pagination($url, $items_count, $per_page, $start);
$filter = [];
$filter = array();
if ($log_rowset) {
$log_type = $log_action->log_type;
@ -223,15 +239,7 @@ if ($log_rowset) {
case $log_type['mod_topic_move']:
case $log_type['mod_topic_lock']:
case $log_type['mod_topic_unlock']:
case $log_type['mod_topic_set_downloaded']:
case $log_type['mod_topic_unset_downloaded']:
case $log_type['mod_topic_change_tor_status']:
case $log_type['mod_topic_change_tor_type']:
case $log_type['mod_topic_tor_unregister']:
case $log_type['mod_topic_renamed']:
case $log_type['mod_post_delete']:
case $log_type['mod_post_pin']:
case $log_type['mod_post_unpin']:
case $log_type['mod_topic_split']:
// topic_title
if (!empty($row['log_topic_title'])) {
@ -265,9 +273,9 @@ if ($log_rowset) {
'ACTION_HREF_S' => url_arg($url, $type_key, $row['log_type_id']),
'USER_ID' => $row['log_user_id'],
'USERNAME' => profile_url($row, true),
'USERNAME' => profile_url($row),
'USER_HREF_S' => url_arg($url, $user_key, $row['log_user_id']),
'USER_IP' => \TorrentPier\Helpers\IPHelper::isValid($row['log_user_ip']) ? \TorrentPier\Helpers\IPHelper::long2ip_extended($row['log_user_ip']) : '127.0.0.1',
'USER_IP' => Longman\IPTools\Ip::isValid($row['log_user_ip']) ? decode_ip($row['log_user_ip']) : '127.0.0.1',
'FORUM_ID' => $row['log_forum_id'],
'FORUM_HREF' => BB_ROOT . FORUM_URL . $row['log_forum_id'],
@ -289,7 +297,8 @@ if ($log_rowset) {
'TOPIC_HREF_NEW_S' => url_arg($url, $topic_key, $row['log_topic_id_new']),
'TOPIC_TITLE_NEW' => $topic_title_new,
'DATETIME' => bb_date($row['log_time'], 'd-M-y H:i'),
'DATE' => bb_date($row['log_time'], 'j-M'),
'TIME' => bb_date($row['log_time'], 'H:i'),
'DATETIME_HREF_S' => $datetime_href_s,
'MSG' => $msg,
'ROW_CLASS' => $row_class,
@ -313,7 +322,7 @@ if ($log_rowset) {
// Users
if ($user_csv && empty($filter['users'])) {
$template->assign_block_vars('users', array(
'USERNAME' => profile_url($row, true),
'USERNAME' => profile_url($row),
));
$filter['users'] = true;
}
@ -326,7 +335,7 @@ if ($log_rowset) {
'FILTER_USERS' => !empty($filter['users']),
));
} else {
$template->assign_block_vars('log_not_found', []);
$template->assign_block_vars('log_not_found', array());
}
// Select

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -14,17 +30,11 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php';
if (!config()->get('emailer.enabled')) {
bb_die($lang['EMAILER_DISABLED']);
}
set_time_limit(1200);
$subject = trim(request_var('subject', ''));
$subject = (string)trim(request_var('subject', ''));
$message = (string)request_var('message', '');
$group_id = (int)request_var(POST_GROUPS_URL, 0);
$reply_to = (string)request_var('reply_to', config()->get('board_email'));
$message_type = (string)request_var('message_type', '');
$errors = $user_id_sql = [];
@ -40,7 +50,12 @@ if (isset($_POST['submit'])) {
}
if (!$errors) {
$banned_users = ($get_banned_users = get_banned_users()) ? (', ' . implode(', ', $get_banned_users)) : '';
$sql = DB()->fetch_rowset('SELECT ban_userid FROM ' . BB_BANLIST . ' WHERE ban_userid != 0');
foreach ($sql as $row) {
$user_id_sql[] = ',' . $row['ban_userid'];
}
$user_id_sql = implode('', $user_id_sql);
if ($group_id != -1) {
$user_list = DB()->fetch_rowset('
@ -50,29 +65,32 @@ if (isset($_POST['submit'])) {
AND ug.user_pending = 0
AND u.user_id = ug.user_id
AND u.user_active = 1
AND u.user_id NOT IN(" . EXCLUDED_USERS . $banned_users . ')
AND u.user_id NOT IN(" . EXCLUDED_USERS . $user_id_sql . ')
');
} else {
$user_list = DB()->fetch_rowset('
SELECT username, user_email, user_lang
FROM ' . BB_USERS . '
WHERE user_active = 1
AND user_id NOT IN(' . EXCLUDED_USERS . $banned_users . ')
AND user_id NOT IN(' . EXCLUDED_USERS . $user_id_sql . ')
');
}
foreach ($user_list as $i => $row) {
// Sending email
$emailer = new TorrentPier\Emailer();
/** @var TorrentPier\Legacy\Emailer() $emailer */
$emailer = new TorrentPier\Legacy\Emailer();
$emailer->set_to($row['user_email'], $row['username']);
$emailer->set_from([$bb_cfg['board_email'] => $bb_cfg['sitename']]);
$emailer->set_to([$row['user_email'] => $row['username']]);
$emailer->set_subject($subject);
$emailer->set_reply($reply_to);
$emailer->set_template('admin_send_email');
$emailer->assign_vars(['MESSAGE' => trim(html_entity_decode($message))]);
$emailer->assign_vars(array(
'SUBJECT' => html_entity_decode($subject),
'MESSAGE' => html_entity_decode($message),
));
$emailer->send($message_type);
$emailer->send();
}
}
}
@ -86,20 +104,19 @@ $sql = 'SELECT group_id, group_name
ORDER BY group_name
';
$groups = ['-- ' . $lang['ALL_USERS'] . ' --' => -1];
$groups = array('-- ' . $lang['ALL_USERS'] . ' --' => -1);
foreach (DB()->fetch_rowset($sql) as $row) {
$groups[$row['group_name']] = $row['group_id'];
}
$template->assign_vars([
$template->assign_vars(array(
'MESSAGE' => $message,
'SUBJECT' => $subject,
'REPLY_TO' => $reply_to,
'ERROR_MESSAGE' => $errors ? implode('<br />', array_unique($errors)) : '',
'S_USER_ACTION' => 'admin_mass_email.php',
'S_GROUP_SELECT' => build_select(POST_GROUPS_URL, $groups)
]);
'S_GROUP_SELECT' => build_select(POST_GROUPS_URL, $groups),
));
print_page('admin_mass_email.tpl', 'admin');

View file

@ -1,79 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!empty($setmodules)) {
if (IS_SUPER_ADMIN) {
$module['GENERAL']['MIGRATIONS_STATUS'] = basename(__FILE__);
}
return;
}
require __DIR__ . '/pagestart.php';
if (!IS_SUPER_ADMIN) {
bb_die(__('ONLY_FOR_SUPER_ADMIN'));
}
use TorrentPier\Database\MigrationStatus;
// Initialize migration status
$migrationStatus = new MigrationStatus();
$status = $migrationStatus->getMigrationStatus();
$schemaInfo = $migrationStatus->getSchemaInfo();
// Template variables
$template->assign_vars([
'PAGE_TITLE' => __('MIGRATIONS_STATUS'),
'CURRENT_TIME' => date('Y-m-d H:i:s'),
// Migration status individual fields
'MIGRATION_TABLE_EXISTS' => $status['table_exists'],
'MIGRATION_CURRENT_VERSION' => $status['current_version'],
'MIGRATION_APPLIED_COUNT' => count($status['applied_migrations']),
'MIGRATION_PENDING_COUNT' => count($status['pending_migrations']),
// Setup status fields
'SETUP_REQUIRES_SETUP' => $status['requires_setup'] ?? false,
'SETUP_TYPE' => $status['setup_status']['type'] ?? __('UNKNOWN'),
'SETUP_MESSAGE' => $status['setup_status']['message'] ?? '',
'SETUP_ACTION_REQUIRED' => $status['setup_status']['action_required'] ?? false,
'SETUP_INSTRUCTIONS' => $status['setup_status']['instructions'] ?? '',
// Schema info individual fields
'SCHEMA_DATABASE_NAME' => $schemaInfo['database_name'],
'SCHEMA_TABLE_COUNT' => $schemaInfo['table_count'],
'SCHEMA_SIZE_MB' => $schemaInfo['size_mb'],
]);
// Assign migration data for template
if (!empty($status['applied_migrations'])) {
foreach ($status['applied_migrations'] as $i => $migration) {
$template->assign_block_vars('applied_migrations', [
'VERSION' => $migration['version'],
'NAME' => $migration['migration_name'] ?? __('UNKNOWN'),
'START_TIME' => $migration['start_time'] ?? __('UNKNOWN'),
'END_TIME' => $migration['end_time'] ?? __('UNKNOWN'),
'ROW_CLASS' => ($i % 2) ? 'row1' : 'row2'
]);
}
}
if (!empty($status['pending_migrations'])) {
foreach ($status['pending_migrations'] as $i => $migration) {
$template->assign_block_vars('pending_migrations', [
'VERSION' => $migration['version'],
'NAME' => $migration['name'],
'FILENAME' => $migration['filename'],
'ROW_CLASS' => ($i % 2) ? 'row1' : 'row2'
]);
}
}
// Output template using standard admin pattern
print_page('admin_migrations.tpl', 'admin');

View file

@ -1,29 +1,33 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
if (IS_SUPER_ADMIN) {
$module['GENERAL']['PHP_INFO'] = basename(__FILE__);
}
$module['GENERAL']['PHP_INFO'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
if (!IS_SUPER_ADMIN) {
bb_die($lang['ONLY_FOR_SUPER_ADMIN']);
}
// Check for demo mode
if (IN_DEMO_MODE) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
/** @noinspection ForgottenDebugOutputInspection */
phpinfo();

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -14,8 +30,11 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php';
$_POST['special_rank'] = 1;
$_POST['min_posts'] = -1;
if (isset($_GET['mode']) || isset($_POST['mode'])) {
$mode = $_GET['mode'] ?? $_POST['mode'];
$mode = isset($_GET['mode']) ? $_GET['mode'] : $_POST['mode'];
} else {
//
// These could be entered via a form button
@ -29,10 +48,6 @@ if (isset($_GET['mode']) || isset($_POST['mode'])) {
}
}
if ($mode == 'delete' && isset($_POST['cancel'])) {
$mode = '';
}
if ($mode != '') {
if ($mode == 'edit' || $mode == 'add') {
//
@ -54,21 +69,29 @@ if ($mode != '') {
$rank_info = DB()->sql_fetchrow($result);
$s_hidden_fields .= '<input type="hidden" name="id" value="' . $rank_id . '" />';
} else {
$rank_info['rank_special'] = 0;
}
$s_hidden_fields .= '<input type="hidden" name="mode" value="save" />';
$template->assign_vars([
$rank_is_special = !empty($rank_info['rank_special']) ? HTML_CHECKED : '';
$rank_is_not_special = empty($rank_info['rank_special']) ? HTML_CHECKED : '';
$template->assign_vars(array(
'TPL_RANKS_EDIT' => true,
'RANK' => !empty($rank_info['rank_title']) ? $rank_info['rank_title'] : '',
'SPECIAL_RANK' => $rank_is_special,
'NOT_SPECIAL_RANK' => $rank_is_not_special,
'MINIMUM' => $rank_is_special ? '' : $rank_info['rank_min'],
'IMAGE' => !empty($rank_info['rank_image']) ? $rank_info['rank_image'] : 'styles/images/ranks/rank_image.png',
'STYLE' => !empty($rank_info['rank_style']) ? $rank_info['rank_style'] : '',
'IMAGE_DISPLAY' => !empty($rank_info['rank_image']) ? '<img src="../' . $rank_info['rank_image'] . '" />' : '',
'S_RANK_ACTION' => 'admin_ranks.php',
'S_HIDDEN_FIELDS' => $s_hidden_fields
]);
'S_HIDDEN_FIELDS' => $s_hidden_fields,
));
} elseif ($mode == 'save') {
//
// Ok, they sent us our info, let's update it.
@ -77,38 +100,47 @@ if ($mode != '') {
$rank_id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
$rank_title = isset($_POST['title']) ? trim($_POST['title']) : '';
$rank_style = isset($_POST['style']) ? trim($_POST['style']) : '';
$special_rank = ($_POST['special_rank'] == 1) ? true : 0;
$min_posts = isset($_POST['min_posts']) ? (int)$_POST['min_posts'] : -1;
$rank_image = isset($_POST['rank_image']) ? trim($_POST['rank_image']) : '';
if ($rank_title == '') {
bb_die($lang['MUST_SELECT_RANK']);
}
if ($special_rank == 1) {
$max_posts = -1;
$min_posts = -1;
}
//
// The rank image has to be a jpg, gif or png
//
if ($rank_image != '') {
if (!preg_match('/(\.gif|\.png|\.jpg|\.jpeg|\.bmp|\.webp|\.avif\.ico)$/is', $rank_image)) {
if (!preg_match('/(\.gif|\.png|\.jpg)$/is', $rank_image)) {
$rank_image = '';
}
}
if ($rank_id) {
$sql = 'UPDATE ' . BB_USERS . " SET user_rank = 0 WHERE user_rank = $rank_id";
if (!$result = DB()->sql_query($sql)) {
bb_die($lang['NO_UPDATE_RANKS']);
if (!$special_rank) {
$sql = 'UPDATE ' . BB_USERS . " SET user_rank = 0 WHERE user_rank = $rank_id";
if (!$result = DB()->sql_query($sql)) {
bb_die($lang['NO_UPDATE_RANKS']);
}
}
$sql = 'UPDATE ' . BB_RANKS . "
SET rank_title = '" . DB()->escape($rank_title) . "',
rank_special = $special_rank,
rank_min = $min_posts,
rank_image = '" . DB()->escape($rank_image) . "',
rank_style = '" . DB()->escape($rank_style) . "'
WHERE rank_id = $rank_id";
$message = $lang['RANK_UPDATED'];
} else {
$sql = 'INSERT INTO ' . BB_RANKS . " (rank_title, rank_image, rank_style)
VALUES ('" . DB()->escape($rank_title) . "', '" . DB()->escape($rank_image) . "', '" . DB()->escape($rank_style) . "')";
$sql = 'INSERT INTO ' . BB_RANKS . " (rank_title, rank_special, rank_min, rank_image, rank_style)
VALUES ('" . DB()->escape($rank_title) . "', $special_rank, $min_posts, '" . DB()->escape($rank_image) . "', '" . DB()->escape($rank_style) . "')";
$message = $lang['RANK_ADDED'];
}
@ -127,40 +159,29 @@ if ($mode != '') {
// Ok, they want to delete their rank
//
$confirmed = isset($_POST['confirm']);
if (isset($_POST['id']) || isset($_GET['id'])) {
$rank_id = isset($_POST['id']) ? (int)$_POST['id'] : (int)$_GET['id'];
} else {
$rank_id = 0;
}
if ($confirmed) {
if ($rank_id) {
$sql = 'DELETE FROM ' . BB_RANKS . " WHERE rank_id = $rank_id";
if ($rank_id) {
$sql = 'DELETE FROM ' . BB_RANKS . " WHERE rank_id = $rank_id";
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not delete rank data');
}
$sql = 'UPDATE ' . BB_USERS . " SET user_rank = 0 WHERE user_rank = $rank_id";
if (!$result = DB()->sql_query($sql)) {
bb_die($lang['NO_UPDATE_RANKS']);
}
$datastore->update('ranks');
bb_die($lang['RANK_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_RANKADMIN'], '<a href="admin_ranks.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} else {
bb_die($lang['MUST_SELECT_RANK']);
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not delete rank data');
}
} else {
$hidden_fields = '<input type="hidden" name="mode" value="' . $mode . '" />';
$hidden_fields .= '<input type="hidden" name="id" value="' . $rank_id . '" />';
print_confirmation([
'FORM_ACTION' => 'admin_ranks.php',
'HIDDEN_FIELDS' => $hidden_fields,
]);
$sql = 'UPDATE ' . BB_USERS . " SET user_rank = 0 WHERE user_rank = $rank_id";
if (!$result = DB()->sql_query($sql)) {
bb_die($lang['NO_UPDATE_RANKS']);
}
$datastore->update('ranks');
bb_die($lang['RANK_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_RANKADMIN'], '<a href="admin_ranks.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} else {
bb_die($lang['MUST_SELECT_RANK']);
}
} else {
bb_die('Invalid mode');
@ -169,33 +190,43 @@ if ($mode != '') {
//
// Show the default page
//
$sql = 'SELECT * FROM ' . BB_RANKS . ' ORDER BY rank_title';
$sql = 'SELECT * FROM ' . BB_RANKS . ' ORDER BY rank_min, rank_title';
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not obtain ranks data #2');
}
$rank_count = DB()->num_rows($result);
$rank_rows = DB()->sql_fetchrowset($result);
$template->assign_vars([
$template->assign_vars(array(
'TPL_RANKS_LIST' => true,
'S_RANKS_ACTION' => 'admin_ranks.php'
]);
'S_RANKS_ACTION' => 'admin_ranks.php',
));
for ($i = 0; $i < $rank_count; $i++) {
$rank = $rank_rows[$i]['rank_title'];
$special_rank = $rank_rows[$i]['rank_special'];
$rank_id = $rank_rows[$i]['rank_id'];
$rank_min = $rank_rows[$i]['rank_min'];
if ($special_rank == 1) {
$rank_min = $rank_max = '-';
}
$row_class = !($i % 2) ? 'row1' : 'row2';
$template->assign_block_vars('ranks', [
$rank_is_special = $special_rank ? $lang['YES'] : $lang['NO'];
$template->assign_block_vars('ranks', array(
'ROW_CLASS' => $row_class,
'RANK' => $rank,
'STYLE' => $rank_rows[$i]['rank_style'],
'IMAGE_DISPLAY' => $rank_rows[$i]['rank_image'] ? '<img src="../' . $rank_rows[$i]['rank_image'] . '" />' : '',
'SPECIAL_RANK' => $rank_is_special,
'RANK_MIN' => $rank_min,
'U_RANK_EDIT' => "admin_ranks.php?mode=edit&amp;id=$rank_id",
'U_RANK_DELETE' => "admin_ranks.php?mode=delete&amp;id=$rank_id"
]);
'U_RANK_DELETE' => "admin_ranks.php?mode=delete&amp;id=$rank_id",
));
}
}

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -13,11 +29,10 @@ if (!empty($setmodules)) {
}
return;
}
require __DIR__ . '/pagestart.php';
if (!IS_SUPER_ADMIN) {
bb_die($lang['ONLY_FOR_SUPER_ADMIN']);
bb_die($lang['NOT_ADMIN']);
}
require INC_DIR . '/bbcode.php';
@ -135,7 +150,7 @@ if ($mode == 'submit' || $mode == 'refresh') {
}
// get the db sizes
[$search_data_size, $search_index_size, $search_tables_size] = get_db_sizes();
list($search_data_size, $search_index_size, $search_tables_size) = get_db_sizes();
// get the post subject/text of each post
$result = DB()->query("
@ -157,7 +172,7 @@ if ($mode == 'submit' || $mode == 'refresh') {
$expire_time = $start_time + $time_limit - 5;
$start_post_id = $end_post_id = $num_rows = 0;
$timer_expired = false;
$words_sql = [];
$words_sql = array();
while ($row = DB()->fetch_next($result) and !$timer_expired) {
set_time_limit(600);
@ -291,7 +306,7 @@ if ($mode == 'submit' || $mode == 'refresh') {
}
// get the db sizes
[$search_data_size, $search_index_size, $search_tables_size] = get_db_sizes();
list($search_data_size, $search_index_size, $search_tables_size) = get_db_sizes();
// calculate the final (estimated) values
$final_search_tables_size = '';
@ -329,7 +344,7 @@ if ($mode == 'submit' || $mode == 'refresh') {
'TOTAL_PERCENT' => sprintf($lang['PERCENT_COMPLETED'], round($total_percent, 2)),
'LAST_CYCLE_TIME' => delta_time(TIMENOW),
'SESSION_TIME' => delta_time(($last_session_data['start_time'] == 0) ? TIMENOW : $last_session_data['start_time']),
'SESSION_TIME' => delta_time($last_session_data['start_time']),
'SESSION_AVERAGE_CYCLE_TIME' => delta_time((int)$session_average_cycle_time, 0),
'SESSION_ESTIMATED_TIME' => delta_time((int)$session_estimated_time, 0),
@ -365,36 +380,36 @@ if ($mode == 'submit' || $mode == 'refresh') {
// check our last status
if ($last_session_data['rebuild_session_status'] == REBUILD_SEARCH_PROCESSED) {
$last_saved_processing = sprintf($lang['INFO_PROCESSING_STOPPED'], $last_saved_post_id, $total_posts_processed, $last_saved_date);
$clear_search_disabled = 'disabled';
$clear_search_disabled = 'disabled="disabled"';
$template->assign_block_vars('start_select_input', []);
$template->assign_block_vars('start_select_input', array());
} elseif ($last_session_data['rebuild_session_status'] == REBUILD_SEARCH_ABORTED) {
$last_saved_processing = sprintf($lang['INFO_PROCESSING_ABORTED'], $last_saved_post_id, $total_posts_processed, $last_saved_date);
// check if the interrupted cycle has finished
if (TIMENOW - $last_session_data['end_time'] < $last_session_data['last_cycle_time']) {
$last_saved_processing .= '<br />' . $lang['INFO_PROCESSING_ABORTED_SOON'];
}
$clear_search_disabled = 'disabled';
$clear_search_disabled = 'disabled="disabled"';
$template->assign_block_vars('start_select_input', []);
$template->assign_block_vars('start_select_input', array());
} else {
// when finished
if ($last_session_data['end_post_id'] < $max_post_id) {
$last_saved_processing = sprintf($lang['INFO_PROCESSING_FINISHED_NEW'], $last_saved_post_id, $total_posts_processed, $last_saved_date, $total_posts - $total_posts_processed);
$clear_search_disabled = 'disabled';
$clear_search_disabled = 'disabled="disabled"';
$template->assign_block_vars('start_select_input', []);
$template->assign_block_vars('start_select_input', array());
} else {
$last_saved_processing = sprintf($lang['INFO_PROCESSING_FINISHED'], $total_posts, $last_saved_date);
$template->assign_block_vars('start_text_input', []);
$template->assign_block_vars('start_text_input', array());
}
}
$template->assign_block_vars('last_saved_info', []);
$template->assign_block_vars('last_saved_info', array());
} else {
$template->assign_block_vars('start_text_input', []);
$template->assign_block_vars('start_text_input', array());
}
// create the output of page
@ -490,7 +505,6 @@ function get_rebuild_session_details($id, $details = 'all')
function get_processed_posts($mode = 'session')
{
global $last_session_data;
$row = [];
if ($mode == 'total') {
$sql = 'SELECT SUM(session_posts) as posts FROM ' . BB_SEARCH_REBUILD;
@ -544,7 +558,6 @@ function clear_search_tables($mode = '')
// We limit the result to 200, in order to avoid white (255).
function create_percent_color($percent)
{
$percent_color = null;
$percent_ary = array(
'r' => array(86, 100),
'g' => array(0, 50),

View file

@ -1,45 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!empty($setmodules)) {
$module['MODS']['ROBOTS_TXT_EDITOR_TITLE'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
$robots_file = BB_ROOT . 'robots.txt';
// Обработка сохранения
if (isset($_POST['save'])) {
$robots_txt = $_POST['robots_txt'] ?? '';
if (!is_writable($robots_file) && is_file($robots_file)) {
bb_die('File robots.txt is not writable #1');
}
$bytes = file_put_contents($robots_file, $robots_txt);
if ($bytes === false) {
bb_die('Could not write robots.txt #2');
}
bb_die($lang['ROBOTS_TXT_UPDATED_SUCCESSFULLY'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_ROBOTS_TXT_CONFIG'], '<a href="admin_robots.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
}
$current_content = '';
if (is_file($robots_file)) {
$current_content = file_get_contents($robots_file);
}
$template->assign_vars([
'S_ACTION' => 'admin_robots.php',
'ROBOTS_TXT' => htmlCHR($current_content),
]);
print_page('admin_robots.tpl', 'admin');

View file

@ -1,31 +1,47 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['MODS']['SITEMAP'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
require INC_DIR . '/functions_selects.php';
$sql = "SELECT * FROM " . BB_CONFIG . " WHERE config_name IN('sitemap_time', 'static_sitemap')";
$sql = 'SELECT * FROM ' . BB_CONFIG;
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not query config information in admin_sitemap');
} else {
$new_params = [];
$new_params = array();
while ($row = DB()->sql_fetchrow($result)) {
$config_name = $row['config_name'];
$config_value = $row['config_value'];
$default_config[$config_name] = $config_value;
$new[$config_name] = $_POST[$config_name] ?? $default_config[$config_name];
$new[$config_name] = isset($_POST[$config_name]) ? $_POST[$config_name] : $default_config[$config_name];
if (isset($_POST['submit']) && $row['config_value'] != $new[$config_name]) {
$new_params[$config_name] = $new[$config_name];
@ -39,12 +55,12 @@ if (!$result = DB()->sql_query($sql)) {
}
}
$s_mess = $lang['SITEMAP_CREATED'] . ': <b>' . bb_date($new['sitemap_time'], config()->get('post_date_format')) . '</b> ' . $lang['SITEMAP_AVAILABLE'] . ': <a href="' . make_url('sitemap/sitemap.xml') . '" target="_blank">' . make_url('sitemap/sitemap.xml') . '</a>';
$message = is_file(SITEMAP_DIR . '/sitemap.xml') ? $s_mess : $lang['SITEMAP_NOT_CREATED'];
$s_mess = $lang['SITEMAP_CREATED'] . ': <b>' . bb_date($new['sitemap_time'], $bb_cfg['post_date_format']) . '</b> ' . $lang['SITEMAP_AVAILABLE'] . ': <a href="' . make_url('sitemap/sitemap.xml') . '" target="_blank">' . make_url('sitemap/sitemap.xml') . '</a>';
$message = file_exists(SITEMAP_DIR . '/sitemap.xml') ? $s_mess : $lang['SITEMAP_NOT_CREATED'];
$template->assign_vars([
$template->assign_vars(array(
'STATIC_SITEMAP' => $new['static_sitemap'],
'MESSAGE' => $message
]);
'MESSAGE' => $message,
));
print_page('admin_sitemap.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -16,35 +32,33 @@ require __DIR__ . '/pagestart.php';
// Check to see what mode we should operate in
if (isset($_POST['mode']) || isset($_GET['mode'])) {
$mode = $_POST['mode'] ?? $_GET['mode'];
$mode = isset($_POST['mode']) ? $_POST['mode'] : $_GET['mode'];
$mode = htmlspecialchars($mode);
} else {
$mode = '';
}
if ($mode == 'delete' && isset($_POST['cancel'])) {
$mode = '';
}
$pathToSmilesDir = BB_ROOT . config()->get('smilies_path');
$delimeter = '=+:';
$s_hidden_fields = '';
$smiley_paks = $smiley_images = [];
$smiley_paks = [];
// Read a listing of uploaded smiles
$smilesDirectory = new DirectoryIterator($pathToSmilesDir);
// Read a listing of uploaded smilies for use in the add or edit smliey code
$dir = opendir(BB_ROOT . $bb_cfg['smilies_path']);
foreach ($smilesDirectory as $files) {
if ($files->isFile()) {
$extension = strtolower(pathinfo($files->getFilename(), PATHINFO_EXTENSION));
if (in_array($extension, ['png', 'gif'], true) && getimagesize($pathToSmilesDir . '/' . $files->getFilename())) {
$smiley_images[] = $files->getFilename();
} else if ($extension === 'pak') {
$smiley_paks[] = $files->getFilename();
while ($file = @readdir($dir)) {
if (!is_dir(bb_realpath(BB_ROOT . $bb_cfg['smilies_path'] . '/' . $file))) {
$img_size = getimagesize(BB_ROOT . $bb_cfg['smilies_path'] . '/' . $file);
if ($img_size[0] && $img_size[1]) {
$smiley_images[] = $file;
} elseif (preg_match('/.pak$/i', $file)) {
$smiley_paks[] = $file;
}
}
}
closedir($dir);
// Select main mode
if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$smile_pak = (string)request_var('smile_pak', '');
@ -73,13 +87,13 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
}
}
$fcontents = file($pathToSmilesDir . '/' . $smile_pak);
$fcontents = file(BB_ROOT . $bb_cfg['smilies_path'] . '/' . $smile_pak);
if (empty($fcontents)) {
bb_die('Could not read smiley pak file');
}
foreach ($fcontents as $i => $iValue) {
for ($i = 0, $iMax = count($fcontents); $i < $iMax; $i++) {
$smile_data = explode($delimeter, trim(addslashes($fcontents[$i])));
for ($j = 2, $jMax = count($smile_data); $j < $jMax; $j++) {
@ -124,13 +138,13 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$hidden_vars = '<input type="hidden" name="mode" value="import">';
$template->assign_vars([
$template->assign_vars(array(
'TPL_SMILE_IMPORT' => true,
'S_SMILEY_ACTION' => 'admin_smilies.php',
'S_SMILE_SELECT' => $smile_paks_select,
'S_HIDDEN_FIELDS' => $hidden_vars
]);
'S_HIDDEN_FIELDS' => $hidden_vars,
));
}
} elseif (isset($_POST['export_pack']) || isset($_GET['export_pack'])) {
$export_pack = (string)request_var('export_pack', '');
@ -167,39 +181,28 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$s_hidden_fields = '<input type="hidden" name="mode" value="savenew" />';
$template->assign_vars([
$template->assign_vars(array(
'TPL_SMILE_EDIT' => true,
'SMILEY_IMG' => $pathToSmilesDir . '/' . $smiley_images[0],
'SMILEY_IMG' => BB_ROOT . $bb_cfg['smilies_path'] . '/' . $smiley_images[0],
'S_SMILEY_ACTION' => 'admin_smilies.php',
'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_FILENAME_OPTIONS' => $filename_list,
'S_SMILEY_BASEDIR' => $pathToSmilesDir
]);
'S_SMILEY_BASEDIR' => BB_ROOT . $bb_cfg['smilies_path']
));
} elseif ($mode != '') {
switch ($mode) {
case 'delete':
$confirmed = isset($_POST['confirm']);
$smiley_id = (!empty($_POST['id'])) ? $_POST['id'] : $_GET['id'];
$smiley_id = (int)$smiley_id;
if ($confirmed) {
$sql = 'DELETE FROM ' . BB_SMILIES . ' WHERE smilies_id = ' . $smiley_id;
$result = DB()->sql_query($sql);
if (!$result) {
bb_die('Could not delete smiley');
}
$datastore->update('smile_replacements');
bb_die($lang['SMILEY_DEL_SUCCESS'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_SMILEADMIN'], '<a href="admin_smilies.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} else {
$hidden_fields = '<input type="hidden" name="mode" value="' . $mode . '" />';
$hidden_fields .= '<input type="hidden" name="id" value="' . $smiley_id . '" />';
print_confirmation([
'FORM_ACTION' => 'admin_smilies.php',
'HIDDEN_FIELDS' => $hidden_fields,
]);
$sql = 'DELETE FROM ' . BB_SMILIES . ' WHERE smilies_id = ' . $smiley_id;
$result = DB()->sql_query($sql);
if (!$result) {
bb_die('Could not delete smiley');
}
$datastore->update('smile_replacements');
bb_die($lang['SMILEY_DEL_SUCCESS'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_SMILEADMIN'], '<a href="admin_smilies.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
break;
case 'edit':
@ -216,7 +219,7 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$filename_list = $smiley_edit_img = '';
for ($i = 0, $iMax = count($smiley_images); $i < $iMax; $i++) {
if ($smiley_images[$i] == $smile_data['smile_url']) {
$smiley_selected = 'selected';
$smiley_selected = 'selected="selected"';
$smiley_edit_img = $smiley_images[$i];
} else {
$smiley_selected = '';
@ -226,23 +229,23 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$s_hidden_fields = '<input type="hidden" name="mode" value="save" /><input type="hidden" name="smile_id" value="' . $smile_data['smilies_id'] . '" />';
$template->assign_vars([
$template->assign_vars(array(
'TPL_SMILE_EDIT' => true,
'SMILEY_CODE' => $smile_data['code'],
'SMILEY_EMOTICON' => $smile_data['emoticon'],
'SMILEY_IMG' => $pathToSmilesDir . '/' . $smiley_edit_img,
'SMILEY_IMG' => BB_ROOT . $bb_cfg['smilies_path'] . '/' . $smiley_edit_img,
'S_SMILEY_ACTION' => 'admin_smilies.php',
'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_FILENAME_OPTIONS' => $filename_list,
'S_SMILEY_BASEDIR' => $pathToSmilesDir
]);
'S_SMILEY_BASEDIR' => BB_ROOT . $bb_cfg['smilies_path'],
));
break;
case 'save':
$smile_code = isset($_POST['smile_code']) ? trim($_POST['smile_code']) : trim($_GET['smile_code']);
$smile_url = isset($_POST['smile_url']) ? trim($_POST['smile_url']) : trim($_GET['smile_url']);
$smile_url = ltrim(basename($smile_url), "'");
$smile_url = bb_ltrim(basename($smile_url), "'");
$smile_emotion = isset($_POST['smile_emotion']) ? trim($_POST['smile_emotion']) : trim($_GET['smile_emotion']);
$smile_id = isset($_POST['smile_id']) ? (int)$_POST['smile_id'] : (int)$_GET['smile_id'];
@ -252,7 +255,8 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
}
// Convert < and > to proper htmlentities for parsing
$smile_code = str_replace(['<', '>'], ['&lt;', '&gt;'], $smile_code);
$smile_code = str_replace('<', '&lt;', $smile_code);
$smile_code = str_replace('>', '&gt;', $smile_code);
// Proceed with updating the smiley table
$sql = 'UPDATE ' . BB_SMILIES . "
@ -267,10 +271,10 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
break;
case 'savenew':
$smile_code = $_POST['smile_code'] ?? $_GET['smile_code'];
$smile_url = $_POST['smile_url'] ?? $_GET['smile_url'];
$smile_url = ltrim(basename($smile_url), "'");
$smile_emotion = $_POST['smile_emotion'] ?? $_GET['smile_emotion'];
$smile_code = isset($_POST['smile_code']) ? $_POST['smile_code'] : $_GET['smile_code'];
$smile_url = isset($_POST['smile_url']) ? $_POST['smile_url'] : $_GET['smile_url'];
$smile_url = bb_ltrim(basename($smile_url), "'");
$smile_emotion = isset($_POST['smile_emotion']) ? $_POST['smile_emotion'] : $_GET['smile_emotion'];
$smile_code = trim($smile_code);
$smile_url = trim($smile_url);
$smile_emotion = trim($smile_emotion);
@ -281,7 +285,8 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
}
// Convert < and > to proper htmlentities for parsing
$smile_code = str_replace(['<', '>'], ['&lt;', '&gt;'], $smile_code);
$smile_code = str_replace('<', '&lt;', $smile_code);
$smile_code = str_replace('>', '&gt;', $smile_code);
// Save the data to the smiley table
$sql = 'INSERT INTO ' . BB_SMILIES . " (code, smile_url, emoticon)
@ -304,11 +309,11 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$smilies = DB()->sql_fetchrowset($result);
$template->assign_vars([
$template->assign_vars(array(
'TPL_SMILE_MAIN' => true,
'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_SMILEY_ACTION' => 'admin_smilies.php'
]);
'S_SMILEY_ACTION' => 'admin_smilies.php',
));
// Loop throuh the rows of smilies setting block vars for the template
for ($i = 0, $iMax = count($smilies); $i < $iMax; $i++) {
@ -318,16 +323,16 @@ if (isset($_GET['import_pack']) || isset($_POST['import_pack'])) {
$row_class = !($i % 2) ? 'row1' : 'row2';
$template->assign_block_vars('smiles', [
$template->assign_block_vars('smiles', array(
'ROW_CLASS' => $row_class,
'SMILEY_IMG' => $pathToSmilesDir . '/' . $smilies[$i]['smile_url'],
'SMILEY_IMG' => BB_ROOT . $bb_cfg['smilies_path'] . '/' . $smilies[$i]['smile_url'],
'CODE' => $smilies[$i]['code'],
'EMOT' => $smilies[$i]['emoticon'],
'U_SMILEY_EDIT' => 'admin_smilies.php?mode=edit&amp;id=' . $smilies[$i]['smilies_id'],
'U_SMILEY_DELETE' => 'admin_smilies.php?mode=delete&amp;id=' . $smilies[$i]['smilies_id'],
]);
));
}
}

View file

@ -1,32 +1,45 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['GENERAL']['TERMS'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
require INC_DIR . '/bbcode.php';
$preview = isset($_POST['preview']);
if (isset($_POST['post']) && (config()->get('terms') !== $_POST['message'])) {
bb_update_config(['terms' => $_POST['message']]);
bb_die($lang['TERMS_UPDATED_SUCCESSFULLY'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_TERMS_CONFIG'], '<a href="admin_terms.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
if (isset($_POST['post']) && $bb_cfg['terms'] != $_POST['message']) {
bb_update_config(array('terms' => $_POST['message']));
bb_die($lang['CONFIG_UPDATED']);
}
$template->assign_vars([
$template->assign_vars(array(
'S_ACTION' => 'admin_terms.php',
'EXT_LINK_NW' => config()->get('ext_link_new_win'),
'MESSAGE' => $preview ? $_POST['message'] : config()->get('terms'),
'PREVIEW_HTML' => $preview ? bbcode2html($_POST['message']) : '',
]);
'EXT_LINK_NW' => $bb_cfg['ext_link_new_win'],
'MESSAGE' => $bb_cfg['terms'] ?: '',
'PREVIEW_HTML' => isset($_REQUEST['preview']) ? bbcode2html($_POST['message']) : '',
));
print_page('admin_terms.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -12,26 +28,22 @@ if (!empty($setmodules)) {
$module['GROUPS']['PERMISSIONS'] = basename(__FILE__) . '?mode=group';
return;
}
require __DIR__ . '/pagestart.php';
$max_forum_name_length = 50;
require INC_DIR . '/functions_group.php';
$yes_sign = '&radic;';
$no_sign = 'x';
$group_id = isset($_REQUEST[POST_GROUPS_URL]) ? (int)$_REQUEST[POST_GROUPS_URL] : 0;
$user_id = isset($_REQUEST[POST_USERS_URL]) ? (int)$_REQUEST[POST_USERS_URL] : 0;
$cat_id = isset($_REQUEST[POST_CAT_URL]) ? (int)$_REQUEST[POST_CAT_URL] : 0;
$group_id = isset($_REQUEST['g']) ? (int)$_REQUEST['g'] : 0;
$user_id = isset($_REQUEST['u']) ? (int)$_REQUEST['u'] : 0;
$cat_id = isset($_REQUEST['c']) ? (int)$_REQUEST['c'] : 0;
$mode = isset($_REQUEST['mode']) ? (string)$_REQUEST['mode'] : '';
$submit = isset($_REQUEST['submit']);
// Check for demo mode
if (IN_DEMO_MODE && $submit) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
$group_data = [];
$group_data = array();
$forum_auth_fields = array(
'auth_view',
@ -67,7 +79,7 @@ if ($submit && $mode == 'user') {
if ($row = DB()->fetch_row($sql)) {
$group_id = $row['group_id'];
} else {
$group_id = \TorrentPier\Legacy\Group::create_user_group($user_id);
$group_id = create_user_group($user_id);
}
if (!$group_id || !$user_id || null === $this_user_level) {
@ -75,47 +87,45 @@ if ($submit && $mode == 'user') {
}
// Make user an admin (if already user)
if (isset($_POST['userlevel'])) {
if ($_POST['userlevel'] === 'admin') {
if ($userdata['user_id'] == $user_id || $user_id == GUEST_UID || $user_id == BOT_UID) {
bb_die($lang['AUTH_GENERAL_ERROR']);
}
DB()->query('UPDATE ' . BB_USERS . ' SET user_level = ' . ADMIN . " WHERE user_id = $user_id");
// Delete any entries in auth_access, they are not required if user is becoming an admin
\TorrentPier\Legacy\Group::delete_permissions($group_id, $user_id);
$message = $lang['AUTH_UPDATED'] . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_USERAUTH'], '<a href="admin_ug_auth.php?mode=' . $mode . '&' . POST_USERS_URL . '=' . $user_id . '">', '</a>') . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
} // Make admin a user (if already admin)
elseif ($_POST['userlevel'] === 'user') {
// ignore if you're trying to change yourself from an admin to user!
if ($userdata['user_id'] == $user_id) {
bb_die($lang['AUTH_SELF_ERROR']);
}
// Update users level, reset to USER
DB()->query('UPDATE ' . BB_USERS . ' SET user_level = ' . USER . " WHERE user_id = $user_id");
\TorrentPier\Legacy\Group::delete_permissions($group_id, $user_id);
$message = $lang['AUTH_UPDATED'] . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_USERAUTH'], '<a href="admin_ug_auth.php?mode=' . $mode . '&' . POST_USERS_URL . '=' . $user_id . '">', '</a>') . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
if ($_POST['userlevel'] === 'admin') {
if ($userdata['user_id'] == $user_id || $user_id == GUEST_UID || $user_id == BOT_UID) {
bb_die('Could not update admin status');
}
DB()->query('UPDATE ' . BB_USERS . ' SET user_level = ' . ADMIN . " WHERE user_id = $user_id");
// Delete any entries in auth_access, they are not required if user is becoming an admin
delete_permissions($group_id, $user_id);
$message = $lang['AUTH_UPDATED'] . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_USERAUTH'], '<a href="admin_ug_auth.php?mode=' . $mode . '">', '</a>') . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
} // Make admin a user (if already admin)
elseif ($_POST['userlevel'] === 'user') {
// ignore if you're trying to change yourself from an admin to user!
if ($userdata['user_id'] == $user_id) {
bb_die('Could not update admin status<br /><br />Could not change yourself from an admin to user');
}
// Update users level, reset to USER
DB()->query('UPDATE ' . BB_USERS . ' SET user_level = ' . USER . " WHERE user_id = $user_id");
delete_permissions($group_id, $user_id);
$message = $lang['AUTH_UPDATED'] . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_USERAUTH'], '<a href="admin_ug_auth.php?mode=' . $mode . '">', '</a>') . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
}
//
// Submit new USER permissions
//
$auth = [];
$auth = array();
if (!empty($_POST['auth']) && is_array($_POST['auth'])) {
if (is_array($_POST['auth'])) {
array_deep($_POST['auth'], 'intval');
foreach ($_POST['auth'] as $f_id => $bf_ary) {
@ -125,13 +135,14 @@ if ($submit && $mode == 'user') {
}
}
\TorrentPier\Legacy\Group::delete_permissions($group_id, null, $cat_id);
\TorrentPier\Legacy\Group::store_permissions($group_id, $auth);
\TorrentPier\Legacy\Group::update_user_level($user_id);
delete_permissions($group_id, null, $cat_id);
store_permissions($group_id, $auth);
update_user_level($user_id);
$l_auth_return = ($mode == 'user') ? $lang['CLICK_RETURN_USERAUTH'] : $lang['CLICK_RETURN_GROUPAUTH'];
$message = $lang['AUTH_UPDATED'] . '<br /><br />';
$message .= sprintf($l_auth_return, '<a href="admin_ug_auth.php?mode=' . $mode . '&' . POST_USERS_URL . '=' . $user_id . '">', '</a>') . '<br /><br />';
$message .= sprintf($l_auth_return, '<a href="admin_ug_auth.php?mode=' . $mode . '">', '</a>') . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
@ -139,12 +150,12 @@ if ($submit && $mode == 'user') {
//
// Submit new GROUP permissions
//
elseif ($submit && $mode == 'group' && (!empty($_POST['auth']) && is_array($_POST['auth']))) {
if (!$group_data = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
elseif ($submit && $mode == 'group' && is_array($_POST['auth'])) {
if (!$group_data = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
$auth = [];
$auth = array();
array_deep($_POST['auth'], 'intval');
foreach ($_POST['auth'] as $f_id => $bf_ary) {
@ -153,13 +164,14 @@ elseif ($submit && $mode == 'group' && (!empty($_POST['auth']) && is_array($_POS
}
}
\TorrentPier\Legacy\Group::delete_permissions($group_id, null, $cat_id);
\TorrentPier\Legacy\Group::store_permissions($group_id, $auth);
\TorrentPier\Legacy\Group::update_user_level('all');
delete_permissions($group_id, null, $cat_id);
store_permissions($group_id, $auth);
update_user_level('all');
$l_auth_return = $lang['CLICK_RETURN_GROUPAUTH'];
$message = $lang['AUTH_UPDATED'] . '<br /><br />';
$message .= sprintf($l_auth_return, '<a href="admin_ug_auth.php?mode=' . $mode . '&' . POST_GROUPS_URL . '=' . $group_id . '">', '</a>') . '<br /><br />';
$message .= sprintf($l_auth_return, '<a href="admin_ug_auth.php?mode=' . $mode . '">', '</a>') . '<br /><br />';
$message .= sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
@ -185,22 +197,22 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
$datastore->update('cat_forums');
$forums = $datastore->get('cat_forums');
}
$base_url = basename(__FILE__) . "?mode=user&amp;" . POST_USERS_URL . "=$user_id";
$base_url = basename(__FILE__) . "?mode=user&amp;u=$user_id";
$ug_data = $this_userdata;
$ug_data['session_logged_in'] = 1;
$u_access = auth(AUTH_ALL, AUTH_LIST_ALL, $ug_data, [], UG_PERM_USER_ONLY);
$g_access = auth(AUTH_ALL, AUTH_LIST_ALL, $ug_data, [], UG_PERM_GROUP_ONLY);
$u_access = auth(AUTH_ALL, AUTH_LIST_ALL, $ug_data, array(), UG_PERM_USER_ONLY);
$g_access = auth(AUTH_ALL, AUTH_LIST_ALL, $ug_data, array(), UG_PERM_GROUP_ONLY);
foreach ($forums['c'] as $c_id => $c_data) {
$template->assign_block_vars('c', array(
'CAT_ID' => $c_id,
'CAT_TITLE' => $forums['cat_title_html'][$c_id],
'CAT_HREF' => "$base_url&amp;" . POST_CAT_URL . "=$c_id",
'CAT_HREF' => "$base_url&amp;c=$c_id",
));
if (!$c =& $_REQUEST[POST_CAT_URL] or !in_array($c, array('all', $c_id)) or empty($c_data['forums'])) {
if (!$c =& $_REQUEST['c'] or !in_array($c, array('all', $c_id)) or empty($c_data['forums'])) {
continue;
}
@ -271,21 +283,21 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
$s_user_type = ($this_userdata['user_level'] == ADMIN) ? '
<select name="userlevel">
<option value="admin" selected>' . $lang['AUTH_ADMIN'] . '</option>
<option value="admin" selected="selected">' . $lang['AUTH_ADMIN'] . '</option>
<option value="user">' . $lang['AUTH_USER'] . '</option>
</select>
' : '
<select name="userlevel">
<option value="admin">' . $lang['AUTH_ADMIN'] . '</option>
<option value="user" selected>' . $lang['AUTH_USER'] . '</option>
<option value="user" selected="selected">' . $lang['AUTH_USER'] . '</option>
</select>
';
$template->assign_block_vars('switch_user_auth', []);
$template->assign_block_vars('switch_user_auth', array());
$template->assign_vars(array(
'TPL_AUTH_UG_MAIN' => true,
'USER_OR_GROUPNAME' => profile_url($this_userdata, true),
'USER_OR_GROUPNAME' => $this_userdata['username'],
'USER_LEVEL' => $lang['USER_LEVEL'] . ' : ' . $s_user_type,
'T_USER_OR_GROUPNAME' => $lang['USERNAME'],
'T_AUTH_TITLE' => $lang['AUTH_CONTROL_USER'],
@ -296,7 +308,7 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
} elseif ($mode == 'group' && $group_id) {
$page_cfg['quirks_mode'] = true;
if (!$group_data = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$group_data = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
@ -304,7 +316,7 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
$datastore->update('cat_forums');
$forums = $datastore->get('cat_forums');
}
$base_url = basename(__FILE__) . "?mode=group&amp;" . POST_GROUPS_URL . "=$group_id";
$base_url = basename(__FILE__) . "?mode=group&amp;g=$group_id";
$ug_data = array('group_id' => $group_id);
$u_access = auth(AUTH_ALL, AUTH_LIST_ALL, $ug_data);
@ -313,10 +325,10 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
$template->assign_block_vars('c', array(
'CAT_ID' => $c_id,
'CAT_TITLE' => $forums['cat_title_html'][$c_id],
'CAT_HREF' => "$base_url&amp;" . POST_CAT_URL . "=$c_id",
'CAT_HREF' => "$base_url&amp;c=$c_id",
));
if (!($c =& $_REQUEST[POST_CAT_URL]) || !in_array($c, array('all', $c_id)) || empty($c_data['forums'])) {
if (!($c =& $_REQUEST['c']) || !in_array($c, array('all', $c_id)) || empty($c_data['forums'])) {
continue;
}
@ -381,7 +393,7 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
$s_hidden_fields = '
<input type="hidden" name="mode" value="' . $mode . '" />
<input type="hidden" name="' . POST_GROUPS_URL . '" value="' . $group_id . '" />
<input type="hidden" name="g" value="' . $group_id . '" />
';
$template->assign_vars(array(
@ -390,7 +402,7 @@ if ($mode == 'user' && (!empty($_POST['username']) || $user_id)) {
'USER_LEVEL' => false,
'T_AUTH_TITLE' => $lang['AUTH_CONTROL_GROUP'],
'T_AUTH_EXPLAIN' => $lang['GROUP_AUTH_EXPLAIN'],
'USER_OR_GROUPNAME' => ('<span class="gen">' . htmlCHR($group_data['group_name']) . '</span>'),
'USER_OR_GROUPNAME' => htmlCHR($group_data['group_name']),
'S_COLUMN_SPAN' => $s_column_span,
'S_HIDDEN_FIELDS' => $s_hidden_fields,
));
@ -419,8 +431,8 @@ $template->assign_vars(array(
'YES_SIGN' => $yes_sign,
'NO_SIGN' => $no_sign,
'S_AUTH_ACTION' => 'admin_ug_auth.php',
'SELECTED_CAT' => !empty($_REQUEST[POST_CAT_URL]) ? $_REQUEST[POST_CAT_URL] : '',
'U_ALL_FORUMS' => !empty($base_url) ? "$base_url&amp;" . POST_CAT_URL . "=all" : '',
'SELECTED_CAT' => !empty($_REQUEST['c']) ? $_REQUEST['c'] : '',
'U_ALL_FORUMS' => !empty($base_url) ? "$base_url&amp;c=all" : '',
));
print_page('admin_ug_auth.tpl', 'admin');

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -14,32 +30,121 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php';
$submit = isset($_POST['submit']);
if (isset($_POST['submit'])) {
$user_bansql = '';
$email_bansql = '';
$ip_bansql = '';
// Check for demo mode
if (IN_DEMO_MODE && $submit) {
bb_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
if ($submit) {
// Ban action
$user_list = [];
if (!empty($_POST['username'])) {
if (!$this_userdata = get_userdata($_POST['username'], true)) {
bb_die($lang['NO_USER_ID_SPECIFIED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_BANADMIN'], '<a href="admin_user_ban.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
$this_userdata = get_userdata($_POST['username'], true);
if (!$this_userdata) {
bb_die($lang['NO_USER_ID_SPECIFIED']);
}
if (!getBanInfo((int)$this_userdata['user_id'])) {
$sql = 'INSERT INTO ' . BB_BANLIST . ' (ban_userid) VALUES (' . $this_userdata['user_id'] . ')';
$user_list[] = $this_userdata['user_id'];
}
$ip_list = [];
if (isset($_POST['ban_ip'])) {
$ip_list_temp = explode(',', $_POST['ban_ip']);
foreach ($ip_list_temp as $ip) {
if (Longman\IPTools\Ip::isValid($ip)) {
$ip_list[] = encode_ip($ip);
}
}
}
$email_list = [];
if (isset($_POST['ban_email'])) {
$email_list_temp = explode(',', $_POST['ban_email']);
for ($i = 0, $iMax = count($email_list_temp); $i < $iMax; $i++) {
if (preg_match('/^(([a-z0-9&\'\.\-_\+])|(\*))+@(([a-z0-9\-])|(\*))+\.([a-z0-9\-]+\.)*?[a-z]+$/is', trim($email_list_temp[$i]))) {
$email_list[] = trim($email_list_temp[$i]);
}
}
}
$sql = 'SELECT * FROM ' . BB_BANLIST;
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not obtain banlist information');
}
$current_banlist = DB()->sql_fetchrowset($result);
DB()->sql_freeresult($result);
$kill_session_sql = '';
for ($i = 0, $iMax = count($user_list); $i < $iMax; $i++) {
$in_banlist = false;
for ($j = 0, $jMax = count($current_banlist); $j < $jMax; $j++) {
if ($user_list[$i] == $current_banlist[$j]['ban_userid']) {
$in_banlist = true;
}
}
if (!$in_banlist) {
$kill_session_sql .= (($kill_session_sql != '') ? ' OR ' : '') . 'session_user_id = ' . $user_list[$i];
$sql = 'INSERT INTO ' . BB_BANLIST . ' (ban_userid) VALUES (' . $user_list[$i] . ')';
if (!DB()->sql_query($sql)) {
bb_die('Could not insert ban_userid info into database');
}
}
}
// Unban action
for ($i = 0, $iMax = count($ip_list); $i < $iMax; $i++) {
$in_banlist = false;
for ($j = 0, $jMax = count($current_banlist); $j < $jMax; $j++) {
if ($ip_list[$i] == $current_banlist[$j]['ban_ip']) {
$in_banlist = true;
}
}
if (!$in_banlist) {
if (preg_match('/(ff\.)|(\.ff)/is', chunk_split($ip_list[$i], 2, '.'))) {
$kill_ip_sql = "session_ip LIKE '" . str_replace('.', '', preg_replace('/(ff\.)|(\.ff)/is', '%', chunk_split($ip_list[$i], 2, '.'))) . "'";
} else {
$kill_ip_sql = "session_ip = '" . $ip_list[$i] . "'";
}
$kill_session_sql .= (($kill_session_sql != '') ? ' OR ' : '') . $kill_ip_sql;
$sql = 'INSERT INTO ' . BB_BANLIST . " (ban_ip) VALUES ('" . $ip_list[$i] . "')";
if (!DB()->sql_query($sql)) {
bb_die('Could not insert ban_ip info into database');
}
}
}
// Now we'll delete all entries from the session table
if ($kill_session_sql != '') {
$sql = 'DELETE FROM ' . BB_SESSIONS . " WHERE $kill_session_sql";
if (!DB()->sql_query($sql)) {
bb_die('Could not delete banned sessions from database');
}
}
for ($i = 0, $iMax = count($email_list); $i < $iMax; $i++) {
$in_banlist = false;
for ($j = 0, $jMax = count($current_banlist); $j < $jMax; $j++) {
if ($email_list[$i] == $current_banlist[$j]['ban_email']) {
$in_banlist = true;
}
}
if (!$in_banlist) {
$sql = 'INSERT INTO ' . BB_BANLIST . " (ban_email) VALUES ('" . DB()->escape($email_list[$i]) . "')";
if (!DB()->sql_query($sql)) {
bb_die('Could not insert ban_email info into database');
}
}
}
$where_sql = '';
if (!empty($_POST['unban_user'])) {
if (isset($_POST['unban_user'])) {
$user_list = $_POST['unban_user'];
for ($i = 0, $iMax = count($user_list); $i < $iMax; $i++) {
@ -47,35 +152,113 @@ if ($submit) {
$where_sql .= (($where_sql != '') ? ', ' : '') . (int)$user_list[$i];
}
}
}
if ($where_sql != '') {
$sql = 'DELETE FROM ' . BB_BANLIST . " WHERE ban_id IN ($where_sql)";
if (!DB()->sql_query($sql)) {
bb_die('Could not delete ban info from database');
if (isset($_POST['unban_ip'])) {
$ip_list = $_POST['unban_ip'];
for ($i = 0, $iMax = count($ip_list); $i < $iMax; $i++) {
if ($ip_list[$i] != -1) {
$where_sql .= (($where_sql != '') ? ', ' : '') . DB()->escape($ip_list[$i]);
}
}
}
$datastore->update('ban_list');
if (isset($_POST['unban_email'])) {
$email_list = $_POST['unban_email'];
for ($i = 0, $iMax = count($email_list); $i < $iMax; $i++) {
if ($email_list[$i] != -1) {
$where_sql .= (($where_sql != '') ? ', ' : '') . DB()->escape($email_list[$i]);
}
}
}
if ($where_sql != '') {
$sql = 'DELETE FROM ' . BB_BANLIST . " WHERE ban_id IN ($where_sql)";
if (!DB()->sql_query($sql)) {
bb_die('Could not delete ban info from database');
}
}
bb_die($lang['BAN_UPDATE_SUCESSFUL'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_BANADMIN'], '<a href="admin_user_ban.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} else {
$template->assign_vars(['S_BANLIST_ACTION' => 'admin_user_ban.php']);
$template->assign_vars(array(
'S_BANLIST_ACTION' => 'admin_user_ban.php',
));
$userban_count = 0;
$ipban_count = 0;
$emailban_count = 0;
$sql = 'SELECT b.ban_id, u.user_id, u.username
FROM ' . BB_BANLIST . ' b, ' . BB_USERS . ' u
WHERE u.user_id = b.ban_userid
AND b.ban_userid <> 0
AND u.user_id <> ' . GUEST_UID . '
ORDER BY u.username ASC';
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not select current user_id ban list');
}
$user_list = DB()->sql_fetchrowset($result);
DB()->sql_freeresult($result);
$select_userlist = '';
foreach (getBanInfo() as $ban) {
$select_userlist .= '<option value="' . $ban['ban_id'] . '">' . get_username($ban['ban_userid']) . '</option>';
for ($i = 0, $iMax = count($user_list); $i < $iMax; $i++) {
$select_userlist .= '<option value="' . $user_list[$i]['ban_id'] . '">' . $user_list[$i]['username'] . '</option>';
$userban_count++;
}
if ($select_userlist == '') {
$select_userlist = '<option value="-1">' . $lang['NO_BANNED_USERS'] . '</option>';
}
$select_userlist = '<select name="unban_user[]" multiple size="5">' . $select_userlist . '</select>';
$template->assign_vars([
$sql = 'SELECT ban_id, ban_ip, ban_email FROM ' . BB_BANLIST . ' ORDER BY ban_ip';
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not select current ip ban list');
}
$banlist = DB()->sql_fetchrowset($result);
DB()->sql_freeresult($result);
$select_iplist = '';
$select_emaillist = '';
for ($i = 0, $iMax = count($banlist); $i < $iMax; $i++) {
$ban_id = $banlist[$i]['ban_id'];
if (!empty($banlist[$i]['ban_ip'])) {
$ban_ip = str_replace('255', '*', decode_ip($banlist[$i]['ban_ip']));
$select_iplist .= '<option value="' . $ban_id . '">' . $ban_ip . '</option>';
$ipban_count++;
} elseif (!empty($banlist[$i]['ban_email'])) {
$ban_email = $banlist[$i]['ban_email'];
$select_emaillist .= '<option value="' . $ban_id . '">' . $ban_email . '</option>';
$emailban_count++;
}
}
if ($select_iplist == '') {
$select_iplist = '<option value="-1">' . $lang['NO_BANNED_IP'] . '</option>';
}
if ($select_emaillist == '') {
$select_emaillist = '<option value="-1">' . $lang['NO_BANNED_EMAIL'] . '</option>';
}
$select_iplist = '<select name="unban_ip[]" multiple size="15">' . $select_iplist . '</select>';
$select_emaillist = '<select name="unban_email[]" multiple size="10">' . $select_emaillist . '</select>';
$template->assign_vars(array(
'U_SEARCH_USER' => './../search.php?mode=searchuser',
'S_UNBAN_USERLIST_SELECT' => $select_userlist,
'S_BAN_ACTION' => 'admin_user_ban.php'
]);
'S_UNBAN_IPLIST_SELECT' => $select_iplist,
'S_UNBAN_EMAILLIST_SELECT' => $select_emaillist,
'S_BAN_ACTION' => 'admin_user_ban.php',
));
}
print_page('admin_user_ban.tpl', 'admin');

View file

@ -1,21 +1,38 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
$module['USERS']['SEARCH'] = basename(__FILE__);
return;
}
require __DIR__ . '/pagestart.php';
array_deep($_POST, 'trim');
require INC_DIR . '/functions_selects.php';
$total_sql = '';
if (!isset($_REQUEST['dosearch'])) {
@ -31,20 +48,20 @@ if (!isset($_REQUEST['dosearch'])) {
$group_list = '';
if (DB()->num_rows($result) != 0) {
$template->assign_block_vars('groups_exist', []);
$template->assign_block_vars('groups_exist', array());
while ($row = DB()->sql_fetchrow($result)) {
$group_list .= '<option value="' . $row['group_id'] . '">' . strip_tags(htmlspecialchars($row['group_name'])) . '</option>';
}
}
$sql = 'SELECT * FROM ' . BB_RANKS . ' ORDER BY rank_title';
$sql = 'SELECT * FROM ' . BB_RANKS . ' WHERE rank_special = 1 ORDER BY rank_title';
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not obtain ranks data');
}
$rank_select_box = '';
if (DB()->num_rows($result) != 0) {
$template->assign_block_vars('ranks_exist', []);
$template->assign_block_vars('ranks_exist', array());
while ($row = DB()->sql_fetchrow($result)) {
$rank = $row['rank_title'];
$rank_id = $row['rank_id'];
@ -52,8 +69,8 @@ if (!isset($_REQUEST['dosearch'])) {
}
}
$language_list = \TorrentPier\Legacy\Common\Select::language('', 'language_type');
$timezone_list = \TorrentPier\Legacy\Common\Select::timezone('', 'timezone_type');
$language_list = language_select('', 'language_type');
$timezone_list = tz_select('', 'timezone_type');
$sql = 'SELECT f.forum_id, f.forum_name, f.forum_parent, c.cat_id, c.cat_title
FROM ( ' . BB_FORUMS . ' AS f INNER JOIN ' . BB_CATEGORIES . ' AS c ON c.cat_id = f.cat_id )
@ -63,12 +80,13 @@ if (!isset($_REQUEST['dosearch'])) {
bb_die('Could not select forum data');
}
$forums = [];
$last_cat_id = -1;
$forums_list = '';
$forums = array();
if (DB()->num_rows($result) != 0) {
$template->assign_block_vars('forums_exist', []);
$template->assign_block_vars('forums_exist', array());
$last_cat_id = -1;
$forums_list = '';
while ($row = DB()->sql_fetchrow($result)) {
if ($row['cat_id'] != $last_cat_id) {
@ -80,14 +98,14 @@ if (!isset($_REQUEST['dosearch'])) {
}
}
$lastvisited = [1, 7, 14, 30, 60, 120, 365, 500, 730, 1000];
$lastvisited = array(1, 7, 14, 30, 60, 120, 365, 500, 730, 1000);
$lastvisited_list = '';
foreach ($lastvisited as $days) {
$lastvisited_list .= '<option value="' . $days . '">' . delta_time((TIMENOW - 86400 * $days), TIMENOW, 'days') . '</option>';
$lastvisited_list .= '<option value="' . $days . '">' . $days . ' ' . (($days > 1) ? $lang['DAYS'] : $lang['DAY']) . '</option>';
}
$template->assign_vars([
$template->assign_vars(array(
'TPL_ADMIN_USER_SEARCH_MAIN' => true,
'YEAR' => date('Y'),
@ -100,9 +118,8 @@ if (!isset($_REQUEST['dosearch'])) {
'FORUMS_LIST' => $forums_list,
'LASTVISITED_LIST' => $lastvisited_list,
'U_SEARCH_USER' => BB_ROOT . 'search.php?mode=searchuser',
'S_SEARCH_ACTION' => 'admin_user_search.php'
]);
'S_SEARCH_ACTION' => 'admin_user_search.php',
));
} else {
$mode = '';
@ -252,8 +269,9 @@ if (!isset($_REQUEST['dosearch'])) {
$text = sprintf($lang['SEARCH_FOR_USERNAME'], strip_tags(htmlspecialchars(stripslashes($username))));
$username = str_replace('*', '%', trim(strip_tags(strtolower($username))));
if (str_contains($username, '%')) {
$username = preg_replace('/\*/', '%', trim(strip_tags(strtolower($username))));
if (false !== strpos($username, '%')) {
$op = 'LIKE';
} else {
$op = '=';
@ -272,8 +290,9 @@ if (!isset($_REQUEST['dosearch'])) {
$text = sprintf($lang['SEARCH_FOR_EMAIL'], strip_tags(htmlspecialchars(stripslashes($email))));
$email = str_replace('*', '%', trim(strip_tags(strtolower($email))));
if (str_contains($email, '%')) {
$email = preg_replace('/\*/', '%', trim(strip_tags(strtolower($email))));
if (false !== strpos($email, '%')) {
$op = 'LIKE';
} else {
$op = '=';
@ -297,8 +316,8 @@ if (!isset($_REQUEST['dosearch'])) {
unset($users);
$users = [];
if (\TorrentPier\Helpers\IPHelper::isValid($ip_address)) {
$ip = \TorrentPier\Helpers\IPHelper::ip2long($ip_address);
if (Longman\IPTools\Ip::isValid($ip_address)) {
$ip = encode_ip($ip_address);
$users[] = $ip;
} else {
bb_die($lang['SEARCH_INVALID_IP']);
@ -312,7 +331,7 @@ if (!isset($_REQUEST['dosearch'])) {
$where_sql = '';
$where_sql .= ($ip_in_sql != '') ? "poster_ip IN ($ip_in_sql)" : '';
$where_sql .= ($ip_like_sql != '') ? ($where_sql != '') ? " OR $ip_like_sql" : (string)$ip_like_sql : '';
$where_sql .= ($ip_like_sql != '') ? ($where_sql != '') ? " OR $ip_like_sql" : "$ip_like_sql" : '';
if (!$where_sql) {
bb_die('invalid request');
@ -339,7 +358,7 @@ if (!isset($_REQUEST['dosearch'])) {
}
$where_sql = '';
$where_sql .= ($ip_in_sql != '') ? "user_last_ip IN ($ip_in_sql)" : '';
$where_sql .= ($ip_like_sql_flylast != '') ? ($where_sql != '') ? " OR $ip_like_sql_flylast" : (string)$ip_like_sql_flylast : '';
$where_sql .= ($ip_like_sql_flylast != '') ? ($where_sql != '') ? " OR $ip_like_sql_flylast" : "$ip_like_sql_flylast" : '';
$sql = 'SELECT user_id FROM ' . BB_USERS . ' WHERE user_id <> ' . GUEST_UID . " AND ($where_sql) GROUP BY user_id";
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not count users #2');
@ -356,7 +375,7 @@ if (!isset($_REQUEST['dosearch'])) {
}
$where_sql = '';
$where_sql .= ($ip_in_sql != '') ? "user_reg_ip IN ($ip_in_sql)" : '';
$where_sql .= ($ip_like_sql_flyreg != '') ? ($where_sql != '') ? " OR $ip_like_sql_flyreg" : (string)$ip_like_sql_flyreg : '';
$where_sql .= ($ip_like_sql_flyreg != '') ? ($where_sql != '') ? " OR $ip_like_sql_flyreg" : "$ip_like_sql_flyreg" : '';
$sql = 'SELECT user_id FROM ' . BB_USERS . ' WHERE user_id <> ' . GUEST_UID . " AND ($where_sql) GROUP BY user_id";
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not count users #3');
@ -463,7 +482,7 @@ if (!isset($_REQUEST['dosearch'])) {
bb_die($lang['SEARCH_INVALID_RANK']);
}
$sql = 'SELECT rank_title FROM ' . BB_RANKS . " WHERE rank_id = $rank_id";
$sql = 'SELECT rank_title FROM ' . BB_RANKS . " WHERE rank_id = $rank_id AND rank_special = 1";
if (!$result = DB()->sql_query($sql)) {
bb_die('Could not select rank data');
@ -521,7 +540,7 @@ if (!isset($_REQUEST['dosearch'])) {
break;
case 'equals':
// looking for a -
if (str_contains($postcount_value, '-')) {
if (false !== strpos($postcount_value, '-')) {
$range = preg_split('/[-\s]+/', $postcount_value);
$range_begin = (int)$range[0];
@ -566,8 +585,9 @@ if (!isset($_REQUEST['dosearch'])) {
$text = strip_tags(htmlspecialchars(stripslashes($userfield_value)));
$userfield_value = str_replace('*', '%', trim(strip_tags(strtolower($userfield_value))));
if (str_contains($userfield_value, '%')) {
$userfield_value = preg_replace('/\*/', '%', trim(strip_tags(strtolower($userfield_value))));
if (false !== strpos($userfield_value, '%')) {
$op = 'LIKE';
} else {
$op = '=';
@ -631,7 +651,7 @@ if (!isset($_REQUEST['dosearch'])) {
switch ($lastvisited_type) {
case 'in':
$text = sprintf($lang['SEARCH_FOR_LASTVISITED_INTHELAST'], delta_time((TIMENOW - 86400 * $lastvisited_days), TIMENOW, 'days'));
$text = sprintf($lang['SEARCH_FOR_LASTVISITED_INTHELAST'], $lastvisited_days, (($lastvisited_days > 1) ? $lang['DAYS'] : $lang['DAY']));
$total_sql .= 'SELECT COUNT(user_id) AS total
FROM ' . BB_USERS . "
@ -642,7 +662,7 @@ if (!isset($_REQUEST['dosearch'])) {
AND u.user_id <> " . GUEST_UID;
break;
case 'after':
$text = sprintf($lang['SEARCH_FOR_LASTVISITED_AFTERTHELAST'], delta_time((TIMENOW - 86400 * $lastvisited_days), TIMENOW, 'days'));
$text = sprintf($lang['SEARCH_FOR_LASTVISITED_AFTERTHELAST'], $lastvisited_days, (($lastvisited_days > 1) ? $lang['DAYS'] : $lang['DAY']));
$total_sql .= 'SELECT COUNT(user_id) AS total
FROM ' . BB_USERS . "
@ -841,10 +861,10 @@ if (!isset($_REQUEST['dosearch'])) {
if ($page == 1) {
$offset = 0;
} else {
$offset = (($page - 1) * config()->get('topics_per_page'));
$offset = (($page - 1) * $bb_cfg['topics_per_page']);
}
$limit = "LIMIT $offset, " . config()->get('topics_per_page');
$limit = "LIMIT $offset, " . $bb_cfg['topics_per_page'];
$select_sql .= " $limit";
@ -859,12 +879,12 @@ if (!isset($_REQUEST['dosearch'])) {
bb_die($lang['SEARCH_NO_RESULTS']);
}
}
$num_pages = ceil($total_pages['total'] / config()->get('topics_per_page'));
$num_pages = ceil($total_pages['total'] / $bb_cfg['topics_per_page']);
$pagination = '';
if ($page > 1) {
$pagination .= '<a href="' . $base_url . '&sort=' . $sort . '&order=' . $order . '&page=' . ($page - 1) . '">' . $lang['BACK'] . '</a>';
$pagination .= '<a href="' . $base_url . '&sort=' . $sort . '&order=' . $order . '&page=' . ($page - 1) . '">' . $lang['PREVIOUS'] . '</a>';
}
if ($page < $num_pages) {
$pagination .= ($pagination == '') ? '<a href="' . $base_url . '&sort=' . $sort . '&order=' . $order . '&page=' . ($page + 1) . '">' . $lang['NEXT'] . '</a>' : ' | <a href="' . $base_url . '&sort=' . $sort . '&order=' . $order . '&page=' . ($page + 1) . '">' . $lang['NEXT'] . '</a>';
@ -872,7 +892,7 @@ if (!isset($_REQUEST['dosearch'])) {
if ($num_pages > 2) {
$pagination .= '&nbsp;&nbsp;<input type="text" name="page" maxlength="5" size="2" class="post" />&nbsp;<input type="submit" name="submit" value="' . $lang['GO'] . '" class="post" />';
}
$template->assign_vars([
$template->assign_vars(array(
'TPL_ADMIN_USER_SEARCH_RESULTS' => true,
'PAGE_NUMBER' => sprintf($lang['PAGE_OF'], $page, $num_pages),
@ -886,7 +906,7 @@ if (!isset($_REQUEST['dosearch'])) {
'U_LASTVISIT' => ($sort == 'lastvisit') ? "$base_url&sort=$sort&order=$o_order" : "$base_url&sort=lastvisit&order=$order",
'S_POST_ACTION' => "$base_url&sort=$sort&order=$order"
]);
));
if (!$result = DB()->sql_query($select_sql)) {
bb_die('Could not select user data');
@ -908,7 +928,7 @@ if (!isset($_REQUEST['dosearch'])) {
unset($banned);
$banned = [];
$banned = array();
while ($row = DB()->sql_fetchrow($result)) {
$banned[$row['user_id']] = true;
@ -917,12 +937,12 @@ if (!isset($_REQUEST['dosearch'])) {
for ($i = 0, $iMax = count($rowset); $i < $iMax; $i++) {
$row_class = !($i % 2) ? 'row1' : 'row2';
$template->assign_block_vars('userrow', [
$template->assign_block_vars('userrow', array(
'ROW_CLASS' => $row_class,
'USER' => profile_url($rowset[$i], true),
'USER' => profile_url($rowset[$i]),
'EMAIL' => $rowset[$i]['user_email'],
'JOINDATE' => bb_date($rowset[$i]['user_regdate']),
'LASTVISIT' => $rowset[$i]['user_lastvisit'] ? bb_date($rowset[$i]['user_lastvisit']) : $lang['NEVER'],
'LASTVISIT' => bb_date($rowset[$i]['user_lastvisit']),
'POSTS' => $rowset[$i]['user_posts'],
'BAN' => (!isset($banned[$rowset[$i]['user_id']])) ? $lang['NOT_BANNED'] : $lang['BANNED'],
'ABLED' => $rowset[$i]['user_active'] ? $lang['ENABLED'] : $lang['DISABLED'],
@ -930,7 +950,7 @@ if (!isset($_REQUEST['dosearch'])) {
'U_VIEWPOSTS' => "../search.php?search_author=1&amp;uid={$rowset[$i]['user_id']}",
'U_MANAGE' => '../profile.php?mode=editprofile&' . POST_USERS_URL . '=' . $rowset[$i]['user_id'] . '&admin=1',
'U_PERMISSIONS' => 'admin_ug_auth.php?mode=user&' . POST_USERS_URL . '=' . $rowset[$i]['user_id'],
]);
));
}
}

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!empty($setmodules)) {
@ -14,8 +30,8 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php';
if (!config()->get('use_word_censor')) {
bb_die('Word censor disabled <br /><br /> (use_word_censor in config.php)');
if (!$bb_cfg['use_word_censor']) {
bb_die('Word censor disabled <br /><br /> ($bb_cfg[\'use_word_censor\'] in config.php)');
}
$mode = request_var('mode', '');
@ -49,13 +65,13 @@ if ($mode != '') {
}
}
$template->assign_vars([
$template->assign_vars(array(
'TPL_ADMIN_WORDS_EDIT' => true,
'WORD' => $word,
'REPLACEMENT' => $replacement,
'S_WORDS_ACTION' => 'admin_words.php',
'S_HIDDEN_FIELDS' => $s_hidden_fields,
]);
));
} elseif ($mode == 'save') {
$word_id = (int)request_var('id', 0);
$word = trim(request_var('word', ''));
@ -80,8 +96,7 @@ if ($mode != '') {
bb_die('Could not insert data into words table');
}
$datastore->update('censor');
censor()->reload(); // Reload the singleton instance with updated words
CACHE('bb_cache')->rm('censored');
$message .= '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message);
@ -95,8 +110,7 @@ if ($mode != '') {
bb_die('Could not remove data from words table');
}
$datastore->update('censor');
censor()->reload(); // Reload the singleton instance with updated words
CACHE('bb_cache')->rm('censored');
bb_die($lang['WORD_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} else {
@ -112,11 +126,11 @@ if ($mode != '') {
$word_rows = DB()->sql_fetchrowset($result);
$word_count = count($word_rows);
$template->assign_vars([
$template->assign_vars(array(
'TPL_ADMIN_WORDS_LIST' => true,
'S_WORDS_ACTION' => 'admin_words.php',
'S_HIDDEN_FIELDS' => ''
]);
'S_HIDDEN_FIELDS' => '',
));
for ($i = 0; $i < $word_count; $i++) {
$word = $word_rows[$i]['word'];
@ -125,13 +139,13 @@ if ($mode != '') {
$row_class = !($i % 2) ? 'row1' : 'row2';
$template->assign_block_vars('words', [
$template->assign_block_vars('words', array(
'ROW_CLASS' => $row_class,
'WORD' => $word,
'REPLACEMENT' => $replacement,
'U_WORD_EDIT' => "admin_words.php?mode=edit&amp;id=$word_id",
'U_WORD_DELETE' => "admin_words.php?mode=delete&amp;id=$word_id"
]);
'U_WORD_DELETE' => "admin_words.php?mode=delete&amp;id=$word_id",
));
}
}

View file

@ -1,34 +1,36 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
require __DIR__ . '/pagestart.php';
// Statistics
if (!$stats = $datastore->get('stats')) {
$datastore->update('stats');
$stats = $datastore->get('stats');
}
// Check for updates
if (!$update_data = $datastore->get('check_updates')) {
$datastore->update('check_updates');
$update_data = $datastore->get('check_updates');
}
// Generate relevant output
if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
$module = [];
// Scan modules
if (!CACHE('bb_cache')->get('admin_module_' . $user->id)) {
if (!$module = CACHE('bb_cache')->get('admin_module_' . $user->id)) {
$dir = opendir('.');
$setmodules = true;
$setmodules = 1;
while ($file = readdir($dir)) {
if (preg_match('/^admin_.*?\.php$/', $file)) {
include './' . $file;
@ -36,28 +38,25 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
}
unset($setmodules);
closedir($dir);
// Set modules into cache
CACHE('bb_cache')->set('admin_module_' . $user->id, $module, 600);
}
// Get modules from cache
$module = CACHE('bb_cache')->get('admin_module_' . $user->id);
$template->assign_vars([
$template->assign_vars(array(
'TPL_ADMIN_NAVIGATE' => true,
'U_FORUM_INDEX' => '../index.php',
'U_ADMIN_INDEX' => 'index.php?pane=right',
]);
));
ksort($module);
foreach ($module as $cat => $action_array) {
$cat = (!empty($lang[$cat])) ? $lang[$cat] : str_replace("_", ' ', $cat);
$cat = (!empty($lang[$cat])) ? $lang[$cat] : preg_replace('/_/', ' ', $cat);
$template->assign_block_vars('catrow', [
$template->assign_block_vars('catrow', array(
'ADMIN_CATEGORY' => $cat,
]);
));
ksort($action_array);
@ -65,41 +64,29 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
foreach ($action_array as $action => $file) {
$row_class = !($row_count % 2) ? 'row1' : 'row2';
$action = (!empty($lang[$action])) ? $lang[$action] : str_replace("_", ' ', $action);
$action = (!empty($lang[$action])) ? $lang[$action] : preg_replace('/_/', ' ', $action);
$template->assign_block_vars('catrow.modulerow', [
$template->assign_block_vars('catrow.modulerow', array(
'ROW_CLASS' => $row_class,
'ADMIN_MODULE' => $action,
'U_ADMIN_MODULE' => $file,
]);
));
$row_count++;
}
}
} elseif (isset($_GET['pane']) && $_GET['pane'] == 'right') {
$template->assign_vars([
$template->assign_vars(array(
'TPL_ADMIN_MAIN' => true,
'ADMIN_LOCK' => (bool)config()->get('board_disable'),
'ADMIN_LOCK_CRON' => is_file(BB_DISABLED),
]);
// Check for updates
if (isset($update_data['available_update'])) {
$template->assign_block_vars('updater', [
'UPDATE_AVAILABLE' => $update_data['available_update'],
'NEW_VERSION_NUMBER' => $update_data['latest_version'],
'NEW_VERSION_SIZE' => $update_data['latest_version_size'],
'NEW_VERSION_DL_LINK' => $update_data['latest_version_dl_link'],
'NEW_VERSION_LINK' => $update_data['latest_version_link'],
'NEW_VERSION_HASH' => $update_data['latest_version_checksum']
]);
}
'ADMIN_LOCK' => $bb_cfg['board_disable'] ? true : false,
'ADMIN_LOCK_CRON' => file_exists(BB_DISABLED) ? true : false,
));
// Get forum statistics
$total_posts = $stats['postcount'];
$total_topics = $stats['topiccount'];
$total_users = $stats['usercount'];
$start_date = bb_date(config()->get('board_startdate'));
$boarddays = (TIMENOW - config()->get('board_startdate')) / 86400;
$total_posts = get_db_stat('postcount');
$total_users = get_db_stat('usercount');
$total_topics = get_db_stat('topiccount');
$start_date = bb_date($bb_cfg['board_startdate']);
$boarddays = (TIMENOW - $bb_cfg['board_startdate']) / 86400;
$posts_per_day = sprintf('%.2f', $total_posts / $boarddays);
$topics_per_day = sprintf('%.2f', $total_topics / $boarddays);
@ -107,10 +94,10 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
$avatar_dir_size = 0;
if ($avatar_dir = opendir(config()->get('avatars.upload_path'))) {
if ($avatar_dir = opendir($bb_cfg['avatars']['upload_path'])) {
while ($file = readdir($avatar_dir)) {
if ($file != '.' && $file != '..') {
$avatar_dir_size += @filesize(config()->get('avatars.upload_path') . $file);
$avatar_dir_size += @filesize(BB_ROOT . $bb_cfg['avatar_path'] . '/' . $file);
}
}
closedir($avatar_dir);
@ -132,7 +119,7 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
$users_per_day = $total_users;
}
$template->assign_vars([
$template->assign_vars(array(
'NUMBER_OF_POSTS' => $total_posts,
'NUMBER_OF_TOPICS' => $total_topics,
'NUMBER_OF_USERS' => $total_users,
@ -141,12 +128,12 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
'TOPICS_PER_DAY' => $topics_per_day,
'USERS_PER_DAY' => $users_per_day,
'AVATAR_DIR_SIZE' => $avatar_dir_size,
]);
));
if (isset($_GET['users_online'])) {
$template->assign_vars([
$template->assign_vars(array(
'SHOW_USERS_ONLINE' => true,
]);
));
// Get users online information.
$sql = 'SELECT u.user_id, u.username, u.user_rank, s.session_time AS user_session_time, u.user_opt, s.session_logged_in, s.session_ip, s.session_start
@ -161,7 +148,6 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
}
$onlinerow_reg = DB()->sql_fetchrowset($result);
// Get guests online information.
$sql = 'SELECT session_logged_in, session_time, session_ip, session_start
FROM ' . BB_SESSIONS . '
WHERE session_logged_in = 0
@ -172,23 +158,38 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
}
$onlinerow_guest = DB()->sql_fetchrowset($result);
// Reg users
$reg_userid_ary = array();
if (count($onlinerow_reg)) {
$users_count = 0;
$registered_users = $hidden_users = 0;
for ($i = 0, $iMax = count($onlinerow_reg); $i < $iMax; $i++) {
$users_count++;
$row_class = 'row1';
$reg_ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($onlinerow_reg[$i]['session_ip']);
if (!in_array($onlinerow_reg[$i]['user_id'], $reg_userid_ary)) {
$reg_userid_ary[] = $onlinerow_reg[$i]['user_id'];
$template->assign_block_vars('reg_user_row', [
'ROW_CLASS' => $row_class,
'USER' => profile_url($onlinerow_reg[$i], true),
'STARTED' => bb_date($onlinerow_reg[$i]['session_start'], 'd-M-Y H:i', false),
'LASTUPDATE' => bb_date($onlinerow_reg[$i]['user_session_time'], 'd-M-Y H:i', false),
'IP_ADDRESS' => $reg_ip,
'U_WHOIS_IP' => config()->get('whois_info') . $reg_ip,
]);
$username = $onlinerow_reg[$i]['username'];
if (bf($onlinerow_reg[$i]['user_opt'], 'user_opt', 'user_viewonline')) {
$hidden_users++;
$hidden = true;
} else {
$registered_users++;
$hidden = false;
}
$row_class = 'row1';
$reg_ip = decode_ip($onlinerow_reg[$i]['session_ip']);
$template->assign_block_vars('reg_user_row', array(
'ROW_CLASS' => $row_class,
'USER' => profile_url($onlinerow_reg[$i]),
'STARTED' => bb_date($onlinerow_reg[$i]['session_start'], 'H:i', false),
'LASTUPDATE' => bb_date($onlinerow_reg[$i]['user_session_time'], 'H:i', false),
'IP_ADDRESS' => $reg_ip,
'U_WHOIS_IP' => $bb_cfg['whois_info'] . $reg_ip,
));
}
}
}
@ -197,30 +198,33 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
$guest_users = 0;
for ($i = 0, $iMax = count($onlinerow_guest); $i < $iMax; $i++) {
$guest_userip_ary[] = $onlinerow_guest[$i]['session_ip'];
$guest_users++;
$row_class = 'row2';
$guest_ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($onlinerow_guest[$i]['session_ip']);
$template->assign_block_vars('guest_user_row', [
$row_class = 'row2';
$guest_ip = decode_ip($onlinerow_guest[$i]['session_ip']);
$template->assign_block_vars('guest_user_row', array(
'ROW_CLASS' => $row_class,
'STARTED' => bb_date($onlinerow_guest[$i]['session_start'], 'd-M-Y H:i', false),
'LASTUPDATE' => bb_date($onlinerow_guest[$i]['session_time'], 'd-M-Y H:i', false),
'STARTED' => bb_date($onlinerow_guest[$i]['session_start'], 'H:i', false),
'LASTUPDATE' => bb_date($onlinerow_guest[$i]['session_time'], 'H:i', false),
'IP_ADDRESS' => $guest_ip,
'U_WHOIS_IP' => config()->get('whois_info') . $guest_ip,
]);
'U_WHOIS_IP' => $bb_cfg['whois_info'] . $guest_ip,
));
}
}
} else {
$template->assign_vars([
$template->assign_vars(array(
'USERS_ONLINE_HREF' => 'index.php?pane=right&users_online=1',
]);
));
}
} else {
// Generate frameset
$template->assign_vars([
'CONTENT_ENCODING' => DEFAULT_CHARSET,
$template->assign_vars(array(
'CONTENT_ENCODING' => $bb_cfg['charset'],
'TPL_ADMIN_FRAMESET' => true,
]);
));
send_no_cache_headers();
print_page('index.tpl', 'admin', 'no_header');
}

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_ROOT', './../');
@ -13,6 +29,7 @@ define('IN_ADMIN', true);
require dirname(__DIR__) . '/common.php';
require ATTACH_DIR . '/attachment_mod.php';
require ATTACH_DIR . '/includes/functions_admin.php';
require_once INC_DIR . '/functions_admin.php';
$user->session_start();
@ -26,5 +43,5 @@ if (!IS_ADMIN) {
if (!$userdata['session_admin']) {
$redirect = url_arg($_SERVER['REQUEST_URI'], 'admin', 1);
redirect(LOGIN_URL . "?redirect=$redirect");
redirect("login.php?redirect=$redirect");
}

View file

@ -1,15 +1,30 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_ROOT', './../../');
define('IN_ADMIN', true);
define('BB_ROOT', './../../');
require BB_ROOT . 'common.php';
$user->session_start();
@ -18,9 +33,9 @@ if (!IS_ADMIN) {
bb_die($lang['NOT_AUTHORISED']);
}
$sql[] = 'SELECT count(*) FROM `' . BB_USERS . '` WHERE `user_lastvisit` < UNIX_TIMESTAMP()-2592000 AND user_id NOT IN (' . EXCLUDED_USERS . ')';
$sql[] = 'SELECT count(*) FROM `' . BB_USERS . '` WHERE `user_lastvisit` < UNIX_TIMESTAMP()-7776000 AND user_id NOT IN (' . EXCLUDED_USERS . ')';
$sql[] = 'SELECT round(avg(size)) FROM `' . BB_BT_TORRENTS . '`';
$sql[] = 'SELECT count(*) FROM `' . BB_USERS . '` WHERE `user_lastvisit` < UNIX_TIMESTAMP()-2592000';
$sql[] = 'SELECT count(*) FROM `' . BB_USERS . '` WHERE `user_lastvisit` < UNIX_TIMESTAMP()-7776000';
$sql[] = 'SELECT round(avg(size)/1048576) FROM `' . BB_BT_TORRENTS . '`';
$sql[] = 'SELECT count(*) FROM `' . BB_BT_TORRENTS . '`';
$sql[] = 'SELECT count(distinct(topic_id)) FROM `' . BB_BT_TRACKER_SNAP . '` WHERE seeders > 0';
$sql[] = 'SELECT count(distinct(topic_id)) FROM `' . BB_BT_TRACKER_SNAP . '` WHERE seeders > 5';
@ -31,15 +46,21 @@ echo '<html><body><head></head>';
echo '<br /><br /><table border="1" cellspacing="0" cellpadding="6" align="center">';
foreach ($sql as $i => $query) {
$result = DB()->fetch_row($query);
$row = array_values($result)[0]; // Get first column value
$row = ($i == 2) ? humn_size($row) : $row;
echo "<tr><td>{$lang['TR_STATS'][$i]}</td><td><b>$row</b></td>";
$row = mysqli_fetch_row(DB()->query($query));
echo "<tr><td>{$lang['TR_STATS'][$i]}</td><td><b>{$row[0]}</b></td>";
}
echo '</table>';
echo '<div align="center"><pre>';
if ($l = sys('la')) {
$l = explode(' ', $l);
for ($i = 0; $i < 3; $i++) {
$l[$i] = round($l[$i], 1);
}
echo "\n\n<b>loadavg: </b>$l[0] $l[1] $l[2]\n\n";
}
echo 'gen time: <b>' . sprintf('%.3f', array_sum(explode(' ', microtime())) - TIMESTART) . "</b> sec\n";
echo '</pre></div>';

View file

@ -1,15 +1,30 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_ROOT', './../../');
define('IN_ADMIN', true);
define('BB_ROOT', './../../');
require BB_ROOT . 'common.php';
$user->session_start();
@ -21,8 +36,8 @@ if (!IS_ADMIN) {
$peers_in_last_minutes = [30, 15, 5, 1];
$peers_in_last_sec_limit = 300;
$announce_interval = (int)config()->get('announce_interval');
$stat = [];
$announce_interval = (int)$bb_cfg['announce_interval'];
$stat = array();
define('TMP_TRACKER_TABLE', 'tmp_tracker');
@ -30,16 +45,14 @@ DB()->query('
CREATE TEMPORARY TABLE ' . TMP_TRACKER_TABLE . " (
`topic_id` mediumint(8) unsigned NOT NULL default '0',
`user_id` mediumint(9) NOT NULL default '0',
`ip` char(42) binary default '0',
`ipv6` char(42) binary default '0',
`peer_id` char(20) binary default '0',
`ip` char(8) binary NOT NULL default '0',
`seeder` tinyint(1) NOT NULL default '0',
`speed_up` mediumint(8) unsigned NOT NULL default '0',
`speed_down` mediumint(8) unsigned NOT NULL default '0',
`update_time` int(11) NOT NULL default '0'
)
SELECT
topic_id, user_id, ip, ipv6, peer_id, seeder, speed_up, speed_down, update_time
topic_id, user_id, ip, seeder, speed_up, speed_down, update_time
FROM " . BB_BT_TRACKER . '
');
@ -52,7 +65,7 @@ $stat += DB()->fetch_row('SELECT COUNT(DISTINCT user_id) AS u_bt_active FROM ' .
// All bt-users
$stat += DB()->fetch_row('SELECT COUNT(*) AS u_bt_all FROM ' . BB_BT_USERS);
// All bb-users
$stat += DB()->fetch_row('SELECT COUNT(*) AS u_bb_all FROM ' . BB_USERS . ' WHERE user_id != ' . BOT_UID);
$stat += DB()->fetch_row('SELECT COUNT(*) AS u_bb_all FROM ' . BB_USERS);
// Active torrents
$stat += DB()->fetch_row('SELECT COUNT(DISTINCT topic_id) AS tor_active FROM ' . TMP_TRACKER_TABLE);
// With seeder
@ -61,7 +74,7 @@ $stat += DB()->fetch_row('SELECT COUNT(DISTINCT topic_id) AS tor_with_seeder FRO
$stat += DB()->fetch_row('SELECT COUNT(*) AS tor_all, SUM(size) AS torrents_size FROM ' . BB_BT_TORRENTS);
// Last xx minutes
$peers_in_last_min = [];
$peers_in_last_min = array();
foreach ($peers_in_last_minutes as $t) {
$row = DB()->fetch_row('
SELECT COUNT(*) AS peers FROM ' . TMP_TRACKER_TABLE . ' WHERE update_time >= ' . (TIMENOW - 60 * $t) . '
@ -69,57 +82,12 @@ foreach ($peers_in_last_minutes as $t) {
$peers_in_last_min[$t] = (int)$row['peers'];
}
// Last xx seconds
$peers_in_last_sec = [];
$rowset = DB()->fetch_rowset('SELECT COUNT(*) AS peers FROM ' . TMP_TRACKER_TABLE . ' ORDER BY update_time DESC LIMIT ' . $peers_in_last_sec_limit);
$peers_in_last_sec = array();
$rowset = DB()->fetch_rowset('SELECT COUNT(*) AS peers FROM ' . TMP_TRACKER_TABLE . " GROUP BY update_time DESC LIMIT $peers_in_last_sec_limit");
foreach ($rowset as $cnt => $row) {
$peers_in_last_sec[] = sprintf('%3s', $row['peers']) . (($cnt && !(++$cnt % 15)) ? " \n" : '');
}
// Detailed statistics for peer clients
$client_list = '';
$clients_percentage = [];
$numwant = !empty($_GET['client_numwant']) ? (int)$_GET['client_numwant'] : 100;
$client_full = !empty($_GET['client_length']) ? (int)$_GET['client_length'] : false;
if ($client_full || !$stats_cache = CACHE('tr_cache')->get('tracker_clients_stats')) {
$rowset = DB()->fetch_rowset('SELECT peer_id AS client FROM ' . TMP_TRACKER_TABLE);
if (!empty($rowset)) {
$client_count = 0;
foreach ($rowset as $cnt => $row) {
$clientString = $client_full ? substr($row['client'], 0, $client_full) : substr($row['client'], 0, 3);
if (!isset($clients[$clientString])) {
$clients[$clientString] = 1;
} else {
$clients[$clientString]++;
}
$client_count++;
}
arsort($clients, SORT_NUMERIC);
foreach ($clients as $client => $count) {
$percentage = number_format(($count / $client_count) * 100, 2);
$clients_percentage[$client] = "[$count] => $percentage%";
}
if (!$client_full) {
CACHE('tr_cache')->set('tracker_clients_stats', $clients_percentage, 3600);
}
}
} else {
$clients_percentage = $stats_cache;
}
$n = 1;
foreach (array_slice($clients_percentage, 0, $numwant) as $client => $value) {
$client_list .= ($client_full) ? ("$client => $value<br/>") : "$n. " . get_user_torrent_client($client) . " $value<br/>";
$n++;
}
function commify_callback($matches)
{
return commify($matches[0]);
@ -159,24 +127,21 @@ echo "\n<tr><td align=center> peers: in last " . implode(' / ', $peers_in_last_m
echo "\n<td align=center>" . implode(' / ', $peers_in_last_min) . "</td></tr>\n";
echo "\n<tr><td align=center> peers in last $peers_in_last_sec_limit sec <br /> [ per second, DESC order --> ] <br /> last peer: $stat[last_peer_time] seconds ago <br /> " . date('j M H:i:s [T O]') . " </td>\n";
echo '<td align=center style="font-size: 13px; font-family: \'Courier New\',Courier,monospace;"><pre> ' . implode(' ', $peers_in_last_sec) . "</pre></td></tr>\n";
echo "\n
<tr><td align=center> clients: </td>
<td align=center>
$client_list
<br/>
\n";
echo (count($clients_percentage) > $numwant) ? ('<a href="' . 'tracker.php?client_numwant=' . ($numwant + 100) . '">' . 'Show more' . '</a><br/>') : '';
echo $client_full ? '<br/><b>Get more length and numbers via modifying the parameters in the url<b>' : (!empty($client_list) ? '<a href="tracker.php?client_length=6&client_numwant=10">Peer_ids with more length (version debugging)</a>' : '');
echo '</td></tr>';
echo '</table>';
echo !$client_full ? '<p style = "text-align:right;">Simple stats for clients are being cached for one hour.</p>' : '';
echo '<div align="center"><pre>';
if ($l = sys('la')) {
$l = explode(' ', $l);
for ($i = 0; $i < 3; $i++) {
$l[$i] = round($l[$i], 1);
}
echo "\n\n<b>loadavg: </b>$l[0] $l[1] $l[2]\n\n";
}
echo 'gen time: <b>' . sprintf('%.3f', array_sum(explode(' ', microtime())) - TIMESTART) . "</b> sec\n";
echo '</pre></div>';
echo '</body></html>';
DB()->query('DROP TEMPORARY TABLE ' . TMP_TRACKER_TABLE);
exit();
bb_exit();

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'ajax');
@ -12,34 +28,66 @@ define('IN_AJAX', true);
require __DIR__ . '/common.php';
// Init Ajax class
$ajax = new TorrentPier\Ajax();
$ajax = new TorrentPier\Legacy\Ajax();
$ajax->init();
// Init userdata
$user->session_start();
// Exit if board is disabled via ON/OFF trigger or by admin
if ($ajax->action != 'manage_admin') {
if ($bb_cfg['board_disable']) {
$ajax->ajax_die($lang['BOARD_DISABLE']);
} elseif (file_exists(BB_DISABLED)) {
$ajax->ajax_die($lang['BOARD_DISABLE_CRON']);
}
}
// Load actions required modules
switch ($ajax->action) {
case 'view_post':
require INC_DIR . '/bbcode.php';
break;
case 'posts':
case 'post_mod_comment':
require INC_DIR . '/bbcode.php';
require INC_DIR . '/functions_post.php';
require INC_DIR . '/functions_admin.php';
break;
case 'view_torrent':
case 'mod_action':
case 'change_tor_status':
case 'change_torrent':
case 'passkey':
case 'gen_passkey':
require ATTACH_DIR . '/attachment_mod.php';
require INC_DIR . '/functions_torrent.php';
break;
case 'user_register':
require INC_DIR . '/functions_validate.php';
break;
case 'manage_user':
case 'manage_admin':
require INC_DIR . '/functions_admin.php';
break;
case 'group_membership':
case 'manage_group':
require INC_DIR . '/functions_group.php';
break;
}
// Position in $ajax->valid_actions['xxx']
define('AJAX_AUTH', 0); // 'guest', 'user', 'mod', 'admin', 'super_admin'
$ajax->exec();
/**
* @deprecated ajax_common
* Dirty class removed from here since 2.2.0
* To add new actions see at src/Ajax.php
* To add new actions see at src/Legacy/Ajax.php
*/

View file

@ -1,25 +1,53 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('IN_TRACKER', true);
define('BB_ROOT', './../');
require dirname(__DIR__) . '/common.php';
// Check User-Agent for existence
$userAgent = (string)$_SERVER['HTTP_USER_AGENT'];
if (empty($userAgent)) {
global $bb_cfg;
if (empty($_SERVER['HTTP_USER_AGENT'])) {
header('Location: http://127.0.0.1', true, 301);
die;
}
$announce_interval = config()->get('announce_interval');
$passkey_key = config()->get('passkey_key');
// Ignore 'completed' event
if (isset($_GET['event']) && $_GET['event'] === 'completed') {
if (DBG_LOG) {
dbg_log(' ', '!die-event-completed');
}
dummy_exit(mt_rand(600, 1200));
}
$announce_interval = $bb_cfg['announce_interval'];
$passkey_key = $bb_cfg['passkey_key'];
$max_left_val = 536870912000; // 500 GB
$max_up_down_val = 5497558138880; // 5 TB
$max_up_add_val = 85899345920; // 80 GB
$max_down_add_val = 85899345920; // 80 GB
// Recover info_hash
if (isset($_GET['?info_hash']) && !isset($_GET['info_hash'])) {
@ -27,197 +55,149 @@ if (isset($_GET['?info_hash']) && !isset($_GET['info_hash'])) {
}
// Initial request verification
if (str_contains($_SERVER['REQUEST_URI'], 'scrape')) {
if (strpos($_SERVER['REQUEST_URI'], 'scrape') !== false) {
msg_die('Please disable SCRAPE!');
}
if (!isset($_GET[$passkey_key]) || !is_string($_GET[$passkey_key])) {
msg_die('Please LOG IN and RE-DOWNLOAD this torrent (passkey not found)');
if (!isset($_GET[$passkey_key]) || !is_string($_GET[$passkey_key]) || strlen($_GET[$passkey_key]) != BT_AUTH_KEY_LENGTH) {
msg_die('Please LOG IN and REDOWNLOAD this torrent (passkey not found)');
}
// Input var names
// String
$input_vars_str = ['info_hash', 'peer_id', 'event', $passkey_key];
$input_vars_str = array(
'info_hash',
'peer_id',
'event',
$passkey_key,
);
// Numeric
$input_vars_num = ['port', 'uploaded', 'downloaded', 'left', 'numwant', 'compact'];
$input_vars_num = array(
'port',
'uploaded',
'downloaded',
'left',
'numwant',
'compact',
);
// Init received data
// String
foreach ($input_vars_str as $var_name) {
$$var_name = isset($_GET[$var_name]) ? (string)$_GET[$var_name] : null;
}
// Numeric
foreach ($input_vars_num as $var_name) {
$$var_name = isset($_GET[$var_name]) ? (float)$_GET[$var_name] : null;
}
// Passkey
$passkey = $$passkey_key ?? null;
$passkey = isset($$passkey_key) ? $$passkey_key : null;
// Verify request
// Required params (info_hash, peer_id, port, uploaded, downloaded, left, passkey)
if (!isset($peer_id)) {
msg_die('peer_id was not provided');
if (!isset($info_hash) || strlen($info_hash) != 20) {
msg_die('Invalid info_hash');
}
if (strlen($peer_id) !== 20) {
msg_die('Invalid peer_id: ' . $peer_id);
if (!isset($peer_id) || strlen($peer_id) != 20) {
msg_die('Invalid peer_id');
}
// Check for client ban
if (config()->get('client_ban.enabled')) {
$targetClient = [];
foreach (config()->get('client_ban.clients') as $clientId => $banReason) {
if (str_starts_with($peer_id, $clientId)) {
$targetClient = [
'peer_id' => $clientId,
'ban_reason' => $banReason
];
break;
}
}
if (config()->get('client_ban.only_allow_mode')) {
if (empty($targetClient['peer_id'])) {
msg_die('Your BitTorrent client has been banned!');
}
} else {
if (!empty($targetClient['peer_id'])) {
msg_die(empty($targetClient['ban_reason']) ? 'Your BitTorrent client has been banned!' : $targetClient['ban_reason']);
}
}
if (!isset($port) || $port < 0 || $port > 0xFFFF) {
msg_die('Invalid port');
}
// Verify info_hash
if (!isset($info_hash)) {
msg_die('info_hash was not provided');
if (!isset($uploaded) || $uploaded < 0 || $uploaded > $max_up_down_val || $uploaded == 1844674407370) {
msg_die('Invalid uploaded value');
}
/**
* Verify event
*
* @see https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/c64275f0b5dcb3c4c845d5204871adfe24f359d6/app/Http/Controllers/AnnounceController.php#L275
*/
$event = strtolower((string)$event);
if (!in_array($event, ['started', 'completed', 'stopped', 'paused', ''])) {
msg_die('Invalid event: ' . $event);
if (!isset($downloaded) || $downloaded < 0 || $downloaded > $max_up_down_val || $downloaded == 1844674407370) {
msg_die('Invalid downloaded value');
}
// Store info hash in hex format
$info_hash_hex = bin2hex($info_hash);
// Store peer id
$peer_id_sql = preg_replace('/[^a-zA-Z0-9\-\_]/', '', $peer_id);
// Stopped event
$stopped = ($event === 'stopped');
// Check info_hash length
if (strlen($info_hash) !== 20) {
msg_die('Invalid info_hash: ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
if (!isset($left) || $left < 0 || $left > $max_left_val) {
msg_die('Invalid left value');
}
/**
* Block system-reserved ports since 99.9% of the time they're fake and thus not connectable
* Some clients will send port of 0 on 'stopped' events. Let them through as they won't receive peers anyway.
*
* @see https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/c64275f0b5dcb3c4c845d5204871adfe24f359d6/app/Http/Controllers/AnnounceController.php#L284
*/
if (
!isset($port)
|| !is_numeric($port)
|| ($port < 1024 && !$stopped)
|| $port > 0xFFFF
|| (!empty(config()->get('disallowed_ports')) && in_array($port, config()->get('disallowed_ports')))
) {
msg_die('Invalid port: ' . $port);
}
if (!isset($uploaded) || !is_numeric($uploaded) || $uploaded < 0) {
msg_die('Invalid uploaded value: ' . $uploaded);
}
if (!isset($downloaded) || !is_numeric($downloaded) || $downloaded < 0) {
msg_die('Invalid downloaded value: ' . $downloaded);
}
if (!isset($left) || !is_numeric($left) || $left < 0) {
msg_die('Invalid left value: ' . $left);
}
/**
* Check User-Agent length
*
* @see https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/c64275f0b5dcb3c4c845d5204871adfe24f359d6/app/Http/Controllers/AnnounceController.php#L177
*/
if (strlen($userAgent) > 64) {
msg_die('User-Agent must be less than 64 characters long');
}
/**
* Block Browser by checking the User-Agent
*
* @see https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/c64275f0b5dcb3c4c845d5204871adfe24f359d6/app/Http/Controllers/AnnounceController.php#L182
*/
if (preg_match('/(Mozilla|Browser|Chrome|Safari|AppleWebKit|Opera|Links|Lynx|Bot|Unknown)/i', $userAgent)) {
msg_die('Browser disallowed');
if (!verify_id($passkey, BT_AUTH_KEY_LENGTH)) {
msg_die('Invalid passkey');
}
// IP
$ip = $_SERVER['REMOTE_ADDR'];
// 'ip' query handling
if (!config()->get('ignore_reported_ip') && isset($_GET['ip']) && $ip !== $_GET['ip']) {
if (!config()->get('verify_reported_ip') && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$x_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
if ($x_ip === $_GET['ip']) {
$filteredIp = filter_var($x_ip, FILTER_VALIDATE_IP);
if ($filteredIp !== false && (config()->get('allow_internal_ip') || !filter_var($filteredIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) {
$ip = $filteredIp;
if (!$bb_cfg['ignore_reported_ip'] && isset($_GET['ip']) && $ip !== $_GET['ip']) {
if (!$bb_cfg['verify_reported_ip']) {
$ip = $_GET['ip'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
foreach ($matches[0] as $x_ip) {
if ($x_ip === $_GET['ip']) {
if (!$bb_cfg['allow_internal_ip'] && preg_match("#^(10|172\.16|192\.168)\.#", $x_ip)) {
break;
}
$ip = $x_ip;
break;
}
}
}
}
// Check that IP format is valid
if (!\TorrentPier\Helpers\IPHelper::isValid($ip)) {
if (!verify_ip($ip)) {
msg_die("Invalid IP: $ip");
}
// Convert IP to long format
$ip_sql = \TorrentPier\Helpers\IPHelper::ip2long($ip);
// Detect IP version
$ipv4 = $ipv6 = null;
$ip_version = \TorrentPier\Helpers\IPHelper::isValidv6($ip) ? 'ipv6' : 'ip';
if ($ip_version === 'ipv6') {
$ipv6 = $ip_sql;
} else {
$ipv4 = $ip_sql;
}
// Convert IP to HEX format
$ip_sql = encode_ip($ip);
// Peer unique id
$peer_hash = hash('xxh128', $passkey . $info_hash_hex . $port);
// Set seeder & complete
$complete = $seeder = ($left == 0) ? 1 : 0;
$peer_hash = md5(
rtrim($info_hash, ' ') . $passkey . $ip . $port
);
// Get cached peer info from previous announce (last peer info)
$lp_info = CACHE('tr_cache')->get(PEER_HASH_PREFIX . $peer_hash);
// Stopped event, slice peer's cache life to 30 seconds
if ($stopped && $lp_info) {
CACHE('tr_cache')->set(PEER_HASH_PREFIX . $peer_hash, $lp_info, 30);
if (DBG_LOG) {
dbg_log(' ', '$lp_info-get_from-CACHE-' . ($lp_info ? 'hit' : 'miss'));
}
// Drop fast announce
if ($lp_info && (!isset($event) || !$stopped)) {
if ($lp_info['ip_ver4'] === $ipv4 || $lp_info['ip_ver6'] === $ipv6 || isset($lp_info['ip_ver4'], $lp_info['ip_ver6'])) {
if ($lp_cached_peers = CACHE('tr_cache')->get(PEERS_LIST_PREFIX . $lp_info['topic_id'])) {
drop_fast_announce($lp_info, $lp_cached_peers); // Use cache but with new calculated interval and seed, peer count set
}
if ($lp_info && (!isset($event) || $event !== 'stopped')) {
drop_fast_announce($lp_info);
}
// Functions
function drop_fast_announce($lp_info)
{
global $announce_interval;
if ($lp_info['update_time'] < (TIMENOW - $announce_interval + 60)) {
return; // if announce interval correct
}
$new_ann_intrv = $lp_info['update_time'] + $announce_interval - TIMENOW;
dummy_exit($new_ann_intrv);
}
function msg_die($msg)
{
if (DBG_LOG) {
dbg_log(' ', '!die-' . clean_filename($msg));
}
$output = \Rych\Bencode\Bencode::encode([
'min interval' => (int)1800,
'failure reason' => (string)$msg,
'warning message' => (string)$msg,
]);
die($output);
}
// Start announcer
require __DIR__ . '/includes/init_tr.php';
$seeder = ($left == 0) ? 1 : 0;
$stopped = ($event === 'stopped');
// Stopped event
if ($stopped) {
CACHE('tr_cache')->rm(PEER_HASH_PREFIX . $peer_hash);
if (DBG_LOG) {
dbg_log(' ', 'stopped');
}
}
@ -226,86 +206,58 @@ if (!CACHE('tr_cache')->used && !$lp_info) {
$lp_info = DB()->fetch_row("
SELECT * FROM " . BB_BT_TRACKER . " WHERE peer_hash = '$peer_hash' LIMIT 1
");
if (DBG_LOG) {
dbg_log(' ', '$lp_info-get_from-DB-' . ($lp_info ? 'hit' : 'miss'));
}
}
if ($lp_info) {
if (!$stopped) {
drop_fast_announce($lp_info);
}
$user_id = $lp_info['user_id'];
$topic_id = $lp_info['topic_id'];
$releaser = $lp_info['releaser'];
$tor_type = $lp_info['tor_type'];
$hybrid_unrecord = $lp_info['hybrid_unrecord'] ?? false;
} else {
// Verify if torrent registered on tracker and user authorized
$info_hash_sql = rtrim(DB()->escape($info_hash), ' ');
/**
* Currently torrent clients send truncated v2 hashes (the design raises questions).
* @see https://github.com/bittorrent/bittorrent.org/issues/145#issuecomment-1720040343
*/
$info_hash_where = "WHERE tor.info_hash = '$info_hash_sql' OR SUBSTRING(tor.info_hash_v2, 1, 20) = '$info_hash_sql'";
$passkey_sql = DB()->escape($passkey);
$sql = "
SELECT tor.topic_id, tor.poster_id, tor.tor_type, tor.tor_status, tor.info_hash, tor.info_hash_v2, bt.*, u.user_level
SELECT tor.topic_id, tor.poster_id, tor.tor_type, u.*
FROM " . BB_BT_TORRENTS . " tor
LEFT JOIN " . BB_BT_USERS . " bt ON bt.auth_key = '$passkey_sql'
LEFT JOIN " . BB_USERS . " u ON u.user_id = bt.user_id
$info_hash_where
LEFT JOIN " . BB_BT_USERS . " u ON u.auth_key = '$passkey_sql'
WHERE tor.info_hash = '$info_hash_sql'
LIMIT 1
";
$row = DB()->fetch_row($sql);
// Verify if torrent registered on tracker and user authorized
if (empty($row['topic_id'])) {
msg_die('Torrent not registered, info_hash = ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
msg_die('Torrent not registered, info_hash = ' . bin2hex($info_hash_sql));
}
if (empty($row['user_id'])) {
msg_die('Please LOG IN and RE-DOWNLOAD this torrent (user not found)');
msg_die('Please LOG IN and REDOWNLOAD this torrent (user not found)');
}
// Assign variables
$user_id = $row['user_id'];
define('IS_GUEST', (int)$user_id === GUEST_UID);
define('IS_ADMIN', !IS_GUEST && (int)$row['user_level'] === ADMIN);
define('IS_MOD', !IS_GUEST && (int)$row['user_level'] === MOD);
define('IS_GROUP_MEMBER', !IS_GUEST && (int)$row['user_level'] === GROUP_MEMBER);
define('IS_USER', !IS_GUEST && (int)$row['user_level'] === USER);
define('IS_SUPER_ADMIN', IS_ADMIN && isset(config()->get('super_admins')[$user_id]));
define('IS_AM', IS_ADMIN || IS_MOD);
$topic_id = $row['topic_id'];
$releaser = (int)($user_id == $row['poster_id']);
$tor_type = $row['tor_type'];
$tor_status = $row['tor_status'];
// Check tor status
if (!IS_AM && isset(config()->get('tor_frozen')[$tor_status]) && !(isset(config()->get('tor_frozen_author_download')[$tor_status]) && $releaser)) {
msg_die('Torrent frozen and cannot be downloaded');
}
// Check hybrid status
if (!empty($row['info_hash']) && !empty($row['info_hash_v2'])) {
$stat_protocol = match ((int)config()->get('tracker.hybrid_stat_protocol')) {
2 => substr($row['info_hash_v2'], 0, 20),
default => $row['info_hash'] // 1
};
if ($info_hash !== $stat_protocol) {
$hybrid_unrecord = true; // This allows us to announce only for one info-hash
}
}
// Ratio limits
if ((RATIO_ENABLED || config()->get('tracker.limit_concurrent_ips')) && !$stopped) {
$user_ratio = get_bt_ratio($row);
if ($user_ratio === null) {
$user_ratio = 1;
}
if ((TR_RATING_LIMITS || $bb_cfg['tracker']['limit_concurrent_ips']) && !$stopped) {
$user_ratio = ($row['u_down_total'] && $row['u_down_total'] > MIN_DL_FOR_RATIO) ? ($row['u_up_total'] + $row['u_up_release'] + $row['u_up_bonus']) / $row['u_down_total'] : 1;
$rating_msg = '';
if (!$seeder) {
foreach (config()->get('rating') as $ratio => $limit) {
foreach ($bb_cfg['rating'] as $ratio => $limit) {
if ($user_ratio < $ratio) {
config()->set('tracker.limit_active_tor', 1);
config()->set('tracker.limit_leech_count', $limit);
$bb_cfg['tracker']['limit_active_tor'] = 1;
$bb_cfg['tracker']['limit_leech_count'] = $limit;
$rating_msg = " (ratio < $ratio)";
break;
}
@ -313,46 +265,46 @@ if ($lp_info) {
}
// Limit active torrents
if (!isset(config()->get('unlimited_users')[$user_id]) && config()->get('tracker.limit_active_tor') && ((config()->get('tracker.limit_seed_count') && $seeder) || (config()->get('tracker.limit_leech_count') && !$seeder))) {
if (!isset($bb_cfg['unlimited_users'][$user_id]) && $bb_cfg['tracker']['limit_active_tor'] && (($bb_cfg['tracker']['limit_seed_count'] && $seeder) || ($bb_cfg['tracker']['limit_leech_count'] && !$seeder))) {
$sql = "SELECT COUNT(DISTINCT topic_id) AS active_torrents
FROM " . BB_BT_TRACKER . "
WHERE user_id = $user_id
AND seeder = $seeder
AND topic_id != $topic_id";
if (!$seeder && config()->get('tracker.leech_expire_factor') && $user_ratio < 0.5) {
$sql .= " AND update_time > " . (TIMENOW - 60 * config()->get('tracker.leech_expire_factor'));
if (!$seeder && $bb_cfg['tracker']['leech_expire_factor'] && $user_ratio < 0.5) {
$sql .= " AND update_time > " . (TIMENOW - 60 * $bb_cfg['tracker']['leech_expire_factor']);
}
$sql .= " GROUP BY user_id";
$sql .= " GROUP BY user_id";
if ($row = DB()->fetch_row($sql)) {
if ($seeder && config()->get('tracker.limit_seed_count') && $row['active_torrents'] >= config()->get('tracker.limit_seed_count')) {
msg_die('Only ' . config()->get('tracker.limit_seed_count') . ' torrent(s) allowed for seeding');
} elseif (!$seeder && config()->get('tracker.limit_leech_count') && $row['active_torrents'] >= config()->get('tracker.limit_leech_count')) {
msg_die('Only ' . config()->get('tracker.limit_leech_count') . ' torrent(s) allowed for leeching' . $rating_msg);
if ($seeder && $bb_cfg['tracker']['limit_seed_count'] && $row['active_torrents'] >= $bb_cfg['tracker']['limit_seed_count']) {
msg_die('Only ' . $bb_cfg['tracker']['limit_seed_count'] . ' torrent(s) allowed for seeding');
} elseif (!$seeder && $bb_cfg['tracker']['limit_leech_count'] && $row['active_torrents'] >= $bb_cfg['tracker']['limit_leech_count']) {
msg_die('Only ' . $bb_cfg['tracker']['limit_leech_count'] . ' torrent(s) allowed for leeching' . $rating_msg);
}
}
}
// Limit concurrent IPs
if (config()->get('tracker.limit_concurrent_ips') && ((config()->get('tracker.limit_seed_ips') && $seeder) || (config()->get('tracker.limit_leech_ips') && !$seeder))) {
if ($bb_cfg['tracker']['limit_concurrent_ips'] && (($bb_cfg['tracker']['limit_seed_ips'] && $seeder) || ($bb_cfg['tracker']['limit_leech_ips'] && !$seeder))) {
$sql = "SELECT COUNT(DISTINCT ip) AS ips
FROM " . BB_BT_TRACKER . "
WHERE topic_id = $topic_id
AND user_id = $user_id
AND seeder = $seeder
AND $ip_version != '$ip_sql'";
AND ip != '$ip_sql'";
if (!$seeder && config()->get('tracker.leech_expire_factor')) {
$sql .= " AND update_time > " . (TIMENOW - 60 * config()->get('tracker.leech_expire_factor'));
if (!$seeder && $bb_cfg['tracker']['leech_expire_factor']) {
$sql .= " AND update_time > " . (TIMENOW - 60 * $bb_cfg['tracker']['leech_expire_factor']);
}
$sql .= " GROUP BY topic_id";
$sql .= " GROUP BY topic_id";
if ($row = DB()->fetch_row($sql)) {
if ($seeder && config()->get('tracker.limit_seed_ips') && $row['ips'] >= config()->get('tracker.limit_seed_ips')) {
msg_die('You can seed only from ' . config()->get('tracker.limit_seed_ips') . " IP's");
} elseif (!$seeder && config()->get('tracker.limit_leech_ips') && $row['ips'] >= config()->get('tracker.limit_leech_ips')) {
msg_die('You can leech only from ' . config()->get('tracker.limit_leech_ips') . " IP's");
if ($seeder && $bb_cfg['tracker']['limit_seed_ips'] && $row['ips'] >= $bb_cfg['tracker']['limit_seed_ips']) {
msg_die('You can seed only from ' . $bb_cfg['tracker']['limit_seed_ips'] . " IP's");
} elseif (!$seeder && $bb_cfg['tracker']['limit_leech_ips'] && $row['ips'] >= $bb_cfg['tracker']['limit_leech_ips']) {
msg_die('You can leech only from ' . $bb_cfg['tracker']['limit_leech_ips'] . " IP's");
}
}
}
@ -360,7 +312,7 @@ if ($lp_info) {
}
// Up/Down speed
$speed_up = $speed_down = 0;
$speed_up = $speed_down = $uploaded = $downloaded = 0;
if ($lp_info && $lp_info['update_time'] < TIMENOW) {
if ($uploaded > $lp_info['uploaded']) {
@ -376,7 +328,7 @@ $up_add = ($lp_info && $uploaded > $lp_info['uploaded']) ? $uploaded - $lp_info[
$down_add = ($lp_info && $downloaded > $lp_info['downloaded']) ? $downloaded - $lp_info['downloaded'] : 0;
// Gold/Silver releases
if (config()->get('tracker.gold_silver_enabled') && $down_add) {
if ($bb_cfg['tracker']['gold_silver_enabled'] && $down_add) {
if ($tor_type == TOR_TYPE_GOLD) {
$down_add = 0;
} // Silver releases
@ -386,7 +338,7 @@ if (config()->get('tracker.gold_silver_enabled') && $down_add) {
}
// Freeleech
if (config()->get('tracker.freeleech') && $down_add) {
if ($bb_cfg['tracker']['freeleech'] && $down_add) {
$down_add = 0;
}
@ -394,11 +346,9 @@ if (config()->get('tracker.freeleech') && $down_add) {
$peer_info_updated = false;
$update_time = ($stopped) ? 0 : TIMENOW;
if ($lp_info && empty($hybrid_unrecord)) {
if ($lp_info) {
$sql = "UPDATE " . BB_BT_TRACKER . " SET update_time = $update_time";
$sql .= ", $ip_version = '$ip_sql'";
$sql .= ", port = '$port'";
$sql .= ", seeder = $seeder";
$sql .= ($releaser != $lp_info['releaser']) ? ", releaser = $releaser" : '';
@ -408,37 +358,42 @@ if ($lp_info && empty($hybrid_unrecord)) {
$sql .= ($downloaded != $lp_info['downloaded']) ? ", downloaded = $downloaded" : '';
$sql .= ", remain = $left";
$sql .= $up_add ? ", up_add = up_add + $up_add" : '';
$sql .= $down_add ? ", down_add = down_add + $down_add" : '';
$sql .= ($up_add) ? ", up_add = up_add + $up_add" : '';
$sql .= ($down_add) ? ", down_add = down_add + $down_add" : '';
$sql .= ", speed_up = $speed_up";
$sql .= ", speed_down = $speed_down";
$sql .= ", complete = $complete";
$sql .= ", peer_id = '$peer_id_sql'";
$sql .= " WHERE peer_hash = '$peer_hash'";
$sql .= " LIMIT 1";
DB()->query($sql);
$peer_info_updated = DB()->affected_rows();
if (DBG_LOG) {
dbg_log(' ', 'this_peer-update' . ($peer_info_updated ? '' : '-FAIL'));
}
}
if ((!$lp_info || !$peer_info_updated) && !$stopped && empty($hybrid_unrecord)) {
$columns = "peer_hash, topic_id, user_id, $ip_version, port, seeder, releaser, tor_type, uploaded, downloaded, remain, speed_up, speed_down, up_add, down_add, update_time, complete, peer_id";
$values = "'$peer_hash', $topic_id, $user_id, '$ip_sql', $port, $seeder, $releaser, $tor_type, $uploaded, $downloaded, $left, $speed_up, $speed_down, $up_add, $down_add, $update_time, $complete, '$peer_id_sql'";
if (!$lp_info || !$peer_info_updated) {
$columns = 'peer_hash, topic_id, user_id, ip, port, seeder, releaser, tor_type, uploaded, downloaded, remain, speed_up, speed_down, up_add, down_add, update_time';
$values = "'$peer_hash', $topic_id, $user_id, '$ip_sql', $port, $seeder, $releaser, $tor_type, $uploaded, $downloaded, $left, $speed_up, $speed_down, $up_add, $down_add, $update_time";
DB()->query("REPLACE INTO " . BB_BT_TRACKER . " ($columns) VALUES ($values)");
if (DBG_LOG) {
dbg_log(' ', 'this_peer-insert');
}
}
// Exit if stopped
if ($stopped) {
dummy_exit();
silent_exit();
}
// Store peer info in cache
$lp_info_new = [
$lp_info = array(
'downloaded' => (float)$downloaded,
'releaser' => (int)$releaser,
'seeder' => (int)$seeder,
@ -447,103 +402,83 @@ $lp_info_new = [
'uploaded' => (float)$uploaded,
'user_id' => (int)$user_id,
'tor_type' => (int)$tor_type,
'complete' => (int)$complete,
'ip_ver4' => $lp_info['ip_ver4'] ?? $ipv4,
'ip_ver6' => $lp_info['ip_ver6'] ?? $ipv6,
];
);
if (!empty($hybrid_unrecord)) {
$lp_info_new['hybrid_unrecord'] = $hybrid_unrecord;
$lp_info_cached = CACHE('tr_cache')->set(PEER_HASH_PREFIX . $peer_hash, $lp_info, PEER_HASH_EXPIRE);
if (DBG_LOG && !$lp_info_cached) {
dbg_log(' ', '$lp_info-caching-FAIL');
}
// Cache new list with peer hash
$lp_info_cached = CACHE('tr_cache')->set(PEER_HASH_PREFIX . $peer_hash, $lp_info_new, PEER_HASH_EXPIRE);
// Get cached output
$output = CACHE('tr_cache')->get(PEERS_LIST_PREFIX . $topic_id);
if (DBG_LOG) {
dbg_log(' ', '$output-get_from-CACHE-' . ($output !== false ? 'hit' : 'miss'));
}
if (!$output) {
// Retrieve peers
$numwant = (int)config()->get('tracker.numwant');
$compact_mode = (config()->get('tracker.compact_mode') || !empty($compact));
$numwant = (int)$bb_cfg['tracker']['numwant'];
$compact_mode = ($bb_cfg['tracker']['compact_mode'] || !empty($compact));
$rowset = DB()->fetch_rowset("
SELECT ip, ipv6, port
FROM " . BB_BT_TRACKER . "
WHERE topic_id = $topic_id
ORDER BY seeder ASC, RAND()
LIMIT $numwant
");
if (empty($rowset)) {
$rowset[] = ['ip' => $ip_sql, 'port' => (int)$port];
}
SELECT ip, port
FROM " . BB_BT_TRACKER . "
WHERE topic_id = $topic_id
ORDER BY RAND()
LIMIT $numwant
");
if ($compact_mode) {
$peers = '';
$peers6 = '';
foreach ($rowset as $peer) {
if (!empty($peer['ip'])) {
$peer_ipv4 = \TorrentPier\Helpers\IPHelper::long2ip_extended($peer['ip']);
$peers .= inet_pton($peer_ipv4) . pack('n', $peer['port']);
}
if (!empty($peer['ipv6'])) {
$peer_ipv6 = \TorrentPier\Helpers\IPHelper::long2ip_extended($peer['ipv6']);
$peers6 .= inet_pton($peer_ipv6) . pack('n', $peer['port']);
}
$peers .= pack('Nn', ip2long(decode_ip($peer['ip'])), $peer['port']);
}
} else {
$peers = [];
$peers = array();
foreach ($rowset as $peer) {
if (!empty($peer['ip'])) {
$peer_ipv4 = \TorrentPier\Helpers\IPHelper::long2ip_extended($peer['ip']);
$peers[] = ['ip' => \TorrentPier\Helpers\IPHelper::long2ip_extended($peer['ip']), 'port' => (int)$peer['port']];
}
if (!empty($peer['ipv6'])) {
$peer_ipv6 = \TorrentPier\Helpers\IPHelper::long2ip_extended($peer['ipv6']);
$peers[] = ['ip' => \TorrentPier\Helpers\IPHelper::long2ip_extended($peer['ipv6']), 'port' => (int)$peer['port']];
}
$peers[] = array(
'ip' => decode_ip($peer['ip']),
'port' => (int)$peer['port'],
);
}
}
$seeders = $leechers = $client_completed = 0;
$seeders = 0;
$leechers = 0;
if (config()->get('tracker.scrape')) {
if ($bb_cfg['tracker']['scrape']) {
$row = DB()->fetch_row("
SELECT seeders, leechers, completed
SELECT seeders, leechers
FROM " . BB_BT_TRACKER_SNAP . "
WHERE topic_id = $topic_id
LIMIT 1
");
$seeders = $row['seeders'] ?? ($seeder ? 1 : 0);
$leechers = $row['leechers'] ?? (!$seeder ? 1 : 0);
$client_completed = $row['completed'] ?? 0;
$seeders = $row['seeders'];
$leechers = $row['leechers'];
}
$output = [
$output = array(
'interval' => (int)$announce_interval,
'min interval' => (int)$announce_interval,
'peers' => $peers,
'complete' => (int)$seeders,
'incomplete' => (int)$leechers,
'downloaded' => (int)$client_completed,
];
if (!empty($peers)) {
$output['peers'] = $peers;
}
if (!empty($peers6)) {
$output['peers6'] = $peers6;
}
);
$peers_list_cached = CACHE('tr_cache')->set(PEERS_LIST_PREFIX . $topic_id, $output, PEERS_LIST_EXPIRE);
if (DBG_LOG && !$peers_list_cached) {
dbg_log(' ', '$output-caching-FAIL');
}
}
$output['external ip'] = inet_pton($ip);
// Return data to client
echo \Arokettu\Bencode\Bencode::encode($output);
echo \Rych\Bencode\Bencode::encode($output);
tracker_exit();
exit;

2
bt/includes/.htaccess Normal file
View file

@ -0,0 +1,2 @@
order allow,deny
deny from all

View file

@ -1,84 +1,81 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_TRACKER')) {
die(basename(__FILE__));
}
global $bb_cfg;
// Exit if tracker is disabled
if (config()->get('tracker.bt_off')) {
msg_die(config()->get('tracker.bt_off_reason'));
if ($bb_cfg['tracker']['off']) {
msg_die($bb_cfg['tracker']['off_reason']);
}
//
// Functions
//
function silent_exit($msg = '')
function tracker_exit()
{
echo \Arokettu\Bencode\Bencode::encode(['warning message' => str_compact($msg)]);
global $DBS;
if (DBG_LOG && DBG_TRACKER) {
if ($gen_time = utime() - TIMESTART) {
$sql_init_perc = round($DBS->sql_inittime * 100 / $gen_time);
$sql_total_perc = round($DBS->sql_timetotal * 100 / $gen_time);
$str = array();
$str[] = substr(TIMENOW, -4, 4);
$str[] = sprintf('%.4f', $gen_time);
$str[] = sprintf('%.4f' . LOG_SEPR . '%02d%%', $DBS->sql_inittime, $sql_init_perc);
$str[] = sprintf('%.4f' . LOG_SEPR . '%02d%%', $DBS->sql_timetotal, $sql_total_perc);
$str[] = $DBS->num_queries;
$str[] = sprintf('%.1f', sys('la'));
$str = implode(LOG_SEPR, $str) . LOG_LF;
dbg_log($str, '!!gentime');
}
}
exit;
}
function silent_exit()
{
while (ob_end_clean()) ;
tracker_exit();
}
function error_exit($msg = '')
{
echo \Arokettu\Bencode\Bencode::encode(['failure reason' => str_compact($msg)]);
exit;
}
function drop_fast_announce($lp_info, $lp_cached_peers = [])
{
global $announce_interval;
if ($lp_info['update_time'] < (TIMENOW - $announce_interval + 60)) {
return; // if announce interval correct
if (DBG_LOG) {
dbg_log(' ', '!err-' . clean_filename($msg));
}
$new_ann_intrv = $lp_info['update_time'] + $announce_interval - TIMENOW;
silent_exit();
dummy_exit($new_ann_intrv, $lp_cached_peers);
}
function msg_die($msg)
{
$output = \Arokettu\Bencode\Bencode::encode([
'interval' => (int)1800,
'failure reason' => (string)$msg,
]);
die($output);
}
function dummy_exit($interval = 1800, $cache_dict = [])
{
$output = [
'interval' => (int)$interval,
'peers' => (string)DUMMY_PEER,
'external ip' => inet_pton($_SERVER['REMOTE_ADDR']),
];
if (!empty($cache_dict)) {
$output['complete'] = $cache_dict['complete'];
$output['incomplete'] = $cache_dict['incomplete'];
$output['downloaded'] = $cache_dict['downloaded'];
}
if (isset($cache_dict['peers'])) {
$output['peers'] = $cache_dict['peers'];
}
if (isset($cache_dict['peers6'])) {
$output['peers6'] = $cache_dict['peers6'];
}
$output = \Arokettu\Bencode\Bencode::encode($output);
die($output);
echo \Rych\Bencode\Bencode::encode(['failure reason' => str_compact($msg)]);
tracker_exit();
}

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
require __DIR__ . '/announce.php';

View file

@ -1,17 +1,35 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('IN_TRACKER', true);
define('BB_ROOT', './../');
require dirname(__DIR__) . '/common.php';
if (!config()->get('tracker.scrape')) {
global $bb_cfg;
if (!$bb_cfg['tracker']['scrape']) {
msg_die('Please disable SCRAPE!');
}
@ -20,84 +38,46 @@ if (isset($_GET['?info_hash']) && !isset($_GET['info_hash'])) {
$_GET['info_hash'] = $_GET['?info_hash'];
}
$info_hash = isset($_GET['info_hash']) ? (string)$_GET['info_hash'] : null;
// Verify info_hash
if (!isset($info_hash)) {
msg_die('info_hash was not provided');
if (!isset($_GET['info_hash']) || strlen($_GET['info_hash']) != 20) {
msg_die('Invalid info_hash');
}
// Store info hash in hex format
$info_hash_hex = bin2hex($info_hash);
$info_hash = $_GET['info_hash'];
// Check info_hash length
if (strlen($info_hash) !== 20) {
msg_die('Invalid info_hash: ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
}
// Handle multiple hashes
preg_match_all('/info_hash=([^&]*)/i', $_SERVER['QUERY_STRING'], $info_hash_array);
$torrents = [];
$info_hashes = [];
foreach ($info_hash_array[1] as $hash) {
$decoded_hash = urldecode($hash);
if (strlen($decoded_hash) !== 20) {
continue;
function msg_die($msg)
{
if (DBG_LOG) {
dbg_log(' ', '!die-' . clean_filename($msg));
}
if ($scrape_cache = CACHE('tr_cache')->get(SCRAPE_LIST_PREFIX . bin2hex($decoded_hash))) {
$torrents['files'][$info_key = array_key_first($scrape_cache)] = $scrape_cache[$info_key];
} else {
$info_hashes[] = DB()->escape(($decoded_hash));
}
$output = \Rych\Bencode\Bencode::encode([
'min interval' => (int)1800,
'failure reason' => (string)$msg,
'warning message' => (string)$msg,
]);
die($output);
}
$info_hash_count = count($info_hashes);
require __DIR__ . '/includes/init_tr.php';
if (!empty($info_hash_count)) {
if ($info_hash_count > config()->get('max_scrapes')) {
$info_hashes = array_slice($info_hashes, 0, config()->get('max_scrapes'));
}
$info_hash_sql = rtrim(DB()->escape($info_hash), ' ');
$info_hashes_sql = implode('\', \'', $info_hashes);
$row = DB()->fetch_row("
SELECT tor.complete_count, snap.seeders, snap.leechers
FROM " . BB_BT_TORRENTS . " tor
LEFT JOIN " . BB_BT_TRACKER_SNAP . " snap ON (snap.topic_id = tor.topic_id)
WHERE tor.info_hash = '$info_hash_sql'
LIMIT 1
");
/**
* Currently torrent clients send truncated v2 hashes (the design raises questions).
* @see https://github.com/bittorrent/bittorrent.org/issues/145#issuecomment-1720040343
*/
$info_hash_where = "tor.info_hash IN ('$info_hashes_sql') OR SUBSTRING(tor.info_hash_v2, 1, 20) IN ('$info_hashes_sql')";
$output['files'][$info_hash] = array(
'complete' => (int)$row['seeders'],
'downloaded' => (int)$row['complete_count'],
'incomplete' => (int)$row['leechers'],
);
$sql = "
SELECT tor.info_hash, tor.info_hash_v2, tor.complete_count, snap.seeders, snap.leechers
FROM " . BB_BT_TORRENTS . " tor
LEFT JOIN " . BB_BT_TRACKER_SNAP . " snap ON (snap.topic_id = tor.topic_id)
WHERE $info_hash_where
";
echo \Rych\Bencode\Bencode::encode($output);
$scrapes = DB()->fetch_rowset($sql);
if (!empty($scrapes)) {
foreach ($scrapes as $scrape) {
$hash_v1 = !empty($scrape['info_hash']) ? $scrape['info_hash'] : '';
$hash_v2 = !empty($scrape['info_hash_v2']) ? substr($scrape['info_hash_v2'], 0, 20) : '';
$info_hash_scrape = (in_array(urlencode($hash_v1), $info_hash_array[1])) ? $hash_v1 : $hash_v2; // Replace logic to prioritize $hash_v2, in case of future prioritization of v2
$torrents['files'][$info_hash_scrape] = [
'complete' => (int)$scrape['seeders'],
'downloaded' => (int)$scrape['complete_count'],
'incomplete' => (int)$scrape['leechers']
];
CACHE('tr_cache')->set(SCRAPE_LIST_PREFIX . bin2hex($info_hash_scrape), array_slice($torrents['files'], -1, null, true), SCRAPE_LIST_EXPIRE);
}
}
}
// Verify if torrent registered on tracker
if (empty($torrents)) {
msg_die('Torrent not registered, info_hash = ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
}
die(\Arokettu\Bencode\Bencode::encode($torrents));
tracker_exit();
exit;

108
callseed.php Normal file
View file

@ -0,0 +1,108 @@
<?php
/**
* MIT License
*
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'callseed');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
// Init userdata
$user->session_start(array('req_login' => true));
$topic_id = (int)request_var('t', 0);
$t_data = topic_info($topic_id);
$forum_id = $t_data['forum_id'];
set_die_append_msg($forum_id, $topic_id);
if ($t_data['seeders'] > 2) {
bb_die(sprintf($lang['CALLSEED_HAVE_SEED'], $t_data['seeders']));
} elseif ($t_data['call_seed_time'] > (TIMENOW - 86400)) {
$time_left = delta_time($t_data['call_seed_time'] + 86400, TIMENOW, 'days');
bb_die(sprintf($lang['CALLSEED_MSG_SPAM'], $time_left));
}
$ban_user_id = [];
$sql = DB()->fetch_rowset("SELECT ban_userid FROM " . BB_BANLIST . " WHERE ban_userid != 0");
foreach ($sql as $row) {
$ban_user_id[] = ',' . $row['ban_userid'];
}
$ban_user_id = implode('', $ban_user_id);
$user_list = DB()->fetch_rowset("
SELECT DISTINCT dl.user_id, u.user_opt, tr.user_id as active_dl
FROM " . BB_BT_DLSTATUS . " dl
LEFT JOIN " . BB_USERS . " u ON(u.user_id = dl.user_id)
LEFT JOIN " . BB_BT_TRACKER . " tr ON(tr.user_id = dl.user_id)
WHERE dl.topic_id = $topic_id
AND dl.user_status IN (" . DL_STATUS_COMPLETE . ", " . DL_STATUS_DOWN . ")
AND dl.user_id NOT IN ({$userdata['user_id']}, " . EXCLUDED_USERS . $ban_user_id . ")
AND u.user_active = 1
GROUP BY dl.user_id
");
$subject = sprintf($lang['CALLSEED_SUBJECT'], $t_data['topic_title']);
$message = sprintf($lang['CALLSEED_TEXT'], make_url(TOPIC_URL . $topic_id), $t_data['topic_title'], make_url(DOWNLOAD_URL . $t_data['attach_id']));
if ($user_list) {
foreach ($user_list as $row) {
if (!empty($row['active_dl'])) {
continue;
}
if (bf($row['user_opt'], 'user_opt', 'user_callseed')) {
send_pm($row['user_id'], $subject, $message, BOT_UID);
}
}
} else {
send_pm($t_data['poster_id'], $subject, $message, BOT_UID);
}
DB()->query("UPDATE " . BB_BT_TORRENTS . " SET call_seed_time = " . TIMENOW . " WHERE topic_id = $topic_id");
meta_refresh(TOPIC_URL . $topic_id);
bb_die($lang['CALLSEED_MSG_OK']);
function topic_info($topic_id)
{
global $lang;
$sql = "
SELECT
tor.poster_id, tor.forum_id, tor.attach_id, tor.call_seed_time,
t.topic_title, sn.seeders
FROM " . BB_BT_TORRENTS . " tor
LEFT JOIN " . BB_TOPICS . " t USING(topic_id)
LEFT JOIN " . BB_BT_TRACKER_SNAP . " sn USING(topic_id)
WHERE tor.topic_id = $topic_id
";
if (!$torrent = DB()->fetch_row($sql)) {
bb_die($lang['TOPIC_POST_NOT_EXIST']);
}
return $torrent;
}

View file

@ -1,126 +0,0 @@
# git-cliff ~ TorrentPier configuration file
# https://git-cliff.org/docs/configuration
#
# Lines starting with "#" are comments.
# Configuration options are organized into tables and keys.
# See documentation for more information on available options.
[remote.github]
owner = "torrentpier"
repo = "torrentpier"
[changelog]
# template for the changelog header
header = """
[![TorrentPier](https://raw.githubusercontent.com/torrentpier/.github/refs/heads/main/versions/Cattle.png)](https://github.com/torrentpier)\n
# 📖 Change Log\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
{%- macro nightly_url() -%}
https://nightly.link/{{ remote.github.owner }}/{{ remote.github.repo }}/workflows/ci/master/TorrentPier-master
{%- endmacro -%}
{% macro print_commit(commit) -%}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }} - \
([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% endmacro -%}
{% if version %}\
{% if previous.version %}\
## [{{ version }}]\
({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) ({{ timestamp | date(format="%Y-%m-%d") }})
{% else %}\
## {{ version }} ({{ timestamp | date(format="%Y-%m-%d") }})
{% endif %}\
{% else %}\
## [nightly]({{ self::nightly_url() }})
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits
| filter(attribute="scope")
| sort(attribute="scope") %}
{{ self::print_commit(commit=commit) }}
{%- endfor %}
{% for commit in commits %}
{%- if not commit.scope -%}
{{ self::print_commit(commit=commit) }}
{% endif -%}
{% endfor -%}
{% endfor -%}
{%- if github -%}
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
## New Contributors ❤️
{% endif %}\
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
* @{{ contributor.username }} made their first contribution
{%- if contributor.pr_number %} in \
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
{%- endif %}
{%- endfor -%}
{%- endif %}
"""
# template for the changelog footer
footer = """
"""
# remove the leading and trailing whitespace from the templates
trim = true
# postprocessors
postprocessors = [
{ pattern = '<REPO>', replace = "https://github.com/torrentpier/torrentpier" }, # replace repository URL
]
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
# Replace issue numbers
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/pull/${2}))" },
# Check spelling of the commit with https://github.com/crate-ci/typos
# If the spelling is incorrect, it will be automatically fixed.
# { pattern = '.*', replace_command = 'typos --write-changes -' },
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
{ message = "^ignore|^release|^changelog", skip = true },
{ message = "^chore|^ci|^misc", group = "<!-- 7 -->⚙️ Miscellaneous" },
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
{ message = "^crowdin|^crodwin", group = "<!-- 10 -->🈳 New translations" }, # crowdin pulls supporting
{ message = "^Composer", group = "<!-- 11 -->📦 Dependencies" }, # dependabot pulls supporting
{ message = "^rem|^drop|^removed", group = "<!-- 12 -->🗑️ Removed" },
{ message = ".*", group = "<!-- 13 -->💼 Other" },
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false
# filter out the commits that are not matched by commit parsers
filter_commits = false
# regex for matching git tags
tag_pattern = "v[0-9].*"
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "newest"

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (isset($_REQUEST['GLOBALS'])) {
@ -13,7 +29,6 @@ if (isset($_REQUEST['GLOBALS'])) {
define('TIMESTART', utime());
define('TIMENOW', time());
define('BB_PATH', __DIR__);
if (empty($_SERVER['REMOTE_ADDR'])) {
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
@ -25,198 +40,204 @@ if (empty($_SERVER['HTTP_REFERER'])) {
$_SERVER['HTTP_REFERER'] = '';
}
if (empty($_SERVER['SERVER_NAME'])) {
$_SERVER['SERVER_NAME'] = getenv('SERVER_NAME');
}
if (empty($_SERVER['SERVER_ADDR'])) {
$_SERVER['SERVER_ADDR'] = getenv('SERVER_ADDR');
$_SERVER['SERVER_NAME'] = '';
}
if (!defined('BB_ROOT')) {
define('BB_ROOT', './');
}
if (!defined('BB_SCRIPT')) {
define('BB_SCRIPT', null);
define('BB_SCRIPT', 'undefined');
}
header('X-Frame-Options: SAMEORIGIN');
date_default_timezone_set('UTC');
// Set remote address
$allowedCDNs = ['HTTP_X_FORWARDED_FOR', 'HTTP_FASTLY_CLIENT_IP', 'HTTP_CF_CONNECTING_IP'];
foreach ($allowedCDNs as $allowedCDN) {
if (isset($_SERVER[$allowedCDN]) && filter_var($_SERVER[$allowedCDN], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$_SERVER['REMOTE_ADDR'] = $_SERVER[$allowedCDN];
}
// Cloudflare
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
// Get all constants
require_once BB_PATH . '/library/defines.php';
require_once __DIR__ . '/library/defines.php';
// Composer
if (!is_file(BB_PATH . '/vendor/autoload.php')) {
die('🔩 Manual install: <a href="https://getcomposer.org/download/" target="_blank" rel="noreferrer" style="color:#0a25bb;">Install composer</a> and run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">composer install</code>.<br/>☕️ Quick install: Run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php install.php</code> in CLI mode.');
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
die('Please <a href="https://getcomposer.org/download/" target="_blank" rel="noreferrer" style="color:#0a25bb;">install composer</a> and run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">composer install</code>');
}
require_once BB_PATH . '/vendor/autoload.php';
require_once __DIR__ . '/vendor/autoload.php';
/**
* Gets the value of an environment variable.
* Gets the value of an environment variable. Supports boolean, empty and null.
*
* @param string $key
* @param mixed|null $default
* @param string $key
* @param mixed $default
* @return mixed
*/
function env(string $key, mixed $default = null): mixed
function env($key, $default = null)
{
return \TorrentPier\Env::get($key, $default);
$value = getenv($key);
if (!$value) return value($default);
switch (strtolower($value)) {
case 'true':
case '(true)':
return true;
case 'false':
case '(false)':
return false;
case '(null)':
return null;
case '(empty)':
return '';
}
return $value;
}
// Load ENV
try {
$dotenv = Dotenv\Dotenv::createMutable(BB_PATH);
$dotenv->load();
} catch (\Dotenv\Exception\InvalidPathException $pathException) {
die('🔩 Manual install: Rename from <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">.env.example</code> to <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">.env</code>, and configure it.<br/>☕️ Quick install: Run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php install.php</code> in CLI mode.');
/**
* Return the default value of the given value.
*
* @param mixed $value
* @return mixed
*/
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
// Load config
require_once BB_PATH . '/library/config.php';
// Get initial config
if (!getenv('APP_DEBUG') && file_exists(__DIR__ . '/.env')) {
(new Symfony\Component\Dotenv\Dotenv())->load(__DIR__ . '/.env');
}
require_once __DIR__ . '/library/config.php';
// Local config
if (is_file(BB_PATH . '/library/config.local.php')) {
require_once BB_PATH . '/library/config.local.php';
if (file_exists(__DIR__ . '/library/config.local.php')) {
require_once __DIR__ . '/library/config.local.php';
}
/** @noinspection PhpUndefinedVariableInspection */
// Initialize Config singleton, bb_cfg from global file config
$config = \TorrentPier\Config::init($bb_cfg);
/**
* Get the Config instance
*
* @return \TorrentPier\Config
*/
function config(): \TorrentPier\Config
{
return \TorrentPier\Config::getInstance();
}
/**
* Get the Censor instance
*
* @return \TorrentPier\Censor
*/
function censor(): \TorrentPier\Censor
{
return \TorrentPier\Censor::getInstance();
}
/**
* Get the Dev instance
*
* @return \TorrentPier\Dev
*/
function dev(): \TorrentPier\Dev
{
return \TorrentPier\Dev::getInstance();
}
/**
* Get the Language instance
*
* @return \TorrentPier\Language
*/
function lang(): \TorrentPier\Language
{
return \TorrentPier\Language::getInstance();
}
/**
* Get a language string (shorthand for lang()->get())
*
* @param string $key Language key, supports dot notation (e.g., 'DATETIME.TODAY')
* @param mixed $default Default value if key doesn't exist
* @return mixed Language string or default value
*/
function __(string $key, mixed $default = null): mixed
{
return \TorrentPier\Language::getInstance()->get($key, $default);
}
/**
* Echo a language string (shorthand for echo __())
*
* @param string $key Language key, supports dot notation
* @param mixed $default Default value if key doesn't exist
* @return void
*/
function _e(string $key, mixed $default = null): void
{
echo \TorrentPier\Language::getInstance()->get($key, $default);
}
/**
* Initialize debug
*/
define('APP_ENV', env('APP_ENV', 'production'));
if (APP_ENV === 'development') {
define('DBG_USER', true); // forced debug
} else {
define('DBG_USER', isset($_COOKIE[COOKIE_DBG]));
}
(\TorrentPier\Dev::init());
/**
* Server variables initialize
*/
$server_protocol = config()->get('cookie_secure') ? 'https://' : 'http://';
$server_port = in_array((int)config()->get('server_port'), [80, 443], true) ? '' : ':' . config()->get('server_port');
define('FORUM_PATH', config()->get('script_path'));
define('FULL_URL', $server_protocol . config()->get('server_name') . $server_port . config()->get('script_path'));
$server_protocol = $bb_cfg['cookie_secure'] ? 'https://' : 'http://';
$server_port = in_array((int)$bb_cfg['server_port'], array(80, 443), true) ? '' : ':' . $bb_cfg['server_port'];
define('FORUM_PATH', $bb_cfg['script_path']);
define('FULL_URL', $server_protocol . $bb_cfg['server_name'] . $server_port . $bb_cfg['script_path']);
unset($server_protocol, $server_port);
// Initialize the new DB factory with database configuration
TorrentPier\Database\DatabaseFactory::init(config()->get('db'), config()->get('db_alias', []));
// Debug options
define('DBG_USER', (isset($_COOKIE[COOKIE_DBG])));
// Board / tracker shared constants and functions
define('BB_BT_TORRENTS', 'bb_bt_torrents');
define('BB_BT_TRACKER', 'bb_bt_tracker');
define('BB_BT_TRACKER_SNAP', 'bb_bt_tracker_snap');
define('BB_BT_USERS', 'bb_bt_users');
define('BT_AUTH_KEY_LENGTH', 10);
define('PEER_HASH_PREFIX', 'peer_');
define('PEERS_LIST_PREFIX', 'peers_list_');
define('PEER_HASH_EXPIRE', round($bb_cfg['announce_interval'] * (0.85 * $bb_cfg['tracker']['expire_factor']))); // sec
define('PEERS_LIST_EXPIRE', round($bb_cfg['announce_interval'] * 0.7)); // sec
define('DL_STATUS_RELEASER', -1);
define('DL_STATUS_DOWN', 0);
define('DL_STATUS_COMPLETE', 1);
define('DL_STATUS_CANCEL', 3);
define('DL_STATUS_WILL', 4);
define('TOR_TYPE_GOLD', 1);
define('TOR_TYPE_SILVER', 2);
define('GUEST_UID', -1);
define('BOT_UID', -746);
/**
* Get the Database instance
*
* @param string $db_alias
* @return \TorrentPier\Database\Database
* Progressive error reporting
*/
function DB(string $db_alias = 'db'): \TorrentPier\Database\Database
{
return TorrentPier\Database\DatabaseFactory::getInstance($db_alias);
if ($bb_cfg['bugsnag']['enabled'] && env('APP_ENV', 'production') !== 'local') {
/** @var Bugsnag\Handler $bugsnag */
$bugsnag = Bugsnag\Client::make($bb_cfg['bugsnag']['api_key']);
Bugsnag\Handler::register($bugsnag);
}
// Initialize Unified Cache System
TorrentPier\Cache\UnifiedCacheSystem::getInstance(config()->all());
/**
* Get cache manager instance (replaces legacy cache system)
*
* @param string $cache_name
* @return \TorrentPier\Cache\CacheManager
*/
function CACHE(string $cache_name): \TorrentPier\Cache\CacheManager
{
return TorrentPier\Cache\UnifiedCacheSystem::getInstance()->get_cache_obj($cache_name);
if (DBG_USER && env('APP_ENV', 'production') === 'local') {
/** @var Whoops\Run $whoops */
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
}
/**
* Get datastore manager instance (replaces legacy datastore system)
*
* @return \TorrentPier\Cache\DatastoreManager
* Database
*/
function datastore(): \TorrentPier\Cache\DatastoreManager
$DBS = new TorrentPier\Legacy\Dbs($bb_cfg);
function DB($db_alias = 'db')
{
return TorrentPier\Cache\UnifiedCacheSystem::getInstance()->getDatastore(config()->get('datastore_type', 'file'));
global $DBS;
return $DBS->get_db_obj($db_alias);
}
/**
* Backward compatibility: Global datastore variable
* This allows existing code to continue using global $datastore
* Cache
*/
$datastore = datastore();
$CACHES = new TorrentPier\Legacy\Caches($bb_cfg);
function CACHE($cache_name)
{
global $CACHES;
return $CACHES->get_cache_obj($cache_name);
}
/**
* Datastore
*/
switch ($bb_cfg['datastore_type']) {
case 'memcache':
$datastore = new TorrentPier\Legacy\Datastore\Memcache($bb_cfg['cache']['memcache'], $bb_cfg['cache']['prefix']);
break;
case 'sqlite':
$default_cfg = array(
'db_file_path' => $bb_cfg['cache']['db_dir'] . 'datastore.sqlite.db',
'pconnect' => true,
'con_required' => true,
);
$datastore = new TorrentPier\Legacy\Datastore\Sqlite($default_cfg, $bb_cfg['cache']['prefix']);
break;
case 'redis':
$datastore = new TorrentPier\Legacy\Datastore\Redis($bb_cfg['cache']['redis'], $bb_cfg['cache']['prefix']);
break;
case 'apc':
$datastore = new TorrentPier\Legacy\Datastore\Apc($bb_cfg['cache']['prefix']);
break;
case 'xcache':
$datastore = new TorrentPier\Legacy\Datastore\Xcache($bb_cfg['cache']['prefix']);
break;
case 'filecache':
default:
$datastore = new TorrentPier\Legacy\Datastore\File($bb_cfg['cache']['db_dir'] . 'datastore/', $bb_cfg['cache']['prefix']);
}
function sql_dbg_enabled()
{
return (SQL_DEBUG && DBG_USER && !empty($_COOKIE['sql_log']));
}
function short_query($sql, $esc_html = false)
{
$max_len = 100;
$sql = str_compact($sql);
if (!empty($_COOKIE['sql_log_full'])) {
if (mb_strlen($sql, 'UTF-8') > $max_len) {
$sql = mb_substr($sql, 0, 50) . ' [...cut...] ' . mb_substr($sql, -50);
}
}
return $esc_html ? htmlCHR($sql, true) : $sql;
}
// Functions
function utime()
@ -224,49 +245,48 @@ function utime()
return array_sum(explode(' ', microtime()));
}
function bb_log($msg, $file_name = 'logs', $return_path = false)
function bb_log($msg, $file_name)
{
if (is_array($msg)) {
$msg = implode(LOG_LF, $msg);
}
$file_name .= (LOG_EXT) ? '.' . LOG_EXT : '';
$path = (LOG_DIR . '/' . $file_name);
if ($return_path) {
return $path;
}
return file_write($msg, $path);
return file_write($msg, LOG_DIR . '/' . $file_name);
}
function file_write($str, $file, $max_size = LOG_MAX_SIZE, $lock = true, $replace_content = false)
{
$bytes_written = false;
clearstatcache();
if (is_file($file) && ($max_size && (filesize($file) >= $max_size))) {
$file_parts = pathinfo($file);
$new_name = ($file_parts['dirname'] . '/' . $file_parts['filename'] . '_[old]_' . date('Y-m-d_H-i-s_') . getmypid() . '.' . $file_parts['extension']);
if ($max_size && file_exists($file) && filesize($file) >= $max_size) {
$old_name = $file;
$ext = '';
if (preg_match('#^(.+)(\.[^\\/]+)$#', $file, $matches)) {
$old_name = $matches[1];
$ext = $matches[2];
}
$new_name = $old_name . '_[old]_' . date('Y-m-d_H-i-s_') . getmypid() . $ext;
clearstatcache();
if (!is_file($new_name)) {
if (!file_exists($new_name)) {
rename($file, $new_name);
}
}
clearstatcache();
if (bb_mkdir(dirname($file))) {
if ($fp = fopen($file, 'ab+')) {
if ($lock) {
flock($fp, LOCK_EX);
}
if ($replace_content) {
ftruncate($fp, 0);
fseek($fp, 0, SEEK_SET);
}
$bytes_written = fwrite($fp, $str);
fclose($fp);
if (!$fp = fopen($file, 'ab')) {
if ($dir_created = bb_mkdir(dirname($file))) {
$fp = fopen($file, 'ab');
}
}
if ($fp) {
if ($lock) {
flock($fp, LOCK_EX);
}
if ($replace_content) {
ftruncate($fp, 0);
fseek($fp, 0, SEEK_SET);
}
$bytes_written = fwrite($fp, $str);
fclose($fp);
}
return $bytes_written;
}
@ -279,96 +299,83 @@ function bb_mkdir($path, $mode = 0777)
return $dir;
}
function mkdir_rec($path, $mode): bool
function mkdir_rec($path, $mode)
{
if (is_dir($path)) {
return ($path !== '.' && $path !== '..') && is_writable($path);
return ($path !== '.' && $path !== '..') ? is_writable($path) : false;
}
return mkdir_rec(dirname($path), $mode) && mkdir($path, $mode);
return mkdir_rec(dirname($path), $mode) ? mkdir($path, $mode) : false;
}
function verify_id($id, $length): bool
function verify_id($id, $length)
{
return (is_string($id) && preg_match('#^[a-zA-Z0-9]{' . $length . '}$#', $id));
}
function clean_filename($fname)
{
static $s = ['\\', '/', ':', '*', '?', '"', '<', '>', '|', ' '];
static $s = array('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' ');
return str_replace($s, '_', str_compact($fname));
}
/**
* Convert special characters to HTML entities
*
* @param $txt
* @param bool $double_encode
* @param int $quote_style
* @param ?string $charset
* Декодирование оригинального IP
* @param $ip
* @return string
*/
function htmlCHR($txt, bool $double_encode = false, int $quote_style = ENT_QUOTES, ?string $charset = DEFAULT_CHARSET): string
function encode_ip($ip)
{
return (string)htmlspecialchars($txt ?? '', $quote_style, $charset, $double_encode);
return Longman\IPTools\Ip::ip2long($ip);
}
/**
* @param string $str
* Восстановление декодированного IP
* @param $ip
* @return string
*/
function decode_ip($ip)
{
return Longman\IPTools\Ip::long2ip($ip);
}
/**
* Проверка IP на валидность
*
* @param $ip
* @return bool
*/
function verify_ip($ip)
{
return Longman\IPTools\Ip::isValid($ip);
}
function bb_crc32($str)
{
return (float)sprintf('%u', crc32($str));
}
function hexhex($value)
{
return dechex(hexdec($value));
}
function str_compact($str)
{
return preg_replace('/\s\s+/', ' ', trim($str ?? ''));
return preg_replace('#\s+#u', ' ', trim($str));
}
/**
* Generate a "random" alphanumeric string.
*
* Should not be considered sufficient for cryptography, etc.
*
* @param int $length
* @return string
* @throws Exception
*/
function make_rand_str(int $length = 10): string
function make_rand_str($len = 10)
{
$pool = str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $pool[random_int(0, 61)];
$str = '';
while (strlen($str) < $len) {
$str .= str_shuffle(preg_replace('#[^0-9a-zA-Z]#', '', password_hash(uniqid(mt_rand(), true), PASSWORD_BCRYPT)));
}
return $randomString;
return substr($str, 0, $len);
}
/**
* Calculates user ratio
*
* @param array $btu
* @return float|null
*/
function get_bt_ratio(array $btu): ?float
function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false)
{
return
(!empty($btu['u_down_total']) && $btu['u_down_total'] > MIN_DL_FOR_RATIO)
? round((($btu['u_up_total'] + $btu['u_up_release'] + $btu['u_up_bonus']) / $btu['u_down_total']), 2)
: null;
}
function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false, $timeout = false)
{
if ($timeout) {
static $recursions = 0;
if (time() > (TIMENOW + $timeout)) {
return [
'timeout' => true,
'recs' => $recursions
];
}
$recursions++;
}
if (is_array($var)) {
foreach ($var as $k => $v) {
if (is_array($v)) {
@ -377,7 +384,7 @@ function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false, $
} elseif ($array_only) {
$var[$k] = $fn($v);
} else {
array_deep($var[$k], $fn, timeout: $timeout);
array_deep($var[$k], $fn);
}
} elseif (!$array_only) {
$var[$k] = $fn($v);
@ -388,69 +395,105 @@ function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false, $
}
}
/**
* Hide BB_PATH
*
* @param string $path
* @return string
*/
function hide_bb_path(string $path): string
function hide_bb_path($path)
{
return ltrim(str_replace(BB_PATH, '', $path), '/\\');
}
/**
* Returns memory usage statistic
*
* @param string $param
* @return int|void
*/
function sys(string $param)
function sys($param)
{
switch ($param) {
case 'la':
return function_exists('sys_getloadavg') ? implode(' ', sys_getloadavg()) : 0;
break;
case 'mem':
return memory_get_usage();
return function_exists('memory_get_usage') ? memory_get_usage() : 0;
break;
case 'mem_peak':
return memory_get_peak_usage();
return function_exists('memory_get_peak_usage') ? memory_get_peak_usage() : 0;
break;
default:
trigger_error("invalid param: $param", E_USER_ERROR);
}
}
/**
* Some shared defines
*/
// Initialize demo mode
define('IN_DEMO_MODE', env('APP_DEMO_MODE', false));
function ver_compare($version1, $operator, $version2)
{
return version_compare($version1, $version2, $operator);
}
// Ratio status
define('RATIO_ENABLED', TR_RATING_LIMITS && MIN_DL_FOR_RATIO > 0);
function dbg_log($str, $file)
{
$dir = LOG_DIR . (defined('IN_TRACKER') ? '/dbg_tr/' : '/dbg_bb/') . date('m-d_H') . '/';
return file_write($str, $dir . $file, false, false);
}
// Initialization
function log_get($file = '', $prepend_str = false)
{
log_request($file, $prepend_str, false);
}
function log_post($file = '', $prepend_str = false)
{
log_request($file, $prepend_str, true);
}
function log_request($file = '', $prepend_str = false, $add_post = true)
{
global $user;
$file = $file ?: 'req/' . date('m-d');
$str = array();
$str[] = date('m-d H:i:s');
if ($prepend_str !== false) {
$str[] = $prepend_str;
}
if (!empty($user->data)) {
$str[] = $user->id . "\t" . html_entity_decode($user->name);
}
$str[] = sprintf('%-15s', $_SERVER['REMOTE_ADDR']);
if (isset($_SERVER['REQUEST_URI'])) {
$str[] = $_SERVER['REQUEST_URI'];
}
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$str[] = $_SERVER['HTTP_USER_AGENT'];
}
if (isset($_SERVER['HTTP_REFERER'])) {
$str[] = $_SERVER['HTTP_REFERER'];
}
if (!empty($_POST) && $add_post) {
$str[] = "post: " . str_compact(urldecode(http_build_query($_POST)));
}
$str = implode("\t", $str) . "\n";
bb_log($str, $file);
}
// Board or tracker init
if (!defined('IN_TRACKER')) {
// Init board
require_once INC_DIR . '/init_bb.php';
require INC_DIR . '/init_bb.php';
} else {
define('DUMMY_PEER', pack('Nn', \TorrentPier\Helpers\IPHelper::ip2long($_SERVER['REMOTE_ADDR']), !empty($_GET['port']) ? (int)$_GET['port'] : random_int(1000, 65000)));
define('DUMMY_PEER', pack('Nn', ip2long($_SERVER['REMOTE_ADDR']), !empty($_GET['port']) ? (int)$_GET['port'] : mt_rand(1000, 65000)));
define('PEER_HASH_EXPIRE', round(config()->get('announce_interval') * (0.85 * config()->get('tracker.expire_factor'))));
define('PEERS_LIST_EXPIRE', round(config()->get('announce_interval') * 0.7));
define('SCRAPE_LIST_EXPIRE', round(config()->get('scrape_interval') * 0.7));
function dummy_exit($interval = 1800)
{
$output = \Rych\Bencode\Bencode::encode([
'interval' => (int)$interval,
'min interval' => (int)$interval,
'peers' => (string)DUMMY_PEER,
]);
define('PEER_HASH_PREFIX', 'peer_');
define('PEERS_LIST_PREFIX', 'peers_list_');
define('SCRAPE_LIST_PREFIX', 'scrape_list_');
// Init tracker
require_once BB_PATH . '/bt/includes/init_tr.php';
die($output);
}
header('Content-Type: text/plain');
header('Pragma: no-cache');
if (!defined('IN_ADMIN')) {
// Exit if tracker is disabled via ON/OFF trigger
if (is_file(BB_DISABLED)) {
dummy_exit(random_int(60, 2400));
if (file_exists(BB_DISABLED)) {
dummy_exit(mt_rand(60, 2400));
}
}
}

View file

@ -8,95 +8,51 @@
"torrent",
"tracker"
],
"homepage": "https://github.com/torrentpier",
"homepage": "https://torrentpier.me/",
"license": "MIT",
"authors": [
{
"name": "Exile",
"email": "admin@torrentpier.com",
"homepage": "https://github.com/Exileum",
"email": "admin@torrentpier.me",
"homepage": "https://torrentpier.me/forum/members/exile.1/",
"role": "Developer"
},
{
"name": "Diolektor",
"homepage": "https://github.com/diolektor",
"homepage": "https://torrentpier.me/forum/members/diolektor.5765/",
"role": "Developer"
},
{
"name": "PheRum",
"homepage": "https://github.com/PheRum",
"role": "Developer"
},
{
"name": "belomaxorka",
"email": "roman25052006.kelesh@gmail.com",
"homepage": "https://github.com/belomaxorka",
"role": "Developer"
},
{
"name": "kovalensky",
"email": "kovalensky@evergarden.ru",
"homepage": "https://github.com/kovalensky",
"homepage": "https://torrentpier.me/forum/members/pherum.23/",
"role": "Developer"
}
],
"support": {
"email": "support@torrentpier.com",
"email": "support@torrentpier.me",
"issues": "https://github.com/torrentpier/torrentpier/issues",
"forum": "https://torrentpier.com"
"forum": "https://torrentpier.me/forum/",
"docs": "https://docs.torrentpier.me/"
},
"require": {
"php": ">=8.2",
"arokettu/bencode": "^4.1.0",
"arokettu/monsterid": "^4.1.0",
"arokettu/random-polyfill": "1.0.2",
"arokettu/torrent-file": "^5.2.1",
"belomaxorka/captcha": "1.*",
"bugsnag/bugsnag": "^v3.29.1",
"claviska/simpleimage": "^4.0",
"egulias/email-validator": "^4.0.1",
"filp/whoops": "^2.15",
"gemorroj/m3u-parser": "^6.0.1",
"gigablah/sphinxphp": "2.0.8",
"google/recaptcha": "^1.3",
"jacklul/monolog-telegram": "^3.1",
"josantonius/cookie": "^2.0",
"league/flysystem": "^3.28",
"longman/ip-tools": "1.2.1",
"monolog/monolog": "^3.4",
"nette/caching": "^3.3",
"nette/database": "^3.2",
"php-curl-class/php-curl-class": "^12.0.0",
"robmorgan/phinx": "^0.16.9",
"samdark/sitemap": "2.4.1",
"symfony/mailer": "^7.3",
"symfony/polyfill": "v1.32.0",
"vlucas/phpdotenv": "^5.5",
"z4kn4fein/php-semver": "^v3.0.0"
},
"require-dev": {
"mockery/mockery": "^1.6",
"pestphp/pest": "^3.8",
"symfony/var-dumper": "^7.3"
"php": "^5.6 || ^7.0",
"bugsnag/bugsnag": "^3.5",
"filp/whoops": "^2.1",
"gigablah/sphinxphp": "^2.0",
"google/recaptcha": "^1.1",
"longman/ip-tools": "^1.2",
"roave/security-advisories": "dev-master",
"rych/bencode": "^1.0",
"samdark/sitemap": "^2.0",
"swiftmailer/swiftmailer": "^5.4",
"symfony/dotenv": "^3.3",
"symfony/var-dumper": "^3.3"
},
"autoload": {
"psr-4": {
"TorrentPier\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"config": {
"sort-packages": true,
"optimize-autoloader": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

7646
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('START_CRON', true);

6
crossdomain.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">
<allow-access-from domain="*" to-ports="*" secure="false"/>
<site-control permitted-cross-domain-policies="master-only"/>
</cross-domain-policy>

View file

@ -1,6 +1,7 @@
files:
- source: /styles/templates/default/images/lang/source/*.*
translation: /styles/templates/default/images/lang/%two_letters_code%/%original_file_name%
translation: >-
/styles/templates/default/images/lang/%two_letters_code%/%original_file_name%
- source: /library/language/source/*.*
translation: /library/language/%two_letters_code%/%original_file_name%
- source: /library/language/source/email/*.*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Before After
Before After

View file

@ -0,0 +1,3 @@
php_flag engine off
RemoveHandler .php .php5 .php4 .php3 .phtml .pl .asp
AddType text/plain .php .php .htm .html .phtml .pl .asp

160
dl.php
View file

@ -1,59 +1,74 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'dl');
define('NO_GZIP', true);
define('BB_ROOT', './');
require __DIR__ . '/common.php';
require ATTACH_DIR . '/attachment_mod.php';
$datastore->enqueue([
$datastore->enqueue(array(
'attach_extensions',
'cat_forums'
]);
'cat_forums',
));
$download_id = request_var('id', 0);
$thumbnail = request_var('thumb', 0);
$m3u = isset($_GET['m3u']) && $_GET['m3u'];
// Send file to browser
function send_file_to_browser($attachment, $upload_dir)
{
global $lang;
global $bb_cfg, $lang, $userdata;
$filename = ($upload_dir == '') ? $attachment['physical_filename'] : $upload_dir . '/' . $attachment['physical_filename'];
$filename = $upload_dir . '/' . $attachment['physical_filename'];
$gotit = false;
if (is_file(realpath($filename))) {
$gotit = true;
if (@!file_exists(@amod_realpath($filename))) {
bb_die($lang['ERROR_NO_ATTACHMENT'] . "<br /><br />" . $filename . "<br /><br />" . $lang['TOR_NOT_FOUND']);
} else {
bb_die($lang['ERROR_NO_ATTACHMENT'] . '<br /><br />' . htmlCHR($filename));
$gotit = true;
}
// Correct the mime type - we force application/octet-stream for all files, except images
// Please do not change this, it is a security precaution
if (!str_contains($attachment['mimetype'], 'image')) {
if (false === strpos($attachment['mimetype'], 'image')) {
$attachment['mimetype'] = 'application/octet-stream';
} else {
header('Cache-Control: public, max-age=3600');
}
//bt
if (!(isset($_GET['original']) && !IS_USER)) {
\TorrentPier\Legacy\Torrent::send_torrent_with_passkey($filename);
include INC_DIR . '/functions_torrent.php';
send_torrent_with_passkey($filename);
}
// Now the tricky part... let's dance
header('Pragma: public');
$real_filename = clean_filename(basename($attachment['real_filename']));
$mimetype = $attachment['mimetype'] . ';';
$charset = 'charset=' . DEFAULT_CHARSET . ';';
$charset = "charset={$bb_cfg['charset']};";
// Send out the Headers
header("Content-Type: $mimetype $charset name=\"$real_filename\"");
@ -62,13 +77,13 @@ function send_file_to_browser($attachment, $upload_dir)
// Now send the File Contents to the Browser
if ($gotit) {
$size = filesize($filename);
$size = @filesize($filename);
if ($size) {
header("Content-length: $size");
}
readfile($filename);
} else {
bb_die($lang['ERROR_NO_ATTACHMENT'] . '<br /><br />' . htmlCHR($filename));
bb_die($lang['ERROR_NO_ATTACHMENT'] . "<br /><br />" . $filename . "<br /><br />" . $lang['TOR_NOT_FOUND']);
}
exit;
@ -101,20 +116,6 @@ if (!($attachment = DB()->sql_fetchrow($result))) {
$attachment['physical_filename'] = basename($attachment['physical_filename']);
if ($thumbnail) {
// Re-define $attachment['physical_filename'] for thumbnails
$attachment['physical_filename'] = THUMB_DIR . '/t_' . $attachment['physical_filename'];
} elseif ($m3u) {
// Check m3u file exist
if (!$m3uFile = (new \TorrentPier\TorrServerAPI())->getM3UPath($download_id)) {
bb_die($lang['ERROR_NO_ATTACHMENT']);
}
$attachment['physical_filename'] = $attachment['real_filename'] = basename($m3uFile);
$attachment['mimetype'] = mime_content_type($m3uFile);
$attachment['extension'] = str_replace('.', '', \TorrentPier\TorrServerAPI::M3U['extension']);
}
DB()->sql_freeresult($result);
// get forum_id for attachment authorization or private message authorization
@ -144,6 +145,7 @@ for ($i = 0; $i < $num_auth_pages && $authorised == false; $i++) {
$topic_id = $row['topic_id'];
$forum_id = $row['forum_id'];
$is_auth = array();
$is_auth = auth(AUTH_ALL, $forum_id, $userdata);
set_die_append_msg($forum_id, $topic_id);
@ -153,58 +155,37 @@ for ($i = 0; $i < $num_auth_pages && $authorised == false; $i++) {
}
}
// Check the auth rights
if (!$authorised) {
bb_die($lang['SORRY_AUTH_VIEW_ATTACH'], 403);
bb_die($lang['SORRY_AUTH_VIEW_ATTACH']);
}
$datastore->rm('cat_forums');
// Check tor status
if (!IS_AM && ($attachment['mimetype'] === TORRENT_MIMETYPE)) {
$sql = 'SELECT tor_status, poster_id FROM ' . BB_BT_TORRENTS . ' WHERE attach_id = ' . (int)$attachment['attach_id'];
if (!($result = DB()->sql_query($sql))) {
bb_die('Could not query tor_status information');
}
$row = DB()->sql_fetchrow($result);
if (isset(config()->get('tor_frozen')[$row['tor_status']]) && !(isset(config()->get('tor_frozen_author_download')[$row['tor_status']]) && $userdata['user_id'] === $row['poster_id'])) {
bb_die($lang['TOR_STATUS_FORBIDDEN'] . $lang['TOR_STATUS_NAME'][$row['tor_status']]);
}
DB()->sql_freeresult($result);
}
//
// Get Information on currently allowed Extensions
//
$rows = get_extension_informations();
$num_rows = count($rows);
$allowed_extensions = $download_mode = [];
for ($i = 0; $i < $num_rows; $i++) {
$extension = strtolower(trim($rows[$i]['extension']));
// Get allowed extensions
if ((int)$rows[$i]['allow_group'] === 1) {
$allowed_extensions[] = $extension;
}
$allowed_extensions[] = $extension;
$download_mode[$extension] = $rows[$i]['download_mode'];
}
// Disallowed
if (!in_array($attachment['extension'], $allowed_extensions) && !IS_ADMIN) {
bb_die(sprintf($lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension']) . '<br /><br />' . $lang['FILENAME'] . ":&nbsp;" . $attachment['physical_filename']);
bb_die(sprintf($lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension']));
}
// Getting download mode by extension
if (isset($download_mode[$attachment['extension']])) {
$download_mode = (int)$download_mode[$attachment['extension']];
} else {
bb_die(sprintf($lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension']) . '<br /><br />' . $lang['FILENAME'] . ":&nbsp;" . $attachment['physical_filename']);
$download_mode = (int)$download_mode[$attachment['extension']];
if ($thumbnail) {
$attachment['physical_filename'] = THUMB_DIR . '/t_' . $attachment['physical_filename'];
}
// Update download count
if (!$m3u && !$thumbnail && is_file(realpath($upload_dir . '/' . $attachment['physical_filename']))) {
if (!$thumbnail) {
$sql = 'UPDATE ' . BB_ATTACHMENTS_DESC . ' SET download_count = download_count + 1 WHERE attach_id = ' . (int)$attachment['attach_id'];
if (!DB()->sql_query($sql)) {
@ -213,32 +194,31 @@ if (!$m3u && !$thumbnail && is_file(realpath($upload_dir . '/' . $attachment['ph
}
// Determine the 'presenting'-method
switch ($download_mode) {
case PHYSICAL_LINK:
$url = make_url($upload_dir . '/' . $attachment['physical_filename']);
header('Location: ' . $url);
exit;
case INLINE_LINK:
if (IS_GUEST && !config()->get('captcha.disabled') && !bb_captcha('check')) {
global $template;
if ($download_mode == PHYSICAL_LINK) {
$url = make_url($upload_dir . '/' . $attachment['physical_filename']);
header('Location: ' . $url);
exit;
} else {
if (IS_GUEST && !bb_captcha('check')) {
global $template;
$redirect_url = $_POST['redirect_url'] ?? $_SERVER['HTTP_REFERER'] ?? '/';
$message = '<form action="' . DL_URL . $attachment['attach_id'] . '" method="post">';
$message .= $lang['CAPTCHA'] . ':';
$message .= '<div class="mrg_10" align="center">' . bb_captcha('get') . '</div>';
$message .= '<input type="hidden" name="redirect_url" value="' . $redirect_url . '" />';
$message .= '<input type="submit" class="bold" value="' . $lang['SUBMIT'] . '" /> &nbsp;';
$message .= '<input type="button" class="bold" value="' . $lang['GO_BACK'] . '" onclick="document.location.href = \'' . $redirect_url . '\';" />';
$message .= '</form>';
$redirect_url = isset($_POST['redirect_url']) ? $_POST['redirect_url'] : (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '/');
$message = '<form action="' . DOWNLOAD_URL . $attachment['attach_id'] . '" method="post">';
$message .= $lang['CAPTCHA'] . ':';
$message .= '<div class="mrg_10" align="center">' . bb_captcha('get') . '</div>';
$message .= '<input type="hidden" name="redirect_url" value="' . $redirect_url . '" />';
$message .= '<input type="submit" class="bold" value="' . $lang['SUBMIT'] . '" /> &nbsp;';
$message .= '<input type="button" class="bold" value="' . $lang['GO_BACK'] . '" onclick="document.location.href = \'' . $redirect_url . '\';" />';
$message .= '</form>';
$template->assign_vars(['ERROR_MESSAGE' => $message]);
$template->assign_vars(array(
'ERROR_MESSAGE' => $message,
));
require(PAGE_HEADER);
require(PAGE_FOOTER);
}
require(PAGE_HEADER);
require(PAGE_FOOTER);
}
send_file_to_browser($attachment, $upload_dir);
exit;
default:
bb_die('Incorrect download mode: ' . $download_mode);
send_file_to_browser($attachment, $upload_dir);
exit;
}

View file

@ -1,18 +1,34 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'dl_list');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
$forum_id = $_REQUEST[POST_FORUM_URL] ?? 0;
$topic_id = $_REQUEST[POST_TOPIC_URL] ?? 0;
$forum_id = isset($_REQUEST[POST_FORUM_URL]) ? (int)$_REQUEST[POST_FORUM_URL] : 0;
$topic_id = isset($_REQUEST[POST_TOPIC_URL]) ? (int)$_REQUEST[POST_TOPIC_URL] : 0;
$mode = isset($_REQUEST['mode']) ? (string)$_REQUEST['mode'] : '';
$confirmed = isset($_POST['confirm']);
@ -39,10 +55,10 @@ if ($mode == 'set_dl_status' || $mode == 'set_topics_dl_status') {
$full_url = isset($_POST['full_url']) ? str_replace('&amp;', '&', htmlspecialchars($_POST['full_url'])) : '';
if (isset($_POST['redirect_type']) && $_POST['redirect_type'] == 'search') {
$redirect_type = 'search.php';
$redirect_type = "search.php";
$redirect = $full_url ?: "$dl_key=1";
} else {
$redirect_type = (!$topic_id) ? 'viewforum.php' : 'viewtopic.php';
$redirect_type = (!$topic_id) ? "viewforum.php" : "viewtopic.php";
$redirect = $full_url ?: ((!$topic_id) ? POST_FORUM_URL . "=$forum_id" : POST_TOPIC_URL . "=$topic_id");
}
@ -52,7 +68,7 @@ $user->session_start();
set_die_append_msg();
// Check if user logged in
if (IS_GUEST) {
if (!$userdata['session_logged_in']) {
redirect(LOGIN_URL . "?redirect=$redirect_type&$redirect");
}
@ -78,16 +94,16 @@ if ($mode == 'dl_delete' && $topic_id) {
}
if (!$confirmed) {
$hidden_fields = [
POST_TOPIC_URL => $topic_id,
$hidden_fields = array(
't' => $topic_id,
'mode' => 'dl_delete',
];
);
print_confirmation([
print_confirmation(array(
'QUESTION' => $lang['DL_LIST_DEL_CONFIRM'],
'FORM_ACTION' => 'dl_list.php',
'HIDDEN_FIELDS' => build_hidden_fields($hidden_fields),
]);
));
}
clear_dl_list($topic_id);
@ -95,7 +111,7 @@ if ($mode == 'dl_delete' && $topic_id) {
}
// Update DL status
$req_topics_ary = $topics_ary = [];
$req_topics_ary = $topics_ary = array();
// Get topics selected by user
if ($mode == 'set_topics_dl_status') {
@ -120,14 +136,14 @@ if ($req_topics_sql = implode(',', $req_topics_ary)) {
}
if ($topics_ary && ($mode == 'set_dl_status' || $mode == 'set_topics_dl_status')) {
$new_dlstatus_ary = [];
$new_dlstatus_ary = array();
foreach ($topics_ary as $topic_id) {
$new_dlstatus_ary[] = [
$new_dlstatus_ary[] = array(
'user_id' => (int)$user->id,
'topic_id' => (int)$topic_id,
'user_status' => (int)$new_dl_status,
];
);
}
$new_dlstatus_sql = DB()->build_array('MULTI_INSERT', $new_dlstatus_ary);
@ -136,4 +152,4 @@ if ($topics_ary && ($mode == 'set_dl_status' || $mode == 'set_topics_dl_status')
redirect("$redirect_type?$redirect");
}
redirect('index.php');
redirect("index.php");

View file

@ -1,50 +1,66 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'feed');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
// Init userdata
$user->session_start(['req_login' => true]);
$user->session_start(array('req_login' => true));
$mode = $_REQUEST['mode'] ?? '';
$type = $_POST['type'] ?? '';
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : '';
$type = isset($_POST['type']) ? $_POST['type'] : '';
$id = isset($_POST['id']) ? $_POST['id'] : 0;
$timecheck = TIMENOW - 600;
if (!$mode) {
bb_simple_die($lang['ATOM_NO_MODE']);
}
if ($mode === 'get_feed_url' && ($type === 'f' || $type === 'u') && $id >= 0) {
if ($mode == 'get_feed_url' && ($type == 'f' || $type == 'u') && $id >= 0) {
if ($type == 'f') {
// Check if the user has actually sent a forum ID
$sql = "SELECT allow_reg_tracker, forum_name FROM " . BB_FORUMS . " WHERE forum_id = $id LIMIT 1";
if (!$forum_data = DB()->fetch_row($sql)) {
if ($id == 0) {
$forum_data = [];
$forum_data = array();
} else {
bb_simple_die($lang['ATOM_ERROR'] . ' #1');
}
}
if (is_file(config()->get('atom.path') . '/f/' . $id . '.atom') && filemtime(config()->get('atom.path') . '/f/' . $id . '.atom') > $timecheck) {
redirect(config()->get('atom.url') . '/f/' . $id . '.atom');
if (file_exists($bb_cfg['atom']['path'] . '/f/' . $id . '.atom') && filemtime($bb_cfg['atom']['path'] . '/f/' . $id . '.atom') > $timecheck) {
redirect($bb_cfg['atom']['url'] . '/f/' . $id . '.atom');
} else {
if (\TorrentPier\Legacy\Atom::update_forum_feed($id, $forum_data)) {
redirect(config()->get('atom.url') . '/f/' . $id . '.atom');
require_once INC_DIR . '/functions_atom.php';
if (update_forum_feed($id, $forum_data)) {
redirect($bb_cfg['atom']['url'] . '/f/' . $id . '.atom');
} else {
bb_simple_die($lang['ATOM_NO_FORUM']);
}
}
}
if ($type === 'u') {
if ($type == 'u') {
// Check if the user has actually sent a user ID
if ($id < 1) {
bb_simple_die($lang['ATOM_ERROR'] . ' #2');
@ -52,11 +68,12 @@ if ($mode === 'get_feed_url' && ($type === 'f' || $type === 'u') && $id >= 0) {
if (!$username = get_username($id)) {
bb_simple_die($lang['ATOM_ERROR'] . ' #3');
}
if (is_file(config()->get('atom.path') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') && filemtime(config()->get('atom.path') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') > $timecheck) {
redirect(config()->get('atom.url') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom');
if (file_exists($bb_cfg['atom']['path'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') && filemtime($bb_cfg['atom']['path'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') > $timecheck) {
redirect($bb_cfg['atom']['url'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom');
} else {
if (\TorrentPier\Legacy\Atom::update_user_feed($id, $username)) {
redirect(config()->get('atom.url') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom');
require_once INC_DIR . '/functions_atom.php';
if (update_user_feed($id, $username)) {
redirect($bb_cfg['atom']['url'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom');
} else {
bb_simple_die($lang['ATOM_NO_USER']);
}

View file

@ -1,117 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
define('BB_SCRIPT', 'filelist');
require __DIR__ . '/common.php';
// Start session management
$user->session_start();
if (config()->get('bt_disable_dht') && IS_GUEST) {
bb_die($lang['BT_PRIVATE_TRACKER'], 403);
}
$topic_id = isset($_GET[POST_TOPIC_URL]) ? (int)$_GET[POST_TOPIC_URL] : 0;
if (!$topic_id) {
bb_die($lang['INVALID_TOPIC_ID'], 404);
}
$sql = 'SELECT t.forum_id, t.attach_id, t.info_hash, t.info_hash_v2, t.size, ad.physical_filename
FROM ' . BB_BT_TORRENTS . ' t
LEFT JOIN ' . BB_ATTACHMENTS_DESC . ' ad
ON t.attach_id = ad.attach_id
WHERE t.topic_id = ' . $topic_id . '
LIMIT 1';
if (!$row = DB()->fetch_row($sql)) {
bb_die($lang['INVALID_TOPIC_ID_DB'], 404);
}
// Check rights
$is_auth = auth(AUTH_ALL, $row['forum_id'], $userdata);
if (!$is_auth['auth_view']) {
bb_die($lang['SORRY_AUTH_VIEW_ATTACH'], 403);
}
// Protocol meta
$meta_v1 = !empty($row['info_hash']);
$meta_v2 = !empty($row['info_hash_v2']);
// Method fields
$t_version_field = $meta_v2 ? 'v2' : 'v1';
$t_files_field = $meta_v2 ? 'getFileTree' : 'getFiles';
$t_hash_field = $meta_v2 ? 'piecesRoot' : 'sha1';
$file_path = get_attachments_dir() . '/' . $row['physical_filename'];
if (!is_file($file_path)) {
bb_die($lang['TOR_NOT_FOUND'], 410);
}
$file_contents = file_get_contents($file_path);
if (config()->get('flist_max_files')) {
$filetree_pos = $meta_v2 ? strpos($file_contents, '9:file tree') : false;
$files_pos = $meta_v1 ? strpos($file_contents, '5:files', $filetree_pos) : false;
if ($filetree_pos) {
$file_count = substr_count($file_contents, '6:length', $filetree_pos, ($files_pos ? ($files_pos - $filetree_pos) : null));
} else {
$file_count = substr_count($file_contents, '6:length', $files_pos);
}
if ($file_count > config()->get('flist_max_files')) {
bb_die(sprintf($lang['BT_FLIST_LIMIT'], config()->get('flist_max_files'), $file_count), 410);
}
}
try {
$torrent = \Arokettu\Torrent\TorrentFile::loadFromString($file_contents);
} catch (\Exception $e) {
bb_die(htmlCHR("{$lang['TORFILE_INVALID']}: {$e->getMessage()}"), 410);
}
if (IS_GUEST && $torrent->isPrivate()) {
bb_die($lang['BT_PRIVATE_TORRENT'], 403);
}
// Get torrent files
$files = $torrent->$t_version_field()->$t_files_field();
if ($meta_v2) {
$files = new \RecursiveIteratorIterator($files); // Flatten the list
}
$files_count = 0;
foreach ($files as $file) {
$files_count++;
$row_class = ($files_count % 2) ? 'row1' : 'row2';
$template->assign_block_vars('filelist', [
'ROW_NUMBER' => $files_count,
'ROW_CLASS' => $row_class,
'FILE_PATH' => clean_tor_dirname(implode('/', $file->path)),
'FILE_LENGTH' => humn_size($file->length, 2),
'FILE_HASH' => $file->$t_hash_field ?? '-'
]);
}
$torrent_name = !empty($t_name = $torrent->getName()) ? str_short(htmlCHR($t_name), 200) : $lang['UNKNOWN'];
$torrent_size = humn_size($row['size'], 2);
// Output page
$template->assign_vars([
'PAGE_TITLE' => "$torrent_name (" . $torrent_size . ")",
'FILES_COUNT' => sprintf($lang['BT_FLIST_FILE_PATH'], declension(iterator_count($files), 'files')),
'TORRENT_CREATION_DATE' => (!empty($dt = $torrent->getCreationDate()) && is_numeric($creation_date = $dt->getTimestamp())) ? date('d-M-Y H:i (e)', $creation_date) : $lang['UNKNOWN'],
'TORRENT_CLIENT' => !empty($creator = $torrent->getCreatedBy()) ? htmlCHR($creator) : $lang['UNKNOWN'],
'TORRENT_PRIVATE' => $torrent->isPrivate() ? $lang['YES'] : $lang['NO'],
'BTMR_NOTICE' => sprintf($lang['BT_FLIST_BTMR_NOTICE'], 'https://github.com/kovalensky/tmrr'),
'U_TOPIC' => TOPIC_URL . $topic_id,
]);
print_page('filelist.tpl');

294
group.php
View file

@ -1,38 +1,81 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'group');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
require INC_DIR . '/bbcode.php';
require INC_DIR . '/functions_group.php';
$page_cfg['use_tablesorter'] = true;
$s_member_groups = $s_pending_groups = $s_member_groups_opt = $s_pending_groups_opt = '';
$select_sort_mode = $select_sort_order = '';
// Init userdata
$user->session_start(['req_login' => true]);
function generate_user_info(&$row, $date_format, $group_mod, &$from, &$posts, &$joined, &$pm, &$email, &$www, &$user_time, &$avatar)
{
global $lang, $images, $bb_cfg;
$from = (!empty($row['user_from'])) ? $row['user_from'] : '';
$joined = bb_date($row['user_regdate']);
$user_time = (!empty($row['user_time'])) ? bb_date($row['user_time']) : $lang['NONE'];
$posts = $row['user_posts'] ?: 0;
$pm = $bb_cfg['text_buttons'] ? '<a class="txtb" href="' . (PM_URL . "?mode=post&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '">' . $lang['SEND_PM_TXTB'] . '</a>' : '<a href="' . (PM_URL . "?mode=post&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '"><img src="' . $images['icon_pm'] . '" alt="' . $lang['SEND_PRIVATE_MESSAGE'] . '" title="' . $lang['SEND_PRIVATE_MESSAGE'] . '" border="0" /></a>';
$avatar = get_avatar($row['user_id'], $row['avatar_ext_id'], !bf($row['user_opt'], 'user_opt', 'dis_avatar'), '', 50, 50);
if (bf($row['user_opt'], 'user_opt', 'user_viewemail') || $group_mod) {
$email_uri = ($bb_cfg['board_email_form']) ? ("profile.php?mode=email&amp;" . POST_USERS_URL . "=" . $row['user_id']) : 'mailto:' . $row['user_email'];
$email = '<a class="editable" href="' . $email_uri . '">' . $row['user_email'] . '</a>';
} else {
$email = '';
}
if ($row['user_website']) {
$www = $bb_cfg['text_buttons'] ? '<a class="txtb" href="' . $row['user_website'] . '" target="_userwww">' . $lang['VISIT_WEBSITE_TXTB'] . '</a>' : '<a class="txtb" href="' . $row['user_website'] . '" target="_userwww"><img src="' . $images['icon_www'] . '" alt="' . $lang['VISIT_WEBSITE'] . '" title="' . $lang['VISIT_WEBSITE'] . '" border="0" /></a>';
} else {
$www = '';
}
return;
}
$user->session_start(array('req_login' => true));
set_die_append_msg();
$group_id = isset($_REQUEST[POST_GROUPS_URL]) ? (int)$_REQUEST[POST_GROUPS_URL] : null;
$start = isset($_REQUEST['start']) ? abs((int)$_REQUEST['start']) : 0;
$per_page = config()->get('group_members_per_page');
$per_page = $bb_cfg['group_members_per_page'];
$view_mode = isset($_REQUEST['view']) ? (string)$_REQUEST['view'] : null;
$rel_limit = 50;
$group_info = [];
$group_info = array();
$is_moderator = false;
if ($group_id) {
if (!$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$group_info = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
if (!$group_info['group_id'] || !$group_info['group_moderator'] || !$group_info['moderator_name']) {
@ -43,7 +86,7 @@ if ($group_id) {
if (!$group_id) {
// Show the main screen where the user can select a group.
$groups = [];
$groups = array();
$pending = 10;
$member = 20;
@ -92,7 +135,7 @@ if (!$group_id) {
continue;
}
$data = ['id' => $row['group_id'], 'm' => ($row['members'] - $row['candidates']), 'c' => $row['candidates'], 'rg' => $row['release_group']];
$data = array('id' => $row['group_id'], 'm' => ($row['members'] - $row['candidates']), 'c' => $row['candidates'], 'rg' => $row['release_group']);
$groups[$type][$row['group_name']] = $data;
}
@ -103,7 +146,7 @@ if (!$group_id) {
$options = '';
foreach ($params as $name => $data) {
$text = str_short(rtrim(htmlCHR($name)), HTML_SELECT_MAX_LENGTH);
$text = htmlCHR(str_short(rtrim($name), HTML_SELECT_MAX_LENGTH));
$members = ($data['m']) ? $lang['MEMBERS_IN_GROUP'] . ': ' . $data['m'] : $lang['NO_GROUP_MEMBERS'];
$candidates = ($data['c']) ? $lang['PENDING_MEMBERS'] . ': ' . $data['c'] : $lang['NO_PENDING_GROUP_MEMBERS'];
@ -123,18 +166,18 @@ if (!$group_id) {
$s_hidden_fields = '';
foreach ($groups as $type => $grp) {
$template->assign_block_vars('groups', [
$template->assign_block_vars('groups', array(
'MEMBERSHIP' => $lang["GROUP_MEMBER_{$type}"],
'GROUP_SELECT' => build_group($grp)
]);
'GROUP_SELECT' => build_group($grp),
));
}
$template->assign_vars([
$template->assign_vars(array(
'SELECT_GROUP' => true,
'PAGE_TITLE' => $lang['GROUP_CONTROL_PANEL'],
'S_USERGROUP_ACTION' => 'group.php',
'S_HIDDEN_FIELDS' => $s_hidden_fields
]);
'S_HIDDEN_FIELDS' => $s_hidden_fields,
));
} else {
if (IS_ADMIN) {
redirect('admin/admin_groups.php');
@ -162,35 +205,37 @@ if (!$group_id) {
bb_die($lang['NO_GROUPS_EXIST']);
}
if ($row['user_id']) {
set_die_append_msg(group_id: $group_id);
set_die_append_msg(false, false, $group_id);
bb_die($lang['ALREADY_MEMBER_GROUP']);
}
\TorrentPier\Legacy\Group::add_user_into_group($group_id, $userdata['user_id'], 1, TIMENOW);
add_user_into_group($group_id, $userdata['user_id'], 1, TIMENOW);
if (config()->get('group_send_email')) {
// Sending email
$emailer = new TorrentPier\Emailer();
if ($bb_cfg['group_send_email']) {
/** @var TorrentPier\Legacy\Emailer() $emailer */
$emailer = new TorrentPier\Legacy\Emailer();
$emailer->set_to($moderator['user_email'], $moderator['username']);
$emailer->set_from([$bb_cfg['board_email'] => $bb_cfg['sitename']]);
$emailer->set_to([$moderator['user_email'] => $moderator['username']]);
$emailer->set_subject($lang['EMAILER_SUBJECT']['GROUP_REQUEST']);
$emailer->set_template('group_request', $moderator['user_lang']);
$emailer->assign_vars([
$emailer->assign_vars(array(
'USER' => $userdata['username'],
'SITENAME' => $bb_cfg['sitename'],
'GROUP_MODERATOR' => $moderator['username'],
'U_GROUP' => make_url(GROUP_URL . $group_id)
]);
'U_GROUP' => make_url(GROUP_URL . $group_id),
));
$emailer->send();
}
set_die_append_msg(group_id: $group_id);
set_die_append_msg(false, false, $group_id);
bb_die($lang['GROUP_JOINED']);
} elseif (!empty($_POST['unsub']) || !empty($_POST['unsubpending'])) {
\TorrentPier\Legacy\Group::delete_user_group($group_id, $userdata['user_id']);
delete_user_group($group_id, $userdata['user_id']);
set_die_append_msg(group_id: $group_id);
set_die_append_msg(false, false, $group_id);
bb_die($lang['UNSUB_SUCCESS']);
} else {
// Handle Additions, removals, approvals and denials
@ -198,44 +243,30 @@ if (!$group_id) {
if (!empty($_POST['add']) || !empty($_POST['remove']) || !empty($_POST['approve']) || !empty($_POST['deny'])) {
if (!$is_moderator) {
set_die_append_msg(group_id: $group_id);
bb_die($lang['NOT_GROUP_MODERATOR']);
}
if (!empty($_POST['add'])) {
if (isset($_POST['username']) && !($row = get_userdata($_POST['username'], true))) {
set_die_append_msg(group_id: $group_id);
bb_die($lang['COULD_NOT_ADD_USER']);
}
// Prevent adding moderator
if ($row['user_id'] == $group_moderator) {
set_die_append_msg(group_id: $group_id);
bb_die(sprintf($lang['USER_IS_MOD_GROUP'], profile_url($row)));
}
add_user_into_group($group_id, $row['user_id']);
// Prevent infinity user adding into group
if ($is_member = DB()->fetch_row("SELECT user_id FROM " . BB_USER_GROUP . " WHERE group_id = $group_id AND user_id = " . $row['user_id'] . " LIMIT 1")) {
if ($is_member['user_id']) {
set_die_append_msg(group_id: $group_id);
bb_die(sprintf($lang['USER_IS_MEMBER_GROUP'], profile_url($row)));
}
}
if ($bb_cfg['group_send_email']) {
/** @var TorrentPier\Legacy\Emailer() $emailer */
$emailer = new TorrentPier\Legacy\Emailer();
\TorrentPier\Legacy\Group::add_user_into_group($group_id, $row['user_id']);
if (config()->get('group_send_email')) {
// Sending email
$emailer = new TorrentPier\Emailer();
$emailer->set_to($row['user_email'], $row['username']);
$emailer->set_from([$bb_cfg['board_email'] => $bb_cfg['sitename']]);
$emailer->set_to([$row['user_email'] => $row['username']]);
$emailer->set_subject($lang['EMAILER_SUBJECT']['GROUP_ADDED']);
$emailer->set_template('group_added', $row['user_lang']);
$emailer->assign_vars([
$emailer->assign_vars(array(
'SITENAME' => $bb_cfg['sitename'],
'GROUP_NAME' => $group_info['group_name'],
'U_GROUP' => make_url(GROUP_URL . $group_id)
]);
'U_GROUP' => make_url(GROUP_URL . $group_id),
));
$emailer->send();
}
@ -243,12 +274,11 @@ if (!$group_id) {
if (((!empty($_POST['approve']) || !empty($_POST['deny'])) && !empty($_POST['pending_members'])) || (!empty($_POST['remove']) && !empty($_POST['members']))) {
$members = (!empty($_POST['approve']) || !empty($_POST['deny'])) ? $_POST['pending_members'] : $_POST['members'];
$sql_in = [];
$sql_in = array();
foreach ($members as $members_id) {
$sql_in[] = (int)$members_id;
}
if (!$sql_in = implode(',', $sql_in)) {
set_die_append_msg(group_id: $group_id);
bb_die($lang['NONE_SELECTED']);
}
@ -260,7 +290,7 @@ if (!$group_id) {
AND group_id = $group_id
");
\TorrentPier\Legacy\Group::update_user_level($sql_in);
update_user_level($sql_in);
} elseif (!empty($_POST['deny']) || !empty($_POST['remove'])) {
DB()->query("
DELETE FROM " . BB_USER_GROUP . "
@ -269,31 +299,33 @@ if (!$group_id) {
");
if (!empty($_POST['remove'])) {
\TorrentPier\Legacy\Group::update_user_level($sql_in);
update_user_level($sql_in);
}
}
// Email users when they are approved
if (!empty($_POST['approve']) && config()->get('group_send_email')) {
if (!empty($_POST['approve']) && $bb_cfg['group_send_email']) {
$sql_select = "SELECT username, user_email, user_lang
FROM " . BB_USERS . "
WHERE user_id IN($sql_in)";
FROM " . BB_USERS . "
WHERE user_id IN($sql_in)";
if (!$result = DB()->sql_query($sql_select)) {
bb_die('Could not get user email information');
}
foreach (DB()->fetch_rowset($sql_select) as $row) {
// Sending email
$emailer = new TorrentPier\Emailer();
/** @var TorrentPier\Legacy\Emailer() $emailer */
$emailer = new TorrentPier\Legacy\Emailer();
$emailer->set_to($row['user_email'], $row['username']);
$emailer->set_from([$bb_cfg['board_email'] => $bb_cfg['sitename']]);
$emailer->set_to([$row['user_email'] => $row['username']]);
$emailer->set_subject($lang['EMAILER_SUBJECT']['GROUP_APPROVED']);
$emailer->set_template('group_approved', $row['user_lang']);
$emailer->assign_vars([
$emailer->assign_vars(array(
'SITENAME' => $bb_cfg['sitename'],
'GROUP_NAME' => $group_info['group_name'],
'U_GROUP' => make_url(GROUP_URL . $group_id)
]);
'U_GROUP' => make_url(GROUP_URL . $group_id),
));
$emailer->send();
}
@ -331,10 +363,10 @@ if (!$group_id) {
$group_details = $lang['ARE_GROUP_MODERATOR'];
$s_hidden_fields = '<input type="hidden" name="' . POST_GROUPS_URL . '" value="' . $group_id . '" />';
} elseif ($is_group_member || $is_group_pending_member) {
$template->assign_vars([
$template->assign_vars(array(
'SHOW_UNSUBSCRIBE_CONTROLS' => true,
'CONTROL_NAME' => ($is_group_member) ? 'unsub' : 'unsubpending',
]);
));
$group_details = ($is_group_pending_member) ? $lang['PENDING_THIS_GROUP'] : $lang['MEMBER_THIS_GROUP'];
$s_hidden_fields = '<input type="hidden" name="' . POST_GROUPS_URL . '" value="' . $group_id . '" />';
} elseif (IS_GUEST) {
@ -359,7 +391,7 @@ if (!$group_id) {
$username = $group_moderator['username'];
$user_id = $group_moderator['user_id'];
$moderator_info = generate_user_info($group_moderator, $is_moderator);
generate_user_info($group_moderator, $bb_cfg['default_dateformat'], $is_moderator, $from, $posts, $joined, $pm, $email, $www, $user_time, $avatar);
$group_type = '';
if ($group_info['group_type'] == GROUP_OPEN) {
@ -371,54 +403,52 @@ if (!$group_id) {
}
$i = 0;
$template->assign_vars([
$template->assign_vars(array(
'ROW_NUMBER' => $i + ($start + 1),
'GROUP_INFO' => true,
'PAGE_TITLE' => $lang['GROUP_CONTROL_PANEL'],
'GROUP_NAME' => htmlCHR($group_info['group_name']),
'GROUP_DESCRIPTION' => bbcode2html($group_info['group_description']),
'GROUP_SIGNATURE' => bbcode2html($group_info['group_signature']),
'GROUP_AVATAR' => get_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']),
'GROUP_AVATAR' => get_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id'], true),
'GROUP_DETAILS' => $group_details,
'GROUP_TIME' => !empty($group_info['group_time']) ? sprintf('%s <span class="signature">(%s)</span>', bb_date($group_info['group_time']), delta_time($group_info['group_time'])) : $lang['NONE'],
'GROUP_TIME' => (!empty($group_info['group_time'])) ? sprintf('%s <span class="posted_since">(%s)</span>', bb_date($group_info['group_time']), delta_time($group_info['group_time'])) : $lang['NONE'],
'MOD_USER' => profile_url($group_moderator),
'MOD_AVATAR' => $moderator_info['avatar'],
'MOD_FROM' => $moderator_info['from'],
'MOD_JOINED' => $moderator_info['joined'],
'MOD_JOINED_RAW' => $moderator_info['joined_raw'],
'MOD_POSTS' => $moderator_info['posts'],
'MOD_PM' => $moderator_info['pm'],
'MOD_EMAIL' => $moderator_info['email'],
'MOD_WWW' => $moderator_info['www'],
'MOD_TIME' => !empty($group_info['mod_time']) ? sprintf('%s <span class="signature">(%s)</span>', bb_date($group_info['mod_time']), delta_time($group_info['mod_time'])) : $lang['NONE'],
'MOD_TIME_RAW' => !empty($group_info['mod_time']) ? $group_info['mod_time'] : '',
'U_SEARCH_USER' => 'search.php?mode=searchuser',
'MOD_AVATAR' => $avatar,
'MOD_FROM' => $from,
'MOD_JOINED' => $joined,
'MOD_POSTS' => $posts,
'MOD_PM' => $pm,
'MOD_EMAIL' => $email,
'MOD_WWW' => $www,
'MOD_TIME' => (!empty($group_info['mod_time'])) ? bb_date($group_info['mod_time']) : $lang['NONE'],
'U_SEARCH_USER' => "search.php?mode=searchuser",
'U_SEARCH_RELEASES' => "tracker.php?srg=$group_id",
'U_GROUP_RELEASES' => GROUP_URL . $group_id . "&view=releases",
'U_GROUP_MEMBERS' => GROUP_URL . $group_id . "&view=members",
'U_GROUP_CONFIG' => "group_edit.php?" . POST_GROUPS_URL . "=$group_id",
'RELEASE_GROUP' => (bool)$group_info['release_group'],
'U_GROUP_RELEASES' => "group.php?view=releases&amp;" . POST_GROUPS_URL . "=$group_id",
'U_GROUP_MEMBERS' => "group.php?view=members&amp;" . POST_GROUPS_URL . "=$group_id",
'U_GROUP_CONFIG' => "group_edit.php?g=$group_id",
'RELEASE_GROUP' => ($group_info['release_group']) ? true : false,
'GROUP_TYPE' => $group_type,
'S_GROUP_OPEN_TYPE' => GROUP_OPEN,
'S_GROUP_CLOSED_TYPE' => GROUP_CLOSED,
'S_GROUP_HIDDEN_TYPE' => GROUP_HIDDEN,
'S_GROUP_OPEN_CHECKED' => ($group_info['group_type'] == GROUP_OPEN) ? ' checked' : '',
'S_GROUP_CLOSED_CHECKED' => ($group_info['group_type'] == GROUP_CLOSED) ? ' checked' : '',
'S_GROUP_HIDDEN_CHECKED' => ($group_info['group_type'] == GROUP_HIDDEN) ? ' checked' : '',
'S_GROUP_OPEN_CHECKED' => ($group_info['group_type'] == GROUP_OPEN) ? ' checked="checked"' : '',
'S_GROUP_CLOSED_CHECKED' => ($group_info['group_type'] == GROUP_CLOSED) ? ' checked="checked"' : '',
'S_GROUP_HIDDEN_CHECKED' => ($group_info['group_type'] == GROUP_HIDDEN) ? ' checked="checked"' : '',
'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_MODE_SELECT' => $select_sort_mode,
'S_ORDER_SELECT' => $select_sort_order,
'S_GROUP_ACTION' => GROUP_URL . $group_id,
]);
'S_GROUP_ACTION' => "group.php?" . POST_GROUPS_URL . "=$group_id",
));
switch ($view_mode) {
case 'releases':
// TODO Correct SQL to posts with attach and limit them, optimization
if (!$group_info['release_group']) {
set_die_append_msg(group_id: $group_id);
set_die_append_msg(false, false, $group_id);
bb_die($lang['NOT_A_RELEASE_GROUP']);
}
@ -449,25 +479,27 @@ if (!$group_id) {
";
if (!$releases = DB()->fetch_rowset($sql)) {
set_die_append_msg(group_id: $group_id);
set_die_append_msg(false, false, $group_id);
bb_die($lang['NO_SEARCH_MATCH']);
}
foreach ($releases as $i => $release) {
$row_class = !($i % 2) ? 'row1' : 'row2';
$template->assign_block_vars('releases', [
$template->assign_block_vars('releases', array(
'ROW_NUMBER' => $i + ($start + 1),
'ROW_CLASS' => $row_class,
'RELEASER' => profile_url(['user_id' => $release['poster_id'], 'username' => $release['username'], 'user_rank' => $release['user_rank']]),
'AVATAR_IMG' => get_avatar($release['poster_id'], $release['avatar_ext_id'], !bf($release['user_opt'], 'user_opt', 'dis_avatar'), 50, 50),
'RELEASER' => profile_url(array('user_id' => $release['poster_id'], 'username' => $release['username'], 'user_rank' => $release['user_rank'])),
'AVATAR_IMG' => get_avatar($release['poster_id'], $release['avatar_ext_id'], !bf($release['user_opt'], 'user_opt', 'dis_avatar'), '', 50, 50),
'RELEASE_NAME' => sprintf('<a href="%s">%s</a>', TOPIC_URL . $release['topic_id'], htmlCHR($release['topic_title'])),
'RELEASE_TIME' => bb_date($release['topic_time']),
'RELEASE_FORUM' => sprintf('<a href="%s">%s</a>', FORUM_URL . $release['forum_id'], htmlCHR($release['forum_name'])),
]);
));
}
$template->assign_vars(['RELEASES' => true]);
$template->assign_vars(array(
'RELEASES' => true,
));
break;
@ -508,42 +540,40 @@ if (!$group_id) {
foreach ($group_members as $i => $member) {
$user_id = $member['user_id'];
$member_info = generate_user_info($member, $is_moderator);
generate_user_info($member, $bb_cfg['default_dateformat'], $is_moderator, $from, $posts, $joined, $pm, $email, $www, $user_time, $avatar);
if ($group_info['group_type'] != GROUP_HIDDEN || $is_group_member || $is_moderator) {
$row_class = !($i % 2) ? 'row1' : 'row2';
$template->assign_block_vars('member', [
$template->assign_block_vars('member', array(
'ROW_NUMBER' => $i + ($start + 1),
'ROW_CLASS' => $row_class,
'USER' => profile_url($member),
'AVATAR_IMG' => $member_info['avatar'],
'FROM' => $member_info['from'],
'JOINED' => $member_info['joined'],
'JOINED_RAW' => $member_info['joined_raw'],
'POSTS' => $member_info['posts'],
'AVATAR_IMG' => $avatar,
'FROM' => $from,
'JOINED' => $joined,
'POSTS' => $posts,
'USER_ID' => $user_id,
'PM' => $member_info['pm'],
'EMAIL' => $member_info['email'],
'WWW' => $member_info['www'],
'TIME' => $member_info['user_time'],
'TIME_RAW' => $member_info['user_time_raw']
]);
'PM' => $pm,
'EMAIL' => $email,
'WWW' => $www,
'TIME' => $user_time,
));
if ($is_moderator) {
$template->assign_block_vars('member.switch_mod_option', []);
$template->assign_block_vars('member.switch_mod_option', array());
}
}
}
// No group members
if (!$members_count) {
$template->assign_block_vars('switch_no_members', []);
$template->assign_block_vars('switch_no_members', array());
}
// No group members
if ($group_info['group_type'] == GROUP_HIDDEN && !$is_group_member && !$is_moderator) {
$template->assign_block_vars('switch_hidden_group', []);
$template->assign_block_vars('switch_hidden_group', array());
}
// Pending
@ -564,36 +594,36 @@ if (!$group_id) {
foreach ($modgroup_pending_list as $i => $member) {
$user_id = $member['user_id'];
$pending_info = generate_user_info($member, $is_moderator);
generate_user_info($member, $bb_cfg['default_dateformat'], $is_moderator, $from, $posts, $joined, $pm, $email, $www, $user_time, $avatar);
$row_class = !($i % 2) ? 'row1' : 'row2';
$user_select = '<input type="checkbox" name="member[]" value="' . $user_id . '">';
$template->assign_block_vars('pending', [
$template->assign_block_vars('pending', array(
'ROW_CLASS' => $row_class,
'AVATAR_IMG' => $pending_info['avatar'],
'AVATAR_IMG' => $avatar,
'USER' => profile_url($member),
'FROM' => $pending_info['from'],
'JOINED' => $pending_info['joined'],
'JOINED_RAW' => $pending_info['joined_raw'],
'POSTS' => $pending_info['posts'],
'FROM' => $from,
'JOINED' => $joined,
'POSTS' => $posts,
'USER_ID' => $user_id,
'PM' => $pending_info['pm'],
'EMAIL' => $pending_info['email'],
'WWW' => $pending_info['www']
]);
'PM' => $pm,
'EMAIL' => $email,
));
}
$template->assign_vars(['PENDING_USERS' => true]);
$template->assign_vars(array(
'PENDING_USERS' => true,
));
}
$template->assign_vars(['MEMBERS' => true]);
$template->assign_vars(array('MEMBERS' => true));
}
if ($is_moderator) {
$template->assign_block_vars('switch_mod_option', []);
$template->assign_block_vars('switch_add_member', []);
$template->assign_block_vars('switch_mod_option', array());
$template->assign_block_vars('switch_add_member', array());
}
}

View file

@ -1,29 +1,46 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'group_edit');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
require INC_DIR . '/functions_group.php';
$page_cfg['include_bbcode_js'] = true;
// Start session management
$user->session_start(['req_login' => true]);
$user->session_start(array('req_login' => true));
$group_id = isset($_REQUEST[POST_GROUPS_URL]) ? (int)$_REQUEST[POST_GROUPS_URL] : null;
$group_info = [];
$group_info = array();
$is_moderator = false;
$submit = !empty($_POST['submit']);
if ($group_id) {
if (!$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$group_info = get_group_data($group_id)) {
bb_die($lang['GROUP_NOT_EXIST']);
}
if (!$group_info['group_id'] || !$group_info['group_moderator'] || !$group_info['moderator_name']) {
@ -35,15 +52,16 @@ if ($group_id) {
if ($is_moderator) {
// Avatar
if ($submit) {
if (!empty($_FILES['avatar']['name']) && config()->get('group_avatars.up_allowed')) {
if (!empty($_FILES['avatar']['name']) && $bb_cfg['group_avatars']['up_allowed']) {
$upload = new TorrentPier\Legacy\Common\Upload();
if ($upload->init(config()->get('group_avatars'), $_FILES['avatar']) and $upload->store('avatar', ['user_id' => GROUP_AVATAR_MASK . $group_id, 'avatar_ext_id' => $group_info['avatar_ext_id']])) {
if ($upload->init($bb_cfg['group_avatars'], $_FILES['avatar']) and $upload->store('avatar', array("user_id" => GROUP_AVATAR_MASK . $group_id, "avatar_ext_id" => $group_info['avatar_ext_id']))) {
$avatar_ext_id = (int)$upload->file_ext_id;
DB()->query("UPDATE " . BB_GROUPS . " SET avatar_ext_id = $avatar_ext_id WHERE group_id = $group_id LIMIT 1");
} else {
bb_die(implode($upload->errors));
}
DB()->query("UPDATE " . BB_GROUPS . " SET avatar_ext_id = $avatar_ext_id WHERE group_id = $group_id");
}
}
@ -58,30 +76,30 @@ if ($is_moderator) {
$s_hidden_fields = '<input type="hidden" name="' . POST_GROUPS_URL . '" value="' . $group_id . '" />';
$template->assign_vars([
$template->assign_vars(array(
'PAGE_TITLE' => $lang['GROUP_CONTROL_PANEL'],
'GROUP_NAME' => htmlCHR($group_info['group_name']),
'GROUP_ID' => $group_id,
'GROUP_DESCRIPTION' => htmlCHR($group_info['group_description']),
'GROUP_SIGNATURE' => htmlCHR($group_info['group_signature']),
'U_GROUP_URL' => GROUP_URL . $group_id,
'RELEASE_GROUP' => (bool)$group_info['release_group'],
'RELEASE_GROUP' => $group_info['release_group'] ? true : false,
'GROUP_TYPE' => $group_type,
'S_GROUP_OPEN_TYPE' => GROUP_OPEN,
'S_GROUP_CLOSED_TYPE' => GROUP_CLOSED,
'S_GROUP_HIDDEN_TYPE' => GROUP_HIDDEN,
'S_GROUP_OPEN_CHECKED' => ($group_info['group_type'] == GROUP_OPEN) ? ' checked' : '',
'S_GROUP_CLOSED_CHECKED' => ($group_info['group_type'] == GROUP_CLOSED) ? ' checked' : '',
'S_GROUP_HIDDEN_CHECKED' => ($group_info['group_type'] == GROUP_HIDDEN) ? ' checked' : '',
'S_GROUP_OPEN_CHECKED' => ($group_info['group_type'] == GROUP_OPEN) ? ' checked="checked"' : '',
'S_GROUP_CLOSED_CHECKED' => ($group_info['group_type'] == GROUP_CLOSED) ? ' checked="checked"' : '',
'S_GROUP_HIDDEN_CHECKED' => ($group_info['group_type'] == GROUP_HIDDEN) ? ' checked="checked"' : '',
'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_GROUP_CONFIG_ACTION' => "group_edit.php?" . POST_GROUPS_URL . "=$group_id",
'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], config()->get('group_avatars.max_width'), config()->get('group_avatars.max_height'), humn_size(config()->get('group_avatars.max_size'))),
'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], $bb_cfg['group_avatars']['max_width'], $bb_cfg['group_avatars']['max_height'], (round($bb_cfg['group_avatars']['max_size'] / 1024))),
'AVATAR_IMG' => get_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']),
]);
));
$template->set_filenames(['body' => 'group_edit.tpl']);
$template->assign_vars(['PAGE_TITLE' => $lang['GROUP_CONFIGURATION']]);
$template->set_filenames(array('body' => 'group_edit.tpl'));
$template->assign_vars(array('PAGE_TITLE' => $lang['GROUP_CONFIGURATION']));
require(PAGE_HEADER);

252
index.php
View file

@ -1,63 +1,66 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'index');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
$page_cfg['load_tpl_vars'] = [
'post_icons'
];
$page_cfg['load_tpl_vars'] = array(
'post_icons',
);
// Show last topic
$show_last_topic = true;
$last_topic_max_len = 28;
// Show online stats
$show_online_users = true;
// Show subforums
$show_subforums = true;
$datastore->enqueue([
$datastore->enqueue(array(
'stats',
'moderators',
'cat_forums'
]);
if (config()->get('show_latest_news')) {
$datastore->enqueue([
'latest_news'
]);
'cat_forums',
));
if ($bb_cfg['show_latest_news']) {
$datastore->enqueue('latest_news');
}
if (config()->get('show_network_news')) {
$datastore->enqueue([
'network_news'
]);
if ($bb_cfg['show_network_news']) {
$datastore->enqueue('network_news');
}
// Init userdata
$user->session_start();
// Set meta description
$page_cfg['meta_description'] = config()->get('site_desc');
// Init main vars
$viewcat = isset($_GET[POST_CAT_URL]) ? (int)$_GET[POST_CAT_URL] : 0;
$lastvisit = IS_GUEST ? TIMENOW : $userdata['user_lastvisit'];
$viewcat = isset($_GET['c']) ? (int)$_GET['c'] : 0;
$lastvisit = (IS_GUEST) ? TIMENOW : $userdata['user_lastvisit'];
// Caching output
$req_page = 'index_page';
$req_page .= $viewcat ? "_c{$viewcat}" : '';
$req_page .= ($viewcat) ? "_c{$viewcat}" : '';
define('REQUESTED_PAGE', $req_page);
caching_output(IS_GUEST, 'send', REQUESTED_PAGE . '_guest_' . config()->get('default_lang'));
caching_output(IS_GUEST, 'send', REQUESTED_PAGE . '_guest_' . $bb_cfg['default_lang']);
$hide_cat_opt = isset($user->opt_js['h_cat']) ? (string)$user->opt_js['h_cat'] : 0;
$hide_cat_user = array_flip(explode('-', $hide_cat_opt));
@ -68,15 +71,13 @@ $tracking_topics = get_tracks('topic');
$tracking_forums = get_tracks('forum');
// Statistics
$stats = $datastore->get('stats');
if ($stats === false) {
if (!$stats = $datastore->get('stats')) {
$datastore->update('stats');
$stats = $datastore->get('stats');
}
// Forums data
$forums = $datastore->get('cat_forums');
if ($forums === false) {
if (!$forums = $datastore->get('cat_forums')) {
$datastore->update('cat_forums');
$forums = $datastore->get('cat_forums');
}
@ -85,63 +86,63 @@ $forum_name_html = $forums['forum_name_html'];
$anon = GUEST_UID;
$excluded_forums_csv = $user->get_excluded_forums(AUTH_VIEW);
$excluded_forums_array = $excluded_forums_csv ? explode(',', $excluded_forums_csv) : [];
$only_new = $user->opt_js['only_new'];
// Validate requested category id
if ($viewcat && !($viewcat =& $forums['c'][$viewcat]['cat_id'])) {
redirect('index.php');
redirect("index.php");
}
// Forums
$forums_join_sql = 'f.cat_id = c.cat_id';
$forums_join_sql .= $viewcat ? "
$forums_join_sql .= ($viewcat) ? "
AND f.cat_id = $viewcat
" : '';
$forums_join_sql .= $excluded_forums_csv ? "
$forums_join_sql .= ($excluded_forums_csv) ? "
AND f.forum_id NOT IN($excluded_forums_csv)
AND f.forum_parent NOT IN($excluded_forums_csv)
" : '';
// Posts
$posts_join_sql = 'p.post_id = f.forum_last_post_id';
$posts_join_sql = "p.post_id = f.forum_last_post_id";
$posts_join_sql .= ($only_new == ONLY_NEW_POSTS) ? "
AND p.post_time > $lastvisit
" : '';
$join_p_type = ($only_new == ONLY_NEW_POSTS) ? 'INNER JOIN' : 'LEFT JOIN';
// Topics
$topics_join_sql = 't.topic_last_post_id = p.post_id';
$topics_join_sql = "t.topic_last_post_id = p.post_id";
$topics_join_sql .= ($only_new == ONLY_NEW_TOPICS) ? "
AND t.topic_time > $lastvisit
" : '';
$join_t_type = ($only_new == ONLY_NEW_TOPICS) ? 'INNER JOIN' : 'LEFT JOIN';
$sql = "
SELECT f.cat_id, f.forum_id, f.forum_status, f.forum_parent, f.show_on_index,
SELECT SQL_CACHE
f.cat_id, f.forum_id, f.forum_status, f.forum_parent, f.show_on_index,
p.post_id AS last_post_id, p.post_time AS last_post_time,
t.topic_id AS last_topic_id, t.topic_title AS last_topic_title,
u.user_id AS last_post_user_id, u.user_rank AS last_post_user_rank,
IF(p.poster_id = $anon, p.post_username, u.username) AS last_post_username
FROM " . BB_CATEGORIES . ' c
INNER JOIN ' . BB_FORUMS . " f ON($forums_join_sql)
FROM " . BB_CATEGORIES . " c
INNER JOIN " . BB_FORUMS . " f ON($forums_join_sql)
$join_p_type " . BB_POSTS . " p ON($posts_join_sql)
$join_t_type " . BB_TOPICS . " t ON($topics_join_sql)
LEFT JOIN " . BB_USERS . ' u ON(u.user_id = p.poster_id)
LEFT JOIN " . BB_USERS . " u ON(u.user_id = p.poster_id)
ORDER BY c.cat_order, f.forum_order
';
";
$replace_in_parent = [
$replace_in_parent = array(
'last_post_id',
'last_post_time',
'last_post_user_id',
'last_post_username',
'last_post_user_rank',
'last_topic_title',
'last_topic_id'
];
'last_topic_id',
);
$cache_name = 'index_sql_' . hash('xxh128', $sql);
$cache_name = 'index_sql_' . md5($sql);
if (!$cat_forums = CACHE('bb_cache')->get($cache_name)) {
$cat_forums = [];
foreach (DB()->fetch_rowset($sql) as $row) {
@ -178,9 +179,8 @@ if (!$cat_forums = CACHE('bb_cache')->get($cache_name)) {
}
// Obtain list of moderators
$moderators = [];
$mod = $datastore->get('moderators');
if ($mod === false) {
$moderators = array();
if (!$mod = $datastore->get('moderators')) {
$datastore->update('moderators');
$mod = $datastore->get('moderators');
}
@ -204,23 +204,25 @@ $datastore->rm('moderators');
// Build index page
$forums_count = 0;
foreach ($cat_forums as $cid => $c) {
$template->assign_block_vars('h_c', [
$template->assign_block_vars('h_c', array(
'H_C_ID' => $cid,
'H_C_TITLE' => $cat_title_html[$cid],
'H_C_CHEKED' => in_array($cid, preg_split('/[-]+/', $hide_cat_opt)) ? 'checked' : '',
]);
'H_C_CHEKED' => in_array($cid, preg_split("/[-]+/", $hide_cat_opt)) ? 'checked' : '',
));
$template->assign_vars(['H_C_AL_MESS' => $hide_cat_opt && !$showhide]);
$template->assign_vars(array(
'H_C_AL_MESS' => ($hide_cat_opt && !$showhide),
));
if (!$showhide && isset($hide_cat_user[$cid]) && !$viewcat) {
continue;
}
$template->assign_block_vars('c', [
$template->assign_block_vars('c', array(
'CAT_ID' => $cid,
'CAT_TITLE' => $cat_title_html[$cid],
'U_VIEWCAT' => CAT_URL . $cid,
]);
));
foreach ($c['f'] as $fid => $f) {
if (!$fname_html =& $forum_name_html[$fid]) {
@ -230,55 +232,55 @@ foreach ($cat_forums as $cid => $c) {
$forums_count++;
$new = is_unread($f['last_post_time'], $f['last_topic_id'], $f['forum_id']) ? '_new' : '';
$folder_image = $is_sf ? $images["icon_minipost{$new}"] : $images["forum{$new}"];
$folder_image = ($is_sf) ? $images["icon_minipost{$new}"] : $images["forum{$new}"];
if ($f['forum_status'] == FORUM_LOCKED) {
$folder_image = $is_sf ? $images['icon_minipost'] : $images['forum_locked'];
$folder_image = ($is_sf) ? $images['icon_minipost'] : $images['forum_locked'];
}
if ($is_sf) {
$template->assign_block_vars('c.f.sf', [
$template->assign_block_vars('c.f.sf', array(
'SF_ID' => $fid,
'SF_NAME' => $fname_html,
'SF_NEW' => $new ? ' new' : ''
]);
'SF_NEW' => $new ? ' new' : '',
));
continue;
}
$template->assign_block_vars('c.f', [
$template->assign_block_vars('c.f', array(
'FORUM_FOLDER_IMG' => $folder_image,
'FORUM_ID' => $fid,
'FORUM_NAME' => $fname_html,
'FORUM_DESC' => $f['forum_desc'],
'POSTS' => commify($f['forum_posts']),
'TOPICS' => commify($f['forum_topics']),
'LAST_SF_ID' => $f['last_sf_id'] ?? null,
'LAST_SF_ID' => isset($f['last_sf_id']) ? $f['last_sf_id'] : null,
'MODERATORS' => isset($moderators[$fid]) ? implode(', ', $moderators[$fid]) : '',
'FORUM_FOLDER_ALT' => $new ? $lang['NEW'] : $lang['OLD']
]);
'FORUM_FOLDER_ALT' => ($new) ? $lang['NEW'] : $lang['OLD'],
));
if ($f['last_post_id']) {
$template->assign_block_vars('c.f.last', [
$template->assign_block_vars('c.f.last', array(
'LAST_TOPIC_ID' => $f['last_topic_id'],
'LAST_TOPIC_TIP' => $f['last_topic_title'],
'LAST_TOPIC_TITLE' => str_short($f['last_topic_title'], $last_topic_max_len),
'LAST_POST_TIME' => bb_date($f['last_post_time'], config()->get('last_post_date_format')),
'LAST_POST_USER' => profile_url(['username' => str_short($f['last_post_username'], 15), 'user_id' => $f['last_post_user_id'], 'user_rank' => $f['last_post_user_rank']]),
]);
'LAST_TOPIC_TITLE' => wbr(str_short($f['last_topic_title'], $last_topic_max_len)),
'LAST_POST_TIME' => bb_date($f['last_post_time'], $bb_cfg['last_post_date_format']),
'LAST_POST_USER' => profile_url(array('username' => str_short($f['last_post_username'], 15), 'user_id' => $f['last_post_user_id'], 'user_rank' => $f['last_post_user_rank'])),
));
}
}
}
$template->assign_vars([
$template->assign_vars(array(
'SHOW_FORUMS' => $forums_count,
'SHOW_MAP' => isset($_GET['map']) && !IS_GUEST,
'PAGE_TITLE' => $viewcat ? $cat_title_html[$viewcat] : $lang['HOME'],
'NO_FORUMS_MSG' => $only_new ? $lang['NO_NEW_POSTS'] : $lang['NO_FORUMS'],
'SHOW_MAP' => (isset($_GET['map']) && !IS_GUEST),
'PAGE_TITLE' => ($viewcat) ? $cat_title_html[$viewcat] : $lang['HOME'],
'NO_FORUMS_MSG' => ($only_new) ? $lang['NO_NEW_POSTS'] : $lang['NO_FORUMS'],
'TOTAL_TOPICS' => sprintf($lang['POSTED_TOPICS_TOTAL'], $stats['topiccount']),
'TOTAL_POSTS' => sprintf($lang['POSTED_ARTICLES_TOTAL'], $stats['postcount']),
'TOTAL_USERS' => sprintf($lang['REGISTERED_USERS_TOTAL'], $stats['usercount']),
'TOTAL_GENDER' => config()->get('gender') ? sprintf(
'TOTAL_GENDER' => $bb_cfg['gender'] ? sprintf(
$lang['USERS_TOTAL_GENDER'],
$stats['male'],
$stats['female'],
@ -287,113 +289,105 @@ $template->assign_vars([
'NEWEST_USER' => sprintf($lang['NEWEST_USER'], profile_url($stats['newestuser'])),
// Tracker stats
'TORRENTS_STAT' => config()->get('tor_stats') ? sprintf(
'TORRENTS_STAT' => $bb_cfg['tor_stats'] ? sprintf(
$lang['TORRENTS_STAT'],
$stats['torrentcount'],
humn_size($stats['size'])
) : '',
'PEERS_STAT' => config()->get('tor_stats') ? sprintf(
'PEERS_STAT' => $bb_cfg['tor_stats'] ? sprintf(
$lang['PEERS_STAT'],
$stats['peers'],
$stats['seeders'],
$stats['leechers']
) : '',
'SPEED_STAT' => config()->get('tor_stats') ? sprintf(
'SPEED_STAT' => $bb_cfg['tor_stats'] ? sprintf(
$lang['SPEED_STAT'],
humn_size($stats['speed']) . '/s'
) : '',
'SHOW_MOD_INDEX' => config()->get('show_mod_index'),
'SHOW_MOD_INDEX' => $bb_cfg['show_mod_index'],
'FORUM_IMG' => $images['forum'],
'FORUM_NEW_IMG' => $images['forum_new'],
'FORUM_LOCKED_IMG' => $images['forum_locked'],
'SHOW_ONLY_NEW_MENU' => true,
'ONLY_NEW_POSTS_ON' => $only_new == ONLY_NEW_POSTS,
'ONLY_NEW_TOPICS_ON' => $only_new == ONLY_NEW_TOPICS,
'ONLY_NEW_POSTS_ON' => ($only_new == ONLY_NEW_POSTS),
'ONLY_NEW_TOPICS_ON' => ($only_new == ONLY_NEW_TOPICS),
'U_SEARCH_NEW' => 'search.php?new=1',
'U_SEARCH_NEW' => "search.php?new=1",
'U_SEARCH_SELF_BY_MY' => "search.php?uid={$userdata['user_id']}&amp;o=1",
'U_SEARCH_LATEST' => 'search.php?search_id=latest',
'U_SEARCH_UNANSWERED' => 'search.php?search_id=unanswered',
'U_ATOM_FEED' => is_file(config()->get('atom.path') . '/f/0.atom') ? make_url(config()->get('atom.url') . '/f/0.atom') : false,
'U_SEARCH_LATEST' => "search.php?search_id=latest",
'U_SEARCH_UNANSWERED' => "search.php?search_id=unanswered",
'SHOW_LAST_TOPIC' => $show_last_topic,
'BOARD_START' => config()->get('show_board_start_index') ? ($lang['BOARD_STARTED'] . ':&nbsp;' . '<b>' . bb_date(config()->get('board_startdate')) . '</b>') : false,
]);
));
// Set tpl vars for bt_userdata
if (config()->get('bt_show_dl_stat_on_index') && !IS_GUEST) {
if ($bb_cfg['bt_show_dl_stat_on_index'] && !IS_GUEST) {
show_bt_userdata($userdata['user_id']);
}
// Latest news
if (config()->get('show_latest_news')) {
$latest_news = $datastore->get('latest_news');
if ($latest_news === false) {
if ($bb_cfg['show_latest_news']) {
if (!$latest_news = $datastore->get('latest_news')) {
$datastore->update('latest_news');
$latest_news = $datastore->get('latest_news');
}
$template->assign_vars(['SHOW_LATEST_NEWS' => true]);
$template->assign_vars(array(
'SHOW_LATEST_NEWS' => true,
));
foreach ($latest_news as $news) {
if (in_array($news['forum_id'], $excluded_forums_array)) {
continue;
}
$template->assign_block_vars('news', [
$template->assign_block_vars('news', array(
'NEWS_TOPIC_ID' => $news['topic_id'],
'NEWS_TITLE' => str_short(censor()->censorString($news['topic_title']), config()->get('max_news_title')),
'NEWS_TITLE' => str_short($news['topic_title'], $bb_cfg['max_news_title']),
'NEWS_TIME' => bb_date($news['topic_time'], 'd-M', false),
'NEWS_IS_NEW' => is_unread($news['topic_time'], $news['topic_id'], $news['forum_id']),
]);
));
}
}
// Network news
if (config()->get('show_network_news')) {
$network_news = $datastore->get('network_news');
if ($network_news === false) {
if ($bb_cfg['show_network_news']) {
if (!$network_news = $datastore->get('network_news')) {
$datastore->update('network_news');
$network_news = $datastore->get('network_news');
}
$template->assign_vars(['SHOW_NETWORK_NEWS' => true]);
$template->assign_vars(array(
'SHOW_NETWORK_NEWS' => true,
));
foreach ($network_news as $net) {
if (in_array($net['forum_id'], $excluded_forums_array)) {
continue;
}
$template->assign_block_vars('net', [
$template->assign_block_vars('net', array(
'NEWS_TOPIC_ID' => $net['topic_id'],
'NEWS_TITLE' => str_short(censor()->censorString($net['topic_title']), config()->get('max_net_title')),
'NEWS_TITLE' => str_short($net['topic_title'], $bb_cfg['max_net_title']),
'NEWS_TIME' => bb_date($net['topic_time'], 'd-M', false),
'NEWS_IS_NEW' => is_unread($net['topic_time'], $net['topic_id'], $net['forum_id']),
]);
));
}
}
if (config()->get('birthday_check_day') && config()->get('birthday_enabled')) {
$week_list = $today_list = [];
if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) {
$week_list = $today_list = array();
$week_all = $today_all = false;
if (!empty($stats['birthday_week_list'])) {
if (isset($stats['birthday_week_list'])) {
shuffle($stats['birthday_week_list']);
foreach ($stats['birthday_week_list'] as $i => $week) {
if ($i >= 5) {
$week_all = true;
continue;
}
$week_list[] = profile_url($week) . ' <span class="small">(' . birthday_age(date('Y-m-d', strtotime('-1 year', strtotime($week['user_birthday'])))) . ')</span>';
$week_list[] = profile_url($week) . ' <span class="small">(' . birthday_age($week['user_birthday'] - 1) . ')</span>';
}
$week_all = $week_all ? '&nbsp;<a class="txtb" href="#" onclick="ajax.exec({action: \'index_data\', mode: \'birthday_week\'}); return false;" title="' . $lang['ALL'] . '">...</a>' : '';
$week_list = sprintf($lang['BIRTHDAY_WEEK'], config()->get('birthday_check_day'), implode(', ', $week_list)) . $week_all;
$week_all = ($week_all) ? '&nbsp;<a class="txtb" href="#" onclick="ajax.exec({action: \'index_data\', mode: \'birthday_week\'}); return false;" title="' . $lang['ALL'] . '">...</a>' : '';
$week_list = sprintf($lang['BIRTHDAY_WEEK'], $bb_cfg['birthday_check_day'], implode(', ', $week_list)) . $week_all;
} else {
$week_list = sprintf($lang['NOBIRTHDAY_WEEK'], config()->get('birthday_check_day'));
$week_list = sprintf($lang['NOBIRTHDAY_WEEK'], $bb_cfg['birthday_check_day']);
}
if (!empty($stats['birthday_today_list'])) {
if (isset($stats['birthday_today_list'])) {
shuffle($stats['birthday_today_list']);
foreach ($stats['birthday_today_list'] as $i => $today) {
if ($i >= 5) {
@ -402,22 +396,22 @@ if (config()->get('birthday_check_day') && config()->get('birthday_enabled')) {
}
$today_list[] = profile_url($today) . ' <span class="small">(' . birthday_age($today['user_birthday']) . ')</span>';
}
$today_all = $today_all ? '&nbsp;<a class="txtb" href="#" onclick="ajax.exec({action: \'index_data\', mode: \'birthday_today\'}); return false;" title="' . $lang['ALL'] . '">...</a>' : '';
$today_all = ($today_all) ? '&nbsp;<a class="txtb" href="#" onclick="ajax.exec({action: \'index_data\', mode: \'birthday_today\'}); return false;" title="' . $lang['ALL'] . '">...</a>' : '';
$today_list = $lang['BIRTHDAY_TODAY'] . implode(', ', $today_list) . $today_all;
} else {
$today_list = $lang['NOBIRTHDAY_TODAY'];
}
$template->assign_vars([
$template->assign_vars(array(
'WHOSBIRTHDAY_WEEK' => $week_list,
'WHOSBIRTHDAY_TODAY' => $today_list
]);
'WHOSBIRTHDAY_TODAY' => $today_list,
));
}
// Allow cron
if (IS_AM) {
if (is_file(CRON_RUNNING)) {
if (is_file(CRON_ALLOWED)) {
if (file_exists(CRON_RUNNING)) {
if (file_exists(CRON_ALLOWED)) {
unlink(CRON_ALLOWED);
}
rename(CRON_RUNNING, CRON_ALLOWED);
@ -428,7 +422,7 @@ if (IS_AM) {
define('SHOW_ONLINE', $show_online_users);
if (isset($_GET['map'])) {
$template->assign_vars(['PAGE_TITLE' => $lang['FORUM_MAP']]);
$template->assign_vars(array('PAGE_TITLE' => $lang['FORUM_MAP']));
}
print_page('index.tpl');

View file

@ -1,24 +1,42 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
define('BB_SCRIPT', 'info');
define('BB_ROOT', './');
require __DIR__ . '/common.php';
// Start session management
$user->session_start();
$info = [];
$htmlDir = LANG_DIR . 'html/';
$show = isset($_REQUEST['show']) ? (string)$_REQUEST['show'] : '';
global $lang;
switch ($show) {
$info = array();
$html_dir = LANG_DIR . 'html/';
$req_mode = !empty($_REQUEST['show']) ? (string)$_REQUEST['show'] : 'not_found';
switch ($req_mode) {
case 'advert':
$info['title'] = $lang['ADVERT'];
$info['src'] = 'advert.html';
@ -29,23 +47,59 @@ switch ($show) {
$info['src'] = 'copyright_holders.html';
break;
case 'not_found':
$info['title'] = $lang['NOT_FOUND'];
$info['src'] = 'not_found.html';
break;
case 'user_agreement':
$info['title'] = $lang['USER_AGREEMENT'];
$info['src'] = 'user_agreement.html';
break;
default:
case 'not_found':
$info['title'] = $lang['NOT_FOUND'];
$info['src'] = 'not_found.html';
break;
bb_simple_die('Invalid request');
}
$require = is_file($htmlDir . $info['src']) ? ($htmlDir . $info['src']) : false;
$require = file_exists($html_dir . $info['src']) ? $html_dir . $info['src'] : $html_dir . 'not_found.html';
$template->assign_vars([
'PAGE_TITLE' => mb_strtoupper($info['title'], DEFAULT_CHARSET),
'REQUIRE' => $require ? file_get_contents($require) : $lang['NOT_FOUND'],
]);
?><!DOCTYPE html>
<html dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<link rel="stylesheet" href="styles/templates/default/css/main.css" type="text/css">
</head>
<body>
<style type="text/css">
#infobox-wrap {
width: 760px;
}
print_page('info.tpl', 'simple');
#infobox-body {
background: #FFFFFF;
color: #000000;
padding: 1em;
height: 400px;
overflow: auto;
border: 1px inset #000000;
}
#infobox-body p {
margin-top: 1em;
margin-bottom: 1em;
}
</style>
<br/>
<div id="infobox-wrap" class="bCenter row1">
<fieldset class="pad_6">
<legend class="med bold mrg_2 warnColor1"><?php echo mb_strtoupper($info['title'], 'UTF-8'); ?></legend>
<div class="bCenter">
<?php require($require); ?>
</div>
</fieldset>
<p class="gen tRight pad_6"><a href="javascript:window.close();" class="gen">[ <?php echo $lang['LOCK']; ?> ]</a>
</p>
</div><!--/infobox-wrap-->
</body>
</html>

View file

@ -1,331 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT);
// Check CLI mode
if (PHP_SAPI != 'cli') {
die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode');
}
// Get all constants
require_once BB_ROOT . 'library/defines.php';
// Include CLI functions
require INC_DIR . '/functions_cli.php';
/**
* System requirements
*/
const CHECK_REQUIREMENTS = [
'php_min_version' => '8.2.0',
'ext_list' => [
'json',
'curl',
'readline',
'mysqli',
'bcmath',
'mbstring',
'intl',
'xml',
'xmlwriter',
'zip',
'gd'
],
];
// Welcoming message
out("--- TorrentPier Installer ---\n", 'info');
// Checking extensions
out("- Checking installed extensions...", 'info');
// [1] Check PHP Version
if (!version_compare(PHP_VERSION, CHECK_REQUIREMENTS['php_min_version'], '>=')) {
out("- TorrentPier requires PHP version " . CHECK_REQUIREMENTS['php_min_version'] . "+ Your PHP version " . PHP_VERSION, 'warning');
}
// [2] Check installed PHP Extensions on server
foreach (CHECK_REQUIREMENTS['ext_list'] as $ext) {
if (!extension_loaded($ext)) {
out("- ext-$ext not installed. Check out php.ini file", 'error');
if (!defined('EXTENSIONS_NOT_INSTALLED')) {
define('EXTENSIONS_NOT_INSTALLED', true);
}
} else {
out("- ext-$ext installed!");
}
}
if (!defined('EXTENSIONS_NOT_INSTALLED')) {
out("- All extensions are installed!\n", 'success');
} else {
exit;
}
// Check if already installed
if (is_file(BB_ROOT . '.env')) {
out('- TorrentPier already installed', 'warning');
echo 'Are you sure want to re-install TorrentPier? [y/N]: ';
if (str_starts_with(mb_strtolower(trim(readline())), 'y')) {
out("\n- Re-install process started...", 'info');
// environment
if (is_file(BB_ROOT . '.env')) {
if (unlink(BB_ROOT . '.env')) {
out('- Environment file successfully removed!');
} else {
out('- Cannot remove environment (.env) file. Delete it manually', 'error');
exit;
}
}
// composer.phar
if (is_file(BB_ROOT . 'composer.phar')) {
if (unlink(BB_ROOT . 'composer.phar')) {
out("- composer.phar file successfully removed!");
} else {
out('- Cannot remove composer.phar file. Delete it manually', 'error');
exit;
}
}
// composer dir
if (is_dir(BB_ROOT . 'vendor')) {
removeDir(BB_ROOT . 'vendor', true);
if (!is_dir(BB_ROOT . 'vendor')) {
out("- Composer directory successfully removed!");
} else {
out('- Cannot remove Composer directory. Delete it manually', 'error');
exit;
}
}
out("- Re-install process completed!\n", 'success');
out('- Starting installation...', 'info');
} else {
exit;
}
}
// Applying permissions
out("- Applying permissions for folders...", 'info');
chmod_r(BB_ROOT . 'data', 0755, 0644);
chmod_r(BB_ROOT . 'internal_data', 0755, 0644);
chmod_r(BB_ROOT . 'sitemap', 0755, 0644);
out("- Permissions successfully applied!\n", 'success');
// Check composer installation
if (!is_file(BB_ROOT . 'vendor/autoload.php')) {
out('- Hmm, it seems there are no Composer dependencies', 'info');
// Downloading composer
if (!is_file(BB_ROOT . 'composer.phar')) {
out('- Downloading Composer...', 'info');
if (copy('https://getcomposer.org/installer', BB_ROOT . 'composer-setup.php')) {
out("- Composer successfully downloaded!\n", 'success');
runProcess('php ' . BB_ROOT . 'composer-setup.php --install-dir=' . BB_ROOT);
} else {
out('- Cannot download Composer. Please, download it (composer.phar) manually', 'error');
exit;
}
if (is_file(BB_ROOT . 'composer-setup.php')) {
if (unlink(BB_ROOT . 'composer-setup.php')) {
out("- Composer installation file successfully removed!\n", 'success');
} else {
out('- Cannot remove Composer installation file (composer-setup.php). Please, delete it manually', 'warning');
}
}
} else {
out("- composer.phar file found!\n", 'success');
}
// Installing dependencies
if (is_file(BB_ROOT . 'composer.phar')) {
out('- Installing dependencies...', 'info');
runProcess('php ' . BB_ROOT . 'composer.phar install --no-interaction --no-ansi');
define('COMPOSER_COMPLETED', true);
} else {
out('- composer.phar not found. Please, download it (composer.phar) manually', 'error');
exit;
}
} else {
out('- Composer dependencies are present!', 'success');
out("- Note: Remove 'vendor' folder if you want to re-install dependencies\n");
}
// Check composer dependencies
if (defined('COMPOSER_COMPLETED')) {
if (is_file(BB_ROOT . 'vendor/autoload.php')) {
out("- Completed! Composer dependencies are installed!\n", 'success');
} else {
exit;
}
}
// Preparing ENV
if (is_file(BB_ROOT . '.env.example') && !is_file(BB_ROOT . '.env')) {
if (copy(BB_ROOT . '.env.example', BB_ROOT . '.env')) {
out("- Environment file created!\n", 'success');
} else {
out('- Cannot create environment file', 'error');
exit;
}
}
// Editing ENV file
$DB_HOST = 'localhost';
$DB_PORT = 3306;
$DB_DATABASE = '';
$DB_USERNAME = '';
$DB_PASSWORD = '';
if (is_file(BB_ROOT . '.env')) {
out("--- Configuring TorrentPier ---", 'info');
$envContent = file_get_contents(BB_ROOT . '.env');
if ($envContent === false) {
out('- Cannot open environment file', 'error');
exit;
}
$envLines = explode("\n", $envContent);
$editedLines = [];
foreach ($envLines as $line) {
if (trim($line) !== '' && !str_starts_with($line, '#')) {
$parts = explode('=', $line, 2);
$key = trim($parts[0]);
$value = (!empty($parts[1]) && $key !== 'DB_PASSWORD') ? trim($parts[1]) : '';
out("\nCurrent value of $key: $value", 'debug');
echo "Enter a new value for $key (or leave empty to not change): ";
$newValue = trim(readline());
if (!empty($newValue) || $key === 'DB_PASSWORD') {
if ($key === 'TP_HOST') {
if (!preg_match('/^https?:\/\//', $newValue)) {
$newValue = 'https://' . $newValue;
}
$newValue = parse_url($newValue, PHP_URL_HOST);
}
$line = "$key=$newValue";
$$key = $newValue;
} else {
$$key = $value;
}
}
$editedLines[] = $line;
}
$newEnvContent = implode("\n", $editedLines);
if (file_put_contents(BB_ROOT . '.env', $newEnvContent)) {
out("- TorrentPier successfully configured!\n", 'success');
} else {
out('- Cannot save environment file', 'error');
exit;
}
} else {
out('- Environment file not found', 'error');
exit;
}
if (!empty($DB_HOST) && !empty($DB_DATABASE) && !empty($DB_USERNAME)) {
out("--- Checking environment settings ---\n", 'info');
// Connecting to database
out("- Trying connect to MySQL...", 'info');
// Checking mysqli extension installed
if (!extension_loaded('mysqli')) {
out('- ext-mysqli not found. Check out php.ini file', 'error');
exit;
}
// Connect to MySQL server
try {
$conn = new mysqli($DB_HOST, $DB_USERNAME, $DB_PASSWORD, port: $DB_PORT);
} catch (mysqli_sql_exception $exception) {
out("- Connection failed: {$exception->getMessage()}", 'error');
exit;
}
if (!$conn->connect_error) {
out('- Connected successfully!', 'success');
}
// Creating database if not exist
if ($conn->query("CREATE DATABASE IF NOT EXISTS $DB_DATABASE CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")) {
out('- Database created successfully!', 'success');
} else {
out("- Cannot create database: $DB_DATABASE", 'error');
exit;
}
$conn->select_db($DB_DATABASE);
// Close database connection - migrations will handle their own connections
$conn->close();
// Run database migrations
out('- Setting up database using migrations...', 'info');
// Check if phinx.php exists
if (!is_file(BB_ROOT . 'phinx.php')) {
out('- Migration configuration (phinx.php) not found', 'error');
exit;
}
// Run migrations
$migrationResult = runProcess('php vendor/bin/phinx migrate --configuration=' . BB_ROOT . 'phinx.php');
if ($migrationResult !== 0) {
out('- Database migration failed', 'error');
exit;
}
out("- Database setup completed!\n", 'success');
// Autofill host in robots.txt
$robots_txt_file = BB_ROOT . 'robots.txt';
if (isset($TP_HOST) && is_file($robots_txt_file)) {
$content = file_get_contents($robots_txt_file);
$content = str_replace('example.com', $TP_HOST, $content);
file_put_contents($robots_txt_file, $content);
}
if (isset($APP_ENV) && $APP_ENV === 'local') {
if (!is_file(BB_ROOT . 'library/config.local.php')) {
if (copy(BB_ROOT . 'library/config.php', BB_ROOT . 'library/config.local.php')) {
out('- Local configuration file created!', 'success');
} else {
out('- Cannot create library/config.local.php file. You can create it manually, just copy config.php and rename it to config.local.php', 'warning');
}
}
} else {
if (rename(__FILE__, __FILE__ . '_' . hash('xxh128', time()))) {
out("- Installation file renamed!", 'success');
} else {
out('- Cannot rename installation file (' . __FILE__ . '). Please, rename it manually for security reasons', 'warning');
}
}
// Cleanup...
if (is_file(BB_ROOT . '_cleanup.php')) {
out("\n--- Finishing installation (Cleanup) ---\n", 'info');
out('The cleanup process will remove:');
out('- Development documentation (README, CHANGELOG)', 'debug');
out('- Git configuration files', 'debug');
out('- CI/CD pipelines and code analysis tools', 'debug');
out('- Translation and contribution guidelines', 'debug');
echo 'Do you want to delete these files permanently? [y/N]: ';
if (str_starts_with(mb_strtolower(trim(readline())), 'y')) {
out("\n- Cleanup...", 'info');
require_once BB_ROOT . '_cleanup.php';
unlink(BB_ROOT . '_cleanup.php');
} else {
out('- Skipping...', 'info');
}
}
out("\n- Voila! Good luck & have fun!", 'success');
}

2
install/.htaccess Normal file
View file

@ -0,0 +1,2 @@
order allow,deny
deny from all

View file

@ -1,27 +0,0 @@
# Example Caddy configuration for TorrentPier
example.com {
root * /path/to/root
encode gzip zstd
php_fastcgi unix//run/php/php-fpm.sock
try_files {path} {path}/ /index.php?{query}
file_server
@blocked {
path /install/* /internal_data/* /library/*
path /.ht* /.en*
path /.git/*
path *.sql *.tpl *.db *.inc *.log *.md
}
respond @blocked 404
redir /sitemap.xml /sitemap/sitemap.xml
@html_css_js {
path *.html *.css *.js *.json *.xml *.txt
}
header @html_css_js Content-Type "{mime}; charset=utf-8"
}
# Refer to the Caddy docs for more information:
# https://caddyserver.com/docs/caddyfile

View file

@ -1,39 +0,0 @@
# Example nginx configuration for TorrentPier
server {
listen 80; # port
server_name example.com; # your domain
root /path/to/root; # folder with TorrentPier installed
index index.php;
charset utf-8;
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \/(install|internal_data|library)\/ {
return 404;
}
location ~ /\.(ht|en) {
return 404;
}
location ~ /\.git {
return 404;
}
location ~ \.(.*sql|tpl|db|inc|log|md)$ {
return 404;
}
rewrite ^/sitemap.xml$ /sitemap/sitemap.xml;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
}
}

1419
install/sql/mysql.sql Normal file

File diff suppressed because it is too large Load diff

53
install/sql/ocelot.sql Normal file
View file

@ -0,0 +1,53 @@
SET SQL_MODE = "";
-- ----------------------------
-- Table structure for `bb_bt_tracker`
-- ----------------------------
DROP TABLE IF EXISTS `bb_bt_tracker`;
CREATE TABLE IF NOT EXISTS `bb_bt_tracker` (
`peer_hash` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
`topic_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
`peer_id` varchar(20) NOT NULL,
`user_id` mediumint(9) NOT NULL DEFAULT '0',
`ip` varchar(42) NOT NULL DEFAULT '0',
`ipv6` varchar(32) DEFAULT NULL,
`port` smallint(5) unsigned NOT NULL DEFAULT '0',
`client` varchar(51) NOT NULL DEFAULT 'Unknown',
`seeder` tinyint(1) NOT NULL DEFAULT '0',
`releaser` tinyint(1) NOT NULL DEFAULT '0',
`tor_type` tinyint(1) NOT NULL DEFAULT '0',
`uploaded` bigint(20) unsigned NOT NULL DEFAULT '0',
`downloaded` bigint(20) unsigned NOT NULL DEFAULT '0',
`remain` bigint(20) unsigned NOT NULL DEFAULT '0',
`speed_up` mediumint(8) unsigned NOT NULL DEFAULT '0',
`speed_down` mediumint(8) unsigned NOT NULL DEFAULT '0',
`up_add` bigint(20) unsigned NOT NULL DEFAULT '0',
`down_add` bigint(20) unsigned NOT NULL DEFAULT '0',
`update_time` int(11) NOT NULL DEFAULT '0',
`complete_percent` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`peer_hash`),
KEY `topic_id` (`topic_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of bb_bt_tracker
-- ----------------------------
-- ----------------------------
-- Table structure for `bb_bt_tracker_snap`
-- ----------------------------
DROP TABLE IF EXISTS `bb_bt_tracker_snap`;
CREATE TABLE IF NOT EXISTS `bb_bt_tracker_snap` (
`topic_id` mediumint(8) unsigned NOT NULL DEFAULT '0',
`seeders` mediumint(8) unsigned NOT NULL DEFAULT '0',
`leechers` mediumint(8) unsigned NOT NULL DEFAULT '0',
`speed_up` int(10) unsigned NOT NULL DEFAULT '0',
`speed_down` int(10) unsigned NOT NULL DEFAULT '0',
`complete` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`topic_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of bb_bt_tracker_snap
-- ----------------------------

View file

@ -0,0 +1,30 @@
Схема изменений начиная с версии 2.1.5:
Изменения в базе:
// 2.2.0
UPDATE `bb_config` SET `config_value` = 'http://whatismyipaddress.com/ip/' WHERE `config_name` = 'whois_info';
DELETE FROM `bb_smilies` WHERE `code` = ':ad:';
INSERT INTO `bb_smilies` VALUES ('', ':сd:', 'сd.gif', 'сd');
DROP TABLE IF EXISTS `bb_ads`;
DELETE FROM `bb_config` WHERE `config_name` = 'active_ads';
ALTER TABLE `bb_log` DROP COLUMN `log_username`;
DELETE FROM `bb_config` WHERE `config_name` = 'new_tpls';
UPDATE `bb_posts` SET `poster_ip` = '0';
ALTER TABLE `bb_posts` CHANGE `poster_ip` `poster_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_bt_tracker` SET `ip` = '0';
ALTER TABLE `bb_bt_tracker` CHANGE `ip` `ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_users` SET `user_last_ip` = '0';
ALTER TABLE `bb_users` CHANGE `user_last_ip` `user_last_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_users` SET `user_reg_ip` = '0';
ALTER TABLE `bb_users` CHANGE `user_reg_ip` `user_reg_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_log` SET `log_user_ip` = '0';
ALTER TABLE `bb_log` CHANGE `log_user_ip` `log_user_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_poll_users` SET `vote_ip` = '0';
ALTER TABLE `bb_poll_users` CHANGE `vote_ip` `vote_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_privmsgs` SET `privmsgs_ip` = '0';
ALTER TABLE `bb_privmsgs` CHANGE `privmsgs_ip` `privmsgs_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_sessions` SET `session_ip` = '0';
ALTER TABLE `bb_sessions` CHANGE `session_ip` `session_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_banlist` SET `ban_ip` = '0';
ALTER TABLE `bb_banlist` CHANGE `ban_ip` `ban_ip` varchar(42) NOT NULL DEFAULT '0';

2
internal_data/cache/.htaccess vendored Normal file
View file

@ -0,0 +1,2 @@
order allow,deny
deny from all

View file

@ -0,0 +1,2 @@
order allow,deny
deny from all

View file

@ -0,0 +1,2 @@
order allow,deny
deny from all

View file

@ -0,0 +1,2 @@
order allow,deny
deny from all

View file

@ -1,45 +1,57 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
die(basename(__FILE__));
}
global $lang, $user;
if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)');
}
global $bb_cfg, $lang, $user;
$mode = (string)$this->request['mode'];
$user_id = (int)$this->request['user_id'];
if (!$user_id or !$u_data = get_userdata($user_id)) {
$this->ajax_die($lang['NO_USER_ID_SPECIFIED']);
$this->ajax_die('Invalid user_id');
}
if (!IS_ADMIN && $user_id != $user->id) {
$this->ajax_die($lang['NOT_AUTHORISED']);
$this->ajax_die($lang['NOT_ADMIN']);
}
$new_ext_id = 0;
$response = '';
switch ($mode) {
case 'delete':
delete_avatar($user_id, $u_data['avatar_ext_id']);
$response = get_avatar($user_id, $new_ext_id);
$new_ext_id = 0;
$response = '<img src="' . $bb_cfg['avatars']['upload_path'] . $bb_cfg['avatars']['no_avatar'] . '" alt="' . $user_id . '" />';
break;
default:
$this->ajax_die('Invalid mode: ' . $mode);
$this->ajax_die('Invalid mode');
}
DB()->query("UPDATE " . BB_USERS . " SET avatar_ext_id = $new_ext_id WHERE user_id = $user_id LIMIT 1");
DB()->query("UPDATE " . BB_USERS . " SET avatar_ext_id = $new_ext_id WHERE user_id = $user_id");
\TorrentPier\Sessions::cache_rm_user_sessions($user_id);
cache_rm_user_sessions($user_id);
$this->response['avatar_html'] = $response;

View file

@ -1,93 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!defined('IN_AJAX')) {
die(basename(__FILE__));
}
global $userdata, $lang;
if (!config()->get('callseed')) {
$this->ajax_die($lang['MODULE_OFF']);
}
if (!$topic_id = (int)$this->request['topic_id']) {
$this->ajax_die($lang['INVALID_TOPIC_ID']);
}
if (!$t_data = topic_info($topic_id)) {
$this->ajax_die($lang['INVALID_TOPIC_ID_DB']);
}
$forum_id = $t_data['forum_id'];
if ($t_data['seeders'] >= 3) {
$this->ajax_die(sprintf($lang['CALLSEED_HAVE_SEED'], $t_data['seeders']));
} elseif ($t_data['call_seed_time'] >= (TIMENOW - 86400)) {
$time_left = delta_time($t_data['call_seed_time'] + 86400, TIMENOW, 'days');
$this->ajax_die(sprintf($lang['CALLSEED_MSG_SPAM'], $time_left));
} elseif (isset(config()->get('tor_no_tor_act')[$t_data['tor_status']])) {
$this->ajax_die($lang['NOT_AVAILABLE']);
}
$banned_users = ($get_banned_users = get_banned_users()) ? (', ' . implode(', ', $get_banned_users)) : '';
$user_list = DB()->fetch_rowset("
SELECT DISTINCT dl.user_id, u.user_opt, tr.user_id as active_dl
FROM " . BB_BT_DLSTATUS . " dl
LEFT JOIN " . BB_USERS . " u ON(u.user_id = dl.user_id)
LEFT JOIN " . BB_BT_TRACKER . " tr ON(tr.user_id = dl.user_id)
WHERE dl.topic_id = $topic_id
AND dl.user_status IN (" . DL_STATUS_COMPLETE . ", " . DL_STATUS_DOWN . ")
AND dl.user_id NOT IN ({$userdata['user_id']}, " . EXCLUDED_USERS . $banned_users . ")
AND u.user_active = 1
GROUP BY dl.user_id
");
$subject = sprintf($lang['CALLSEED_SUBJECT'], $t_data['topic_title']);
$message = sprintf($lang['CALLSEED_TEXT'], make_url(TOPIC_URL . $topic_id), $t_data['topic_title'], make_url(DL_URL . $t_data['attach_id']));
if ($user_list) {
foreach ($user_list as $row) {
if (!empty($row['active_dl'])) {
continue;
}
if (bf($row['user_opt'], 'user_opt', 'user_callseed')) {
send_pm($row['user_id'], $subject, $message, BOT_UID);
}
}
} else {
send_pm($t_data['poster_id'], $subject, $message, BOT_UID);
}
DB()->query("UPDATE " . BB_BT_TORRENTS . " SET call_seed_time = " . TIMENOW . " WHERE topic_id = $topic_id LIMIT 1");
function topic_info($topic_id)
{
global $lang;
$sql = "
SELECT
tor.poster_id, tor.forum_id, tor.attach_id, tor.call_seed_time, tor.tor_status,
t.topic_title, sn.seeders
FROM " . BB_BT_TORRENTS . " tor
LEFT JOIN " . BB_TOPICS . " t USING(topic_id)
LEFT JOIN " . BB_BT_TRACKER_SNAP . " sn USING(topic_id)
WHERE tor.topic_id = $topic_id
";
if (!$torrent = DB()->fetch_row($sql)) {
bb_die($lang['TOPIC_POST_NOT_EXIST'], 404);
}
return $torrent;
}
$this->response['response'] = $lang['CALLSEED_MSG_OK'];

View file

@ -1,28 +1,42 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
die(basename(__FILE__));
}
global $userdata, $lang, $log_action;
global $userdata, $bb_cfg, $lang;
if (!$attach_id = (int)$this->request['attach_id']) {
if (!isset($this->request['attach_id'])) {
$this->ajax_die($lang['EMPTY_ATTACH_ID']);
}
if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)');
}
$attach_id = (int)$this->request['attach_id'];
$mode = (string)$this->request['mode'];
$comment = false;
if (config()->get('tor_comment')) {
if ($bb_cfg['tor_comment']) {
$comment = (string)$this->request['comment'];
}
@ -44,7 +58,7 @@ switch ($mode) {
case 'status':
$new_status = (int)$this->request['status'];
// Check status validity
// Валидность статуса
if (!isset($lang['TOR_STATUS_NAME'][$new_status])) {
$this->ajax_die($lang['TOR_STATUS_FAILED']);
}
@ -55,27 +69,27 @@ switch ($mode) {
$this->ajax_die($lang['NOT_MODERATOR']);
}
// Error if same status
// Тот же статус
if ($tor['tor_status'] == $new_status) {
$this->ajax_die($lang['TOR_STATUS_DUB']);
}
// Prohibition on changing/assigning CH-status by moderator
// Запрет на изменение/присвоение CH-статуса модератором
if ($new_status == TOR_CLOSED_CPHOLD && !IS_ADMIN) {
$this->ajax_die($lang['TOR_DONT_CHANGE']);
}
// Check rights to change status
// Права на изменение статуса
if ($tor['tor_status'] == TOR_CLOSED_CPHOLD) {
if (!IS_ADMIN) {
$this->verify_mod_rights($tor['forum_id']);
}
DB()->query("UPDATE " . BB_TOPICS . " SET topic_status = " . TOPIC_UNLOCKED . " WHERE topic_id = {$tor['topic_id']} LIMIT 1");
DB()->query("UPDATE " . BB_TOPICS . " SET topic_status = " . TOPIC_UNLOCKED . " WHERE topic_id = {$tor['topic_id']}");
} else {
$this->verify_mod_rights($tor['forum_id']);
}
// Confirmation of status change set by another moderator
// Подтверждение изменения статуса, выставленного другим модератором
if ($tor['tor_status'] != TOR_NOT_APPROVED && $tor['checked_user_id'] != $userdata['user_id'] && $tor['checked_time'] + 2 * 3600 > TIMENOW) {
if (empty($this->request['confirmed'])) {
$msg = $lang['TOR_STATUS_OF'] . " {$lang['TOR_STATUS_NAME'][$tor['tor_status']]}\n\n";
@ -85,39 +99,27 @@ switch ($mode) {
}
}
\TorrentPier\Legacy\Torrent::change_tor_status($attach_id, $new_status);
change_tor_status($attach_id, $new_status);
// Log action
$log_msg = sprintf($lang['TOR_STATUS_LOG_ACTION'], config()->get('tor_icons')[$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b>', config()->get('tor_icons')[$tor['tor_status']] . ' <b> ' . $lang['TOR_STATUS_NAME'][$tor['tor_status']] . '</b>');
if ($comment && $comment != $lang['COMMENT']) {
$log_msg .= "<br/>{$lang['COMMENT']}: <b>$comment</b>.";
}
$log_action->mod('mod_topic_change_tor_status', [
'forum_id' => $tor['forum_id'],
'topic_id' => $tor['topic_id'],
'topic_title' => $tor['topic_title'],
'log_msg' => $log_msg . '<br/>-------------',
]);
$this->response['status'] = $bb_cfg['tor_icons'][$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b> &middot; ' . profile_url($userdata) . ' &middot; <i>' . delta_time(TIMENOW) . $lang['TOR_BACK'] . '</i>';
$this->response['status'] = config()->get('tor_icons')[$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b> &middot; ' . profile_url($userdata) . ' &middot; <i>' . delta_time(TIMENOW) . $lang['TOR_BACK'] . '</i>';
if (config()->get('tor_comment') && (($comment && $comment != $lang['COMMENT']) || in_array($new_status, config()->get('tor_reply')))) {
if ($bb_cfg['tor_comment'] && (($comment && $comment != $lang['COMMENT']) || in_array($new_status, $bb_cfg['tor_reply']))) {
if ($tor['poster_id'] > 0) {
$subject = sprintf($lang['TOR_MOD_TITLE'], $tor['topic_title']);
$message = sprintf($lang['TOR_MOD_MSG'], get_username($tor['poster_id']), make_url(TOPIC_URL . $tor['topic_id']), config()->get('tor_icons')[$new_status] . ' ' . $lang['TOR_STATUS_NAME'][$new_status]);
$message = sprintf($lang['TOR_MOD_MSG'], get_username($tor['poster_id']), make_url(TOPIC_URL . $tor['topic_id']), $bb_cfg['tor_icons'][$new_status] . ' ' . $lang['TOR_STATUS_NAME'][$new_status]);
if ($comment && $comment != $lang['COMMENT']) {
$message .= "\n\n[b]" . $lang['COMMENT'] . '[/b]: ' . $comment;
}
send_pm($tor['poster_id'], $subject, $message, $userdata['user_id']);
\TorrentPier\Sessions::cache_rm_user_sessions($tor['poster_id']);
cache_rm_user_sessions($tor['poster_id']);
}
}
break;
case 'status_reply':
if (!config()->get('tor_comment')) {
if (!$bb_cfg['tor_comment']) {
$this->ajax_die($lang['MODULE_OFF']);
}
@ -129,11 +131,8 @@ switch ($mode) {
}
send_pm($tor['checked_user_id'], $subject, $message, $userdata['user_id']);
\TorrentPier\Sessions::cache_rm_user_sessions($tor['checked_user_id']);
cache_rm_user_sessions($tor['checked_user_id']);
break;
default:
$this->ajax_die('Invalid mode: ' . $mode);
}
$this->response['attach_id'] = $attach_id;

View file

@ -1,34 +1,71 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
die(basename(__FILE__));
}
global $userdata, $lang, $log_action;
global $userdata, $bb_cfg, $lang;
if (!isset($this->request['attach_id'])) {
$this->ajax_die($lang['EMPTY_ATTACH_ID']);
}
if (!isset($this->request['type'])) {
$this->ajax_die('empty type');
$this->ajax_die('type');
}
$attach_id = (int)$this->request['attach_id'];
$type = (string)$this->request['type'];
if (!$torrent = \TorrentPier\Legacy\Torrent::get_torrent_info($attach_id)) {
$torrent = DB()->fetch_row("
SELECT
a.post_id, d.physical_filename, d.extension, d.tracker_status,
t.topic_first_post_id,
p.poster_id, p.topic_id, p.forum_id,
f.allow_reg_tracker
FROM
" . BB_ATTACHMENTS . " a,
" . BB_ATTACHMENTS_DESC . " d,
" . BB_POSTS . " p,
" . BB_TOPICS . " t,
" . BB_FORUMS . " f
WHERE
a.attach_id = $attach_id
AND d.attach_id = $attach_id
AND p.post_id = a.post_id
AND t.topic_id = p.topic_id
AND f.forum_id = p.forum_id
LIMIT 1
");
if (!$torrent) {
$this->ajax_die($lang['INVALID_ATTACH_ID']);
}
if ($torrent['poster_id'] == $userdata['user_id'] && !IS_AM) {
if ($type == 'del_torrent' || $type == 'reg' || $type == 'unreg') {
true;
} else {
$this->ajax_die($lang['ONLY_FOR_MOD']);
}
@ -43,36 +80,23 @@ switch ($type) {
case 'unset_silver_gold':
if ($type == 'set_silver') {
$tor_type = TOR_TYPE_SILVER;
$tor_type_lang = $lang['SILVER'];
} elseif ($type == 'set_gold') {
$tor_type = TOR_TYPE_GOLD;
$tor_type_lang = $lang['GOLD'];
} else {
$tor_type = TOR_TYPE_DEFAULT;
$tor_type_lang = "{$lang['UNSET_GOLD_TORRENT']} / {$lang['UNSET_SILVER_TORRENT']}";
$tor_type = 0;
}
\TorrentPier\Legacy\Torrent::change_tor_type($attach_id, $tor_type);
// Log action
$log_action->mod('mod_topic_change_tor_type', [
'forum_id' => $torrent['forum_id'],
'topic_id' => $torrent['topic_id'],
'topic_title' => $torrent['topic_title'],
'log_msg' => sprintf($lang['TOR_TYPE_LOG_ACTION'], $tor_type_lang),
]);
change_tor_type($attach_id, $tor_type);
$title = $lang['CHANGE_TOR_TYPE'];
$url = make_url(TOPIC_URL . $torrent['topic_id']);
break;
case 'reg':
\TorrentPier\Legacy\Torrent::tracker_register($attach_id);
tracker_register($attach_id);
$url = (TOPIC_URL . $torrent['topic_id']);
break;
case 'unreg':
\TorrentPier\Legacy\Torrent::tracker_unregister($attach_id);
tracker_unregister($attach_id);
$url = (TOPIC_URL . $torrent['topic_id']);
break;
@ -80,7 +104,7 @@ switch ($type) {
if (empty($this->request['confirmed'])) {
$this->prompt_for_confirm($lang['DEL_TORRENT']);
}
\TorrentPier\Legacy\Torrent::delete_torrent($attach_id);
delete_torrent($attach_id);
$url = make_url(TOPIC_URL . $torrent['topic_id']);
break;
@ -88,8 +112,8 @@ switch ($type) {
if (empty($this->request['confirmed'])) {
$this->prompt_for_confirm($lang['DEL_MOVE_TORRENT']);
}
\TorrentPier\Legacy\Torrent::delete_torrent($attach_id);
$url = make_url("modcp.php?" . POST_TOPIC_URL . "={$torrent['topic_id']}&mode=move&sid={$userdata['session_id']}");
delete_torrent($attach_id);
$url = make_url("modcp.php?t={$torrent['topic_id']}&mode=move&sid={$userdata['session_id']}");
break;
}

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
@ -14,10 +30,10 @@ if (!defined('IN_AJAX')) {
global $bf, $lang;
$user_id = (int)$this->request['user_id'];
$new_opt = json_decode($this->request['user_opt'], true, 512, JSON_THROW_ON_ERROR);
$new_opt = json_decode($this->request['user_opt'], true);
if (!$user_id or !$u_data = get_userdata($user_id)) {
$this->ajax_die($lang['NO_USER_ID_SPECIFIED']);
$this->ajax_die('invalid user_id');
}
if (!is_array($new_opt)) {
@ -30,9 +46,9 @@ foreach ($bf['user_opt'] as $opt_name => $opt_bit) {
}
}
DB()->query("UPDATE " . BB_USERS . " SET user_opt = {$u_data['user_opt']} WHERE user_id = $user_id LIMIT 1");
DB()->query("UPDATE " . BB_USERS . " SET user_opt = {$u_data['user_opt']} WHERE user_id = $user_id");
// Remove data from cache
\TorrentPier\Sessions::cache_rm_user_sessions($user_id);
// Удаляем данные из кеша
cache_rm_user_sessions($user_id);
$this->response['resp_html'] = $lang['SAVED'];

View file

@ -1,10 +1,26 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
@ -13,26 +29,22 @@ if (!defined('IN_AJAX')) {
global $datastore, $lang;
if (!$ranks = $datastore->get('ranks')) {
$datastore->update('ranks');
$ranks = $datastore->get('ranks');
}
$ranks = $datastore->get('ranks');
$rank_id = (int)$this->request['rank_id'];
if (!$user_id = (int)$this->request['user_id'] or !$profiledata = get_userdata($user_id)) {
$this->ajax_die($lang['NO_USER_ID_SPECIFIED']);
$this->ajax_die("invalid user_id: $user_id");
}
if ($rank_id != 0 && !isset($ranks[$rank_id])) {
$this->ajax_die("invalid rank_id: $rank_id");
}
DB()->query("UPDATE " . BB_USERS . " SET user_rank = $rank_id WHERE user_id = $user_id LIMIT 1");
DB()->query("UPDATE " . BB_USERS . " SET user_rank = $rank_id WHERE user_id = $user_id");
\TorrentPier\Sessions::cache_rm_user_sessions($user_id);
cache_rm_user_sessions($user_id);
$user_rank = $rank_id ? '<span class="' . $ranks[$rank_id]['rank_style'] . '">' . $ranks[$rank_id]['rank_title'] . '</span>' : '';
$user_rank = ($rank_id) ? '<span class="' . $ranks[$rank_id]['rank_style'] . '">' . $ranks[$rank_id]['rank_title'] . '</span>' : '';
$this->response['html'] = $rank_id ? $lang['AWARDED_RANK'] . "<b> $user_rank </b>" : $lang['SHOT_RANK'];
$this->response['rank_name'] = $rank_id ? $user_rank : $lang['USER'];
$this->response['html'] = ($rank_id) ? $lang['AWARDED_RANK'] . "<b> $user_rank </b>" : $lang['SHOT_RANK'];
$this->response['rank_name'] = ($rank_id) ? $user_rank : $lang['USER'];

View file

@ -1,19 +1,35 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
die(basename(__FILE__));
}
global $userdata, $lang;
global $bb_cfg, $userdata, $lang;
if (!$group_id = (int)$this->request['group_id'] or !$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
if (!$group_id = (int)$this->request['group_id'] or !$group_info = get_group_data($group_id)) {
$this->ajax_die($lang['NO_GROUP_ID_SPECIFIED']);
}
if (!$mode = (string)$this->request['mode']) {
@ -34,16 +50,19 @@ switch ($mode) {
$this->response['new_value'] = $value;
break;
case 'release_group':
case 'group_type':
$this->response['new_value'] = $value;
break;
case 'release_group':
$this->response['new_value'] = $value;
break;
case 'delete_avatar':
delete_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']);
$value = 0;
$mode = 'avatar_ext_id';
$this->response['remove_avatar'] = get_avatar(GROUP_AVATAR_MASK . $group_id, $value);
$this->response['act'] = $value;
break;
default:
@ -51,4 +70,4 @@ switch ($mode) {
}
$value_sql = DB()->escape($value, true);
DB()->query("UPDATE " . BB_GROUPS . " SET $mode = $value_sql WHERE group_id = $group_id LIMIT 1");
DB()->query("UPDATE " . BB_GROUPS . " SET $mode = $value_sql WHERE group_id = $group_id");

View file

@ -1,46 +1,58 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
* MIT License
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
* Copyright (c) 2005-2017 TorrentPier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
if (!defined('IN_AJAX')) {
die(basename(__FILE__));
}
global $lang;
global $bb_cfg, $lang;
if (!$user_id = (int)$this->request['user_id'] or !$profiledata = get_userdata($user_id)) {
$this->ajax_die($lang['NO_USER_ID_SPECIFIED']);
}
if (!$field = (string)$this->request['field']) {
$this->ajax_die('invalid profile field');
}
// Check for demo mode
if (IN_DEMO_MODE && in_array($field, ['username', 'user_email'])) {
$this->ajax_die($lang['CANT_EDIT_IN_DEMO_MODE']);
}
$table = BB_USERS;
$value = $this->request['value'] = (string)(isset($this->request['value'])) ? $this->request['value'] : 0;
switch ($field) {
case 'username':
require_once INC_DIR . '/functions_validate.php';
$value = clean_username($value);
if ($err = \TorrentPier\Validate::username($value)) {
$this->ajax_die($err);
if ($err = validate_username($value)) {
$this->ajax_die(strip_tags($err));
}
$this->response['new_value'] = $this->request['value'];
break;
case 'user_email':
require_once INC_DIR . '/functions_validate.php';
$value = htmlCHR($value);
if ($err = \TorrentPier\Validate::email($value)) {
if ($err = validate_email($value)) {
$this->ajax_die($err);
}
$this->response['new_value'] = $this->request['value'];
@ -55,17 +67,18 @@ switch ($field) {
break;
case 'user_gender':
if (!config()->get('gender')) {
if (!$bb_cfg['gender']) {
$this->ajax_die($lang['MODULE_OFF']);
}
if (!isset($lang['GENDER_SELECT'][$value])) {
$this->ajax_die($lang['ERROR']);
} else {
$this->response['new_value'] = $lang['GENDER_SELECT'][$value];
}
$this->response['new_value'] = $lang['GENDER_SELECT'][$value];
break;
case 'user_birthday':
if (!config()->get('birthday_enabled')) {
if (!$bb_cfg['birthday_enabled']) {
$this->ajax_die($lang['MODULE_OFF']);
}
$birthday_date = date_parse($value);
@ -73,10 +86,10 @@ switch ($field) {
if (!empty($birthday_date['year'])) {
if (strtotime($value) >= TIMENOW) {
$this->ajax_die($lang['WRONG_BIRTHDAY_FORMAT']);
} elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] > config()->get('birthday_max_age')) {
$this->ajax_die(sprintf($lang['BIRTHDAY_TO_HIGH'], config()->get('birthday_max_age')));
} elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] < config()->get('birthday_min_age')) {
$this->ajax_die(sprintf($lang['BIRTHDAY_TO_LOW'], config()->get('birthday_min_age')));
} elseif (bb_date(TIMENOW, 'Y', 'false') - $birthday_date['year'] > $bb_cfg['birthday_max_age']) {
$this->ajax_die(sprintf($lang['BIRTHDAY_TO_HIGH'], $bb_cfg['birthday_max_age']));
} elseif (bb_date(TIMENOW, 'Y', 'false') - $birthday_date['year'] < $bb_cfg['birthday_min_age']) {
$this->ajax_die(sprintf($lang['BIRTHDAY_TO_LOW'], $bb_cfg['birthday_min_age']));
}
}
@ -104,9 +117,20 @@ switch ($field) {
$this->response['new_value'] = $this->request['value'];
break;
case 'user_from':
case 'user_occ':
case 'user_interests':
$this->response['new_value'] = htmlCHR($value);
$value = htmlCHR($value);
$this->response['new_value'] = $value;
break;
case 'user_regdate':
case 'user_lastvisit':
$tz = TIMENOW + (3600 * $bb_cfg['board_timezone']);
if (($value = strtotime($value, $tz)) < $bb_cfg['board_startdate'] or $value > TIMENOW) {
$this->ajax_die($lang['INVALID_DATE'] . $this->request['value']);
}
$this->response['new_value'] = bb_date($value, 'Y-m-d H:i', false);
break;
case 'u_up_total':
@ -118,32 +142,30 @@ switch ($field) {
}
$table = BB_BT_USERS;
$value = (int)$this->request['value'];
$value = (float)str_replace(',', '.', $this->request['value']);
if ($value < 0) {
$this->ajax_die($lang['WRONG_INPUT']);
}
foreach (['KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4, 'PB' => 5, 'EB' => 6, 'ZB' => 7, 'YB' => 8] as $s => $m) {
if (stripos($this->request['value'], $s) !== false) {
$value *= (1024 ** $m);
foreach (array('KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4) as $s => $m) {
if (strpos($this->request['value'], $s) !== false) {
$value *= pow(1024, $m);
break;
}
}
$this->response['new_value'] = humn_size($value, space: ' ');
$value = sprintf('%.0f', $value);
$this->response['new_value'] = humn_size($value, null, null, ' ');
$btu = get_bt_userdata($user_id);
if (!$btu = get_bt_userdata($user_id)) {
require INC_DIR . '/functions_torrent.php';
generate_passkey($user_id, true);
$btu = get_bt_userdata($user_id);
}
$btu[$field] = $value;
$this->response['update_ids']['u_ratio'] = (string)get_bt_ratio($btu);
CACHE('bb_cache')->rm('btu_' . $user_id);
break;
case 'user_points':
$value = htmlCHR($value);
$value = (float)str_replace(',', '.', $this->request['value']);
$value = sprintf('%.2f', $value);
if ($value < 0.0 || strlen(strstr($value, '.', true)) > 14) {
$this->ajax_die($lang['WRONG_INPUT']);
}
$this->response['new_value'] = $value;
break;
@ -152,8 +174,8 @@ switch ($field) {
}
$value_sql = DB()->escape($value, true);
DB()->query("UPDATE $table SET $field = $value_sql WHERE user_id = $user_id LIMIT 1");
DB()->query("UPDATE $table SET $field = $value_sql WHERE user_id = $user_id");
\TorrentPier\Sessions::cache_rm_user_sessions($user_id);
cache_rm_user_sessions($user_id);
$this->response['edit_id'] = $this->request['edit_id'];

Some files were not shown because too many files have changed in this diff Show more