2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.ftpupload.internal.ftp;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.Iterator;
18 import java.util.LinkedHashMap;
19 import java.util.List;
22 import org.apache.ftpserver.DataConnectionConfiguration;
23 import org.apache.ftpserver.FtpServerConfigurationException;
24 import org.apache.ftpserver.FtpServerFactory;
25 import org.apache.ftpserver.ftplet.DefaultFtplet;
26 import org.apache.ftpserver.ftplet.FileSystemFactory;
27 import org.apache.ftpserver.ftplet.FileSystemView;
28 import org.apache.ftpserver.ftplet.FtpException;
29 import org.apache.ftpserver.ftplet.FtpRequest;
30 import org.apache.ftpserver.ftplet.FtpSession;
31 import org.apache.ftpserver.ftplet.FtpStatistics;
32 import org.apache.ftpserver.ftplet.Ftplet;
33 import org.apache.ftpserver.ftplet.FtpletContext;
34 import org.apache.ftpserver.ftplet.FtpletResult;
35 import org.apache.ftpserver.ftplet.User;
36 import org.apache.ftpserver.listener.Listener;
37 import org.apache.ftpserver.listener.ListenerFactory;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 * Simple FTP server implementation to receive files via FTP.
45 * @author Pauli Anttila - Initial contribution
47 public class FtpServer {
49 private final Logger logger = LoggerFactory.getLogger(FtpServer.class);
52 private DataConnectionConfiguration dataConnectionConfiguration;
55 private org.apache.ftpserver.FtpServer server;
56 private List<FtpServerEventListener> listeners;
57 private MyFTPLet myFTPLet;
58 private FTPUserManager FTPUserManager;
59 private String ftpStartUpErrorReason;
62 listeners = new ArrayList<>();
63 FTPUserManager = new FTPUserManager();
66 public void startServer(int port, int idleTimeout, DataConnectionConfiguration dataConnectionConfiguration)
70 this.idleTimeout = idleTimeout;
71 this.dataConnectionConfiguration = dataConnectionConfiguration;
72 FTPUserManager.setIdleTimeout(idleTimeout);
76 public void stopServer() {
82 public String getStartUpErrorReason() {
83 return ftpStartUpErrorReason;
86 public synchronized void addEventListener(FtpServerEventListener listener) {
87 if (!listeners.contains(listener)) {
88 listeners.add(listener);
92 public synchronized void addAuthenticationCredentials(String username, String password)
93 throws IllegalArgumentException {
94 FTPUserManager.addAuthenticationCredentials(username, password);
97 public synchronized void removeAuthenticationCredentials(String username) {
98 FTPUserManager.removeAuthenticationCredentials(username);
101 public synchronized void removeEventListener(FtpServerEventListener listener) {
102 listeners.remove(listener);
105 private void sendMsgToListeners(String userName, String filename, byte[] data) {
106 Iterator<FtpServerEventListener> iterator = listeners.iterator();
108 while (iterator.hasNext()) {
110 iterator.next().fileReceived(userName, filename, data);
111 } catch (Exception e) {
112 // catch all exceptions give all handlers a fair chance of handling the messages
113 logger.debug("Event listener invoking error: {}", e.getMessage());
118 public void printStats() {
119 FtpStatistics ftpStats = myFTPLet.getStats();
121 logger.debug("TotalConnectionNumber: {}", ftpStats.getTotalConnectionNumber());
122 logger.debug("TotalLoginNumber: {}", ftpStats.getTotalLoginNumber());
123 logger.debug("TotalFailedLoginNumber: {}", ftpStats.getTotalFailedLoginNumber());
124 logger.debug("TotalUploadNumber: {}", ftpStats.getTotalUploadNumber());
125 logger.debug("TotalUploadSize: {}", ftpStats.getTotalUploadSize());
127 logger.debug("CurrentConnectionNumber: {}", ftpStats.getCurrentConnectionNumber());
128 logger.debug("CurrentLoginNumber: {}", ftpStats.getCurrentLoginNumber());
131 private void initServer() throws FtpException {
132 FtpServerFactory serverFactory = new FtpServerFactory();
133 ListenerFactory listenerFactory = new ListenerFactory();
135 listenerFactory.setPort(port);
136 listenerFactory.setIdleTimeout(idleTimeout);
137 listenerFactory.setDataConnectionConfiguration(dataConnectionConfiguration);
139 Listener listener = listenerFactory.createListener();
141 serverFactory.addListener("default", listener);
143 Map<String, Ftplet> ftplets = new LinkedHashMap<>();
144 myFTPLet = new MyFTPLet();
146 ftplets.put("ftplet", myFTPLet);
148 serverFactory.setFtplets(ftplets);
149 serverFactory.setFileSystem(new FileSystemFactory() {
151 public FileSystemView createFileSystemView(User user) throws FtpException {
152 logger.debug("createFileSystemView: {}", user.getName());
153 return new SimpleFileSystemView();
157 // set the user manager
158 serverFactory.setUserManager(FTPUserManager);
159 server = serverFactory.createServer();
163 ftpStartUpErrorReason = null;
164 } catch (FtpException | FtpServerConfigurationException e) {
165 ftpStartUpErrorReason = "Failed to start FTP server";
166 if (!e.getMessage().isEmpty()) {
167 ftpStartUpErrorReason += ": " + e.getMessage();
173 private class MyFTPLet extends DefaultFtplet {
174 FtpletContext ftpletContext;
176 public FtpStatistics getStats() {
177 return ftpletContext.getFtpStatistics();
181 public void init(FtpletContext ftpletContext) throws FtpException {
182 this.ftpletContext = ftpletContext;
186 public void destroy() {
187 logger.trace("destroy");
191 public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
192 logger.debug("User connected to FtpServer");
193 return super.onConnect(session);
197 public FtpletResult onUploadEnd(final FtpSession session, final FtpRequest request)
198 throws FtpException, IOException {
199 String userRoot = session.getUser().getHomeDirectory();
200 String currDir = session.getFileSystemView().getWorkingDirectory().getAbsolutePath();
201 String fileName = request.getArgument();
203 logger.debug("File {} upload to FTP server", userRoot + currDir + "/" + fileName);
205 SimpleFtpFile file = (SimpleFtpFile) session.getFileSystemView().getFile(fileName);
206 byte[] data = file.getData();
208 sendMsgToListeners(session.getUser().getName(), fileName, data);
209 return FtpletResult.SKIP;