1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
25 #include <com/sun/star/sdbc/XResultSet.hpp>
26 #include <com/sun/star/task/XInteractionHandler.hpp>
27 #include <com/sun/star/task/InteractionHandler.hpp>
28 #include <com/sun/star/ucb/CommandAbortedException.hpp>
29 #include <com/sun/star/ucb/ContentInfo.hpp>
30 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
31 #include <com/sun/star/ucb/IOErrorCode.hpp>
32 #include <com/sun/star/ucb/InteractiveIOException.hpp>
33 #include <com/sun/star/ucb/NameClashException.hpp>
34 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
35 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
36 #include <com/sun/star/ucb/XContentAccess.hpp>
37 #include <com/sun/star/ucb/XUniversalContentBroker.hpp>
38 #include <com/sun/star/uno/Any.hxx>
39 #include <com/sun/star/uno/Exception.hpp>
40 #include <com/sun/star/uno/Reference.hxx>
41 #include <com/sun/star/uno/RuntimeException.hpp>
42 #include <com/sun/star/uno/Sequence.hxx>
43 #include <comphelper/processfactory.hxx>
44 #include <comphelper/simplefileaccessinteraction.hxx>
45 #include <osl/file.hxx>
46 #include <rtl/ustring.hxx>
47 #include <sal/log.hxx>
48 #include <tools/datetime.hxx>
49 #include <tools/urlobj.hxx>
50 #include <comphelper/diagnose_ex.hxx>
51 #include <ucbhelper/commandenvironment.hxx>
52 #include <ucbhelper/content.hxx>
53 #include <unotools/ucbhelper.hxx>
55 namespace com::sun::star::ucb
{ class XProgressHandler
; }
56 namespace com::sun::star::uno
{ class XComponentContext
; }
57 namespace com::sun::star::util
{ struct DateTime
; }
61 OUString
canonic(OUString
const & url
) {
63 SAL_WARN_IF(o
.HasError(), "unotools.ucbhelper", "Invalid URL \"" << url
<< '"');
64 return o
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
67 ucbhelper::Content
content(OUString
const & url
) {
68 return ucbhelper::Content(
70 utl::UCBContentHelper::getDefaultCommandEnvironment(),
71 comphelper::getProcessComponentContext());
74 ucbhelper::Content
content(INetURLObject
const & url
) {
75 return ucbhelper::Content(
76 url
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
77 utl::UCBContentHelper::getDefaultCommandEnvironment(),
78 comphelper::getProcessComponentContext());
81 std::vector
<OUString
> getContents(OUString
const & url
) {
83 std::vector
<OUString
> cs
;
84 ucbhelper::Content
c(content(url
));
85 css::uno::Sequence
<OUString
> args
{ "Title" };
86 css::uno::Reference
<css::sdbc::XResultSet
> res( c
.createCursor(args
), css::uno::UNO_SET_THROW
);
87 css::uno::Reference
<css::ucb::XContentAccess
> acc( res
, css::uno::UNO_QUERY_THROW
);
89 cs
.push_back(acc
->queryContentIdentifierString());
92 } catch (css::uno::RuntimeException
const &) {
94 } catch (css::ucb::CommandAbortedException
const &) {
95 assert(false && "this cannot happen");
97 } catch (css::uno::Exception
const &) {
98 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "getContents(" << url
<< ")");
99 return std::vector
<OUString
>();
103 OUString
getCasePreservingUrl(const INetURLObject
& url
) {
105 content(url
).executeCommand(
106 "getCasePreservingURL",
111 DateTime
convert(css::util::DateTime
const & dt
) {
117 css::uno::Reference
< css::ucb::XCommandEnvironment
> utl::UCBContentHelper::getDefaultCommandEnvironment()
119 css::uno::Reference
< css::task::XInteractionHandler
> xIH(
120 css::task::InteractionHandler::createWithParent(
121 comphelper::getProcessComponentContext(), nullptr ) );
123 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
124 rtl::Reference
<ucbhelper::CommandEnvironment
> pCommandEnv
=
125 new ::ucbhelper::CommandEnvironment(
126 new comphelper::SimpleFileAccessInteraction( xIH
), xProgress
);
131 bool utl::UCBContentHelper::IsDocument(OUString
const & url
) {
133 return content(url
).isDocument();
134 } catch (css::uno::RuntimeException
const &) {
136 } catch (css::ucb::CommandAbortedException
const &) {
137 assert(false && "this cannot happen");
139 } catch (css::uno::Exception
const &) {
140 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url
<< ")");
145 css::uno::Any
utl::UCBContentHelper::GetProperty(
146 OUString
const & url
, OUString
const & property
)
149 return content(url
).getPropertyValue(property
);
150 } catch (css::uno::RuntimeException
const &) {
152 } catch (css::ucb::CommandAbortedException
const &) {
153 assert(false && "this cannot happen");
155 } catch (css::uno::Exception
const &) {
156 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetProperty(" << url
<< ", " << property
<< ")");
157 return css::uno::Any();
161 bool utl::UCBContentHelper::IsFolder(OUString
const & url
) {
163 return content(url
).isFolder();
164 } catch (css::uno::RuntimeException
const &) {
166 } catch (css::ucb::CommandAbortedException
const &) {
167 assert(false && "this cannot happen");
169 } catch (css::uno::Exception
const &) {
170 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url
<< ")");
175 bool utl::UCBContentHelper::GetTitle(
176 OUString
const & url
, OUString
* title
)
178 assert(title
!= nullptr);
180 return content(url
).getPropertyValue("Title") >>= *title
;
181 } catch (css::uno::RuntimeException
const &) {
183 } catch (css::ucb::CommandAbortedException
const &) {
184 assert(false && "this cannot happen");
186 } catch (css::uno::Exception
const &) {
187 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url
<< ")");
192 bool utl::UCBContentHelper::Kill(OUString
const & url
) {
194 content(url
).executeCommand(
196 css::uno::Any(true));
198 } catch (css::uno::RuntimeException
const &) {
200 } catch (css::ucb::CommandAbortedException
const &) {
201 assert(false && "this cannot happen");
203 } catch (css::uno::Exception
const &) {
204 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url
<< ")");
209 bool utl::UCBContentHelper::MakeFolder(
210 ucbhelper::Content
& parent
, OUString
const & title
,
211 ucbhelper::Content
& result
)
215 const css::uno::Sequence
<css::ucb::ContentInfo
> info(
216 parent
.queryCreatableContentsInfo());
217 for (const auto& rInfo
: info
) {
218 // Simply look for the first KIND_FOLDER:
219 if ((rInfo
.Attributes
220 & css::ucb::ContentInfoAttribute::KIND_FOLDER
)
223 // Make sure the only required bootstrap property is "Title":
224 if ( rInfo
.Properties
.getLength() != 1 || rInfo
.Properties
[0].Name
!= "Title" )
228 if (parent
.insertNewContent(rInfo
.Type
, { "Title" }, { css::uno::Any(title
) }, result
))
234 } catch (css::ucb::InteractiveIOException
const & e
) {
235 if (e
.Code
== css::ucb::IOErrorCode_ALREADY_EXISTING
) {
238 TOOLS_INFO_EXCEPTION(
239 "unotools.ucbhelper",
240 "UCBContentHelper::MakeFolder(" << title
<< ")");
242 } catch (css::ucb::NameClashException
const &) {
244 } catch (css::uno::RuntimeException
const &) {
246 } catch (css::ucb::CommandAbortedException
const &) {
247 assert(false && "this cannot happen");
249 } catch (css::uno::Exception
const &) {
250 TOOLS_INFO_EXCEPTION(
251 "unotools.ucbhelper",
252 "UCBContentHelper::MakeFolder(" << title
<< ") ");
255 INetURLObject
o(parent
.getURL());
264 bool utl::UCBContentHelper::IsYounger(
265 OUString
const & younger
, OUString
const & older
)
270 content(younger
).getPropertyValue(
272 get
<css::util::DateTime
>())
274 content(older
).getPropertyValue(
276 get
<css::util::DateTime
>());
277 } catch (css::uno::RuntimeException
const &) {
279 } catch (css::ucb::CommandAbortedException
const &) {
280 assert(false && "this cannot happen");
282 } catch (css::uno::Exception
const &) {
283 TOOLS_INFO_EXCEPTION(
284 "unotools.ucbhelper",
285 "UCBContentHelper::IsYounger(" << younger
<< ", " << older
<< ")");
290 bool utl::UCBContentHelper::Exists(OUString
const & url
) {
292 if (osl::FileBase::getSystemPathFromFileURL(url
, pathname
)
293 == osl::FileBase::E_None
)
295 // Try to create a directory entry for the given URL:
297 if (osl::FileBase::getFileURLFromSystemPath(pathname
, url2
)
298 == osl::FileBase::E_None
)
300 // #106526 osl_getDirectoryItem is an existence check, no further
301 // osl_getFileStatus call necessary:
302 osl::DirectoryItem item
;
303 return osl::DirectoryItem::get(url2
, item
) == osl::FileBase::E_None
;
308 // Divide URL into folder and name part:
309 INetURLObject
o(url
);
312 INetURLObject::LAST_SEGMENT
, true,
313 INetURLObject::DecodeMechanism::WithCharset
));
315 o
.removeFinalSlash();
316 std::vector
<OUString
> cs(
317 getContents(o
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)));
318 return std::any_of(cs
.begin(), cs
.end(),
319 [&name
](const OUString
& rItem
) {
320 return INetURLObject(rItem
).
321 getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
).
322 equalsIgnoreAsciiCase(name
); });
326 bool utl::UCBContentHelper::IsSubPath(
327 OUString
const & parent
, OUString
const & child
)
329 // The comparison is done in the following way:
330 // - First, compare case sensitively
331 // - If names are different, try a fallback comparing case insensitively
332 // - If the last comparison succeeded, get case preserving normalized names
333 // for the files and compare them
334 // (The second step is required because retrieving the normalized names
335 // might be very expensive in some cases.)
336 INetURLObject
candidate(child
);
337 INetURLObject
folder(parent
);
338 if (candidate
.GetProtocol() != folder
.GetProtocol()) {
341 INetURLObject
candidateLower(child
.toAsciiLowerCase());
342 INetURLObject
folderLower(parent
.toAsciiLowerCase());
346 if (candidate
== folder
347 || (candidate
.GetProtocol() == INetProtocol::File
348 && candidateLower
== folderLower
349 && (getCasePreservingUrl(candidate
)
350 == getCasePreservingUrl(folder
))))
355 } while (candidate
.removeSegment() && candidateLower
.removeSegment()
356 && candidate
!= tmp
);
357 // INetURLObject::removeSegment sometimes returns true without
358 // modifying the URL, e.g., in case of "file:///"
359 } catch (css::uno::RuntimeException
const &) {
361 } catch (css::ucb::CommandAbortedException
const &) {
362 assert(false && "this cannot happen");
364 } catch (css::uno::Exception
const &) {
365 TOOLS_INFO_EXCEPTION(
366 "unotools.ucbhelper",
367 "UCBContentHelper::IsSubPath(" << parent
<< ", " << child
<< ")");
372 bool utl::UCBContentHelper::EqualURLs(
373 OUString
const & url1
, OUString
const & url2
)
375 if (url1
.isEmpty() || url2
.isEmpty()) {
378 css::uno::Reference
< css::ucb::XUniversalContentBroker
> ucb(
379 css::ucb::UniversalContentBroker::create(
380 comphelper::getProcessComponentContext()));
382 ucb
->compareContentIds(
383 ucb
->createContentIdentifier(canonic(url1
)),
384 ucb
->createContentIdentifier(canonic(url2
)))
388 bool utl::UCBContentHelper::ensureFolder(
389 const css::uno::Reference
< css::uno::XComponentContext
>& xCtx
,
390 const css::uno::Reference
< css::ucb::XCommandEnvironment
>& xEnv
,
391 std::u16string_view rFolder
, ucbhelper::Content
& result
) noexcept
395 INetURLObject
aURL( rFolder
);
396 OUString aTitle
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
397 aURL
.removeSegment();
398 ::ucbhelper::Content aParent
;
400 if ( ::ucbhelper::Content::create( aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
401 xEnv
, xCtx
, aParent
) )
403 return ::utl::UCBContentHelper::MakeFolder(aParent
, aTitle
, result
);
413 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */