forked from LeenkxTeam/LNXSDK
116 lines
2.8 KiB
Haxe
116 lines
2.8 KiB
Haxe
package leenkx.network.torrent;
|
|
|
|
import haxe.io.Bytes;
|
|
|
|
class Magnet {
|
|
public var infoHash:Bytes;
|
|
public var infoHashHex:String;
|
|
public var infoHashV2:Bytes;
|
|
public var infoHashV2Hex:String;
|
|
public var name:String = null;
|
|
public var trackers:Array<String> = [];
|
|
public var webSeeds:Array<String> = [];
|
|
|
|
public function new() {}
|
|
|
|
public static function parse(uri:String):Magnet {
|
|
if (uri.indexOf("magnet:?") != 0) return null;
|
|
var m = new Magnet();
|
|
var query = uri.substr(8);
|
|
for (param in query.split("&")) {
|
|
var eq = param.indexOf("=");
|
|
if (eq < 0) continue;
|
|
var key = param.substr(0, eq);
|
|
var value = StringTools.urlDecode(param.substr(eq + 1));
|
|
switch (key) {
|
|
case "xt":
|
|
if (value.indexOf("urn:btih:") == 0) {
|
|
var hash = value.substr(9);
|
|
m.infoHash = decodeBtih(hash);
|
|
if (m.infoHash != null) {
|
|
m.infoHashHex = Crypto.toHex(m.infoHash);
|
|
}
|
|
} else if (value.indexOf("urn:btmh:") == 0) {
|
|
var hash = value.substr(9);
|
|
if (hash.length == 68) hash = hash.substr(4);
|
|
if (hash.length == 64) {
|
|
try {
|
|
m.infoHashV2 = Bytes.ofHex(hash);
|
|
} catch(e:Dynamic) {}
|
|
if (m.infoHashV2 != null) {
|
|
m.infoHashV2Hex =
|
|
Crypto.toHex(m.infoHashV2);
|
|
}
|
|
}
|
|
}
|
|
case "dn":
|
|
m.name = value;
|
|
case "tr":
|
|
m.trackers.push(value);
|
|
case "ws":
|
|
m.webSeeds.push(value);
|
|
default:
|
|
}
|
|
}
|
|
if (m.infoHash == null && m.infoHashV2 == null) return null;
|
|
return m;
|
|
}
|
|
|
|
public static function build(infoHash:Bytes, name:String, trackers:Array<String>, ?webSeeds:Array<String>, ?infoHashV2:Bytes):String {
|
|
var parts:Array<String> = [];
|
|
if (infoHash != null) {
|
|
parts.push("xt=urn:btih:" + Crypto.toHex(infoHash));
|
|
}
|
|
if (infoHashV2 != null) {
|
|
parts.push("xt=urn:btmh:1220"
|
|
+ Crypto.toHex(infoHashV2));
|
|
}
|
|
if (name != null) {
|
|
parts.push("dn=" + StringTools.urlEncode(name));
|
|
}
|
|
if (trackers != null) {
|
|
for (t in trackers) {
|
|
parts.push("tr=" + StringTools.urlEncode(t));
|
|
}
|
|
}
|
|
if (webSeeds != null) {
|
|
for (w in webSeeds) {
|
|
parts.push("ws=" + StringTools.urlEncode(w));
|
|
}
|
|
}
|
|
return "magnet:?" + parts.join("&");
|
|
}
|
|
|
|
static function decodeBtih(s:String):Bytes {
|
|
if (s.length == 40) {
|
|
try {
|
|
return Bytes.ofHex(s);
|
|
} catch(e:Dynamic) {
|
|
return null;
|
|
}
|
|
}
|
|
if (s.length == 32) {
|
|
return base32Decode(s.toUpperCase());
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static function base32Decode(s:String):Bytes {
|
|
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
var out = new haxe.io.BytesBuffer();
|
|
var buffer = 0;
|
|
var bits = 0;
|
|
for (i in 0...s.length) {
|
|
var v = alphabet.indexOf(s.charAt(i));
|
|
if (v < 0) return null;
|
|
buffer = (buffer << 5) | v;
|
|
bits += 5;
|
|
if (bits >= 8) {
|
|
bits -= 8;
|
|
out.addByte((buffer >> bits) & 0xFF);
|
|
}
|
|
}
|
|
return out.getBytes();
|
|
}
|
|
}
|