/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"

/**************************************************************************
                                TODO
 **************************************************************************

 *************************************************************************/
#include <com/sun/star/beans/PropertyAttribute.hpp>

#include "ftpdynresultset.hxx"
#include "ftpresultsetfactory.hxx"
#include "ftpresultsetI.hxx"
#include "ftpcontent.hxx"
#include "ftpcontentprovider.hxx"
#include "ftpinpstr.hxx"
#include "ftpdirp.hxx"
#include "ftpcontentidentifier.hxx"
#include "ftpcfunc.hxx"
#include "ftpstrcont.hxx"
#include "ftpintreq.hxx"

#include <memory>
#include <vector>
#include <rtl/memory.h>
#include "curl.hxx"
#include <curl/easy.h>
#include <ucbhelper/cancelcommandexecution.hxx>
#include <ucbhelper/contentidentifier.hxx>
#include <ucbhelper/propertyvalueset.hxx>
#include <ucbhelper/contentidentifier.hxx>
#include <ucbhelper/cancelcommandexecution.hxx>
#include <ucbhelper/simpleauthenticationrequest.hxx>
#include <com/sun/star/lang/IllegalAccessException.hpp>
#include <com/sun/star/ucb/ContentInfoAttribute.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/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/NameClash.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< XMultiServiceFactory >& rxSMgr,
                        FTPContentProvider* pProvider,
                        const Reference< XContentIdentifier >& Identifier,
                        const FTPURL& aFTPURL)
    : ContentImplHelper(rxSMgr,pProvider,Identifier),
      m_pFCP(pProvider),
      m_aFTPURL(aFTPURL),
      m_bInserted(false),
      m_bTitleSet(false)
{
}



FTPContent::FTPContent( const Reference< XMultiServiceFactory >& rxSMgr,
                        FTPContentProvider* pProvider,
                        const Reference< XContentIdentifier >& Identifier,
                        const ContentInfo& Info)
    : ContentImplHelper(rxSMgr,pProvider,Identifier),
      m_pFCP(pProvider),
      m_aFTPURL(Identifier->getContentIdentifier(),
                pProvider),
      m_bInserted(true),
      m_bTitleSet(false),
      m_aInfo(Info)
{
}



//=========================================================================

FTPContent::~FTPContent()
{
}


//=========================================================================
//
// XInterface methods.
//
//=========================================================================

XINTERFACE_IMPL_6( FTPContent,
                   XTypeProvider,
                   XServiceInfo,
                   XContent,
                   XCommandProcessor,
                   XContentCreator,
                   XChild);

//=========================================================================
//
// XTypeProvider methods.
//
//=========================================================================

XTYPEPROVIDER_IMPL_6( FTPContent,
                          XTypeProvider,
                          XServiceInfo,
                          XContent,
                      XCommandProcessor,
                      XContentCreator,
                      XChild);

//=========================================================================
//
// XServiceInfo methods.
//
//=========================================================================

// needed, because the service shall not be creatable!!
#undef XSERVICEINFO_CREATE_INSTANCE_IMPL
#define XSERVICEINFO_CREATE_INSTANCE_IMPL( Class )

XSERVICEINFO_IMPL_1( FTPContent,
                     rtl::OUString::createFromAscii(
                         "com.sun.star.comp.FTPContent"),
                     rtl::OUString::createFromAscii(
                         "com.sun.star.ucb.FTPContent"));



//=========================================================================
//
// XContent methods.
//
//=========================================================================

// virtual
rtl::OUString SAL_CALL FTPContent::getContentType()
    throw( RuntimeException )
{
    return rtl::OUString::createFromAscii(FTP_CONTENT_TYPE);
}


//=========================================================================
//
// XCommandProcessor methods.
//
//=========================================================================


//virtual
void SAL_CALL FTPContent::abort( sal_Int32 /*CommandId*/ )
    throw( RuntimeException )
{
}



/***************************************************************************/
/*                                                                         */
/*                     Internal implementation class.                      */
/*                                                                         */
/***************************************************************************/


class ResultSetFactoryI
    : public ResultSetFactory
{
public:

    ResultSetFactoryI(const Reference<XMultiServiceFactory >&  xSMgr,
                      const Reference<XContentProvider >&  xProvider,
                      sal_Int32 nOpenMode,
                      const Sequence<Property>& seq,
                      const Sequence<NumberedSortingInfo>& seqSort,
                      const std::vector<FTPDirentry>& dirvec)
        : m_xSMgr(xSMgr),
          m_xProvider(xProvider),
          m_nOpenMode(nOpenMode),
          m_seq(seq),
          m_seqSort(seqSort),
          m_dirvec(dirvec)
    {
    }

    virtual ResultSetBase* createResultSet()
    {
        return new ResultSetI(m_xSMgr,
                              m_xProvider,
                              m_nOpenMode,
                              m_seq,
                              m_seqSort,
                              m_dirvec);
    }

public:

    Reference< XMultiServiceFactory >               m_xSMgr;
    Reference< XContentProvider >                   m_xProvider;
    sal_Int32                                       m_nOpenMode;
    Sequence< Property >                            m_seq;
    Sequence< NumberedSortingInfo >                 m_seqSort;
    std::vector<FTPDirentry>                        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
)
    throw(
        Exception,
        CommandAbortedException,
        RuntimeException
    )
{
    ACTION action(NOACTION);
    Any aRet;

    while(true)
        try {
            if(action == THROWAUTHENTICATIONREQUEST) {
                // try to get a continuation first
                rtl::OUString aRealm,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,
                        aRealm,
                        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.compareToAscii(
//                  "getPropertyValues") == 0 &&
//                 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>(0));
                break;

            case THROWACCESSDENIED:
                {
                    Sequence<Any> seq(1);
                    PropertyValue value;
                    value.Name = rtl::OUString::createFromAscii("Uri");
                    value.Handle = -1;
                    value.Value <<= m_aFTPURL.ident(false,false);
                    value.State = PropertyState_DIRECT_VALUE;
                    seq[0] <<= value;
                    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(1);
                    PropertyValue value;
                    value.Name = rtl::OUString::createFromAscii("Uri");
                    value.Handle = -1;
                    value.Value <<= m_aFTPURL.ident(false,false);
                    value.State = PropertyState_DIRECT_VALUE;
                    seq[0] <<= value;
                    ucbhelper::cancelCommandExecution(
                        IOErrorCode_NO_FILE,
                        seq,
                        Environment);
                    break;
                }
            case THROWQUOTE:
            case THROWGENERAL:
                ucbhelper::cancelCommandExecution(
                    IOErrorCode_GENERAL,
                    Sequence<Any>(0),
                    Environment);
                break;
            }

            if(aCommand.Name.compareToAscii("getPropertyValues") == 0) {
                Sequence<Property> Properties;
                if(!(aCommand.Argument >>= Properties))
                {
                    aRet <<= IllegalArgumentException(
                                rtl::OUString::createFromAscii(
                                    "Wrong argument type!" ),
                                static_cast< cppu::OWeakObject * >(this),
                                -1);
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }

                aRet <<= getPropertyValues(Properties,Environment);
            }
            else if(aCommand.Name.compareToAscii("setPropertyValues") == 0)
            {
                Sequence<PropertyValue> propertyValues;

                if( ! ( aCommand.Argument >>= propertyValues ) ) {
                    aRet <<= IllegalArgumentException(
                                rtl::OUString::createFromAscii(
                                    "Wrong argument type!" ),
                                static_cast< cppu::OWeakObject * >(this),
                                -1);
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }

                aRet <<= setPropertyValues(propertyValues);
            }
            else if(aCommand.Name.compareToAscii("getCommandInfo") == 0) {
                // Note: Implemented by base class.
                aRet <<= getCommandInfo(Environment);
            }
            else if(aCommand.Name.compareToAscii("getPropertySetInfo") == 0) {
                // Note: Implemented by base class.
                aRet <<= getPropertySetInfo(Environment);
            }
            else if(aCommand.Name.compareToAscii( "insert" ) == 0)
            {
                InsertCommandArgument aInsertArgument;
                if ( ! ( aCommand.Argument >>= aInsertArgument ) ) {
                    aRet <<= IllegalArgumentException(
                                rtl::OUString::createFromAscii(
                                    "Wrong argument type!" ),
                                static_cast< cppu::OWeakObject * >(this),
                                -1);
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }
                insert(aInsertArgument,Environment);
            }
            else if(aCommand.Name.compareToAscii("delete") == 0) {
                m_aFTPURL.del();
                deleted();
            }
            else if(aCommand.Name.compareToAscii( "open" ) == 0) {
                OpenCommandArgument2 aOpenCommand;
                if ( !( aCommand.Argument >>= aOpenCommand ) ) {
                    aRet <<= IllegalArgumentException(
                                rtl::OUString::createFromAscii(
                                    "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 FTPInputStream(m_aFTPURL.open()));
                    }
                    else if(xOutputStream.is()) {
                        Reference<XInputStream> xStream(
                            new FTPInputStream(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 =
                                rtl::OUString::createFromAscii("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(
                            rtl::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_xSMgr,
                            this,
                            aOpenCommand,
                            Environment,
                            new ResultSetFactoryI(m_xSMgr,
                                                  m_xProvider.get(),
                                                  aOpenCommand.Mode,
                                                  aOpenCommand.Properties,
                                                  aOpenCommand.SortingInfo,
                                                  resvec));
                    aRet <<= xSet;
                }
                else if(aOpenCommand.Mode ==
                        OpenMode::DOCUMENT_SHARE_DENY_NONE ||
                        aOpenCommand.Mode ==
                        OpenMode::DOCUMENT_SHARE_DENY_WRITE) {
                    // Unsupported OpenMode
                    aRet <<= UnsupportedOpenModeException(
                        rtl::OUString(),
                        static_cast< cppu::OWeakObject * >(this),
                        static_cast< sal_Int16 >(aOpenCommand.Mode));
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }
                else {
                    aRet <<= IllegalArgumentException(
                                rtl::OUString::createFromAscii(
                                    "Unexpected OpenMode!" ),
                                static_cast< cppu::OWeakObject * >(this),
                                -1);

                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }
            } else if(aCommand.Name.compareToAscii("createNewContent") == 0) {
                ContentInfo aArg;
                if (!(aCommand.Argument >>= aArg)) {
                    ucbhelper::cancelCommandExecution(
                        makeAny(
                            IllegalArgumentException(
                                rtl::OUString::createFromAscii(
                                    "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 rtl::OUString::createFromAscii(     \
                                   "application/"    \
                                   "vnd.sun.staroffice.ftp-file")

#define FTP_FOLDER rtl::OUString::createFromAscii(     \
                                   "application/"    \
                                   "vnd.sun.staroffice.ftp-folder")

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

// static
Sequence<ContentInfo >
FTPContent::queryCreatableContentsInfo_Static(  )
    throw (RuntimeException)
{
    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(
        rtl::OUString::createFromAscii( "Title" ),
        -1,
        getCppuType( static_cast< rtl::OUString* >( 0 ) ),
        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 )
    throw (RuntimeException)
{
    if(Info.Type.equalsAscii("application/"
                             "vnd.sun.staroffice.ftp-file") ||
       Info.Type.equalsAscii("application/"
                             "vnd.sun.staroffice.ftp-folder"))
        return new FTPContent(m_xSMgr,
                              m_pFCP,
                              m_xIdentifier,Info);
    else
        return Reference<XContent>(0);
}




Reference<XInterface > SAL_CALL
FTPContent::getParent(  )
    throw (RuntimeException)
{
    Reference<XContentIdentifier>
        xIdent(new FTPContentIdentifier(m_aFTPURL.parent(false)));
    Reference<XContent> xContent(m_xProvider->queryContent(xIdent));
    return Reference<XInterface>(xContent,UNO_QUERY);
}


void SAL_CALL
FTPContent::setParent(const Reference<XInterface >& /*Parent*/ )
    throw (NoSupportException,
           RuntimeException)
{
    throw NoSupportException();
}



rtl::OUString FTPContent::getParentURL()
{
    return m_aFTPURL.parent();
}


class InsertData
    : public CurlInput {

public:

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

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);
        rtl_copyMemory(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] = rtl::OUString::createFromAscii("Title");
        Any aAny; aAny <<= excep;
        ucbhelper::cancelCommandExecution(aAny,Env);
    }

    if(m_bInserted &&
       m_aInfo.Type == FTP_FILE &&
       !aInsertCommand.Data.is())
    {
        MissingInputStreamException excep;
        Any aAny; aAny <<= excep;
        ucbhelper::cancelCommandExecution(aAny,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() == FILE_EXIST_DURING_INSERT ||
           e.code() == FOLDER_EXIST_DURING_INSERT) {
            // Deprecated, not used anymore:
            NameClashException excep;
            excep.Name = m_aFTPURL.child();
            Any aAny;
            aAny <<= excep;
            ucbhelper::cancelCommandExecution(aAny,Env);
        } else 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()) {
                Any aAny;
                aAny <<= excep;
                ucbhelper::cancelCommandExecution(aAny,Env);
            }

            XInteractionRequestImpl* p =
                new XInteractionRequestImpl(m_aFTPURL.child());
            Reference<XInteractionRequest> req(p);
            xInt->handle(req);
            if(p->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,
    const Reference<XCommandEnvironment>& /*environment*/
)
{
    rtl::Reference<ucbhelper::PropertyValueSet> xRow =
        new ucbhelper::PropertyValueSet(m_xSMgr);

    FTPDirentry aDirEntry = m_aFTPURL.direntry();

    for(sal_Int32 i = 0; i < seqProp.getLength(); ++i) {
        const rtl::OUString& Name = seqProp[i].Name;
        if(Name.compareToAscii("Title") == 0)
            xRow->appendString(seqProp[i],aDirEntry.m_aName);
        else if(Name.compareToAscii("CreatableContentsInfo") == 0)
            xRow->appendObject(seqProp[i],
                               makeAny(queryCreatableContentsInfo()));
        else if(aDirEntry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN) {
            if(Name.compareToAscii("ContentType") == 0)
                xRow->appendString(seqProp[i],
                                   aDirEntry.m_nMode&INETCOREFTP_FILEMODE_ISDIR
                                   ? FTP_FOLDER
                                   : FTP_FILE );
            else if(Name.compareToAscii("IsReadOnly") == 0)
                xRow->appendBoolean(seqProp[i],
                                    aDirEntry.m_nMode
                                    & INETCOREFTP_FILEMODE_WRITE
                                    ? 0
                                    : 1 );
            else if(Name.compareToAscii("IsDocument") == 0)
                xRow->appendBoolean(seqProp[i],
                                    ! sal_Bool(aDirEntry.m_nMode &
                                               INETCOREFTP_FILEMODE_ISDIR));
            else if(Name.compareToAscii("IsFolder") == 0)
                xRow->appendBoolean(seqProp[i],
                                    sal_Bool(aDirEntry.m_nMode &
                                             INETCOREFTP_FILEMODE_ISDIR));
            else if(Name.compareToAscii("Size") == 0)
                xRow->appendLong(seqProp[i],
                                 aDirEntry.m_nSize);
            else if(Name.compareToAscii("DateCreated") == 0)
                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.equalsAscii("Title")) {
            rtl::OUString Title;
            if(!(seqPropVal[i].Value >>= Title)) {
                ret[i] <<= IllegalTypeException();
                continue;
            } else if(!Title.getLength()) {
                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 {
                    rtl::OUString OldTitle = m_aFTPURL.ren(Title);
                    evt.realloc(1);
                    evt[0].PropertyName =
                        rtl::OUString::createFromAscii("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>(0));

            // 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(
                        rtl::OUString::createFromAscii(
                            "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);
        exchange(new FTPContentIdentifier(m_aFTPURL.ident(false,false)));
    }

    return ret;
}
