/*******************************************************************************
* Copyright 2012 I3M-GRyCAP
*
* 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 org.grycap.vmrc.repository.transfer;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.Ftplet;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.DbUserManagerFactory;
import org.apache.ftpserver.usermanager.Md5PasswordEncryptor;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.DbUserManager;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.apache.log4j.Logger;
import org.grycap.vmrc.utils.VMRCServerUtils;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class FTPServer {
public static int MAX_FTP_USERS = 100;
public static int DEFAULT_FTP_PORT = 21000;
public static String FTP_USERS_FILE = "users.properties";
private int numConnections;
private String host;
private FtpServerFactory serverFactory;
private PropertiesUserManagerFactory userManagerFactory;
private UserManager userManager;
private FtpServer server;
private Logger log;
public FTPServer() throws IOException{
this.log = Logger.getLogger(getClass());
this.numConnections = 0;
if (this.serverFactory == null)
this.serverFactory = configureFtpServerFactory();
}
private String selectNewUser() {
String newUser = null;
for (int i=0; i<MAX_FTP_USERS; i++) {
try {
newUser = "user"+i;
if (! userManager.doesExist(newUser)) break;
} catch (FtpException e) {
log.error("Error reading user file from FTP server.");
return null;
}
if (i == MAX_FTP_USERS-1) {
log.error("Maximum number of users exceeded.");
return null;
}
}
return newUser;
}
public void deleteNewUser(String user) throws FtpException {
if (userManager.doesExist(user)) userManager.delete(user);
}
/**
* Create
* @param vmiName The name of the VMI
* @param vmiFileName The filename of the VMI (to be uploaded or retrieved)
* @param type The type of connection (upload, download) (@see FTPSession)
* @return
*/
public FTPTransferParams createOnTheFlyFTPConfiguration(String vmiName, String vmiFileName, int type) throws FtpException{
log.debug("Creating on-the-fly FTP configuration");
FTPTransferParams tp = new FTPTransferParams();
BaseUser user = new BaseUser();
String filePath, msg;
String newUser = selectNewUser();
if (newUser == null) {
msg = "Error creating new user.";
log.error(msg);
throw new FtpException(msg);
}
// Create a temporary user in the FTP server.
user.setName(newUser);
String newPassword = VMRCServerUtils.generateRandomChain(8);
user.setPassword(newPassword);
if (type == FTPSession.TYPE_UPLOAD){
filePath = RepositoryManager.getRepositoryLocationToStoreFile(vmiName, vmiFileName);
log.debug("Creating the appropriate dirs to host file: " + vmiFileName);
new File(filePath).getParentFile().mkdirs();
user.setHomeDirectory(new File(filePath).getParent());
List<Authority> auths = new ArrayList<Authority>();
Authority auth = new WritePermission();
auths.add(auth);
user.setAuthorities(auths);
} else{ //DOWNLOAD
String[] files = VMRCServerUtils.listDirectory(RepositoryManager.getRepositoryLocationForVMI(vmiName));
if (files == null){
msg = "No file in the repository found for VMI " + vmiName;
log.error(msg);
throw new FtpException(msg);
}
log.debug("Found " + files.length + " related to " + vmiName + " in the repository. Will pick the first one.");
filePath = RepositoryManager.getRepositoryLocationToStoreFile(vmiName,files[0]);
log.debug("Obtained file " + filePath);
}
try {
// Store the user into users.properties file
userManager.save(user);
} catch (FtpException e) {
deleteNewUser(newUser);
}
tp.setUser(newUser);
tp.setPass(newPassword);
tp.setPath(filePath);
tp.setHost(this.host);
tp.setPort(DEFAULT_FTP_PORT);
return tp;
}
private void startServerInternal() throws FtpException {
log.info("Creating FTP server.");
if (server == null){
log.debug("Creating and starting FtpServer instance");
server = serverFactory.createServer();
server.start();
}
log.info("Resuming already existing FTP server instance.");
server.resume();
log.info("Available FTP server: " + server);
}
/**
* @param args
*/
public void suspendServerInternal() {
server.suspend();
}
/**
* @param args
*/
public synchronized void notifyEndOfTransfer() {
numConnections--;
log.debug("Finalizing FTP session. ("+ numConnections + ") active sessions");
//TODO Removed created user
if (numConnections==0) {
log.debug("No active FTP sessions. Suspending FTP server.");
suspendServerInternal();
}
}
/**
* @param args
* @throws FtpException
*/
public synchronized void notifyStartOfTransfer() throws FtpException {
if (numConnections == 0) {
log.debug("Initiating first FTP session. Starting up the FTP server.");
startServerInternal();
numConnections++;
}
}
/**
* @param args
* @throws IOException
*/
public FtpServerFactory configureFtpServerFactory() throws IOException{
FtpServerFactory fsf = new FtpServerFactory();
log.info("Configuring an on-the-fly FTP server.");
String ftpUsersFilePath = null;
File fFTPServerUserFile = null;
ftpUsersFilePath = RepositoryManager.getRepositoryLocation() + File.separator + "conf" + File.separator + FTP_USERS_FILE;
log.debug("Creating FTP users file at " + ftpUsersFilePath);
fFTPServerUserFile = new File(ftpUsersFilePath);
fFTPServerUserFile.mkdirs();
fFTPServerUserFile.delete();
fFTPServerUserFile.createNewFile();
final Map<String, Ftplet> ftpletMap = new HashMap<String, Ftplet>();
Ftplet ftplet = new ConfFtplet(this);
ftpletMap.put("default",ftplet);
fsf.setFtplets(ftpletMap);
userManagerFactory = new PropertiesUserManagerFactory();
userManagerFactory.setFile(new File(ftpUsersFilePath));
userManagerFactory.setPasswordEncryptor(new Md5PasswordEncryptor());
userManager = userManagerFactory.createUserManager();
ListenerFactory listenerFactory = new ListenerFactory();
listenerFactory.setPort(DEFAULT_FTP_PORT);
fsf.addListener("default", listenerFactory.createListener());
fsf.setUserManager(userManager);
// Inicializar host ip.
this.host = VMRCServerUtils.getPublicIP();
return fsf;
}
}