/**
*
*/
package uk.bl.api;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.net.InternetDomainName;
import play.Logger;
import uk.bl.Const.ScopeType;
import uk.bl.exception.ActException;
import uk.bl.scope.Scope;
import models.FieldUrl;
import models.Target;
/**
* Look for any kind of license at a higher level.
*
* Loop over Targets that may cover each URL.
* For each one, it checks if it might apply to the current target, and whether
* it's a NPLD or licensed Target.
* This means checking that all URLs are covered by the 'parent'.
* Every URL must be covered by at least on Target.
*
* @author andy
*
*/
public class OverallLicenseStatus {
public final boolean inNPLDScope;
public final boolean licensePending;
public final boolean licensedOrPending;
public final boolean inheritedLicense;
public final boolean inheritedLicensePending;
public final boolean inheritedLicenseOrPending;
public final boolean licensedOrPendingIncludingInherited;
public final boolean pendingIncludingInherited;
public final boolean inheritedNPLDScope;
public final Set<Target> NPLDParents = new LinkedHashSet<Target>();
public final Set<Target> licenseParents = new LinkedHashSet<Target>();
public OverallLicenseStatus(Target target) {
int npldc = 0, lc = 0, total_lc = 0;
for (FieldUrl fieldUrl : target.fieldUrls) {
Logger.info("Looking for inherited licensed for "+fieldUrl.url);
if( fieldUrl.domain == null && fieldUrl.url != null ) {
try {
fieldUrl.domain = Scope.getDomainFromUrl(fieldUrl.url);
} catch (ActException e) {
Logger.warn("Could not parse "+fieldUrl.url);
}
}
// Skip null domains:
if( StringUtils.isEmpty(fieldUrl.domain) ) {
Logger.error("No fieldUrl.domain for "+fieldUrl.url);
continue;
}
// Determine the parent paths:
List<String> parentPaths = null;
try {
parentPaths = getParentPaths(fieldUrl.url);
} catch (Exception e) {
continue;
}
// Look for targets:
List<Target> tp = Target.findAllTargetsForParentUrls(getParentDomain(fieldUrl.domain),parentPaths);
if( tp == null ) {
Logger.debug("Found no potential matches.");
continue;
}
Logger.debug("Found " + tp.size() + " potential matches.");
for( Target t : tp ) {
if( t.id.equals(target.id)) {
Logger.info("Skipping "+t.title);
continue;
}
Logger.debug("Checking "+t.title +" "+t.fieldUrls);
// Check if the scoping of the target applies here:
for( FieldUrl pt : t.fieldUrls) {
Logger.debug("Checking "+pt.url);
// If one of the 'parent's' URLs is a prefix of this one:
if( inheritsFromTo( pt,t.scope, fieldUrl ) ) {
// Check if this is in scope, but without forcing a full re-check (e.g. whois):
boolean isInScope = t.isInScopeAllWithoutLicense();
if( isInScope ) {
Logger.info("Found licensed parent for "+fieldUrl.url+", inScope="+isInScope);
NPLDParents.add(t);
npldc++;
}
// Also check if a license process is underway:
boolean midLic = ! t.enableLicenseCreation();
// Check is mid-licensed or licensed but without a licenseStatus:
if( midLic || t.checkLicense() ) {
Logger.info("Found licensed parent for "+fieldUrl.url+", mid-licensing="+midLic);
licenseParents.add(t);
if( t.checkLicense() ) lc++;
total_lc++;
}
} else {
Logger.info("Parent license does not apply to "+fieldUrl.url);
}
}
}
}
// Record if either was sufficient:
if(total_lc == target.fieldUrls.size()) {
if( lc == target.fieldUrls.size() ) {
this.inheritedLicense = true;
this.inheritedLicensePending = false;
} else {
this.inheritedLicense = false;
this.inheritedLicensePending = true;
}
} else {
this.inheritedLicense = false;
if( lc > 0 ) {
this.inheritedLicensePending = true;
} else {
this.inheritedLicensePending = false;
}
}
if(npldc == target.fieldUrls.size()) {
this.inheritedNPLDScope = true;
} else {
this.inheritedNPLDScope = false;
}
// And finally record the direct status:
this.inNPLDScope = target.isInScopeAllWithoutLicense();
if (target.enableLicenseCreation() == false) {
licensedOrPending = true;
if( target.checkLicense() ) {
licensePending = false;
} else {
licensePending = true;
}
} else {
licensedOrPending = false;
licensePending = false;
}
// Overall
inheritedLicenseOrPending = (inheritedLicensePending || inheritedLicense);
pendingIncludingInherited = (licensePending||inheritedLicensePending);
licensedOrPendingIncludingInherited = (licensedOrPending || inheritedLicense || inheritedLicensePending);
}
/**
* Get the parent domain name, in order to find inheritence candidates:
* @param domain
* @return
*/
private static String getParentDomain(String hostname) {
hostname = stripWWW(hostname);
try {
InternetDomainName idn = InternetDomainName.from(hostname);
// Get the private domain, if there is one:
if( idn.isUnderPublicSuffix() ) {
InternetDomainName domain = idn.topPrivateDomain();
// If the domain is not just a naked TLD, return it:
if( domain != null && domain.toString().contains(".")) {
return domain.toString();
}
}
} catch( IllegalArgumentException e ) {
Logger.info("Could not parse "+hostname+" as a domain name.");
}
return hostname;
}
/**
*
* @param url
* @return
* @throws URISyntaxException
*/
private static List<String> getParentPaths(String url) throws URISyntaxException {
ArrayList<String> parentPaths = new ArrayList<String>();
URI turl = new URI(url);
String path = turl.normalize().getPath();
String[] parts = path.split("/");
String current = "";
for( int i = 0; i < parts.length; i++ ) {
// Don't include the last bit or any blanks:
if( i < parts.length -1 && (! "".equals(parts[i]) ) ) {
current = current + "/" + parts[i];
parentPaths.add(current);
Logger.info("Scanning path parts "+current);
}
}
return parentPaths;
}
/**
* Checks whether a child URL should inherit from a parent one, given the scope.
*
* @param parent
* @param scope
* @param child
* @return
*/
private boolean inheritsFromTo(FieldUrl parent, String scope, FieldUrl child ) {
if( ScopeType.root.name().equals(scope) ) {
String nurl = stripWWW(child.url);
String pnurl = stripWWW(parent.url);
Logger.debug("Checking root scope for: "+nurl+" under "+pnurl);
return nurl.startsWith(pnurl);
} else if( ScopeType.subdomains.name().equals(scope)) {
String pdom = parent.domain;
if( pdom != null) {
pdom = pdom.replaceFirst("^www\\.", "");
Logger.debug("Checking domain scope for: "+child.domain+" under "+pdom);
return child.domain.endsWith(pdom);
} else {
Logger.error("The DOMAIN should not be null for " + parent);
return false;
}
} else {
Logger.error("UNSUPPORTED SCOPE "+scope);
}
return false;
}
private static String stripWWW( String url ) {
String nurl = url.toLowerCase();
if( nurl.startsWith("http://www.") || nurl.startsWith("https://www.")) {
nurl = nurl.replaceFirst("www\\.", "");
}
return nurl;
}
}