# Copyright 1999-2014. Parallels IP Holdings GmbH. All Rights Reserved.
package Validator;

use Logging;
use XmlNode;
use Exception::XmlNode;

sub validateCpAccountNode {
  my $cpAccountNode = shift;

  verifyAttributeExists($cpAccountNode, 'name');
  verifyAttributeExists($cpAccountNode, 'home');

  verifyChild($cpAccountNode,'suspended', 0, 1);

  my @cpLimitNodes = $cpAccountNode->getChildren('limit');
  foreach my $cpLimitNode ( @cpLimitNodes ) {
    verifyAttributeExists($cpLimitNode, 'name');
    verifyAttributeToken($cpLimitNode, 'name','disk_space','max_subdom','max_box','max_ftpusers','max_parked','max_db','max_maillists','max_traffic','max_addon','max_accounts');
  }

  my @cpPermissionNodes = $cpAccountNode->getChildren('permission');
  foreach my $cpPermissionNode ( @cpPermissionNodes ) {
    verifyAttributeExists($cpPermissionNode, 'name');
    verifyAttributeToken($cpPermissionNode, 'name','create_accounts','create_domains','manage_dns');
  }

  my @cpFeatureNodes = $cpAccountNode->getChildren('feature');
  foreach my $cpFeatureNode ( @cpFeatureNodes ) {
    verifyAttributeExists($cpFeatureNode, 'name');
    verifyAttributeToken($cpFeatureNode, 'name','cron','lists','password','parkeddomains','rawlog','subdomains','backup','ftpsetup','addondomains','statmanager','spamassassin','clamavconnector_scan');
    verifyAttributeExists($cpFeatureNode, 'allowed');
    verifyAttributeToken($cpFeatureNode, 'allowed','true','false');
  }

  verifyChild($cpAccountNode,'spamassassin', 0, 1);

  verifyChild($cpAccountNode,'ip', 0, 1);

  verifyChild($cpAccountNode,'domain', 1, 1);

  validateCpDomainNode($cpAccountNode->getChild( 'domain' ) );
  return;
}

sub validateCpDomainNode {
  my $cpDomainNode = shift;
  
  verifyAttributeExists($cpDomainNode, 'name');
  verifyAttributeExists($cpDomainNode, 'www_root');  

  verifyChild($cpDomainNode,'anonftp', 0, 1);
  my $cpAnonftpNode = $cpDomainNode->getChild('anonftp');
  if ( defined ( $cpAnonftpNode ) ) {
    verifyAttributeToken($cpAnonftpNode, 'pub', 'true', 'false');
    verifyAttributeToken($cpAnonftpNode, 'incoming', 'true', 'false');
  }

  verifyChild($cpDomainNode,'ftpuser', 1);

  my @cpFtpuserNodes = $cpDomainNode->getChildren('ftpuser');
  foreach my $cpFtpuserNode ( @cpFtpuserNodes ) {
    verifyAttributeExists($cpFtpuserNode, 'name');
  }

  my @cpPdirNodes = $cpDomainNode->getChildren('pdir');
  foreach my $cpPdirNode ( @cpPdirNodes ) {
    verifyAttributeExists($cpPdirNode, 'name');
    my @cpPduserNodes = $cpPdirNode->getChildren('pduser');
    foreach my $cpPduserNode ( @cpPduserNodes ) {
      verifyAttributeExists($cpPduserNode, 'name');
      verifyAttributeToken($cpPduserNode, 'encoding','plain','base64');
    }
  }

  my @cpSubdomainNodes = $cpDomainNode->getChildren('subdomain');
  foreach my $cpSubdomainNode ( @cpSubdomainNodes ) {
    validateCpSubdomainNode ( $cpSubdomainNode );
  }

  my @cpAddondomainNodes = $cpDomainNode->getChildren('addondomain');
  foreach my $cpAddondomainNode ( @cpAddondomainNodes ) {
    validateCpAddondomainNode ( $cpAddondomainNode );
  }

  verifyChild($cpDomainNode,'mail', 0, 1);
  my $cpMailNode = $cpDomainNode->getChild( 'mail' );
  validateCpMailNode($cpMailNode) if ( defined ( $cpMailNode ) );

  my @cpDatabaseNodes = $cpDomainNode->getChildren('database');
  foreach my $cpDatabaseNode ( @cpDatabaseNodes ) {
    validateCpDatabaseNode ( $cpDatabaseNode );
  }

  verifyChild($cpDomainNode,'maillists', 0, 1);
  my $cpMaillistsNode = $cpDomainNode->getChild( 'maillists' );
  if ( defined ( $cpMaillistsNode ) ) {
    my @cpMaillistNodes = $cpMaillistsNode->getChildren('maillist');
    foreach my $cpMaillistNode ( @cpMaillistNodes ) {
      validateCpMaillistNode ( $cpMaillistNode );
    }
  }

  verifyChild($cpDomainNode,'dns-zone', 0, 1);
  my $cpDnsZoneNode = $cpDomainNode->getChild( 'dns-zone' );
  if ( defined ( $cpDnsZoneNode ) ) {
    my @cpDnsrecNodes = $cpDnsZoneNode->getChildren('dnsrec');
    foreach my $cpDnsrecNode ( @cpDnsrecNodes ) {
      validateCpDnsrecNode ( $cpDnsrecNode );
    }
  }

  return;
}

sub validateCpSubdomainNode {
  my $cpSubdomainNode = shift;

  verifyAttributeExists($cpSubdomainNode, 'name');
  verifyAttributeExists($cpSubdomainNode, 'www_root');  

  verifyChild($cpSubdomainNode,'ftpuser', 0, 1);

  my $cpSubdomainFtpUser = $cpSubdomainNode->getChild( 'ftpuser' );
  verifyAttributeExists($cpSubdomainFtpUser, 'name') if defined ( $cpSubdomainFtpUser );

  my @cpAddondomainNodes = $cpSubdomainNode->getChildren('addondomain');
  foreach my $cpAddondomainNode ( @cpAddondomainNodes ) {
    validateCpAddondomainNode ( $cpAddondomainNode );
  }

  verifyChild($cpSubdomainNode,'mail', 0, 1);
  my $cpMailNode = $cpSubdomainNode->getChild( 'mail' );
  validateCpMailNode($cpMailNode) if ( defined ( $cpMailNode ) );
  return;
}

sub validateCpAddondomainNode {
  my $cpAddondomainNode = shift;

  verifyAttributeExists($cpAddondomainNode, 'name');

  verifyChild($cpAddondomainNode,'mail', 0, 1);
  my $cpMailNode = $cpAddondomainNode->getChild( 'mail' );
  validateCpMailNode($cpMailNode) if ( defined ( $cpMailNode ) );

  verifyChild($cpAddondomainNode,'dns-zone', 0, 1);
  my $cpDnsZoneNode = $cpAddondomainNode->getChild( 'dns-zone' );
  if ( defined ( $cpDnsZoneNode ) ) {
    my @cpDnsrecNodes = $cpDnsZoneNode->getChildren('dnsrec');
    foreach my $cpDnsrecNode ( @cpDnsrecNodes ) {
      validateCpDnsrecNode ( $cpDnsrecNode );
    }
  }

  return;
}

sub validateCpMailNode {
  my $cpMailNode = shift;

  my $cpDefaultNode = $cpMailNode->getChild( 'default' );
  verifyAttributeToken($cpDefaultNode, 'target','email','fail', 'ignore') if ( defined ( $cpDefaultNode ) );

  my @cpMailnameNodes = $cpMailNode->getChildren('mailname');
  foreach my $cpMailnameNode ( @cpMailnameNodes ) {
    validateCpMailnameNode( $cpMailnameNode );
  }

  my @cpForwardNodes = $cpMailNode->getChildren('forward');
  foreach my $cpForwardNode ( @cpForwardNodes ) {
    verifyAttributeExists($cpForwardNode, 'mailname');
    verifyAttributeExists($cpForwardNode, 'redirect');
  }

  my @cpAutoresponderNodes = $cpMailNode->getChildren('autoresponder');
  foreach my $cpAutoresponderNode ( @cpAutoresponderNodes ) {
    verifyAttributeExists($cpAutoresponderNode, 'mailname');
    verifyAttributeToken($cpAutoresponderNode, 'type','plain','html');
  }
  return;
}

sub validateCpSpamassassinNode {
  my $cpSpamassassinNode = shift;
  verifyAttributeToken($cpSpamassassinNode, 'status', 'off', 'on');
  verifyAttributeToken($cpSpamassassinNode, 'action', 'del', 'mark');
  return;
}

sub validateCpMailnameNode {
  my $cpMailnameNode = shift;
  verifyAttributeExists($cpMailnameNode, 'name');
  verifyAttributeExists($cpMailnameNode, 'password');

  verifyChild($cpMailnameNode, 'spamassassin', 0, 1);
  my $cpSpamassassinNode = $cpMailnameNode->getChild( 'spamassassin' );
  if ( defined ( $cpSpamassassinNode ) ) {
    validateCpSpamassassinNode($cpSpamassassinNode);
  }
  return;
}

sub validateCpDatabaseNode {
  my $cpDatabaseNode = shift;
  verifyAttributeExists($cpDatabaseNode, 'name');
  verifyAttributeExists($cpDatabaseNode, 'type');
  verifyAttributeToken($cpDatabaseNode, 'type', 'mysql', 'postgresql');

  my @cpDbuserNodes = $cpDatabaseNode->getChildren('dbuser');
  foreach my $cpDbuserNode ( @cpDbuserNodes ) {
    verifyAttributeExists($cpDbuserNode, 'name');
    verifyChild($cpDbuserNode, 'password', 1, 1);
    verifyAttributeExists($cpDbuserNode->getChild('password'), 'type');
    verifyAttributeToken($cpDbuserNode->getChild('password'), 'type','plain','encrypted');
  }
  return;
}

sub validateCpMaillistNode {
  my $cpMaillistNode = shift;
  verifyAttributeExists($cpMaillistNode, 'name');

  verifyChild($cpMaillistNode, 'owner', 1);

  verifyChild($cpMaillistNode, 'password', 0, 1);
  my $cpPasswordNode = $cpMaillistNode->getChild( 'password' );
  if ( defined ( $cpPasswordNode ) ) {
    verifyAttributeExists($cpPasswordNode, 'type');
    verifyAttributeToken($cpPasswordNode, 'type','plain','encrypted');
  }

  verifyChild($cpMaillistNode, 'owner', 1);
  return;
}

sub validateCpDnsrecNode {
  my $cpDnsrecNode = shift;
  verifyAttributeExists($cpDnsrecNode, 'src');
  verifyAttributeExists($cpDnsrecNode, 'dst');
  verifyAttributeExists($cpDnsrecNode, 'type');
  verifyAttributeToken($cpDnsrecNode, 'type','A','AAAA','NS','MX','CNAME','TXT', 'SRV', 'CAA');

  return;
}

#
# Begin routines that verify xmlNode with throwing Exception::XmlException
# Examples:
#   verifyAttributeToken($xmlNode,'name', 'true', 'false');
#   verifyAttributeExists($xmlNode, 'name');
#   verifyChild($xmlNode,'domain', 1, 1);

# verifyChild routine
# Examples:
#   verifyChild($xmlNode, 'element-name', 0, 1)  =>  minOccurs="0" maxOccurs="1" (default)
#   verifyChild($xmlNode, 'element-name', 1, 1)  =>  minOccurs="1" (default) maxOccurs="1" (default)
#   verifyChild($xmlNode, 'element-name', 1)  =>  minOccurs="1" maxOccurs="unbounded"
#
sub verifyChild {
  my ($xmlNode, $child, $minOccurs, $maxOccurs) = @_;
  unless ( ref($xmlNode) =~ /XmlNode/ ) {
    throw Exception::XmlNode(undef,"No XmlNode passed");
  }
  unless ( $child ) {
    Logging::warning("Try check existence for unspecified child");
    return;
  }

  my @childNodes = $xmlNode->getChildren( $child );
  if ( ( $minOccurs > 0 ) and (scalar(@childNodes) == 0) ) {
    throw Exception::XmlNode( $xmlNode->copy(),"Invalid XmlNode. Element '".$xmlNode->getName()."'","Child element '".$child."' is required but isn't found");
  }
  if ( ( $maxOccurs == 1 ) and (scalar(@childNodes) > 1) ) {
    throw Exception::XmlNode( $xmlNode->copy(),"Invalid XmlNode. Element '".$xmlNode->getName()."'","Multiple child elements '".$child."' found that is not allowed");
  }
  return;
}

sub verifyAttributeExists {
  my ($xmlNode, $attribute) = @_;
  unless ( ref($xmlNode) =~ /XmlNode/ ) {
    throw Exception::XmlNode(undef,"No XmlNode passed");
  }
  unless ( defined ( $attribute ) ) {
    Logging::warning("Try check existence for attribute 'undef'");
    return;
  }
  unless ( defined ($xmlNode->getAttribute( $attribute ) ) ) {
    throw Exception::XmlNode( $xmlNode->copy(),"Invalid XmlNode. Element '".$xmlNode->getName()."'","Attribute '".$attribute."' is required but isn't found");
  }
  return;
}

sub verifyAttributeToken {
  my $xmlNode = shift;
  my $attribute = shift;
  my $tokenArray = \@_;

  unless ( ref($xmlNode) =~ /XmlNode/ ) {
    throw Exception::XmlNode(undef,"No XmlNode passed");
  }
  unless ( defined ( $attribute ) ) {
    Logging::warning("Try check existence for attribute 'undef'");
    return;
  }

  my $attributeValue = $xmlNode->getAttribute( $attribute );
  if ( defined ( $attributeValue ) ) {
    foreach my $tokenItem ( @$tokenArray ) {
      if ( $tokenItem eq $attributeValue ) {
        return;
      }
    }
    throw Exception::XmlNode($xmlNode->copy(),"Invalid XmlNode. Element '".$xmlNode->getName()."'","Attribute '".$attribute."' has invalid value ('".$attributeValue."')","Possible values are: @$tokenArray");
  }
  return;
}

#
# End of routines that verify xmlNode
#


1;