diff --git a/addons/binding/org.openhab.binding.ftpupload/.classpath b/addons/binding/org.openhab.binding.ftpupload/.classpath
new file mode 100644
index 0000000000000..c9d8a81b5173f
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.ftpupload/.project b/addons/binding/org.openhab.binding.ftpupload/.project
new file mode 100644
index 0000000000000..aad3a6997b495
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/.project
@@ -0,0 +1,33 @@
+
+
+ org.openhab.binding.ftpupload
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.pde.ds.core.builder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/addons/binding/org.openhab.binding.ftpupload/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.ftpupload/ESH-INF/binding/binding.xml
new file mode 100644
index 0000000000000..1cc2058021475
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/ESH-INF/binding/binding.xml
@@ -0,0 +1,22 @@
+
+
+
+ FTP Upload Binding
+ This binding is for receiving files via FTP.
+ Pauli Anttila
+
+
+
+ TCP Port
+ TCP port of the FTP server
+ 2121
+
+
+ Idle timeout
+ The number of seconds before an inactive client is disconnected. If this value is set to 0, the idle time is disabled.
+ 60
+
+
+
diff --git a/addons/binding/org.openhab.binding.ftpupload/ESH-INF/thing/thing-types.xml b/addons/binding/org.openhab.binding.ftpupload/ESH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..8a28bd28ea34c
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/ESH-INF/thing/thing-types.xml
@@ -0,0 +1,59 @@
+
+
+
+
+ Image Receiver
+ Receive image files via FTP.
+
+
+
+
+
+
+
+
+ User Name
+ Username
+
+
+ Password
+ Password
+ password
+
+
+
+
+
+ Image
+ Image
+ Image received via FTP
+
+
+
+ Filename
+ Filename to match received files. Supports regular expression patterns.
+ .*
+
+
+
+
+ trigger
+ Image file received trigger channel
+
+
+ Image received
+
+
+
+
+ Filename
+ Filename to match received files. Supports regular expression patterns.
+ .*
+
+
+
+
+
diff --git a/addons/binding/org.openhab.binding.ftpupload/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.ftpupload/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000000..e32ebf403435c
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/META-INF/MANIFEST.MF
@@ -0,0 +1,29 @@
+Manifest-Version: 1.0
+Bundle-ActivationPolicy: lazy
+Bundle-ManifestVersion: 2
+Bundle-Name: FTP Upload Binding
+Bundle-SymbolicName: org.openhab.binding.ftpupload;singleton:=true
+Bundle-Vendor: openHAB
+Bundle-Version: 2.3.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ClassPath: .,
+ lib/ftplet-api-1.1.0.jar,
+ lib/ftpserver-core-1.1.0.jar,
+ lib/mina-core-2.0.16.jar
+Export-Package:
+ org.openhab.binding.ftpupload,
+ org.openhab.binding.ftpupload.handler
+Import-Package:
+ org.apache.commons.lang,
+ org.eclipse.jdt.annotation;resolution:=optional,
+ org.eclipse.smarthome.config.core,
+ org.eclipse.smarthome.core.library.types,
+ org.eclipse.smarthome.core.thing,
+ org.eclipse.smarthome.core.thing.binding,
+ org.eclipse.smarthome.core.thing.binding.builder,
+ org.eclipse.smarthome.core.thing.type,
+ org.eclipse.smarthome.core.types,
+ org.eclipse.smarthome.io.net.http,
+ org.osgi.service.component,
+ org.slf4j
+Service-Component: OSGI-INF/*.xml
diff --git a/addons/binding/org.openhab.binding.ftpupload/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.ftpupload/OSGI-INF/.gitignore
new file mode 100644
index 0000000000000..6722cd96e785a
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/OSGI-INF/.gitignore
@@ -0,0 +1 @@
+*.xml
diff --git a/addons/binding/org.openhab.binding.ftpupload/README.md b/addons/binding/org.openhab.binding.ftpupload/README.md
new file mode 100644
index 0000000000000..9df77d9ad8388
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/README.md
@@ -0,0 +1,174 @@
+# FTP Upload Binding
+
+This binding can be used to receive image files from FTP clients.
+The binding acts as a FTP server.
+Images stored on the FTP server are not saved to the file system, therefore the binding shouldn't cause any problems on flash based openHAB installations.
+
+## Supported Things
+
+This binding supports Things of type ```ftpupload```.
+Every Thing is identified by FTP user name.
+Therefore, every thing should use unique user name to login FTP server.
+
+## Discovery
+
+Automatic discovery is not supported.
+
+## Binding Configuration
+
+The binding has the following configuration options:
+
+| Parameter | Name | Description | Required | Default value |
+|-------------|--------------|------------------------------------------------------------------------------------------------------------------------|----------|---------------|
+| port | TCP Port | TCP port of the FTP server | no | 2121 |
+| idleTimeout | Idle timeout | The number of seconds before an inactive client is disconnected. If this value is set to 0, the idle time is disabled. | no | 60 |
+
+## Channels
+
+This binding currently supports the following channels:
+
+| Channel Type ID | Item Type | Description |
+|-----------------|--------------|----------------------------------------------------------------------------------------|
+| image | Image | Image file received via FTP. |
+
+When an image file is uploaded to FTP server, the binding tries to find the channel whose filename matches the uploaded image filename.
+If no match is found, no channel is updated.
+The filename parameter supports regular expression patterns.
+See more details in the Things example.
+
+Image channel supports following options:
+
+| Parameter | Name | Description | Required | Default value |
+|-------------|--------------|--------------------------------------------------------------------------|----------|---------------|
+| filename | Filename | Filename to match received files. Supports regular expression patterns. | yes | .* |
+
+
+### Trigger Channels
+
+| Channel Type ID | Options | Description |
+|-----------------|------------------------|-----------------------------------------------------|
+| image-received | IMAGE_RECEIVED | Triggered when image file received from FTP client. |
+
+When an image file is uploaded to FTP server, the binding tries to find the trigger channel whose filename matches the upload image filename.
+If no match is found, no channel is updated.
+The filename parameter supports regular expression patterns.
+See more details in the Things example.
+
+Trigger channels supports following options:
+
+| Parameter | Name | Description | Required | Default value |
+|-------------|--------------|--------------------------------------------------------------------------|----------|---------------|
+| filename | Filename | Filename to match received files. Supports regular expression patterns. | yes | .* |
+
+## Full Example
+
+Things:
+
+```
+Thing ftpupload:imagereceiver:images1 [ userName="test1", password="12345" ] {
+
+Thing ftpupload:imagereceiver:images2 [ userName="test2", password="12345" ] {
+ Channels:
+ Type image-channel : my_image1 "My Image channel 1" [
+ filename="test12[0-9]{2}.png" // match to filename test12xx.png, where xx can be numbers between 00-99
+ ]
+ Type image-channel : my_image2 "My Image channel 2" [
+ filename="test.jpg"
+ ]
+ Trigger String : my_image_trigger1 [
+ filename="test12[0-9]{2}.png"
+ ]
+ Trigger String : my_image_trigger2 [
+ filename="test.jpg"
+ ]
+}
+```
+
+Items:
+
+```
+Image Image1 { channel="ftpupload:imagereceiver:images1:image" }
+Image Image2 { channel="ftpupload:imagereceiver:images2:my_image1" }
+```
+
+Rules:
+
+```
+rule "example trigger rule 1"
+when
+ Channel 'ftpupload:imagereceiver:images1:image-received' triggered IMAGE_RECEIVED
+then
+ logInfo("Test","Image received")
+end
+
+rule "example trigger rule 2"
+when
+ Channel 'ftpupload:imagereceiver:images2:my_image_trigger1' triggered IMAGE_RECEIVED
+then
+ logInfo("Test","Image received")
+end
+
+```
+
+Sitemap:
+
+```
+Frame label="FTP images" {
+ Image item=Image1
+ Image item=Image2
+}
+```
+
+## Use case example
+
+The binding can be used to receive images from network cameras that send images to a FTP server when motion or sound is detected.
+
+Things:
+
+```
+Thing ftpupload:imagereceiver:garagecamera [ userName="garage", password="12345" ]
+```
+
+Items:
+
+```
+Image Garage_NetworkCamera_Motion_Image { channel="ftpupload:imagereceiver:garagecamera:image" }
+```
+
+Rules:
+
+```
+rule "example trigger rule"
+when
+ Channel 'ftpupload:imagereceiver:garagecamera:image-received' triggered IMAGE_RECEIVED
+then
+ logInfo("Test","Garage motion detected")
+end
+```
+
+Sitemap:
+
+```
+Frame label="Garage network camera" icon="camera" {
+ Image item=Garage_NetworkCamera_Motion_Image
+}
+```
+
+## Logging and Problem Solving
+
+For problem solving, if binding logging is not enough, Apache FTP server logging can also be enabled by the following command in the karaf console:
+
+```
+log:set DEBUG org.apache.ftpserver
+```
+
+and set back to default level:
+
+```
+log:set DEFAULT org.apache.ftpserver
+```
+
+If you meet any problems to receive images from the network cameras, you could test connection to binding with any FTP client.
+You can send image files via FTP client and thing channels should be updated accordingly.
+
+
\ No newline at end of file
diff --git a/addons/binding/org.openhab.binding.ftpupload/about.html b/addons/binding/org.openhab.binding.ftpupload/about.html
new file mode 100644
index 0000000000000..34f40eecb27d6
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/about.html
@@ -0,0 +1,49 @@
+
+
+
+
+About
+
+
+About This Content
+
+Nov 10, 2016
+License
+
+The openHAB community makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html .
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the openHAB community, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at openhab.org .
+
+
+ Third Party Content
+ The Content includes items that have been sourced from third parties as set out below. If you
+ did not receive this Content directly from the openHAB community, the following is provided
+ for informational purposes only, and you should look to the Redistributor's license for
+ terms and conditions of use.
+
+ Apache MINA
+ Apache MINA under
+ Apache License .
+
+
+ Apache FtpServer
+ Apache FtpServer under
+ Apache License .
+
+
+ Apache Ftplet
+ Apache Ftplet under
+ Apache License .
+
+
+
diff --git a/addons/binding/org.openhab.binding.ftpupload/build.properties b/addons/binding/org.openhab.binding.ftpupload/build.properties
new file mode 100644
index 0000000000000..2e82976f5b510
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/build.properties
@@ -0,0 +1,10 @@
+source.. = src/main/java/
+output.. = target/classes
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/,\
+ ESH-INF/,\
+ about.html,\
+ lib/ftplet-api-1.1.0.jar,\
+ lib/ftpserver-core-1.1.0.jar,\
+ lib/mina-core-2.0.16.jar
diff --git a/addons/binding/org.openhab.binding.ftpupload/lib/ftplet-api-1.1.0.jar b/addons/binding/org.openhab.binding.ftpupload/lib/ftplet-api-1.1.0.jar
new file mode 100644
index 0000000000000..36784e9744c2b
Binary files /dev/null and b/addons/binding/org.openhab.binding.ftpupload/lib/ftplet-api-1.1.0.jar differ
diff --git a/addons/binding/org.openhab.binding.ftpupload/lib/ftpserver-core-1.1.0.jar b/addons/binding/org.openhab.binding.ftpupload/lib/ftpserver-core-1.1.0.jar
new file mode 100644
index 0000000000000..00bf2a16d87a6
Binary files /dev/null and b/addons/binding/org.openhab.binding.ftpupload/lib/ftpserver-core-1.1.0.jar differ
diff --git a/addons/binding/org.openhab.binding.ftpupload/lib/mina-core-2.0.16.jar b/addons/binding/org.openhab.binding.ftpupload/lib/mina-core-2.0.16.jar
new file mode 100644
index 0000000000000..0e6d9883e6917
Binary files /dev/null and b/addons/binding/org.openhab.binding.ftpupload/lib/mina-core-2.0.16.jar differ
diff --git a/addons/binding/org.openhab.binding.ftpupload/pom.xml b/addons/binding/org.openhab.binding.ftpupload/pom.xml
new file mode 100644
index 0000000000000..4adc2e36a2ba9
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/pom.xml
@@ -0,0 +1,20 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.binding
+ pom
+ 2.3.0-SNAPSHOT
+
+
+ org.openhab.binding
+ org.openhab.binding.ftpupload
+ 2.3.0-SNAPSHOT
+
+ FTP Upload Binding
+ eclipse-plugin
+
+
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/FtpUploadBindingConstants.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/FtpUploadBindingConstants.java
new file mode 100644
index 0000000000000..4d635ed6bbfa3
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/FtpUploadBindingConstants.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.ftpupload;
+
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+
+/**
+ * The {@link FtpUploadBinding} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Pauli Anttila - Initial contribution
+ */
+public class FtpUploadBindingConstants {
+
+ public static final String BINDING_ID = "ftpupload";
+
+ // List of all Thing Type UIDs
+ public final static ThingTypeUID THING_TYPE_IMAGERECEIVER = new ThingTypeUID(BINDING_ID, "imagereceiver");
+
+ // List of all Channel ids
+ public final static String IMAGE = "image";
+ public final static String IMAGE_RECEIVED_TRIGGER = "image-received";
+
+ // List of all channel parameters
+ public final static String PARAM_FILENAME_PATTERN = "filename";
+
+ public final static String EVENT_IMAGE_RECEIVED = "IMAGE_RECEIVED";
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/handler/FtpUploadHandler.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/handler/FtpUploadHandler.java
new file mode 100644
index 0000000000000..8e8c520023648
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/handler/FtpUploadHandler.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.ftpupload.handler;
+
+import static org.openhab.binding.ftpupload.FtpUploadBindingConstants.*;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.eclipse.smarthome.core.library.types.RawType;
+import org.eclipse.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.core.types.RefreshType;
+import org.eclipse.smarthome.io.net.http.HttpUtil;
+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.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;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java
new file mode 100644
index 0000000000000..af606f85ff9cc
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.ftpupload.internal;
+
+import static org.openhab.binding.ftpupload.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.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingStatusInfo;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
+import org.openhab.binding.ftpupload.handler.FtpUploadHandler;
+import org.openhab.binding.ftpupload.internal.ftp.FtpServer;
+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, immediate = true, configurationPid = "binding.ftpupload")
+public class FtpUploadHandlerFactory extends BaseThingHandlerFactory {
+ private final Logger logger = LoggerFactory.getLogger(FtpUploadHandlerFactory.class);
+
+ private final static Set 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 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.info("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.info("Stopping FTP server");
+ ftpServer.stopServer();
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java
new file mode 100644
index 0000000000000..648da874d2568
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+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;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java
new file mode 100644
index 0000000000000..cecafc4d35d3a
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+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 getAuthorities() {
+ logger.trace("getAuthorities");
+ return null;
+ }
+
+ @Override
+ public List getAuthorities(Class extends Authority> arg0) {
+ logger.trace("getAuthorities: {}", arg0);
+ return null;
+ }
+
+ @Override
+ public String getPassword() {
+ logger.trace("getPassword");
+ return null;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java
new file mode 100644
index 0000000000000..d1b4373a66985
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.ftpupload.internal.ftp;
+
+import java.util.HashMap;
+
+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 HashMap 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);
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java
new file mode 100644
index 0000000000000..648eadb22772e
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java
@@ -0,0 +1,204 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+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 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 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 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;
+ }
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java
new file mode 100644
index 0000000000000..ecf69aae24541
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+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);
+
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java
new file mode 100644
index 0000000000000..732bf18dfa285
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+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;
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java
new file mode 100644
index 0000000000000..19cb68fc47958
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java
@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.openhab.binding.ftpupload.internal.ftp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.ftpserver.ftplet.FtpFile;
+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 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 = DatatypeConverter.parseHexBinary(data.toString());
+ logger.debug("File len: {}", d.length);
+ return d;
+ } catch (IllegalArgumentException e) {
+ logger.debug("Exception occured during data conversion: {}", e.getMessage());
+ }
+ return null;
+ }
+ }
+}
diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java
new file mode 100644
index 0000000000000..8cf1f0018daa5
--- /dev/null
+++ b/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2018 by the respective copyright holders.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+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;
+ }
+}
diff --git a/addons/binding/pom.xml b/addons/binding/pom.xml
index 99e7bd44e6191..0183b1f7f45e9 100644
--- a/addons/binding/pom.xml
+++ b/addons/binding/pom.xml
@@ -38,6 +38,7 @@
org.openhab.binding.folding
org.openhab.binding.freebox
org.openhab.binding.fronius
+ org.openhab.binding.ftpupload
org.openhab.binding.gardena
org.openhab.binding.harmonyhub
org.openhab.binding.hdanywhere
diff --git a/features/openhab-addons/src/main/feature/feature.xml b/features/openhab-addons/src/main/feature/feature.xml
index cfc97abc0e1c8..23bfbf3950959 100644
--- a/features/openhab-addons/src/main/feature/feature.xml
+++ b/features/openhab-addons/src/main/feature/feature.xml
@@ -112,10 +112,17 @@
openhab-transport-mdns
mvn:org.openhab.binding/org.openhab.binding.freebox/${project.version}
+
openhab-runtime-base
mvn:org.openhab.binding/org.openhab.binding.fronius/${project.version}
+
+
+ openhab-runtime-base
+ mvn:org.openhab.binding/org.openhab.binding.ftpupload/${project.version}
+
+
openhab-runtime-base
mvn:org.openhab.binding/org.openhab.binding.gardena/${project.version}