Package com.google.enterprise.connector.filesystem

Source Code of com.google.enterprise.connector.filesystem.SmbReadonlyFile

// Copyright 2009 Google 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 com.google.enterprise.connector.filesystem;

import com.google.common.annotations.VisibleForTesting;
import com.google.enterprise.connector.filesystem.SmbFileSystemType.SmbFileProperties;
import com.google.enterprise.connector.spi.DocumentAccessException;
import com.google.enterprise.connector.spi.DocumentNotFoundException;
import com.google.enterprise.connector.spi.RepositoryDocumentException;
import com.google.enterprise.connector.spi.RepositoryException;

import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Implementation of ReadonlyFile that delegates to {@code jcifs.smb.SmbFile}.
*
* @see PathParser
*/
/* TODO: This class should be made easier to test - perhaps somehow
* mock an SMB connection or SmbFileDelegate (but adding yet another
* mockable layer here just seems odd).
*/
public class SmbReadonlyFile
    extends AccessTimePreservingReadonlyFile<SmbReadonlyFile> {

  private static final Logger LOG =
      Logger.getLogger(SmbReadonlyFile.class.getName());

  /** The delegate file implementation. */
  @VisibleForTesting
  protected final SmbFileDelegate delegate;

  /** The Credentials used to access the SMB share. */
  @VisibleForTesting
  protected final Credentials credentials;

  /**
   * Implementation of {@link SmbFileProperties} that gives the
   * required properties for SMB crawling.
   */
  @VisibleForTesting
  protected final SmbFileProperties smbPropertyFetcher;

  /**
   * Cached AclBuilder for this file.
   */
  @VisibleForTesting
  protected AclBuilder aclBuilder;

  /**
   * @param type a FileSystemType instance
   * @param path see {@code jcifs.org.SmbFile} for path syntax.
   * @param credentials
   * @param propertyFetcher Fetcher object that gives the required properties
   *        for SMB crawling.
   * @throws RepositoryDocumentException if the path is malformed
   */
  public SmbReadonlyFile(SmbFileSystemType type, String path,
      Credentials credentials, SmbFileProperties propertyFetcher)
      throws RepositoryException {
    this(type, newDelegate(path, credentials), credentials, propertyFetcher);
  }

  /**
   * Create a ReadonlyFile that delegates to {@code smbFile}.
   *
   * @param type a FileSystemType instance
   * @param delegate a SmbFileDelegate instance
   * @param credentials
   * @param propertyFetcher Fetcher object that gives the required properties
   *        for SMB crawling.
   * @throws RepositoryDocumentException
   */
  @VisibleForTesting
  SmbReadonlyFile(SmbFileSystemType type, SmbFileDelegate delegate,
      Credentials credentials, SmbFileProperties propertyFetcher) {
    super(type, delegate, propertyFetcher.isLastAccessResetFlagForSmb());
    this.delegate = delegate;
    this.credentials = credentials;
    this.smbPropertyFetcher = propertyFetcher;
  }

  private static SmbFileDelegate newDelegate(String path,
      Credentials credentials) throws RepositoryException {
    try {
      SmbFileDelegate delegate =
          new SmbFileDelegate(path, credentials.getNtlmAuthorization());
      // Directories must end in "/", or listFiles() fails.
      if ((path.lastIndexOf('/') != (path.length() - 1))
          && delegate.isDirectory()) {
        delegate =
            new SmbFileDelegate(path + "/", credentials.getNtlmAuthorization());
      }
      return delegate;
    } catch (SmbException e) {
      staticDetectGeneralErrors(e, path);
      throw new RepositoryDocumentException(e);
    } catch (MalformedURLException e) {
      throw new IncorrectURLException("Malformed SMB path: " + path, e);
    }
  }

  @Override
  protected SmbReadonlyFile newChild(String name) throws RepositoryException {
    String path = delegate.getPath() + name;
    return new SmbReadonlyFile((SmbFileSystemType) getFileSystemType(),
                                path, credentials, smbPropertyFetcher);
  }

  /** If repository cannot be contacted throws RepositoryException. */
  private static void staticDetectServerDown(IOException e)
      throws RepositoryException {
    if (!(e instanceof SmbException))
      return;
    SmbException smbe = (SmbException) e;
    // Not 100% sure if identifying all server downs and only server downs.
    boolean badCommunication =
        SmbException.NT_STATUS_UNSUCCESSFUL == smbe.getNtStatus();
    Throwable rootCause = smbe.getRootCause();
    String rootCauseString =
        (null == rootCause) ? "" : " " + smbe.getRootCause().getClass();
    boolean noTransport =
        rootCause instanceof jcifs.util.transport.TransportException;
    LOG.finest("server down variables:" + smbe.getNtStatus() + rootCauseString
        + " " + smbe.getMessage());

    // All pipe instances are busy.
    if (SmbException.NT_STATUS_INSTANCE_NOT_AVAILABLE == smbe.getNtStatus()
        || SmbException.NT_STATUS_PIPE_NOT_AVAILABLE == smbe.getNtStatus()
        || SmbException.NT_STATUS_PIPE_BUSY == smbe.getNtStatus()
        || SmbException.NT_STATUS_REQUEST_NOT_ACCEPTED == smbe.getNtStatus()) {
      throw new RepositoryException("Server busy", smbe);
    }

    // Timeouts waiting for response.
    if (badCommunication && noTransport &&
        ("" + smbe).contains("timedout waiting for response")) {
      throw new RepositoryException("Server busy", smbe);
    }

    // Cannot connect to server.
    if (badCommunication && noTransport &&
        ("" + smbe).contains("Failed to connect")) {
      throw new RepositoryException("Server down", smbe);
    }
  }

  /** Checks for general document access problems, including server down. */
  private static void staticDetectGeneralErrors(IOException e, String path)
      throws RepositoryException {
    if (!(e instanceof SmbException))
      return;
    staticDetectServerDown(e);
    SmbException smbe = (SmbException) e;
    if (smbe.getNtStatus() == SmbException.NT_STATUS_LOGON_FAILURE) {
      throw new InvalidUserException(
          "Please specify correct user name and password for " + path,
          smbe);
    } else if (smbe.getNtStatus() == SmbException.NT_STATUS_ACCESS_DENIED) {
      throw new DocumentAccessException(
          "Access denied for " + path, smbe);
    } else if (smbe.getNtStatus() == SmbException.NT_STATUS_BAD_NETWORK_NAME) {
      throw new DocumentNotFoundException(
          "Path does not exist: " + path, smbe);
    }
  }

  /** If repository cannot be contacted throws RepositoryException. */
  @Override
  protected void detectServerDown(IOException e)
      throws RepositoryException {
    staticDetectServerDown(e);
  }

  /** Checks for general document access problems, including server down. */
  @Override
  protected void detectGeneralErrors(IOException e)
      throws RepositoryException {
    staticDetectGeneralErrors(e, getPath());
  }

  @Override
  public String getDisplayUrl() {
    URL documentUrl = delegate.getURL();
    try {
      int port = (documentUrl.getPort() == documentUrl.getDefaultPort())
                 ? -1 : documentUrl.getPort();
      URI displayUri = new URI("file", null /* userInfo */,
          documentUrl.getHost(),
          port,
          documentUrl.getPath(),
          null /* query */,
          null /* fragment */);
      return displayUri.toASCIIString();
    } catch (URISyntaxException use) {
      throw new IllegalStateException(
          "Delegate URL not valid " + delegate.getURL(), use);
    }
  }

  @Override
  public Acl getAcl() throws IOException, RepositoryException {
    try {
      return getAclBuilder().getAcl();
    } catch (IOException e) {
      return processIOException(e, "");
    }
  }

  @Override
  public boolean hasInheritedAcls() throws IOException, RepositoryException {
    try {
      return getAclBuilder().hasInheritedAcls();
    } catch (IOException e) {
      processIOException(e, "hasInherited");
      return false;
    }
  }

  @Override
  public Acl getContainerInheritAcl() throws IOException, RepositoryException {
    try {
      return getAclBuilder().getContainerInheritAcl();
    } catch (IOException e) {
      return processIOException(e, "container inherit");
    }
  }

  @Override
  public Acl getFileInheritAcl() throws IOException, RepositoryException {
    try {
      return getAclBuilder().getFileInheritAcl();
    } catch (IOException e) {
      return processIOException(e, "file inherit");
    }
  }

  @Override
  public Acl getInheritedAcl() throws IOException, RepositoryException {
    try {
      return getAclBuilder().getInheritedAcl();
    } catch (IOException e) {
      return processIOException(e, "inherited");
    }
  }

  @Override
  public Acl getShareAcl() throws IOException, RepositoryException {
    try {
      return getAclBuilder().getShareAcl();
    } catch (IOException e) {
      processIOException(e, "share");
      throw e;
    }
  }

  private Acl processIOException(IOException e, String aclType)
      throws IOException, RepositoryException {
    detectServerDown(e);   
    LOG.warning("Failed to get " + aclType + " ACL: " + e.getMessage());
    if (LOG.isLoggable(Level.FINEST)) {
      LOG.log(Level.FINEST, "Got Exception while getting " + aclType
              + " ACL for " + this.getPath(), e);
    }
    if (e instanceof SmbException && smbPropertyFetcher.useAuthzOnAclError()) {
      return Acl.USE_HEAD_REQUEST;
    } else {
      throw e;
    }
  }

  @Override
  public boolean isDirectory() throws RepositoryException {
    // There appears to be a bug in (at least) v1.2.13 that causes
    // non-existent paths to return true.
    return exists() ? super.isDirectory() : false;
  }

  /**
   * Returns true if either the create timestamp or the last modified
   * timestamp of the file is newer than the supplied time.
   * <p>
   * According to <a href="http://support.microsoft.com/kb/299648">this
   * Microsoft document</a>, moving or renaming a file within the same file
   * system does not change either the last-modify timestamp of a file or
   * the create timestamp of a file.  However, copying a file or moving it
   * across filesystems (which involves an implicit copy) sets a new create
   * timestamp, but does not alter the last modified timestamp.
   */
  @Override
  public boolean isModifiedSince(long time) throws RepositoryException {
    try {
      long lastModified =
          Math.max(delegate.lastModified(), delegate.createTime());
      return (lastModified > 0L) ? (lastModified >= time) : true;
    } catch (IOException e) {
      detectServerDown(e);
      throw new RepositoryDocumentException(
          "Failed to get last modified time for " + getPath(), e);
    }
  }

  @VisibleForTesting
  protected synchronized AclBuilder getAclBuilder() throws IOException {
    if (aclBuilder == null) {
      if (smbPropertyFetcher.supportsInheritedAcls()) {
        aclBuilder = new SmbAclBuilder(delegate, smbPropertyFetcher);
      } else {
        aclBuilder = new LegacySmbAclBuilder(delegate, smbPropertyFetcher);
      }
    }
    return aclBuilder;
  }

  boolean isTraversable() throws RepositoryDocumentException {
    try {
      int type = delegate.getType();
      return type == SmbFile.TYPE_SHARE || type == SmbFile.TYPE_FILESYSTEM;
    } catch (SmbException e) {
      throw new RepositoryDocumentException(e);
    }
  }
}
TOP

Related Classes of com.google.enterprise.connector.filesystem.SmbReadonlyFile

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.