/*
 * FixedSftpSubsystem.java
 * Copyright 2016 Rob Spoor
 *
 * 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.github.robtimus.filesystems.sftp.server;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.subsystem.sftp.AbstractSftpEventListenerAdapter;
import org.apache.sshd.server.subsystem.sftp.DirectoryHandle;
import org.apache.sshd.server.subsystem.sftp.Handle;
import org.apache.sshd.server.subsystem.sftp.SftpErrorStatusDataHandler;
import org.apache.sshd.server.subsystem.sftp.SftpEventListener;
import org.apache.sshd.server.subsystem.sftp.SftpFileSystemAccessor;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.server.subsystem.sftp.UnsupportedAttributePolicy;

/**
 * {@link SftpSubsystem} does not follow links when the {@code stat} command is executed. This class fixes that.
 *
 * @author Rob Spoor
 */
@SuppressWarnings("javadoc")
public class FixedSftpSubsystem extends SftpSubsystem {

    public FixedSftpSubsystem(ExecutorService executorService, boolean shutdownOnExit, UnsupportedAttributePolicy policy,
            SftpFileSystemAccessor accessor, SftpErrorStatusDataHandler errorStatusDataHandler) {
        super(executorService, shutdownOnExit, policy, accessor, errorStatusDataHandler);
    }

    @Override
    protected Map<String, Object> doStat(int id, String path, int flags) throws IOException {
        Path p = resolveFile(path);
        return resolveFileAttributes(p, flags, IoUtils.getLinkOptions(true));
    }

    public static final class Factory extends SftpSubsystemFactory {

        @Override
        public Command create() {
            SftpSubsystem subsystem = new FixedSftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(),
                    getFileSystemAccessor(), getErrorStatusDataHandler());
            Collection<? extends SftpEventListener> listeners = getRegisteredListeners();
            if (GenericUtils.size(listeners) > 0) {
                for (SftpEventListener l : listeners) {
                    subsystem.addSftpEventListener(l);
                }
            }

            return subsystem;
        }
    }

    public static final class FactoryWithoutSystemDirs extends SftpSubsystemFactory {

        @Override
        public Command create() {
            SftpSubsystem subsystem = new FixedSftpSubsystem(getExecutorService(), isShutdownOnExit(), getUnsupportedAttributePolicy(),
                    getFileSystemAccessor(), getErrorStatusDataHandler());
            Collection<? extends SftpEventListener> listeners = getRegisteredListeners();
            if (GenericUtils.size(listeners) > 0) {
                for (SftpEventListener l : listeners) {
                    subsystem.addSftpEventListener(l);
                }
            }
            subsystem.addSftpEventListener(new AbstractSftpEventListenerAdapter() {
                @Override
                public void open(ServerSession session, String remoteHandle, Handle localHandle) {
                    if (localHandle instanceof DirectoryHandle) {
                        DirectoryHandle directoryHandle = (DirectoryHandle) localHandle;
                        directoryHandle.markDotSent();
                        directoryHandle.markDotDotSent();
                    }
                }
            });

            return subsystem;
        }
    }
}