diff --git a/net/ftp-proxy/+POST_DEINSTALL b/net/ftp-proxy/+POST_DEINSTALL
new file mode 100644
index 000000000..9b8f25f33
--- /dev/null
+++ b/net/ftp-proxy/+POST_DEINSTALL
@@ -0,0 +1,28 @@
+DIFF='--- filter.inc.ftpproxy 2016-09-21 16:38:53.947075272 +0200
++++ filter.inc.orig 2016-09-21 16:47:29.239370565 +0200
+@@ -1450,7 +1450,6 @@
+
+ $natrules = "no nat proto carp\n";
+ $natrules .= "no rdr proto carp\n";
+- $natrules .= "nat-anchor \"ftp-proxy/*\"\n";
+ $natrules .= "nat-anchor \"natearly/*\"\n";
+
+ $natrules .= "nat-anchor \"natrules/*\"\n\n";
+@@ -1703,7 +1702,6 @@
+ unset($tonathosts, $tonathosts_array, $numberofnathosts);
+ }
+
+- $natrules .= "rdr-anchor \"ftp-proxy/*\"\n";
+ $natrules .= "\n# Load balancing\n";
+ $natrules .= "rdr-anchor \"relayd/*\"\n";
+
+@@ -2482,7 +2480,6 @@
+
+ $ipfrules = "";
+
+- $ipfrules .= "anchor \"ftp-proxy/*\"\n";
+ /* relayd */
+ $ipfrules .= "anchor \"relayd/*\"\n";
+ /* OpenVPN user rules from radius */'
+
+echo "$DIFF" | patch -b -p1 /usr/local/etc/inc/filter.inc
diff --git a/net/ftp-proxy/+POST_INSTALL b/net/ftp-proxy/+POST_INSTALL
new file mode 100644
index 000000000..074888adf
--- /dev/null
+++ b/net/ftp-proxy/+POST_INSTALL
@@ -0,0 +1,28 @@
+DIFF='--- filter.inc.orig 2016-09-21 16:39:02.853045967 +0200
++++ filter.inc.ftpproxy 2016-09-21 16:38:53.947075272 +0200
+@@ -1450,6 +1450,7 @@
+
+ $natrules = "no nat proto carp\n";
+ $natrules .= "no rdr proto carp\n";
++ $natrules .= "nat-anchor \"ftp-proxy/*\"\n";
+ $natrules .= "nat-anchor \"natearly/*\"\n";
+
+ $natrules .= "nat-anchor \"natrules/*\"\n\n";
+@@ -1702,6 +1703,7 @@
+ unset($tonathosts, $tonathosts_array, $numberofnathosts);
+ }
+
++ $natrules .= "rdr-anchor \"ftp-proxy/*\"\n";
+ $natrules .= "\n# Load balancing\n";
+ $natrules .= "rdr-anchor \"relayd/*\"\n";
+
+@@ -2480,6 +2482,7 @@
+
+ $ipfrules = "";
+
++ $ipfrules .= "anchor \"ftp-proxy/*\"\n";
+ /* relayd */
+ $ipfrules .= "anchor \"relayd/*\"\n";
+ /* OpenVPN user rules from radius */'
+
+echo "$DIFF" | patch -b -p1 /usr/local/etc/inc/filter.inc
diff --git a/net/ftp-proxy/Makefile b/net/ftp-proxy/Makefile
new file mode 100644
index 000000000..289c1f62c
--- /dev/null
+++ b/net/ftp-proxy/Makefile
@@ -0,0 +1,7 @@
+PLUGIN_NAME= ftp-proxy
+PLUGIN_VERSION= 0.1
+PLUGIN_COMMENT= Control ftp-proxy processes
+PLUGIN_MAINTAINER= frank.brendel@eurolog.com
+PLUGIN_PRIVATE= yes
+
+.include "../../Mk/plugins.mk"
diff --git a/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/Api/ServiceController.php b/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/Api/ServiceController.php
new file mode 100644
index 000000000..cb125671b
--- /dev/null
+++ b/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/Api/ServiceController.php
@@ -0,0 +1,42 @@
+getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
+ if ($node != null) {
+ // return node
+ return array("ftpproxy" => $node->getNodes());
+ }
+ } else {
+ // generate new node, but don't save to disc
+ $node = $mdlFtpProxy->ftpproxies->ftpproxy->Add();
+ return array("ftpproxy" => $node->getNodes());
+ }
+ return array();
+ }
+
+ /**
+ * update ftpproxy with given properties
+ * @param $uuid item unique id
+ * @return array
+ */
+ public function setProxyAction($uuid)
+ {
+ if ($this->request->isPost() && $this->request->hasPost("ftpproxy")) {
+ $mdlFtpProxy = new FtpProxy();
+ // keep a list to detect duplicates later
+ $CurrentProxies = $mdlFtpProxy->getNodes();
+ if ($uuid != null) {
+ $node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
+ if ($node != null) {
+ $Enabled = $node->enabled->__toString();
+ // get current ftp-proxy flags for stopping it later
+ $OldFlags = $mdlFtpProxy->configToFlags($node);
+ $result = array("result" => "failed", "validations" => array());
+ $proxyInfo = $this->request->getPost("ftpproxy");
+
+ $node->setNodes($proxyInfo);
+ $valMsgs = $mdlFtpProxy->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "ftpproxy", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+
+ if (count($result['validations']) == 0) {
+ // check for duplicates
+ foreach ($CurrentProxies['ftpproxies']['ftpproxy'] as $CurrentUUID => &$CurrentProxy) {
+ if ($node->listenaddress->__toString() == $CurrentProxy['listenaddress'] &&
+ $node->listenport->__toString() == $CurrentProxy['listenport'] &&
+ $uuid != $CurrentUUID) {
+ return array(
+ "result" => "failed",
+ "validations" => array(
+ "ftpproxy.listenaddress" => "Listen address in combination with Listen port already exists.",
+ "ftpproxy.listenport" => "Listen port in combination with Listen address already exists."
+ )
+ );
+ }
+ }
+ // retrieve ftp-proxy flags and set defaults
+ $NewFlags = $mdlFtpProxy->configToFlags($node);
+ // save config if validated correctly
+ $mdlFtpProxy->serializeToConfig();
+ Config::getInstance()->save();
+
+ $backend = new Backend();
+ // apply new settings to the ftp-proxy process
+ // stop ftp-proxy with old flags
+ if ($Enabled == 1) {
+ $backend->configdpRun('ftpproxy stop ', array($OldFlags));
+ }
+ $node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
+ // start ftp-proxy with new flags
+ if ($node != null && $node->enabled->__toString() == 1) {
+ $backend->configdpRun('ftpproxy start ', array($NewFlags));
+ }
+ // make the changes boot resistant in /etc/rc.conf.d/ftpproxy
+ $backend->configdRun("template reload OPNsense.FtpProxy");
+ $result = array("result" => "saved");
+ }
+ return $result;
+ }
+ }
+ }
+ return array("result" => "failed");
+ }
+
+ /**
+ * add new ftpproxy and set with attributes from post
+ * @return array
+ */
+ public function addProxyAction()
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost() && $this->request->hasPost("ftpproxy")) {
+ $result = array("result" => "failed", "validations" => array());
+ $mdlFtpProxy = new FtpProxy();
+ // keep a list to detect duplicates later
+ $CurrentProxies = $mdlFtpProxy->getNodes();
+ $node = $mdlFtpProxy->ftpproxies->ftpproxy->Add();
+ $node->setNodes($this->request->getPost("ftpproxy"));
+
+ $valMsgs = $mdlFtpProxy->performValidation();
+
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "ftpproxy", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+
+ if (count($result['validations']) == 0) {
+ foreach ($CurrentProxies['ftpproxies']['ftpproxy'] as &$CurrentProxy) {
+ if ($node->listenaddress->__toString() == $CurrentProxy['listenaddress']
+ && $node->listenport->__toString() == $CurrentProxy['listenport']) {
+ return array(
+ "result" => "failed",
+ "validations" => array(
+ "ftpproxy.listenaddress" => "Listen address in combination with Listen port already exists.",
+ "ftpproxy.listenport" => "Listen port in combination with Listen address already exists."
+ )
+ );
+ }
+ }
+ // retrieve ftp-proxy flags and set defaults
+ $Flags = $mdlFtpProxy->configToFlags($node);
+ // save config if validated correctly
+ $mdlFtpProxy->serializeToConfig();
+ Config::getInstance()->save();
+ if ($node->enabled->__toString() == 1) {
+ $backend = new Backend();
+ $backend->configdpRun('ftpproxy start ', array($Flags));
+ // add it to /etc/rc.conf.d/ftpproxy
+ $backend->configdRun("template reload OPNsense.FtpProxy");
+ }
+ $result = array("result" => "saved");
+ }
+ return $result;
+ }
+ return $result;
+ }
+
+ /**
+ * delete ftpproxy by uuid
+ * @param $uuid item unique id
+ * @return array status
+ */
+ public function delProxyAction($uuid)
+ {
+
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlFtpProxy = new FtpProxy();
+ if ($uuid != null) {
+ $node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
+ if ($node != null) {
+ $backend = new Backend();
+ // stop if the ftp-proxy is running
+ if ($node->enabled->__toString() == 1) {
+ $backend->configdpRun('ftpproxy stop ', array($mdlFtpProxy->configToFlags($node)));
+ }
+ if ($mdlFtpProxy->ftpproxies->ftpproxy->del($uuid) == true) {
+ // if item is removed, serialize to config and save
+ $mdlFtpProxy->serializeToConfig();
+ Config::getInstance()->save();
+ $result['result'] = 'deleted';
+ // remove it from /etc/rc.conf.d/ftpproxy
+ $backend->configdRun("template reload OPNsense.FtpProxy");
+ }
+ } else {
+ $result['result'] = 'not found';
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * toggle ftpproxy by uuid (enable/disable)
+ * @param $uuid item unique id
+ * @return array status
+ */
+ public function toggleProxyAction($uuid)
+ {
+
+ $result = array("result" => "failed");
+
+ if ($this->request->isPost()) {
+ $mdlFtpProxy = new FtpProxy();
+ if ($uuid != null) {
+ $node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
+ if ($node != null) {
+ $backend = new Backend();
+ if ($node->enabled->__toString() == "1") {
+ $result['result'] = "Disabled";
+ $node->enabled = "0";
+ $response = $backend->configdpRun('ftpproxy stop ', array($mdlFtpProxy->configToFlags($node)));
+ } else {
+ $result['result'] = "Enabled";
+ $node->enabled = "1";
+ $response = $backend->configdpRun('ftpproxy start ', array($mdlFtpProxy->configToFlags($node)));
+ }
+
+ // if item has toggled, serialize to config and save
+ $mdlFtpProxy->serializeToConfig();
+ Config::getInstance()->save();
+ $backend->configdRun("template reload OPNsense.FtpProxy");
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ *
+ * search ftpproxy
+ * @return array
+ */
+ public function searchProxyAction()
+ {
+ $this->sessionClose();
+ $fields = array(
+ "enabled",
+ "listenaddress",
+ "listenport",
+ "sourceaddress",
+ "rewritesourceport",
+ "idletimeout",
+ "maxsessions",
+ "reverseaddress",
+ "reverseport",
+ "logconnections",
+ "debuglevel",
+ "description"
+ );
+ $mdlFtpProxy = new FtpProxy();
+
+ $grid = new UIModelGrid($mdlFtpProxy->ftpproxies->ftpproxy);
+ $response = $grid->fetchBindRequest(
+ $this->request,
+ $fields,
+ "listenport"
+ );
+
+ $backend = new Backend();
+ foreach($response['rows'] as &$row) {
+ $node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $row['uuid']);
+ $status = trim($backend->configdpRun('ftpproxy status ', array($mdlFtpProxy->configToFlags($node))));
+ if ($status == 'OK') {
+ $row['status'] = 0;
+ continue;
+ }
+ $row['status'] = 2;
+ }
+
+ return $response;
+ }
+}
diff --git a/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/IndexController.php b/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/IndexController.php
new file mode 100644
index 000000000..9aae5620b
--- /dev/null
+++ b/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/IndexController.php
@@ -0,0 +1,50 @@
+view->title = gettext('FTP Proxy Server');
+ // include dialog form definitions
+ $this->view->formDialogEdit = $this->getForm("dialogEdit");
+ $this->view->pick('OPNsense/FtpProxy/index');
+ }
+}
diff --git a/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/ItemController.php b/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/ItemController.php
new file mode 100644
index 000000000..66ad6ef6c
--- /dev/null
+++ b/net/ftp-proxy/src/opnsense/mvc/app/controllers/OPNsense/FtpProxy/ItemController.php
@@ -0,0 +1,40 @@
+
+
| {{ lang._('Enabled') }} | +{{ lang._('Listen Address') }} | +{{ lang._('Listen Port') }} | +{{ lang._('Source Address') }} | +{{ lang._('Description') }} | +{{ lang._('ID') }} | +{{ lang._('Commands') }} | +
|---|---|---|---|---|---|---|
| + | + + + | +