package com.limegroup.gnutella.licenses; import java.io.IOException; import java.io.Serializable; import java.io.StringReader; import java.io.ObjectInputStream; import com.limegroup.gnutella.http.HttpClientManager; import com.limegroup.gnutella.util.ProcessingQueue; import com.limegroup.gnutella.util.CommonUtils; import org.apache.xerces.parsers.DOMParser; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.w3c.dom.Node; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.URI; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; /** * A base license class, implementing common functionality. */ abstract class AbstractLicense implements NamedLicense, Serializable, Cloneable { private static final Log LOG = LogFactory.getLog(AbstractLicense.class); /** * The queue that all license verification attempts are processed in. */ private static final ProcessingQueue VQUEUE = new ProcessingQueue("LicenseVerifier"); private static final long serialVersionUID = 6508972367931096578L; /** Whether or not this license has been verified. */ protected transient int verified = UNVERIFIED; /** The URI where verification will be performed. */ protected transient URI licenseLocation; /** The license name. */ private transient String licenseName; /** The last time this license was verified. */ private long lastVerifiedTime; /** Constructs a new AbstractLicense. */ AbstractLicense(URI uri) { this.licenseLocation = uri; } public void setLicenseName(String name) { this.licenseName = name; } public boolean isVerifying() { return verified == VERIFYING; } public boolean isVerified() { return verified == VERIFIED; } public String getLicenseName() { return licenseName; } public URI getLicenseURI() { return licenseLocation; } public long getLastVerifiedTime() { return lastVerifiedTime; } /** * Assume that all serialized licenses were verified. * (Otherwise they wouldn't have been serialized. */ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); verified = VERIFIED; } /** * Clears all internal state that could be set while verifying. */ protected abstract void clear(); /** * Starts verification of the license. * * The listener is notified when verification is finished. */ public void verify(VerificationListener listener) { verified = VERIFYING; clear(); VQUEUE.add(new Verifier(listener)); } /** * Retrieves the body of a URL from a webserver. * * Returns null if the page could not be found. */ protected String getBody(String url) { return getBodyFromURL(url); } /** * Contacts the given URL and downloads returns the body of the * HTTP request. */ protected String getBodyFromURL(String url) { if(LOG.isTraceEnabled()) LOG.trace("Contacting: " + url); HttpClient client = HttpClientManager.getNewClient(); GetMethod get = new GetMethod(url); get.addRequestHeader("User-Agent", CommonUtils.getHttpServer()); try { HttpClientManager.executeMethodRedirecting(client, get); return get.getResponseBodyAsString(); } catch(IOException ioe) { LOG.warn("Can't contact license server: " + url, ioe); return null; } finally { get.releaseConnection(); } } /** Parses the document node of the XML. */ protected abstract void parseDocumentNode(Node node, boolean liveData); /** * Attempts to parse the given XML. * The actual handling of the XML is sent to parseDocumentNode, * which subclasses can implement as they see fit. * * If this is a request directly from our Verifier, 'liveData' is true. * Subclasses may use this to know where the XML data is coming from. */ protected void parseXML(String xml, boolean liveData) { if(xml == null) return; if(LOG.isTraceEnabled()) LOG.trace("Attempting to parse: " + xml); DOMParser parser = new DOMParser(); InputSource is = new InputSource(new StringReader(xml)); try { parser.parse(is); } catch (IOException ioe) { LOG.debug("IOX parsing XML\n" + xml, ioe); return; } catch (SAXException saxe) { LOG.debug("SAX parsing XML\n" + xml, saxe); return; } parseDocumentNode(parser.getDocument().getDocumentElement(), liveData); } /** * Runnable that actually does the verification. * This will retrieve the body of a webpage from the licenseURI, * parse it, set the last verified time, and cache it in the LicenseCache. */ private class Verifier implements Runnable { private final VerificationListener vc; Verifier(VerificationListener listener) { vc = listener; } public void run() { String body = getBody(getLicenseURI().toString()); parseXML(body, true); lastVerifiedTime = System.currentTimeMillis(); verified = VERIFIED; LicenseCache.instance().addVerifiedLicense(AbstractLicense.this); if(vc != null) vc.licenseVerified(AbstractLicense.this); } } }