    /* -*- 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 "ftpcfunc.hxx"
    #include "ftpintreq.hxx"

    #include <memory>
    #include <vector>
    #include <string.h>
    #include "curl.hxx"
    #include <curl/easy.h>
    #include <comphelper/propertysequence.hxx>
    #include <o3tl/make_unique.hxx>
    #include <ucbhelper/cancelcommandexecution.hxx>
    #include <ucbhelper/contentidentifier.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/beans/PropertyValue.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/io/XActiveDataStreamer.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/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/NameClashException.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()
        throw()
    {
        OWeakObject::acquire();
    }

    void SAL_CALL FTPContent::release()
        throw()
    {
        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 OUString( "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 OUString(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,
                      const std::vector<FTPDirentry>& dirvec)
            : m_xContext(rxContext),
              m_xProvider(xProvider),
              m_seq(seq),
              m_dirvec(dirvec)
    {
    }

    ResultSetBase* ResultSetFactory::createResultSet()
    {
        return new ResultSetI(m_xContext,
                              m_xProvider,
                              m_seq,
                              m_dirvec);
    }

    // XCommandProcessor methods.

    enum ACTION { NOACTION,
                  THROWAUTHENTICATIONREQUEST,
                  THROWACCESSDENIED,
                  THROWINTERACTIVECONNECT,
                  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.get());

                        Reference<XInterface> xSelection(
                            p->getSelection().get());

                        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 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()));
                            Sequence<sal_Int8> byte_seq(4096);
                            sal_Int32 n = 1000; // value does not matter here
                            for (;;) {
                                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&) {

                                }
                            }
                            if(n) {
                                Sequence<Any> seq(1);
                                PropertyValue value;
                                value.Name = "Uri";
                                value.Handle = -1;
                                value.Value <<= m_aFTPURL.ident(false,false);
                                value.State = PropertyState_DIRECT_VALUE;
                                seq[0] <<= value;
                                ucbhelper::cancelCommandExecution(
                                    IOErrorCode_UNKNOWN,
                                    seq,
                                    Environment);
                            }
                        }
                        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,
                                o3tl::make_unique<ResultSetFactory>(m_xContext,
                                                     m_xProvider.get(),
                                                     aOpenCommand.Properties,
                                                     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(
                            makeAny(
                                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_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;
            }
        }
    }

    #define FTP_FILE "application/vnd.sun.staroffice.ftp-file"

    #define FTP_FOLDER "application/vnd.sun.staroffice.ftp-folder"

    Sequence<ContentInfo > SAL_CALL
    FTPContent::queryCreatableContentsInfo(  )
    {
        return queryCreatableContentsInfo_Static();
    }

    // static
    Sequence<ContentInfo >
    FTPContent::queryCreatableContentsInfo_Static(  )
    {
        Sequence< ContentInfo > seq(2);

        seq[0].Type = FTP_FILE;
        seq[0].Attributes = ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
            | ContentInfoAttribute::KIND_DOCUMENT;
        Sequence< Property > props( 1 );
        props[0] = Property(
            "Title",
            -1,
            cppu::UnoType<OUString>::get(),
            PropertyAttribute::MAYBEVOID
            | PropertyAttribute::BOUND );
        seq[0].Properties = props;

        // folder
        seq[1].Type       = FTP_FOLDER;
        seq[1].Attributes = ContentInfoAttribute::KIND_FOLDER;
        seq[1].Properties = props;

        return seq;
    }

    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();
    }

    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.realloc(1);
            excep.Properties[0] = "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;
                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(sal_Int32 i = 0; i < seqProp.getLength(); ++i) {
            const OUString& Name = seqProp[i].Name;
            if(Name == "Title")
                xRow->appendString(seqProp[i],aDirEntry.m_aName);
            else if(Name == "CreatableContentsInfo")
                xRow->appendObject(seqProp[i],
                                   makeAny(queryCreatableContentsInfo()));
            else if(aDirEntry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN) {
                if(Name == "ContentType")
                    xRow->appendString(seqProp[i],
                                       (aDirEntry.m_nMode & INETCOREFTP_FILEMODE_ISDIR)
                                       ? OUString(FTP_FOLDER)
                                       : OUString(FTP_FILE) );
                else if(Name == "IsReadOnly")
                    xRow->appendBoolean(seqProp[i],
                                        (aDirEntry.m_nMode
                                         & INETCOREFTP_FILEMODE_WRITE) == 0 );
                else if(Name == "IsDocument")
                    xRow->appendBoolean(seqProp[i],
                                        (aDirEntry.m_nMode &
                                                   INETCOREFTP_FILEMODE_ISDIR) != INETCOREFTP_FILEMODE_ISDIR);
                else if(Name == "IsFolder")
                    xRow->appendBoolean(seqProp[i],
                                        (aDirEntry.m_nMode &
                                                 INETCOREFTP_FILEMODE_ISDIR) == INETCOREFTP_FILEMODE_ISDIR);
                else if(Name == "Size")
                    xRow->appendLong(seqProp[i],
                                     aDirEntry.m_nSize);
                else if(Name == "DateCreated")
                    xRow->appendTimestamp(seqProp[i],
                                          aDirEntry.m_aDate);
                else
                    xRow->appendVoid(seqProp[i]);
            } else
                xRow->appendVoid(seqProp[i]);
        }

        return Reference<XRow>(xRow.get());
    }

    Sequence<Any> FTPContent::setPropertyValues(
        const Sequence<PropertyValue>& seqPropVal)
    {
        Sequence<Any> ret(seqPropVal.getLength());
        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)) {
                    ret[i] <<= IllegalTypeException();
                    continue;
                } else if(Title.isEmpty()) {
                    ret[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.realloc(1);
                        evt[0].PropertyName = "Title";
                        evt[0].Further = false;
                        evt[0].PropertyHandle = -1;
                        evt[0].OldValue <<= OldTitle;
                        evt[0].NewValue <<= 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;
                        ret[i] <<= excep;
                    }
            } else {
                Sequence<Property> props =
                    getProperties(Reference<XCommandEnvironment>(nullptr));

                // either unknown or read-only
                ret[i] <<= UnknownPropertyException();
                for(sal_Int32 j = 0; j < props.getLength(); ++j)
                    if(props[j].Name == seqPropVal[i].Name) {
                        ret[i] <<= IllegalAccessException(
                            "Property is read-only!",
                                //props[j].Attributes & PropertyAttribute::READONLY
                                //    ? "Property is read-only!"
                                //    : "Access denied!"),
                            static_cast< cppu::OWeakObject * >( this ));
                        break;
                    }
            }
        }

        if(evt.getLength()) {
            // title has changed
            notifyPropertiesChange(evt);
            (void)exchange(new FTPContentIdentifier(m_aFTPURL.ident(false,false)));
        }

        return ret;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression 'n' is always false.