Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / unotools / source / ucbhelper / ucbhelper.cxx
blobaaa9d214bb2b82438f018fac3baf3236adcf3a4b
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 <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; }
59 namespace {
61 OUString canonic(OUString const & url) {
62 INetURLObject o(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(
69 canonic(url),
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) {
82 try {
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);
88 while (res->next()) {
89 cs.push_back(acc->queryContentIdentifierString());
91 return cs;
92 } catch (css::uno::RuntimeException const &) {
93 throw;
94 } catch (css::ucb::CommandAbortedException const &) {
95 assert(false && "this cannot happen");
96 throw;
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) {
104 return
105 content(url).executeCommand(
106 "getCasePreservingURL",
107 css::uno::Any()).
108 get<OUString>();
111 DateTime convert(css::util::DateTime const & dt) {
112 return DateTime(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 );
128 return pCommandEnv;
131 bool utl::UCBContentHelper::IsDocument(OUString const & url) {
132 try {
133 return content(url).isDocument();
134 } catch (css::uno::RuntimeException const &) {
135 throw;
136 } catch (css::ucb::CommandAbortedException const &) {
137 assert(false && "this cannot happen");
138 throw;
139 } catch (css::uno::Exception const &) {
140 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url << ")");
141 return false;
145 css::uno::Any utl::UCBContentHelper::GetProperty(
146 OUString const & url, OUString const & property)
148 try {
149 return content(url).getPropertyValue(property);
150 } catch (css::uno::RuntimeException const &) {
151 throw;
152 } catch (css::ucb::CommandAbortedException const &) {
153 assert(false && "this cannot happen");
154 throw;
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) {
162 try {
163 return content(url).isFolder();
164 } catch (css::uno::RuntimeException const &) {
165 throw;
166 } catch (css::ucb::CommandAbortedException const &) {
167 assert(false && "this cannot happen");
168 throw;
169 } catch (css::uno::Exception const &) {
170 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url << ")");
171 return false;
175 bool utl::UCBContentHelper::GetTitle(
176 OUString const & url, OUString * title)
178 assert(title != nullptr);
179 try {
180 return content(url).getPropertyValue("Title") >>= *title;
181 } catch (css::uno::RuntimeException const &) {
182 throw;
183 } catch (css::ucb::CommandAbortedException const &) {
184 assert(false && "this cannot happen");
185 throw;
186 } catch (css::uno::Exception const &) {
187 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url << ")");
188 return false;
192 bool utl::UCBContentHelper::Kill(OUString const & url) {
193 try {
194 content(url).executeCommand(
195 "delete",
196 css::uno::Any(true));
197 return true;
198 } catch (css::uno::RuntimeException const &) {
199 throw;
200 } catch (css::ucb::CommandAbortedException const &) {
201 assert(false && "this cannot happen");
202 throw;
203 } catch (css::uno::Exception const &) {
204 TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url << ")");
205 return false;
209 bool utl::UCBContentHelper::MakeFolder(
210 ucbhelper::Content & parent, OUString const & title,
211 ucbhelper::Content & result)
213 bool exists = false;
214 try {
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)
221 != 0)
223 // Make sure the only required bootstrap property is "Title":
224 if ( rInfo.Properties.getLength() != 1 || rInfo.Properties[0].Name != "Title" )
226 continue;
228 if (parent.insertNewContent(rInfo.Type, { "Title" }, { css::uno::Any(title) }, result))
230 return true;
234 } catch (css::ucb::InteractiveIOException const & e) {
235 if (e.Code == css::ucb::IOErrorCode_ALREADY_EXISTING) {
236 exists = true;
237 } else {
238 TOOLS_INFO_EXCEPTION(
239 "unotools.ucbhelper",
240 "UCBContentHelper::MakeFolder(" << title << ")");
242 } catch (css::ucb::NameClashException const &) {
243 exists = true;
244 } catch (css::uno::RuntimeException const &) {
245 throw;
246 } catch (css::ucb::CommandAbortedException const &) {
247 assert(false && "this cannot happen");
248 throw;
249 } catch (css::uno::Exception const &) {
250 TOOLS_INFO_EXCEPTION(
251 "unotools.ucbhelper",
252 "UCBContentHelper::MakeFolder(" << title << ") ");
254 if (exists) {
255 INetURLObject o(parent.getURL());
256 o.Append(title);
257 result = content(o);
258 return true;
259 } else {
260 return false;
264 bool utl::UCBContentHelper::IsYounger(
265 OUString const & younger, OUString const & older)
267 try {
268 return
269 convert(
270 content(younger).getPropertyValue(
271 "DateModified").
272 get<css::util::DateTime>())
273 > convert(
274 content(older).getPropertyValue(
275 "DateModified").
276 get<css::util::DateTime>());
277 } catch (css::uno::RuntimeException const &) {
278 throw;
279 } catch (css::ucb::CommandAbortedException const &) {
280 assert(false && "this cannot happen");
281 throw;
282 } catch (css::uno::Exception const &) {
283 TOOLS_INFO_EXCEPTION(
284 "unotools.ucbhelper",
285 "UCBContentHelper::IsYounger(" << younger << ", " << older << ")");
286 return false;
290 bool utl::UCBContentHelper::Exists(OUString const & url) {
291 OUString pathname;
292 if (osl::FileBase::getSystemPathFromFileURL(url, pathname)
293 == osl::FileBase::E_None)
295 // Try to create a directory entry for the given URL:
296 OUString url2;
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;
304 } else {
305 return false;
307 } else {
308 // Divide URL into folder and name part:
309 INetURLObject o(url);
310 OUString name(
311 o.getName(
312 INetURLObject::LAST_SEGMENT, true,
313 INetURLObject::DecodeMechanism::WithCharset));
314 o.removeSegment();
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()) {
339 return false;
341 INetURLObject candidateLower(child.toAsciiLowerCase());
342 INetURLObject folderLower(parent.toAsciiLowerCase());
343 try {
344 INetURLObject tmp;
345 do {
346 if (candidate == folder
347 || (candidate.GetProtocol() == INetProtocol::File
348 && candidateLower == folderLower
349 && (getCasePreservingUrl(candidate)
350 == getCasePreservingUrl(folder))))
352 return true;
354 tmp = candidate;
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 &) {
360 throw;
361 } catch (css::ucb::CommandAbortedException const &) {
362 assert(false && "this cannot happen");
363 throw;
364 } catch (css::uno::Exception const &) {
365 TOOLS_INFO_EXCEPTION(
366 "unotools.ucbhelper",
367 "UCBContentHelper::IsSubPath(" << parent << ", " << child << ")");
369 return false;
372 bool utl::UCBContentHelper::EqualURLs(
373 OUString const & url1, OUString const & url2)
375 if (url1.isEmpty() || url2.isEmpty()) {
376 return false;
378 css::uno::Reference< css::ucb::XUniversalContentBroker > ucb(
379 css::ucb::UniversalContentBroker::create(
380 comphelper::getProcessComponentContext()));
381 return
382 ucb->compareContentIds(
383 ucb->createContentIdentifier(canonic(url1)),
384 ucb->createContentIdentifier(canonic(url2)))
385 == 0;
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);
406 catch (...)
410 return false;
413 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */