Package cu.ftpd.commands.transfer

Source Code of cu.ftpd.commands.transfer.CommandRETR

/**
* Copyright (c) 2007, Markus Jevring <markus@jevring.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The names of the contributors may not be used to endorse or promote
*    products derived from this software without specific prior written
*    permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
package cu.ftpd.commands.transfer;

import cu.ftpd.Connection;
import cu.ftpd.Server;
import cu.ftpd.ServiceManager;
import cu.ftpd.events.Event;
import cu.ftpd.events.EventFactory;
import cu.ftpd.filesystem.FileSystem;
import cu.ftpd.filesystem.Section;
import cu.ftpd.filesystem.filters.ForbiddenFilesFilter;
import cu.ftpd.filesystem.permissions.ActionPermission;
import cu.ftpd.filesystem.permissions.SpeedPermission;
import cu.ftpd.logging.Logging;
import cu.ftpd.user.User;
import cu.ftpd.user.UserPermission;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

/**
* @author Markus Jevring <markus@jevring.net>
* @since 2008-jan-09 - 10:26:17
* @version $Id: CommandRETR.java 305 2011-02-07 18:33:18Z jevring $
*/
public class CommandRETR implements TransferController {
    private final Connection connection;
    private final FileSystem fs;
    private final User user;
    private final String filename;
    private final boolean encryptedDataConnection;
    private final boolean sscn;
    private final boolean fastAsciiTransfer;
    private boolean semaphoreTaken;
    private FileInputStream in;
    private TransferThread transfer;
    private Section section;
    private Socket dataConnection;
    private File file;
    private static String[] freeFiles;
    private InetAddress remoteHost;

    public CommandRETR(Connection connection, FileSystem fs, User user, String filename, boolean encryptedDataConnection, boolean sscn, boolean fastAsciiTransfer) {
        this.connection = connection;
        this.fs = fs;
        this.user = user;
        this.filename = filename;
        this.encryptedDataConnection = encryptedDataConnection;
        this.sscn = sscn;
        this.fastAsciiTransfer = fastAsciiTransfer;
    }

    public void start() {
        connection.dataConnectionSemaphore.acquireUninterruptibly();
        try {
            // note: we must collect the data connection here, because if we don't, then it will be null when we close due to an error, but won't be closed in Connection
            dataConnection = connection.getDataConnection();
            // due to the fact that getInetAddress() can return null if we are not connected, we have to do a little special thing here, which can cause irregularities in the xferlog, but so be it
            remoteHost = dataConnection.getInetAddress();
            // try semaphores first, as the other way around never makes privileged users count towards the max for unprivileged users, effectively dividing them into two groups
            if ((semaphoreTaken = Server.getInstance().getDownloadSemaphore().tryAcquire()) || user.hasPermission(UserPermission.PRIVILEGED)) {
                try {
                    file = fs.resolveFile(filename);
                    String ftpPathToFile = FileSystem.resolvePath(file);
                    // NOTE: the FileInputStream will take care of throwing an exception if the file turns out to be a dir
                    if (file.exists()) {
                        if (ServiceManager.getServices().getPermissions().hasPermission(ActionPermission.DOWNLOAD, ftpPathToFile, user)) {
                            if (!ForbiddenFilesFilter.getForbiddenFiles().contains(filename)) {
                                if (user.hasLeech() || file.length() < user.getCredits()) {
                                    if (remoteHost != null) {
                                        // if we have an address, and we are either transferring data to the same host that we are connecting from, or we have permission to FXPUP, then continue
                                        if (remoteHost.equals(connection.getClientHost()) || ServiceManager.getServices().getPermissions().hasPermission(ActionPermission.FXPDOWNLOAD, ftpPathToFile, user)) {
                                            Event event = EventFactory.download(user, fs, file, remoteHost, 0, 0, "PENDING");
                                            boolean proceed = ServiceManager.getServices().getEventHandler().handleBeforeEvent(event, connection);
                                            if (proceed) {
                                                section = fs.getSection(file);
                                                in = new FileInputStream(file);
                                                createTransfer();
                                                connection.respond("150 Opening " + fs.getType() + " mode data connection for RETR command" + (encryptedDataConnection ? " with SSL/TLS encryption." : "."));
                                                connection.setControlConnectionTimeout(0);
                                                transfer.start();
                                            } else {
                                                close();
                                                // Note: we don't send anything to the connection here, because that is the job of the event handler
                                                // when no handlers are registered, proceed will always be 'true'
                                            }
                                        } else {
                                            close();
                                            connection.respond("531 You do not have permission to download to another host than the one you are connecting from (FXPDOWN)");
                                        }
                                    } else {
                                        close();
                                        connection.respond("500 Remote host unknown, probably due to a closed data connection, transfer failed.");
                                    }
                                } else {
                                    close();
                                    connection.respond("500 Insufficient credits, transfer aborted.");
                                }
                            } else {
                                close();
                                connection.respond("531 Forbidden.");
                            }
                        } else {
                            close();
                            connection.respond("531 Permission denied.");
                        }
                    } else {
                        close();
                        connection.respond("550 File not found: " + FileSystem.resolvePath(file));
                    }
                } catch (IOException e) {
                    close();
                    connection.respond("500 " + e.getMessage());
                }
            } else {
                close();
                connection.respond("553 Too many users downloading at the moment.");
            }
        } catch (IllegalStateException e) {
            connection.respond("500 " + e.getMessage());
            connection.dataConnectionSemaphore.release();
        }
    }

    public long getSpeed() {
        if (transfer != null) {
            return transfer.getCurrentSpeed();
        } else {
            return 0;
        }
    }

    private void createTransfer() throws IOException {
        if (encryptedDataConnection) {
            ((javax.net.ssl.SSLSocket)dataConnection).setUseClientMode(sscn);
        }
        if ("ASCII".equals(fs.getType())) {
            if (fastAsciiTransfer) {
                transfer = new CharacterTransferThread(this, new InputStreamReader(in, "ISO-8859-1"), new BufferedWriter(new OutputStreamWriter(dataConnection.getOutputStream(), "ISO-8859-1")));
            } else {
                transfer = new TranslatingCharacterTransferThread(this, new BufferedReader(new InputStreamReader(in, "ISO-8859-1")), new BufferedWriter(new OutputStreamWriter(dataConnection.getOutputStream(), "ISO-8859-1")), false);
            }
        } else {
            if (fs.getOffset() > 0) {
                in.skip(fs.getOffset()); // it's native, so it'll be fast
            }
            long limit = ServiceManager.getServices().getPermissions().getLimit(user, FileSystem.resolvePath(file.getParentFile()), SpeedPermission.DOWNLOAD);
            if (limit <= 0) {
                transfer = new ByteTransferThread(this, in, dataConnection.getOutputStream());
            } else {
                transfer = new SpeedLimitingByteTransferThread(this, in, dataConnection.getOutputStream(), limit);
            }
        }
    }

    public void error(Exception e, long bytesTransferred, long transferTime) {
        close();
        log(bytesTransferred, transferTime);
        connection.reportTransferFailure(e.getMessage());
        Event event = EventFactory.download(user, fs, file, remoteHost, bytesTransferred, transferTime, "FAILED");
        ServiceManager.getServices().getEventHandler().handleAfterEvent(event);

    }

    public void complete(long bytesTransferred, long transferTime) {
        close();
        log(bytesTransferred, transferTime);
        connection.respond("226- " + fs.getType() + " transfer of " + filename + " completed.");
        connection.statline(transfer.getSpeed());
        Event event = EventFactory.download(user, fs, file, remoteHost, bytesTransferred, transferTime, "COMPLETE");
        ServiceManager.getServices().getEventHandler().handleAfterEvent(event);
    }

    private void log(long bytesTransferred, long transferTime) {
        if (!user.hasLeech() && !isFreeFile(filename)) {
            user.takeCredits(bytesTransferred);
        }
        ServiceManager.getServices().getUserStatistics().download(user.getUsername(), section.getName(), bytesTransferred, transferTime);
    }

    private void close() {
        if (semaphoreTaken) {
            Server.getInstance().getDownloadSemaphore().release();
        }
        fs.rest(0);
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                Logging.getErrorLog().reportException("Failed to close file input stream", e);
                //e.printStackTrace();
            }
        }

        if (dataConnection != null) {
            try {
                dataConnection.close();
            } catch (IOException e) {
                Logging.getErrorLog().reportException("Failed to close data connection", e);
                //e.printStackTrace();
            }
        }
        // this ensures that, if a connection is broken, the timeout for the control connection is reset, so that it, too, can timeout
        connection.resetControlConnectionTimeout();
        connection.dataConnectionSemaphore.release();
    }

    public boolean isRunning() {
        return transfer != null && transfer.isAlive();
    }

    private static boolean isFreeFile(String filename) {
        if (freeFiles == null) {
            return false;
        } else {
            for (int i = 0; i < freeFiles.length; i++) {
                String freeFile = freeFiles[i];
                if (filename.matches(freeFile)) {
                    //System.out.println(filename + " matches the free_file pattern " + freeFile);
                    return true;
                }
            }
            return false;
        }
    }

    private static String[] createFreeFiles(String freeFilesString) {
        String[] freeFiles = null;
        if (freeFilesString != null) {
            freeFiles = freeFilesString.split(" ");
            for (int i = 0; i < freeFiles.length; i++) {
                freeFiles[i] = freeFiles[i].replace(".","\\.").replace("*",".*");
            }
        }
        return freeFiles;
    }

    public static void initialize(String freeFilesString) {
        freeFiles = createFreeFiles(freeFilesString);
    }
}
TOP

Related Classes of cu.ftpd.commands.transfer.CommandRETR

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.