/* * ------------------------------------------------------------------------------ * Hermes FTP Server * Copyright (c) 2005-2014 Lars Behnke * ------------------------------------------------------------------------------ * * This file is part of Hermes FTP Server. * * Hermes FTP Server is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Hermes FTP Server 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Hermes FTP Server; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ------------------------------------------------------------------------------ */ package com.apporiented.hermesftp.session.impl; import java.net.SocketTimeoutException; import java.text.MessageFormat; import com.apporiented.hermesftp.cmd.FtpCmd; import com.apporiented.hermesftp.common.FtpConstants; import com.apporiented.hermesftp.common.FtpSessionContext; import com.apporiented.hermesftp.exception.FtpCmdException; import com.apporiented.hermesftp.exception.FtpCmdResponseException; import com.apporiented.hermesftp.exception.FtpIllegalCmdException; import com.apporiented.hermesftp.exception.FtpQuitException; import com.apporiented.hermesftp.parser.FtpCmdReader; import com.apporiented.hermesftp.session.FtpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Default FTP session implementation. * * @author Lars Behnke */ public class FtpSessionImpl extends Thread implements FtpSession, FtpConstants { private static final int DEFAULT_IDLE_SECONDS = 60; private static final int COMMAND_TIMEOUT = 3000; private static Log log = LogFactory.getLog(FtpSessionImpl.class); private FtpCmdReader cmdReader; private FtpSessionContext ftpContext; private boolean terminated; /** * Constructor. */ public FtpSessionImpl() { super(); } /** * {@inheritDoc} */ public void run() { try { printWelcome(); getCmdReader().setCtx(getFtpContext()); getCmdReader().start(); long startWaiting = System.currentTimeMillis(); while (!isTerminated()) { FtpCmd cmd; try { cmd = getCmdReader().waitForNextCommand(COMMAND_TIMEOUT); terminated = !executeCmd(cmd); startWaiting = System.currentTimeMillis(); } catch (FtpIllegalCmdException e) { String msg = formatResString(MSG500_CMD, new Object[] {e.getCmdLine()}); out(msg); } catch (SocketTimeoutException e) { long maxIdleSecs = getFtpContext().getOptions().getInt(OPT_MAX_IDLE_SECONDS, DEFAULT_IDLE_SECONDS); if (System.currentTimeMillis() - startWaiting > (maxIdleSecs * MILLI)) { out(formatResString(MSG421, new Object[0])); log.debug("Session timeout after " + maxIdleSecs + " seconds"); terminated = true; } } } } catch (FtpCmdException e) { log.error("Session closed because of error while executing command", e); } finally { terminated = true; getCmdReader().abort(); getFtpContext().closeSockets(); getFtpContext().getEventListener().sessionClosed(this); } } private boolean executeCmd(FtpCmd cmd) throws FtpCmdException { boolean proceed = true; if (cmd != null) { synchronized (cmd) { if (cmd.isAuthenticationRequired() && !getFtpContext().isAuthenticated()) { String msg = getFtpContext().getRes(MSG530); out(msg); } else { try { cmd.setCtx(getFtpContext()); cmd.execute(); } catch (FtpQuitException e) { proceed = false; } catch (FtpCmdResponseException e) { out(e.getMessage()); } finally { cmd.notifyAll(); } } } } return proceed; } private void printWelcome() { String title = getFtpContext().getOptions().getAppTitle(); String version = getFtpContext().getOptions().getAppVersion(); String welcome = formatResString(MSG220_WEL, new Object[] {title + " " + version}); getFtpContext().getClientResponseWriter().println(welcome); welcome = getFtpContext().getOption(OPT_MSG_WELCOME); if (welcome != null && welcome.length() > 0) { welcome = formatResString(MSG220_WEL, new Object[] {welcome}); out(welcome); } out(getFtpContext().getRes(MSG220)); } private String formatResString(String resourceKey, Object[] args) { String msg = getFtpContext().getRes(resourceKey); if (args != null) { msg = MessageFormat.format(msg, args); } return msg; } private void out(String msg) { getFtpContext().getClientResponseWriter().println(msg); getFtpContext().getClientResponseWriter().flush(); } /** * Getter method for the java bean <code>ftpContext</code>. * * @return Returns the value of the java bean <code>ftpContext</code>. */ public FtpSessionContext getFtpContext() { return ftpContext; } /** * Setter method for the java bean <code>ftpContext</code>. * * @param ftpContext The value of ftpContext to set. */ public void setFtpContext(FtpSessionContext ftpContext) { this.ftpContext = ftpContext; } /** * {@inheritDoc} */ public void setCmdReader(FtpCmdReader reader) { this.cmdReader = reader; } private FtpCmdReader getCmdReader() { return cmdReader; } /** * Getter method for the java bean <code>terminated</code>. * * @return Returns the value of the java bean <code>terminated</code>. */ public boolean isTerminated() { return terminated; } /** * @see com.apporiented.hermesftp.session.FtpSession#abort() */ public void abort() { terminated = true; } }