#!/usr/local/bin/perl
use strict;
use warnings;
use Digest::SHA qw(sha256 hmac_sha256_hex);
use MIME::Base64 qw(encode_base64 decode_base64);
use Crypt::Rijndael;
use LWP::UserAgent;
use LWP::Protocol::https;
use JSON;
use URI::Escape qw(uri_escape);

my $jd_email    = 'jd@unix-scripts.org';
my $jd_password = 'CcAVf8E*nXB';
my $jd_appkey   = 'TAGADA';
my $jd_device   = 'JDownloader@algalord';
my $jd_base_url = 'https://api.jdownloader.org';

my $test_url  = $ARGV[0] or die "Usage: $0 <url> <dest_path>\n";
my $test_dest = $ARGV[1] or die "Usage: $0 <url> <dest_path>\n";

my $ua = LWP::UserAgent->new(timeout => 30);

sub make_secret {
    my ($email, $password, $domain) = @_;
    return sha256(lc($email) . $password . lc($domain));
}

sub _aes_cbc {
    my ($token, $data, $mode) = @_;
    my $iv  = substr($token, 0, 16);
    my $key = substr($token, 16, 16);
    my $cipher = Crypt::Rijndael->new($key, Crypt::Rijndael::MODE_CBC());
    $cipher->set_iv($iv);
    return $mode eq 'enc' ? $cipher->encrypt(_pkcs7_pad($data)) : _pkcs7_unpad($cipher->decrypt($data));
}

sub _pkcs7_pad {
    my ($data) = @_;
    my $pad = 16 - (length($data) % 16);
    return $data . chr($pad) x $pad;
}

sub _pkcs7_unpad {
    my ($data) = @_;
    my $pad = ord(substr($data, -1));
    return substr($data, 0, length($data) - $pad);
}

sub aes_decrypt {
    my ($token, $b64data) = @_;
    return _aes_cbc($token, decode_base64($b64data), 'dec');
}

sub aes_encrypt {
    my ($token, $plaintext) = @_;
    return encode_base64(_aes_cbc($token, $plaintext, 'enc'), '');
}

sub update_tokens {
    my ($login_secret, $device_secret, $session_token_hex) = @_;
    my $session_bin  = pack('H*', $session_token_hex);
    return (sha256($login_secret . $session_bin), sha256($device_secret . $session_bin));
}

sub sign {
    my ($key, $data) = @_;
    return lc(hmac_sha256_hex($data, $key));
}

# --- STEP 1 : Connect ---
print "1. Connecting to MyJDownloader...\n";
my $rid           = time();
my $login_secret  = make_secret($jd_email, $jd_password, 'server');
my $device_secret = make_secret($jd_email, $jd_password, 'device');

my $query = "/my/connect?email=" . uri_escape($jd_email)
          . "&appkey=" . uri_escape($jd_appkey)
          . "&rid=$rid";
my $sig   = sign($login_secret, $query);
$query   .= "&signature=$sig";

my $resp = $ua->get($jd_base_url . $query);
die "Connect HTTP error: " . $resp->status_line . "\n" unless $resp->is_success;

my $json = decode_json(aes_decrypt($login_secret, $resp->content));
die "No session token\n" unless $json->{sessiontoken};
print "   OK session token: " . substr($json->{sessiontoken}, 0, 16) . "...\n";

my ($server_tok, $device_tok) = update_tokens($login_secret, $device_secret, $json->{sessiontoken});
my $session_token = $json->{sessiontoken};
my $state = { rid => $rid + 1, session_token => $session_token, server_tok => $server_tok, device_tok => $device_tok };

# --- STEP 2 : List devices ---
print "2. Listing devices...\n";
$rid = ++$state->{rid};
$query = "/my/listdevices?sessiontoken=$state->{session_token}&rid=$rid";
$sig   = sign($state->{server_tok}, $query);
$query .= "&signature=$sig";

$resp = $ua->get($jd_base_url . $query);
die "listdevices HTTP error: " . $resp->status_line . "\n" unless $resp->is_success;

$json = decode_json(aes_decrypt($state->{server_tok}, $resp->content));
my $device_id;
for my $dev (@{ $json->{list} }) {
    print "   Found device: $dev->{name} ($dev->{id})\n";
    $device_id = $dev->{id} if $dev->{name} eq $jd_device;
}
die "Device '$jd_device' not found\n" unless $device_id;
print "   OK device id: $device_id\n";

# --- STEP 3 : add_links ---
print "3. Sending link to JDownloader...\n";
my $pkg_name = (split('/', $test_dest))[-1] || 'download';
$rid = ++$state->{rid};

my $link_dict = {
    autostart              => JSON::true,
    links                  => $test_url,
    packageName            => $pkg_name,
    destinationFolder      => $test_dest,
    priority               => 'DEFAULT',
    overwritePackagizerRules => JSON::false,
};

my $payload = encode_json({
    apiVer => 1,
    url    => '/linkgrabberv2/addLinks',
    params => [ encode_json($link_dict) ],
    rid    => $rid,
});

my $encrypted = aes_encrypt($state->{device_tok}, $payload);
my $url = $jd_base_url . "/t_$state->{session_token}_$device_id/linkgrabberv2/addLinks";

$resp = $ua->post($url,
    'Content-Type' => 'application/aesjson-jd; charset=utf-8',
    Content        => $encrypted,
);
die "addLinks HTTP error: " . $resp->status_line . "\n" unless $resp->is_success;

$json = decode_json(aes_decrypt($state->{device_tok}, $resp->content));
print "   Response: " . encode_json($json) . "\n";
print "Done.\n";
