/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <sal/config.h>
 
#include <dp_misc.h>
#include <dp_ucb.h>
#include <rtl/uri.hxx>
#include <rtl/ustrbuf.hxx>
#include <ucbhelper/content.hxx>
#include <xmlscript/xml_helper.hxx>
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/ucb/CommandFailedException.hpp>
#include <com/sun/star/ucb/ContentInfo.hpp>
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <comphelper/processfactory.hxx>
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
 
namespace dp_misc
{
 
 
bool create_ucb_content(
    ::ucbhelper::Content * ret_ucbContent, OUString const & url,
    Reference<XCommandEnvironment> const & xCmdEnv,
    bool throw_exc )
{
    try {
        // Existence check...
        // content ctor/isFolder() will throw exception in case the resource
        // does not exist.
 
        // dilemma: no chance to use the given handler here, because it would
        //          raise no such file dialogs, else no interaction for
        //          passwords, ...? xxx todo
        ::ucbhelper::Content ucbContent(
            url, Reference<XCommandEnvironment>(),
            comphelper::getProcessComponentContext() );
 
        ucbContent.isFolder();
 
        if (ret_ucbContent != nullptr)
        {
            ucbContent.setCommandEnvironment( xCmdEnv );
            *ret_ucbContent = std::move(ucbContent);
        }
        return true;
    }
    catch (const RuntimeException &) {
        throw;
    }
    catch (const Exception &) {
        if (throw_exc)
            throw;
    }
    return false;
}
 
 
bool create_folder(
    ::ucbhelper::Content * ret_ucb_content, OUString const & url_,
    Reference<XCommandEnvironment> const & xCmdEnv, bool throw_exc )
{
    ::ucbhelper::Content ucb_content;
    if (create_ucb_content(
            &ucb_content, url_, xCmdEnv, false /* no throw */ ))
    {
        if (ucb_content.isFolder()) {
            if (ret_ucb_content != nullptr)
                *ret_ucb_content = ucb_content;
            return true;
        }
    }
 
    OUString url( url_ );
    // xxx todo: find parent
    sal_Int32 slash = url.lastIndexOf( '/' );
    if (slash < 0) {
        // fallback:
        url = expandUnoRcUrl( url );
        slash = url.lastIndexOf( '/' );
    }
    if (slash < 0) {
        // invalid: has to be at least "auth:/..."
        if (throw_exc)
            throw ContentCreationException(
                "Cannot create folder (invalid path): '" + url + "'",
                Reference<XInterface>(), ContentCreationError_UNKNOWN );
        return false;
    }
    ::ucbhelper::Content parentContent;
    if (! create_folder(
            &parentContent, url.copy( 0, slash ), xCmdEnv, throw_exc ))
        return false;
    const Any title( ::rtl::Uri::decode( url.copy( slash + 1 ),
                                         rtl_UriDecodeWithCharset,
                                         RTL_TEXTENCODING_UTF8 ) );
    const Sequence<ContentInfo> infos(
        parentContent.queryCreatableContentsInfo() );
    for ( ContentInfo const & info : infos )
    {
        // look KIND_FOLDER:
        if ((info.Attributes & ContentInfoAttribute::KIND_FOLDER) != 0)
        {
            // make sure the only required bootstrap property is "Title":
            Sequence<beans::Property> const & rProps = info.Properties;
            if ( rProps.getLength() != 1 || rProps[ 0 ].Name != "Title" )
                continue;
 
            try {
                if (parentContent.insertNewContent(
                        info.Type,
                        StrTitle::getTitleSequence(),
                        Sequence<Any>( &title, 1 ),
                        ucb_content )) {
                    if (ret_ucb_content != nullptr)
                        *ret_ucb_content = ucb_content;
                    return true;
                }
            }
            catch (const RuntimeException &) {
                throw;
            }
            catch (const CommandFailedException &) {
                // Interaction Handler already handled the error
                // that has occurred...
            }
            catch (const Exception &) {
                if (throw_exc)
                    throw;
                return false;
            }
        }
    }
    if (throw_exc)
        throw ContentCreationException(
            "Cannot create folder: '" + url + "'",
            Reference<XInterface>(), ContentCreationError_UNKNOWN );
    return false;
}
 
 
bool erase_path( OUString const & url,
                 Reference<XCommandEnvironment> const & xCmdEnv,
                 bool throw_exc )
{
    ::ucbhelper::Content ucb_content;
    if (create_ucb_content( &ucb_content, url, xCmdEnv, false /* no throw */ ))
    {
        try {
            ucb_content.executeCommand(
                u"delete"_ustr, Any( true /* delete physically */ ) );
        }
        catch (const RuntimeException &) {
            throw;
        }
        catch (const Exception &) {
            if (throw_exc)
                throw;
            return false;
        }
    }
    return true;
}
 
 
std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content )
{
    std::vector<sal_Int8> bytes;
    Reference<io::XOutputStream> xStream(
        ::xmlscript::createOutputStream( &bytes ) );
    if (! ucb_content.openStream( xStream ))
        throw RuntimeException(
            u"::ucbhelper::Content::openStream( XOutputStream ) failed!"_ustr,
            nullptr );
    return bytes;
}
 
 
bool readLine( OUString * res, std::u16string_view startingWith,
               ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc )
{
    // read whole file:
    std::vector<sal_Int8> bytes( readFile( ucb_content ) );
    OUString file( reinterpret_cast<char const *>(bytes.data()),
                   bytes.size(), textenc );
    sal_Int32 pos = 0;
    for (;;)
    {
        if (file.match( startingWith, pos ))
        {
            OUStringBuffer buf;
            sal_Int32 start = pos;
            pos += startingWith.size();
            for (;;)
            {
                pos = file.indexOf( LF, pos );
                if (pos < 0) { // EOF
                    buf.append( file.subView(start) );
                }
                else
                {
                    if (pos > 0 && file[ pos - 1 ] == CR)
                    {
                        // consume extra CR
                        buf.append( file.subView(start, pos - start - 1) );
                        ++pos;
                    }
                    else
                        buf.append( file.subView(start, pos - start) );
                    ++pos; // consume LF
                    // check next line:
                    if (pos < file.getLength() &&
                        (file[ pos ] == ' ' || file[ pos ] == '\t'))
                    {
                        buf.append( ' ' );
                        ++pos;
                        start = pos;
                        continue;
                    }
                }
                break;
            }
            *res = buf.makeStringAndClear();
            return true;
        }
        // next line:
        sal_Int32 next_lf = file.indexOf( LF, pos );
        if (next_lf < 0) // EOF
            break;
        pos = next_lf + 1;
    }
    return false;
}
 
bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
                     ::ucbhelper::Content & ucb_content )
{
    // read whole file:
    std::vector<sal_Int8> bytes( readFile( ucb_content ) );
    OUString file( reinterpret_cast<char const *>(bytes.data()),
                   bytes.size(), RTL_TEXTENCODING_UTF8);
    sal_Int32 pos = 0;
 
    for (;;)
    {
 
        OUStringBuffer buf;
        sal_Int32 start = pos;
 
        bool bEOF = false;
        pos = file.indexOf( LF, pos );
        if (pos < 0) { // EOF
            buf.append( file.subView(start) );
            bEOF = true;
        }
        else
        {
            if (pos > 0 && file[ pos - 1 ] == CR)
                // consume extra CR
                buf.append( file.subView(start, pos - start - 1) );
            else
                buf.append( file.subView(start, pos - start) );
            pos++;
        }
        OUString aLine = buf.makeStringAndClear();
 
        sal_Int32 posEqual = aLine.indexOf('=');
        if (posEqual > 0 && (posEqual + 1) <  aLine.getLength())
        {
            OUString name = aLine.copy(0, posEqual);
            OUString value = aLine.copy(posEqual + 1);
            out_result.emplace_back(name, value);
        }
 
        if (bEOF)
            break;
    }
    return false;
}
 
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.