/*************************************************************************
 *
 *  $RCSfile: ftpcontent.cxx,v $
 *
 *  $Revision: 1.23 $
 *
 *  last change: $Author: obo $ $Date: 2005-01-27 11:07:47 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

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

 *************************************************************************/

#ifndef _COM_SUN_STAR_BEANS_PROPERTYATTRIBUTE_HPP_
#include <com/sun/star/beans/PropertyAttribute.hpp>
#endif

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

#include <memory>
#include <vector>
#include <rtl/memory.h>
#include <curl/curl.h>
#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)
    : ContentImplHelper(rxSMgr,pProvider,Identifier),
      m_pFCP(pProvider),
      m_aFTPURL(Identifier->getContentIdentifier(),
                pProvider),
      m_bInserted(false),
      m_bTitleSet(false)
{
}


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


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


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



/***************************************************************************/
/*                                                                         */
/*                           Interne Implklasse                            */
/*                                                                         */
/***************************************************************************/


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

#include "debughelper.hxx"

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.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
//                  vos::ORef<ucb::PropertyValueSet> xRow =
//                      new ucb::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.getBodyPtr());
//                  return aRet;
//              }

            if(action == THROWAUTHENTICATIONREQUEST) {
                ucbhelper::cancelCommandExecution(
                    aRet,
                    Reference<XCommandEnvironment>(0));
            } else if(action == 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);
            } else if(action == THROWINTERACTIVECONNECT) {
                InteractiveNetworkConnectException
                    excep;
                excep.Server = m_aFTPURL.host();
                aRet <<= excep;
                ucbhelper::cancelCommandExecution(
                    aRet,
                    Environment);
            } else if(action == THROWRESOLVENAME) {
                InteractiveNetworkResolveNameException
                    excep;
                excep.Server = m_aFTPURL.host();
                aRet <<= excep;
                ucbhelper::cancelCommandExecution(
                    aRet,
                    Environment);
            } else if(action == 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);
            } else if(action == THROWQUOTE ||
                      action == THROWGENERAL) {
                ucbhelper::cancelCommandExecution(
                    IOErrorCode_GENERAL,
                    Sequence<Any>(0),
                    Environment);
            }


            if(aCommand.Name.compareToAscii("getPropertyValues") == 0) {
                Sequence<Property> Properties;
                if(!(aCommand.Argument >>= Properties))
                {
                    aRet <<= IllegalArgumentException();
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }

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

                if( ! ( aCommand.Argument >>= propertyValues ) ) {
                    aRet <<= IllegalArgumentException();
                    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();
                    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();
                    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()) {
#ifndef ABI_IS_ON_VACATION_AETSCH
                        FTPDownloadThread* p =
                            new FTPDownloadThread(
                                m_xIdentifier->getContentIdentifier(),
                                m_pFCP);
                        if(!p->Get(xActiveDataSink)) {
                            curl_exception e = p->GetException();
                            delete p;
                            throw e;
                        }
#else
                        xActiveDataSink->setInputStream(
                            new FTPInputStream(m_aFTPURL.open()));
#endif
                    }
                    else if(xOutputStream.is()) {
                        Reference<XInputStream> xStream(
                            new FTPInputStream(m_aFTPURL.open()));
                        Sequence<sal_Int8> seq(4096);
                        sal_Int32 n = 1000; // value does not matter here
                        while(n = xStream->readBytes(seq,4096))
                            try {
                                if(seq.getLength() != n)
                                    seq.realloc(n);
                                xOutputStream->writeBytes(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();
                        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.getBodyPtr(),
                                                  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();
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }
                else {
                    // IllegalArgumentException:: No OpenMode
                    aRet <<= IllegalArgumentException();
                    ucbhelper::cancelCommandExecution(aRet,Environment);
                }
            }
            else {
                aRet <<= UnsupportedCommandException();
                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_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 course of the error
                action = THROWGENERAL;
        }

    return aRet;
}

#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)
{
    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) { }

    // 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
)
{
    vos::ORef<ucb::PropertyValueSet> xRow =
        new ucb::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(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.getBodyPtr());
}



Sequence<Any> FTPContent::setPropertyValues(
    const Sequence<PropertyValue>& seqPropVal)
{
    Sequence<Property> props =
        getProperties(Reference<XCommandEnvironment>(0));

    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 {
            // either not unknown or illegal
            ret[i] <<= UnknownPropertyException();
            for(sal_Int32 j = 0; j < props.getLength(); ++j)
                if(props[j].Name == seqPropVal[i].Name) {
                    ret[i] <<= IllegalAccessException();
                    break;
                }
        }
    }

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

    return ret;
}
