/**
 * PODD is an OWL ontology database used for scientific project management
 *
 * Copyright (C) 2009-2013 The University Of Queensland
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Affero General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package com.github.podd.impl.data;

import java.io.IOException;

import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.FileAttributes;
import net.schmizz.sshj.sftp.SFTPClient;

import org.openrdf.model.Model;
import org.openrdf.model.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.podd.api.data.PoddDataRepository;
import com.github.podd.api.data.SSHFileReference;
import com.github.podd.exception.DataReferenceNotSupportedException;
import com.github.podd.exception.DataRepositoryIncompleteException;
import com.github.podd.ontologies.PODDDATAREPOSITORY;
import com.github.podd.utils.PODD;

/**
 * @author kutila
 *
 */
public class SSHFileRepositoryImpl extends AbstractPoddDataRepositoryImpl<SSHFileReference>
{
    private static final DefaultConfig DEFAULT_CONFIG = new DefaultConfig();
    
    protected final Logger log = LoggerFactory.getLogger(this.getClass());
    
    public SSHFileRepositoryImpl(final Resource nextDataRepository, final Model model)
        throws DataRepositoryIncompleteException
    {
        super(nextDataRepository, model);
        
        // check that the model contains values for protocol, host, port,
        // fingerprint, username, and
        // secret
        final String protocol = model.filter(this.aliasUri, PODD.PODD_DATA_REPOSITORY_PROTOCOL, null).objectString();
        final String host = model.filter(this.aliasUri, PODD.PODD_DATA_REPOSITORY_HOST, null).objectString();
        final String port = model.filter(this.aliasUri, PODD.PODD_DATA_REPOSITORY_PORT, null).objectString();
        final String fingerprint =
                model.filter(this.aliasUri, PODD.PODD_FILE_REPOSITORY_FINGERPRINT, null).objectString();
        final String username = model.filter(this.aliasUri, PODD.PODD_FILE_REPOSITORY_USERNAME, null).objectString();
        final String secret = model.filter(this.aliasUri, PODD.PODD_FILE_REPOSITORY_SECRET, null).objectString();
        final String privateKey =
                model.filter(this.aliasUri, PODDDATAREPOSITORY.HAS_DATA_REPOSITORY_PRIVATE_KEY, null).objectString();
        
        if(protocol == null || host == null || port == null || fingerprint == null || username == null
                || (secret == null && privateKey == null))
        {
            throw new DataRepositoryIncompleteException("SSH repository configuration incomplete");
        }
        
        if(!PoddDataRepository.PROTOCOL_SSH.equalsIgnoreCase(protocol))
        {
            throw new DataRepositoryIncompleteException("Protocol needs to be SSH");
        }
    }
    
    @Override
    public boolean canHandle(final SSHFileReference reference)
    {
        if(reference == null)
        {
            return false;
        }
        
        // unnecessary as Generics ensure only an SSHFileReference can be passed
        // in
        if(!(reference instanceof SSHFileReference))
        {
            return false;
        }
        
        final String aliasFromFileRef = reference.getRepositoryAlias();
        if(aliasFromFileRef == null || !this.alias.equalsIgnoreCase(aliasFromFileRef))
        {
            return false;
        }
        
        return true;
    }
    
    @Override
    public boolean validate(final SSHFileReference dataReference) throws DataReferenceNotSupportedException,
        IOException
    {
        if(!this.canHandle(dataReference))
        {
            throw new DataReferenceNotSupportedException(dataReference, "cannot handle file reference for validation");
        }
        
        final String host = this.model.filter(this.aliasUri, PODD.PODD_DATA_REPOSITORY_HOST, null).objectString();
        final String port = this.model.filter(this.aliasUri, PODD.PODD_DATA_REPOSITORY_PORT, null).objectString();
        final String fingerprint =
                this.model.filter(this.aliasUri, PODD.PODD_FILE_REPOSITORY_FINGERPRINT, null).objectString();
        final String username =
                this.model.filter(this.aliasUri, PODD.PODD_FILE_REPOSITORY_USERNAME, null).objectString();
        final String secret = this.model.filter(this.aliasUri, PODD.PODD_FILE_REPOSITORY_SECRET, null).objectString();
        
        int portNo = -1;
        try
        {
            portNo = Integer.parseInt(port);
        }
        catch(final NumberFormatException e)
        {
            throw new IOException("Port number could not be parsed correctly: " + port);
        }
        
        String fileName = dataReference.getFilename();
        final String path = dataReference.getPath();
        if(path != null && path.trim().length() > 0)
        {
            fileName = path + "/" + fileName;
        }
        
        this.log.info("Validating file reference: " + host + ":" + port + " " + fileName);
        
        try (SSHClient sshClient = new SSHClient(SSHFileRepositoryImpl.DEFAULT_CONFIG);)
        {
            sshClient.addHostKeyVerifier(fingerprint);
            sshClient.connect(host, portNo);
            
            sshClient.authPassword(username, secret);
            
            try (SFTPClient sftp = sshClient.newSFTPClient();)
            {
                // check details of a remote file
                final FileAttributes attribs = sftp.lstat(fileName);
                if(attribs == null || attribs.getSize() <= 0)
                {
                    return false;
                }
            }
            catch(final IOException e)
            {
                // lstat() throws an IOException if the file does not exist
                return false;
            }
        }
        return true;
    }
    
}

@@ -2923,23 +2923,53 @@
 VATE_KEY
+_PATH
 , null)
+%0A                        
 .objectS

fix broken reference