diff --git a/package-lock.json b/package-lock.json index a153c9b8..0f238646 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3298,6 +3298,14 @@ "tweetnacl": "^0.14.3" } }, + "bencode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.1.tgz", + "integrity": "sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -14493,6 +14501,14 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "sass-graph": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", @@ -16198,6 +16214,14 @@ "glob": "^7.1.2" } }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, "tsconfig-paths": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", @@ -16917,6 +16941,11 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=" + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", diff --git a/package.json b/package.json index 7316d5de..b22379dc 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "axios": "^0.20.0", "babel-eslint": "^10.1.0", "babel-loader": "^8.0.6", + "bencode": "^2.0.1", "body-parser": "^1.18.3", "case-sensitive-paths-webpack-plugin": "2.3.0", "chalk": "^4.1.0", @@ -99,6 +100,7 @@ "react-transition-group": "^4.4.1", "ress": "^3.0.0", "run-series": "^1.1.6", + "sanitize-filename": "^1.6.3", "sass-loader": "^10.0.1", "saxen": "^8.1.2", "source-map-loader": "^1.1.0", diff --git a/server/models/ClientRequest.js b/server/models/ClientRequest.js index 5b297f47..3665c8e3 100644 --- a/server/models/ClientRequest.js +++ b/server/models/ClientRequest.js @@ -380,6 +380,10 @@ class ClientRequest { this.requests.push(getMethodCall('d.close', [hash])); }); } + + getSessionPath() { + this.requests.push(getMethodCall('session.path')); + } } module.exports = ClientRequest; diff --git a/server/models/client.js b/server/models/client.js index 662a6301..d23d11ae 100644 --- a/server/models/client.js +++ b/server/models/client.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const sanitize = require('sanitize-filename'); const series = require('run-series'); const tar = require('tar-stream'); @@ -10,6 +11,7 @@ const fileUtil = require('../util/fileUtil'); const settings = require('./settings'); const torrentFilePropsMap = require('../../shared/constants/torrentFilePropsMap'); const torrentPeerPropsMap = require('../../shared/constants/torrentPeerPropsMap'); +const torrentFileUtil = require('../util/torrentFileUtil'); const torrentStatusMap = require('../../shared/constants/torrentStatusMap'); const torrentTrackerPropsMap = require('../../shared/constants/torrentTrackerPropsMap'); @@ -383,7 +385,23 @@ const client = { setTracker(user, services, data, callback) { const request = new ClientRequest(user, services); + request.getSessionPath(); request.setTracker(data); + request.postProcess((response) => { + // Modify tracker URL in torrent files + const {tracker, hashes} = data; + const sessionPath = `${response.shift()}`; + + if (typeof sessionPath === 'string') { + // Deduplicate hashes via Set() to avoid file ops on the same files + [...new Set(hashes)].forEach((hash) => { + const torrent = path.join(sessionPath, sanitize(`${hash}.torrent`)); + torrentFileUtil.setTracker(torrent, tracker); + }); + } + + return response; + }); request.onComplete((response, error) => { // Fetch the latest torrent list to re-index trackerURI. services.torrentService.fetchTorrentList(); diff --git a/server/util/torrentFileUtil.js b/server/util/torrentFileUtil.js new file mode 100644 index 00000000..183d7e8c --- /dev/null +++ b/server/util/torrentFileUtil.js @@ -0,0 +1,24 @@ +const bencode = require('bencode'); +const fs = require('fs'); + +const setTracker = (torrent, tracker) => { + fs.readFile(torrent, (err, data) => { + if (err) { + return; + } + + const torrentData = bencode.decode(data); + + if (torrentData.announce != null) { + torrentData.announce = Buffer.from(tracker); + + fs.writeFileSync(torrent, bencode.encode(torrentData)); + } + }); +}; + +const torrentFileUtil = { + setTracker, +}; + +module.exports = torrentFileUtil;