# addkey v0.3 by blackmail
# Oct 30, 2010
#
# Another delphine key leecher -
# edited from delphine plugin by eternalharvest, based on original patches and scripts by ToXCiL
# http://openkore.svn.sourceforge.net/viewvc/openkore/plugins/delphine/trunk/
# http://forums.openkore.com/viewtopic.php?p=35377#p35377
#
# credits to Smith_ for warpra autokey (sori kodenya banyak yang ane contek, hehe)
# http://www.d-bests.com/viewtopic.php?f=46&t=12540#p102448
#
# This source code is licensed under the
# GNU General Public License, Version 2.
# See http://www.gnu.org/licenses/gpl.html
#
# CONTOH CONFIG:
# delphine 1
# delphine_web_1 http://key.d-bests.com
# delphine_web_2 http://www.facebook.com/topic.php?uid=107506092605605&topic=1011, tail
# delphine_web_3 http://dwarna-ro.blog****.com/feeds/3019710138396137383/comments/default
package addkey;
use strict;
# use Data::Dumper;
# use warnings;
use Globals qw/$net %config/;
use Log qw/message warning error debug/;
use Translation qw/T TF/;
use Utils qw/timeOut getFormattedDate getHex/;
use Utils::HttpReader;
use constant {
NAME => 'addkey',
CF_KEY => 'delphine-key.txt',
};
{
my $hooks = Plugins::addHooks(
['start3', \&start],
['Commands::cmdConf', \&onCmdConf],
['postloadfiles', \&parseConfig],
['Network::serverRecv', \&serverRecv], # hook in DirectConnection
);
Plugins::register(NAME, T('Another delphine-key leecher'), sub { Plugins::delHooks($hooks) });
}
our $cf_key;
our @key;
our $encrypter = sub {};
our @check; # hold delphine-key.txt n web to check
# Check delphine config, add table file.
sub start {
$cf_key = Settings::addTableFile(CF_KEY, loader => [\&loadKey, \@key], mustExist => 0);
my %keyCheck = (
'delphine' => 1,
'delphine_web_1' => 'http://key.eliteenterpriseidro.com/',
'delphine_web_2' => 'http://www.facebook.com/topic.php?uid=107506092605605&topic=1011, tail');
foreach my $k (keys %keyCheck) {
if (!exists($config{$k})) {
error TF("Cannot find %s on config file. Setting to default (%s)\n", $k, $keyCheck{$k}), NAME;
Misc::configModify($k, $keyCheck{$k});
delete $keyCheck{$k};
}
}
&parseConfig() if !%keyCheck;
}
# check url when delphine config called
sub onCmdConf {
my (undef, $args) = @_;
return unless ($args->{key} =~ m/^delphine_web_(\d+)$/g && exists $config{$args->{key}} && ${$args->{val}});
my $i = $1;
my ($url, $tail) = split / *, */, ${$args->{val}};
$tail =~ s/\s+//g;
$tail = lc($tail) eq 'tail'? 1 : 0;
$url = urlCheck($url);
if ($url) {
$check[$i]->{url} = $url;
$check[$i]->{tail} = 1;
} else {
error TF("Invalid! (%s)\n", $url), NAME;
${$args->{val}} = undef;
}
}
# push url from delphine config into @check
sub parseConfig {
@check = ();
push(@check, 'self');
for (my $i = 1; exists $config{"delphine_web_$i"}; $i++) {
my ($url, $tail) = split / *, */, $config{"delphine_web_$i"};
my $print;
if ($config{"delphine_web_$i"}) {
$url = urlCheck($url);
} else {
$print = TF("Config delphine_web_%s has no value!\n", $i);
}
if ($url) {
push (@check, {'url' => $url, 'tail' => $tail});
} elsif (!$print) {
$print = TF("Unknown value (%s) on config delphine_web_%s!\n", $config{"delphine_web_$i"}, $i);
Misc::configModify("delphine_web_$i", undef);
}
error($print, NAME) if $print;
}
}
# Encrypt Delphine
sub serverRecv {
my (undef, $args) = @_;
return unless ($net->getState < Network::CONNECTED_TO_MASTER_SERVER && $config{'delphine'});
if (length(${$args->{msg}}) == 20) {
my $print = "Our delphine key expired!\n";
foreach my $site (@check) {
my $msg = ${$args->{msg}};
my $expired;
if ($site eq 'self'){
Settings::loadByHandle($cf_key);
} elsif (my $temp = &checkWeb($site->{url}, $site->{tail})) {
$expired = $temp;
$print = TF("Key from %s expired!\n", $site->{url});
}
if (@key) {
# Guess missing key
my %testKey;
map { $testKey{$_} = 1 } (0..255);
my @check;
for (my $i = 0; $i < @key; $i++) {
exists $testKey{$key[$i]} ? delete $testKey{$key[$i]} : push @check, $i;
}
map { $key[shift @check] = $_} keys %testKey;
$encrypter = eval sprintf 'sub { ${$_[0]} =~ y/\x00-\xff/%s/ }', join '', map { sprintf '\x%.2x', $_ } @key;
}
$encrypter->(\$msg);
if (getHex($msg) =~ m/^DC 01 14 00/g) {
warning ("OK!\n", NAME);
&writeKey($site->{url}, $expired) if ($site ne 'self');
last;
} else {
warning ($print, NAME) if $print;
}
}
} #else { debug TF("serverRecv hex: %s\n", getHex(${$args->{msg}})), NAME, 0 }
$encrypter->($args->{msg});
}
# urlCheck($url)
# returns: valid http url or undef
sub urlCheck {
my($value) = shift;
# check for illegal characters
return if $value =~ /[^a-z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\.\-\_\~\%]/i;
# check for hex escapes that aren't complete
return if $value =~ /%[^0-9a-f]/i; #TODO: %???
return if $value =~ /%[0-9a-f](:?[^0-9a-f]|$)/i;
$value = 'http://'. $value if ($value !~ m!^(\w+)://!i);
my($scheme, $authority, $path, $query, $fragment) = $value =~ m|(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?|;
return unless lc($scheme) eq 'http';
# fully-qualified URIs must have an authority section that is a valid host
return unless($authority);
# allow a port component
my($port) = $authority =~ /:(\d+)$/;
$authority =~ s/:\d+$//;
# re-assemble the URL per section 5.3 in RFC 3986
my $out = $scheme . ':';
$out .= '//' . $authority;
$out .= ':' . $port if $port;
$out .= $path;
$out .= '?' . $query if (defined $query && length($query));
$out .= '#' . $fragment if (defined $fragment && length($fragment));
return $out;
}
# Load Key dari delphine-key.txt
sub loadKey {
my ($file, $key) = @_;
@$key = ();
message TF("Loading key...\n"), NAME;
my $reader = new Utils::TextReader($file);
until ($reader->eof) {
local $_ = $reader->readLine;
if ($_ =~ m/^#+/) {
my $print = $_;
$print =~ s/^#+//g;
$print =~ s/[\r\n]//g; # Remove line endings
$print =~ s/^[\t\s]*//; # Remove leading tabs and whitespace
$print =~ s/\s+$//g; # Remove trailing whitespace
message TF("%s\n", $print), NAME;
next;
}
s/\s+//g;
push @$key, map hex, map /(.{2})/g, $_;
}
unless (@$key == 0x100) {
error TF("%s is in unknown format\n", $file), NAME;
@$key = ();
}
}
# checkWeb(url, [tail])
# Returns: Expired date or undef if we can't find expired date
# map found key from url to @key
sub checkWeb {
my ($web, $tail) = @_;
$tail =~ s/\s+//g;
$tail = lc($tail) eq 'tail'? 1 : 0;
my $http = new StdHttpReader($web);
message TF("Connect to %s...\n", $web), NAME;
my %timeCheck = ( 'time' => time, 'timeout' => '60');
# FIXME: ada cara lain kah??
sleep 1 until ($http->getStatus == HttpReader::DONE || $http->getStatus == HttpReader::ERROR || timeOut(\%timeCheck));
if ($http->getStatus== HttpReader::DONE) {
my $result = $http->getData($http->getSize());
my $expired;
my $tempKey;
while ($result =~ m/((([A-F0-9]{2}|[X]{2})\s*){256})/gi) {
$tempKey = $1;
my $temp = substr($`, -500,500); # FIXME: ada cara lain?!!
if ($temp =~ m/Expired\s*?((0[1-9]|[12][0-9]|3[01])[\s\S]+?)</i) {
$expired = $1;
$expired =~ s/[\n\r]+.*//g;
$expired =~ s/\[.*\]$//g;
$expired =~ s/(^\s+?)//;
$expired =~ s/\s+$//g;
$expired =~ s/\s*?((?![\x{0000}-\x{007F}]).|(&#\d+;))\s*?//g;
}
last unless $tail;
}
message TF("Expired : %s\n", $expired||"Unknown"), NAME;
if ($tempKey) {
@key = ();
$tempKey =~ s/\s+//g;
push @key, map hex, map /(.{2})/g, $tempKey;
return ($expired||'Unknown');
} else { error TF("Unable to get delphine key from %s\n", $web), NAME }
} else {
error TF("Unable to reach %s [%s]\n", $web, (timeOut(\%timeCheck) ? 'timeout' : 'HttpReader::ERROR')), NAME;
}
return;
}
# writeKey(url, [expired])
# writeKey to delphine-key.txt
sub writeKey {
my ($url, $expired) = @_;
return unless $url;
error TF("Can't find %s in folder %s\n", CF_KEY, (Settings::getTablesFolders)[1]), NAME
unless Settings::getTableFilename(CF_KEY);
message TF("Writing key...\n"), NAME;
open my $f, '>:utf8', (Settings::getTableFilename(CF_KEY)
|| File::Spec->catfile((Settings::getTablesFolders)[1], CF_KEY));
print $f "# Expired : $expired\n" if $expired;
print $f "# Log : ".getFormattedDate(time)." (".time.") from $url\n";
print $f join ' ', map { sprintf "%.2X", $_ } @key;
close $f;
}
1;