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/lang/XMultiServiceFactory.hpp"
26 #include "com/sun/star/sdbc/XResultSet.hpp"
27 #include "com/sun/star/task/XInteractionHandler.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/XContentIdentifier.hpp"
38 #include "com/sun/star/ucb/XProgressHandler.hpp"
39 #include "com/sun/star/ucb/XUniversalContentBroker.hpp"
40 #include "com/sun/star/uno/Any.hxx"
41 #include "com/sun/star/uno/Exception.hpp"
42 #include "com/sun/star/uno/Reference.hxx"
43 #include "com/sun/star/uno/RuntimeException.hpp"
44 #include "com/sun/star/uno/Sequence.hxx"
45 #include "com/sun/star/uno/XComponentContext.hpp"
46 #include "com/sun/star/util/DateTime.hpp"
47 #include "comphelper/processfactory.hxx"
48 #include "cppuhelper/exc_hlp.hxx"
49 #include "osl/file.hxx"
50 #include "rtl/string.h"
51 #include "rtl/ustring.h"
52 #include "rtl/ustring.hxx"
53 #include "sal/log.hxx"
54 #include "sal/types.h"
55 #include "tools/datetime.hxx"
56 #include "tools/urlobj.hxx"
57 #include "ucbhelper/commandenvironment.hxx"
58 #include "ucbhelper/content.hxx"
59 #include "unotools/localfilehelper.hxx"
60 #include "unotools/ucbhelper.hxx"
64 OUString
canonic(OUString
const & url
) {
66 SAL_WARN_IF(o
.HasError(), "unotools.ucbhelper", "Invalid URL \"" << url
<< '"');
67 return o
.GetMainURL(INetURLObject::NO_DECODE
);
70 ucbhelper::Content
content(OUString
const & url
) {
71 return ucbhelper::Content(
73 css::uno::Reference
<css::ucb::XCommandEnvironment
>(),
74 comphelper::getProcessComponentContext());
77 ucbhelper::Content
content(INetURLObject
const & url
) {
78 return ucbhelper::Content(
79 url
.GetMainURL(INetURLObject::NO_DECODE
),
80 css::uno::Reference
<css::ucb::XCommandEnvironment
>(),
81 comphelper::getProcessComponentContext());
84 std::vector
<OUString
> getContents(OUString
const & url
) {
86 std::vector
<OUString
> cs
;
87 ucbhelper::Content
c(content(url
));
88 css::uno::Sequence
<OUString
> args(1);
89 args
[0] = OUString("Title");
90 css::uno::Reference
<css::sdbc::XResultSet
> res(
91 c
.createCursor(args
, ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS
),
92 css::uno::UNO_SET_THROW
);
93 css::uno::Reference
<com::sun::star::ucb::XContentAccess
> acc(
94 res
, css::uno::UNO_QUERY_THROW
);
96 cs
.push_back(acc
->queryContentIdentifierString());
99 } catch (css::uno::RuntimeException
const &) {
101 } catch (css::ucb::CommandAbortedException
const &) {
102 assert(false); // this cannot happen
104 } catch (css::uno::Exception
const &) {
105 css::uno::Any
e(cppu::getCaughtException());
107 "unotools.ucbhelper",
108 "getContents(" << url
<< ") " << e
.getValueType().getTypeName()
109 << " \"" << e
.get
<css::uno::Exception
>().Message
<< '"');
110 return std::vector
<OUString
>();
114 OUString
getCasePreservingUrl(INetURLObject url
) {
116 content(url
).executeCommand(
117 OUString("getCasePreservingURL"),
122 DateTime
convert(css::util::DateTime
const & dt
) {
124 Date(dt
.Day
, dt
.Month
, dt
.Year
),
125 Time(dt
.Hours
, dt
.Minutes
, dt
.Seconds
, dt
.NanoSeconds
));
130 bool utl::UCBContentHelper::IsDocument(OUString
const & url
) {
132 return content(url
).isDocument();
133 } catch (css::uno::RuntimeException
const &) {
135 } catch (css::ucb::CommandAbortedException
const &) {
136 assert(false); // this cannot happen
138 } catch (css::uno::Exception
const &) {
139 css::uno::Any
e(cppu::getCaughtException());
141 "unotools.ucbhelper",
142 "UCBContentHelper::IsDocument(" << url
<< ") "
143 << e
.getValueType().getTypeName() << " \""
144 << e
.get
<css::uno::Exception
>().Message
<< '"');
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 css::uno::Any
e(cppu::getCaughtException());
162 "unotools.ucbhelper",
163 "UCBContentHelper::GetProperty(" << url
<< ", " << property
<< ") "
164 << e
.getValueType().getTypeName() << " \""
165 << e
.get
<css::uno::Exception
>().Message
<< '"');
166 return css::uno::Any();
170 bool utl::UCBContentHelper::IsFolder(OUString
const & url
) {
172 return content(url
).isFolder();
173 } catch (css::uno::RuntimeException
const &) {
175 } catch (css::ucb::CommandAbortedException
const &) {
176 assert(false); // this cannot happen
178 } catch (css::uno::Exception
const &) {
179 css::uno::Any
e(cppu::getCaughtException());
181 "unotools.ucbhelper",
182 "UCBContentHelper::IsFolder(" << url
<< ") "
183 << e
.getValueType().getTypeName() << " \""
184 << e
.get
<css::uno::Exception
>().Message
<< '"');
189 bool utl::UCBContentHelper::GetTitle(
190 OUString
const & url
, OUString
* title
)
194 return content(url
).getPropertyValue(OUString("Title")) >>= *title
;
195 } catch (css::uno::RuntimeException
const &) {
197 } catch (css::ucb::CommandAbortedException
const &) {
198 assert(false); // this cannot happen
200 } catch (css::uno::Exception
const &) {
201 css::uno::Any
e(cppu::getCaughtException());
203 "unotools.ucbhelper",
204 "UCBContentHelper::GetTitle(" << url
<< ") "
205 << e
.getValueType().getTypeName() << " \""
206 << e
.get
<css::uno::Exception
>().Message
<< '"');
211 bool utl::UCBContentHelper::Kill(OUString
const & url
) {
213 content(url
).executeCommand(
215 css::uno::makeAny(true));
217 } catch (css::uno::RuntimeException
const &) {
219 } catch (css::ucb::CommandAbortedException
const &) {
220 assert(false); // this cannot happen
222 } catch (css::uno::Exception
const &) {
223 css::uno::Any
e(cppu::getCaughtException());
225 "unotools.ucbhelper",
226 "UCBContentHelper::Kill(" << url
<< ") "
227 << e
.getValueType().getTypeName() << " \""
228 << e
.get
<css::uno::Exception
>().Message
<< '"');
233 bool utl::UCBContentHelper::MakeFolder(
234 ucbhelper::Content
& parent
, OUString
const & title
,
235 ucbhelper::Content
& result
, bool exclusive
)
239 css::uno::Sequence
<css::ucb::ContentInfo
> info(
240 parent
.queryCreatableContentsInfo());
241 for (sal_Int32 i
= 0; i
< info
.getLength(); ++i
) {
242 // Simply look for the first KIND_FOLDER:
243 if ((info
[i
].Attributes
244 & css::ucb::ContentInfoAttribute::KIND_FOLDER
)
247 // Make sure the only required bootstrap property is "Title":
248 if ( info
[i
].Properties
.getLength() != 1 || info
[i
].Properties
[0].Name
!= "Title" )
252 css::uno::Sequence
<OUString
> keys(1);
253 keys
[0] = OUString("Title");
254 css::uno::Sequence
<css::uno::Any
> values(1);
256 if (parent
.insertNewContent(info
[i
].Type
, keys
, values
, result
))
262 } catch (css::ucb::InteractiveIOException
const & e
) {
263 if (e
.Code
== css::ucb::IOErrorCode_ALREADY_EXISTING
) {
267 "unotools.ucbhelper",
268 "UCBContentHelper::MakeFolder(" << title
269 << ") InteractiveIOException \"" << e
.Message
270 << "\", code " << +e
.Code
);
272 } catch (css::ucb::NameClashException
const &) {
274 } catch (css::uno::RuntimeException
const &) {
276 } catch (css::ucb::CommandAbortedException
const &) {
277 assert(false); // this cannot happen
279 } catch (css::uno::Exception
const &) {
280 css::uno::Any
e(cppu::getCaughtException());
282 "unotools.ucbhelper",
283 "UCBContentHelper::MakeFolder(" << title
<< ") "
284 << e
.getValueType().getTypeName() << " \""
285 << e
.get
<css::uno::Exception
>().Message
<< '"');
287 if (exists
&& !exclusive
) {
288 INetURLObject
o(parent
.getURL());
297 sal_Int64
utl::UCBContentHelper::GetSize(OUString
const & url
) {
300 bool ok
= (content(url
).getPropertyValue(OUString("Size")) >>= n
);
302 !ok
, "unotools.ucbhelper",
303 "UCBContentHelper::GetSize(" << url
304 << "): Size cannot be determined");
306 } catch (css::uno::RuntimeException
const &) {
308 } catch (css::ucb::CommandAbortedException
const &) {
309 assert(false); // this cannot happen
311 } catch (css::uno::Exception
const &) {
312 css::uno::Any
e(cppu::getCaughtException());
314 "unotools.ucbhelper",
315 "UCBContentHelper::GetSize(" << url
<< ") "
316 << e
.getValueType().getTypeName() << " \""
317 << e
.get
<css::uno::Exception
>().Message
<< '"');
322 bool utl::UCBContentHelper::IsYounger(
323 OUString
const & younger
, OUString
const & older
)
328 content(younger
).getPropertyValue(
329 OUString("DateModified")).
330 get
<css::util::DateTime
>())
332 content(older
).getPropertyValue(
333 OUString("DateModified")).
334 get
<css::util::DateTime
>());
335 } catch (css::uno::RuntimeException
const &) {
337 } catch (css::ucb::CommandAbortedException
const &) {
338 assert(false); // this cannot happen
340 } catch (css::uno::Exception
const &) {
341 css::uno::Any
e(cppu::getCaughtException());
343 "unotools.ucbhelper",
344 "UCBContentHelper::IsYounger(" << younger
<< ", " << older
<< ") "
345 << e
.getValueType().getTypeName() << " \""
346 << e
.get
<css::uno::Exception
>().Message
<< '"');
351 bool utl::UCBContentHelper::Exists(OUString
const & url
) {
353 if (utl::LocalFileHelper::ConvertURLToPhysicalName(url
, pathname
)) {
354 // Try to create a directory entry for the given URL:
356 if (osl::FileBase::getFileURLFromSystemPath(pathname
, url2
)
357 == osl::FileBase::E_None
)
359 // #106526 osl_getDirectoryItem is an existence check, no further
360 // osl_getFileStatus call necessary:
361 osl::DirectoryItem item
;
362 return osl::DirectoryItem::get(url2
, item
) == osl::FileBase::E_None
;
367 // Divide URL into folder and name part:
368 INetURLObject
o(url
);
371 INetURLObject::LAST_SEGMENT
, true,
372 INetURLObject::DECODE_WITH_CHARSET
));
374 o
.removeFinalSlash();
375 std::vector
<OUString
> cs(
376 getContents(o
.GetMainURL(INetURLObject::NO_DECODE
)));
377 for (std::vector
<OUString
>::iterator
i(cs
.begin()); i
!= cs
.end();
380 if (INetURLObject(*i
).getName(
381 INetURLObject::LAST_SEGMENT
, true,
382 INetURLObject::DECODE_WITH_CHARSET
).
383 equalsIgnoreAsciiCase(name
))
392 bool utl::UCBContentHelper::IsSubPath(
393 OUString
const & parent
, OUString
const & child
)
395 // The comparison is done in the following way:
396 // - First, compare case sensitively
397 // - If names are different, try a fallback comparing case insensitively
398 // - If the last comparison succeeded, get case preserving normalized names
399 // for the files and compare them
400 // (The second step is required because retrieving the normalized names
401 // might be very expensive in some cases.)
402 INetURLObject
candidate(child
);
403 INetURLObject
folder(parent
);
404 if (candidate
.GetProtocol() != folder
.GetProtocol()) {
407 INetURLObject
candidateLower(child
.toAsciiLowerCase());
408 INetURLObject
folderLower(parent
.toAsciiLowerCase());
412 if (candidate
== folder
413 || (candidate
.GetProtocol() == INET_PROT_FILE
414 && candidateLower
== folderLower
415 && (getCasePreservingUrl(candidate
)
416 == getCasePreservingUrl(folder
))))
421 } while (candidate
.removeSegment() && candidateLower
.removeSegment()
422 && candidate
!= tmp
);
423 // INetURLObject::removeSegment sometimes returns true without
424 // modifying the URL, e.g., in case of "file:///"
425 } catch (css::uno::RuntimeException
const &) {
427 } catch (css::ucb::CommandAbortedException
const &) {
428 assert(false); // this cannot happen
430 } catch (css::uno::Exception
const &) {
431 css::uno::Any
e(cppu::getCaughtException());
433 "unotools.ucbhelper",
434 "UCBContentHelper::IsSubPath(" << parent
<< ", " << child
<< ") "
435 << e
.getValueType().getTypeName() << " \""
436 << e
.get
<css::uno::Exception
>().Message
<< '"');
441 bool utl::UCBContentHelper::EqualURLs(
442 OUString
const & url1
, OUString
const & url2
)
444 if (url1
.isEmpty() || url2
.isEmpty()) {
447 css::uno::Reference
< css::ucb::XUniversalContentBroker
> ucb(
448 css::ucb::UniversalContentBroker::create(
449 comphelper::getProcessComponentContext()));
451 ucb
->compareContentIds(
452 ucb
->createContentIdentifier(canonic(url1
)),
453 ucb
->createContentIdentifier(canonic(url2
)))
457 bool utl::UCBContentHelper::ensureFolder(
458 css::uno::Reference
< css::uno::XComponentContext
> xCtx
,
459 css::uno::Reference
< css::ucb::XCommandEnvironment
> xEnv
,
460 const OUString
& rFolder
, ucbhelper::Content
& result
)
462 INetURLObject
aURL( rFolder
);
463 OUString aTitle
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DECODE_WITH_CHARSET
);
464 aURL
.removeSegment();
465 ::ucbhelper::Content aParent
;
467 if ( ::ucbhelper::Content::create( aURL
.GetMainURL( INetURLObject::NO_DECODE
),
468 xEnv
, xCtx
, aParent
) )
470 return ::utl::UCBContentHelper::MakeFolder(aParent
, aTitle
, result
);
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */