-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTorrentInfo.java
More file actions
executable file
·230 lines (198 loc) · 8.07 KB
/
Copy pathTorrentInfo.java
File metadata and controls
executable file
·230 lines (198 loc) · 8.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
* RUBTClient is a BitTorrent client written at Rutgers University for
* instructional use.
* Copyright (C) 2009 Robert Moore II
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package edu.rutgers.cs.cs352.bt;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import edu.rutgers.cs.cs352.bt.exceptions.BencodingException;
import edu.rutgers.cs.cs352.bt.util.Bencoder2;
/**
* This is a data structure class that extracts basic information from a bencoded torrent metainfo
* file and stores it in public fields. Note that this class only works for torrent metainfo files
* for a single-file torrent.
*
* @author Robert Moore II
*
*/
public class TorrentInfo
{
/**
* Key used to retrieve the info dictionary from the torrent metainfo file.
*/
public final static ByteBuffer KEY_INFO = ByteBuffer.wrap(new byte[]
{ 'i', 'n', 'f', 'o' });
/**
* Key used to retrieve the length of the torrent.
*/
public final static ByteBuffer KEY_LENGTH = ByteBuffer.wrap(new byte[]
{ 'l', 'e', 'n', 'g', 't', 'h' });
/**
* Key used to retrieve the piece hashes.
*/
public final static ByteBuffer KEY_PIECES = ByteBuffer.wrap(new byte[]
{ 'p', 'i', 'e', 'c', 'e', 's' });
/**
* Key used to retrieve the file name.
*/
public final static ByteBuffer KEY_NAME = ByteBuffer.wrap(new byte[]
{ 'n', 'a', 'm', 'e' });
/**
* Key used to retrieve the default piece length.
*/
public final static ByteBuffer KEY_PIECE_LENGTH = ByteBuffer.wrap(new byte[]
{ 'p', 'i', 'e', 'c', 'e', ' ', 'l', 'e', 'n', 'g', 't', 'h' });
/**
* ByteBuffer to retrieve the announce URL from the metainfo dictionary.
*/
public static final ByteBuffer KEY_ANNOUNCE = ByteBuffer.wrap(new byte[] {'a','n','n','o','u','n','c','e'});
/**
* A byte array containing the raw bytes of the torrent metainfo file.
*/
public final byte[] torrent_file_bytes;
/**
* The base dictionary of the torrent metainfo file.
* See <a href="http://www.bittorrent.org/beps/bep_0003.html">http://www.bittorrent.org/beps/bep_0003.html</a>
* for an explanation of what keys are available and how they map.
*/
public final Map<ByteBuffer,Object> torrent_file_map;
/**
* The unbencoded info dictionary of the torrent metainfo file.
* See <a href="http://www.bittorrent.org/beps/bep_0003.html">http://www.bittorrent.org/beps/bep_0003.html</a> for
* an explanation of what keys are available and how they map.
*/
public final Map<ByteBuffer,Object> info_map;
/**
* The SHA-1 hash of the bencoded form of the info dictionary from the torrent metainfo file.
*/
public final ByteBuffer info_hash;
/**
* The base URL of the tracker for client scrapes.
*/
public final URL announce_url;
/**
* The default length of each piece in bytes. Note that the last piece may be irregularly-sized (less than the value of piece_length)
* if the file size is not a multiple of the piece size.
*/
public final int piece_length;
/**
* The name of the file referenced in the torrent metainfo file.
*/
public final String file_name;
/**
* The length of the file in bytes.
*/
public final int file_length;
/**
* The SHA-1 hashes of each piece of the file.
*/
public final ByteBuffer[] piece_hashes;
/**
* Creates a new TorrentInfo object from the specified byte array. If the byte array is {@code null} or
* has a length of 0(zero), then an {@code IllegalArgumentException} is thrown.
* @param torrent_file_bytes
* @throws BencodingException
*/
@SuppressWarnings("unchecked")
public TorrentInfo(byte[] torrent_file_bytes) throws BencodingException
{
// Make sure the input is valid
if(torrent_file_bytes == null || torrent_file_bytes.length == 0)
throw new IllegalArgumentException("Torrent file bytes must be non-null and have at least 1 byte.");
// Assign the byte array
this.torrent_file_bytes = torrent_file_bytes;
// Assign the metainfo map
this.torrent_file_map = (Map<ByteBuffer,Object>)Bencoder2.decode(torrent_file_bytes);
// Try to extract the announce URL
ByteBuffer url_buff = (ByteBuffer)this.torrent_file_map.get(TorrentInfo.KEY_ANNOUNCE);
if(url_buff == null)
throw new BencodingException("Could not retrieve anounce URL from torrent metainfo. Corrupt file?");
try {
String url_string = new String(url_buff.array(), "ASCII");
URL announce_url = new URL(url_string);
this.announce_url = announce_url;
}
catch(UnsupportedEncodingException uee)
{
throw new BencodingException(uee.getLocalizedMessage());
}
catch(MalformedURLException murle)
{
throw new BencodingException(murle.getLocalizedMessage());
}
// Try to extract the info dictionary
ByteBuffer info_bytes = Bencoder2.getInfoBytes(torrent_file_bytes);
Map<ByteBuffer,Object> info_map = (Map<ByteBuffer,Object>)this.torrent_file_map.get(TorrentInfo.KEY_INFO);
if(info_map == null)
throw new BencodingException("Could not extract info dictionary from torrent metainfo dictionary. Corrupt file?");
this.info_map = info_map;
// Try to generate the info hash value
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(info_bytes.array());
byte[] info_hash = digest.digest();
this.info_hash = ByteBuffer.wrap(info_hash);
}
catch(NoSuchAlgorithmException nsae)
{
throw new BencodingException(nsae.getLocalizedMessage());
}
// Extract the piece length from the info dictionary
Integer piece_length = (Integer)this.info_map.get(TorrentInfo.KEY_PIECE_LENGTH);
if(piece_length == null)
throw new BencodingException("Could not extract piece length from info dictionary. Corrupt file?");
this.piece_length = piece_length.intValue();
// Extract the file name from the info dictionary
ByteBuffer name_bytes = (ByteBuffer)this.info_map.get(TorrentInfo.KEY_NAME);
if(name_bytes == null)
throw new BencodingException("Could not retrieve file name from info dictionary. Corrupt file?");
try {
this.file_name = new String(name_bytes.array(),"ASCII");
}
catch(UnsupportedEncodingException uee)
{
throw new BencodingException(uee.getLocalizedMessage());
}
// Extract the file length from the info dictionary
Integer file_length = (Integer)this.info_map.get(TorrentInfo.KEY_LENGTH);
if(file_length == null)
throw new BencodingException("Could not extract file length from info dictionary. Corrupt file?");
this.file_length = file_length.intValue();
// Extract the piece hashes from the info dictionary
ByteBuffer all_hashes = (ByteBuffer)this.info_map.get(TorrentInfo.KEY_PIECES);
if(all_hashes == null)
throw new BencodingException("Could not extract piece hashes from info dictionary. Corrupt file?");
byte[] all_hashes_array = all_hashes.array();
// Verify that the length of the array is a multiple of 20 bytes (160 bits)
if(all_hashes_array.length % 20 != 0)
throw new BencodingException("Piece hashes length is not a multiple of 20. Corrupt file?");
int num_pieces = all_hashes_array.length / 20;
// Copy the values of the piece hashes into the local field
this.piece_hashes = new ByteBuffer[num_pieces];
for(int i = 0; i < num_pieces; i++)
{
byte[] temp_buff = new byte[20];
System.arraycopy(all_hashes_array,i*20,temp_buff,0,20);
this.piece_hashes[i] = ByteBuffer.wrap(temp_buff);
}
}
}