chg: replaced ant with gradle
This commit is contained in:
parent
cee0be52e0
commit
5cbeb9f01d
609 changed files with 87626 additions and 638 deletions
429
modules/jala/code/BitTorrent.js
Normal file
429
modules/jala/code/BitTorrent.js
Normal file
|
@ -0,0 +1,429 @@
|
|||
//
|
||||
// Jala Project [http://opensvn.csie.org/traccgi/jala]
|
||||
//
|
||||
// Copyright 2004 ORF Online und Teletext GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the ``License'');
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an ``AS IS'' BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// $Revision$
|
||||
// $LastChangedBy$
|
||||
// $LastChangedDate$
|
||||
// $HeadURL$
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* @fileoverview Fields and methods of the jala.BitTorrent class.
|
||||
*/
|
||||
|
||||
|
||||
// Define the global namespace for Jala modules
|
||||
if (!global.jala) {
|
||||
global.jala = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
app.addRepository("modules/core/String.js");
|
||||
app.addRepository("modules/helma/File.js");
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new BitTorrent file.
|
||||
* @class This class provides methods to create a BitTorrent
|
||||
* metadata file from any desired file.
|
||||
* @param {String} trackerUrl The URL string of the tracker.
|
||||
* @param {String} filePath The path to the original file.
|
||||
* @returns A new BitTorrent file.
|
||||
* @constructor
|
||||
*/
|
||||
jala.BitTorrent = function(filePath, trackerUrl) {
|
||||
var self = this;
|
||||
self.arguments = arguments;
|
||||
|
||||
// FIXME: Add support for multitracker mode as specified in
|
||||
// http://www.bittornado.com/docs/multitracker-spec.txt
|
||||
|
||||
var torrent, sourceFile, torrentFile;
|
||||
var pieceLength = 256;
|
||||
|
||||
var updateTorrent = function() {
|
||||
if (torrent.info) {
|
||||
return torrent;
|
||||
}
|
||||
|
||||
var file = new java.io.File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw Error("File " + file + " does not exist!");
|
||||
}
|
||||
|
||||
var md5 = java.security.MessageDigest.getInstance("MD5");
|
||||
var sha1 = java.security.MessageDigest.getInstance("SHA-1");
|
||||
|
||||
var fis = new java.io.FileInputStream(file);
|
||||
var bis = new java.io.BufferedInputStream(fis);
|
||||
var cache = new java.io.ByteArrayOutputStream();
|
||||
|
||||
var pieces = [];
|
||||
var length = pieceLength * 1024;
|
||||
var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, length);
|
||||
|
||||
while (bis.read(buffer, 0, buffer.length) > -1) {
|
||||
app.debug("Updating SHA-1 hash with " + buffer.length + " bytes");
|
||||
sha1.reset();
|
||||
sha1["update(byte[])"](buffer);
|
||||
cache["write(byte[])"](buffer);
|
||||
pieces.push(new java.lang.String(sha1.digest()));
|
||||
}
|
||||
|
||||
bis.close();
|
||||
fis.close();
|
||||
|
||||
torrent.info = {
|
||||
//md5sum: new java.lang.String(md5.digest(cache.toByteArray())),
|
||||
length: cache.size(),
|
||||
name: file.getName(),
|
||||
"piece length": length,
|
||||
pieces: pieces.join("")
|
||||
};
|
||||
|
||||
return torrent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all available property names.
|
||||
* @returns The list of property names.
|
||||
* @type Array
|
||||
*/
|
||||
this.keys = function() {
|
||||
var keys = [];
|
||||
for (var i in torrent) {
|
||||
keys.push(i);
|
||||
}
|
||||
keys.sort();
|
||||
return keys;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a torrent property.
|
||||
* @param {String} name The name of the property.
|
||||
* @returns The value of the property.
|
||||
*/
|
||||
this.get = function(name) {
|
||||
return torrent[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a torrent property.
|
||||
* @param {String} name The name of the property.
|
||||
* @param {Object} value The property's value.
|
||||
*/
|
||||
this.set = function(name, value) {
|
||||
if (typeof torrent[name] == "undefined") {
|
||||
throw Error("Cannot set torrent property " + name);
|
||||
}
|
||||
torrent[name] = value;
|
||||
delete torrent.info;
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the creation date of the torrent.
|
||||
* @returns The torrent's creation date.
|
||||
* @type Date
|
||||
*/
|
||||
this.getCreationDate = function() {
|
||||
return new Date(torrent["creation date"] * 1000);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the creation date of the torrent.
|
||||
* @param {Date} date The desired creation date.
|
||||
*/
|
||||
this.setCreationDate = function(date) {
|
||||
this.set("creation date", Math.round((date || new Date()).getTime() / 1000));
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the piece length of the torrent.
|
||||
* @returns The torrent's piece length.
|
||||
* @type Number
|
||||
*/
|
||||
this.getPieceLength = function() {
|
||||
return pieceLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the piece length of the torrent.
|
||||
* @param {Number} length The desired piece length.
|
||||
*/
|
||||
this.setPieceLength = function(length) {
|
||||
pieceLength = length;
|
||||
delete torrent.info;
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the underlying torrent file.
|
||||
* @returns The torrent file.
|
||||
* @type helma.File
|
||||
*/
|
||||
this.getTorrentFile = function() {
|
||||
return torrentFile;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the underlying source file.
|
||||
* @returns The source file.
|
||||
* @type helma.File
|
||||
*/
|
||||
this.getSourceFile = function() {
|
||||
return sourceFile;
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the torrent as file.
|
||||
* @param {String} filename An optional name for the torrent file.
|
||||
* If no name is given it will be composed from name of source
|
||||
* file as defined in the torrent plus the ending ".torrent".
|
||||
*/
|
||||
this.save = function(filename) {
|
||||
updateTorrent();
|
||||
if (!filename) {
|
||||
filename = torrent.info.name + ".torrent";
|
||||
}
|
||||
torrentFile = new helma.File(sourceFile.getParent(), filename);
|
||||
torrentFile.remove();
|
||||
torrentFile.open();
|
||||
torrentFile.write(jala.BitTorrent.bencode(torrent));
|
||||
torrentFile.close();
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a string representation of the torrent.
|
||||
* @returns The torrent as string.
|
||||
* @type String
|
||||
*/
|
||||
this.toString = function() {
|
||||
return "[jala.BitTorrent " + filePath + "]";
|
||||
};
|
||||
|
||||
if (String(filePath).endsWith(".torrent")) {
|
||||
torrentFile = new helma.File(filePath);
|
||||
torrent = jala.BitTorrent.bdecode(torrentFile.readAll());
|
||||
sourceFile = new helma.File(torrent.info.name);
|
||||
} else {
|
||||
torrent = {
|
||||
announce: trackerUrl || null,
|
||||
"announce-list": null,
|
||||
"creation date": null,
|
||||
comment: null,
|
||||
"created by": null,
|
||||
};
|
||||
this.setCreationDate();
|
||||
sourceFile = new helma.File(filePath);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The bencode method. Turns an arbitrary JavaScript
|
||||
* object structure into a corresponding encoded
|
||||
* string.
|
||||
* @param {Object} obj The target JavaScript object.
|
||||
* @returns The encoded string.
|
||||
* @type String
|
||||
*/
|
||||
jala.BitTorrent.bencode = function(obj) {
|
||||
var bencode = arguments.callee;
|
||||
var str = obj.toString();
|
||||
res.push();
|
||||
switch (obj.constructor) {
|
||||
case Array:
|
||||
res.write("l");
|
||||
for (var i in obj) {
|
||||
if (obj[i])
|
||||
res.write(bencode(obj[i]));
|
||||
}
|
||||
res.write("e");
|
||||
break;
|
||||
|
||||
case Number:
|
||||
res.write("i" + str + "e");
|
||||
break;
|
||||
|
||||
case Object:
|
||||
res.write("d");
|
||||
var keys = [];
|
||||
for (var i in obj) {
|
||||
keys.push(i);
|
||||
}
|
||||
keys.sort();
|
||||
var key;
|
||||
for (var i in keys) {
|
||||
key = keys[i];
|
||||
if (obj[key]) {
|
||||
res.write(bencode(key));
|
||||
res.write(bencode(obj[key]));
|
||||
}
|
||||
}
|
||||
res.write("e");
|
||||
break;
|
||||
|
||||
default:
|
||||
res.write(str.length + ":" + str);
|
||||
}
|
||||
return res.pop();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The bdecode method. Turns an encoded string into
|
||||
* a corresponding JavaScript object structure.
|
||||
* FIXME: Handle with caution...
|
||||
* @param {String} code The encoded string.
|
||||
* @returns The decoded JavaScript structure.
|
||||
* @type Object
|
||||
*/
|
||||
jala.BitTorrent.bdecode = function(code) {
|
||||
var DICTIONARY = "d";
|
||||
var LIST = "l";
|
||||
var INTEGER = "i";
|
||||
var STRING = "s";
|
||||
var END = "e";
|
||||
|
||||
var stack = [];
|
||||
var overflowCounter = 0;
|
||||
|
||||
var position = -1, current;
|
||||
|
||||
function getResult() {
|
||||
update();
|
||||
var result;
|
||||
switch (current) {
|
||||
case DICTIONARY:
|
||||
result = bdecodeDictionary();
|
||||
break;
|
||||
case LIST:
|
||||
result = bdecodeList();
|
||||
break;
|
||||
case INTEGER:
|
||||
result = bdecodeInteger();
|
||||
break;
|
||||
case END:
|
||||
case null:
|
||||
//res.debug("*** end detected in getResult()");
|
||||
result = null;
|
||||
break;
|
||||
default:
|
||||
result = bdecodeString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function update() {
|
||||
position += 1;
|
||||
current = code.charAt(position);
|
||||
/* res.debug("stack: " + stack);
|
||||
res.debug("position: " + position);
|
||||
res.debug("current: " + current);
|
||||
res.debug("remains: " + code.substr(position)); */
|
||||
return;
|
||||
}
|
||||
|
||||
function overflow() {
|
||||
if (overflowCounter++ > 100)
|
||||
throw Error("Error parsing bdecoded string");
|
||||
return false;
|
||||
}
|
||||
|
||||
function bdecodeDictionary() {
|
||||
stack.push(DICTIONARY);
|
||||
var dictionary = {}, key, value;
|
||||
while (current && !overflow()) {
|
||||
key = getResult();
|
||||
if (key === null)
|
||||
break;
|
||||
value = getResult();
|
||||
if (key && value)
|
||||
dictionary[key] = value;
|
||||
else
|
||||
break;
|
||||
}
|
||||
stack.pop();
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
function bdecodeList() {
|
||||
stack.push(LIST);
|
||||
var list = [], value;
|
||||
while (current && !overflow()) {
|
||||
var value = getResult();
|
||||
if (value)
|
||||
list.push(value);
|
||||
else
|
||||
break;
|
||||
}
|
||||
stack.pop();
|
||||
return list;
|
||||
}
|
||||
|
||||
function bdecodeInteger() {
|
||||
var integer = "";
|
||||
stack.push(integer);
|
||||
while (current && !overflow()) {
|
||||
update();
|
||||
if (current == "e")
|
||||
break;
|
||||
integer += current;
|
||||
}
|
||||
if (isNaN(integer))
|
||||
throw("Error in bdecoded integer: " + integer + " is not a number");
|
||||
//res.debug("integer = " + integer);
|
||||
stack.pop();
|
||||
return parseInt(integer);
|
||||
}
|
||||
|
||||
function bdecodeString() {
|
||||
var length = current, string = "";
|
||||
stack.push(string);
|
||||
update();
|
||||
while (current && current != ":" && !overflow()) {
|
||||
length += current;
|
||||
update();
|
||||
}
|
||||
if (isNaN(length))
|
||||
throw("Error in bdecoded string: invalid length " + length);
|
||||
//res.debug("length = " + length);
|
||||
length = parseInt(length);
|
||||
if (length > code.length - position)
|
||||
throw Error("Error parsing bdecoded string");
|
||||
for (var i=0; i<length; i+=1) {
|
||||
update();
|
||||
string += current;
|
||||
}
|
||||
//res.debug("string = " + string);
|
||||
if (string == "creation date")
|
||||
void(null);
|
||||
stack.pop();
|
||||
return string;
|
||||
}
|
||||
|
||||
return getResult();
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue