Package org.sonatype.nexus.proxy.repository.validator

Source Code of org.sonatype.nexus.proxy.repository.validator.AbstractMimeMagicFileTypeValidator

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.proxy.repository.validator;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.sonatype.nexus.mime.MimeSupport;
import org.sonatype.nexus.mime.NexusMimeTypes;
import org.sonatype.nexus.proxy.item.StorageFileItem;
import org.sonatype.nexus.util.SystemPropertiesHelper;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* Helper base class for implementing {@link FileTypeValidator} components that want to verify the content's MIME magic
* signature using {@link MimeSupport}. The main method {@link #isExpectedFileType(StorageFileItem)} has to be
* implemented by implementor in a way it collects it's "expectations" from some source (internal state, some config,
* whatever) and invokes the {@link #isExpectedFileTypeByDetectedMimeType(StorageFileItem, Set, boolean)} method with
* the file
* item and the set of "expectations". If the set has intersection, file is claimed valid, otherwise invalid.
*
* @author cstamas
* @since 2.0
*/
public abstract class AbstractMimeMagicFileTypeValidator
    extends AbstractFileTypeValidator
{

  public static final String XML_DETECTION_LAX_KEY = FileTypeValidator.class.getName() + ".relaxedXmlValidation";

  private static final boolean XML_DETECTION_LAX = SystemPropertiesHelper.getBoolean(
      XML_DETECTION_LAX_KEY, true
  );

  private final MimeSupport mimeSupport;

  private final NexusMimeTypes mimeTypes;

  protected AbstractMimeMagicFileTypeValidator(final MimeSupport mimeSupport) {
    this.mimeSupport = checkNotNull(mimeSupport);
    this.mimeTypes = new NexusMimeTypes();
  }

  protected AbstractMimeMagicFileTypeValidator(final NexusMimeTypes mimeTypes,
                                               final MimeSupport mimeSupport)
  {
    this.mimeSupport = checkNotNull(mimeSupport);
    this.mimeTypes = checkNotNull(mimeTypes);
  }

  /**
   * This method accepts the file item which content needs MIME magic detection, and the set of "expectations" to
   * match against. If the detected set of MIME types and passed in set of MIME types has intersection, file is
   * claimed VALID, otherwise INVALID. If the passed in set of expectations is empty of {@code null}, NEUTRAL stance
   * is claimed and nothing is done.
   *
   * @param file              to have checked content.
   * @param expectedMimeTypes the "expectations" against detected MIME types.
   * @param contentOnly       {@code true} to match content only, otherwise file name will be taken into account too
   *                          (potentially resulting in misleading matches, see "empty XML file with lax=false" case!).
   * @return {@link FileTypeValidity#VALID} if detected MIME types and passed in expectations has intersection,
   *         {@link FileTypeValidity#INVALID} otherwise. {@link FileTypeValidity#NEUTRAL} if passed in expectations
   *         are {@code null} or empty.
   * @throws IOException in case of some IO problem.
   */
  protected FileTypeValidity isExpectedFileTypeByDetectedMimeType(final StorageFileItem file,
                                                                  final Set<String> expectedMimeTypes,
                                                                  final boolean contentOnly)
      throws IOException
  {
    if (expectedMimeTypes == null || expectedMimeTypes.isEmpty()) {
      // we have nothing to work against, cannot take side
      return FileTypeValidity.NEUTRAL;
    }

    if (file.getContentLocator().getLength() == 0) {
      // zero length FILE sent for CONTENT validation: FAIL
      log.info(
          "StorageFileItem {} MIME-magic validation failed: 0 bytes length file, no content to validate",
          file.getRepositoryItemUid()
      );
      return FileTypeValidity.INVALID;
    }

    final List<String> magicMimeTypes;
    if (contentOnly) {
      magicMimeTypes = mimeSupport.detectMimeTypes(file.getContentLocator().getContent(), null);
    }
    else {
      magicMimeTypes = mimeSupport.detectMimeTypes(file.getInputStream(), file.getName());
    }

    if (log.isDebugEnabled()) {
      log.debug(
          "Checking StorageFileItem {} is one of the expected mime types: {}, detected mime types are: {}",
          file.getRepositoryItemUid(), expectedMimeTypes, magicMimeTypes
      );
    }

    for (String magicMimeType : magicMimeTypes) {
      if (expectedMimeTypes.contains(magicMimeType)) {
        return FileTypeValidity.VALID;
      }
    }

    log.info(
        "StorageFileItem {} MIME-magic validation failed: expected MIME types: {}, detected MIME types: {}",
        file.getRepositoryItemUid(), expectedMimeTypes, magicMimeTypes
    );

    return FileTypeValidity.INVALID;
  }

  @Override
  public FileTypeValidity isExpectedFileType(final StorageFileItem file) {
    boolean xmlLaxValidation = isXmlLaxValidation(file);

    final String filePath = file.getPath().toLowerCase();

    final Set<String> expectedMimeTypes = new HashSet<String>();

    final NexusMimeTypes.NexusMimeType type = mimeTypes.getMimeTypes(filePath);
    if (type != null) {
      expectedMimeTypes.addAll(type.getMimetypes());
    }

    // If we expect XML content, and XML LAX is FALSE, we have to do content only MAGIC detection!
    boolean contentOnly = (expectedMimeTypes.contains("application/xml") || expectedMimeTypes.contains("text/xml")) &&
        !xmlLaxValidation;

    try {
      // the expectedMimeTypes will be empty, see map in constructor which extensions we check at all.
      // The isExpectedFileTypeByDetectedMimeType() method will claim NEUTRAL when expectancies are empty/null
      final FileTypeValidity mimeDetectionResult = isExpectedFileTypeByDetectedMimeType(
          file, expectedMimeTypes, contentOnly
      );

      if (FileTypeValidity.INVALID.equals(mimeDetectionResult)
          && xmlLaxValidation
          && filePath.endsWith(".xml")) {
        // we go LAX way, if MIME detection says INVALID (does for XMLs missing preamble too)
        // we just stay put saying we are "neutral" on this question
        log.info("StorageFileItem {} detected as INVALID XML file but relaxed XML validation is in effect.",
            file.getRepositoryItemUid());
        return FileTypeValidity.NEUTRAL;
      }

      return mimeDetectionResult;
    }
    catch (IOException e) {
      log.warn(
          "Cannot detect MIME type and validate content of StorageFileItem: " + file.getRepositoryItemUid(),
          e);

      return FileTypeValidity.NEUTRAL;
    }
  }

  protected boolean isXmlLaxValidation(final StorageFileItem file) {
    // Note: this here is an ugly hack: enables per-request control of
    // LAX XML validation: if key not present, "system wide" settings used.
    // If key present, it's interpreted as Boolean and it's value is used to
    // drive LAX XML validation enable/disable.
    boolean xmlLaxValidation = XML_DETECTION_LAX;
    if (file.getItemContext().containsKey(XML_DETECTION_LAX_KEY)) {
      xmlLaxValidation = Boolean.parseBoolean(
          String.valueOf(file.getItemContext().get(XML_DETECTION_LAX_KEY))
      );
    }
    return xmlLaxValidation;
  }

}
TOP

Related Classes of org.sonatype.nexus.proxy.repository.validator.AbstractMimeMagicFileTypeValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.