/*************************************************************************
 *
 *  $RCSfile: ftpurl.cxx,v $
 *
 *  $Revision: 1.19 $
 *
 *  last change: $Author: obo $ $Date: 2005-01-27 11:08:18 $
 *
 *  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
 **************************************************************************

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

#include <memory>
#include <rtl/ustrbuf.hxx>
#include <com/sun/star/ucb/OpenMode.hpp>
#include <string.h>
#include <rtl/uri.hxx>

#include "ftpstrcont.hxx"
#include "ftpurl.hxx"
#include "ftphandleprovider.hxx"
#include "ftpinpstr.hxx"
#include "ftpcfunc.hxx"
#include "debughelper.hxx"
#include "ftpcontainer.hxx"

using namespace ftp;
using namespace com::sun::star::ucb;
using namespace com::sun::star::uno;
using namespace com::sun::star::io;

namespace {

rtl::OUString encodePathSegment(rtl::OUString const & decoded) {
    return rtl::Uri::encode(
        decoded, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
        RTL_TEXTENCODING_UTF8);
}

rtl::OUString decodePathSegment(rtl::OUString const & encoded) {
    return rtl::Uri::decode(
        encoded, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
}

}

MemoryContainer::MemoryContainer()
    : m_nLen(0),
      m_nWritePos(0),
      m_pBuffer(0)
{
}

MemoryContainer::~MemoryContainer()
{
    rtl_freeMemory(m_pBuffer);
}


int MemoryContainer::append(
    const void* pBuffer,
    size_t size,
    size_t nmemb
) throw()
{
    sal_uInt32 nLen = size*nmemb;
    sal_uInt32 tmp(nLen + m_nWritePos);

    if(m_nLen < tmp) { // enlarge in steps of multiples of 1K
        do {
            m_nLen+=1024;
        } while(m_nLen < tmp);

        m_pBuffer = rtl_reallocateMemory(m_pBuffer,m_nLen);
    }

    rtl_copyMemory(static_cast<sal_Int8*>(m_pBuffer)+m_nWritePos,
                   pBuffer,nLen);
    m_nWritePos = tmp;
    return nLen;
}


extern "C" {

    int memory_write(void *buffer,size_t size,size_t nmemb,void *stream)
    {
        MemoryContainer *_stream =
            reinterpret_cast<MemoryContainer*>(stream);

        if(!_stream)
            return 0;

        return _stream->append(buffer,size,nmemb);
    }

}


FTPURL::FTPURL(const FTPURL& r)
    : m_pFCP(r.m_pFCP),
      m_aUsername(r.m_aUsername),
      m_bShowPassword(r.m_bShowPassword),
      m_aHost(r.m_aHost),
      m_aPort(r.m_aPort),
      m_aPathSegmentVec(r.m_aPathSegmentVec),
      m_mutex()
{
}


FTPURL::FTPURL(const rtl::OUString& url,
               FTPHandleProvider* pFCP)
    throw(
        malformed_exception
    )
    : m_aUsername(rtl::OUString::createFromAscii("anonymous")),
      m_aPort(rtl::OUString::createFromAscii("21")),
      m_pFCP(pFCP),
      m_bShowPassword(false)
{
    parse(url);  // can reset m_bShowPassword
}


FTPURL::~FTPURL()
{
}


void FTPURL::parse(const rtl::OUString& url)
    throw(
        malformed_exception
    )
{
    rtl::OUString aPassword,aAccount;
    rtl::OString aIdent(url.getStr(),
                        url.getLength(),
                        RTL_TEXTENCODING_UTF8);
    char *buffer = new char[1+aIdent.getLength()];

    const char* p2 = aIdent.getStr();

    rtl::OString lower = aIdent.toAsciiLowerCase();
    if(lower.getLength() >= 6 &&
       strncmp("ftp://",lower.getStr(),6))
        throw malformed_exception();

    p2 += 6;

    char ch;
    char *p1 = buffer;  // determine "username:password@host:port"
    while((ch = *p2++) != '/' && ch)
        *p1++ = ch;
    *p1 = 0;

    rtl::OUString aExpr(rtl::OUString(buffer,strlen(buffer),
                                      RTL_TEXTENCODING_UTF8));

    sal_Int32 l = aExpr.indexOf(sal_Unicode('@'));
    m_aHost = aExpr.copy(1+l);

    if(l != -1) {
        // Now username and password.
        aExpr = aExpr.copy(0,l);
        l = aExpr.indexOf(sal_Unicode(':'));
        if(l != -1) {
            aPassword = aExpr.copy(1+l);
            if(aPassword.getLength())
                m_bShowPassword = true;
        }
        if(l > 0)
            // Overwritte only if the username is not empty.
            m_aUsername = aExpr.copy(0,l);
        else if(aExpr.getLength())
            m_aUsername = aExpr;
    }

    l = m_aHost.lastIndexOf(sal_Unicode(':'));
    sal_Int32 ipv6Back = m_aHost.lastIndexOf(sal_Unicode(']'));
    if((ipv6Back == -1 && l != -1) // not ipv6, but a port
       ||
       (ipv6Back != -1 && 1+ipv6Back == l) // ipv6, and a port
    )
    {
        if(1+l<m_aHost.getLength())
            m_aPort = m_aHost.copy(1+l);
        m_aHost = m_aHost.copy(0,l);
    }

    while(ch) {  // now determine the pathsegments ...
        p1 = buffer;
        while((ch = *p2++) != '/' && ch)
            *p1++ = ch;
        *p1 = 0;

        if(buffer[0]) {
            if(strcmp(buffer,"..") == 0 &&
               m_aPathSegmentVec.size() &&
               ! m_aPathSegmentVec.back().equalsAscii(".."))
                m_aPathSegmentVec.pop_back();
            else if(strcmp(buffer,".") == 0)
                ; // Ignore
            else
                // This is a legal name.
                m_aPathSegmentVec.push_back(
                    rtl::OUString(buffer,
                                  strlen(buffer),
                                  RTL_TEXTENCODING_UTF8));
        }
    }

    delete[] buffer;

    if(m_bShowPassword)
        m_pFCP->setHost(m_aHost,
                        m_aPort,
                        m_aUsername,
                        aPassword,
                        aAccount);

    // now check for something like ";type=i" at end of url
    if(m_aPathSegmentVec.size() &&
       (l = m_aPathSegmentVec.back().indexOf(sal_Unicode(';'))) != -1) {
        m_aType = m_aPathSegmentVec.back().copy(l);
        m_aPathSegmentVec.back() = m_aPathSegmentVec.back().copy(0,l);
    }
}


rtl::OUString FTPURL::ident(bool withslash,bool internal) const
{
    // rebuild the url as one without ellipses,
    // and more important, as one without username and
    // password. ( These are set together with the command. )

    rtl::OUStringBuffer bff;
    bff.appendAscii("ftp://");

    if(!m_aUsername.equalsAscii("anonymous")) {
        bff.append(m_aUsername);

        rtl::OUString aPassword,aAccount;
        m_pFCP->forHost(m_aHost,
                        m_aPort,
                        m_aUsername,
                        aPassword,
                        aAccount);

        if((m_bShowPassword || internal) &&
           aPassword.getLength() )
            bff.append(sal_Unicode(':'))
                .append(aPassword);

        bff.append(sal_Unicode('@'));
    }
    bff.append(m_aHost);

    if(!m_aPort.equalsAscii("21"))
        bff.append(sal_Unicode(':'))
            .append(m_aPort)
            .append(sal_Unicode('/'));
    else
        bff.append(sal_Unicode('/'));

    for(unsigned i = 0; i < m_aPathSegmentVec.size(); ++i)
        if(i == 0)
            bff.append(m_aPathSegmentVec[i]);
        else
            bff.append(sal_Unicode('/')).append(m_aPathSegmentVec[i]);
    if(withslash)
        if(bff.getLength() && bff[bff.getLength()-1] != sal_Unicode('/'))
            bff.append(sal_Unicode('/'));

    bff.append(m_aType);
    return bff.makeStringAndClear();
}


rtl::OUString FTPURL::parent(bool internal) const
{
    rtl::OUStringBuffer bff;

    bff.appendAscii("ftp://");

    if(!m_aUsername.equalsAscii("anonymous")) {
        bff.append(m_aUsername);

        rtl::OUString aPassword,aAccount;
        m_pFCP->forHost(m_aHost,
                        m_aPort,
                        m_aUsername,
                        aPassword,
                        aAccount);

        if((internal || m_bShowPassword) && aPassword.getLength())
            bff.append(sal_Unicode(':'))
                .append(aPassword);

        bff.append(sal_Unicode('@'));
    }

    bff.append(m_aHost);

    if(!m_aPort.equalsAscii("21"))
        bff.append(sal_Unicode(':'))
            .append(m_aPort)
            .append(sal_Unicode('/'));
    else
        bff.append(sal_Unicode('/'));

    rtl::OUString last;

    for(unsigned int i = 0; i < m_aPathSegmentVec.size(); ++i)
        if(1+i == m_aPathSegmentVec.size())
            last = m_aPathSegmentVec[i];
        else if(i == 0)
            bff.append(m_aPathSegmentVec[i]);
        else
            bff.append(sal_Unicode('/')).append(m_aPathSegmentVec[i]);

    if(!last.getLength())
        bff.appendAscii("..");
    else if(last.equalsAscii(".."))
        bff.append(last).appendAscii("/..");

    bff.append(m_aType);
    return bff.makeStringAndClear();
}


void FTPURL::child(const rtl::OUString& title)
{
    m_aPathSegmentVec.push_back(encodePathSegment(title));
}


rtl::OUString FTPURL::child() const
{
    return
        m_aPathSegmentVec.size() ?
        decodePathSegment(m_aPathSegmentVec.back()) : rtl::OUString();
}



/** Listing of a directory.
 */

namespace ftp {

    enum OS {
        FTP_DOS,FTP_UNIX,FTP_VMS,FTP_UNKNOWN
    };

}


#define SET_CONTROL_CONTAINER \
    MemoryContainer control;                                      \
    curl_easy_setopt(curl,                                        \
                     CURLOPT_HEADERFUNCTION,                      \
                     memory_write);                               \
    curl_easy_setopt(curl,                                        \
                     CURLOPT_WRITEHEADER,                         \
                     &control)


#define SET_DATA_CONTAINER                                        \
       curl_easy_setopt(curl,CURLOPT_NOBODY,false);                  \
    MemoryContainer data;                                         \
    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,memory_write);    \
    curl_easy_setopt(curl,CURLOPT_WRITEDATA,&data)

#define SET_URL(url)                                              \
    rtl::OString urlParAscii(url.getStr(),                        \
                             url.getLength(),                     \
                             RTL_TEXTENCODING_UTF8);              \
    curl_easy_setopt(curl,                                        \
                     CURLOPT_URL,                                 \
                     urlParAscii.getStr());

        // Setting username:password
#define SET_USER_PASSWORD(username,password)                      \
   rtl::OUString combi(username  +                                \
                       rtl::OUString::createFromAscii(":") +      \
                       password);                                 \
   rtl::OString aUserPsswd(combi.getStr(),                        \
                           combi.getLength(),                     \
                           RTL_TEXTENCODING_UTF8);                \
   curl_easy_setopt(curl,                                         \
                    CURLOPT_USERPWD,                              \
                    aUserPsswd.getStr())



FILE* FTPURL::open()
    throw(curl_exception)
{
    if(!m_aPathSegmentVec.size())
        throw curl_exception(CURLE_FTP_COULDNT_RETR_FILE);

    CURL *curl = m_pFCP->handle();

    SET_CONTROL_CONTAINER;
    rtl::OUString url(ident(false,true));
    SET_URL(url);
    FILE *res = tmpfile();
    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,file_write);
    curl_easy_setopt(curl,CURLOPT_WRITEDATA,res);

    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,0);
    CURLcode err = curl_easy_perform(curl);

    if(err == CURLE_OK)
        rewind(res);
    else {
        fclose(res),res = 0;
        throw curl_exception(err);
    }

    return res;
}


std::vector<FTPDirentry> FTPURL::list(
    sal_Int16 nMode
) const
    throw(
        curl_exception
    )
{
    CURL *curl = m_pFCP->handle();

    SET_CONTROL_CONTAINER;
    SET_DATA_CONTAINER;
    rtl::OUString url(ident(true,true));
    SET_URL(url);
    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,0);

    CURLcode err = curl_easy_perform(curl);
    if(err != CURLE_OK)
        throw curl_exception(err);

    // now evaluate the error messages

    sal_uInt32 len = data.m_nWritePos;
    char* fwd = (char*) data.m_pBuffer;
    rtl::OString str(fwd,len);
    char *p1, *p2;
    p1 = p2 = fwd;

    OS osKind(FTP_UNKNOWN);
    std::vector<FTPDirentry> resvec;
    FTPDirentry aDirEntry;
    // ensure slash at the end
    rtl::OUString viewurl(ident(true,false));

    while(true) {
        while(p2-fwd < int(len) && *p2 != '\n') ++p2;
        if(p2-fwd == int(len)) break;

        *p2 = 0;
        switch(osKind) {
            // While FTP knows the 'system'-command,
            // which returns the operating system type,
            // this is not usable here: There are Windows-server
            // formatting the output like UNIX-ls command.
        case FTP_DOS:
            FTPDirectoryParser::parseDOS(aDirEntry,p1);
            break;
        case FTP_UNIX:
            FTPDirectoryParser::parseUNIX(aDirEntry,p1);
            break;
        case FTP_VMS:
            FTPDirectoryParser::parseVMS(aDirEntry,p1);
            break;
        default:
            if(FTPDirectoryParser::parseUNIX(aDirEntry,p1))
                osKind = FTP_UNIX;
            else if(FTPDirectoryParser::parseDOS(aDirEntry,p1))
                osKind = FTP_DOS;
            else if(FTPDirectoryParser::parseVMS(aDirEntry,p1))
                osKind = FTP_VMS;
        }
        aDirEntry.m_aName = aDirEntry.m_aName.trim();
        if(osKind != int(FTP_UNKNOWN) &&
           !aDirEntry.m_aName.equalsAscii("..") &&
           !aDirEntry.m_aName.equalsAscii(".")) {
            aDirEntry.m_aURL = viewurl + encodePathSegment(aDirEntry.m_aName);

            sal_Bool isDir =
                sal_Bool(aDirEntry.m_nMode&INETCOREFTP_FILEMODE_ISDIR);
            switch(nMode) {
                case OpenMode::DOCUMENTS:
                    if(!isDir)
                        resvec.push_back(aDirEntry);
                    break;
                case OpenMode::FOLDERS:
                    if(isDir)
                        resvec.push_back(aDirEntry);
                    break;
                default:
                    resvec.push_back(aDirEntry);
            };
        }
        aDirEntry.clear();
        p1 = p2 + 1;
    }

    return resvec;
}


rtl::OUString FTPURL::net_title() const
    throw(curl_exception)
{
    CURL *curl = m_pFCP->handle();

    SET_CONTROL_CONTAINER;
    curl_easy_setopt(curl,CURLOPT_NOBODY,true);       // no data => no transfer
    struct curl_slist *slist = 0;
    // post request
    slist = curl_slist_append(slist,"PWD");
    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);

    bool try_more(true);
    CURLcode err;
    rtl::OUString net_title;

    while(true) {
        rtl::OUString url(ident(false,true));

        if(try_more &&
           1+url.lastIndexOf(sal_Unicode('/')) != url.getLength())
            url += rtl::OUString::createFromAscii("/");  // add end-slash
        else if(!try_more &&
                1+url.lastIndexOf(sal_Unicode('/')) == url.getLength())
            url = url.copy(0,url.getLength()-1);         // remove end-slash

        SET_URL(url);
        err = curl_easy_perform(curl);

        if(err == CURLE_OK) {       // get the title from the server
            char* fwd = (char*) control.m_pBuffer;
            sal_uInt32 len = (sal_uInt32) control.m_nWritePos;

            net_title = rtl::OUString(fwd,len,RTL_TEXTENCODING_UTF8);
            // the buffer now contains the name of the file;
            // analyze the output:
            // Format of current working directory:
            // 257 "/bla/bla" is current directory
            sal_Int32 index1 = net_title.lastIndexOf(
                rtl::OUString::createFromAscii("257"));
            index1 = 1+net_title.indexOf(sal_Unicode('"'),index1);
            sal_Int32 index2 = net_title.indexOf(sal_Unicode('"'),index1);
            net_title = net_title.copy(index1,index2-index1);
            if(!net_title.equalsAscii("/")) {
                index1 = net_title.lastIndexOf(sal_Unicode('/'));
                net_title = net_title.copy(1+index1);
            }
            try_more = false;
        } else if(err == CURLE_BAD_PASSWORD_ENTERED)
            // the client should retry after getting the correct
            // username + password
            throw curl_exception(err);
        else if(try_more && err == CURLE_FTP_ACCESS_DENIED) {
            // We  were  either denied access when trying to login to
            //  an FTP server or when trying to change working directory
            //  to the one given in the URL.
            if(m_aPathSegmentVec.size())
                // determine title form url
                net_title = decodePathSegment(m_aPathSegmentVec.back());
            else
                // must be root
                net_title = rtl::OUString::createFromAscii("/");
            try_more = false;
        }

        if(try_more)
            try_more = false;
        else
            break;
    }

    curl_slist_free_all(slist);
    return net_title;
}


FTPDirentry FTPURL::direntry() const
    throw(curl_exception)
{
    rtl::OUString nettitle = net_title();
    FTPDirentry aDirentry;

    aDirentry.m_aName = nettitle;                 // init aDirentry
    if(nettitle.equalsAscii("/") ||
       nettitle.equalsAscii(".."))
        aDirentry.m_nMode = INETCOREFTP_FILEMODE_ISDIR;
    else
        aDirentry.m_nMode = INETCOREFTP_FILEMODE_UNKNOWN;

    aDirentry.m_nSize = 0;

    if(!nettitle.equalsAscii("/")) {
        // try to open the parent directory
        FTPURL aURL(parent(),m_pFCP);

        std::vector<FTPDirentry> list = aURL.list(OpenMode::ALL);

        for(unsigned i = 0; i < list.size(); ++i) {
            if(list[i].m_aName == nettitle) { // the relevant file is found
                aDirentry = list[i];
                break;
            }
        }
    }
    return aDirentry;
}


extern "C" {

    size_t memory_read(void *ptr,size_t size,size_t nmemb,void *stream)
    {
        sal_Int32 nRequested = sal_Int32(size*nmemb);
        CurlInput *curlInput = static_cast<CurlInput*>(stream);
        if(curlInput)
            return size_t(curlInput->read(((sal_Int8*)ptr),nRequested));
        else
            return 0;
    }

}


void FTPURL::insert(bool replaceExisting,void* stream) const
    throw(curl_exception)
{
    if(!replaceExisting) {
//      FTPDirentry aDirentry(direntry());
//      if(aDirentry.m_nMode == INETCOREFTP_FILEMODE_UNKNOWN)
        // throw curl_exception(FILE_EXIST_DURING_INSERT);
        throw curl_exception(FILE_MIGHT_EXIST_DURING_INSERT);
    } // else
    // overwrite is default in libcurl

    CURL *curl = m_pFCP->handle();

    SET_CONTROL_CONTAINER;
    curl_easy_setopt(curl,CURLOPT_NOBODY,false);    // no data => no transfer
    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,0);
    curl_easy_setopt(curl,CURLOPT_QUOTE,0);
    curl_easy_setopt(curl,CURLOPT_READFUNCTION,memory_read);
    curl_easy_setopt(curl,CURLOPT_READDATA,stream);
    curl_easy_setopt(curl, CURLOPT_UPLOAD,1);

    rtl::OUString url(ident(false,true));
    SET_URL(url);

    CURLcode err = curl_easy_perform(curl);
    curl_easy_setopt(curl, CURLOPT_UPLOAD,false);

    if(err != CURLE_OK)
        throw curl_exception(err);
}



void FTPURL::mkdir(bool ReplaceExisting) const
    throw(curl_exception)
{
    rtl::OString title;
    if(m_aPathSegmentVec.size()) {
        rtl::OUString titleOU = m_aPathSegmentVec.back();
        titleOU = decodePathSegment(titleOU);
        title = rtl::OString(titleOU.getStr(),
                            titleOU.getLength(),
                            RTL_TEXTENCODING_UTF8);
    }
    else
        // will give an error
        title = rtl::OString("/");

    rtl::OString del("del "); del += title;
    rtl::OString mkd("mkd "); mkd += title;

    struct curl_slist *slist = 0;

    FTPDirentry aDirentry(direntry());
    if(!ReplaceExisting) {
//      if(aDirentry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN)
//          throw curl_exception(FOLDER_EXIST_DURING_INSERT);
        throw curl_exception(FOLDER_MIGHT_EXIST_DURING_INSERT);
    } else if(aDirentry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN)
        slist = curl_slist_append(slist,del.getStr());

    slist = curl_slist_append(slist,mkd.getStr());

    CURL *curl = m_pFCP->handle();
    SET_CONTROL_CONTAINER;
    curl_easy_setopt(curl,CURLOPT_NOBODY,true);       // no data => no transfer
    curl_easy_setopt(curl,CURLOPT_QUOTE,0);

    // post request
    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);

    rtl::OUString url(parent(true));
    if(1+url.lastIndexOf(sal_Unicode('/')) != url.getLength())
        url += rtl::OUString::createFromAscii("/");
    SET_URL(url);

    CURLcode err = curl_easy_perform(curl);
    curl_slist_free_all(slist);
    if(err != CURLE_OK)
        throw curl_exception(err);
}


rtl::OUString FTPURL::ren(const rtl::OUString& NewTitle)
    throw(curl_exception)
{
    CURL *curl = m_pFCP->handle();

    // post request
    rtl::OString renamefrom("RNFR ");
    rtl::OUString OldTitle = net_title();
    renamefrom +=
        rtl::OString(OldTitle.getStr(),
                     OldTitle.getLength(),
                     RTL_TEXTENCODING_UTF8);

    rtl::OString renameto("RNTO ");
    renameto +=
        rtl::OString(NewTitle.getStr(),
                     NewTitle.getLength(),
                     RTL_TEXTENCODING_UTF8);

    struct curl_slist *slist = 0;
    slist = curl_slist_append(slist,renamefrom.getStr());
    slist = curl_slist_append(slist,renameto.getStr());
    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);

    SET_CONTROL_CONTAINER;
    curl_easy_setopt(curl,CURLOPT_NOBODY,true);       // no data => no transfer
    curl_easy_setopt(curl,CURLOPT_QUOTE,0);

    rtl::OUString url(parent(true));
    if(1+url.lastIndexOf(sal_Unicode('/')) != url.getLength())
        url += rtl::OUString::createFromAscii("/");
    SET_URL(url);

    CURLcode err = curl_easy_perform(curl);
    curl_slist_free_all(slist);
    if(err != CURLE_OK)
        throw curl_exception(err);
    else if(m_aPathSegmentVec.size() &&
            !m_aPathSegmentVec.back().equalsAscii(".."))
        m_aPathSegmentVec.back() = encodePathSegment(NewTitle);
    return OldTitle;
}



void FTPURL::del() const
    throw(curl_exception)
{
    FTPDirentry aDirentry(direntry());

    rtl::OString dele(aDirentry.m_aName.getStr(),
                      aDirentry.m_aName.getLength(),
                      RTL_TEXTENCODING_UTF8);

    if(aDirentry.m_nMode & INETCOREFTP_FILEMODE_ISDIR) {
        std::vector<FTPDirentry> vec = list(sal_Int16(OpenMode::ALL));
        for( unsigned int i = 0; i < vec.size(); ++i )
            try {
                FTPURL url(vec[i].m_aURL,m_pFCP);
                url.del();
            } catch(const curl_exception&) {
            }
        dele = rtl::OString("RMD ") + dele;
    }
    else if(aDirentry.m_nMode != INETCOREFTP_FILEMODE_UNKNOWN)
        dele = rtl::OString("DELE ") + dele;
    else
        return;

    // post request
    CURL *curl = m_pFCP->handle();
    struct curl_slist *slist = 0;
    slist = curl_slist_append(slist,dele.getStr());
    curl_easy_setopt(curl,CURLOPT_POSTQUOTE,slist);

    SET_CONTROL_CONTAINER;
    curl_easy_setopt(curl,CURLOPT_NOBODY,true);       // no data => no transfer
    curl_easy_setopt(curl,CURLOPT_QUOTE,0);

    rtl::OUString url(parent(true));
    if(1+url.lastIndexOf(sal_Unicode('/')) != url.getLength())
        url += rtl::OUString::createFromAscii("/");
    SET_URL(url);

    CURLcode err = curl_easy_perform(curl);
    curl_slist_free_all(slist);
    if(err != CURLE_OK)
        throw curl_exception(err);
}

