/*
 * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved.
 * See LICENCE file for licensing information.
 *
 * Derived from the code copyrighted and licensed as follows:
 * 
 * Copyright (c) Members of the EGEE Collaboration. 2004.
 * See http://www.eu-egee.org/partners/ for details on the copyright
 * holders.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.emi.security.authn.x509.helpers.proxy;

import java.io.IOException;
import java.security.cert.X509Certificate;

import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;

import eu.emi.security.authn.x509.helpers.CertificateHelpers;

/**
 * A class for generating and parsing the proxy tracing extensions. <br>
 * See <a href="http://www.eugridpma.org/documentation/OIDProxyDelegationTracing.pdf"> OIDProxyDelegationTracing
 * documentation. </a> <br>
 * The proxy tracing extension format is below. It is used to trace the proxy delegation chain by putting in each proxy
 * the url of the service accepting the delegation and the url of the client initiating it. Often the delegation is from
 * service to service, in which case it is easy to use the url of the service. If the initiator of the delegation is a
 * user, then the client should put an url containing the client program as the scheme, the host name or IP address and
 * possibly the username as the path. <br>
 * At the moment only the URI is supported.
 * 
 * <pre>
 *  iGTFProxyTracingIssuerName ::= GeneralNames
 *  iGTFProxyTracingSubjectName ::= GeneralNames
 *  
 *  GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
 *  
 *  GeneralName ::= CHOICE {
 *           otherName                       [0]     OtherName,
 *           rfc822Name                      [1]     IA5String,
 *           dNSName                         [2]     IA5String,
 *           x400Address                     [3]     ORAddress,
 *           directoryName                   [4]     Name,
 *           ediPartyName                    [5]     EDIPartyName,
 *           uniformResourceIdentifier       [6]     IA5String,
 *           iPAddress                       [7]     OCTET STRING,
 *           registeredID                    [8]     OBJECT IDENTIFIER}
 *  
 *  OtherName ::= SEQUENCE {
 *           type-id    OBJECT IDENTIFIER,
 *           value      [0] EXPLICIT ANY DEFINED BY type-id }
 *  
 *  EDIPartyName ::= SEQUENCE {
 *           nameAssigner            [0]     DirectoryString OPTIONAL,
 *           partyName               [1]     DirectoryString }
 *  
 *  DirectoryString ::= CHOICE {
 *     teletexString           TeletexString (SIZE (1..maxSize),
 *     printableString         PrintableString (SIZE (1..maxSize)),
 *     universalString         UniversalString (SIZE (1..maxSize)),
 *     bmpString               BMPString (SIZE(1..maxSIZE))
 *  }
 * </pre>
 * 
 * @author joni.hahkala@cern.ch
 * @author K. Benedyczak
 */
public class ProxyTracingExtension extends ASN1Object
{
	/** The OID to identify issuer proxy tracing extension. */
	public static final String PROXY_TRACING_ISSUER_EXTENSION_OID = "1.2.840.113612.5.5.1.1.1.1";
	/** The OID to identify subject proxy tracing extension. */
	public static final String PROXY_TRACING_SUBJECT_EXTENSION_OID = "1.2.840.113612.5.5.1.1.1.2";
	/** The OID to identify issuer proxy tracing type. */
	public static final int ISSUER_EXTENSION = 1;
	/** The OID to identify issuer proxy tracing type. */
	public static final int SUBJECT_EXTENSION = 2;

	/**
	 * The tracing generalNames object that wraps the generalName.
	 */
	private GeneralNames names = null;
	/**
	 * The tracing generalName object.
	 */
	private GeneralName name = null;

	/**
	 * Generates a new proxy tracing item from the URL.
	 * 
	 * @param url
	 *                The URL to identify the issuer or the subject.
	 */
	public ProxyTracingExtension(String url)
	{
		name = new GeneralName(GeneralName.uniformResourceIdentifier, url);
		names = new GeneralNames(name);
	}

	/**
	 * Parses the information in the byte array (GeneralNames ASN1 sequence
	 * of GeneralName) into a proxy tracing extension object.
	 * 
	 * @param bytes
	 *                The bytes of ASN1 encoded proxy tracing extension.
	 * @throws IOException
	 *                 In case the byte array does not contain a valid ASN1
	 *                 encoded proxy tracing extension.
	 */
	public ProxyTracingExtension(byte[] bytes) throws IOException
	{
		names = GeneralNames.getInstance(ASN1Primitive.fromByteArray(bytes));
		name = names.getNames()[0];
	}

	/**
	 * Returns the URL of the proxy tracing issuer if present.
	 * 
	 * @param cert certificate
	 * @param issuer issuer
	 * @return The proxy tracing issuer URL in String format, or null if no
	 *         extension was found or it was empty.
	 * @throws IOException
	 *                 Thrown in case the parsing of the information failed.
	 */
	public static ProxyTracingExtension getInstance(X509Certificate cert, boolean issuer) throws IOException
	{
		byte[] bytes = CertificateHelpers.getExtensionBytes(cert,
				issuer ? ProxyTracingExtension.PROXY_TRACING_ISSUER_EXTENSION_OID : 
					ProxyTracingExtension.PROXY_TRACING_SUBJECT_EXTENSION_OID);

		if (bytes == null || bytes.length == 0)
			return null;

		return new ProxyTracingExtension(bytes);
	}

	/**
	 * Returns the URL inside the proxy tracing data structure.
	 * 
	 * @return The URL in String format.
	 */
	public String getURL()
	{
		if (name.getTagNo() != GeneralName.uniformResourceIdentifier)
			return null;

		DERIA5String ia5String = (DERIA5String) name.getName();
		return ia5String.getString();
	}

	/**
	 * Returns the general names structure that holds the trace information.
	 * 
	 * @return The generalNames object that has the trace information.
	 */
	public GeneralNames getNames()
	{
		return names;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ASN1Primitive toASN1Primitive()
	{
		return names.toASN1Primitive();
	}
}
