Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialization no longer changes torrent hash. #253

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ public interface TorrentMetadata extends TorrentHash {
int getPiecesCount();

/**
* @return The filename of the directory in which to store all the files
* @return The filename of single file or the directory in which to store all the files
*/
String getName();

/**
* @return The filename of the directory in which to store all the files, null if it is a single file
*/
String getDirectoryName();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.turn.ttorrent.common;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
Expand All @@ -15,6 +14,7 @@ public class TorrentMetadataImpl implements TorrentMetadata {
private final String myComment;
private final String myCreatedBy;
private final String myName;
private final String myDirName;
private final List<TorrentFile> myFiles;
private final int myPieceCount;
private final int myPieceLength;
Expand All @@ -28,6 +28,7 @@ public class TorrentMetadataImpl implements TorrentMetadata {
String comment,
String createdBy,
String name,
String dirName,
List<TorrentFile> files,
int pieceCount,
int pieceLength,
Expand All @@ -39,6 +40,7 @@ public class TorrentMetadataImpl implements TorrentMetadata {
myComment = comment;
myCreatedBy = createdBy;
myName = name;
myDirName = dirName;
myFiles = files;
myPieceCount = pieceCount;
myPieceLength = pieceLength;
Expand All @@ -47,10 +49,16 @@ public class TorrentMetadataImpl implements TorrentMetadata {
}

@Override
public String getDirectoryName() {
public String getName() {
return myName;
}

@Override
public String getDirectoryName() {
return myDirName;
}


@Override
public List<TorrentFile> getFiles() {
return myFiles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ public TorrentMetadata parse(byte[] metadata) throws InvalidBEncodingException,

final boolean torrentContainsManyFiles = infoTable.get(FILES) != null;

final String dirName = getRequiredValueOrThrowException(infoTable, NAME).getString();
final String name = getRequiredValueOrThrowException(infoTable, NAME).getString();
final String dirName = torrentContainsManyFiles ? name : null;

final List<TorrentFile> files = parseFiles(infoTable, torrentContainsManyFiles, dirName);
final List<TorrentFile> files = parseFiles(infoTable, torrentContainsManyFiles, name);

if (piecesHashes.length % Constants.PIECE_HASH_SIZE != 0)
throw new InvalidBEncodingException("Incorrect size of pieces hashes");
Expand All @@ -78,6 +79,7 @@ public TorrentMetadata parse(byte[] metadata) throws InvalidBEncodingException,
creationDate,
comment,
createdBy,
name,
dirName,
files,
piecesCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

import static com.turn.ttorrent.common.TorrentMetadataKeys.*;

Expand Down Expand Up @@ -39,8 +36,9 @@ public byte[] serialize(TorrentMetadata metadata) throws IOException {
infoTable.put(PRIVATE, new BEValue(1));
}

infoTable.put(NAME, new BEValue(metadata.getDirectoryName()));
if (metadata.getFiles().size() == 1) {
infoTable.put(NAME, new BEValue(metadata.getName()));

if (metadata.getFiles().size() == 1 && metadata.getDirectoryName() == null) {
final TorrentFile torrentFile = metadata.getFiles().get(0);
infoTable.put(FILE_LENGTH, new BEValue(torrentFile.size));
putOptionalIfPresent(infoTable, MD5_SUM, torrentFile.md5Hash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ private BEValue doBuild() throws IOException {
Map<String, BEValue> info = new HashMap<String, BEValue>();
info.put(PIECE_LENGTH, new BEValue(pieceLength));
info.put(PIECES, concatHashes(hashingResult.getHashes()));
info.put(PRIVATE, new BEValue(isPrivate ? 1 : 0));
if (isPrivate) {
info.put(PRIVATE, new BEValue(1));
}
info.put(NAME, new BEValue(name));
if (isSingleMode) {
Long sourceSize = hashingResult.getSourceSizes().get(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void parseTest() throws IOException {

assertEquals(torrentMetadata.getPieceLength(), 4);
assertEquals(torrentMetadata.getAnnounce(), "http://localhost/announce");
assertEquals(torrentMetadata.getDirectoryName(), "test.file");
assertEquals(torrentMetadata.getName(), "test.file");
assertNull(torrentMetadata.getAnnounceList());

List<BEValue> announceList = new ArrayList<BEValue>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import com.turn.ttorrent.Constants;
import com.turn.ttorrent.bcodec.BEValue;
import com.turn.ttorrent.common.TorrentMetadata;
import com.turn.ttorrent.common.TorrentParser;
import com.turn.ttorrent.common.TorrentSerializer;
import com.turn.ttorrent.common.TorrentUtils;
import org.testng.annotations.Test;

Expand All @@ -33,10 +36,10 @@
public class MetadataBuilderTest {

public void testMultiFileModeWithOneFile() throws IOException {
Map<String, BEValue> map = new MetadataBuilder()
MetadataBuilder builder = new MetadataBuilder()
.setDirectoryName("root")
.addDataSource(new ByteArrayInputStream(new byte[]{1, 2}), "path/some_file", true)
.buildBEP().getMap();
.addDataSource(new ByteArrayInputStream(new byte[]{1, 2}), "path/some_file", true);
Map<String, BEValue> map = builder.buildBEP().getMap();
Map<String, BEValue> info = map.get(INFO_TABLE).getMap();
assertEquals(info.get(NAME).getString(), "root");
List<BEValue> files = info.get(FILES).getList();
Expand All @@ -53,11 +56,40 @@ public void testMultiFileModeWithOneFile() throws IOException {
path.append("/").append(iterator.next().getString());
}
assertEquals(path.toString(), "path/some_file");

assertConsistentWithSerializer(builder.buildBinary());
assertConsistentWithSerializer(builder.build());
}

public void testMultiFileModeWithOneFileSameName() throws IOException {
MetadataBuilder builder = new MetadataBuilder()
.setDirectoryName("abc")
.addDataSource(new ByteArrayInputStream(new byte[]{1, 2}), "abc", true);
Map<String, BEValue> map = builder.buildBEP().getMap();
Map<String, BEValue> info = map.get(INFO_TABLE).getMap();
assertEquals(info.get(NAME).getString(), "abc");
List<BEValue> files = info.get(FILES).getList();
assertEquals(files.size(), 1);
Map<String, BEValue> file = files.get(0).getMap();
assertEquals(file.get(FILE_LENGTH).getInt(), 2);

StringBuilder path = new StringBuilder();
Iterator<BEValue> iterator = file.get(FILE_PATH).getList().iterator();
if (iterator.hasNext()) {
path = new StringBuilder(iterator.next().getString());
}
while (iterator.hasNext()) {
path.append("/").append(iterator.next().getString());
}
assertEquals(path.toString(), "abc");

assertConsistentWithSerializer(builder.buildBinary());
assertConsistentWithSerializer(builder.build());
}

public void testBuildWithSpecifiedHashes() throws IOException {
byte[] expectedHash = TorrentUtils.calculateSha1Hash(new byte[]{1, 2, 3});
Map<String, BEValue> metadata = new MetadataBuilder()
MetadataBuilder builder = new MetadataBuilder()
.setPiecesHashesCalculator(new PiecesHashesCalculator() {
@Override
public HashingResult calculateHashes(List<DataSourceHolder> sources, int pieceSize) {
Expand All @@ -69,23 +101,26 @@ public HashingResult calculateHashes(List<DataSourceHolder> sources, int pieceSi
Collections.singletonList("file"),
Collections.singletonList(42L))
.setPieceLength(512)
.setTracker("http://localhost:12346")
.buildBEP().getMap();
.setTracker("http://localhost:12346");
Map<String, BEValue> metadata = builder.buildBEP().getMap();

assertEquals(metadata.get(ANNOUNCE).getString(), "http://localhost:12346");
Map<String, BEValue> info = metadata.get(INFO_TABLE).getMap();
assertEquals(info.get(PIECES).getBytes(), expectedHash);
assertEquals(info.get(NAME).getString(), "file");
assertEquals(info.get(FILE_LENGTH).getLong(), 42);

assertConsistentWithSerializer(builder.buildBinary());
assertConsistentWithSerializer(builder.build());
}

public void testSingleFile() throws IOException {

byte[] data = {1, 2, 12, 4, 5};
Map<String, BEValue> metadata = new MetadataBuilder()
MetadataBuilder builder = new MetadataBuilder()
.addDataSource(new ByteArrayInputStream(data), "singleFile.txt", true)
.setTracker("http://localhost:12346")
.buildBEP().getMap();
.setTracker("http://localhost:12346");
Map<String, BEValue> metadata = builder.buildBEP().getMap();
assertEquals(metadata.get(ANNOUNCE).getString(), "http://localhost:12346");
assertNull(metadata.get(CREATION_DATE_SEC));
assertNull(metadata.get(COMMENT));
Expand All @@ -98,17 +133,19 @@ public void testSingleFile() throws IOException {
assertEquals(info.get(FILE_LENGTH).getInt(), data.length);
assertEquals(info.get(NAME).getString(), "singleFile.txt");

assertConsistentWithSerializer(builder.buildBinary());
assertConsistentWithSerializer(builder.build());
}

public void testMultiFileWithOneFileValues() throws IOException {

byte[] data = {34, 2, 12, 4, 5};
List<String> paths = Arrays.asList("unix/path", "win\\path");
Map<String, BEValue> metadata = new MetadataBuilder()
MetadataBuilder builder = new MetadataBuilder()
.addDataSource(new ByteArrayInputStream(data), paths.get(0), true)
.addDataSource(new ByteArrayInputStream(data), paths.get(1), true)
.setDirectoryName("downloadDirName")
.buildBEP().getMap();
.setDirectoryName("downloadDirName");
Map<String, BEValue> metadata = builder.buildBEP().getMap();

Map<String, BEValue> info = metadata.get(INFO_TABLE).getMap();
assertEquals(info.get(PIECES).getBytes().length, Constants.PIECE_HASH_SIZE);
Expand All @@ -131,5 +168,32 @@ public void testMultiFileWithOneFileValues() throws IOException {
}
}

assertConsistentWithSerializer(builder.buildBinary());
assertConsistentWithSerializer(builder.build());
}

private static void assertConsistentWithSerializer(TorrentMetadata metadata) throws IOException {
assertConsistentWithSerializer(new TorrentSerializer().serialize(metadata));
}

private static void assertConsistentWithSerializer(byte[] binary) throws IOException {
TorrentMetadata metadata = new TorrentParser().parse(binary);
byte[] backToBinary = new TorrentSerializer().serialize(metadata);
assertEquals(binary, backToBinary,
"\n Before:" + printable(binary) +
" \n After :" + printable(backToBinary) + "\n"
);
}

private static String printable(byte[] binary) {
StringBuilder buf = new StringBuilder();
for (byte b : binary) {
if ((' ' <= b) && (b <= 'z')) {
buf.append((char) b);
} else {
buf.append('.');
}
}
return buf.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ private void validatePieceAsync(final SharedTorrent torrent, final Piece piece,
isTorrentComplete = torrent.isComplete();

if (isTorrentComplete) {
logger.info("Download of {} complete.", torrent.getDirectoryName());
logger.info("Download of {} complete.", torrent.getName());

torrent.finish();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ private void hashSingleThread() {
initPieces();

logger.debug("Analyzing local data for {} with {} threads...",
myTorrentMetadata.getDirectoryName(), TorrentCreator.HASHING_THREADS_COUNT);
myTorrentMetadata.getName(), TorrentCreator.HASHING_THREADS_COUNT);
for (int idx = 0; idx < this.pieces.length; idx++) {
byte[] hash = new byte[Constants.PIECE_HASH_SIZE];
this.piecesHashes.get(hash);
Expand All @@ -268,7 +268,7 @@ private void hashSingleThread() {
}

public synchronized void close() {
logger.trace("Closing torrent", myTorrentMetadata.getDirectoryName());
logger.trace("Closing torrent", myTorrentMetadata.getName());
try {
this.pieceStorage.close();
isFileChannelOpen = false;
Expand All @@ -279,7 +279,7 @@ public synchronized void close() {
}

public synchronized void closeFully() {
logger.trace("Closing torrent", myTorrentMetadata.getDirectoryName());
logger.trace("Closing torrent", myTorrentMetadata.getName());
try {
this.pieceStorage.closeFully();
isFileChannelOpen = false;
Expand Down Expand Up @@ -745,6 +745,11 @@ public String toString() {
"}";
}

@Override
public String getName() {
return myTorrentMetadata.getName();
}

@Override
public String getDirectoryName() {
return myTorrentMetadata.getDirectoryName();
Expand Down