Home | History | Annotate | Download | only in instr
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
      3  * All rights reserved. This program and the accompanying materials
      4  * are made available under the terms of the Eclipse Public License v1.0
      5  * which accompanies this distribution, and is available at
      6  * http://www.eclipse.org/legal/epl-v10.html
      7  *
      8  * Contributors:
      9  *    Marc R. Hoffmann - initial API and implementation
     10  *
     11  *******************************************************************************/
     12 package org.jacoco.core.internal.instr;
     13 
     14 import java.io.IOException;
     15 import java.io.InputStream;
     16 import java.io.OutputStream;
     17 import java.util.Collection;
     18 import java.util.Iterator;
     19 import java.util.jar.Attributes;
     20 import java.util.jar.Manifest;
     21 import java.util.regex.Pattern;
     22 
     23 /**
     24  * Support class to filter entries from JARs related to signatures.
     25  */
     26 public class SignatureRemover {
     27 
     28 	private static final Pattern SIGNATURE_FILES = Pattern
     29 			.compile("META-INF/[^/]*\\.SF|" //
     30 					+ "META-INF/[^/]*\\.DSA|" //
     31 					+ "META-INF/[^/]*\\.RSA|" //
     32 					+ "META-INF/SIG-[^/]*");
     33 
     34 	private static final String MANIFEST_MF = "META-INF/MANIFEST.MF";
     35 
     36 	private static final String DIGEST_SUFFIX = "-Digest";
     37 
     38 	private boolean active;
     39 
     40 	/**
     41 	 * Creates a new remover which is active.
     42 	 */
     43 	public SignatureRemover() {
     44 		active = true;
     45 	}
     46 
     47 	/**
     48 	 * Defines whether this remover should be active. If it is not active it
     49 	 * will not remove any entries.
     50 	 *
     51 	 * @param active
     52 	 *            <code>true</code> if it should remove signature related
     53 	 *            entries.
     54 	 */
     55 	public void setActive(final boolean active) {
     56 		this.active = active;
     57 	}
     58 
     59 	/**
     60 	 * Checks whether a entry with the provided name should be ignored at all.
     61 	 *
     62 	 * @param name
     63 	 *            path name of the entry in question
     64 	 * @return true is the entry should be ignored
     65 	 */
     66 	public boolean removeEntry(final String name) {
     67 		return active && SIGNATURE_FILES.matcher(name).matches();
     68 	}
     69 
     70 	/**
     71 	 * Filters the content of the entry with the provided name if necessary.
     72 	 *
     73 	 * @param name
     74 	 *            path name of the entry in question
     75 	 * @param in
     76 	 *            source for the element to filter
     77 	 * @param out
     78 	 *            output for the filtered contents
     79 	 * @return <code>true</code> if the content was filtered
     80 	 * @throws IOException
     81 	 *             if the content can't be read or written
     82 	 */
     83 	public boolean filterEntry(final String name, final InputStream in,
     84 			final OutputStream out) throws IOException {
     85 		if (!active || !MANIFEST_MF.equals(name)) {
     86 			return false;
     87 		}
     88 		final Manifest mf = new Manifest(in);
     89 		filterManifestEntry(mf.getEntries().values());
     90 		mf.write(out);
     91 		return true;
     92 	}
     93 
     94 	private void filterManifestEntry(final Collection<Attributes> entry) {
     95 		for (final Iterator<Attributes> i = entry.iterator(); i.hasNext();) {
     96 			final Attributes attributes = i.next();
     97 			filterManifestEntryAttributes(attributes);
     98 			if (attributes.isEmpty()) {
     99 				i.remove();
    100 			}
    101 		}
    102 	}
    103 
    104 	private void filterManifestEntryAttributes(final Attributes attrs) {
    105 		for (final Iterator<Object> i = attrs.keySet().iterator(); i.hasNext();) {
    106 			if (String.valueOf(i.next()).endsWith(DIGEST_SUFFIX)) {
    107 				i.remove();
    108 			}
    109 		}
    110 	}
    111 
    112 }
    113