added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.ftpupload-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-ftpupload" description="FTP Upload Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ftpupload/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link FtpUploadBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FtpUploadBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "ftpupload";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_IMAGERECEIVER = new ThingTypeUID(BINDING_ID, "imagereceiver");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String IMAGE = "image";
|
||||
public static final String IMAGE_RECEIVED_TRIGGER = "image-received";
|
||||
|
||||
// List of all channel parameters
|
||||
public static final String PARAM_FILENAME_PATTERN = "filename";
|
||||
|
||||
public static final String EVENT_IMAGE_RECEIVED = "IMAGE_RECEIVED";
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal;
|
||||
|
||||
import static org.openhab.binding.ftpupload.internal.FtpUploadBindingConstants.THING_TYPE_IMAGERECEIVER;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Dictionary;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.ftpserver.FtpServerConfigurationException;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.openhab.binding.ftpupload.internal.ftp.FtpServer;
|
||||
import org.openhab.binding.ftpupload.internal.handler.FtpUploadHandler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FtpUploadHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.ftpupload")
|
||||
public class FtpUploadHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final Logger logger = LoggerFactory.getLogger(FtpUploadHandlerFactory.class);
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_IMAGERECEIVER);
|
||||
|
||||
private final int DEFAULT_PORT = 2121;
|
||||
private final int DEFAULT_IDLE_TIMEOUT = 60;
|
||||
|
||||
private FtpServer ftpServer;
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_IMAGERECEIVER)) {
|
||||
if (ftpServer.getStartUpErrorReason() != null) {
|
||||
thing.setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
ftpServer.getStartUpErrorReason()));
|
||||
}
|
||||
return new FtpUploadHandler(thing, ftpServer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void activate(ComponentContext componentContext) {
|
||||
super.activate(componentContext);
|
||||
ftpServer = new FtpServer();
|
||||
modified(componentContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void deactivate(ComponentContext componentContext) {
|
||||
stopFtpServer();
|
||||
ftpServer = null;
|
||||
super.deactivate(componentContext);
|
||||
}
|
||||
|
||||
protected synchronized void modified(ComponentContext componentContext) {
|
||||
stopFtpServer();
|
||||
Dictionary<String, Object> properties = componentContext.getProperties();
|
||||
|
||||
int port = DEFAULT_PORT;
|
||||
int idleTimeout = DEFAULT_IDLE_TIMEOUT;
|
||||
|
||||
if (properties.get("port") != null) {
|
||||
String strPort = properties.get("port").toString();
|
||||
if (StringUtils.isNotEmpty(strPort)) {
|
||||
try {
|
||||
port = Integer.valueOf(strPort);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("Invalid port number '{}', using default port {}", strPort, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.get("idleTimeout") != null) {
|
||||
String strIdleTimeout = properties.get("idleTimeout").toString();
|
||||
if (StringUtils.isNotEmpty(strIdleTimeout)) {
|
||||
try {
|
||||
idleTimeout = Integer.valueOf(strIdleTimeout);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("Invalid idle timeout '{}', using default timeout {}", strIdleTimeout, idleTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("Starting FTP server, port={}, idleTimeout={}", port, idleTimeout);
|
||||
ftpServer.startServer(port, idleTimeout);
|
||||
} catch (FtpException | FtpServerConfigurationException e) {
|
||||
logger.warn("FTP server starting failed, reason: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void stopFtpServer() {
|
||||
logger.debug("Stopping FTP server");
|
||||
ftpServer.stopServer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.config;
|
||||
|
||||
/**
|
||||
* Configuration class for {@link FtpUploadBinding} device.
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
|
||||
public class FtpUploadConfig {
|
||||
|
||||
public String userName;
|
||||
public String password;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String str = "";
|
||||
|
||||
str += "userName = " + userName;
|
||||
str += ", password = *****";
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ftpserver.ftplet.Authority;
|
||||
import org.apache.ftpserver.ftplet.AuthorizationRequest;
|
||||
import org.apache.ftpserver.ftplet.User;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple FTP user implementation.
|
||||
*
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public class FTPUser implements User {
|
||||
private static Logger logger = LoggerFactory.getLogger(FTPUser.class);
|
||||
|
||||
private final String login;
|
||||
private int idleTimeout;
|
||||
|
||||
public FTPUser(String login, int idleTimeout) {
|
||||
this.login = login;
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationRequest authorize(final AuthorizationRequest authRequest) {
|
||||
logger.trace("authorize: {}", authRequest);
|
||||
return authRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnabled() {
|
||||
logger.trace("getEnabled");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHomeDirectory() {
|
||||
logger.trace("getHomeDirectory");
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxIdleTime() {
|
||||
logger.trace("getMaxIdleTime");
|
||||
return idleTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
logger.trace("getName");
|
||||
return this.login;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authority> getAuthorities() {
|
||||
logger.trace("getAuthorities");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authority> getAuthorities(Class<? extends Authority> arg0) {
|
||||
logger.trace("getAuthorities: {}", arg0);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
logger.trace("getPassword");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ftpserver.ftplet.Authentication;
|
||||
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.ftplet.User;
|
||||
import org.apache.ftpserver.ftplet.UserManager;
|
||||
import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple FTP user manager implementation.
|
||||
*
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public class FTPUserManager implements UserManager {
|
||||
private final Logger logger = LoggerFactory.getLogger(FTPUserManager.class);
|
||||
|
||||
private int idleTimeout;
|
||||
private Map<String, UsernamePassword> authenticationData = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public User authenticate(final Authentication inAuth) throws AuthenticationFailedException {
|
||||
logger.trace("authenticate: {}", inAuth);
|
||||
|
||||
UsernamePasswordAuthentication upa = (UsernamePasswordAuthentication) inAuth;
|
||||
String login = upa.getUsername();
|
||||
String password = upa.getPassword();
|
||||
|
||||
if (!autheticate(login, password)) {
|
||||
throw new AuthenticationFailedException();
|
||||
}
|
||||
return new FTPUser(login, idleTimeout);
|
||||
}
|
||||
|
||||
private boolean autheticate(String login, String password) {
|
||||
boolean result = false;
|
||||
|
||||
if (login != null && password != null) {
|
||||
UsernamePassword credential = authenticationData.get(login);
|
||||
|
||||
if (credential != null) {
|
||||
if (login.equals(credential.getUsername()) && password.equals(credential.getPassword())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setIdleTimeout(int idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
public synchronized void addAuthenticationCredentials(String username, String password)
|
||||
throws IllegalArgumentException {
|
||||
if (authenticationData.containsKey(username)) {
|
||||
throw new IllegalArgumentException("Credentials for user '" + username + "' already exists!");
|
||||
}
|
||||
authenticationData.put(username, new UsernamePassword(username, password));
|
||||
}
|
||||
|
||||
public synchronized void removeAuthenticationCredentials(String username) {
|
||||
authenticationData.remove(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByName(final String login) throws FtpException {
|
||||
logger.trace("getUserByName: {}", login);
|
||||
return new FTPUser(login, idleTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String arg0) throws FtpException {
|
||||
logger.trace("delete: {}", arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesExist(String arg0) throws FtpException {
|
||||
logger.trace("doesExist: {}", arg0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdminName() throws FtpException {
|
||||
logger.trace("getAdminName");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAllUserNames() throws FtpException {
|
||||
logger.trace("getAllUserNames");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdmin(String arg0) throws FtpException {
|
||||
logger.trace("isAdmin: {}", arg0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(User arg0) throws FtpException {
|
||||
logger.trace("save: {}", arg0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ftpserver.FtpServerConfigurationException;
|
||||
import org.apache.ftpserver.FtpServerFactory;
|
||||
import org.apache.ftpserver.ftplet.DefaultFtplet;
|
||||
import org.apache.ftpserver.ftplet.FileSystemFactory;
|
||||
import org.apache.ftpserver.ftplet.FileSystemView;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.ftplet.FtpRequest;
|
||||
import org.apache.ftpserver.ftplet.FtpSession;
|
||||
import org.apache.ftpserver.ftplet.FtpStatistics;
|
||||
import org.apache.ftpserver.ftplet.Ftplet;
|
||||
import org.apache.ftpserver.ftplet.FtpletContext;
|
||||
import org.apache.ftpserver.ftplet.FtpletResult;
|
||||
import org.apache.ftpserver.ftplet.User;
|
||||
import org.apache.ftpserver.listener.Listener;
|
||||
import org.apache.ftpserver.listener.ListenerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple FTP server implementation to receive files via FTP.
|
||||
*
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public class FtpServer {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FtpServer.class);
|
||||
|
||||
private int port;
|
||||
int idleTimeout;
|
||||
|
||||
private org.apache.ftpserver.FtpServer server;
|
||||
private List<FtpServerEventListener> listeners;
|
||||
private MyFTPLet myFTPLet;
|
||||
private FTPUserManager FTPUserManager;
|
||||
private String ftpStartUpErrorReason;
|
||||
|
||||
public FtpServer() {
|
||||
listeners = new ArrayList<>();
|
||||
FTPUserManager = new FTPUserManager();
|
||||
}
|
||||
|
||||
public void startServer(int port, int idleTimeout) throws FtpException {
|
||||
stopServer();
|
||||
this.port = port;
|
||||
this.idleTimeout = idleTimeout;
|
||||
FTPUserManager.setIdleTimeout(idleTimeout);
|
||||
initServer();
|
||||
}
|
||||
|
||||
public void stopServer() {
|
||||
if (server != null) {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public String getStartUpErrorReason() {
|
||||
return ftpStartUpErrorReason;
|
||||
}
|
||||
|
||||
public synchronized void addEventListener(FtpServerEventListener listener) {
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addAuthenticationCredentials(String username, String password)
|
||||
throws IllegalArgumentException {
|
||||
FTPUserManager.addAuthenticationCredentials(username, password);
|
||||
}
|
||||
|
||||
public synchronized void removeAuthenticationCredentials(String username) {
|
||||
FTPUserManager.removeAuthenticationCredentials(username);
|
||||
}
|
||||
|
||||
public synchronized void removeEventListener(FtpServerEventListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private void sendMsgToListeners(String userName, String filename, byte[] data) {
|
||||
Iterator<FtpServerEventListener> iterator = listeners.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
try {
|
||||
iterator.next().fileReceived(userName, filename, data);
|
||||
} catch (Exception e) {
|
||||
// catch all exceptions give all handlers a fair chance of handling the messages
|
||||
logger.debug("Event listener invoking error: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void printStats() {
|
||||
FtpStatistics ftpStats = myFTPLet.getStats();
|
||||
|
||||
logger.debug("TotalConnectionNumber: {}", ftpStats.getTotalConnectionNumber());
|
||||
logger.debug("TotalLoginNumber: {}", ftpStats.getTotalLoginNumber());
|
||||
logger.debug("TotalFailedLoginNumber: {}", ftpStats.getTotalFailedLoginNumber());
|
||||
logger.debug("TotalUploadNumber: {}", ftpStats.getTotalUploadNumber());
|
||||
logger.debug("TotalUploadSize: {}", ftpStats.getTotalUploadSize());
|
||||
|
||||
logger.debug("CurrentConnectionNumber: {}", ftpStats.getCurrentConnectionNumber());
|
||||
logger.debug("CurrentLoginNumber: {}", ftpStats.getCurrentLoginNumber());
|
||||
}
|
||||
|
||||
private void initServer() throws FtpException {
|
||||
FtpServerFactory serverFactory = new FtpServerFactory();
|
||||
ListenerFactory listenerFactory = new ListenerFactory();
|
||||
listenerFactory.setPort(port);
|
||||
listenerFactory.setIdleTimeout(idleTimeout);
|
||||
|
||||
Listener listener = listenerFactory.createListener();
|
||||
|
||||
serverFactory.addListener("default", listener);
|
||||
|
||||
Map<String, Ftplet> ftplets = new LinkedHashMap<>();
|
||||
myFTPLet = new MyFTPLet();
|
||||
|
||||
ftplets.put("ftplet", myFTPLet);
|
||||
|
||||
serverFactory.setFtplets(ftplets);
|
||||
serverFactory.setFileSystem(new FileSystemFactory() {
|
||||
@Override
|
||||
public FileSystemView createFileSystemView(User user) throws FtpException {
|
||||
logger.debug("createFileSystemView: {}", user.getName());
|
||||
return new SimpleFileSystemView();
|
||||
}
|
||||
});
|
||||
|
||||
// set the user manager
|
||||
serverFactory.setUserManager(FTPUserManager);
|
||||
server = serverFactory.createServer();
|
||||
|
||||
try {
|
||||
server.start();
|
||||
ftpStartUpErrorReason = null;
|
||||
} catch (FtpException | FtpServerConfigurationException e) {
|
||||
ftpStartUpErrorReason = "Failed to start FTP server";
|
||||
if (!e.getMessage().isEmpty()) {
|
||||
ftpStartUpErrorReason += ": " + e.getMessage();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private class MyFTPLet extends DefaultFtplet {
|
||||
FtpletContext ftpletContext;
|
||||
|
||||
public FtpStatistics getStats() {
|
||||
return ftpletContext.getFtpStatistics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FtpletContext ftpletContext) throws FtpException {
|
||||
this.ftpletContext = ftpletContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
logger.trace("destroy");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
|
||||
logger.debug("User connected to FtpServer");
|
||||
return super.onConnect(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onUploadEnd(final FtpSession session, final FtpRequest request)
|
||||
throws FtpException, IOException {
|
||||
String userRoot = session.getUser().getHomeDirectory();
|
||||
String currDir = session.getFileSystemView().getWorkingDirectory().getAbsolutePath();
|
||||
String fileName = request.getArgument();
|
||||
|
||||
logger.debug("File {} upload to FTP server", userRoot + currDir + "/" + fileName);
|
||||
|
||||
SimpleFtpFile file = (SimpleFtpFile) session.getFileSystemView().getFile(fileName);
|
||||
byte[] data = file.getData();
|
||||
|
||||
sendMsgToListeners(session.getUser().getName(), fileName, data);
|
||||
return FtpletResult.SKIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* This interface defines interface to receive data from FTP server.
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public interface FtpServerEventListener {
|
||||
|
||||
/**
|
||||
* Procedure for receive raw data from FTP server.
|
||||
*
|
||||
* @param userName User name.
|
||||
* @param filename Received filename.
|
||||
* @param data Received raw data.
|
||||
*/
|
||||
void fileReceived(@NonNull String userName, @NonNull String filename, byte[] data);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FileSystemView;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.ftplet.FtpFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple FTP file system view implementation.
|
||||
*
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public class SimpleFileSystemView implements FileSystemView {
|
||||
private Logger logger = LoggerFactory.getLogger(SimpleFileSystemView.class);
|
||||
|
||||
SimpleFtpFile file = new SimpleFtpFile();
|
||||
|
||||
@Override
|
||||
public boolean changeWorkingDirectory(String arg0) throws FtpException {
|
||||
logger.trace("changeWorkingDirectory: {}", arg0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.trace("dispose");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpFile getFile(String arg0) throws FtpException {
|
||||
logger.trace("getFile: {}", arg0);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpFile getHomeDirectory() throws FtpException {
|
||||
logger.trace("getHomeDirectory");
|
||||
return new SimpleFtpFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpFile getWorkingDirectory() throws FtpException {
|
||||
logger.trace("getWorkingDirectory");
|
||||
return new SimpleFtpFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRandomAccessible() throws FtpException {
|
||||
logger.trace("isRandomAccessible");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FtpFile;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple FTP file implementation.
|
||||
*
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public class SimpleFtpFile implements FtpFile {
|
||||
private Logger logger = LoggerFactory.getLogger(SimpleFtpFile.class);
|
||||
|
||||
MyOutputStream file;
|
||||
|
||||
public byte[] getData() {
|
||||
return file.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createInputStream(long arg0) throws IOException {
|
||||
logger.trace("createInputStream: {}", arg0);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream createOutputStream(long arg0) throws IOException {
|
||||
logger.trace("createOutputStream: {}", arg0);
|
||||
file = new MyOutputStream();
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
logger.trace("delete");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesExist() {
|
||||
logger.trace("doesExist");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAbsolutePath() {
|
||||
logger.trace("getAbsolutePath");
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupName() {
|
||||
logger.trace("getGroupName");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
logger.trace("getLastModified");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLinkCount() {
|
||||
logger.trace("getLinkCount");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
logger.trace("getName");
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwnerName() {
|
||||
logger.trace("getOwnerName");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
logger.trace("getSize");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
logger.trace("isDirectory");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
logger.trace("isFile");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
logger.trace("isHidden");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadable() {
|
||||
logger.trace("isReadable");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemovable() {
|
||||
logger.trace("isRemovable");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
logger.trace("isWritable");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FtpFile> listFiles() {
|
||||
logger.trace("listFiles");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir() {
|
||||
logger.trace("mkdir");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean move(FtpFile arg0) {
|
||||
logger.trace("move: {}", arg0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLastModified(long arg0) {
|
||||
logger.trace("setLastModified: {}", arg0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPhysicalFile() {
|
||||
logger.trace("getPhysicalFile");
|
||||
return null;
|
||||
}
|
||||
|
||||
private class MyOutputStream extends OutputStream {
|
||||
private StringBuilder data = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
data.append(String.format("%02X", (byte) b));
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
try {
|
||||
byte[] d = HexUtils.hexToBytes(data.toString());
|
||||
logger.debug("File len: {}", d.length);
|
||||
return d;
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Exception occured during data conversion: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.ftp;
|
||||
|
||||
/**
|
||||
* Simple wrapper class to store user name and password pairs.
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
class UsernamePassword {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
UsernamePassword(String username, String password) {
|
||||
this.setUsername(username);
|
||||
this.setPassword(password);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ftpupload.internal.handler;
|
||||
|
||||
import static org.openhab.binding.ftpupload.internal.FtpUploadBindingConstants.*;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.openhab.binding.ftpupload.internal.config.FtpUploadConfig;
|
||||
import org.openhab.binding.ftpupload.internal.ftp.FtpServer;
|
||||
import org.openhab.binding.ftpupload.internal.ftp.FtpServerEventListener;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.core.library.types.RawType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FtpUploadHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Pauli Anttila - Initial contribution
|
||||
*/
|
||||
public class FtpUploadHandler extends BaseThingHandler implements FtpServerEventListener {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(FtpUploadHandler.class);
|
||||
|
||||
private FtpUploadConfig configuration;
|
||||
private FtpServer ftpServer;
|
||||
|
||||
public FtpUploadHandler(Thing thing, FtpServer ftpServer) {
|
||||
super(thing);
|
||||
this.ftpServer = ftpServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand for channel {}: {}", channelUID.getId(), command.toString());
|
||||
logger.debug("Command sending not supported by this binding");
|
||||
|
||||
if (command.equals(RefreshType.REFRESH)) {
|
||||
ftpServer.printStats();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing handler for FTP Upload Binding");
|
||||
configuration = getConfigAs(FtpUploadConfig.class);
|
||||
logger.debug("Using configuration: {}", configuration.toString());
|
||||
|
||||
ftpServer.addEventListener(this);
|
||||
try {
|
||||
ftpServer.addAuthenticationCredentials(configuration.userName, configuration.password);
|
||||
} catch (IllegalArgumentException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ftpServer.removeAuthenticationCredentials(configuration.userName);
|
||||
ftpServer.removeEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileReceived(String userName, String filename, byte[] data) {
|
||||
if (configuration.userName.equals(userName)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateChannels(filename, data);
|
||||
updateTriggers(filename);
|
||||
}
|
||||
}
|
||||
|
||||
private String guessMimeTypeFromData(byte[] data) {
|
||||
String mimeType = HttpUtil.guessContentTypeFromData(data);
|
||||
logger.debug("Mime type guess from content: {}", mimeType);
|
||||
if (mimeType == null) {
|
||||
mimeType = RawType.DEFAULT_MIME_TYPE;
|
||||
}
|
||||
logger.debug("Mime type: {}", mimeType);
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
private void updateChannels(String filename, byte[] data) {
|
||||
for (Channel channel : thing.getChannels()) {
|
||||
String channelConf = (String) channel.getConfiguration().get(PARAM_FILENAME_PATTERN);
|
||||
if (channelConf != null) {
|
||||
if (filenameMatch(filename, channelConf)) {
|
||||
if ("Image".equals(channel.getAcceptedItemType())) {
|
||||
updateState(channel.getUID().getId(), new RawType(data, guessMimeTypeFromData(data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTriggers(String filename) {
|
||||
for (Channel channel : thing.getChannels()) {
|
||||
String channelConf = (String) channel.getConfiguration().get(PARAM_FILENAME_PATTERN);
|
||||
if (channelConf != null) {
|
||||
if (filenameMatch(filename, channelConf)) {
|
||||
if ("TRIGGER".equals(channel.getKind().toString())) {
|
||||
triggerChannel(channel.getUID().getId(), EVENT_IMAGE_RECEIVED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean filenameMatch(String filename, String pattern) {
|
||||
try {
|
||||
return Pattern.compile(pattern).matcher(filename).find();
|
||||
} catch (PatternSyntaxException e) {
|
||||
logger.warn("Invalid filename pattern '{}', reason: {}", pattern, e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="ftpupload" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>FTP Upload Binding</name>
|
||||
<description>This binding is for receiving files via FTP.</description>
|
||||
<author>Pauli Anttila</author>
|
||||
|
||||
<config-description>
|
||||
<parameter name="port" type="integer" min="1" max="65535">
|
||||
<label>TCP Port</label>
|
||||
<description>TCP port of the FTP server</description>
|
||||
<default>2121</default>
|
||||
</parameter>
|
||||
<parameter name="idleTimeout" type="integer" min="0" max="65535">
|
||||
<label>Idle Timeout</label>
|
||||
<description>The number of seconds before an inactive client is disconnected. If this value is set to 0, the idle
|
||||
time is disabled.</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ftpupload"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="imagereceiver" extensible="image-channel, image-received">
|
||||
<label>Image Receiver</label>
|
||||
<description>Receive image files via FTP.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="image" typeId="image-channel"/>
|
||||
<channel id="image-received" typeId="image-received"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="userName" type="text" required="true">
|
||||
<label>User Name</label>
|
||||
<description>Username</description>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="true">
|
||||
<label>Password</label>
|
||||
<description>Password</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="image-channel">
|
||||
<item-type>Image</item-type>
|
||||
<label>Image</label>
|
||||
<description>Image received via FTP</description>
|
||||
<state readOnly="true"></state>
|
||||
<config-description>
|
||||
<parameter name="filename" type="text" required="true">
|
||||
<label>Filename</label>
|
||||
<description>Filename to match received files. Supports regular expression patterns.</description>
|
||||
<default>.*</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
<channel-type id="image-received">
|
||||
<kind>trigger</kind>
|
||||
<label>Image File Received Trigger Channel</label>
|
||||
<event>
|
||||
<options>
|
||||
<option value="IMAGE_RECEIVED">Image received</option>
|
||||
</options>
|
||||
</event>
|
||||
<config-description>
|
||||
<parameter name="filename" type="text" required="true">
|
||||
<label>Filename</label>
|
||||
<description>Filename to match received files. Supports regular expression patterns.</description>
|
||||
<default>.*</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user