/*******************************************************************************
* Copyright (c) 2007, 2010 compeople AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* 	compeople AG (Stefan Liebig) - initial API and implementation
*  IBM - continuing development
*******************************************************************************/
package org.eclipse.equinox.internal.p2.artifact.repository;

import java.io.*;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep;
import org.eclipse.osgi.signedcontent.*;

/**
 * Processing step validating the signature of the artifact being downloaded  
 */
public class SignatureVerifier extends ProcessingStep {
	private File inputFile;
	private OutputStream tempStream;

	public boolean areRequirementsSatisfied() {
		return true;
	}

	public void write(int b) throws IOException {
		getOutputStream().write(b);
	}

	public void write(byte[] bytes, int off, int len) throws IOException {
		getOutputStream().write(bytes, off, len);
	}

	private OutputStream getOutputStream() throws IOException {
		if (tempStream != null)
			return tempStream;
		// store input stream in temporary file
		inputFile = File.createTempFile("signatureFile", ".jar"); //$NON-NLS-1$ //$NON-NLS-2$
		tempStream = new BufferedOutputStream(new FileOutputStream(inputFile));
		return tempStream;
	}

	private void verify() throws IOException {
		BufferedInputStream resultStream = null;
		try {
			if (tempStream == null)
				// hmmm, no one wrote to this stream so there is nothing to pass on
				return;
			// Ok, so there is content, close the tempStream
			tempStream.close();
			setStatus(verifyContent());

			// now write the  content to the final destination
			resultStream = new BufferedInputStream(new FileInputStream(inputFile));
			FileUtils.copyStream(resultStream, true, getDestination(), false);
			resultStream = null;
		} finally {
			if (inputFile != null)
				inputFile.delete();
			if (resultStream != null)
				resultStream.close();
		}
	}

	private IStatus verifyContent() throws IOException {
		SignedContentFactory verifierFactory = (SignedContentFactory) ServiceHelper.getService(Activator.getContext(), SignedContentFactory.class.getName());
		SignedContent signedContent;
		try {
			signedContent = verifierFactory.getSignedContent(inputFile);
		} catch (GeneralSecurityException e) {
			return new Status(IStatus.ERROR, Activator.ID, Messages.SignatureVerification_failedRead + inputFile, e);
		}
		ArrayList<IStatus> allStatus = new ArrayList<IStatus>(0);
		SignedContentEntry[] entries = signedContent.getSignedEntries();
		for (int i = 0; i < entries.length; i++)
			try {
				entries[i].verify();
			} catch (InvalidContentException e) {
				allStatus.add(new Status(IStatus.ERROR, Activator.ID, Messages.SignatureVerification_invalidContent + entries[i].getName(), e));
			} catch (OutOfMemoryError e) {
				allStatus.add(new Status(IStatus.ERROR, Activator.ID, Messages.SignatureVerifier_OutOfMemory, e));
				break;
			}
		if (allStatus.size() > 0)
			return new MultiStatus(Activator.ID, IStatus.ERROR, allStatus.toArray(new IStatus[allStatus.size()]), Messages.SignatureVerification_invalidFileContent + inputFile, null);
		return Status.OK_STATUS;
	}

	public void close() throws IOException {
		// When we go to close we must have seen all the content we are going to see
		// So before closing, verify and write the result to the destination
		verify();
		super.close();
	}

}