<commit_before>/*-
 * Copyright (c) 2012-2016 Red Hat, Inc.
 *
 * 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 org.fedoraproject.xmvn.tools.install;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.fedoraproject.xmvn.artifact.Artifact;

/**
 * @author <NAME>
 */
public final class JarUtils
{
    private static final Logger LOGGER = LoggerFactory.getLogger( JarUtils.class );

    // From /usr/include/linux/elf.h
    private static final int ELFMAG0 = 0x7F;

    private static final int ELFMAG1 = 'E';

    private static final int ELFMAG2 = 'L';

    private static final int ELFMAG3 = 'F';

    private JarUtils()
    {
        // Avoid generating default public constructor
    }

    /**
     * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file contains native (architecture-dependent)
     * code.
     * <p>
     * Currently this code only checks only for ELF binaries, but that behavior can change in future.
     * 
     * @return {@code true} if native code was found inside given JAR
     */
    public static boolean containsNativeCode( Path jar )
    {
        try ( ZipInputStream jis = new ZipInputStream( Files.newInputStream( jar ) ) )
        {
            ZipEntry ent;
            while ( ( ent = jis.getNextEntry() ) != null )
            {
                if ( ent.isDirectory() )
                    continue;
                if ( jis.read() == ELFMAG0 && jis.read() == ELFMAG1 && jis.read() == ELFMAG2 && jis.read() == ELFMAG3 )
                {
                    LOGGER.debug( "Native code found inside {}: {}", jar, ent.getName() );
                    return true;
                }
            }

            LOGGER.trace( "Native code not found inside {}", jar );
            return false;
        }
        catch ( IOException e )
        {
            LOGGER.debug( "I/O exception caught when trying to determine whether JAR contains native code: {}", jar,
                          e );
            return false;
        }
    }

    static class NativeMethodFound
        extends RuntimeException
    {
        private static final long serialVersionUID = 1;

        final String className;

        final String methodName;

        final String methodSignature;

        NativeMethodFound( String className, String methodName, String methodSignature )
        {
            this.className = className;
            this.methodName = methodName;
            this.methodSignature = methodSignature;
        }
    }

    /**
     * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file is using native (architecture-dependent)
     * code.
     * <p>
     * Currently this code only checks if any class file declares Java native methods, but that behavior can change in
     * future.
     * 
     * @return {@code true} given JAR as found inside to use native code
     */
    public static boolean usesNativeCode( Path jar )
    {
        try ( ZipInputStream jis = new ZipInputStream( Files.newInputStream( jar ) ) )
        {
            ZipEntry ent;
            while ( ( ent = jis.getNextEntry() ) != null )
            {
                final String entryName = ent.getName();
                if ( ent.isDirectory() || !entryName.endsWith( ".class" ) )
                    continue;

                new ClassReader( jis ).accept( new ClassVisitor( Opcodes.ASM4 )
                {
                    @Override
                    public MethodVisitor visitMethod( int flags, String name, String desc, String sig, String[] exc )
                    {
                        if ( ( flags & Opcodes.ACC_NATIVE ) != 0 )
                            throw new NativeMethodFound( entryName, name, sig );

                        return super.visitMethod( flags, name, desc, sig, exc );
                    }
                }, ClassReader.SKIP_CODE );
            }

            return false;
        }
        catch ( NativeMethodFound e )
        {
            LOGGER.debug( "Native method {}({}) found in {}: {}", e.methodName, e.methodSignature, jar, e.className );
            return true;
        }
        catch ( IOException e )
        {
            LOGGER.debug( "I/O exception caught when trying to determine whether JAR uses native code: {}", jar, e );
            return false;
        }
        catch ( IllegalArgumentException e )
        {
            return false;
        }
    }

    private static void putAttribute( Manifest manifest, String key, String value, String defaultValue )
    {
        if ( defaultValue == null || !value.equals( defaultValue ) )
        {
            Attributes attributes = manifest.getMainAttributes();
            attributes.putValue( key, value );
            LOGGER.trace( "Injected field {}: {}", key, value );
        }
        else
        {
            LOGGER.trace( "Not injecting field {} (it has default value \"{}\")", key, defaultValue );
        }
    }

    /**
     * OpenJDK has a sanity check that prevents adding duplicate entries to ZIP streams. The problem is that some of
     * JARs we try to inject manifests to (especially the ones created by Gradle) already contain duplicate entries, so
     * manifest injection would always fail for them with "ZipException: duplicate entry".
     * <p>
     * This function tries to work around this OpenJDK sanity check, effectively allowing creating ZIP files with
     * duplicated entries. It should be called on particular ZIP output stream before adding each duplicate entry.<commit_msg>Ignore all runtime exceptions thrown by ASM<commit_after>/*-
 * Copyright (c) 2012-2016 Red Hat, Inc.
 *
 * 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 org.fedoraproject.xmvn.tools.install;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.fedoraproject.xmvn.artifact.Artifact;

/**
 * @author <NAME>
 */
public final class JarUtils
{
    private static final Logger LOGGER = LoggerFactory.getLogger( JarUtils.class );

    // From /usr/include/linux/elf.h
    private static final int ELFMAG0 = 0x7F;

    private static final int ELFMAG1 = 'E';

    private static final int ELFMAG2 = 'L';

    private static final int ELFMAG3 = 'F';

    private JarUtils()
    {
        // Avoid generating default public constructor
    }

    /**
     * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file contains native (architecture-dependent)
     * code.
     * <p>
     * Currently this code only checks only for ELF binaries, but that behavior can change in future.
     * 
     * @return {@code true} if native code was found inside given JAR
     */
    public static boolean containsNativeCode( Path jar )
    {
        try ( ZipInputStream jis = new ZipInputStream( Files.newInputStream( jar ) ) )
        {
            ZipEntry ent;
            while ( ( ent = jis.getNextEntry() ) != null )
            {
                if ( ent.isDirectory() )
                    continue;
                if ( jis.read() == ELFMAG0 && jis.read() == ELFMAG1 && jis.read() == ELFMAG2 && jis.read() == ELFMAG3 )
                {
                    LOGGER.debug( "Native code found inside {}: {}", jar, ent.getName() );
                    return true;
                }
            }

            LOGGER.trace( "Native code not found inside {}", jar );
            return false;
        }
        catch ( IOException e )
        {
            LOGGER.debug( "I/O exception caught when trying to determine whether JAR contains native code: {}", jar,
                          e );
            return false;
        }
    }

    static class NativeMethodFound
        extends RuntimeException
    {
        private static final long serialVersionUID = 1;

        final String className;

        final String methodName;

        final String methodSignature;

        NativeMethodFound( String className, String methodName, String methodSignature )
        {
            this.className = className;
            this.methodName = methodName;
            this.methodSignature = methodSignature;
        }
    }

    /**
     * Heuristically try to determine whether given JAR (or WAR, EAR, ...) file is using native (architecture-dependent)
     * code.
     * <p>
     * Currently this code only checks if any class file declares Java native methods, but that behavior can change in
     * future.
     * 
     * @return {@code true} given JAR as found inside to use native code
     */
    public static boolean usesNativeCode( Path jar )
    {
        try ( ZipInputStream jis = new ZipInputStream( Files.newInputStream( jar ) ) )
        {
            ZipEntry ent;
            while ( ( ent = jis.getNextEntry() ) != null )
            {
                final String entryName = ent.getName();
                if ( ent.isDirectory() || !entryName.endsWith( ".class" ) )
                    continue;

                new ClassReader( jis ).accept( new ClassVisitor( Opcodes.ASM4 )
                {
                    @Override
                    public MethodVisitor visitMethod( int flags, String name, String desc, String sig, String[] exc )
                    {
                        if ( ( flags & Opcodes.ACC_NATIVE ) != 0 )
                            throw new NativeMethodFound( entryName, name, sig );

                        return super.visitMethod( flags, name, desc, sig, exc );
                    }
                }, ClassReader.SKIP_CODE );
            }

            return false;
        }
        catch ( NativeMethodFound e )
        {
            LOGGER.debug( "Native method {}({}) found in {}: {}", e.methodName, e.methodSignature, jar, e.className );
            return true;
        }
        catch ( IOException e )
        {
            LOGGER.debug( "I/O exception caught when trying to determine whether JAR uses native code: {}", jar, e );
            return false;
        }
        catch ( RuntimeException e )
        {
            return false;
        }
    }

    private static void putAttribute( Manifest manifest, String key, String value, String defaultValue )
    {
        if ( defaultValue == null || !value.equals( defaultValue ) )
        {
            Attributes attributes = manifest.getMainAttributes();
            attributes.putValue( key, value );
            LOGGER.trace( "Injected field {}: {}", key, value );
        }
        else
        {
            LOGGER.trace( "Not injecting field {} (it has default value \"{}\")", key, defaultValue );
        }
    }

    /**
     * OpenJDK has a sanity check that prevents adding duplicate entries to ZIP streams. The problem is that some of
     * JARs we try to inject manifests to (especially the ones created by Gradle) already contain duplicate entries, so
     * manifest injection would always fail for them with "ZipException: duplicate entry".
     * <p>
     * This function tries to work around this OpenJDK sanity check, effectively allowing creating ZIP files with
     * duplicated entries. It should be called on particular ZIP output stream before adding each duplicate entry.
{'fraction_non_alphanumeric': 0.08967185846060789, 'fraction_numerical': 0.004082867079993951, 'mean_word_length': 2.0839356493355092, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7664752', 'n_tokens_mistral': 3523, 'n_tokens_neox': 3402, 'n_words': 1195}