update credits
[LibreOffice.git] / unotools / source / ucbhelper / ucbhelper.cxx
blob2dc2bffa684c68204ac1d87a37ba80f2609f4b09
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/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"
62 namespace {
64 OUString canonic(OUString const & url) {
65 INetURLObject o(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(
72 canonic(url),
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) {
85 try {
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);
95 while (res->next()) {
96 cs.push_back(acc->queryContentIdentifierString());
98 return cs;
99 } catch (css::uno::RuntimeException const &) {
100 throw;
101 } catch (css::ucb::CommandAbortedException const &) {
102 assert(false); // this cannot happen
103 throw;
104 } catch (css::uno::Exception const &) {
105 css::uno::Any e(cppu::getCaughtException());
106 SAL_INFO(
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) {
115 return
116 content(url).executeCommand(
117 OUString("getCasePreservingURL"),
118 css::uno::Any()).
119 get<OUString>();
122 DateTime convert(css::util::DateTime const & dt) {
123 return DateTime(
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) {
131 try {
132 return content(url).isDocument();
133 } catch (css::uno::RuntimeException const &) {
134 throw;
135 } catch (css::ucb::CommandAbortedException const &) {
136 assert(false); // this cannot happen
137 throw;
138 } catch (css::uno::Exception const &) {
139 css::uno::Any e(cppu::getCaughtException());
140 SAL_INFO(
141 "unotools.ucbhelper",
142 "UCBContentHelper::IsDocument(" << url << ") "
143 << e.getValueType().getTypeName() << " \""
144 << e.get<css::uno::Exception>().Message << '"');
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 css::uno::Any e(cppu::getCaughtException());
161 SAL_INFO(
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) {
171 try {
172 return content(url).isFolder();
173 } catch (css::uno::RuntimeException const &) {
174 throw;
175 } catch (css::ucb::CommandAbortedException const &) {
176 assert(false); // this cannot happen
177 throw;
178 } catch (css::uno::Exception const &) {
179 css::uno::Any e(cppu::getCaughtException());
180 SAL_INFO(
181 "unotools.ucbhelper",
182 "UCBContentHelper::IsFolder(" << url << ") "
183 << e.getValueType().getTypeName() << " \""
184 << e.get<css::uno::Exception>().Message << '"');
185 return false;
189 bool utl::UCBContentHelper::GetTitle(
190 OUString const & url, OUString * title)
192 assert(title != 0);
193 try {
194 return content(url).getPropertyValue(OUString("Title")) >>= *title;
195 } catch (css::uno::RuntimeException const &) {
196 throw;
197 } catch (css::ucb::CommandAbortedException const &) {
198 assert(false); // this cannot happen
199 throw;
200 } catch (css::uno::Exception const &) {
201 css::uno::Any e(cppu::getCaughtException());
202 SAL_INFO(
203 "unotools.ucbhelper",
204 "UCBContentHelper::GetTitle(" << url << ") "
205 << e.getValueType().getTypeName() << " \""
206 << e.get<css::uno::Exception>().Message << '"');
207 return false;
211 bool utl::UCBContentHelper::Kill(OUString const & url) {
212 try {
213 content(url).executeCommand(
214 OUString("delete"),
215 css::uno::makeAny(true));
216 return true;
217 } catch (css::uno::RuntimeException const &) {
218 throw;
219 } catch (css::ucb::CommandAbortedException const &) {
220 assert(false); // this cannot happen
221 throw;
222 } catch (css::uno::Exception const &) {
223 css::uno::Any e(cppu::getCaughtException());
224 SAL_INFO(
225 "unotools.ucbhelper",
226 "UCBContentHelper::Kill(" << url << ") "
227 << e.getValueType().getTypeName() << " \""
228 << e.get<css::uno::Exception>().Message << '"');
229 return false;
233 bool utl::UCBContentHelper::MakeFolder(
234 ucbhelper::Content & parent, OUString const & title,
235 ucbhelper::Content & result, bool exclusive)
237 bool exists = false;
238 try {
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)
245 != 0)
247 // Make sure the only required bootstrap property is "Title":
248 if ( info[i].Properties.getLength() != 1 || info[i].Properties[0].Name != "Title" )
250 continue;
252 css::uno::Sequence<OUString> keys(1);
253 keys[0] = OUString("Title");
254 css::uno::Sequence<css::uno::Any> values(1);
255 values[0] <<= title;
256 if (parent.insertNewContent(info[i].Type, keys, values, result))
258 return true;
262 } catch (css::ucb::InteractiveIOException const & e) {
263 if (e.Code == css::ucb::IOErrorCode_ALREADY_EXISTING) {
264 exists = true;
265 } else {
266 SAL_INFO(
267 "unotools.ucbhelper",
268 "UCBContentHelper::MakeFolder(" << title
269 << ") InteractiveIOException \"" << e.Message
270 << "\", code " << +e.Code);
272 } catch (css::ucb::NameClashException const &) {
273 exists = true;
274 } catch (css::uno::RuntimeException const &) {
275 throw;
276 } catch (css::ucb::CommandAbortedException const &) {
277 assert(false); // this cannot happen
278 throw;
279 } catch (css::uno::Exception const &) {
280 css::uno::Any e(cppu::getCaughtException());
281 SAL_INFO(
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());
289 o.Append(title);
290 result = content(o);
291 return true;
292 } else {
293 return false;
297 sal_Int64 utl::UCBContentHelper::GetSize(OUString const & url) {
298 try {
299 sal_Int64 n = 0;
300 bool ok = (content(url).getPropertyValue(OUString("Size")) >>= n);
301 SAL_INFO_IF(
302 !ok, "unotools.ucbhelper",
303 "UCBContentHelper::GetSize(" << url
304 << "): Size cannot be determined");
305 return n;
306 } catch (css::uno::RuntimeException const &) {
307 throw;
308 } catch (css::ucb::CommandAbortedException const &) {
309 assert(false); // this cannot happen
310 throw;
311 } catch (css::uno::Exception const &) {
312 css::uno::Any e(cppu::getCaughtException());
313 SAL_INFO(
314 "unotools.ucbhelper",
315 "UCBContentHelper::GetSize(" << url << ") "
316 << e.getValueType().getTypeName() << " \""
317 << e.get<css::uno::Exception>().Message << '"');
318 return 0;
322 bool utl::UCBContentHelper::IsYounger(
323 OUString const & younger, OUString const & older)
325 try {
326 return
327 convert(
328 content(younger).getPropertyValue(
329 OUString("DateModified")).
330 get<css::util::DateTime>())
331 > convert(
332 content(older).getPropertyValue(
333 OUString("DateModified")).
334 get<css::util::DateTime>());
335 } catch (css::uno::RuntimeException const &) {
336 throw;
337 } catch (css::ucb::CommandAbortedException const &) {
338 assert(false); // this cannot happen
339 throw;
340 } catch (css::uno::Exception const &) {
341 css::uno::Any e(cppu::getCaughtException());
342 SAL_INFO(
343 "unotools.ucbhelper",
344 "UCBContentHelper::IsYounger(" << younger << ", " << older << ") "
345 << e.getValueType().getTypeName() << " \""
346 << e.get<css::uno::Exception>().Message << '"');
347 return false;
351 bool utl::UCBContentHelper::Exists(OUString const & url) {
352 OUString pathname;
353 if (utl::LocalFileHelper::ConvertURLToPhysicalName(url, pathname)) {
354 // Try to create a directory entry for the given URL:
355 OUString url2;
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;
363 } else {
364 return false;
366 } else {
367 // Divide URL into folder and name part:
368 INetURLObject o(url);
369 OUString name(
370 o.getName(
371 INetURLObject::LAST_SEGMENT, true,
372 INetURLObject::DECODE_WITH_CHARSET));
373 o.removeSegment();
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();
378 ++i)
380 if (INetURLObject(*i).getName(
381 INetURLObject::LAST_SEGMENT, true,
382 INetURLObject::DECODE_WITH_CHARSET).
383 equalsIgnoreAsciiCase(name))
385 return true;
388 return false;
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()) {
405 return false;
407 INetURLObject candidateLower(child.toAsciiLowerCase());
408 INetURLObject folderLower(parent.toAsciiLowerCase());
409 try {
410 INetURLObject tmp;
411 do {
412 if (candidate == folder
413 || (candidate.GetProtocol() == INET_PROT_FILE
414 && candidateLower == folderLower
415 && (getCasePreservingUrl(candidate)
416 == getCasePreservingUrl(folder))))
418 return true;
420 tmp = candidate;
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 &) {
426 throw;
427 } catch (css::ucb::CommandAbortedException const &) {
428 assert(false); // this cannot happen
429 throw;
430 } catch (css::uno::Exception const &) {
431 css::uno::Any e(cppu::getCaughtException());
432 SAL_INFO(
433 "unotools.ucbhelper",
434 "UCBContentHelper::IsSubPath(" << parent << ", " << child << ") "
435 << e.getValueType().getTypeName() << " \""
436 << e.get<css::uno::Exception>().Message << '"');
438 return false;
441 bool utl::UCBContentHelper::EqualURLs(
442 OUString const & url1, OUString const & url2)
444 if (url1.isEmpty() || url2.isEmpty()) {
445 return false;
447 css::uno::Reference< css::ucb::XUniversalContentBroker > ucb(
448 css::ucb::UniversalContentBroker::create(
449 comphelper::getProcessComponentContext()));
450 return
451 ucb->compareContentIds(
452 ucb->createContentIdentifier(canonic(url1)),
453 ucb->createContentIdentifier(canonic(url2)))
454 == 0;
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);
473 return false;
476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */