/* -*- 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.