summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/ftp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ucb/source/ucp/ftp
parentInitial commit. (diff)
downloadlibreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.tar.xz
libreoffice-cb75148ebd0135178ff46f89a30139c44f8d2040.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ucb/source/ucp/ftp')
-rw-r--r--ucb/source/ucp/ftp/curl.hxx24
-rw-r--r--ucb/source/ucp/ftp/ftpcfunc.cxx50
-rw-r--r--ucb/source/ucp/ftp/ftpcfunc.hxx39
-rw-r--r--ucb/source/ucp/ftp/ftpcontainer.hxx56
-rw-r--r--ucb/source/ucp/ftp/ftpcontent.cxx852
-rw-r--r--ucb/source/ucp/ftp/ftpcontent.hxx146
-rw-r--r--ucb/source/ucp/ftp/ftpcontentcaps.cxx167
-rw-r--r--ucb/source/ucp/ftp/ftpcontentidentifier.cxx63
-rw-r--r--ucb/source/ucp/ftp/ftpcontentidentifier.hxx59
-rw-r--r--ucb/source/ucp/ftp/ftpcontentprovider.cxx244
-rw-r--r--ucb/source/ucp/ftp/ftpcontentprovider.hxx107
-rw-r--r--ucb/source/ucp/ftp/ftpdirp.cxx1269
-rw-r--r--ucb/source/ucp/ftp/ftpdirp.hxx158
-rw-r--r--ucb/source/ucp/ftp/ftpdynresultset.cxx67
-rw-r--r--ucb/source/ucp/ftp/ftpdynresultset.hxx48
-rw-r--r--ucb/source/ucp/ftp/ftpintreq.cxx64
-rw-r--r--ucb/source/ucp/ftp/ftpintreq.hxx84
-rw-r--r--ucb/source/ucp/ftp/ftploaderthread.cxx92
-rw-r--r--ucb/source/ucp/ftp/ftploaderthread.hxx59
-rw-r--r--ucb/source/ucp/ftp/ftpresultsetI.cxx90
-rw-r--r--ucb/source/ucp/ftp/ftpresultsetI.hxx46
-rw-r--r--ucb/source/ucp/ftp/ftpresultsetbase.cxx509
-rw-r--r--ucb/source/ucp/ftp/ftpresultsetbase.hxx410
-rw-r--r--ucb/source/ucp/ftp/ftpresultsetfactory.hxx57
-rw-r--r--ucb/source/ucp/ftp/ftpurl.cxx804
-rw-r--r--ucb/source/ucp/ftp/ftpurl.hxx161
-rw-r--r--ucb/source/ucp/ftp/ucpftp1.component26
27 files changed, 5751 insertions, 0 deletions
diff --git a/ucb/source/ucp/ftp/curl.hxx b/ucb/source/ucp/ftp/curl.hxx
new file mode 100644
index 000000000..3dac8a057
--- /dev/null
+++ b/ucb/source/ucp/ftp/curl.hxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <curl/curl.h>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcfunc.cxx b/ucb/source/ucp/ftp/ftpcfunc.cxx
new file mode 100644
index 000000000..a39043aa7
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcfunc.cxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#include <osl/file.h>
+#include "ftpcontentidentifier.hxx"
+#include "ftpcfunc.hxx"
+
+using namespace ftp;
+using namespace com::sun::star::uno;
+
+extern "C" {
+
+ int file_write(void *buffer,size_t size,size_t nmemb,void *stream)
+ {
+ oslFileHandle aFile = reinterpret_cast< oslFileHandle >( stream );
+ if( !aFile )
+ return 0;
+
+ sal_uInt64 nWritten = 0;
+ sal_uInt64 nToWrite( size * nmemb );
+ osl_writeFile( aFile, buffer, nToWrite, &nWritten );
+
+ return nWritten != nToWrite ? 0 : nmemb;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcfunc.hxx b/ucb/source/ucp/ftp/ftpcfunc.hxx
new file mode 100644
index 000000000..f5a29a3fb
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcfunc.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#pragma once
+
+#include <stddef.h>
+
+extern "C" {
+
+/** callback for curl_easy_perform(),
+ * forwarding the written content to the stream.
+ * stream has to be of type oslFileHandle.
+ */
+
+int file_write(void* buffer, size_t size, size_t nmemb, void* stream);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontainer.hxx b/ucb/source/ucp/ftp/ftpcontainer.hxx
new file mode 100644
index 000000000..e555111a2
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontainer.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#pragma once
+
+#include <sal/types.h>
+
+namespace ftp {
+
+class MemoryContainer {
+
+public:
+
+ MemoryContainer();
+
+ ~MemoryContainer();
+
+ int append(
+ const void* pBuffer,
+ size_t size,
+ size_t nmemb
+ ) noexcept;
+
+
+ sal_uInt32 m_nLen,m_nWritePos;
+ void *m_pBuffer;
+};
+
+}
+
+
+extern "C" int memory_write(
+ void *buffer,size_t size,size_t nmemb,void *stream);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontent.cxx b/ucb/source/ucp/ftp/ftpcontent.cxx
new file mode 100644
index 000000000..4c7888e25
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontent.cxx
@@ -0,0 +1,852 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include "ftpdynresultset.hxx"
+#include "ftpresultsetfactory.hxx"
+#include "ftpresultsetI.hxx"
+#include "ftpcontent.hxx"
+#include "ftpcontentprovider.hxx"
+#include "ftpdirp.hxx"
+#include "ftpcontentidentifier.hxx"
+#include "ftpintreq.hxx"
+
+#include <memory>
+#include <vector>
+#include <string.h>
+#include "curl.hxx"
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/fd_inputstream.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/simpleauthenticationrequest.hxx>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/MissingPropertiesException.hpp>
+#include <com/sun/star/ucb/MissingInputStreamException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+
+using namespace ftp;
+using namespace com::sun::star::task;
+using namespace com::sun::star::container;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::io;
+using namespace com::sun::star::sdbc;
+
+
+// Content Implementation.
+
+FTPContent::FTPContent( const Reference< XComponentContext >& rxContext,
+ FTPContentProvider* pProvider,
+ const Reference< XContentIdentifier >& Identifier,
+ const FTPURL& aFTPURL)
+ : ContentImplHelper(rxContext,pProvider,Identifier)
+ , m_pFCP(pProvider)
+ , m_aFTPURL(aFTPURL)
+ , m_bInserted(false)
+ , m_bTitleSet(false)
+{
+}
+
+FTPContent::FTPContent( const Reference< XComponentContext >& rxContext,
+ FTPContentProvider* pProvider,
+ const Reference< XContentIdentifier >& Identifier,
+ const ContentInfo& Info)
+ : ContentImplHelper(rxContext,pProvider,Identifier)
+ , m_pFCP(pProvider)
+ , m_aFTPURL(Identifier->getContentIdentifier(), pProvider)
+ , m_bInserted(true)
+ , m_bTitleSet(false)
+ , m_aInfo(Info)
+{
+}
+
+FTPContent::~FTPContent()
+{
+}
+
+// XInterface methods.
+
+void SAL_CALL FTPContent::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL FTPContent::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL FTPContent::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< XTypeProvider* >(this),
+ static_cast< XServiceInfo* >(this),
+ static_cast< XContent* >(this),
+ static_cast< XCommandProcessor* >(this),
+ static_cast< XContentCreator* >(this),
+ static_cast< XChild* >(this)
+ );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+
+css::uno::Sequence< sal_Int8 > SAL_CALL FTPContent::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL FTPContent::getTypes()
+{
+ static cppu::OTypeCollection s_aCollection(
+ cppu::UnoType<XTypeProvider>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XContent>::get(),
+ cppu::UnoType<XCommandProcessor>::get(),
+ cppu::UnoType<XContentCreator>::get(),
+ cppu::UnoType<XChild>::get()
+ );
+
+ return s_aCollection.getTypes();
+}
+
+
+// XServiceInfo methods.
+
+OUString SAL_CALL FTPContent::getImplementationName()
+{
+ return "com.sun.star.comp.FTPContent";
+}
+
+sal_Bool SAL_CALL FTPContent::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+css::uno::Sequence< OUString > SAL_CALL FTPContent::getSupportedServiceNames()
+{
+ return { "com.sun.star.ucb.FTPContent" };
+}
+
+
+// XContent methods.
+
+// virtual
+OUString SAL_CALL FTPContent::getContentType()
+{
+ return FTP_CONTENT_TYPE;
+}
+
+// XCommandProcessor methods.
+
+//virtual
+void SAL_CALL FTPContent::abort( sal_Int32 /*CommandId*/ )
+{
+}
+
+
+ResultSetFactory::ResultSetFactory(const Reference<XComponentContext >& rxContext,
+ const Reference<XContentProvider >& xProvider,
+ const Sequence<Property>& seq,
+ std::vector<FTPDirentry>&& dirvec)
+ : m_xContext(rxContext),
+ m_xProvider(xProvider),
+ m_seq(seq),
+ m_dirvec(std::move(dirvec))
+{
+}
+
+
+rtl::Reference<ResultSetBase> ResultSetFactory::createResultSet()
+{
+ return new ResultSetI(m_xContext,
+ m_xProvider,
+ m_seq,
+ m_dirvec);
+}
+
+
+// XCommandProcessor methods.
+
+namespace {
+
+enum ACTION { NOACTION,
+ THROWAUTHENTICATIONREQUEST,
+ THROWACCESSDENIED,
+ THROWINTERACTIVECONNECT,
+ THROWMALFORMED,
+ THROWRESOLVENAME,
+ THROWQUOTE,
+ THROWNOFILE,
+ THROWGENERAL };
+
+}
+
+// virtual
+Any SAL_CALL FTPContent::execute( const Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const Reference<
+ XCommandEnvironment >& Environment)
+{
+ ACTION action(NOACTION);
+ Any aRet;
+
+ while(true)
+ {
+ try
+ {
+ if(action == THROWAUTHENTICATIONREQUEST)
+ {
+ // try to get a continuation first
+ OUString aPassword,aAccount;
+ m_pFCP->forHost(m_aFTPURL.host(),
+ m_aFTPURL.port(),
+ m_aFTPURL.username(),
+ aPassword,
+ aAccount);
+ rtl::Reference<ucbhelper::SimpleAuthenticationRequest>
+ p( new ucbhelper::SimpleAuthenticationRequest(
+ m_aFTPURL.ident(false, false),
+ m_aFTPURL.host(), // ServerName
+ ucbhelper::SimpleAuthenticationRequest::ENTITY_NA,
+ OUString(),
+ ucbhelper::SimpleAuthenticationRequest
+ ::ENTITY_FIXED,
+ m_aFTPURL.username(),
+ ucbhelper::SimpleAuthenticationRequest
+ ::ENTITY_MODIFY,
+ aPassword));
+
+ Reference<XInteractionHandler> xInteractionHandler;
+ if(Environment.is())
+ xInteractionHandler =
+ Environment->getInteractionHandler();
+
+ if( xInteractionHandler.is()) {
+ xInteractionHandler->handle(p);
+
+ Reference<XInterface> xSelection(
+ p->getSelection());
+
+ if(Reference<XInteractionRetry>(
+ xSelection,UNO_QUERY).is())
+ action = NOACTION;
+ else if(Reference<XInteractionSupplyAuthentication>(
+ xSelection,UNO_QUERY).is()) {
+ m_pFCP->setHost(
+ m_aFTPURL.host(),
+ m_aFTPURL.port(),
+ m_aFTPURL.username(),
+ p->getAuthenticationSupplier()->getPassword(),
+ aAccount);
+ action = NOACTION;
+ }
+ }
+ aRet = p->getRequest();
+ }
+
+// if(aCommand.Name.equalsAscii(
+// "getPropertyValues") &&
+// action != NOACTION) {
+// // It is not allowed to throw if
+// // command is getPropertyValues
+// rtl::Reference<ucbhelper::PropertyValueSet> xRow =
+// new ucbhelper::PropertyValueSet(m_xSMgr);
+// Sequence<Property> Properties;
+// aCommand.Argument >>= Properties;
+// for(int i = 0; i < Properties.getLength(); ++i)
+// xRow->appendVoid(Properties[i]);
+// aRet <<= Reference<XRow>(xRow.get());
+// return aRet;
+// }
+
+ switch (action)
+ {
+ case NOACTION:
+ break;
+
+ case THROWAUTHENTICATIONREQUEST:
+ ucbhelper::cancelCommandExecution(
+ aRet,
+ Reference<XCommandEnvironment>(nullptr));
+ break;
+
+ case THROWACCESSDENIED:
+ {
+ Sequence<Any> seq(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", Any(m_aFTPURL.ident(false,false))}
+ }));
+ ucbhelper::cancelCommandExecution(
+ IOErrorCode_ACCESS_DENIED,
+ seq,
+ Environment);
+ break;
+ }
+ case THROWINTERACTIVECONNECT:
+ {
+ InteractiveNetworkConnectException excep;
+ excep.Server = m_aFTPURL.host();
+ aRet <<= excep;
+ ucbhelper::cancelCommandExecution(
+ aRet,
+ Environment);
+ break;
+ }
+ case THROWMALFORMED:
+ {
+ IllegalIdentifierException ex;
+ aRet <<= ex;
+ ucbhelper::cancelCommandExecution(
+ aRet,
+ Environment);
+ break;
+ }
+ case THROWRESOLVENAME:
+ {
+ InteractiveNetworkResolveNameException excep;
+ excep.Server = m_aFTPURL.host();
+ aRet <<= excep;
+ ucbhelper::cancelCommandExecution(
+ aRet,
+ Environment);
+ break;
+ }
+ case THROWNOFILE:
+ {
+ Sequence<Any> seq(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", Any(m_aFTPURL.ident(false,false))}
+ }));
+ ucbhelper::cancelCommandExecution(
+ IOErrorCode_NO_FILE,
+ seq,
+ Environment);
+ break;
+ }
+ case THROWQUOTE:
+ case THROWGENERAL:
+ ucbhelper::cancelCommandExecution(
+ IOErrorCode_GENERAL,
+ Sequence<Any>(0),
+ Environment);
+ break;
+ }
+
+ if(aCommand.Name == "getPropertyValues") {
+ Sequence<Property> Properties;
+ if(!(aCommand.Argument >>= Properties))
+ {
+ aRet <<= IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >(this),
+ -1);
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ aRet <<= getPropertyValues(Properties);
+ }
+ else if(aCommand.Name == "setPropertyValues")
+ {
+ Sequence<PropertyValue> propertyValues;
+
+ if( ! ( aCommand.Argument >>= propertyValues ) ) {
+ aRet <<= IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >(this),
+ -1);
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ aRet <<= setPropertyValues(propertyValues);
+ }
+ else if(aCommand.Name == "getCommandInfo") {
+ // Note: Implemented by base class.
+ aRet <<= getCommandInfo(Environment);
+ }
+ else if(aCommand.Name == "getPropertySetInfo") {
+ // Note: Implemented by base class.
+ aRet <<= getPropertySetInfo(Environment);
+ }
+ else if(aCommand.Name == "insert")
+ {
+ InsertCommandArgument aInsertArgument;
+ if ( ! ( aCommand.Argument >>= aInsertArgument ) ) {
+ aRet <<= IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >(this),
+ -1);
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+ insert(aInsertArgument,Environment);
+ }
+ else if(aCommand.Name == "delete") {
+ m_aFTPURL.del();
+ deleted();
+ }
+ else if(aCommand.Name == "open") {
+ OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) ) {
+ aRet <<= IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >(this),
+ -1);
+
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ if(aOpenCommand.Mode == OpenMode::DOCUMENT) {
+ // Open as a document
+ Reference<XActiveDataSink>
+ xActiveDataSink(aOpenCommand.Sink,UNO_QUERY);
+ Reference< XOutputStream >
+ xOutputStream(aOpenCommand.Sink,UNO_QUERY);
+
+ if(xActiveDataSink.is()) {
+ xActiveDataSink->setInputStream(
+ new ucbhelper::FdInputStream(m_aFTPURL.open()));
+ }
+ else if(xOutputStream.is()) {
+ Reference<XInputStream> xStream(
+ new ucbhelper::FdInputStream(m_aFTPURL.open()));
+ for (;;) {
+ Sequence<sal_Int8> byte_seq(4096);
+ sal_Int32 n = xStream->readBytes(byte_seq, 4096);
+ if (n == 0) {
+ break;
+ }
+ try {
+ if(byte_seq.getLength() != n)
+ byte_seq.realloc(n);
+ xOutputStream->writeBytes(byte_seq);
+ } catch(const NotConnectedException&) {
+
+ } catch(const BufferSizeExceededException&) {
+
+ } catch(const IOException&) {
+
+ }
+ }
+ }
+ else {
+ aRet <<= UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >(this),
+ aOpenCommand.Sink);
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+ }
+ else if(aOpenCommand.Mode == OpenMode::ALL ||
+ aOpenCommand.Mode == OpenMode::DOCUMENTS ||
+ aOpenCommand.Mode == OpenMode::FOLDERS ) {
+ std::vector<FTPDirentry> resvec =
+ m_aFTPURL.list(sal_Int16(aOpenCommand.Mode));
+ Reference< XDynamicResultSet > xSet
+ = new DynamicResultSet(
+ m_xContext,
+ aOpenCommand,
+ std::make_unique<ResultSetFactory>(m_xContext,
+ m_xProvider.get(),
+ aOpenCommand.Properties,
+ std::move(resvec)));
+ aRet <<= xSet;
+ }
+ else if(aOpenCommand.Mode ==
+ OpenMode::DOCUMENT_SHARE_DENY_NONE ||
+ aOpenCommand.Mode ==
+ OpenMode::DOCUMENT_SHARE_DENY_WRITE) {
+ // Unsupported OpenMode
+ aRet <<= UnsupportedOpenModeException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >(this),
+ static_cast< sal_Int16 >(aOpenCommand.Mode));
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+ else {
+ aRet <<= IllegalArgumentException(
+ "Unexpected OpenMode!",
+ static_cast< cppu::OWeakObject * >(this),
+ -1);
+
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+ } else if(aCommand.Name == "createNewContent") {
+ ContentInfo aArg;
+ if (!(aCommand.Argument >>= aArg)) {
+ ucbhelper::cancelCommandExecution(
+ Any(
+ IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >(this),
+ -1)),
+ Environment);
+ // Unreachable
+ }
+ aRet <<= createNewContent(aArg);
+ } else {
+ aRet <<= UnsupportedCommandException(
+ aCommand.Name,
+ static_cast< cppu::OWeakObject * >(this));
+ ucbhelper::cancelCommandExecution(aRet,Environment);
+ }
+
+ return aRet;
+ }
+ catch(const curl_exception& e)
+ {
+ if(e.code() == CURLE_COULDNT_CONNECT)
+ action = THROWINTERACTIVECONNECT;
+ else if (e.code() == CURLE_URL_MALFORMAT)
+ {
+ action = THROWMALFORMED;
+ }
+ else if(e.code() == CURLE_COULDNT_RESOLVE_HOST )
+ action = THROWRESOLVENAME;
+ else if(e.code() == CURLE_FTP_USER_PASSWORD_INCORRECT ||
+ e.code() == CURLE_LOGIN_DENIED ||
+ e.code() == CURLE_BAD_PASSWORD_ENTERED ||
+ e.code() == CURLE_FTP_WEIRD_PASS_REPLY)
+ action = THROWAUTHENTICATIONREQUEST;
+ else if(e.code() == CURLE_FTP_ACCESS_DENIED)
+ action = THROWACCESSDENIED;
+ else if(e.code() == CURLE_FTP_QUOTE_ERROR)
+ action = THROWQUOTE;
+ else if(e.code() == CURLE_FTP_COULDNT_RETR_FILE)
+ action = THROWNOFILE;
+ else
+ // nothing known about the cause of the error
+ action = THROWGENERAL;
+ }
+ }
+}
+
+constexpr OUStringLiteral FTP_FILE = u"application/vnd.sun.staroffice.ftp-file";
+
+constexpr OUStringLiteral FTP_FOLDER = u"application/vnd.sun.staroffice.ftp-folder";
+
+Sequence<ContentInfo > SAL_CALL
+FTPContent::queryCreatableContentsInfo( )
+{
+ return queryCreatableContentsInfo_Static();
+}
+
+// static
+Sequence<ContentInfo >
+FTPContent::queryCreatableContentsInfo_Static( )
+{
+ Sequence< Property > props{ Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::MAYBEVOID
+ | PropertyAttribute::BOUND ) };
+ return
+ {
+ { FTP_FILE, ContentInfoAttribute::INSERT_WITH_INPUTSTREAM | ContentInfoAttribute::KIND_DOCUMENT, props },
+ { FTP_FOLDER, ContentInfoAttribute::KIND_FOLDER, props }
+ };
+}
+
+Reference<XContent > SAL_CALL
+FTPContent::createNewContent( const ContentInfo& Info )
+{
+ if( Info.Type =="application/vnd.sun.staroffice.ftp-file" || Info.Type == "application/vnd.sun.staroffice.ftp-folder" )
+ return new FTPContent(m_xContext,
+ m_pFCP,
+ m_xIdentifier,Info);
+ else
+ return Reference<XContent>(nullptr);
+}
+
+
+Reference<XInterface > SAL_CALL
+FTPContent::getParent( )
+{
+ Reference<XContentIdentifier>
+ xIdent(new FTPContentIdentifier(m_aFTPURL.parent()));
+ return Reference<XInterface>( m_xProvider->queryContent(xIdent), UNO_QUERY );
+}
+
+
+void SAL_CALL
+FTPContent::setParent(const Reference<XInterface >& /*Parent*/ )
+{
+ throw NoSupportException();
+}
+
+
+OUString FTPContent::getParentURL()
+{
+ return m_aFTPURL.parent();
+}
+
+namespace {
+
+class InsertData
+ : public CurlInput {
+
+public:
+
+ explicit InsertData(const Reference<XInputStream>& xInputStream)
+ : m_xInputStream(xInputStream) { }
+ virtual ~InsertData() {}
+
+ // returns the number of bytes actually read
+ virtual sal_Int32 read(sal_Int8 *dest,sal_Int32 nBytesRequested) override;
+
+private:
+
+ Reference<XInputStream> m_xInputStream;
+};
+
+}
+
+sal_Int32 InsertData::read(sal_Int8 *dest,sal_Int32 nBytesRequested)
+{
+ sal_Int32 m = 0;
+
+ if(m_xInputStream.is()) {
+ Sequence<sal_Int8> seq(nBytesRequested);
+ m = m_xInputStream->readBytes(seq,nBytesRequested);
+ memcpy(dest,seq.getConstArray(),m);
+ }
+ return m;
+}
+
+
+void FTPContent::insert(const InsertCommandArgument& aInsertCommand,
+ const Reference<XCommandEnvironment>& Env)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if(m_bInserted && !m_bTitleSet) {
+ MissingPropertiesException excep;
+ excep.Properties = { "Title" };
+ ucbhelper::cancelCommandExecution(Any(excep), Env);
+ }
+
+ if(m_bInserted &&
+ m_aInfo.Type == FTP_FILE &&
+ !aInsertCommand.Data.is())
+ {
+ MissingInputStreamException excep;
+ ucbhelper::cancelCommandExecution(Any(excep), Env);
+ }
+
+ bool bReplace(aInsertCommand.ReplaceExisting);
+
+ retry:
+ try {
+ if(m_aInfo.Type == FTP_FILE) {
+ InsertData data(aInsertCommand.Data);
+ m_aFTPURL.insert(bReplace,&data);
+ } else if(m_aInfo.Type == FTP_FOLDER)
+ m_aFTPURL.mkdir(bReplace);
+ } catch(const curl_exception& e) {
+ if(e.code() == FOLDER_MIGHT_EXIST_DURING_INSERT ||
+ e.code() == FILE_MIGHT_EXIST_DURING_INSERT) {
+ // Interact
+ Reference<XInteractionHandler> xInt;
+ if(Env.is())
+ xInt = Env->getInteractionHandler();
+
+ UnsupportedNameClashException excep;
+ excep.NameClash = 0; //NameClash::ERROR;
+
+ if(!xInt.is()) {
+ ucbhelper::cancelCommandExecution(Any(excep), Env);
+ }
+
+ XInteractionRequestImpl request;
+ const Reference<XInteractionRequest>& xReq(request.getRequest());
+ xInt->handle(xReq);
+ if (request.approved()) {
+ bReplace = true;
+ goto retry;
+ }
+ else
+ throw excep;
+ }
+ else
+ throw;
+ }
+
+ // May not be reached, because both mkdir and insert can throw curl-
+ // exceptions
+ m_bInserted = false;
+ inserted();
+}
+
+
+Reference< XRow > FTPContent::getPropertyValues(
+ const Sequence< Property >& seqProp
+)
+{
+ rtl::Reference<ucbhelper::PropertyValueSet> xRow =
+ new ucbhelper::PropertyValueSet(m_xContext);
+
+ FTPDirentry aDirEntry = m_aFTPURL.direntry();
+
+ for(const auto& rProp : seqProp) {
+ const OUString& Name = rProp.Name;
+ if(Name == "Title")
+ xRow->appendString(rProp,aDirEntry.m_aName);
+ else if(Name == "CreatableContentsInfo")
+ xRow->appendObject(rProp,
+ Any(queryCreatableContentsInfo()));
+ else if(aDirEntry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN) {
+ if(Name == "ContentType")
+ xRow->appendString(rProp,
+ (aDirEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR)
+ ? OUString(FTP_FOLDER)
+ : OUString(FTP_FILE) );
+ else if(Name == "IsReadOnly")
+ xRow->appendBoolean(rProp,
+ (aDirEntry.m_nMode
+ & INETCOREFTP_FILEMODE_WRITE) == 0 );
+ else if(Name == "IsDocument")
+ xRow->appendBoolean(rProp,
+ (aDirEntry.m_nMode &
+ INETCOREFTP_FILEMODE_ISDIR) != INETCOREFTP_FILEMODE_ISDIR);
+ else if(Name == "IsFolder")
+ xRow->appendBoolean(rProp,
+ (aDirEntry.m_nMode &
+ INETCOREFTP_FILEMODE_ISDIR) == INETCOREFTP_FILEMODE_ISDIR);
+ else if(Name == "Size")
+ xRow->appendLong(rProp,
+ aDirEntry.m_nSize);
+ else if(Name == "DateCreated")
+ xRow->appendTimestamp(rProp,
+ aDirEntry.m_aDate);
+ else
+ xRow->appendVoid(rProp);
+ } else
+ xRow->appendVoid(rProp);
+ }
+
+ return xRow;
+}
+
+
+Sequence<Any> FTPContent::setPropertyValues(
+ const Sequence<PropertyValue>& seqPropVal)
+{
+ Sequence<Any> ret(seqPropVal.getLength());
+ auto retRange = asNonConstRange(ret);
+ Sequence<PropertyChangeEvent > evt;
+
+ osl::MutexGuard aGuard(m_aMutex);
+ for(sal_Int32 i = 0; i < ret.getLength(); ++i) {
+ if ( seqPropVal[i].Name == "Title" ) {
+ OUString Title;
+ if(!(seqPropVal[i].Value >>= Title)) {
+ retRange[i] <<= IllegalTypeException();
+ continue;
+ } else if(Title.isEmpty()) {
+ retRange[i] <<= IllegalArgumentException();
+ continue;
+ }
+
+ if(m_bInserted) {
+ m_aFTPURL.child(Title);
+ m_xIdentifier =
+ new FTPContentIdentifier(m_aFTPURL.ident(false,false));
+ m_bTitleSet = true;
+ } else
+ try {
+ OUString OldTitle = m_aFTPURL.ren(Title);
+ evt = { { /* Source */ {},
+ /* PropertyName */ "Title",
+ /* Further */ false,
+ /* PropertyHandle */ -1,
+ /* OldValue */ Any(OldTitle),
+ /* NewValue */ Any(Title) } };
+ } catch(const curl_exception&) {
+ InteractiveIOException excep;
+ // any better possibility here?
+ // ( the error code is always CURLE_FTP_QUOTE_ERROR )
+ excep.Code = IOErrorCode_ACCESS_DENIED;
+ retRange[i] <<= excep;
+ }
+ } else {
+ const Sequence<Property> props =
+ getProperties(Reference<XCommandEnvironment>(nullptr));
+
+ // either unknown or read-only
+ retRange[i] <<= UnknownPropertyException();
+ const auto& rName = seqPropVal[i].Name;
+ auto pProp = std::find_if(props.begin(), props.end(),
+ [&rName](const Property& rProp) { return rProp.Name == rName; });
+ if (pProp != props.end()) {
+ retRange[i] <<= IllegalAccessException(
+ "Property is read-only!",
+ //props[j].Attributes & PropertyAttribute::READONLY
+ // ? "Property is read-only!"
+ // : "Access denied!"),
+ static_cast< cppu::OWeakObject * >( this ));
+ }
+ }
+ }
+
+ if(evt.hasElements()) {
+ // title has changed
+ notifyPropertiesChange(evt);
+ (void)exchange(new FTPContentIdentifier(m_aFTPURL.ident(false,false)));
+ }
+
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontent.hxx b/ucb/source/ucp/ftp/ftpcontent.hxx
new file mode 100644
index 000000000..ab0bda66a
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontent.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ucbhelper/contenthelper.hxx>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/XContentCreator.hpp>
+#include "ftpurl.hxx"
+
+namespace com::sun::star::beans {
+ struct Property;
+ struct PropertyValue;
+}
+
+namespace com::sun::star::sdbc {
+ class XRow;
+}
+
+
+namespace ftp
+{
+
+class FTPContentProvider;
+
+class FTPContent : public ::ucbhelper::ContentImplHelper,
+ public css::ucb::XContentCreator
+{
+public:
+
+ FTPContent( const css::uno::Reference<
+ css::uno::XComponentContext >& rxContext,
+ FTPContentProvider* pProvider,
+ const css::uno::Reference<
+ css::ucb::XContentIdentifier >& Identifier,
+ const FTPURL& FtpUrl);
+
+ FTPContent( const css::uno::Reference<
+ css::uno::XComponentContext >& rxContext,
+ FTPContentProvider* pProvider,
+ const css::uno::Reference<
+ css::ucb::XContentIdentifier >& Identifier,
+ const css::ucb::ContentInfo& aInfo);
+
+
+ virtual ~FTPContent() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XContent
+ virtual OUString SAL_CALL getContentType() override;
+
+ // XCommandProcessor
+ virtual css::uno::Any SAL_CALL execute( const css::ucb::Command& aCommand,
+ sal_Int32 CommandId,
+ const css::uno::Reference<
+ css::ucb::XCommandEnvironment >& Environment ) override;
+
+ virtual void SAL_CALL abort(sal_Int32 CommandId) override;
+
+ // XContentCreator
+ virtual css::uno::Sequence<
+ css::ucb::ContentInfo > SAL_CALL
+ queryCreatableContentsInfo( ) override;
+
+ virtual css::uno::Reference<
+ css::ucb::XContent > SAL_CALL
+ createNewContent( const css::ucb::ContentInfo& Info ) override;
+
+ // XChild
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override;
+
+ virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override;
+
+ /// @throws css::uno::RuntimeException
+ static css::uno::Sequence< css::ucb::ContentInfo > queryCreatableContentsInfo_Static();
+
+private:
+
+ FTPContentProvider *m_pFCP;
+ FTPURL m_aFTPURL;
+ bool m_bInserted;
+ bool m_bTitleSet;
+ css::ucb::ContentInfo m_aInfo;
+
+ virtual css::uno::Sequence< css::beans::Property >
+ getProperties( const css::uno::Reference<
+ css::ucb::XCommandEnvironment > & xEnv ) override;
+
+
+ virtual css::uno::Sequence< css::ucb::CommandInfo>
+ getCommands(const css::uno::Reference<
+ css::ucb::XCommandEnvironment > & xEnv) override;
+
+
+ virtual OUString getParentURL() override;
+
+ css::uno::Reference<css::sdbc::XRow>
+ getPropertyValues(
+ const css::uno::Sequence<
+ css::beans::Property>& seqProp
+ );
+
+ css::uno::Sequence<css::uno::Any>
+ setPropertyValues( const css::uno::Sequence<
+ css::beans::PropertyValue>& seqPropVal);
+
+ void insert(const css::ucb::InsertCommandArgument&,
+ const css::uno::Reference<
+ css::ucb::XCommandEnvironment>&);
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontentcaps.cxx b/ucb/source/ucp/ftp/ftpcontentcaps.cxx
new file mode 100644
index 000000000..64dd0e92d
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontentcaps.cxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/CommandInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include "ftpcontent.hxx"
+
+using namespace com::sun::star;
+using namespace ftp;
+
+// virtual
+uno::Sequence< beans::Property > FTPContent::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/)
+{
+ #define PROPS_COUNT 8
+
+ static const beans::Property aPropsInfoTable[] =
+ {
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ // | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Size",
+ -1,
+ cppu::UnoType<sal_Int64>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "DateCreated",
+ -1,
+ cppu::UnoType<util::DateTime>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsReadOnly",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ )
+ };
+
+ return uno::Sequence< beans::Property >( aPropsInfoTable, PROPS_COUNT );
+}
+
+
+// virtual
+uno::Sequence< ucb::CommandInfo > FTPContent::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+// osl::MutexGuard aGuard( m_aMutex );
+
+
+ // Supported commands
+
+
+ #define COMMAND_COUNT 8
+
+ static const ucb::CommandInfo aCommandInfoTable[] =
+ {
+
+ // Required commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<ucb::InsertCommandArgument>::get()
+ ),
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ )
+ };
+
+ return uno::Sequence< ucb::CommandInfo >( aCommandInfoTable, COMMAND_COUNT );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontentidentifier.cxx b/ucb/source/ucp/ftp/ftpcontentidentifier.cxx
new file mode 100644
index 000000000..8bdc1b935
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontentidentifier.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#include "ftpcontentidentifier.hxx"
+
+using namespace ftp;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::lang;
+
+
+FTPContentIdentifier::FTPContentIdentifier(
+ const OUString& ident
+)
+ : m_ident(ident)
+{
+}
+
+
+FTPContentIdentifier::~FTPContentIdentifier()
+{
+}
+
+
+OUString SAL_CALL
+FTPContentIdentifier::getContentIdentifier(
+)
+{
+ return m_ident;
+}
+
+
+OUString SAL_CALL
+FTPContentIdentifier::getContentProviderScheme(
+)
+{
+ return "ftp";
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontentidentifier.hxx b/ucb/source/ucp/ftp/ftpcontentidentifier.hxx
new file mode 100644
index 000000000..c91ad704f
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontentidentifier.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ucb/XContentIdentifier.hpp>
+
+
+namespace ftp {
+
+ class FTPContentIdentifier :
+ public cppu::WeakImplHelper<css::ucb::XContentIdentifier>
+ {
+ public:
+
+ explicit FTPContentIdentifier(const OUString& ident);
+
+ virtual ~FTPContentIdentifier() override;
+
+ // XContentIdentifier
+
+ virtual OUString SAL_CALL
+ getContentIdentifier() override;
+
+ virtual OUString SAL_CALL
+ getContentProviderScheme() override;
+
+
+ private:
+
+ OUString m_ident;
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontentprovider.cxx b/ucb/source/ucp/ftp/ftpcontentprovider.cxx
new file mode 100644
index 000000000..5200fcfd1
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontentprovider.cxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include "ftpcontentprovider.hxx"
+#include "ftpcontent.hxx"
+#include "ftploaderthread.hxx"
+
+using namespace ftp;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::beans;
+
+// ContentProvider Implementation.
+
+FTPContentProvider::FTPContentProvider( const Reference< XComponentContext >& rxContext)
+ : ::ucbhelper::ContentProviderImplHelper(rxContext)
+{
+}
+
+
+// virtual
+FTPContentProvider::~FTPContentProvider()
+{
+ m_ftpLoaderThread.reset();
+ m_pProxyDecider.reset();
+}
+
+// XInterface methods.
+void SAL_CALL FTPContentProvider::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL FTPContentProvider::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL FTPContentProvider::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< XTypeProvider* >(this),
+ static_cast< XServiceInfo* >(this),
+ static_cast< XContentProvider* >(this)
+ );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+css::uno::Sequence< sal_Int8 > SAL_CALL FTPContentProvider::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL FTPContentProvider::getTypes()
+{
+ static cppu::OTypeCollection s_aCollection(
+ cppu::UnoType<XTypeProvider>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XContentProvider>::get()
+ );
+
+ return s_aCollection.getTypes();
+}
+
+
+// XServiceInfo methods.
+
+OUString SAL_CALL FTPContentProvider::getImplementationName()
+{
+ return "com.sun.star.comp.FTPContentProvider";
+}
+
+sal_Bool SAL_CALL FTPContentProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+css::uno::Sequence< OUString > SAL_CALL FTPContentProvider::getSupportedServiceNames()
+{
+ return { FTP_CONTENT_PROVIDER_SERVICE_NAME };
+}
+
+
+
+// XContentProvider methods.
+
+// virtual
+Reference<XContent> SAL_CALL FTPContentProvider::queryContent(
+ const Reference< XContentIdentifier >& xCanonicId)
+{
+ // Check, if a content with given id already exists...
+ Reference<XContent> xContent = queryExistingContent(xCanonicId);
+ if(xContent.is())
+ return xContent;
+
+ // A new content has to be returned:
+ {
+ // Initialize
+ osl::MutexGuard aGuard( m_aMutex );
+ if(!m_ftpLoaderThread || !m_pProxyDecider)
+ {
+ try {
+ init();
+ } catch (css::uno::Exception const & ex) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( ex.Message,
+ css::uno::Reference< css::uno::XInterface >(),
+ anyEx );
+ } catch( ... ) {
+ throw RuntimeException();
+ }
+
+ if(!m_ftpLoaderThread || !m_pProxyDecider)
+ throw RuntimeException();
+ }
+ }
+
+ try {
+ FTPURL aURL(xCanonicId->getContentIdentifier(),
+ this);
+
+ if(!m_pProxyDecider->shouldUseProxy(
+ "ftp",
+ aURL.host(),
+ aURL.port().toInt32()))
+ {
+ xContent = new FTPContent( m_xContext, this,xCanonicId,aURL);
+ registerNewContent(xContent);
+ }
+ else {
+ Reference<XContentProvider> xProvider(UniversalContentBroker::create( m_xContext )->queryContentProvider("http:"));
+ if(!xProvider.is())
+ throw RuntimeException();
+ return xProvider->queryContent(xCanonicId);
+ }
+ } catch(const malformed_exception&) {
+ throw IllegalIdentifierException();
+ }
+
+ // may throw IllegalIdentifierException
+ return xContent;
+}
+
+void FTPContentProvider::init()
+{
+ m_ftpLoaderThread.reset( new FTPLoaderThread() );
+ m_pProxyDecider.reset( new ucbhelper::InternetProxyDecider( m_xContext ) );
+}
+
+CURL* FTPContentProvider::handle()
+{
+ // Cannot be zero if called from here;
+ return m_ftpLoaderThread->handle();
+}
+
+
+void FTPContentProvider::forHost( std::u16string_view host,
+ std::u16string_view port,
+ std::u16string_view username,
+ OUString& password,
+ OUString& account)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ for(const ServerInfo & i : m_ServerInfo)
+ if(host == i.host &&
+ port == i.port &&
+ username == i.username )
+ {
+ password = i.password;
+ account = i.account;
+ return;
+ }
+}
+
+bool FTPContentProvider::setHost( const OUString& host,
+ const OUString& port,
+ const OUString& username,
+ const OUString& password,
+ const OUString& account)
+{
+ ServerInfo inf;
+ inf.host = host;
+ inf.port = port;
+ inf.username = username;
+ inf.password = password;
+ inf.account = account;
+
+ bool present(false);
+ osl::MutexGuard aGuard(m_aMutex);
+ for(ServerInfo & i : m_ServerInfo)
+ if(host == i.host &&
+ port == i.port &&
+ username == i.username)
+ {
+ present = true;
+ i.password = password;
+ i.account = account;
+ }
+
+ if(!present)
+ m_ServerInfo.push_back(inf);
+
+ return !present;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ucb_ftp_FTPContentProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new FTPContentProvider(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpcontentprovider.hxx b/ucb/source/ucp/ftp/ftpcontentprovider.hxx
new file mode 100644
index 000000000..fe1247b9e
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpcontentprovider.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <ucbhelper/proxydecider.hxx>
+#include <ucbhelper/providerhelper.hxx>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include "curl.hxx"
+
+// UNO service name for the provider. This name will be used by the UCB to
+// create instances of the provider.
+
+inline constexpr OUStringLiteral FTP_CONTENT_PROVIDER_SERVICE_NAME = u"com.sun.star.ucb.FTPContentProvider";
+inline constexpr OUStringLiteral FTP_CONTENT_TYPE = u"application/ftp-content";
+
+/**
+ * Definition of ftpcontentprovider
+ */
+namespace ftp
+{
+ class FTPLoaderThread;
+
+ class FTPContentProvider:
+ public ::ucbhelper::ContentProviderImplHelper
+ {
+ public:
+
+ explicit FTPContentProvider( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual ~FTPContentProvider() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XContentProvider
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override;
+
+ CURL* handle();
+
+ /** host is in the form host:port.
+ */
+
+ void forHost(std::u16string_view host,
+ std::u16string_view port,
+ std::u16string_view username,
+ OUString& password,
+ OUString& account);
+
+ bool setHost(const OUString& host,
+ const OUString& port,
+ const OUString& username,
+ const OUString& password,
+ const OUString& account);
+
+ struct ServerInfo
+ {
+ OUString host;
+ OUString port;
+ OUString username;
+ OUString password;
+ OUString account;
+ };
+
+ private:
+ std::unique_ptr<FTPLoaderThread> m_ftpLoaderThread;
+ std::unique_ptr<ucbhelper::InternetProxyDecider> m_pProxyDecider;
+ std::vector<ServerInfo> m_ServerInfo;
+
+ void init();
+ }; // end class FTPContentProvider
+
+} // end namespace ftp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpdirp.cxx b/ucb/source/ucp/ftp/ftpdirp.cxx
new file mode 100644
index 000000000..69bea64ab
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpdirp.cxx
@@ -0,0 +1,1269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#include "ftpdirp.hxx"
+#include <osl/time.h>
+
+
+using namespace ftp;
+
+static bool ascii_isWhitespace( sal_Unicode ch )
+{
+ return ((ch <= 0x20) && ch);
+}
+
+
+/*========================================================================
+ *
+ * FTPDirectoryParser implementation.
+ *
+ *======================================================================*/
+/*
+ * parseDOS.
+ * Accepts one of two styles:
+ *
+ * 1 *WSP 1*2DIGIT ("." / "-") 1*2DIGIT ("." / "-") 1*4DIGIT 1*WSP
+ * 1*2DIGIT ":" 1*2DIGIT [*WSP ("A" / "P") "M"] 1*WSP
+ * ((DIGIT *(DIGIT / "." / ",")) / "<DIR>") 1*WSP 1*OCTET
+ *
+ * interpreted as: mm.dd.yy hh:mm (size / <DIR>) name
+ *
+ * 2 *WSP 1*DIGIT 1*WSP *(1*CHAR *WSP) *1("DIR" 1*WSP) 1*2DIGIT "-" 1*2DIGIT
+ * "-" 1*4DIGIT 1*WSP 1*2DIGIT ":" 1*2DIGIT 1*WSP 1*OCTET
+ *
+ * interpreted as: size attribs DIR mm-dd-yy hh:mm name
+ */
+
+bool FTPDirectoryParser::parseDOS (
+ FTPDirentry &rEntry,
+ const char *pBuffer)
+{
+ bool bDirectory = false;
+ sal_uInt32 nSize = 0;
+ sal_uInt16 nYear = 0;
+ sal_uInt16 nMonth = 0;
+ sal_uInt16 nDay = 0;
+ sal_uInt16 nHour = 0;
+ sal_uInt16 nMinute = 0;
+
+ enum StateType
+ {
+ STATE_INIT_LWS,
+ STATE_MONTH_OR_SIZE,
+ STATE_1_DAY, STATE_1_YEAR, STATE_1_YEAR_LWS, STATE_1_HOUR,
+ STATE_1_MINUTE, STATE_1_MINUTE_LWS, STATE_1_AP,
+ STATE_1_APM, STATE_1_LESS, STATE_1_D, STATE_1_DI,
+ STATE_1_DIR, STATE_1_SIZE,
+ STATE_2_SIZE, STATE_2_SIZE_LWS, STATE_2_ATTRIB,
+ STATE_2_D, STATE_2_DI, STATE_2_DIR_LWS,
+ STATE_2_MONTH, STATE_2_DAY, STATE_2_YEAR, STATE_2_YEAR_LWS,
+ STATE_2_HOUR, STATE_2_MINUTE,
+ STATE_LWS_NAME,
+ STATE_ERROR
+ };
+
+ int nDigits = 0;
+ enum StateType eState = STATE_INIT_LWS;
+ for (const char *p = pBuffer;
+ eState != STATE_ERROR && *p;
+ ++p)
+ {
+ switch (eState)
+ {
+ case STATE_INIT_LWS:
+ if (*p >= '0' && *p <= '9')
+ {
+ nMonth = *p - '0';
+ nDigits = 1;
+ eState = STATE_MONTH_OR_SIZE;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_MONTH_OR_SIZE:
+ if (*p >= '0' && *p <= '9')
+ {
+ nMonth = 10 * nMonth + (*p - '0');
+ if (nDigits < 2)
+ ++nDigits;
+ else
+ {
+ nSize = nMonth;
+ nMonth = 0;
+ eState = STATE_2_SIZE;
+ }
+ }
+ else if (ascii_isWhitespace(*p))
+ {
+ nSize = nMonth;
+ nMonth = 0;
+ eState = STATE_2_SIZE_LWS;
+ }
+ else if ((*p == '.' || *p == '-') && nMonth && nMonth <= 12)
+ {
+ nDigits = 0;
+ eState = STATE_1_DAY;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_DAY:
+ if (*p >= '0' && *p <= '9')
+ if (nDigits < 2)
+ {
+ nDay = 10 * nDay + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ else if ((*p == '.' || *p == '-') && nDay && nDay <= 31)
+ {
+ nDigits = 0;
+ eState = STATE_1_YEAR;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_YEAR:
+ if (*p >= '0' && *p <= '9')
+ {
+ if (nDigits < 4)
+ {
+ nYear = 10 * nYear + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ }
+ else
+ {
+ if (ascii_isWhitespace(*p))
+ eState = STATE_1_YEAR_LWS;
+ else
+ eState = STATE_ERROR;
+ }
+ break;
+
+ case STATE_1_YEAR_LWS:
+ if (*p >= '0' && *p <= '9')
+ {
+ nHour = *p - '0';
+ nDigits = 1;
+ eState = STATE_1_HOUR;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_HOUR:
+ if (*p >= '0' && *p <= '9')
+ if (nDigits < 2)
+ {
+ nHour = 10 * nHour + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ else if (*p == ':' && nHour < 24)
+ {
+ nDigits = 0;
+ eState = STATE_1_MINUTE;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_MINUTE:
+ if (*p >= '0' && *p <= '9')
+ if (nDigits < 2)
+ {
+ nMinute = 10 * nMinute + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ else if ((*p == 'a' || *p == 'A') && nMinute < 60)
+ if (nHour >= 1 && nHour <= 11)
+ eState = STATE_1_AP;
+ else if (nHour == 12)
+ {
+ nHour = 0;
+ eState = STATE_1_AP;
+ }
+ else
+ eState = STATE_ERROR;
+ else if ((*p == 'p' || *p == 'P') && nMinute < 60)
+ if (nHour >= 1 && nHour <= 11)
+ {
+ nHour += 12;
+ eState = STATE_1_AP;
+ }
+ else if (nHour == 12)
+ eState = STATE_1_AP;
+ else
+ eState = STATE_ERROR;
+ else if (ascii_isWhitespace(*p) && (nMinute < 60))
+ eState = STATE_1_MINUTE_LWS;
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_MINUTE_LWS:
+ if (*p == 'a' || *p == 'A')
+ if (nHour >= 1 && nHour <= 11)
+ eState = STATE_1_AP;
+ else if (nHour == 12)
+ {
+ nHour = 0;
+ eState = STATE_1_AP;
+ }
+ else
+ eState = STATE_ERROR;
+ else if (*p == 'p' || *p == 'P')
+ if (nHour >= 1 && nHour <= 11)
+ {
+ nHour += 12;
+ eState = STATE_1_AP;
+ }
+ else if (nHour == 12)
+ eState = STATE_1_AP;
+ else
+ eState = STATE_ERROR;
+ else if (*p == '<')
+ eState = STATE_1_LESS;
+ else if (*p >= '0' && *p <= '9')
+ {
+ nSize = *p - '0';
+ eState = STATE_1_SIZE;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_AP:
+ eState = *p == 'm' || *p == 'M' ? STATE_1_APM : STATE_ERROR;
+ break;
+
+ case STATE_1_APM:
+ if (*p == '<')
+ eState = STATE_1_LESS;
+ else if (*p >= '0' && *p <= '9')
+ {
+ nSize = *p - '0';
+ eState = STATE_1_SIZE;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_LESS:
+ eState = *p == 'd' || *p == 'D' ? STATE_1_D : STATE_ERROR;
+ break;
+
+ case STATE_1_D:
+ eState = *p == 'i' || *p == 'I' ? STATE_1_DI : STATE_ERROR;
+ break;
+
+ case STATE_1_DI:
+ eState = *p == 'r' || *p == 'R' ? STATE_1_DIR : STATE_ERROR;
+ break;
+
+ case STATE_1_DIR:
+ if (*p == '>')
+ {
+ bDirectory = true;
+ eState = STATE_LWS_NAME;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_1_SIZE:
+ if (*p >= '0' && *p <= '9')
+ nSize = 10 * nSize + (*p - '0');
+ else if (ascii_isWhitespace(*p))
+ eState = STATE_LWS_NAME;
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_SIZE:
+ if (*p >= '0' && *p <= '9')
+ nSize = 10 * nSize + (*p - '0');
+ else if (ascii_isWhitespace(*p))
+ eState = STATE_2_SIZE_LWS;
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_SIZE_LWS:
+ if (*p == 'd' || *p == 'D')
+ eState = STATE_2_D;
+ else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
+ eState = STATE_2_ATTRIB;
+ else if (*p >= '0' && *p <= '9')
+ {
+ nMonth = *p - '0';
+ nDigits = 1;
+ eState = STATE_2_MONTH;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_ATTRIB:
+ if (ascii_isWhitespace(*p))
+ eState = STATE_2_SIZE_LWS;
+ else if ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z'))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_D:
+ if (*p == 'i' || *p == 'I')
+ eState = STATE_2_DI;
+ else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
+ eState = STATE_2_ATTRIB;
+ else if (ascii_isWhitespace(*p))
+ eState = STATE_2_SIZE_LWS;
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_DI:
+ if (*p == 'r' || *p == 'R')
+ {
+ bDirectory = true;
+ eState = STATE_2_DIR_LWS;
+ }
+ else
+ {
+ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
+ eState = STATE_2_ATTRIB;
+ else if (ascii_isWhitespace(*p))
+ eState = STATE_2_SIZE_LWS;
+ else
+ eState = STATE_ERROR;
+ }
+ break;
+
+ case STATE_2_DIR_LWS:
+ if (*p >= '0' && *p <= '9')
+ {
+ nMonth = *p - '0';
+ nDigits = 1;
+ eState = STATE_2_MONTH;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_MONTH:
+ if (*p >= '0' && *p <= '9')
+ if (nDigits < 2)
+ {
+ nMonth = 10 * nMonth + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ else if (*p == '-' && nMonth && nMonth <= 12)
+ {
+ nDigits = 0;
+ eState = STATE_2_DAY;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_DAY:
+ if (*p >= '0' && *p <= '9')
+ if (nDigits < 2)
+ {
+ nDay = 10 * nDay + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ else if (*p == '-' && nDay && nDay <= 31)
+ {
+ nDigits = 0;
+ eState = STATE_2_YEAR;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_YEAR:
+ if (*p >= '0' && *p <= '9')
+ {
+ if (nDigits < 4)
+ {
+ nYear = 10 * nYear + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ }
+ else
+ {
+ if (ascii_isWhitespace(*p))
+ eState = STATE_2_YEAR_LWS;
+ else
+ eState = STATE_ERROR;
+ }
+ break;
+
+ case STATE_2_YEAR_LWS:
+ if (*p >= '0' && *p <= '9')
+ {
+ nHour = *p - '0';
+ nDigits = 1;
+ eState = STATE_2_HOUR;
+ }
+ else if (!ascii_isWhitespace(*p))
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_HOUR:
+ if (*p >= '0' && *p <= '9')
+ if (nDigits < 2)
+ {
+ nHour = 10 * nHour + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ else if (*p == ':' && nHour < 24)
+ {
+ nDigits = 0;
+ eState = STATE_2_MINUTE;
+ }
+ else
+ eState = STATE_ERROR;
+ break;
+
+ case STATE_2_MINUTE:
+ if (*p >= '0' && *p <= '9')
+ {
+ if (nDigits < 2)
+ {
+ nMinute = 10 * nMinute + (*p - '0');
+ ++nDigits;
+ }
+ else
+ eState = STATE_ERROR;
+ }
+ else
+ {
+ if (ascii_isWhitespace(*p) && (nMinute < 60))
+ eState = STATE_LWS_NAME;
+ else
+ eState = STATE_ERROR;
+ }
+ break;
+
+ case STATE_LWS_NAME:
+ if (!ascii_isWhitespace(*p))
+ {
+ setPath (rEntry.m_aName, p);
+ if (bDirectory)
+ rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
+ rEntry.m_nSize = nSize;
+
+ setYear (rEntry.m_aDate, nYear);
+
+ rEntry.m_aDate.SetMonth(nMonth);
+ rEntry.m_aDate.SetDay(nDay);
+ rEntry.m_aDate.SetHour(nHour);
+ rEntry.m_aDate.SetMin(nMinute);
+
+ return true;
+ }
+ break;
+ case STATE_ERROR:
+ break;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * parseVMS.
+ * Directory entries may span one or two lines:
+ *
+ * entry: *lws name *1(*lws <NEWLINE>) 1*lws size 1*lws datetime rest
+ *
+ * name: filename "." filetype ";" version
+ * filename: 1*39fchar
+ * filetype: 1*39fchar
+ * version: non0digit *digit
+ *
+ * size: "0" / non0digit *digit
+ *
+ * datetime: date 1*lwsp time
+ * date: day "-" month "-" year
+ * day: (*1"0" non0digit) / ("1"-"2" digit) / ("3" "0"-"1")
+ * month: "JAN" / "FEB" / "MAR" / "APR" / "MAY" / "JUN" / "JUL" / "AUG"
+ * / "SEP" / "OCT" / "NOV" / "DEC" ; all case insensitive
+ * year: 2digit / 4digit
+ * time: hour ":" minute
+ * hour: ((*1"0" / "1") digit) / ("2" "0"-"3")
+ * minute: "0"-"5" digit
+ *
+ * rest: *1(lws *<ANY>)
+ *
+ * lws: <TAB> / <SPACE>
+ * non0digit: "1"-"9"
+ * digit: "0" / non0digit
+ * fchar: "A"-"Z" / "a"-"z" / digit / "-" / "_" / "$"
+ *
+ * For directories, the returned name is the <filename> part; for non-
+ * directory files, the returned name is the <filename "." filetype> part.
+ * An entry is a directory iff its filetype is "DIR" (ignoring case).
+ *
+ * The READ, WRITE, and ISLINK mode bits are not supported.
+ *
+ * The returned size is the <size> part, multiplied by 512, and with the high
+ * order bits truncated to fit into a sal_uInt32.
+ *
+ */
+bool FTPDirectoryParser::parseVMS (
+ FTPDirentry &rEntry,
+ const char *pBuffer)
+{
+ static OUString aFirstLineName;
+ static bool bFirstLineDir = false;
+
+ for (bool bFirstLine = true;; bFirstLine = false)
+ {
+ const char *p = pBuffer;
+ if (bFirstLine)
+ {
+ // Skip <*lws> part:
+ while (*p == '\t' || *p == ' ')
+ ++p;
+
+ // Parse <filename "."> part:
+ const char *pFileName = p;
+ while ((*p >= 'A' && *p <= 'Z') ||
+ (*p >= 'a' && *p <= 'z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '-' || *p == '_' || *p == '$')
+ ++p;
+
+ if (*p != '.' || p == pFileName || p - pFileName > 39)
+ {
+ if (!aFirstLineName.isEmpty())
+ continue;
+ else
+ return false;
+ }
+
+ // Parse <filetype ";"> part:
+ const char *pFileType = ++p;
+ while ((*p >= 'A' && *p <= 'Z') ||
+ (*p >= 'a' && *p <= 'z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '-' || *p == '_' || *p == '$')
+ ++p;
+
+ if (*p != ';' || p == pFileName || p - pFileName > 39)
+ {
+ if (!aFirstLineName.isEmpty())
+ continue;
+ else
+ return false;
+ }
+ ++p;
+
+ // Set entry's name and mode (ISDIR flag):
+ if ((p - pFileType == 4) &&
+ (pFileType[0] == 'D' || pFileType[0] == 'd') &&
+ (pFileType[1] == 'I' || pFileType[1] == 'i') &&
+ (pFileType[2] == 'R' || pFileType[2] == 'r') )
+ {
+ setPath (rEntry.m_aName, pFileName, (pFileType - pFileName));
+ rEntry.m_nMode = INETCOREFTP_FILEMODE_ISDIR;
+ }
+ else
+ {
+ setPath (rEntry.m_aName, pFileName, (p - pFileName));
+ rEntry.m_nMode = 0;
+ }
+
+ // Skip <version> part:
+ if (*p < '1' || *p > '9')
+ {
+ if (!aFirstLineName.isEmpty())
+ continue;
+ else
+ return false;
+ }
+ ++p;
+ while (*p >= '0' && *p <= '9')
+ ++p;
+
+ // Parse <1*lws> or <*lws <NEWLINE>> part:
+ bool bLWS = false;
+ while (*p == '\t' || *p == ' ')
+ {
+ bLWS = true;
+ ++p;
+ }
+ if (*p)
+ {
+ if (!bLWS)
+ {
+ if (!aFirstLineName.isEmpty())
+ continue;
+ else
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * First line of entry spanning two lines,
+ * wait for second line.
+ */
+ aFirstLineName = rEntry.m_aName;
+ bFirstLineDir =
+ ((rEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) != 0);
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * Second line of entry spanning two lines,
+ * restore entry's name and mode (ISDIR flag).
+ */
+ rEntry.m_aName = aFirstLineName;
+ rEntry.m_nMode = (bFirstLineDir ? INETCOREFTP_FILEMODE_ISDIR : 0);
+
+ // Skip <1*lws> part:
+ if (*p != '\t' && *p != ' ')
+ return false;
+ ++p;
+ while (*p == '\t' || *p == ' ')
+ ++p;
+ }
+
+ // Parse <size> part and set entry's size:
+ if (*p < '0' || *p > '9')
+ return false;
+ sal_uInt32 nSize = *p - '0';
+ if (*p++ != '0')
+ while (*p >= '0' && *p <= '9')
+ nSize = 10 * rEntry.m_nSize + (*p++ - '0');
+ rEntry.m_nSize = 512 * nSize;
+
+ // Skip <1*lws> part:
+ if (*p != '\t' && *p != ' ')
+ return false;
+ ++p;
+ while (*p == '\t' || *p == ' ')
+ ++p;
+
+ // Parse <day "-"> part and set entry date's day:
+ sal_uInt16 nDay;
+ if (*p == '0')
+ {
+ ++p;
+ if (*p < '1' || *p > '9')
+ return false;
+ nDay = *p++ - '0';
+ }
+ else if (*p == '1' || *p == '2')
+ {
+ nDay = *p++ - '0';
+ if (*p >= '0' && *p <= '9')
+ nDay = 10 * nDay + (*p++ - '0');
+ }
+ else if (*p == '3')
+ {
+ ++p;
+ nDay = (*p == '0' || *p == '1') ? 30 + (*p++ - '0') : 3;
+ }
+ else if (*p >= '4' && *p <= '9')
+ nDay = *p++ - '0';
+ else
+ return false;
+
+ rEntry.m_aDate.SetDay(nDay);
+ if (*p++ != '-')
+ return false;
+
+ // Parse <month "-"> part and set entry date's month:
+ char const * pMonth = p;
+ sal_Int32 const monthLen = 3;
+ for (int i = 0; i < monthLen; ++i)
+ {
+ if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')))
+ return false;
+ ++p;
+ }
+ if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "JAN", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(1);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "FEB", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(2);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "MAR", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(3);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "APR", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(4);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "MAY", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(5);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "JUN", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(6);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "JUL", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(7);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "AUG", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(8);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "SEP", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(9);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "OCT", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(10);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "NOV", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(11);
+ else if (rtl_str_compareIgnoreAsciiCase_WithLength(
+ pMonth, monthLen, "DEC", monthLen) == 0)
+ rEntry.m_aDate.SetMonth(12);
+ else
+ return false;
+ if (*p++ != '-')
+ return false;
+
+ // Parse <year> part and set entry date's year:
+ sal_uInt16 nYear = 0;
+ for (int i = 0; i < 2; ++i)
+ {
+ if (*p < '0' || *p > '9')
+ return false;
+ nYear = 10 * nYear + (*p++ - '0');
+ }
+ if (*p >= '0' && *p <= '9')
+ {
+ nYear = 10 * nYear + (*p++ - '0');
+ if (*p < '0' || *p > '9')
+ return false;
+ nYear = 10 * nYear + (*p++ - '0');
+ }
+ setYear (rEntry.m_aDate, nYear);
+
+ // Skip <1*lws> part:
+ if (*p != '\t' && *p != ' ')
+ return false;
+ ++p;
+ while (*p == '\t' || *p == ' ')
+ ++p;
+
+ // Parse <hour ":"> part and set entry time's hour:
+ sal_uInt16 nHour;
+ if (*p == '0' || *p == '1')
+ {
+ nHour = *p++ - '0';
+ if (*p >= '0' && *p <= '9')
+ nHour = 10 * nHour + (*p++ - '0');
+ }
+ else if (*p == '2')
+ {
+ ++p;
+ nHour = (*p >= '0' && *p <= '3') ? 20 + (*p++ - '0') : 2;
+ }
+ else if (*p >= '3' && *p <= '9')
+ nHour = *p++ - '0';
+ else
+ return false;
+
+ rEntry.m_aDate.SetHour(nHour);
+ if (*p++ != ':')
+ return false;
+
+ /*
+ * Parse <minute> part and set entry time's minutes,
+ * seconds (0), and nanoseconds (0).
+ */
+ if (*p < '0' || *p > '5')
+ return false;
+
+ sal_uInt16 nMinute = *p++ - '0';
+ if (*p < '0' || *p > '9')
+ return false;
+
+ nMinute = 10 * nMinute + (*p++ - '0');
+ rEntry.m_aDate.SetMin(nMinute);
+ rEntry.m_aDate.SetSec(0);
+ rEntry.m_aDate.SetNanoSec(0);
+
+ // Skip <rest> part:
+ return !*p || *p == '\t' || *p == ' ';
+ }
+}
+
+/*
+ * parseUNIX
+ */
+bool FTPDirectoryParser::parseUNIX (
+ FTPDirentry &rEntry,
+ const char *pBuffer)
+{
+ const char *p1, *p2;
+ p1 = pBuffer;
+
+ if (!((*p1 == '-') || (*p1 == 'd') || (*p1 == 'l')))
+ return false;
+
+ // 1st column: FileMode.
+ if (*p1 == 'd')
+ rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISDIR;
+
+ if (*p1 == 'l')
+ rEntry.m_nMode |= INETCOREFTP_FILEMODE_ISLINK;
+
+ // Skip to end of column and set rights by the way
+ while (*p1 && !ascii_isWhitespace(*p1)) {
+ if(*p1 == 'r')
+ rEntry.m_nMode |= INETCOREFTP_FILEMODE_READ;
+ else if(*p1 == 'w')
+ rEntry.m_nMode |= INETCOREFTP_FILEMODE_WRITE;
+ p1++;
+ }
+
+ /*
+ * Scan for the sequence of size and date fields:
+ * *LWS 1*DIGIT 1*LWS 3CHAR 1*LWS 1*2DIGIT 1*LWS
+ * (4DIGIT / (1*2DIGIT ":" 2DIGIT)) 1*LWS
+ */
+ enum Mode
+ {
+ FOUND_NONE, FOUND_SIZE, FOUND_MONTH, FOUND_DAY, FOUND_YEAR_TIME
+ };
+
+ const char *pDayStart = nullptr;
+ const char *pDayEnd = nullptr;
+ Mode eMode;
+ for (eMode = FOUND_NONE; *p1 && eMode != FOUND_YEAR_TIME; p1 = p2 + 1)
+ {
+ while (*p1 && ascii_isWhitespace(*p1))
+ ++p1;
+ p2 = p1;
+ while (*p2 && !ascii_isWhitespace(*p2))
+ ++p2;
+
+ switch (eMode)
+ {
+ case FOUND_NONE:
+ if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
+ eMode = FOUND_SIZE;
+ break;
+
+ case FOUND_SIZE:
+ if (parseUNIX_isMonthField (p1, p2, rEntry.m_aDate))
+ eMode = FOUND_MONTH;
+ else if (!parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
+ eMode = FOUND_NONE;
+ break;
+
+ case FOUND_MONTH:
+ if (parseUNIX_isDayField (p1, p2, rEntry.m_aDate))
+ {
+ pDayStart = p1;
+ pDayEnd = p2;
+ eMode = FOUND_DAY;
+ }
+ else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
+ eMode = FOUND_SIZE;
+ else
+ eMode = FOUND_NONE;
+ break;
+
+ case FOUND_DAY:
+ if (parseUNIX_isYearTimeField (p1, p2, rEntry.m_aDate))
+ eMode = FOUND_YEAR_TIME;
+ else if (
+ parseUNIX_isSizeField (
+ pDayStart, pDayEnd, rEntry.m_nSize) &&
+ parseUNIX_isMonthField (
+ p1, p2, rEntry.m_aDate))
+ eMode = FOUND_MONTH;
+ else if (parseUNIX_isSizeField (p1, p2, rEntry.m_nSize))
+ eMode = FOUND_SIZE;
+ else
+ eMode = FOUND_NONE;
+ break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case FOUND_YEAR_TIME:
+ break;
+ }
+ }
+
+ if (eMode == FOUND_YEAR_TIME)
+ {
+ // 9th column: FileName (rest of line).
+ while (*p1 && ascii_isWhitespace(*p1)) p1++;
+ setPath (rEntry.m_aName, p1);
+
+ // Done.
+ return true;
+ }
+ return false;
+}
+
+/*
+ * parseUNIX_isSizeField.
+ */
+bool FTPDirectoryParser::parseUNIX_isSizeField (
+ const char *pStart,
+ const char *pEnd,
+ sal_uInt32 &rSize)
+{
+ if (!*pStart || !*pEnd || pStart == pEnd)
+ return false;
+
+ rSize = 0;
+ if (*pStart >= '0' && *pStart <= '9')
+ {
+ for (; pStart < pEnd; ++pStart)
+ if ((*pStart >= '0') && (*pStart <= '9'))
+ rSize = 10 * rSize + (*pStart - '0');
+ else
+ return false;
+ return true;
+ }
+ else
+ {
+ /*
+ * For a combination of long group name and large file size,
+ * some FTPDs omit LWS between those two columns.
+ */
+ int nNonDigits = 0;
+ int nDigits = 0;
+
+ for (; pStart < pEnd; ++pStart)
+ if ((*pStart >= '1') && (*pStart <= '9'))
+ {
+ ++nDigits;
+ rSize = 10 * rSize + (*pStart - '0');
+ }
+ else if ((*pStart == '0') && nDigits)
+ {
+ ++nDigits;
+ rSize *= 10;
+ }
+ else if ((*pStart > ' ') && (sal::static_int_cast<sal_uInt8>(*pStart) <= '\x7F'))
+ {
+ nNonDigits += nDigits + 1;
+ nDigits = 0;
+ rSize = 0;
+ }
+ else
+ return false;
+ return ((nNonDigits >= 9) && (nDigits >= 7));
+ }
+}
+
+/*
+ * parseUNIX_isMonthField.
+ */
+bool FTPDirectoryParser::parseUNIX_isMonthField (
+ const char *pStart,
+ const char *pEnd,
+ DateTime &rDateTime)
+{
+ if (!*pStart || !*pEnd || pStart + 3 != pEnd)
+ return false;
+
+ if ((pStart[0] == 'j' || pStart[0] == 'J') &&
+ (pStart[1] == 'a' || pStart[1] == 'A') &&
+ (pStart[2] == 'n' || pStart[2] == 'N') )
+ {
+ rDateTime.SetMonth(1);
+ return true;
+ }
+ if ((pStart[0] == 'f' || pStart[0] == 'F') &&
+ (pStart[1] == 'e' || pStart[1] == 'E') &&
+ (pStart[2] == 'b' || pStart[2] == 'B') )
+ {
+ rDateTime.SetMonth(2);
+ return true;
+ }
+ if ((pStart[0] == 'm' || pStart[0] == 'M') &&
+ (pStart[1] == 'a' || pStart[1] == 'A') &&
+ (pStart[2] == 'r' || pStart[2] == 'R') )
+ {
+ rDateTime.SetMonth(3);
+ return true;
+ }
+ if ((pStart[0] == 'a' || pStart[0] == 'A') &&
+ (pStart[1] == 'p' || pStart[1] == 'P') &&
+ (pStart[2] == 'r' || pStart[2] == 'R') )
+ {
+ rDateTime.SetMonth(4);
+ return true;
+ }
+ if ((pStart[0] == 'm' || pStart[0] == 'M') &&
+ (pStart[1] == 'a' || pStart[1] == 'A') &&
+ (pStart[2] == 'y' || pStart[2] == 'Y') )
+ {
+ rDateTime.SetMonth(5);
+ return true;
+ }
+ if ((pStart[0] == 'j' || pStart[0] == 'J') &&
+ (pStart[1] == 'u' || pStart[1] == 'U') &&
+ (pStart[2] == 'n' || pStart[2] == 'N') )
+ {
+ rDateTime.SetMonth(6);
+ return true;
+ }
+ if ((pStart[0] == 'j' || pStart[0] == 'J') &&
+ (pStart[1] == 'u' || pStart[1] == 'U') &&
+ (pStart[2] == 'l' || pStart[2] == 'L') )
+ {
+ rDateTime.SetMonth(7);
+ return true;
+ }
+ if ((pStart[0] == 'a' || pStart[0] == 'A') &&
+ (pStart[1] == 'u' || pStart[1] == 'U') &&
+ (pStart[2] == 'g' || pStart[2] == 'G') )
+ {
+ rDateTime.SetMonth(8);
+ return true;
+ }
+ if ((pStart[0] == 's' || pStart[0] == 'S') &&
+ (pStart[1] == 'e' || pStart[1] == 'E') &&
+ (pStart[2] == 'p' || pStart[2] == 'P') )
+ {
+ rDateTime.SetMonth(9);
+ return true;
+ }
+ if ((pStart[0] == 'o' || pStart[0] == 'O') &&
+ (pStart[1] == 'c' || pStart[1] == 'C') &&
+ (pStart[2] == 't' || pStart[2] == 'T') )
+ {
+ rDateTime.SetMonth(10);
+ return true;
+ }
+ if ((pStart[0] == 'n' || pStart[0] == 'N') &&
+ (pStart[1] == 'o' || pStart[1] == 'O') &&
+ (pStart[2] == 'v' || pStart[2] == 'V') )
+ {
+ rDateTime.SetMonth(11);
+ return true;
+ }
+ if ((pStart[0] == 'd' || pStart[0] == 'D') &&
+ (pStart[1] == 'e' || pStart[1] == 'E') &&
+ (pStart[2] == 'c' || pStart[2] == 'C') )
+ {
+ rDateTime.SetMonth(12);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * parseUNIX_isDayField.
+ */
+bool FTPDirectoryParser::parseUNIX_isDayField (
+ const char *pStart,
+ const char *pEnd,
+ DateTime &rDateTime)
+{
+ if (!*pStart || !*pEnd || pStart == pEnd)
+ return false;
+ if (*pStart < '0' || *pStart > '9')
+ return false;
+
+ sal_uInt16 nDay = *pStart - '0';
+ if (pStart + 1 < pEnd)
+ {
+ if (pStart + 2 != pEnd || pStart[1] < '0' || pStart[1] > '9')
+ return false;
+ nDay = 10 * nDay + (pStart[1] - '0');
+ }
+ if (!nDay || nDay > 31)
+ return false;
+
+ rDateTime.SetDay(nDay);
+ return true;
+}
+
+/*
+ * parseUNIX_isYearTimeField.
+ */
+bool FTPDirectoryParser::parseUNIX_isYearTimeField (
+ const char *pStart,
+ const char *pEnd,
+ DateTime &rDateTime)
+{
+ if (!*pStart || !*pEnd || pStart == pEnd ||
+ *pStart < '0' || *pStart > '9')
+ return false;
+
+ sal_uInt16 nNumber = *pStart - '0';
+ ++pStart;
+
+ if (pStart == pEnd)
+ return false;
+ if (*pStart == ':')
+ return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
+ if (*pStart < '0' || *pStart > '9')
+ return false;
+
+ nNumber = 10 * nNumber + (*pStart - '0');
+ ++pStart;
+
+ if (pStart == pEnd)
+ return false;
+ if (*pStart == ':')
+ return parseUNIX_isTime (pStart, pEnd, nNumber, rDateTime);
+ if (*pStart < '0' || *pStart > '9')
+ return false;
+
+ nNumber = 10 * nNumber + (*pStart - '0');
+ ++pStart;
+
+ if (pStart == pEnd || *pStart < '0' || *pStart > '9')
+ return false;
+
+ nNumber = 10 * nNumber + (*pStart - '0');
+ if (pStart + 1 != pEnd || nNumber < 1970)
+ return false;
+
+ rDateTime.SetYear(nNumber);
+ rDateTime.SetTime();
+ return true;
+}
+
+/*
+ * parseUNIX_isTime.
+ */
+bool FTPDirectoryParser::parseUNIX_isTime (
+ const char *pStart,
+ const char *pEnd,
+ sal_uInt16 nHour,
+ DateTime &rDateTime)
+{
+ if ((nHour > 23 ) || (pStart + 3 != pEnd) ||
+ (pStart[1] < '0') || (pStart[1] > '5') ||
+ (pStart[2] < '0') || (pStart[2] > '9') )
+ return false;
+
+ sal_uInt16 nMin = 10 * (pStart[1] - '0') + (pStart[2] - '0');
+
+ rDateTime.SetHour (nHour);
+ rDateTime.SetMin (nMin);
+ rDateTime.SetSec (0);
+ rDateTime.SetNanoSec (0);
+
+// Date aCurDate;
+// if (rDateTime.GetMonth() > aCurDate.GetMonth())
+// rDateTime.SetYear(aCurDate.GetYear() - 1);
+// else
+// rDateTime.SetYear(aCurDate.GetYear());
+// return sal_True;
+
+ TimeValue aTimeVal;
+ osl_getSystemTime(&aTimeVal);
+ oslDateTime aCurrDateTime;
+ osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
+
+ if (rDateTime.GetMonth() > aCurrDateTime.Month)
+ rDateTime.SetYear(aCurrDateTime.Year - 1);
+ else
+ rDateTime.SetYear(aCurrDateTime.Year);
+ return true;
+}
+
+/*
+ * setYear.
+ *
+ * Two-digit years are taken as within 50 years back and 49 years forward
+ * (both ends inclusive) from the current year. The returned date is not
+ * checked for validity of the given day in the given month and year.
+ *
+ */
+void FTPDirectoryParser::setYear (
+ DateTime &rDateTime, sal_uInt16 nYear)
+{
+ if (nYear < 100)
+ {
+ TimeValue aTimeVal;
+ osl_getSystemTime(&aTimeVal);
+ oslDateTime aCurrDateTime;
+ osl_getDateTimeFromTimeValue(&aTimeVal,&aCurrDateTime);
+ sal_uInt16 nCurrentYear = aCurrDateTime.Year;
+// sal_uInt16 nCurrentYear = Date().GetYear();
+ sal_uInt16 nCurrentCentury = nCurrentYear / 100;
+ nCurrentYear %= 100;
+ if (nCurrentYear < 50)
+ if (nYear <= nCurrentYear)
+ nYear += nCurrentCentury * 100;
+ else if (nYear < nCurrentYear + 50)
+ nYear += nCurrentCentury * 100;
+ else
+ nYear += (nCurrentCentury - 1) * 100;
+ else
+ if (nYear >= nCurrentYear)
+ nYear += nCurrentCentury * 100;
+ else if (nYear >= nCurrentYear - 50)
+ nYear += nCurrentCentury * 100;
+ else
+ nYear += (nCurrentCentury + 1) * 100;
+ }
+
+ rDateTime.SetYear(nYear);
+}
+
+/*
+ * setPath.
+ */
+bool FTPDirectoryParser::setPath (
+ OUString &rPath, const char *value, sal_Int32 length)
+{
+ if (value)
+ {
+ if (length < 0)
+ length = rtl_str_getLength (value);
+ rPath = OUString (value, length, RTL_TEXTENCODING_UTF8);
+ }
+ return (!!value);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpdirp.hxx b/ucb/source/ucp/ftp/ftpdirp.hxx
new file mode 100644
index 000000000..ae9f36a47
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpdirp.hxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+
+
+namespace ftp {
+
+ /*========================================================================
+ *
+ * the DateTime structure
+ *
+ *======================================================================*/
+
+ struct DateTime
+ : public css::util::DateTime
+ {
+ DateTime() : css::util::DateTime(0, 0, 0, 0, 0, 0, 0, false) { }
+
+ void SetYear(sal_uInt16 year) { Year = year; }
+ void SetMonth(sal_uInt16 month) { Month = month; }
+ void SetDay(sal_uInt16 day) { Day = day; }
+ // Only zero allowed and used for time-argument
+ void SetTime() { Hours = 0; Minutes = 0; Seconds = 0; NanoSeconds = 0; }
+ void SetHour(sal_uInt16 hours) { Hours = hours; }
+ void SetMin(sal_uInt16 minutes) { Minutes = minutes; }
+ void SetSec(sal_uInt16 seconds) { Seconds = seconds; }
+ void SetNanoSec(sal_uInt32 nanoSec) { NanoSeconds = nanoSec; }
+
+ sal_uInt16 GetMonth() const { return Month; }
+ };
+
+
+/*========================================================================
+ *
+ * the directory information structure
+ *
+ *======================================================================*/
+
+ enum FTPDirentryMode { INETCOREFTP_FILEMODE_UNKNOWN = 0x00,
+ INETCOREFTP_FILEMODE_READ = 0x01,
+ INETCOREFTP_FILEMODE_WRITE = 0x02,
+ INETCOREFTP_FILEMODE_ISDIR = 0x04,
+ INETCOREFTP_FILEMODE_ISLINK = 0x08 };
+
+ struct FTPDirentry
+ {
+ OUString m_aURL;
+ OUString m_aName;
+ DateTime m_aDate;
+ sal_uInt32 m_nMode;
+ sal_uInt32 m_nSize;
+
+ FTPDirentry()
+ : m_aDate(),
+ m_nMode(INETCOREFTP_FILEMODE_UNKNOWN),
+ m_nSize(sal_uInt32(-1)) { }
+
+ void clear() {
+ m_aURL.clear();
+ m_aName.clear();
+ m_aDate = DateTime();
+ m_nMode = INETCOREFTP_FILEMODE_UNKNOWN;
+ m_nSize = sal_uInt32(-1);
+ }
+ };
+
+
+/*========================================================================
+ *
+ * the directory parser
+ *
+ *======================================================================*/
+
+
+ class FTPDirectoryParser
+ {
+ public:
+ static bool parseDOS (
+ FTPDirentry &rEntry,
+ const char *pBuffer );
+
+ static bool parseVMS (
+ FTPDirentry &rEntry,
+ const char *pBuffer );
+
+ static bool parseUNIX (
+ FTPDirentry &rEntry,
+ const char *pBuffer );
+
+
+ private:
+
+ static bool parseUNIX_isSizeField (
+ const char *pStart,
+ const char *pEnd,
+ sal_uInt32 &rSize);
+
+ static bool parseUNIX_isMonthField (
+ const char *pStart,
+ const char *pEnd,
+ DateTime& rDateTime);
+
+ static bool parseUNIX_isDayField (
+ const char *pStart,
+ const char *pEnd,
+ DateTime& rDateTime);
+
+ static bool parseUNIX_isYearTimeField (
+ const char *pStart,
+ const char *pEnd,
+ DateTime& rDateTime);
+
+ static bool parseUNIX_isTime (
+ const char *pStart,
+ const char *pEnd,
+ sal_uInt16 nHour,
+ DateTime& rDateTime);
+
+ static void setYear (
+ DateTime& rDateTime,
+ sal_uInt16 nYear);
+
+ static bool setPath (
+ OUString& rPath,
+ const char *value,
+ sal_Int32 length = -1);
+ };
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpdynresultset.cxx b/ucb/source/ucp/ftp/ftpdynresultset.cxx
new file mode 100644
index 000000000..8021f9dd4
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpdynresultset.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include "ftpdynresultset.hxx"
+#include "ftpresultsetfactory.hxx"
+
+using namespace com::sun::star::lang;
+using namespace com::sun::star::sdbc;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::uno;
+
+
+using namespace ftp;
+
+
+// DynamicResultSet Implementation.
+
+
+DynamicResultSet::DynamicResultSet(
+ const Reference< XComponentContext >& rxContext,
+ const OpenCommandArgument2& rCommand,
+ std::unique_ptr<ResultSetFactory> pFactory )
+ : ResultSetImplHelper( rxContext, rCommand ),
+ m_pFactory( std::move(pFactory) )
+{
+}
+
+DynamicResultSet::~DynamicResultSet()
+{
+}
+
+
+// Non-interface methods.
+
+
+void DynamicResultSet::initStatic()
+{
+ m_xResultSet1.set( m_pFactory->createResultSet() );
+}
+
+
+void DynamicResultSet::initDynamic()
+{
+ m_xResultSet1.set( m_pFactory->createResultSet() );
+
+ m_xResultSet2 = m_xResultSet1;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpdynresultset.hxx b/ucb/source/ucp/ftp/ftpdynresultset.hxx
new file mode 100644
index 000000000..76a070a6d
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpdynresultset.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <ucbhelper/resultsethelper.hxx>
+
+namespace ftp {
+
+ class ResultSetFactory;
+
+ class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper
+ {
+ std::unique_ptr<ResultSetFactory> m_pFactory;
+
+ private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+
+ public:
+ DynamicResultSet(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::ucb::OpenCommandArgument2& rCommand,
+ std::unique_ptr<ResultSetFactory> pFactory );
+
+ virtual ~DynamicResultSet() override;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpintreq.cxx b/ucb/source/ucp/ftp/ftpintreq.cxx
new file mode 100644
index 000000000..32c765a4e
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpintreq.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ftpintreq.hxx"
+
+#include <comphelper/interaction.hxx>
+
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+
+using namespace cppu;
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::task;
+using namespace ftp;
+
+XInteractionApproveImpl::XInteractionApproveImpl()
+ : m_bSelected(false)
+{
+}
+
+void SAL_CALL XInteractionApproveImpl::select() { m_bSelected = true; }
+
+// XInteractionDisapproveImpl
+
+XInteractionDisapproveImpl::XInteractionDisapproveImpl() {}
+
+void SAL_CALL XInteractionDisapproveImpl::select() {}
+
+// XInteractionRequestImpl
+
+XInteractionRequestImpl::XInteractionRequestImpl()
+ : p1(new XInteractionApproveImpl)
+{
+ std::vector<uno::Reference<task::XInteractionContinuation>> continuations{
+ Reference<XInteractionContinuation>(p1),
+ Reference<XInteractionContinuation>(new XInteractionDisapproveImpl)
+ };
+ UnsupportedNameClashException excep;
+ excep.NameClash = NameClash::ERROR;
+ m_xRequest.set(new ::comphelper::OInteractionRequest(Any(excep), std::move(continuations)));
+}
+
+bool XInteractionRequestImpl::approved() const { return p1->isSelected(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpintreq.hxx b/ucb/source/ucp/ftp/ftpintreq.hxx
new file mode 100644
index 000000000..79bf74ed7
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpintreq.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/task/XInteractionDisapprove.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <cppuhelper/implbase.hxx>
+
+
+namespace ftp {
+
+ class XInteractionApproveImpl : public cppu::WeakImplHelper <
+ css::task::XInteractionApprove >
+ {
+ public:
+
+ XInteractionApproveImpl();
+
+ virtual void SAL_CALL select() override;
+
+ bool isSelected() const { return m_bSelected;}
+
+ private:
+
+ bool m_bSelected;
+ };
+
+
+ class XInteractionDisapproveImpl : public cppu::WeakImplHelper <
+ css::task::XInteractionDisapprove >
+ {
+ public:
+
+ XInteractionDisapproveImpl();
+
+ virtual void SAL_CALL select() override;
+ };
+
+
+ class XInteractionRequestImpl
+ {
+ public:
+
+ XInteractionRequestImpl();
+
+ bool approved() const;
+
+ css::uno::Reference<css::task::XInteractionRequest> const& getRequest() const
+ {
+ return m_xRequest;
+ }
+
+ private:
+
+ XInteractionApproveImpl* p1;
+
+ css::uno::Reference<css::task::XInteractionRequest> m_xRequest;
+
+ XInteractionRequestImpl(const XInteractionRequestImpl&) = delete;
+ XInteractionRequestImpl& operator=(const XInteractionRequestImpl&) = delete;
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftploaderthread.cxx b/ucb/source/ucp/ftp/ftploaderthread.cxx
new file mode 100644
index 000000000..f5ebfe36c
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftploaderthread.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#include "ftploaderthread.hxx"
+#include "curl.hxx"
+
+using namespace ftp;
+
+
+/********************************************************************************/
+/* */
+/* cleanup function for thread specific data */
+/* */
+/********************************************************************************/
+
+extern "C" {
+
+ static int memory_write_dummy(void *,size_t,size_t,void *)
+ {
+ return 0;
+ }
+
+ static void delete_CURL(void *pData)
+ {
+ // Otherwise response for QUIT will be sent to already destroyed
+ // MemoryContainer via non-dummy memory_write function.
+ (void)curl_easy_setopt(static_cast<CURL*>(pData),
+ CURLOPT_HEADERFUNCTION,
+ memory_write_dummy);
+ curl_easy_cleanup(static_cast<CURL*>(pData));
+ }
+
+}
+
+/********************************************************************************/
+/* */
+/* Member part of FTPLoaderThread */
+/* */
+/********************************************************************************/
+
+
+FTPLoaderThread::FTPLoaderThread()
+ : m_threadKey(osl_createThreadKey(delete_CURL)) {
+}
+
+
+FTPLoaderThread::~FTPLoaderThread() {
+ osl_destroyThreadKey(m_threadKey);
+}
+
+
+CURL* FTPLoaderThread::handle() {
+ CURL* ret = static_cast<CURL*>(osl_getThreadKeyData(m_threadKey));
+ if(!ret) {
+ ret = curl_easy_init();
+ if (ret != nullptr) {
+ // Make sure curl is not internally using environment variables like
+ // "ftp_proxy":
+ if (curl_easy_setopt(ret, CURLOPT_PROXY, "") != CURLE_OK) {
+ curl_easy_cleanup(ret);
+ ret = nullptr;
+ }
+ }
+ osl_setThreadKeyData(m_threadKey, ret);
+ }
+
+ return ret;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftploaderthread.hxx b/ucb/source/ucp/ftp/ftploaderthread.hxx
new file mode 100644
index 000000000..79ba60d1d
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftploaderthread.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#pragma once
+
+#include <osl/thread.h>
+#include "curl.hxx"
+
+namespace ftp {
+
+ /** A loaderthread acts as factory for CURL-handles,
+ * the key being ( implicit ) the threadid.
+ * Owner is a FTPContentProvider-instance
+ */
+
+ class FTPLoaderThread
+ {
+ public:
+
+ FTPLoaderThread();
+ ~FTPLoaderThread();
+
+ CURL* handle();
+
+
+ private:
+ FTPLoaderThread(const FTPLoaderThread&) = delete;
+ FTPLoaderThread& operator=(const FTPLoaderThread&) = delete;
+
+ oslThreadKey m_threadKey;
+
+ }; // end class FTPLoaderThread
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpresultsetI.cxx b/ucb/source/ucp/ftp/ftpresultsetI.cxx
new file mode 100644
index 000000000..baef97284
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpresultsetI.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <ucbhelper/propertyvalueset.hxx>
+#include <rtl/ref.hxx>
+#include <com/sun/star/ucb/Command.hpp>
+#include "ftpresultsetI.hxx"
+#include "ftpcontent.hxx"
+
+
+using namespace ftp;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::sdbc;
+
+
+ResultSetI::ResultSetI(const Reference<XComponentContext>& rxContext,
+ const Reference<XContentProvider>& xProvider,
+ const Sequence<Property>& seqProp,
+ const std::vector<FTPDirentry>& dirvec)
+ : ResultSetBase(rxContext,xProvider,seqProp)
+{
+ for(const auto & i : dirvec)
+ m_aPath.push_back(i.m_aURL);
+
+ // m_aIdents holds the content identifiers
+
+ m_aItems.resize( m_aPath.size() );
+ m_aIdents.resize( m_aPath.size() );
+
+ for(size_t n = 0; n < m_aItems.size(); ++n) {
+ rtl::Reference<ucbhelper::PropertyValueSet> xRow =
+ new ucbhelper::PropertyValueSet(rxContext);
+
+ for( const auto& rProp : seqProp) {
+ const OUString& Name = rProp.Name;
+ if(Name == "ContentType")
+ xRow->appendString(rProp,
+ OUString( "application/ftp" ));
+ else if(Name == "Title")
+ xRow->appendString(rProp,dirvec[n].m_aName);
+ else if(Name == "IsReadOnly")
+ xRow->appendBoolean(rProp,
+ (dirvec[n].m_nMode &
+ INETCOREFTP_FILEMODE_WRITE) == INETCOREFTP_FILEMODE_WRITE);
+ else if(Name == "IsDocument")
+ xRow->appendBoolean(rProp,
+ (dirvec[n].m_nMode &
+ INETCOREFTP_FILEMODE_ISDIR) != INETCOREFTP_FILEMODE_ISDIR);
+ else if(Name == "IsFolder")
+ xRow->appendBoolean(rProp,
+ ( dirvec[n].m_nMode &
+ INETCOREFTP_FILEMODE_ISDIR) == INETCOREFTP_FILEMODE_ISDIR);
+ else if(Name == "Size")
+ xRow->appendLong(rProp,
+ dirvec[n].m_nSize);
+ else if(Name == "DateCreated")
+ xRow->appendTimestamp(rProp,
+ dirvec[n].m_aDate);
+ else if(Name == "CreatableContentsInfo")
+ xRow->appendObject(
+ rProp,
+ Any(FTPContent::queryCreatableContentsInfo_Static()));
+ else
+ xRow->appendVoid(rProp);
+ }
+ m_aItems[n].set(xRow);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpresultsetI.hxx b/ucb/source/ucp/ftp/ftpresultsetI.hxx
new file mode 100644
index 000000000..259469dd1
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpresultsetI.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/ucb/XContentProvider.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include "ftpresultsetbase.hxx"
+#include "ftpdirp.hxx"
+
+
+namespace ftp {
+
+ class ResultSetI
+ : public ResultSetBase
+ {
+ public:
+
+ ResultSetI(
+ const css::uno::Reference< css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference< css::ucb::XContentProvider>& xProvider,
+ const css::uno::Sequence< css::beans::Property >& seq,
+ const std::vector<FTPDirentry>& dirvec);
+
+ private:
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpresultsetbase.cxx b/ucb/source/ucp/ftp/ftpresultsetbase.cxx
new file mode 100644
index 000000000..815145918
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpresultsetbase.cxx
@@ -0,0 +1,509 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ucbhelper/contentidentifier.hxx>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <ucbhelper/resultsetmetadata.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include "ftpresultsetbase.hxx"
+
+using namespace ftp;
+using namespace com::sun::star;
+
+ResultSetBase::ResultSetBase(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< ucb::XContentProvider >& xProvider,
+ const uno::Sequence< beans::Property >& seq )
+ : m_xContext( rxContext ),
+ m_xProvider( xProvider ),
+ m_nRow( -1 ),
+ m_nWasNull( true ),
+ m_sProperty( seq )
+{
+}
+
+ResultSetBase::~ResultSetBase()
+{
+}
+
+
+// XInterface
+
+void SAL_CALL
+ResultSetBase::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void SAL_CALL
+ResultSetBase::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+
+uno::Any SAL_CALL
+ResultSetBase::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = cppu::queryInterface(
+ rType,
+ static_cast< lang::XComponent* >(this),
+ static_cast< sdbc::XRow* >(this),
+ static_cast< sdbc::XResultSet* >(this),
+ static_cast< sdbc::XResultSetMetaDataSupplier* >(this),
+ static_cast< beans::XPropertySet* >(this),
+ static_cast< ucb::XContentAccess* >(this) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+
+// XComponent
+
+
+void SAL_CALL
+ResultSetBase::addEventListener(
+ const uno::Reference< lang::XEventListener >& Listener )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( ! m_pDisposeEventListeners )
+ m_pDisposeEventListeners.reset(
+ new comphelper::OInterfaceContainerHelper3<lang::XEventListener>( m_aMutex ) );
+
+ m_pDisposeEventListeners->addInterface( Listener );
+}
+
+
+void SAL_CALL
+ResultSetBase::removeEventListener(
+ const uno::Reference< lang::XEventListener >& Listener )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pDisposeEventListeners )
+ m_pDisposeEventListeners->removeInterface( Listener );
+}
+
+
+void SAL_CALL
+ResultSetBase::dispose()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ lang::EventObject aEvt;
+ aEvt.Source = static_cast< lang::XComponent * >( this );
+
+ if ( m_pDisposeEventListeners && m_pDisposeEventListeners->getLength() )
+ {
+ m_pDisposeEventListeners->disposeAndClear( aEvt );
+ }
+ if( m_pRowCountListeners && m_pRowCountListeners->getLength() )
+ {
+ m_pRowCountListeners->disposeAndClear( aEvt );
+ }
+ if( m_pIsFinalListeners && m_pIsFinalListeners->getLength() )
+ {
+ m_pIsFinalListeners->disposeAndClear( aEvt );
+ }
+}
+
+
+// XResultSet
+
+sal_Bool SAL_CALL
+ResultSetBase::next()
+{
+ ++m_nRow;
+ return m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size());
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isBeforeFirst()
+{
+ return m_nRow == -1;
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isAfterLast()
+{
+ return m_nRow >= sal::static_int_cast<sal_Int32>(m_aItems.size()); // Cannot happen, if m_aFolder.isOpen()
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isFirst()
+{
+ return m_nRow == 0;
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::isLast()
+{
+ if( m_nRow == sal::static_int_cast<sal_Int32>(m_aItems.size()) - 1 )
+ return true;
+ else
+ return false;
+}
+
+
+void SAL_CALL
+ResultSetBase::beforeFirst()
+{
+ m_nRow = -1;
+}
+
+
+void SAL_CALL
+ResultSetBase::afterLast()
+{
+ m_nRow = m_aItems.size();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::first()
+{
+ m_nRow = -1;
+ return next();
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::last()
+{
+ m_nRow = m_aItems.size() - 1;
+ return true;
+}
+
+
+sal_Int32 SAL_CALL
+ResultSetBase::getRow()
+{
+ // Test, whether behind last row
+ if( -1 == m_nRow || m_nRow >= sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return 0;
+ else
+ return m_nRow+1;
+}
+
+
+sal_Bool SAL_CALL ResultSetBase::absolute( sal_Int32 row )
+{
+ if( row >= 0 )
+ m_nRow = row - 1;
+ else
+ {
+ last();
+ m_nRow += ( row + 1 );
+ if( m_nRow < -1 )
+ m_nRow = -1;
+ }
+
+ return 0<= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size());
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::relative( sal_Int32 row )
+{
+ if( isAfterLast() || isBeforeFirst() )
+ throw sdbc::SQLException();
+
+ if( row > 0 )
+ while( row-- )
+ next();
+ else if( row < 0 )
+ while( row++ && m_nRow > - 1 )
+ previous();
+
+ return 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size());
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::previous()
+{
+ if( m_nRow > sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ m_nRow = m_aItems.size(); // Correct Handling of afterLast
+ if( 0 <= m_nRow ) -- m_nRow;
+
+ return 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size());
+}
+
+
+void SAL_CALL
+ResultSetBase::refreshRow()
+{
+}
+
+
+sal_Bool SAL_CALL
+ResultSetBase::rowUpdated()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL
+ResultSetBase::rowInserted()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL
+ResultSetBase::rowDeleted()
+{
+ return false;
+}
+
+
+uno::Reference< uno::XInterface > SAL_CALL
+ResultSetBase::getStatement()
+{
+ return uno::Reference< uno::XInterface >();
+}
+
+
+// XCloseable
+
+void SAL_CALL
+ResultSetBase::close()
+{
+}
+
+
+OUString SAL_CALL
+ResultSetBase::queryContentIdentifierString()
+{
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aPath[m_nRow];
+ else
+ return OUString();
+}
+
+
+uno::Reference< ucb::XContentIdentifier > SAL_CALL
+ResultSetBase::queryContentIdentifier()
+{
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ {
+ if(!m_aIdents[m_nRow].is()) {
+ OUString url = queryContentIdentifierString();
+ if(!url.isEmpty() )
+ m_aIdents[m_nRow] =
+ uno::Reference< ucb::XContentIdentifier >(
+ new ::ucbhelper::ContentIdentifier(url) );
+ }
+ return m_aIdents[m_nRow];
+ }
+
+ return uno::Reference<ucb::XContentIdentifier>();
+}
+
+
+uno::Reference< ucb::XContent > SAL_CALL
+ResultSetBase::queryContent()
+{
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_xProvider->queryContent(queryContentIdentifier());
+ else
+ return uno::Reference< ucb::XContent >();
+}
+
+namespace {
+
+class XPropertySetInfoImpl
+ : public cppu::OWeakObject,
+ public beans::XPropertySetInfo
+{
+public:
+
+ explicit XPropertySetInfoImpl( const uno::Sequence< beans::Property >& aSeq )
+ : m_aSeq( aSeq )
+ {
+ }
+
+ void SAL_CALL acquire()
+ noexcept override
+ {
+ OWeakObject::acquire();
+ }
+
+
+ void SAL_CALL release()
+ noexcept override
+ {
+ OWeakObject::release();
+ }
+
+ uno::Any SAL_CALL queryInterface( const uno::Type& rType ) override
+ {
+ uno::Any aRet = cppu::queryInterface(
+ rType,
+ static_cast< beans::XPropertySetInfo* >(this) );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+ }
+
+ uno::Sequence< beans::Property > SAL_CALL getProperties() override
+ {
+ return m_aSeq;
+ }
+
+ beans::Property SAL_CALL getPropertyByName( const OUString& aName ) override
+ {
+ auto pProp = std::find_if(std::cbegin(m_aSeq), std::cend(m_aSeq),
+ [&aName](const beans::Property& rProp) { return aName == rProp.Name; });
+ if (pProp != std::cend(m_aSeq))
+ return *pProp;
+ throw beans::UnknownPropertyException(aName);
+ }
+
+ sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override
+ {
+ return std::any_of(std::cbegin(m_aSeq), std::cend(m_aSeq),
+ [&Name](const beans::Property& rProp) { return Name == rProp.Name; });
+ }
+
+private:
+
+ uno::Sequence< beans::Property > m_aSeq;
+};
+
+}
+
+// XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+ResultSetBase::getPropertySetInfo()
+{
+ uno::Sequence< beans::Property > seq
+ {
+ { "RowCount", -1, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY },
+ { "IsRowCountFinal", -1, cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::READONLY }
+ };
+
+ return uno::Reference< beans::XPropertySetInfo > (
+ new XPropertySetInfoImpl( seq ) );
+}
+
+
+void SAL_CALL ResultSetBase::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& /*aValue*/ )
+{
+ if( aPropertyName == "IsRowCountFinal" ||
+ aPropertyName == "RowCount" )
+ return;
+
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+
+uno::Any SAL_CALL ResultSetBase::getPropertyValue(
+ const OUString& PropertyName )
+{
+ if( PropertyName == "IsRowCountFinal" )
+ {
+ return uno::Any(true);
+ }
+ else if ( PropertyName == "RowCount" )
+ {
+ sal_Int32 count = m_aItems.size();
+ return uno::Any(count);
+ }
+ else
+ throw beans::UnknownPropertyException(PropertyName);
+}
+
+
+void SAL_CALL ResultSetBase::addPropertyChangeListener(
+ const OUString& aPropertyName,
+ const uno::Reference< beans::XPropertyChangeListener >& xListener )
+{
+ if( aPropertyName == "IsRowCountFinal" )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( ! m_pIsFinalListeners )
+ m_pIsFinalListeners.reset(
+ new comphelper::OInterfaceContainerHelper3<beans::XPropertyChangeListener>( m_aMutex ) );
+
+ m_pIsFinalListeners->addInterface( xListener );
+ }
+ else if ( aPropertyName == "RowCount" )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( ! m_pRowCountListeners )
+ m_pRowCountListeners.reset(
+ new comphelper::OInterfaceContainerHelper3<beans::XPropertyChangeListener>( m_aMutex ) );
+ m_pRowCountListeners->addInterface( xListener );
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+
+void SAL_CALL ResultSetBase::removePropertyChangeListener(
+ const OUString& aPropertyName,
+ const uno::Reference< beans::XPropertyChangeListener >& aListener )
+{
+ if( aPropertyName == "IsRowCountFinal" &&
+ m_pIsFinalListeners )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_pIsFinalListeners->removeInterface( aListener );
+ }
+ else if ( aPropertyName == "RowCount" &&
+ m_pRowCountListeners )
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+ m_pRowCountListeners->removeInterface( aListener );
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+
+void SAL_CALL ResultSetBase::addVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+
+void SAL_CALL ResultSetBase::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+
+// XResultSetMetaDataSupplier
+uno::Reference< sdbc::XResultSetMetaData > SAL_CALL
+ResultSetBase::getMetaData()
+{
+ return new ::ucbhelper::ResultSetMetaData( m_xContext, m_sProperty );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpresultsetbase.hxx b/ucb/source/ucp/ftp/ftpresultsetbase.hxx
new file mode 100644
index 000000000..cfb447f6e
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpresultsetbase.hxx
@@ -0,0 +1,410 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vector>
+#include <cppuhelper/weak.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/ucb/XContentProvider.hpp>
+#include <com/sun/star/ucb/XContentIdentifier.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/Property.hpp>
+
+
+namespace ftp {
+
+ class ResultSetBase
+ : public cppu::OWeakObject,
+ public css::lang::XComponent,
+ public css::sdbc::XRow,
+ public css::sdbc::XResultSet,
+ public css::sdbc::XCloseable,
+ public css::sdbc::XResultSetMetaDataSupplier,
+ public css::beans::XPropertySet,
+ public css::ucb::XContentAccess
+ {
+ public:
+
+ ResultSetBase(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::ucb::XContentProvider >& xProvider,
+ const css::uno::Sequence< css::beans::Property >& seq);
+
+ virtual ~ResultSetBase() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL
+ queryInterface( const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL
+ acquire()
+ noexcept override;
+
+ virtual void SAL_CALL
+ release()
+ noexcept override;
+
+ // XComponent
+ virtual void SAL_CALL
+ dispose() override;
+
+ virtual void SAL_CALL
+ addEventListener(
+ const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+
+ virtual void SAL_CALL
+ removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+
+ // XRow
+ virtual sal_Bool SAL_CALL
+ wasNull() override
+ {
+ if( 0<= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ m_nWasNull = m_aItems[m_nRow]->wasNull();
+ else
+ m_nWasNull = true;
+ return m_nWasNull;
+ }
+
+ virtual OUString SAL_CALL
+ getString( sal_Int32 columnIndex ) override
+ {
+ OUString ret;
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ ret = m_aItems[m_nRow]->getString( columnIndex );
+
+ return ret;
+ }
+
+ virtual sal_Bool SAL_CALL
+ getBoolean( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getBoolean( columnIndex );
+ else
+ return false;
+ }
+
+ virtual sal_Int8 SAL_CALL
+ getByte( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getByte( columnIndex );
+ else
+ return sal_Int8( 0 );
+ }
+
+ virtual sal_Int16 SAL_CALL
+ getShort( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getShort( columnIndex );
+ else
+ return sal_Int16( 0 );
+ }
+
+ virtual sal_Int32 SAL_CALL
+ getInt( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getInt( columnIndex );
+ else
+ return 0;
+ }
+
+ virtual sal_Int64 SAL_CALL
+ getLong( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getLong( columnIndex );
+ else
+ return sal_Int64( 0 );
+ }
+
+ virtual float SAL_CALL
+ getFloat( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getFloat( columnIndex );
+ else
+ return float( 0 );
+ }
+
+ virtual double SAL_CALL
+ getDouble( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getDouble( columnIndex );
+ else
+ return double( 0 );
+ }
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getBytes( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getBytes( columnIndex );
+ else
+ return css::uno::Sequence< sal_Int8 >();
+ }
+
+ virtual css::util::Date SAL_CALL
+ getDate( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getDate( columnIndex );
+ else
+ return css::util::Date();
+ }
+
+ virtual css::util::Time SAL_CALL
+ getTime( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getTime( columnIndex );
+ else
+ return css::util::Time();
+ }
+
+ virtual css::util::DateTime SAL_CALL
+ getTimestamp( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getTimestamp( columnIndex );
+ else
+ return css::util::DateTime();
+ }
+
+
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getBinaryStream( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getBinaryStream( columnIndex );
+ else
+ return css::uno::Reference< css::io::XInputStream >();
+ }
+
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getCharacterStream( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getCharacterStream( columnIndex );
+ else
+ return css::uno::Reference< css::io::XInputStream >();
+ }
+
+ virtual css::uno::Any SAL_CALL
+ getObject( sal_Int32 columnIndex,
+ const css::uno::Reference< css::container::XNameAccess >& typeMap ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getObject( columnIndex,typeMap );
+ else
+ return css::uno::Any();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XRef > SAL_CALL
+ getRef( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getRef( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XRef >();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XBlob > SAL_CALL
+ getBlob( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getBlob( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XBlob >();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XClob > SAL_CALL
+ getClob( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getClob( columnIndex );
+ else
+ return css::uno::Reference< css::sdbc::XClob >();
+ }
+
+ virtual css::uno::Reference< css::sdbc::XArray > SAL_CALL
+ getArray( sal_Int32 columnIndex ) override
+ {
+ if( 0 <= m_nRow && m_nRow < sal::static_int_cast<sal_Int32>(m_aItems.size()) )
+ return m_aItems[m_nRow]->getArray( columnIndex );
+ else
+ return css::uno::Reference<
+ css::sdbc::XArray >();
+ }
+
+
+ // XResultSet
+
+ virtual sal_Bool SAL_CALL
+ next() override;
+
+ virtual sal_Bool SAL_CALL
+ isBeforeFirst() override;
+
+ virtual sal_Bool SAL_CALL
+ isAfterLast() override;
+
+ virtual sal_Bool SAL_CALL
+ isFirst() override;
+
+ virtual sal_Bool SAL_CALL
+ isLast() override;
+
+ virtual void SAL_CALL
+ beforeFirst() override;
+
+ virtual void SAL_CALL
+ afterLast() override;
+
+ virtual sal_Bool SAL_CALL
+ first() override;
+
+ virtual sal_Bool SAL_CALL
+ last() override;
+
+ virtual sal_Int32 SAL_CALL
+ getRow() override;
+
+ virtual sal_Bool SAL_CALL
+ absolute(
+ sal_Int32 row ) override;
+
+ virtual sal_Bool SAL_CALL
+ relative(
+ sal_Int32 rows ) override;
+
+ virtual sal_Bool SAL_CALL
+ previous() override;
+
+ virtual void SAL_CALL
+ refreshRow() override;
+
+ virtual sal_Bool SAL_CALL
+ rowUpdated() override;
+
+ virtual sal_Bool SAL_CALL
+ rowInserted() override;
+
+ virtual sal_Bool SAL_CALL
+ rowDeleted() override;
+
+
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
+ getStatement() override;
+
+ // XCloseable
+
+ virtual void SAL_CALL
+ close() override;
+
+ // XContentAccess
+
+ virtual OUString SAL_CALL
+ queryContentIdentifierString() override;
+
+ virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL
+ queryContentIdentifier() override;
+
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ queryContent() override;
+
+ // XResultSetMetaDataSupplier
+ virtual css::uno::Reference< css::sdbc::XResultSetMetaData > SAL_CALL
+ getMetaData() override;
+
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& aPropertyName,
+ const css::uno::Any& aValue ) override;
+
+ virtual css::uno::Any SAL_CALL
+ getPropertyValue(
+ const OUString& PropertyName ) override;
+
+ virtual void SAL_CALL
+ addPropertyChangeListener(
+ const OUString& aPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+
+ virtual void SAL_CALL
+ removePropertyChangeListener(
+ const OUString& aPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+
+ virtual void SAL_CALL
+ addVetoableChangeListener(
+ const OUString& PropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& PropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ protected:
+
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xContext;
+ css::uno::Reference< css::ucb::XContentProvider >
+ m_xProvider;
+ sal_Int32 m_nRow;
+ bool m_nWasNull;
+
+ typedef std::vector< css::uno::Reference<css::ucb::XContentIdentifier > >
+ IdentSet;
+ typedef std::vector< css::uno::Reference< css::sdbc::XRow > >
+ ItemSet;
+
+ IdentSet m_aIdents;
+ ItemSet m_aItems;
+ std::vector<OUString> m_aPath;
+
+ css::uno::Sequence< css::beans::Property >
+ m_sProperty;
+
+ osl::Mutex m_aMutex;
+ std::unique_ptr<comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>> m_pDisposeEventListeners;
+ std::unique_ptr<comphelper::OInterfaceContainerHelper3<css::beans::XPropertyChangeListener>> m_pRowCountListeners;
+ std::unique_ptr<comphelper::OInterfaceContainerHelper3<css::beans::XPropertyChangeListener>> m_pIsFinalListeners;
+ };
+
+
+} // end namespace fileaccess
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpresultsetfactory.hxx b/ucb/source/ucp/ftp/ftpresultsetfactory.hxx
new file mode 100644
index 000000000..d521c570d
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpresultsetfactory.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+#pragma once
+
+#include "ftpresultsetbase.hxx"
+#include "ftpdirp.hxx"
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <rtl/ref.hxx>
+#include <vector>
+
+namespace ftp {
+
+class ResultSetBase;
+
+class ResultSetFactory
+{
+public:
+ ResultSetFactory(const css::uno::Reference<css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference<css::ucb::XContentProvider >& xProvider,
+ const css::uno::Sequence<css::beans::Property>& seq,
+ std::vector<FTPDirentry>&& dirvec);
+
+ rtl::Reference<ResultSetBase> createResultSet();
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::ucb::XContentProvider > m_xProvider;
+ css::uno::Sequence< css::beans::Property > m_seq;
+ std::vector<FTPDirentry> m_dirvec;
+};
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpurl.cxx b/ucb/source/ucp/ftp/ftpurl.cxx
new file mode 100644
index 000000000..13b7e839c
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpurl.cxx
@@ -0,0 +1,804 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <string.h>
+#include <rtl/uri.hxx>
+#include <o3tl/safeint.hxx>
+
+#include "ftpurl.hxx"
+#include "ftpcontentprovider.hxx"
+#include "ftpcfunc.hxx"
+#include "ftpcontainer.hxx"
+#include <memory>
+
+using namespace ftp;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::uno;
+
+namespace {
+
+OUString encodePathSegment(OUString const & decoded) {
+ return rtl::Uri::encode(
+ decoded, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+}
+
+OUString decodePathSegment(OUString const & encoded) {
+ return rtl::Uri::decode(
+ encoded, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
+}
+
+}
+
+MemoryContainer::MemoryContainer()
+ : m_nLen(0),
+ m_nWritePos(0),
+ m_pBuffer(nullptr)
+{
+}
+
+MemoryContainer::~MemoryContainer()
+{
+ std::free(m_pBuffer);
+}
+
+
+int MemoryContainer::append(
+ const void* pBuffer,
+ size_t size,
+ size_t nmemb
+) noexcept
+{
+ sal_uInt32 nLen = size*nmemb;
+ sal_uInt32 tmp(nLen + m_nWritePos);
+
+ if(m_nLen < tmp) { // enlarge in steps of multiples of 1K
+ do {
+ m_nLen+=1024;
+ } while(m_nLen < tmp);
+
+ if (auto p = std::realloc(m_pBuffer, m_nLen))
+ m_pBuffer = p;
+ else
+ return 0;
+ }
+
+ memcpy(static_cast<sal_Int8*>(m_pBuffer)+m_nWritePos,
+ pBuffer,nLen);
+ m_nWritePos = tmp;
+ return nLen;
+}
+
+
+extern "C" {
+
+ int memory_write(void *buffer,size_t size,size_t nmemb,void *stream)
+ {
+ MemoryContainer *_stream =
+ static_cast<MemoryContainer*>(stream);
+
+ if(!_stream)
+ return 0;
+
+ return _stream->append(buffer,size,nmemb);
+ }
+
+}
+
+
+FTPURL::FTPURL(const FTPURL& r)
+ : m_pFCP(r.m_pFCP),
+ m_aUsername(r.m_aUsername),
+ m_bShowPassword(r.m_bShowPassword),
+ m_aHost(r.m_aHost),
+ m_aPort(r.m_aPort),
+ m_aPathSegmentVec(r.m_aPathSegmentVec)
+
+{
+}
+
+
+FTPURL::FTPURL(const OUString& url,
+ FTPContentProvider* pFCP)
+ : m_pFCP(pFCP),
+ m_aUsername("anonymous"),
+ m_bShowPassword(false),
+ m_aPort("21")
+{
+ parse(url); // can reset m_bShowPassword
+}
+
+
+FTPURL::~FTPURL()
+{
+}
+
+
+void FTPURL::parse(const OUString& url)
+{
+ OUString aPassword, urlRest;
+
+ if(url.getLength() < 6 || !url.startsWithIgnoreAsciiCase("ftp://", &urlRest))
+ throw malformed_exception();
+
+ // determine "username:password@host:port"
+ OUString aExpr;
+ sal_Int32 nIdx = urlRest.indexOf('/');
+ if (nIdx == -1)
+ {
+ aExpr = urlRest;
+ urlRest = "";
+ }
+ else
+ {
+ aExpr = urlRest.copy(0, nIdx);
+ urlRest = urlRest.copy(nIdx + 1);
+ }
+
+ sal_Int32 l = aExpr.indexOf('@');
+ m_aHost = aExpr.copy(1+l);
+
+ if(l != -1) {
+ // Now username and password.
+ aExpr = aExpr.copy(0,l);
+ l = aExpr.indexOf(':');
+ if(l != -1) {
+ aPassword = aExpr.copy(1+l);
+ if(!aPassword.isEmpty())
+ m_bShowPassword = true;
+ }
+ if(l > 0)
+ // Overwritten only if the username is not empty.
+ m_aUsername = aExpr.copy(0,l);
+ else if(!aExpr.isEmpty())
+ m_aUsername = aExpr;
+ }
+
+ l = m_aHost.lastIndexOf(':');
+ sal_Int32 ipv6Back = m_aHost.lastIndexOf(']');
+ if((ipv6Back == -1 && l != -1) // not ipv6, but a port
+ ||
+ (ipv6Back != -1 && 1+ipv6Back == l) // ipv6, and a port
+ )
+ {
+ if(1+l<m_aHost.getLength())
+ m_aPort = m_aHost.copy(1+l);
+ m_aHost = m_aHost.copy(0,l);
+ }
+
+ // now determine the pathsegments ...
+ while(!urlRest.isEmpty())
+ {
+ nIdx = urlRest.indexOf('/');
+ OUString segment;
+ if(nIdx == -1)
+ {
+ segment = urlRest;
+ urlRest = "";
+ }
+ else
+ {
+ segment = urlRest.copy(0, nIdx);
+ urlRest = urlRest.copy(nIdx + 1);
+ }
+ if( segment == ".." && !m_aPathSegmentVec.empty() && m_aPathSegmentVec.back() != ".." )
+ m_aPathSegmentVec.pop_back();
+ else if( segment == "." )
+ ; // Ignore
+ else
+ // This is a legal name.
+ m_aPathSegmentVec.push_back( segment );
+ }
+
+ if(m_bShowPassword)
+ m_pFCP->setHost(m_aHost,
+ m_aPort,
+ m_aUsername,
+ aPassword,
+ ""/*aAccount*/);
+
+ // now check for something like ";type=i" at end of url
+ if(!m_aPathSegmentVec.empty())
+ {
+ l = m_aPathSegmentVec.back().indexOf(';');
+ if (l != -1)
+ {
+ m_aType = m_aPathSegmentVec.back().copy(l);
+ m_aPathSegmentVec.back() = m_aPathSegmentVec.back().copy(0,l);
+ }
+ }
+}
+
+
+OUString FTPURL::ident(bool withslash,bool internal) const
+{
+ // rebuild the url as one without ellipses,
+ // and more important, as one without username and
+ // password. ( These are set together with the command. )
+
+ OUStringBuffer bff;
+ bff.append("ftp://");
+
+ if( m_aUsername != "anonymous" ) {
+ bff.append(m_aUsername);
+
+ OUString aPassword,aAccount;
+ m_pFCP->forHost(m_aHost,
+ m_aPort,
+ m_aUsername,
+ aPassword,
+ aAccount);
+
+ if((m_bShowPassword || internal) &&
+ !aPassword.isEmpty() )
+ bff.append(':')
+ .append(aPassword);
+
+ bff.append('@');
+ }
+ bff.append(m_aHost);
+
+ if( m_aPort != "21" )
+ bff.append(':')
+ .append(m_aPort)
+ .append('/');
+ else
+ bff.append('/');
+
+ for(size_t i = 0; i < m_aPathSegmentVec.size(); ++i)
+ if(i == 0)
+ bff.append(m_aPathSegmentVec[i]);
+ else
+ bff.append('/').append(m_aPathSegmentVec[i]);
+ if(withslash)
+ if(!bff.isEmpty() && bff[bff.getLength()-1] != '/')
+ bff.append('/');
+
+ bff.append(m_aType);
+ return bff.makeStringAndClear();
+}
+
+
+OUString FTPURL::parent(bool internal) const
+{
+ OUStringBuffer bff;
+
+ bff.append("ftp://");
+
+ if( m_aUsername != "anonymous" ) {
+ bff.append(m_aUsername);
+
+ OUString aPassword,aAccount;
+ m_pFCP->forHost(m_aHost,
+ m_aPort,
+ m_aUsername,
+ aPassword,
+ aAccount);
+
+ if((internal || m_bShowPassword) && !aPassword.isEmpty())
+ bff.append(':')
+ .append(aPassword);
+
+ bff.append('@');
+ }
+
+ bff.append(m_aHost);
+
+ if( m_aPort != "21" )
+ bff.append(':')
+ .append(m_aPort)
+ .append('/');
+ else
+ bff.append('/');
+
+ OUString last;
+
+ for(size_t i = 0; i < m_aPathSegmentVec.size(); ++i)
+ if(1+i == m_aPathSegmentVec.size())
+ last = m_aPathSegmentVec[i];
+ else if(i == 0)
+ bff.append(m_aPathSegmentVec[i]);
+ else
+ bff.append('/').append(m_aPathSegmentVec[i]);
+
+ if(last.isEmpty())
+ bff.append("..");
+ else if ( last == ".." )
+ bff.append(last + "/..");
+
+ bff.append(m_aType);
+ return bff.makeStringAndClear();
+}
+
+
+void FTPURL::child(const OUString& title)
+{
+ m_aPathSegmentVec.push_back(encodePathSegment(title));
+}
+
+
+OUString FTPURL::child() const
+{
+ return
+ !m_aPathSegmentVec.empty() ?
+ decodePathSegment(m_aPathSegmentVec.back()) : OUString();
+}
+
+
+/** Listing of a directory.
+ */
+
+namespace ftp {
+
+ namespace {
+
+ enum OS {
+ FTP_DOS,FTP_UNIX,FTP_VMS,FTP_UNKNOWN
+ };
+
+ }
+
+}
+
+
+#define SET_CONTROL_CONTAINER \
+ MemoryContainer control; \
+ (void)curl_easy_setopt(curl, \
+ CURLOPT_HEADERFUNCTION, \
+ memory_write); \
+ (void)curl_easy_setopt(curl, \
+ CURLOPT_WRITEHEADER, \
+ &control)
+
+
+static void setCurlUrl(CURL* curl, OUString const & url)
+{
+ OString urlParAscii(url.getStr(),
+ url.getLength(),
+ RTL_TEXTENCODING_UTF8);
+ (void)curl_easy_setopt(curl,
+ CURLOPT_URL,
+ urlParAscii.getStr());
+};
+
+oslFileHandle FTPURL::open()
+{
+ if(m_aPathSegmentVec.empty())
+ throw curl_exception(CURLE_FTP_COULDNT_RETR_FILE);
+
+ CURL *curl = m_pFCP->handle();
+
+ SET_CONTROL_CONTAINER;
+ OUString url(ident(false,true));
+ setCurlUrl(curl, url);
+
+ oslFileHandle res( nullptr );
+ if ( osl_createTempFile( nullptr, &res, nullptr ) == osl_File_E_None )
+ {
+ (void)curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,file_write);
+ (void)curl_easy_setopt(curl,CURLOPT_WRITEDATA,res);
+
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,0);
+ CURLcode err = curl_easy_perform(curl);
+
+ if(err == CURLE_OK)
+ {
+ oslFileError rc = osl_setFilePos( res, osl_Pos_Absolut, 0 );
+ SAL_WARN_IF(rc != osl_File_E_None, "ucb.ucp.ftp",
+ "osl_setFilePos failed");
+ }
+ else {
+ osl_closeFile(res);
+ res = nullptr;
+ throw curl_exception(err);
+ }
+ }
+
+ return res;
+}
+
+
+std::vector<FTPDirentry> FTPURL::list(
+ sal_Int16 nMode
+) const
+{
+ CURL *curl = m_pFCP->handle();
+
+ SET_CONTROL_CONTAINER;
+ (void)curl_easy_setopt(curl,CURLOPT_NOBODY,false);
+ MemoryContainer data;
+ (void)curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,memory_write);
+ (void)curl_easy_setopt(curl,CURLOPT_WRITEDATA,&data);
+
+ OUString url(ident(true,true));
+ setCurlUrl(curl, url);
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,0);
+
+ CURLcode err = curl_easy_perform(curl);
+ if(err != CURLE_OK)
+ throw curl_exception(err);
+
+ // now evaluate the error messages
+
+ sal_uInt32 len = data.m_nWritePos;
+ char* fwd = static_cast<char*>(data.m_pBuffer);
+ char *p1, *p2;
+ p1 = p2 = fwd;
+
+ OS osKind(FTP_UNKNOWN);
+ std::vector<FTPDirentry> resvec;
+ FTPDirentry aDirEntry;
+ // ensure slash at the end
+ OUString viewurl(ident(true,false));
+
+ while(true) {
+ while(o3tl::make_unsigned(p2-fwd) < len && *p2 != '\n') ++p2;
+ if(o3tl::make_unsigned(p2-fwd) == len) break;
+
+ *p2 = 0;
+ switch(osKind) {
+ // While FTP knows the 'system'-command,
+ // which returns the operating system type,
+ // this is not usable here: There are Windows-server
+ // formatting the output like UNIX-ls command.
+ case FTP_DOS:
+ FTPDirectoryParser::parseDOS(aDirEntry,p1);
+ break;
+ case FTP_UNIX:
+ FTPDirectoryParser::parseUNIX(aDirEntry,p1);
+ break;
+ case FTP_VMS:
+ FTPDirectoryParser::parseVMS(aDirEntry,p1);
+ break;
+ default:
+ if(FTPDirectoryParser::parseUNIX(aDirEntry,p1))
+ osKind = FTP_UNIX;
+ else if(FTPDirectoryParser::parseDOS(aDirEntry,p1))
+ osKind = FTP_DOS;
+ else if(FTPDirectoryParser::parseVMS(aDirEntry,p1))
+ osKind = FTP_VMS;
+ }
+ aDirEntry.m_aName = aDirEntry.m_aName.trim();
+ if( osKind != int(FTP_UNKNOWN) && aDirEntry.m_aName != ".." && aDirEntry.m_aName != "." ) {
+ aDirEntry.m_aURL = viewurl + encodePathSegment(aDirEntry.m_aName);
+
+ bool isDir = (aDirEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) == INETCOREFTP_FILEMODE_ISDIR;
+ switch(nMode) {
+ case OpenMode::DOCUMENTS:
+ if(!isDir)
+ resvec.push_back(aDirEntry);
+ break;
+ case OpenMode::FOLDERS:
+ if(isDir)
+ resvec.push_back(aDirEntry);
+ break;
+ default:
+ resvec.push_back(aDirEntry);
+ };
+ }
+ aDirEntry.clear();
+ p1 = p2 + 1;
+ }
+
+ return resvec;
+}
+
+
+OUString FTPURL::net_title() const
+{
+ CURL *curl = m_pFCP->handle();
+
+ SET_CONTROL_CONTAINER;
+ (void)curl_easy_setopt(curl,CURLOPT_NOBODY,true); // no data => no transfer
+ struct curl_slist *slist = nullptr;
+ // post request
+ slist = curl_slist_append(slist,"PWD");
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);
+
+ bool try_more(true);
+ CURLcode err;
+ OUString aNetTitle;
+
+ while(true) {
+ OUString url(ident(false,true));
+
+ if(try_more && !url.endsWith("/"))
+ url += "/"; // add end-slash
+ else if(!try_more && url.endsWith("/"))
+ url = url.copy(0,url.getLength()-1); // remove end-slash
+
+ setCurlUrl(curl, url);
+ err = curl_easy_perform(curl);
+
+ if(err == CURLE_OK) { // get the title from the server
+ char* fwd = static_cast<char*>(control.m_pBuffer);
+ sal_uInt32 len = control.m_nWritePos;
+
+ aNetTitle = OUString(fwd,len,RTL_TEXTENCODING_UTF8);
+ // the buffer now contains the name of the file;
+ // analyze the output:
+ // Format of current working directory:
+ // 257 "/bla/bla" is current directory
+ sal_Int32 index1 = aNetTitle.lastIndexOf("257");
+ index1 = aNetTitle.indexOf('"', index1 + std::strlen("257")) + 1;
+ sal_Int32 index2 = aNetTitle.indexOf('"', index1);
+ aNetTitle = index2 > index1
+ ? aNetTitle.copy(index1, index2 - index1) : OUString();
+ if( aNetTitle != "/" ) {
+ index1 = aNetTitle.lastIndexOf('/');
+ aNetTitle = aNetTitle.copy(1+index1);
+ }
+ try_more = false;
+ } else if(err == CURLE_BAD_PASSWORD_ENTERED)
+ // the client should retry after getting the correct
+ // username + password
+ throw curl_exception(err);
+#if LIBCURL_VERSION_NUM>=0x070d01 /* 7.13.1 */
+ else if(err == CURLE_LOGIN_DENIED)
+ // the client should retry after getting the correct
+ // username + password
+ throw curl_exception(err);
+#endif
+ else if(try_more && err == CURLE_FTP_ACCESS_DENIED) {
+ // We were either denied access when trying to login to
+ // an FTP server or when trying to change working directory
+ // to the one given in the URL.
+ if(!m_aPathSegmentVec.empty())
+ // determine title from URL
+ aNetTitle = decodePathSegment(m_aPathSegmentVec.back());
+ else
+ // must be root
+ aNetTitle = "/";
+ try_more = false;
+ }
+
+ if(try_more)
+ try_more = false;
+ else
+ break;
+ }
+
+ curl_slist_free_all(slist);
+ return aNetTitle;
+}
+
+
+FTPDirentry FTPURL::direntry() const
+{
+ OUString nettitle = net_title();
+ FTPDirentry aDirentry;
+
+ aDirentry.m_aName = nettitle; // init aDirentry
+ if( nettitle == "/" || nettitle == ".." )
+ aDirentry.m_nMode = INETCOREFTP_FILEMODE_ISDIR;
+ else
+ aDirentry.m_nMode = INETCOREFTP_FILEMODE_UNKNOWN;
+
+ aDirentry.m_nSize = 0;
+
+ if( nettitle != "/" ) {
+ // try to open the parent directory
+ FTPURL aURL(parent(),m_pFCP);
+
+ std::vector<FTPDirentry> aList = aURL.list(OpenMode::ALL);
+
+ for(const FTPDirentry & d : aList) {
+ if(d.m_aName == nettitle) { // the relevant file is found
+ aDirentry = d;
+ break;
+ }
+ }
+ }
+ return aDirentry;
+}
+
+
+extern "C" {
+
+ static size_t memory_read(void *ptr,size_t size,size_t nmemb,void *stream)
+ {
+ sal_Int32 nRequested = sal_Int32(size*nmemb);
+ CurlInput *curlInput = static_cast<CurlInput*>(stream);
+ if(curlInput)
+ return size_t(curlInput->read(static_cast<sal_Int8*>(ptr),nRequested));
+ else
+ return 0;
+ }
+
+}
+
+
+void FTPURL::insert(bool replaceExisting,void* stream) const
+{
+ if(!replaceExisting) {
+// FTPDirentry aDirentry(direntry());
+// if(aDirentry.m_nMode == INETCOREFTP_FILEMODE_UNKNOWN)
+ // throw curl_exception(FILE_EXIST_DURING_INSERT);
+ throw curl_exception(FILE_MIGHT_EXIST_DURING_INSERT);
+ } // else
+ // overwrite is default in libcurl
+
+ CURL *curl = m_pFCP->handle();
+
+ SET_CONTROL_CONTAINER;
+ (void)curl_easy_setopt(curl,CURLOPT_NOBODY,false); // no data => no transfer
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,0);
+ (void)curl_easy_setopt(curl,CURLOPT_QUOTE,0);
+ (void)curl_easy_setopt(curl,CURLOPT_READFUNCTION,memory_read);
+ (void)curl_easy_setopt(curl,CURLOPT_READDATA,stream);
+ (void)curl_easy_setopt(curl, CURLOPT_UPLOAD,1);
+
+ OUString url(ident(false,true));
+ setCurlUrl(curl, url);
+
+ CURLcode err = curl_easy_perform(curl);
+ (void)curl_easy_setopt(curl, CURLOPT_UPLOAD,false);
+
+ if(err != CURLE_OK)
+ throw curl_exception(err);
+}
+
+
+void FTPURL::mkdir(bool ReplaceExisting) const
+{
+ OString title;
+ if(!m_aPathSegmentVec.empty()) {
+ OUString titleOU = m_aPathSegmentVec.back();
+ titleOU = decodePathSegment(titleOU);
+ title = OString(titleOU.getStr(),
+ titleOU.getLength(),
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ // will give an error
+ title = OString("/");
+
+ OString aDel = "del " + title;
+ OString mkd = "mkd " + title;
+
+ struct curl_slist *slist = nullptr;
+
+ FTPDirentry aDirentry(direntry());
+ if(!ReplaceExisting) {
+// if(aDirentry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN)
+// throw curl_exception(FOLDER_EXIST_DURING_INSERT);
+ throw curl_exception(FOLDER_MIGHT_EXIST_DURING_INSERT);
+ } else if(aDirentry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN)
+ slist = curl_slist_append(slist,aDel.getStr());
+
+ slist = curl_slist_append(slist,mkd.getStr());
+
+ CURL *curl = m_pFCP->handle();
+ SET_CONTROL_CONTAINER;
+ (void)curl_easy_setopt(curl,CURLOPT_NOBODY,true); // no data => no transfer
+ (void)curl_easy_setopt(curl,CURLOPT_QUOTE,0);
+
+ // post request
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);
+
+ OUString url(parent(true));
+ if(!url.endsWith("/"))
+ url += "/";
+ setCurlUrl(curl, url);
+
+ CURLcode err = curl_easy_perform(curl);
+ curl_slist_free_all(slist);
+ if(err != CURLE_OK)
+ throw curl_exception(err);
+}
+
+
+OUString FTPURL::ren(const OUString& NewTitle)
+{
+ CURL *curl = m_pFCP->handle();
+
+ // post request
+ OUString OldTitle = net_title();
+ OString renamefrom = "RNFR " +
+ OString(OldTitle.getStr(),
+ OldTitle.getLength(),
+ RTL_TEXTENCODING_UTF8);
+
+ OString renameto = "RNTO " +
+ OString(NewTitle.getStr(),
+ NewTitle.getLength(),
+ RTL_TEXTENCODING_UTF8);
+
+ struct curl_slist *slist = nullptr;
+ slist = curl_slist_append(slist,renamefrom.getStr());
+ slist = curl_slist_append(slist,renameto.getStr());
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);
+
+ SET_CONTROL_CONTAINER;
+ (void)curl_easy_setopt(curl,CURLOPT_NOBODY,true); // no data => no transfer
+ (void)curl_easy_setopt(curl,CURLOPT_QUOTE,0);
+
+ OUString url(parent(true));
+ if(!url.endsWith("/"))
+ url += "/";
+ setCurlUrl(curl, url);
+
+ CURLcode err = curl_easy_perform(curl);
+ curl_slist_free_all(slist);
+ if(err != CURLE_OK)
+ throw curl_exception(err);
+ else if( !m_aPathSegmentVec.empty() && m_aPathSegmentVec.back() != ".." )
+ m_aPathSegmentVec.back() = encodePathSegment(NewTitle);
+ return OldTitle;
+}
+
+
+void FTPURL::del() const
+{
+ FTPDirentry aDirentry(direntry());
+
+ OString dele(aDirentry.m_aName.getStr(),
+ aDirentry.m_aName.getLength(),
+ RTL_TEXTENCODING_UTF8);
+
+ if(aDirentry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) {
+ std::vector<FTPDirentry> vec = list(sal_Int16(OpenMode::ALL));
+ for(const FTPDirentry & i : vec)
+ {
+ try {
+ FTPURL url(i.m_aURL,m_pFCP);
+ url.del();
+ } catch(const curl_exception&) {
+ }
+ }
+ dele = "RMD " + dele;
+ }
+ else if(aDirentry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN)
+ dele = "DELE " + dele;
+ else
+ return;
+
+ // post request
+ CURL *curl = m_pFCP->handle();
+ struct curl_slist *slist = nullptr;
+ slist = curl_slist_append(slist,dele.getStr());
+ (void)curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);
+
+ SET_CONTROL_CONTAINER;
+ (void)curl_easy_setopt(curl,CURLOPT_NOBODY,true); // no data => no transfer
+ (void)curl_easy_setopt(curl,CURLOPT_QUOTE,0);
+
+ OUString url(parent(true));
+ if(!url.endsWith("/"))
+ url += "/";
+ setCurlUrl(curl, url);
+
+ CURLcode err = curl_easy_perform(curl);
+ curl_slist_free_all(slist);
+ if(err != CURLE_OK)
+ throw curl_exception(err);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ftpurl.hxx b/ucb/source/ucp/ftp/ftpurl.hxx
new file mode 100644
index 000000000..b64eb6ecf
--- /dev/null
+++ b/ucb/source/ucp/ftp/ftpurl.hxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ *************************************************************************/
+
+#pragma once
+
+#include "curl.hxx"
+
+#include <rtl/ustring.hxx>
+#include <osl/file.h>
+#include <vector>
+
+#include "ftpdirp.hxx"
+
+namespace ftp {
+
+ /** Forward declarations.
+ */
+
+ class FTPContentProvider;
+
+
+ enum FTPErrors { FOLDER_MIGHT_EXIST_DURING_INSERT = CURL_LAST,
+ FILE_MIGHT_EXIST_DURING_INSERT };
+
+ class malformed_exception : public std::exception { };
+
+ class curl_exception : public std::exception
+ {
+ public:
+
+ explicit curl_exception(sal_Int32 err)
+ : n_err(err) { }
+
+ sal_Int32 code() const { return n_err; }
+
+
+ private:
+
+ sal_Int32 n_err;
+ };
+
+ class CurlInput {
+ public:
+ // returns the number of bytes actually read
+ virtual sal_Int32 read(sal_Int8 *dest,sal_Int32 nBytesRequested) = 0;
+
+ protected:
+ ~CurlInput() {}
+ };
+
+
+ class FTPURL
+ {
+ public:
+ /// @throws malformed_exception
+ FTPURL(
+ const OUString& aIdent,
+ FTPContentProvider* pFCP
+ );
+
+ FTPURL(const FTPURL& r);
+
+ ~FTPURL();
+
+ const OUString& host() const { return m_aHost; }
+
+ const OUString& port() const { return m_aPort; }
+
+ const OUString& username() const { return m_aUsername; }
+
+ /** This returns the URL, but cleaned from
+ * unnecessary ellipses.
+ */
+
+ OUString ident(bool withslash,bool internal) const;
+
+ /** returns the parent url.
+ */
+
+ OUString parent(bool internal = false) const;
+
+ /** sets the unencoded title */
+ void child(const OUString& title);
+
+ /** returns the unencoded title */
+ OUString child() const;
+
+ /// @throws curl_exception
+ std::vector<FTPDirentry> list(sal_Int16 nMode) const;
+
+ // returns a pointer to an open tempfile,
+ // sought to the beginning of.
+ /// @throws curl_exception
+ oslFileHandle open();
+
+ /// @throws curl_exception
+ /// @throws malformed_exception
+ FTPDirentry direntry() const;
+
+ /// @throws curl_exception
+ void insert(bool ReplaceExisting,void* stream) const;
+
+ /// @throws curl_exception
+ /// @throws malformed_exception
+ void mkdir(bool ReplaceExisting) const;
+
+ /// @throws curl_exception
+ OUString ren(const OUString& NewTitle);
+
+ /// @throws curl_exception
+ /// @throws malformed_exception
+ void del() const;
+
+
+ private:
+
+ FTPContentProvider *m_pFCP;
+
+ mutable OUString m_aUsername;
+ bool m_bShowPassword;
+ mutable OUString m_aHost;
+ mutable OUString m_aPort;
+ mutable OUString m_aType;
+
+ /** Contains the encoded pathsegments of the url.
+ */
+ std::vector<OUString> m_aPathSegmentVec;
+
+ /// @throws malformed_exception
+ void parse(const OUString& url);
+
+ /// @throws curl_exception
+ OUString net_title() const;
+ };
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/ftp/ucpftp1.component b/ucb/source/ucp/ftp/ucpftp1.component
new file mode 100644
index 000000000..e71a0837d
--- /dev/null
+++ b/ucb/source/ucp/ftp/ucpftp1.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.FTPContentProvider"
+ constructor="ucb_ftp_FTPContentProvider_get_implementation" single-instance="true">
+ <service name="com.sun.star.ucb.FTPContentProvider"/>
+ </implementation>
+</component>