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 <cppuhelper/exc_hlp.hxx>
45 #include <comphelper/simplefileaccessinteraction.hxx>
46 #include <osl/file.hxx>
47 #include <rtl/ustring.hxx>
48 #include <sal/log.hxx>
49 #include <sal/types.h>
50 #include <tools/datetime.hxx>
51 #include <tools/urlobj.hxx>
52 #include <tools/diagnose_ex.h>
53 #include <ucbhelper/commandenvironment.hxx>
54 #include <ucbhelper/content.hxx>
55 #include <unotools/ucbhelper.hxx>
57 namespace com::sun::star::ucb
{ class XProgressHandler
; }
58 namespace com::sun::star::uno
{ class XComponentContext
; }
59 namespace com::sun::star::util
{ struct DateTime
; }
63 OUString
canonic(OUString
const & url
) {
65 SAL_WARN_IF(o
.HasError(), "unotools.ucbhelper", "Invalid URL \"" << url
<< '"');
66 return o
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
69 ucbhelper::Content
content(OUString
const & url
) {
70 return ucbhelper::Content(
72 utl::UCBContentHelper::getDefaultCommandEnvironment(),
73 comphelper::getProcessComponentContext());
76 ucbhelper::Content
content(INetURLObject
const & url
) {
77 return ucbhelper::Content(
78 url
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
79 utl::UCBContentHelper::getDefaultCommandEnvironment(),
80 comphelper::getProcessComponentContext());
83 std::vector
<OUString
> getContents(OUString
const & url
) {
85 std::vector
<OUString
> cs
;
86 ucbhelper::Content
c(content(url
));
87 css::uno::Sequence
<OUString
> args
{ "Title" };
88 css::uno::Reference
<css::sdbc::XResultSet
> res( c
.createCursor(args
), css::uno::UNO_SET_THROW
);
89 css::uno::Reference
<css::ucb::XContentAccess
> acc( res
, css::uno::UNO_QUERY_THROW
);
91 cs
.push_back(acc
->queryContentIdentifierString());
94 } catch (css::uno::RuntimeException
const &) {
96 } catch (css::ucb::CommandAbortedException
const &) {
97 assert(false && "this cannot happen");
99 } catch (css::uno::Exception
const &) {
100 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "getContents(" << url
<< ")");
101 return std::vector
<OUString
>();
105 OUString
getCasePreservingUrl(const INetURLObject
& url
) {
107 content(url
).executeCommand(
108 "getCasePreservingURL",
113 DateTime
convert(css::util::DateTime
const & dt
) {
119 css::uno::Reference
< css::ucb::XCommandEnvironment
> utl::UCBContentHelper::getDefaultCommandEnvironment()
121 css::uno::Reference
< css::task::XInteractionHandler
> xIH(
122 css::task::InteractionHandler::createWithParent(
123 comphelper::getProcessComponentContext(), nullptr ) );
125 css::uno::Reference
< css::ucb::XProgressHandler
> xProgress
;
126 ucbhelper::CommandEnvironment
* pCommandEnv
=
127 new ::ucbhelper::CommandEnvironment(
128 new comphelper::SimpleFileAccessInteraction( xIH
), xProgress
);
130 css::uno::Reference
< css::ucb::XCommandEnvironment
> xEnv(
131 static_cast< css::ucb::XCommandEnvironment
* >(pCommandEnv
), css::uno::UNO_QUERY
);
135 bool utl::UCBContentHelper::IsDocument(OUString
const & url
) {
137 return content(url
).isDocument();
138 } catch (css::uno::RuntimeException
const &) {
140 } catch (css::ucb::CommandAbortedException
const &) {
141 assert(false && "this cannot happen");
143 } catch (css::uno::Exception
const &) {
144 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url
<< ")");
149 css::uno::Any
utl::UCBContentHelper::GetProperty(
150 OUString
const & url
, OUString
const & property
)
153 return content(url
).getPropertyValue(property
);
154 } catch (css::uno::RuntimeException
const &) {
156 } catch (css::ucb::CommandAbortedException
const &) {
157 assert(false && "this cannot happen");
159 } catch (css::uno::Exception
const &) {
160 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetProperty(" << url
<< ", " << property
<< ")");
161 return css::uno::Any();
165 bool utl::UCBContentHelper::IsFolder(OUString
const & url
) {
167 return content(url
).isFolder();
168 } catch (css::uno::RuntimeException
const &) {
170 } catch (css::ucb::CommandAbortedException
const &) {
171 assert(false && "this cannot happen");
173 } catch (css::uno::Exception
const &) {
174 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url
<< ")");
179 bool utl::UCBContentHelper::GetTitle(
180 OUString
const & url
, OUString
* title
)
182 assert(title
!= nullptr);
184 return content(url
).getPropertyValue("Title") >>= *title
;
185 } catch (css::uno::RuntimeException
const &) {
187 } catch (css::ucb::CommandAbortedException
const &) {
188 assert(false && "this cannot happen");
190 } catch (css::uno::Exception
const &) {
191 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url
<< ")");
196 bool utl::UCBContentHelper::Kill(OUString
const & url
) {
198 content(url
).executeCommand(
200 css::uno::makeAny(true));
202 } catch (css::uno::RuntimeException
const &) {
204 } catch (css::ucb::CommandAbortedException
const &) {
205 assert(false && "this cannot happen");
207 } catch (css::uno::Exception
const &) {
208 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url
<< ")");
213 bool utl::UCBContentHelper::MakeFolder(
214 ucbhelper::Content
& parent
, OUString
const & title
,
215 ucbhelper::Content
& result
)
219 const css::uno::Sequence
<css::ucb::ContentInfo
> info(
220 parent
.queryCreatableContentsInfo());
221 for (const auto& rInfo
: info
) {
222 // Simply look for the first KIND_FOLDER:
223 if ((rInfo
.Attributes
224 & css::ucb::ContentInfoAttribute::KIND_FOLDER
)
227 // Make sure the only required bootstrap property is "Title":
228 if ( rInfo
.Properties
.getLength() != 1 || rInfo
.Properties
[0].Name
!= "Title" )
232 css::uno::Sequence
<OUString
> keys
{ "Title" };
233 css::uno::Sequence
<css::uno::Any
> values(1);
235 if (parent
.insertNewContent(rInfo
.Type
, keys
, values
, result
))
241 } catch (css::ucb::InteractiveIOException
const & e
) {
242 if (e
.Code
== css::ucb::IOErrorCode_ALREADY_EXISTING
) {
245 TOOLS_INFO_EXCEPTION(
246 "unotools.ucbhelper",
247 "UCBContentHelper::MakeFolder(" << title
<< ")");
249 } catch (css::ucb::NameClashException
const &) {
251 } catch (css::uno::RuntimeException
const &) {
253 } catch (css::ucb::CommandAbortedException
const &) {
254 assert(false && "this cannot happen");
256 } catch (css::uno::Exception
const &) {
257 TOOLS_INFO_EXCEPTION(
258 "unotools.ucbhelper",
259 "UCBContentHelper::MakeFolder(" << title
<< ") ");
262 INetURLObject
o(parent
.getURL());
271 bool utl::UCBContentHelper::IsYounger(
272 OUString
const & younger
, OUString
const & older
)
277 content(younger
).getPropertyValue(
279 get
<css::util::DateTime
>())
281 content(older
).getPropertyValue(
283 get
<css::util::DateTime
>());
284 } catch (css::uno::RuntimeException
const &) {
286 } catch (css::ucb::CommandAbortedException
const &) {
287 assert(false && "this cannot happen");
289 } catch (css::uno::Exception
const &) {
290 TOOLS_INFO_EXCEPTION(
291 "unotools.ucbhelper",
292 "UCBContentHelper::IsYounger(" << younger
<< ", " << older
<< ")");
297 bool utl::UCBContentHelper::Exists(OUString
const & url
) {
299 if (osl::FileBase::getSystemPathFromFileURL(url
, pathname
)
300 == osl::FileBase::E_None
)
302 // Try to create a directory entry for the given URL:
304 if (osl::FileBase::getFileURLFromSystemPath(pathname
, url2
)
305 == osl::FileBase::E_None
)
307 // #106526 osl_getDirectoryItem is an existence check, no further
308 // osl_getFileStatus call necessary:
309 osl::DirectoryItem item
;
310 return osl::DirectoryItem::get(url2
, item
) == osl::FileBase::E_None
;
315 // Divide URL into folder and name part:
316 INetURLObject
o(url
);
319 INetURLObject::LAST_SEGMENT
, true,
320 INetURLObject::DecodeMechanism::WithCharset
));
322 o
.removeFinalSlash();
323 std::vector
<OUString
> cs(
324 getContents(o
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)));
325 return std::any_of(cs
.begin(), cs
.end(),
326 [&name
](const OUString
& rItem
) {
327 return INetURLObject(rItem
).
328 getName(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
).
329 equalsIgnoreAsciiCase(name
); });
333 bool utl::UCBContentHelper::IsSubPath(
334 OUString
const & parent
, OUString
const & child
)
336 // The comparison is done in the following way:
337 // - First, compare case sensitively
338 // - If names are different, try a fallback comparing case insensitively
339 // - If the last comparison succeeded, get case preserving normalized names
340 // for the files and compare them
341 // (The second step is required because retrieving the normalized names
342 // might be very expensive in some cases.)
343 INetURLObject
candidate(child
);
344 INetURLObject
folder(parent
);
345 if (candidate
.GetProtocol() != folder
.GetProtocol()) {
348 INetURLObject
candidateLower(child
.toAsciiLowerCase());
349 INetURLObject
folderLower(parent
.toAsciiLowerCase());
353 if (candidate
== folder
354 || (candidate
.GetProtocol() == INetProtocol::File
355 && candidateLower
== folderLower
356 && (getCasePreservingUrl(candidate
)
357 == getCasePreservingUrl(folder
))))
362 } while (candidate
.removeSegment() && candidateLower
.removeSegment()
363 && candidate
!= tmp
);
364 // INetURLObject::removeSegment sometimes returns true without
365 // modifying the URL, e.g., in case of "file:///"
366 } catch (css::uno::RuntimeException
const &) {
368 } catch (css::ucb::CommandAbortedException
const &) {
369 assert(false && "this cannot happen");
371 } catch (css::uno::Exception
const &) {
372 TOOLS_INFO_EXCEPTION(
373 "unotools.ucbhelper",
374 "UCBContentHelper::IsSubPath(" << parent
<< ", " << child
<< ")");
379 bool utl::UCBContentHelper::EqualURLs(
380 OUString
const & url1
, OUString
const & url2
)
382 if (url1
.isEmpty() || url2
.isEmpty()) {
385 css::uno::Reference
< css::ucb::XUniversalContentBroker
> ucb(
386 css::ucb::UniversalContentBroker::create(
387 comphelper::getProcessComponentContext()));
389 ucb
->compareContentIds(
390 ucb
->createContentIdentifier(canonic(url1
)),
391 ucb
->createContentIdentifier(canonic(url2
)))
395 bool utl::UCBContentHelper::ensureFolder(
396 const css::uno::Reference
< css::uno::XComponentContext
>& xCtx
,
397 const css::uno::Reference
< css::ucb::XCommandEnvironment
>& xEnv
,
398 const OUString
& rFolder
, ucbhelper::Content
& result
) throw()
402 INetURLObject
aURL( rFolder
);
403 OUString aTitle
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
404 aURL
.removeSegment();
405 ::ucbhelper::Content aParent
;
407 if ( ::ucbhelper::Content::create( aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
408 xEnv
, xCtx
, aParent
) )
410 return ::utl::UCBContentHelper::MakeFolder(aParent
, aTitle
, result
);
420 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */