Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / unotools / source / ucbhelper / ucbhelper.cxx
blob7916259f1919062d1dc93274a81fee7c0e24babd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cassert>
23 #include <vector>
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; }
61 namespace {
63 OUString canonic(OUString const & url) {
64 INetURLObject o(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(
71 canonic(url),
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) {
84 try {
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);
90 while (res->next()) {
91 cs.push_back(acc->queryContentIdentifierString());
93 return cs;
94 } catch (css::uno::RuntimeException const &) {
95 throw;
96 } catch (css::ucb::CommandAbortedException const &) {
97 assert(false && "this cannot happen");
98 throw;
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) {
106 return
107 content(url).executeCommand(
108 "getCasePreservingURL",
109 css::uno::Any()).
110 get<OUString>();
113 DateTime convert(css::util::DateTime const & dt) {
114 return DateTime(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 );
132 return xEnv;
135 bool utl::UCBContentHelper::IsDocument(OUString const & url) {
136 try {
137 return content(url).isDocument();
138 } catch (css::uno::RuntimeException const &) {
139 throw;
140 } catch (css::ucb::CommandAbortedException const &) {
141 assert(false && "this cannot happen");
142 throw;
143 } catch (css::uno::Exception const &) {
144 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url << ")");
145 return false;
149 css::uno::Any utl::UCBContentHelper::GetProperty(
150 OUString const & url, OUString const & property)
152 try {
153 return content(url).getPropertyValue(property);
154 } catch (css::uno::RuntimeException const &) {
155 throw;
156 } catch (css::ucb::CommandAbortedException const &) {
157 assert(false && "this cannot happen");
158 throw;
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) {
166 try {
167 return content(url).isFolder();
168 } catch (css::uno::RuntimeException const &) {
169 throw;
170 } catch (css::ucb::CommandAbortedException const &) {
171 assert(false && "this cannot happen");
172 throw;
173 } catch (css::uno::Exception const &) {
174 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url << ")");
175 return false;
179 bool utl::UCBContentHelper::GetTitle(
180 OUString const & url, OUString * title)
182 assert(title != nullptr);
183 try {
184 return content(url).getPropertyValue("Title") >>= *title;
185 } catch (css::uno::RuntimeException const &) {
186 throw;
187 } catch (css::ucb::CommandAbortedException const &) {
188 assert(false && "this cannot happen");
189 throw;
190 } catch (css::uno::Exception const &) {
191 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url << ")");
192 return false;
196 bool utl::UCBContentHelper::Kill(OUString const & url) {
197 try {
198 content(url).executeCommand(
199 "delete",
200 css::uno::makeAny(true));
201 return true;
202 } catch (css::uno::RuntimeException const &) {
203 throw;
204 } catch (css::ucb::CommandAbortedException const &) {
205 assert(false && "this cannot happen");
206 throw;
207 } catch (css::uno::Exception const &) {
208 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url << ")");
209 return false;
213 bool utl::UCBContentHelper::MakeFolder(
214 ucbhelper::Content & parent, OUString const & title,
215 ucbhelper::Content & result)
217 bool exists = false;
218 try {
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)
225 != 0)
227 // Make sure the only required bootstrap property is "Title":
228 if ( rInfo.Properties.getLength() != 1 || rInfo.Properties[0].Name != "Title" )
230 continue;
232 css::uno::Sequence<OUString> keys { "Title" };
233 css::uno::Sequence<css::uno::Any> values(1);
234 values[0] <<= title;
235 if (parent.insertNewContent(rInfo.Type, keys, values, result))
237 return true;
241 } catch (css::ucb::InteractiveIOException const & e) {
242 if (e.Code == css::ucb::IOErrorCode_ALREADY_EXISTING) {
243 exists = true;
244 } else {
245 TOOLS_INFO_EXCEPTION(
246 "unotools.ucbhelper",
247 "UCBContentHelper::MakeFolder(" << title << ")");
249 } catch (css::ucb::NameClashException const &) {
250 exists = true;
251 } catch (css::uno::RuntimeException const &) {
252 throw;
253 } catch (css::ucb::CommandAbortedException const &) {
254 assert(false && "this cannot happen");
255 throw;
256 } catch (css::uno::Exception const &) {
257 TOOLS_INFO_EXCEPTION(
258 "unotools.ucbhelper",
259 "UCBContentHelper::MakeFolder(" << title << ") ");
261 if (exists) {
262 INetURLObject o(parent.getURL());
263 o.Append(title);
264 result = content(o);
265 return true;
266 } else {
267 return false;
271 bool utl::UCBContentHelper::IsYounger(
272 OUString const & younger, OUString const & older)
274 try {
275 return
276 convert(
277 content(younger).getPropertyValue(
278 "DateModified").
279 get<css::util::DateTime>())
280 > convert(
281 content(older).getPropertyValue(
282 "DateModified").
283 get<css::util::DateTime>());
284 } catch (css::uno::RuntimeException const &) {
285 throw;
286 } catch (css::ucb::CommandAbortedException const &) {
287 assert(false && "this cannot happen");
288 throw;
289 } catch (css::uno::Exception const &) {
290 TOOLS_INFO_EXCEPTION(
291 "unotools.ucbhelper",
292 "UCBContentHelper::IsYounger(" << younger << ", " << older << ")");
293 return false;
297 bool utl::UCBContentHelper::Exists(OUString const & url) {
298 OUString pathname;
299 if (osl::FileBase::getSystemPathFromFileURL(url, pathname)
300 == osl::FileBase::E_None)
302 // Try to create a directory entry for the given URL:
303 OUString url2;
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;
311 } else {
312 return false;
314 } else {
315 // Divide URL into folder and name part:
316 INetURLObject o(url);
317 OUString name(
318 o.getName(
319 INetURLObject::LAST_SEGMENT, true,
320 INetURLObject::DecodeMechanism::WithCharset));
321 o.removeSegment();
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()) {
346 return false;
348 INetURLObject candidateLower(child.toAsciiLowerCase());
349 INetURLObject folderLower(parent.toAsciiLowerCase());
350 try {
351 INetURLObject tmp;
352 do {
353 if (candidate == folder
354 || (candidate.GetProtocol() == INetProtocol::File
355 && candidateLower == folderLower
356 && (getCasePreservingUrl(candidate)
357 == getCasePreservingUrl(folder))))
359 return true;
361 tmp = candidate;
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 &) {
367 throw;
368 } catch (css::ucb::CommandAbortedException const &) {
369 assert(false && "this cannot happen");
370 throw;
371 } catch (css::uno::Exception const &) {
372 TOOLS_INFO_EXCEPTION(
373 "unotools.ucbhelper",
374 "UCBContentHelper::IsSubPath(" << parent << ", " << child << ")");
376 return false;
379 bool utl::UCBContentHelper::EqualURLs(
380 OUString const & url1, OUString const & url2)
382 if (url1.isEmpty() || url2.isEmpty()) {
383 return false;
385 css::uno::Reference< css::ucb::XUniversalContentBroker > ucb(
386 css::ucb::UniversalContentBroker::create(
387 comphelper::getProcessComponentContext()));
388 return
389 ucb->compareContentIds(
390 ucb->createContentIdentifier(canonic(url1)),
391 ucb->createContentIdentifier(canonic(url2)))
392 == 0;
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);
413 catch (...)
417 return false;
420 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */