#!/usr/bin/env perl
# by virii - C3L

use strict;
use warnings;
use diagnostics;

use utf8;

use YAML::Tiny;
use XML::LibXML;
use MIME::Base64;
use Net::EasyTCP;
use AnyEvent::XMLRPC;
use experimental 'smartmatch';

##################################################################################################
my ($YAMLoutput, $YAMLbase64, $YAMLconfig);
my ($member, $memberstatus, $membercounter, $username);
my ($voting, $birthday, $address, $votingright, $votingrights);
my ($regularmember, $studentmember, $supportmember, $starvingmember, $bifröstconfig);
my ($serv, $sharedsecret, $XMLparser, $XMLfile, $XMLquery, $XMLnode, $TCPclient, $TCPreply);
my ($chaosid, $lastname, $forename, $email, $status, $date, $paid, $userbox, $keyowner, $comment);
my (@members, @usernames, %usercontacts);
my @sharedsecrets = ('secret', 'password');
##################################################################################################

##################################################################################################
$YAMLconfig    = YAML::Tiny->read( '/etc/bifröst/client-config.yml' ) || die "[-] File not found or not a valid YAML";
$bifröstconfig = $YAMLconfig->[0];
##################################################################################################

$serv = AnyEvent::XMLRPC->new(
                port    => 9090,
                uri     => "/RPC2",
                ssl     => {
                         cert_file => "/etc/midgard/cert.pem",
                         key_file  => "/etc/midgard/key.pem"
                },
                methods => {
                        'CHAOSID'     => \&CHAOSID,
                        'COUNTER'     => \&COUNTER,
                        'MEMBERINFO'  => \&MEMBERINFO,
                        'VOTINGRIGHT' => \&VOTINGRIGHT
                },
        );



sub CHAOSID {
  ($sharedsecret, $chaosid) = ("none", 0);
  ($sharedsecret, $chaosid) = @_;
  return "FUCK OFF"    unless ($sharedsecret ~~ @sharedsecrets);
  return "NO CHAOS ID" unless (length $chaosid == 36);

  _OPEN_DATABASE();

  $username = 0;

  $XMLquery  = "/memberdatebank/memberen/member[id/text() = '$chaosid']";
    foreach $XMLnode ($XMLfile->findnodes($XMLquery)) {
      $username = $XMLnode->findnodes('./nick');
    }

  return "NO SUCH USER" unless $username;

  $YAMLoutput = YAML::Tiny->new( { ChaosID => "$chaosid", Username => "$username" } );
  chomp ($YAMLbase64 = encode_base64 $YAMLoutput->write_string);
  return $YAMLbase64;
}


sub MEMBERINFO {
  ($sharedsecret, $username) = ("none", 0);
  ($sharedsecret, $username) = @_;
  return "FUCK OFF" unless ($sharedsecret ~~ @sharedsecrets);
  return "NO USER"  unless $username;

  _OPEN_DATABASE();

  ($chaosid, $lastname, $forename, $email, $status, $date, $paid, $userbox, $keyowner, $comment) = 0;

  $XMLquery  = "/memberdatebank/memberen/member[nick/text() = '$username']";
    foreach $XMLnode ($XMLfile->findnodes($XMLquery)) {
        $chaosid  = $XMLnode->findnodes('./id');
        $forename = $XMLnode->findnodes('./numm');
        $lastname = $XMLnode->findnodes('./nonumm');
        $birthday = $XMLnode->findnodes('./gebuertsdag');
        $address  = $XMLnode->findnodes('./adress');
        $email    = $XMLnode->findnodes('./email');
        $status   = $XMLnode->findnodes('./status');
        $voting   = int $XMLnode->findnodes('./stemmrecht');
        $date     = $XMLnode->findnodes('./datum');
        $paid     = int $XMLnode->findnodes('./bezuelt');
        $userbox  = int $XMLnode->findnodes('./kescht');
        $keyowner = int $XMLnode->findnodes('./badge');
        $comment  = $XMLnode->findnodes('./kommentar');
    }

  return "NO SUCH USER" unless $chaosid;

  $YAMLoutput =  YAML::Tiny->new( {
  ChaosID => "$chaosid",
  Names   => {
    'Forename' => "$forename",
    'Lastname' => "$lastname",
    'Username' => "$username"
  },
  Email  => "$email",
  Status => "$status",
  Voters => "$voting",
  More   => {
    'Address'    => "$address",
    'Birthday'   => "$birthday",
    'Storagebox' => "$userbox",
    'Payment'    => "$date",
    'Paid'       => "$paid",
    'Keyowner'   => "$keyowner",
    'Comment'    => "$comment"
  }  
  } );

  chomp ($YAMLbase64 = encode_base64 $YAMLoutput->write_string);
  return $YAMLbase64;

}


sub COUNTER {
  $sharedsecret = shift||"none";
  return "FUCK OFF" unless ($sharedsecret ~~ @sharedsecrets);

  _OPEN_DATABASE();

  ($regularmember, $supportmember, $studentmember, $starvingmember, $votingrights) = 0;

    foreach $member (@members) {
        $memberstatus = $member->findnodes('./status');
        $votingright  = $member->findnodes('./stemmrecht');   
        $votingrights++     if int $votingright; 
        $regularmember++    if ($memberstatus  =~ /^Regular$/i);
        $studentmember++    if ($memberstatus  =~ /^Student$/i);
        $supportmember++    if ($memberstatus  =~ /^Supporter/i);
        $starvingmember++   if ($memberstatus  =~ /^Starving$/i);
    }  
    

  $membercounter = scalar @members;

  $YAMLoutput = YAML::Tiny->new( { Counter => $membercounter, Voters => $votingrights, Regular => $regularmember, Student => $studentmember, Supporter => $supportmember, Starving => $starvingmember } );
  chomp ($YAMLbase64 = encode_base64 $YAMLoutput->write_string);
  return $YAMLbase64;
}


sub VOTINGRIGHT {
  $sharedsecret = shift||"none";
  return "FUCK OFF" unless ($sharedsecret ~~ @sharedsecrets);

  _OPEN_DATABASE();

  $votingrights = 0;
  
  $XMLquery  = "/memberdatebank/memberen/member[stemmrecht/text() = '1']";
    foreach $XMLnode ($XMLfile->findnodes($XMLquery)) {
      ($username, $email) = ( $XMLnode->findnodes('./nick') . '', $XMLnode->findnodes('./email') . '');  # Don't know why but I have to add "nothing" to the string to make YAML::Tiny work with XML::LibXML
      push @usernames, $username;
      $usercontacts{$username} = $email;
      $votingrights++;
    }
  

  $YAMLoutput = YAML::Tiny->new( { Voters => "$votingrights", Usernames => [@usernames], Contact => { %usercontacts } } );
  
  chomp ($YAMLbase64 = encode_base64 $YAMLoutput->write_string);
  undef @usernames;
  return $YAMLbase64;
}

##
# Internal subroutine
##

sub _OPEN_DATABASE {
  $TCPclient = new Net::EasyTCP( mode      => "client",
                                 host      => $bifröstconfig->{host},
                                 port      => $bifröstconfig->{port},
                                 password  => $bifröstconfig->{pass}
                               ) || die "[-] Cannot connect!\n";
  $TCPclient->send("SERVE") || die "[-] Error sending: $@\n";
  $TCPreply = $TCPclient->receive() || die "[-] Error receiving: $@\n";
  $XMLparser = XML::LibXML->new();
  $XMLfile   = $XMLparser->parse_string($TCPreply);
  @members   = $XMLfile->findnodes('/memberdatebank/memberen/member');
}


$serv->run;
