1
0
Fork 0
libreoffice/unotools/source/ucbhelper/ucbhelper.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

413 lines
14 KiB
C++

/* -*- 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 <cassert>
#include <vector>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/task/XInteractionHandler.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/ucb/ContentInfo.hpp>
#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
#include <com/sun/star/ucb/IOErrorCode.hpp>
#include <com/sun/star/ucb/InteractiveIOException.hpp>
#include <com/sun/star/ucb/NameClashException.hpp>
#include <com/sun/star/ucb/UniversalContentBroker.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/XContentAccess.hpp>
#include <com/sun/star/ucb/XUniversalContentBroker.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Exception.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/simplefileaccessinteraction.hxx>
#include <osl/file.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <tools/datetime.hxx>
#include <tools/urlobj.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <ucbhelper/commandenvironment.hxx>
#include <ucbhelper/content.hxx>
#include <unotools/ucbhelper.hxx>
namespace com::sun::star::ucb { class XProgressHandler; }
namespace com::sun::star::uno { class XComponentContext; }
namespace com::sun::star::util { struct DateTime; }
namespace {
OUString canonic(OUString const & url) {
INetURLObject o(url);
SAL_WARN_IF(o.HasError(), "unotools.ucbhelper", "Invalid URL \"" << url << '"');
return o.GetMainURL(INetURLObject::DecodeMechanism::NONE);
}
ucbhelper::Content content(OUString const & url) {
return ucbhelper::Content(
canonic(url),
utl::UCBContentHelper::getDefaultCommandEnvironment(),
comphelper::getProcessComponentContext());
}
ucbhelper::Content content(INetURLObject const & url) {
return ucbhelper::Content(
url.GetMainURL(INetURLObject::DecodeMechanism::NONE),
utl::UCBContentHelper::getDefaultCommandEnvironment(),
comphelper::getProcessComponentContext());
}
std::vector<OUString> getContents(OUString const & url) {
try {
std::vector<OUString> cs;
ucbhelper::Content c(content(url));
css::uno::Sequence<OUString> args { u"Title"_ustr };
css::uno::Reference<css::sdbc::XResultSet> res( c.createCursor(args), css::uno::UNO_SET_THROW);
css::uno::Reference<css::ucb::XContentAccess> acc( res, css::uno::UNO_QUERY_THROW);
while (res->next()) {
cs.push_back(acc->queryContentIdentifierString());
}
return cs;
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "getContents(" << url << ")");
return std::vector<OUString>();
}
}
OUString getCasePreservingUrl(const INetURLObject& url) {
return
content(url).executeCommand(
u"getCasePreservingURL"_ustr,
css::uno::Any()).
get<OUString>();
}
DateTime convert(css::util::DateTime const & dt) {
return DateTime(dt);
}
}
css::uno::Reference< css::ucb::XCommandEnvironment > utl::UCBContentHelper::getDefaultCommandEnvironment()
{
css::uno::Reference< css::task::XInteractionHandler > xIH(
css::task::InteractionHandler::createWithParent(
comphelper::getProcessComponentContext(), nullptr ) );
css::uno::Reference< css::ucb::XProgressHandler > xProgress;
rtl::Reference<ucbhelper::CommandEnvironment> pCommandEnv =
new ::ucbhelper::CommandEnvironment(
new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
return pCommandEnv;
}
bool utl::UCBContentHelper::IsDocument(OUString const & url) {
try {
return content(url).isDocument();
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url << ")");
return false;
}
}
css::uno::Any utl::UCBContentHelper::GetProperty(
OUString const & url, OUString const & property)
{
try {
return content(url).getPropertyValue(property);
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetProperty(" << url << ", " << property << ")");
return css::uno::Any();
}
}
bool utl::UCBContentHelper::IsFolder(OUString const & url) {
try {
return content(url).isFolder();
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url << ")");
return false;
}
}
bool utl::UCBContentHelper::GetTitle(
OUString const & url, OUString * title)
{
assert(title != nullptr);
try {
return content(url).getPropertyValue(u"Title"_ustr) >>= *title;
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url << ")");
return false;
}
}
bool utl::UCBContentHelper::Kill(OUString const & url) {
try {
content(url).executeCommand(
u"delete"_ustr,
css::uno::Any(true));
return true;
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url << ")");
return false;
}
}
bool utl::UCBContentHelper::MakeFolder(
ucbhelper::Content & parent, OUString const & title,
ucbhelper::Content & result)
{
bool exists = false;
try {
const css::uno::Sequence<css::ucb::ContentInfo> info(
parent.queryCreatableContentsInfo());
for (const auto& rInfo : info) {
// Simply look for the first KIND_FOLDER:
if ((rInfo.Attributes
& css::ucb::ContentInfoAttribute::KIND_FOLDER)
!= 0)
{
// Make sure the only required bootstrap property is "Title":
if ( rInfo.Properties.getLength() != 1 || rInfo.Properties[0].Name != "Title" )
{
continue;
}
if (parent.insertNewContent(rInfo.Type, { u"Title"_ustr }, { css::uno::Any(title) }, result))
{
return true;
}
}
}
} catch (css::ucb::InteractiveIOException const & e) {
if (e.Code == css::ucb::IOErrorCode_ALREADY_EXISTING) {
exists = true;
} else {
TOOLS_INFO_EXCEPTION(
"unotools.ucbhelper",
"UCBContentHelper::MakeFolder(" << title << ")");
}
} catch (css::ucb::NameClashException const &) {
exists = true;
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION(
"unotools.ucbhelper",
"UCBContentHelper::MakeFolder(" << title << ") ");
}
if (exists) {
INetURLObject o(parent.getURL());
o.Append(title);
result = content(o);
return true;
} else {
return false;
}
}
bool utl::UCBContentHelper::IsYounger(
OUString const & younger, OUString const & older)
{
try {
return
convert(
content(younger).getPropertyValue(
u"DateModified"_ustr).
get<css::util::DateTime>())
> convert(
content(older).getPropertyValue(
u"DateModified"_ustr).
get<css::util::DateTime>());
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION(
"unotools.ucbhelper",
"UCBContentHelper::IsYounger(" << younger << ", " << older << ")");
return false;
}
}
bool utl::UCBContentHelper::Exists(OUString const & url) {
OUString pathname;
if (osl::FileBase::getSystemPathFromFileURL(url, pathname)
== osl::FileBase::E_None)
{
// Try to create a directory entry for the given URL:
OUString url2;
if (osl::FileBase::getFileURLFromSystemPath(pathname, url2)
== osl::FileBase::E_None)
{
// #106526 osl_getDirectoryItem is an existence check, no further
// osl_getFileStatus call necessary:
osl::DirectoryItem item;
return osl::DirectoryItem::get(url2, item) == osl::FileBase::E_None;
} else {
return false;
}
} else {
// Divide URL into folder and name part:
INetURLObject o(url);
OUString name(
o.getName(
INetURLObject::LAST_SEGMENT, true,
INetURLObject::DecodeMechanism::WithCharset));
o.removeSegment();
o.removeFinalSlash();
std::vector<OUString> cs(
getContents(o.GetMainURL(INetURLObject::DecodeMechanism::NONE)));
return std::any_of(cs.begin(), cs.end(),
[&name](const OUString& rItem) {
return INetURLObject(rItem).
getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset).
equalsIgnoreAsciiCase(name); });
}
}
bool utl::UCBContentHelper::IsSubPath(
OUString const & parent, OUString const & child)
{
// The comparison is done in the following way:
// - First, compare case sensitively
// - If names are different, try a fallback comparing case insensitively
// - If the last comparison succeeded, get case preserving normalized names
// for the files and compare them
// (The second step is required because retrieving the normalized names
// might be very expensive in some cases.)
INetURLObject candidate(child);
INetURLObject folder(parent);
if (candidate.GetProtocol() != folder.GetProtocol()) {
return false;
}
INetURLObject candidateLower(child.toAsciiLowerCase());
INetURLObject folderLower(parent.toAsciiLowerCase());
try {
INetURLObject tmp;
do {
if (candidate == folder
|| (candidate.GetProtocol() == INetProtocol::File
&& candidateLower == folderLower
&& (getCasePreservingUrl(candidate)
== getCasePreservingUrl(folder))))
{
return true;
}
tmp = candidate;
} while (candidate.removeSegment() && candidateLower.removeSegment()
&& candidate != tmp);
// INetURLObject::removeSegment sometimes returns true without
// modifying the URL, e.g., in case of "file:///"
} catch (css::uno::RuntimeException const &) {
throw;
} catch (css::ucb::CommandAbortedException const &) {
assert(false && "this cannot happen");
throw;
} catch (css::uno::Exception const &) {
TOOLS_INFO_EXCEPTION(
"unotools.ucbhelper",
"UCBContentHelper::IsSubPath(" << parent << ", " << child << ")");
}
return false;
}
bool utl::UCBContentHelper::EqualURLs(
OUString const & url1, OUString const & url2)
{
if (url1.isEmpty() || url2.isEmpty()) {
return false;
}
css::uno::Reference< css::ucb::XUniversalContentBroker > ucb(
css::ucb::UniversalContentBroker::create(
comphelper::getProcessComponentContext()));
return
ucb->compareContentIds(
ucb->createContentIdentifier(canonic(url1)),
ucb->createContentIdentifier(canonic(url2)))
== 0;
}
bool utl::UCBContentHelper::ensureFolder(
const css::uno::Reference< css::uno::XComponentContext >& xCtx,
const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
std::u16string_view rFolder, ucbhelper::Content & result) noexcept
{
try
{
INetURLObject aURL( rFolder );
OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
aURL.removeSegment();
::ucbhelper::Content aParent;
if ( ::ucbhelper::Content::create( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
xEnv, xCtx, aParent ) )
{
return ::utl::UCBContentHelper::MakeFolder(aParent, aTitle, result);
}
}
catch (...)
{
}
return false;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */