Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / ucb / source / core / ucbcmds.cxx
blobb0264080de00f683aa7d36c9cebc689d327634d9
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 <osl/diagnose.h>
21 #include <comphelper/propertysequence.hxx>
22 #include <cppuhelper/implbase.hxx>
23 #include <cppuhelper/exc_hlp.hxx>
24 #include <rtl/ustring.hxx>
25 #include <com/sun/star/uno/XInterface.hpp>
26 #include <com/sun/star/beans/PropertyState.hpp>
27 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <com/sun/star/beans/XPropertySetInfo.hpp>
29 #include <com/sun/star/io/IOException.hpp>
30 #include <com/sun/star/io/Pipe.hpp>
31 #include <com/sun/star/io/XActiveDataSink.hpp>
32 #include <com/sun/star/io/XOutputStream.hpp>
33 #include <com/sun/star/io/XSeekable.hpp>
34 #include <com/sun/star/lang/IllegalArgumentException.hpp>
35 #include <com/sun/star/sdbc/SQLException.hpp>
36 #include <com/sun/star/sdbc/XRow.hpp>
37 #include <com/sun/star/task/XInteractionHandler.hpp>
38 #include <com/sun/star/ucb/CommandEnvironment.hpp>
39 #include <com/sun/star/ucb/CommandFailedException.hpp>
40 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
41 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
42 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
43 #include <com/sun/star/ucb/InsertCommandArgument2.hpp>
44 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
45 #include <com/sun/star/ucb/NameClash.hpp>
46 #include <com/sun/star/ucb/NameClashException.hpp>
47 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
48 #include <com/sun/star/ucb/OpenMode.hpp>
49 #include <com/sun/star/ucb/TransferInfo2.hpp>
50 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
51 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
52 #include <com/sun/star/ucb/XCommandInfo.hpp>
53 #include <com/sun/star/ucb/XContentAccess.hpp>
54 #include <com/sun/star/ucb/XContentCreator.hpp>
55 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
56 #include <com/sun/star/ucb/XInteractionSupplyName.hpp>
57 #include <com/sun/star/uno/Any.hxx>
58 #include <com/sun/star/uno/Sequence.hxx>
59 #include <ucbhelper/cancelcommandexecution.hxx>
60 #include <ucbhelper/simplenameclashresolverequest.hxx>
61 #include <utility>
62 #include "ucbcmds.hxx"
63 #include "ucb.hxx"
65 using namespace com::sun::star;
67 namespace
69 // Helper to provide defaults for type and attributes (save some typing)
70 beans::Property makeProperty(const OUString& n, sal_Int32 h, uno::Type t = {}, sal_Int16 a = {})
72 return { n, h, t, a };
75 // struct TransferCommandContext.
78 struct TransferCommandContext
80 uno::Reference< uno::XComponentContext > m_xContext;
81 uno::Reference< ucb::XCommandProcessor > xProcessor;
82 uno::Reference< ucb::XCommandEnvironment > xEnv;
83 uno::Reference< ucb::XCommandEnvironment > xOrigEnv;
84 ucb::GlobalTransferCommandArgument2 aArg;
86 TransferCommandContext(
87 uno::Reference< uno::XComponentContext > xContext,
88 uno::Reference< ucb::XCommandProcessor > _xProcessor,
89 uno::Reference< ucb::XCommandEnvironment > _xEnv,
90 uno::Reference< ucb::XCommandEnvironment > _xOrigEnv,
91 ucb::GlobalTransferCommandArgument2 _aArg )
92 : m_xContext(std::move( xContext )), xProcessor(std::move( _xProcessor )), xEnv(std::move( _xEnv )),
93 xOrigEnv(std::move( _xOrigEnv )), aArg(std::move( _aArg )) {}
99 class InteractionHandlerProxy :
100 public cppu::WeakImplHelper< task::XInteractionHandler >
102 uno::Reference< task::XInteractionHandler > m_xOrig;
104 public:
105 explicit InteractionHandlerProxy(
106 uno::Reference< task::XInteractionHandler > xOrig )
107 : m_xOrig(std::move( xOrig )) {}
109 // XInteractionHandler methods.
110 virtual void SAL_CALL handle(
111 const uno::Reference< task::XInteractionRequest >& Request ) override;
115 // virtual
116 void SAL_CALL InteractionHandlerProxy::handle(
117 const uno::Reference< task::XInteractionRequest >& Request )
119 if ( !m_xOrig.is() )
120 return;
122 // Filter unwanted requests by just not handling them.
123 uno::Any aRequest = Request->getRequest();
125 // "transfer"
126 ucb::InteractiveBadTransferURLException aBadTransferURLEx;
127 if ( aRequest >>= aBadTransferURLEx )
129 return;
131 else
133 // "transfer"
134 ucb::UnsupportedNameClashException aUnsupportedNameClashEx;
135 if ( aRequest >>= aUnsupportedNameClashEx )
137 if ( aUnsupportedNameClashEx.NameClash
138 != ucb::NameClash::ERROR )
139 return;
141 else
143 // "insert"
144 ucb::NameClashException aNameClashEx;
145 if ( aRequest >>= aNameClashEx )
147 return;
149 else
151 // "transfer"
152 ucb::UnsupportedCommandException aUnsupportedCommandEx;
153 if ( aRequest >>= aUnsupportedCommandEx )
155 return;
161 // not filtered; let the original handler do the work.
162 m_xOrig->handle( Request );
168 class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink >
170 uno::Reference< io::XInputStream > m_xStream;
172 public:
173 // XActiveDataSink methods.
174 virtual void SAL_CALL setInputStream(
175 const uno::Reference< io::XInputStream >& aStream ) override;
176 virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override;
180 // virtual
181 void SAL_CALL ActiveDataSink::setInputStream(
182 const uno::Reference< io::XInputStream >& aStream )
184 m_xStream = aStream;
188 // virtual
189 uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream()
191 return m_xStream;
197 class CommandProcessorInfo :
198 public cppu::WeakImplHelper< ucb::XCommandInfo >
200 uno::Sequence< ucb::CommandInfo > m_xInfo;
202 public:
203 CommandProcessorInfo();
205 // XCommandInfo methods
206 virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() override;
207 virtual ucb::CommandInfo SAL_CALL
208 getCommandInfoByName( const OUString& Name ) override;
209 virtual ucb::CommandInfo SAL_CALL
210 getCommandInfoByHandle( sal_Int32 Handle ) override;
211 virtual sal_Bool SAL_CALL hasCommandByName( const OUString& Name ) override;
212 virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) override;
216 CommandProcessorInfo::CommandProcessorInfo()
217 : m_xInfo{
218 ucb::CommandInfo(
219 GETCOMMANDINFO_NAME, // Name
220 GETCOMMANDINFO_HANDLE, // Handle
221 cppu::UnoType<void>::get() ), // ArgType
222 ucb::CommandInfo(
223 GLOBALTRANSFER_NAME, // Name
224 GLOBALTRANSFER_HANDLE, // Handle
225 cppu::UnoType<ucb::GlobalTransferCommandArgument>::get() ), // ArgType
226 ucb::CommandInfo(
227 CHECKIN_NAME, // Name
228 CHECKIN_HANDLE, // Handle
229 cppu::UnoType<ucb::CheckinArgument>::get() ) } // ArgType
234 // virtual
235 uno::Sequence< ucb::CommandInfo > SAL_CALL
236 CommandProcessorInfo::getCommands()
238 return m_xInfo;
242 // virtual
243 ucb::CommandInfo SAL_CALL
244 CommandProcessorInfo::getCommandInfoByName( const OUString& Name )
246 auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo),
247 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
248 if (pInfo != std::cend(m_xInfo))
249 return *pInfo;
251 throw ucb::UnsupportedCommandException();
255 // virtual
256 ucb::CommandInfo SAL_CALL
257 CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle )
259 auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo),
260 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
261 if (pInfo != std::cend(m_xInfo))
262 return *pInfo;
264 throw ucb::UnsupportedCommandException();
268 // virtual
269 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName(
270 const OUString& Name )
272 return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo),
273 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
277 // virtual
278 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle )
280 return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo),
281 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
285 OUString createDesiredName(
286 const OUString & rSourceURL, const OUString & rNewTitle )
288 OUString aName( rNewTitle );
289 if ( aName.isEmpty() )
291 // calculate name using source URL
293 // @@@ It's not guaranteed that slashes contained in the URL are
294 // actually path separators. This depends on the fact whether the
295 // URL is hierarchical. Only then the slashes are path separators.
296 // Therefore this algorithm is not guaranteed to work! But, ATM
297 // I don't know a better solution. It would have been better to
298 // have a member for the clashing name in
299 // UnsupportedNameClashException...
301 sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' );
302 bool bTrailingSlash = false;
303 if ( nLastSlash == rSourceURL.getLength() - 1 )
305 nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash );
306 bTrailingSlash = true;
309 if ( nLastSlash != -1 )
311 if ( bTrailingSlash )
312 aName = rSourceURL.copy(
313 nLastSlash + 1,
314 rSourceURL.getLength() - nLastSlash - 2 );
315 else
316 aName = rSourceURL.copy( nLastSlash + 1 );
318 else
320 aName = rSourceURL;
323 // query, fragment present?
324 sal_Int32 nPos = aName.indexOf( '?' );
325 if ( nPos == -1 )
326 nPos = aName.indexOf( '#' );
328 if ( nPos != -1 )
329 aName = aName.copy( 0, nPos );
331 return aName;
334 OUString createDesiredName(
335 const ucb::GlobalTransferCommandArgument & rArg )
337 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
340 OUString createDesiredName(
341 const ucb::TransferInfo & rArg )
343 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
347 enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN };
349 NameClashContinuation interactiveNameClashResolve(
350 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
351 const OUString & rTargetURL,
352 const OUString & rClashingName,
353 /* [out] */ uno::Any & rException,
354 /* [out] */ OUString & rNewName )
356 rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest(
357 new ucbhelper::SimpleNameClashResolveRequest(
358 rTargetURL, // target folder URL
359 rClashingName
360 ) );
362 rException = xRequest->getRequest();
363 if ( xEnv.is() )
365 uno::Reference< task::XInteractionHandler > xIH
366 = xEnv->getInteractionHandler();
367 if ( xIH.is() )
370 xIH->handle( xRequest );
372 rtl::Reference< ucbhelper::InteractionContinuation >
373 xSelection( xRequest->getSelection() );
375 if ( xSelection.is() )
377 // Handler handled the request.
378 uno::Reference< task::XInteractionAbort > xAbort(
379 xSelection.get(), uno::UNO_QUERY );
380 if ( xAbort.is() )
382 // Abort.
383 return ABORT;
385 else
387 uno::Reference<
388 ucb::XInteractionReplaceExistingData >
389 xReplace(
390 xSelection.get(), uno::UNO_QUERY );
391 if ( xReplace.is() )
393 // Try again: Replace existing data.
394 return OVERWRITE;
396 else
398 uno::Reference<
399 ucb::XInteractionSupplyName >
400 xSupplyName(
401 xSelection.get(), uno::UNO_QUERY );
402 if ( xSupplyName.is() )
404 // Try again: Use new name.
405 rNewName = xRequest->getNewName();
406 return NEW_NAME;
408 else
410 OSL_FAIL( "Unknown interaction continuation!" );
411 return UNKNOWN;
418 return NOT_HANDLED;
421 /// @throws uno::RuntimeException
422 bool setTitle(
423 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor,
424 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
425 const OUString & rNewTitle )
429 uno::Sequence< beans::PropertyValue > aPropValues{ { /* Name */ "Title",
430 /* Handle */ -1,
431 /* Value */ uno::Any(rNewTitle),
432 /* State */ {} } };
434 ucb::Command aSetPropsCommand(
435 "setPropertyValues",
437 uno::Any( aPropValues ) );
439 uno::Any aResult
440 = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv );
442 uno::Sequence< uno::Any > aErrors;
443 aResult >>= aErrors;
445 OSL_ENSURE( aErrors.getLength() == 1,
446 "getPropertyValues return value invalid!" );
448 if ( aErrors[ 0 ].hasValue() )
450 // error occurred.
451 OSL_FAIL( "error setting Title property!" );
452 return false;
455 catch ( uno::RuntimeException const & )
457 throw;
459 catch ( uno::Exception const & )
461 return false;
464 return true;
467 /// @throws uno::Exception
468 uno::Reference< ucb::XContent > createNew(
469 const TransferCommandContext & rContext,
470 const uno::Reference< ucb::XContent > & xTarget,
471 bool bSourceIsFolder,
472 bool bSourceIsDocument,
473 bool bSourceIsLink )
477 // (1) Obtain creatable types from target.
480 // First, try it using "CreatabeleContentsInfo" property and
481 // "createNewContent" command -> the "new" way.
483 uno::Reference< ucb::XCommandProcessor > xCommandProcessorT(
484 xTarget, uno::UNO_QUERY );
485 if ( !xCommandProcessorT.is() )
487 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
489 {"Folder", uno::Any(rContext.aArg.TargetURL)}
490 }));
491 ucbhelper::cancelCommandExecution(
492 ucb::IOErrorCode_CANT_CREATE,
493 aArgs,
494 rContext.xOrigEnv,
495 "Target is no XCommandProcessor!",
496 rContext.xProcessor );
497 // Unreachable
500 uno::Sequence< beans::Property > aPropsToObtain{ makeProperty("CreatableContentsInfo", -1) };
502 ucb::Command aGetPropsCommand(
503 "getPropertyValues",
505 uno::Any( aPropsToObtain ) );
507 uno::Reference< sdbc::XRow > xRow;
508 xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
510 uno::Sequence< ucb::ContentInfo > aTypesInfo;
511 bool bGotTypesInfo = false;
513 if ( xRow.is() )
515 uno::Any aValue = xRow->getObject(
516 1, uno::Reference< container::XNameAccess >() );
517 if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) )
519 bGotTypesInfo = true;
523 uno::Reference< ucb::XContentCreator > xCreator;
525 if ( !bGotTypesInfo )
527 // Second, try it using XContentCreator interface -> the "old" way (not
528 // providing the chance to supply an XCommandEnvironment.
530 xCreator.set( xTarget, uno::UNO_QUERY );
532 if ( !xCreator.is() )
534 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
536 {"Folder", uno::Any(rContext.aArg.TargetURL)}
537 }));
538 ucbhelper::cancelCommandExecution(
539 ucb::IOErrorCode_CANT_CREATE,
540 aArgs,
541 rContext.xOrigEnv,
542 "Target is no XContentCreator!",
543 rContext.xProcessor );
544 // Unreachable
547 aTypesInfo = xCreator->queryCreatableContentsInfo();
550 if ( !aTypesInfo.hasElements() )
552 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
554 {"Folder", uno::Any(rContext.aArg.TargetURL)}
555 }));
556 ucbhelper::cancelCommandExecution(
557 ucb::IOErrorCode_CANT_CREATE,
558 aArgs,
559 rContext.xOrigEnv,
560 "No types creatable!",
561 rContext.xProcessor );
562 // Unreachable
565 // (2) Try to find a matching target type for the source object.
567 std::function<bool(const sal_Int32)> lCompare;
569 if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK )
571 // Create link
572 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
574 else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) ||
575 ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) )
577 // Copy / Move
578 // Is source a link? Create link in target folder then.
579 if ( bSourceIsLink )
581 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
583 else
585 // (not a and not b) or (a and b)
586 // not( a or b) or (a and b)
587 lCompare = [bSourceIsFolder, bSourceIsDocument](const sal_Int32 nAttribs) {
588 return ( bSourceIsFolder == !!( nAttribs & ucb::ContentInfoAttribute::KIND_FOLDER ) )
589 && ( bSourceIsDocument == !!( nAttribs & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) ;
593 else
595 ucbhelper::cancelCommandExecution(
596 uno::Any( lang::IllegalArgumentException(
597 "Unknown transfer operation!",
598 rContext.xProcessor,
599 -1 ) ),
600 rContext.xOrigEnv );
601 // Unreachable
604 uno::Reference< ucb::XContent > xNew;
605 auto pTypeInfo = std::find_if(std::cbegin(aTypesInfo), std::cend(aTypesInfo),
606 [&lCompare](const ucb::ContentInfo& rTypeInfo) { return lCompare(rTypeInfo.Attributes); });
607 if (pTypeInfo != std::cend(aTypesInfo))
609 // (3) Create a new, empty object of matched type.
611 if ( !xCreator.is() )
613 // First, try it using "CreatabeleContentsInfo" property and
614 // "createNewContent" command -> the "new" way.
615 ucb::Command aCreateNewCommand(
616 "createNewContent",
618 uno::Any( *pTypeInfo ) );
620 xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv )
621 >>= xNew;
623 else
625 // Second, try it using XContentCreator interface -> the "old"
626 // way (not providing the chance to supply an XCommandEnvironment.
628 xNew = xCreator->createNewContent( *pTypeInfo );
631 if ( !xNew.is() )
633 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
635 {"Folder", uno::Any(rContext.aArg.TargetURL)}
636 }));
637 ucbhelper::cancelCommandExecution(
638 ucb::IOErrorCode_CANT_CREATE,
639 aArgs,
640 rContext.xOrigEnv,
641 "createNewContent failed!",
642 rContext.xProcessor );
643 // Unreachable
647 return xNew;
650 /// @throws uno::Exception
651 void transferProperties(
652 const TransferCommandContext & rContext,
653 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS,
654 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN )
656 ucb::Command aGetPropertySetInfoCommand(
657 "getPropertySetInfo",
659 uno::Any() );
661 uno::Reference< beans::XPropertySetInfo > xInfo;
662 xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv )
663 >>= xInfo;
665 if ( !xInfo.is() )
667 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
669 {"Uri", uno::Any(rContext.aArg.SourceURL)}
670 }));
671 ucbhelper::cancelCommandExecution(
672 ucb::IOErrorCode_CANT_READ,
673 aArgs,
674 rContext.xOrigEnv,
675 "Unable to get propertyset info from source object!",
676 rContext.xProcessor );
677 // Unreachable
680 uno::Sequence< beans::Property > aAllProps = xInfo->getProperties();
682 ucb::Command aGetPropsCommand1(
683 "getPropertyValues",
685 uno::Any( aAllProps ) );
687 uno::Reference< sdbc::XRow > xRow1;
688 xCommandProcessorS->execute(
689 aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1;
691 if ( !xRow1.is() )
693 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
695 {"Uri", uno::Any(rContext.aArg.SourceURL)}
696 }));
697 ucbhelper::cancelCommandExecution(
698 ucb::IOErrorCode_CANT_READ,
699 aArgs,
700 rContext.xOrigEnv,
701 "Unable to get properties from source object!",
702 rContext.xProcessor );
703 // Unreachable
706 // Assemble data structure for setPropertyValues command.
708 // Note: Make room for additional Title and TargetURL too. -> + 2
709 uno::Sequence< beans::PropertyValue > aPropValues(
710 aAllProps.getLength() + 2 );
711 auto pPropValues = aPropValues.getArray();
713 bool bHasTitle = rContext.aArg.NewTitle.isEmpty();
714 bool bHasTargetURL = ( rContext.aArg.Operation
715 != ucb::TransferCommandOperation_LINK );
717 sal_Int32 nWritePos = 0;
718 for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m )
720 const beans::Property & rCurrProp = aAllProps[ m ];
721 beans::PropertyValue & rCurrValue = pPropValues[ nWritePos ];
723 uno::Any aValue;
725 if ( rCurrProp.Name == "Title" )
727 // Supply new title, if given.
728 if ( !bHasTitle )
730 bHasTitle = true;
731 aValue <<= rContext.aArg.NewTitle;
734 else if ( rCurrProp.Name == "TargetURL" )
736 // Supply source URL as link target for the new link to create.
737 if ( !bHasTargetURL )
739 bHasTargetURL = true;
740 aValue <<= rContext.aArg.SourceURL;
744 if ( !aValue.hasValue() )
748 aValue = xRow1->getObject(
749 m + 1, uno::Reference< container::XNameAccess >() );
751 catch ( sdbc::SQLException const & )
753 // Argh! But try to bring things to an end. Perhaps the
754 // mad property is not really important...
758 if ( aValue.hasValue() )
760 rCurrValue.Name = rCurrProp.Name;
761 rCurrValue.Handle = rCurrProp.Handle;
762 rCurrValue.Value = aValue;
763 // rCurrValue.State =
765 nWritePos++;
769 // Title needed, but not set yet?
770 if ( !bHasTitle && !rContext.aArg.NewTitle.isEmpty() )
772 pPropValues[ nWritePos ].Name = "Title";
773 pPropValues[ nWritePos ].Handle = -1;
774 pPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle;
776 nWritePos++;
779 // TargetURL needed, but not set yet?
780 if ( !bHasTargetURL && ( rContext.aArg.Operation
781 == ucb::TransferCommandOperation_LINK ) )
783 pPropValues[ nWritePos ].Name = "TargetURL";
784 pPropValues[ nWritePos ].Handle = -1;
785 pPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL;
787 nWritePos++;
790 aPropValues.realloc( nWritePos );
792 // Set properties at new object.
794 ucb::Command aSetPropsCommand(
795 "setPropertyValues",
797 uno::Any( aPropValues ) );
799 xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv );
801 // @@@ What to do with source props that are not supported by the
802 // new object? addProperty ???
805 /// @throws uno::Exception
806 uno::Reference< io::XInputStream > getInputStream(
807 const TransferCommandContext & rContext,
808 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
810 uno::Reference< io::XInputStream > xInputStream;
813 // (1) Try to get data as XInputStream via XActiveDataSink.
818 uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink;
820 ucb::OpenCommandArgument2 aArg;
821 aArg.Mode = ucb::OpenMode::DOCUMENT;
822 aArg.Priority = 0; // unused
823 aArg.Sink = xSink;
824 aArg.Properties = uno::Sequence< beans::Property >( 0 ); // unused
826 ucb::Command aOpenCommand(
827 "open",
829 uno::Any( aArg ) );
831 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
832 xInputStream = xSink->getInputStream();
834 catch ( uno::RuntimeException const & )
836 throw;
838 catch ( uno::Exception const & )
840 // will be handled below.
843 if ( !xInputStream.is() )
847 // (2) Try to get data via XOutputStream.
852 uno::Reference< io::XOutputStream > xOutputStream( io::Pipe::create(rContext.m_xContext), uno::UNO_QUERY_THROW );
854 ucb::OpenCommandArgument2 aArg;
855 aArg.Mode = ucb::OpenMode::DOCUMENT;
856 aArg.Priority = 0; // unused
857 aArg.Sink = xOutputStream;
858 aArg.Properties = uno::Sequence< beans::Property >( 0 );
860 ucb::Command aOpenCommand(
861 "open",
863 uno::Any( aArg ) );
865 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
867 xInputStream.set( xOutputStream, uno::UNO_QUERY );
869 catch ( uno::RuntimeException const & )
871 throw;
873 catch ( uno::Exception const & )
875 OSL_FAIL( "unable to get input stream from document!" );
879 return xInputStream;
882 /// @throws uno::Exception
883 uno::Reference< sdbc::XResultSet > getResultSet(
884 const TransferCommandContext & rContext,
885 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
887 uno::Reference< sdbc::XResultSet > xResultSet;
889 uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */),
890 makeProperty("IsDocument", -1 /* unknown */),
891 makeProperty("TargetURL", -1 /* unknown */) };
893 ucb::OpenCommandArgument2 aArg;
894 aArg.Mode = ucb::OpenMode::ALL;
895 aArg.Priority = 0; // unused
896 aArg.Sink = nullptr;
897 aArg.Properties = aProps;
899 ucb::Command aOpenCommand( "open",
901 uno::Any( aArg ) );
904 uno::Reference< ucb::XDynamicResultSet > xSet;
905 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet;
907 if ( xSet.is() )
908 xResultSet = xSet->getStaticResultSet();
910 catch ( uno::RuntimeException const & )
912 throw;
914 catch ( uno::Exception const & )
916 OSL_FAIL( "unable to get result set from folder!" );
919 return xResultSet;
922 /// @throws uno::Exception
923 void handleNameClashRename(
924 const TransferCommandContext & rContext,
925 const uno::Reference< ucb::XContent > & xNew,
926 const uno::Reference<
927 ucb::XCommandProcessor > & xCommandProcessorN,
928 const uno::Reference<
929 ucb::XCommandProcessor > & xCommandProcessorS,
930 /* [inout] */ uno::Reference< io::XInputStream > & xInputStream )
932 sal_Int32 nTry = 0;
934 // Obtain old title.
935 uno::Sequence< beans::Property > aProps{ makeProperty("Title", -1) };
937 ucb::Command aGetPropsCommand(
938 "getPropertyValues",
940 uno::Any( aProps ) );
942 uno::Reference< sdbc::XRow > xRow;
943 xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
945 if ( !xRow.is() )
947 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
949 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
950 }));
951 ucbhelper::cancelCommandExecution(
952 ucb::IOErrorCode_CANT_READ,
953 aArgs,
954 rContext.xOrigEnv,
955 "Unable to get properties from new object!",
956 rContext.xProcessor );
957 // Unreachable
960 OUString aOldTitle = xRow->getString( 1 );
961 if ( aOldTitle.isEmpty() )
963 ucbhelper::cancelCommandExecution(
964 uno::Any( beans::UnknownPropertyException(
965 "Unable to get property 'Title' from new object!",
966 rContext.xProcessor ) ),
967 rContext.xOrigEnv );
968 // Unreachable
971 // Some pseudo-intelligence for not destroying file extensions.
972 OUString aOldTitlePre;
973 OUString aOldTitlePost;
974 sal_Int32 nPos = aOldTitle.lastIndexOf( '.' );
975 if ( nPos != -1 )
977 aOldTitlePre = aOldTitle.copy( 0, nPos );
978 aOldTitlePost = aOldTitle.copy( nPos );
980 else
981 aOldTitlePre = aOldTitle;
983 if ( nPos > 0 )
984 aOldTitlePre += "_";
986 bool bContinue = true;
989 nTry++;
991 OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) +
992 aOldTitlePost;
994 // Set new title
995 setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle );
997 // Retry inserting the content.
1000 // Previous try may have read from stream. Seek to begin (if
1001 // optional interface XSeekable is supported) or get a new stream.
1002 if ( xInputStream.is() )
1004 uno::Reference< io::XSeekable > xSeekable(
1005 xInputStream, uno::UNO_QUERY );
1006 if ( xSeekable.is() )
1010 xSeekable->seek( 0 );
1012 catch ( lang::IllegalArgumentException const & )
1014 xInputStream.clear();
1016 catch ( io::IOException const & )
1018 xInputStream.clear();
1021 else
1022 xInputStream.clear();
1024 if ( !xInputStream.is() )
1026 xInputStream
1027 = getInputStream( rContext, xCommandProcessorS );
1028 if ( !xInputStream.is() )
1030 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1032 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
1033 }));
1034 ucbhelper::cancelCommandExecution(
1035 ucb::IOErrorCode_CANT_READ,
1036 aArgs,
1037 rContext.xOrigEnv,
1038 "Got no data stream from source!",
1039 rContext.xProcessor );
1040 // Unreachable
1045 ucb::InsertCommandArgument2 aArg;
1046 aArg.Data = xInputStream;
1047 aArg.ReplaceExisting = false;
1049 ucb::Command aInsertCommand(
1050 "insert",
1052 uno::Any( aArg ) );
1054 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1056 // Success!
1057 bContinue = false;
1059 catch ( uno::RuntimeException const & )
1061 throw;
1063 catch ( uno::Exception const & )
1067 while ( bContinue && ( nTry < 50 ) );
1069 if ( nTry == 50 )
1071 ucbhelper::cancelCommandExecution(
1072 uno::Any(
1073 ucb::UnsupportedNameClashException(
1074 "Unable to resolve name clash!",
1075 rContext.xProcessor,
1076 ucb::NameClash::RENAME ) ),
1077 rContext.xOrigEnv );
1078 // Unreachable
1082 /// @throws uno::Exception
1083 void globalTransfer_(
1084 const TransferCommandContext & rContext,
1085 const uno::Reference< ucb::XContent > & xSource,
1086 const uno::Reference< ucb::XContent > & xTarget,
1087 const uno::Reference< sdbc::XRow > & xSourceProps )
1089 // IsFolder: property is required.
1090 bool bSourceIsFolder = xSourceProps->getBoolean( 1 );
1091 if ( !bSourceIsFolder && xSourceProps->wasNull() )
1093 ucbhelper::cancelCommandExecution(
1094 uno::Any( beans::UnknownPropertyException(
1095 "Unable to get property 'IsFolder' from source object!",
1096 rContext.xProcessor ) ),
1097 rContext.xOrigEnv );
1098 // Unreachable
1101 // IsDocument: property is required.
1102 bool bSourceIsDocument = xSourceProps->getBoolean( 2 );
1103 if ( !bSourceIsDocument && xSourceProps->wasNull() )
1105 ucbhelper::cancelCommandExecution(
1106 uno::Any( beans::UnknownPropertyException(
1107 "Unable to get property 'IsDocument' from source object!",
1108 rContext.xProcessor ) ),
1109 rContext.xOrigEnv );
1110 // Unreachable
1113 // TargetURL: property is optional.
1114 bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty();
1117 // (1) Try to find a matching target type for the source object and
1118 // create a new, empty object of that type.
1121 uno::Reference< ucb::XContent > xNew = createNew( rContext,
1122 xTarget,
1123 bSourceIsFolder,
1124 bSourceIsDocument,
1125 bSourceIsLink );
1126 if ( !xNew.is() )
1128 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1130 {"Folder", uno::Any(rContext.aArg.TargetURL)}
1131 }));
1132 ucbhelper::cancelCommandExecution(
1133 ucb::IOErrorCode_CANT_CREATE,
1134 aArgs,
1135 rContext.xOrigEnv,
1136 "No matching content type at target!",
1137 rContext.xProcessor );
1138 // Unreachable
1142 // (2) Transfer property values from source to new object.
1145 uno::Reference< ucb::XCommandProcessor > xCommandProcessorN(
1146 xNew, uno::UNO_QUERY );
1147 if ( !xCommandProcessorN.is() )
1149 uno::Any aProps(beans::PropertyValue(
1150 "Uri",
1152 uno::Any(
1153 xNew->getIdentifier()->
1154 getContentIdentifier()),
1155 beans::PropertyState_DIRECT_VALUE));
1156 ucbhelper::cancelCommandExecution(
1157 ucb::IOErrorCode_CANT_WRITE,
1158 uno::Sequence< uno::Any >(&aProps, 1),
1159 rContext.xOrigEnv,
1160 "New content is not a XCommandProcessor!",
1161 rContext.xProcessor );
1162 // Unreachable
1165 // Obtain all properties from source.
1167 uno::Reference< ucb::XCommandProcessor > xCommandProcessorS(
1168 xSource, uno::UNO_QUERY );
1169 if ( !xCommandProcessorS.is() )
1171 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1173 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1174 }));
1175 ucbhelper::cancelCommandExecution(
1176 ucb::IOErrorCode_CANT_READ,
1177 aArgs,
1178 rContext.xOrigEnv,
1179 "Source content is not a XCommandProcessor!",
1180 rContext.xProcessor );
1181 // Unreachable
1184 transferProperties( rContext, xCommandProcessorS, xCommandProcessorN );
1187 // (3) Try to obtain a data stream from source.
1190 uno::Reference< io::XInputStream > xInputStream;
1192 if ( bSourceIsDocument && ( rContext.aArg.Operation
1193 != ucb::TransferCommandOperation_LINK ) )
1194 xInputStream = getInputStream( rContext, xCommandProcessorS );
1197 // (4) Try to obtain a resultset (children) from source.
1200 uno::Reference< sdbc::XResultSet > xResultSet;
1202 if ( bSourceIsFolder && ( rContext.aArg.Operation
1203 != ucb::TransferCommandOperation_LINK ) )
1204 xResultSet = getResultSet( rContext, xCommandProcessorS );
1207 // (5) Insert (store) new content.
1210 ucb::InsertCommandArgument2 aArg;
1211 aArg.Data = xInputStream;
1212 aArg.MimeType = rContext.aArg.MimeType;
1213 aArg.DocumentId = rContext.aArg.DocumentId;
1215 switch ( rContext.aArg.NameClash )
1217 case ucb::NameClash::OVERWRITE:
1218 aArg.ReplaceExisting = true;
1219 break;
1221 case ucb::NameClash::ERROR:
1222 case ucb::NameClash::RENAME:
1223 case ucb::NameClash::KEEP: // deprecated
1224 case ucb::NameClash::ASK:
1225 aArg.ReplaceExisting = false;
1226 break;
1228 default:
1229 aArg.ReplaceExisting = false;
1230 OSL_FAIL( "Unknown nameclash directive!" );
1231 break;
1234 OUString aDesiredName = createDesiredName( rContext.aArg );
1236 bool bRetry;
1239 bRetry = false;
1243 ucb::Command aInsertCommand(
1244 "insert",
1246 uno::Any( aArg ) );
1248 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1250 catch ( ucb::UnsupportedNameClashException const & exc )
1252 OSL_ENSURE( !aArg.ReplaceExisting,
1253 "BUG: UnsupportedNameClashException not allowed here!" );
1255 if (exc.NameClash != ucb::NameClash::ERROR) {
1256 OSL_FAIL( "BUG: NameClash::ERROR expected!" );
1259 // No chance to solve name clashes, because I'm not able to detect
1260 // whether there is one.
1261 throw ucb::UnsupportedNameClashException(
1262 "Unable to resolve name clashes, no chance to detect "
1263 "that there is one!",
1264 rContext.xProcessor,
1265 rContext.aArg.NameClash );
1267 catch ( ucb::NameClashException const & )
1269 // The 'insert' command throws a NameClashException if the parameter
1270 // ReplaceExisting of the command's argument was set to false and
1271 // there exists a resource with a clashing name in the target folder
1272 // of the operation.
1274 // 'insert' command has no direct support for name clashes other
1275 // than ERROR ( ReplaceExisting == false ) and OVERWRITE
1276 // ( ReplaceExisting == true ). So we have to implement the
1277 // other name clash handling directives on top of the content.
1279 // @@@ 'insert' command should be extended that it accepts a
1280 // name clash handling directive, exactly like 'transfer' command.
1282 switch ( rContext.aArg.NameClash )
1284 case ucb::NameClash::OVERWRITE:
1286 ucbhelper::cancelCommandExecution(
1287 uno::Any(
1288 ucb::UnsupportedNameClashException(
1289 "BUG: insert + replace == true MUST NOT "
1290 "throw NameClashException.",
1291 rContext.xProcessor,
1292 rContext.aArg.NameClash ) ),
1293 rContext.xOrigEnv );
1294 [[fallthrough]]; // Unreachable
1297 case ucb::NameClash::ERROR:
1298 throw;
1300 case ucb::NameClash::RENAME:
1302 // "invent" a new valid title.
1303 handleNameClashRename( rContext,
1304 xNew,
1305 xCommandProcessorN,
1306 xCommandProcessorS,
1307 xInputStream );
1308 break;
1311 case ucb::NameClash::ASK:
1313 uno::Any aExc;
1314 OUString aNewTitle;
1315 NameClashContinuation eCont
1316 = interactiveNameClashResolve(
1317 rContext.xOrigEnv, // always use original environment!
1318 rContext.aArg.TargetURL, // target folder URL
1319 aDesiredName,
1320 aExc,
1321 aNewTitle );
1323 switch ( eCont )
1325 case NOT_HANDLED:
1326 // Not handled.
1327 cppu::throwException( aExc );
1328 [[fallthrough]]; // break;
1330 case UNKNOWN:
1331 // Handled, but not clear, how...
1332 // fall through intended.
1334 case ABORT:
1335 throw ucb::CommandFailedException(
1336 "abort requested via interaction "
1337 "handler",
1338 uno::Reference< uno::XInterface >(),
1339 aExc );
1340 // break;
1342 case OVERWRITE:
1343 OSL_ENSURE( !aArg.ReplaceExisting,
1344 "Hu? ReplaceExisting already true?"
1346 aArg.ReplaceExisting = true;
1347 bRetry = true;
1348 break;
1350 case NEW_NAME:
1352 // set new name -> set "Title" property...
1353 if ( setTitle( xCommandProcessorN,
1354 rContext.xEnv,
1355 aNewTitle ) )
1357 // remember suggested title...
1358 aDesiredName = aNewTitle;
1360 // ... and try again.
1361 bRetry = true;
1363 else
1365 // error setting title. Abort.
1366 throw ucb::CommandFailedException(
1367 "error setting Title property!",
1368 uno::Reference< uno::XInterface >(),
1369 aExc );
1371 break;
1375 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1377 break;
1379 case ucb::NameClash::KEEP: // deprecated
1380 default:
1382 ucbhelper::cancelCommandExecution(
1383 uno::Any(
1384 ucb::UnsupportedNameClashException(
1385 "default action, don't know how to "
1386 "handle name clash",
1387 rContext.xProcessor,
1388 rContext.aArg.NameClash ) ),
1389 rContext.xOrigEnv );
1390 // Unreachable
1395 while ( bRetry );
1398 // (6) Process children of source.
1401 if ( xResultSet.is() )
1405 // Iterate over children...
1407 uno::Reference< sdbc::XRow > xChildRow(
1408 xResultSet, uno::UNO_QUERY );
1410 if ( !xChildRow.is() )
1412 uno::Any aProps(
1413 beans::PropertyValue(
1414 "Uri",
1416 uno::Any(rContext.aArg.SourceURL),
1417 beans::PropertyState_DIRECT_VALUE));
1418 ucbhelper::cancelCommandExecution(
1419 ucb::IOErrorCode_CANT_READ,
1420 uno::Sequence< uno::Any >(&aProps, 1),
1421 rContext.xOrigEnv,
1422 "Unable to get properties from children of source!",
1423 rContext.xProcessor );
1424 // Unreachable
1427 uno::Reference< ucb::XContentAccess > xChildAccess(
1428 xResultSet, uno::UNO_QUERY );
1430 if ( !xChildAccess.is() )
1432 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1434 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1435 }));
1436 ucbhelper::cancelCommandExecution(
1437 ucb::IOErrorCode_CANT_READ,
1438 aArgs,
1439 rContext.xOrigEnv,
1440 "Unable to get children of source!",
1441 rContext.xProcessor );
1442 // Unreachable
1445 if ( xResultSet->first() )
1447 ucb::GlobalTransferCommandArgument2 aTransArg(
1448 rContext.aArg.Operation,
1449 OUString(), // SourceURL; filled later
1450 xNew->getIdentifier()
1451 ->getContentIdentifier(), // TargetURL
1452 OUString(), // NewTitle;
1453 rContext.aArg.NameClash,
1454 rContext.aArg.MimeType,
1455 rContext.aArg.DocumentId);
1457 TransferCommandContext aSubCtx(
1458 rContext.m_xContext,
1459 rContext.xProcessor,
1460 rContext.xEnv,
1461 rContext.xOrigEnv,
1462 std::move(aTransArg) );
1465 uno::Reference< ucb::XContent > xChild
1466 = xChildAccess->queryContent();
1467 if ( xChild.is() )
1469 // Recursion!
1471 aSubCtx.aArg.SourceURL
1472 = xChild->getIdentifier()->getContentIdentifier();
1474 globalTransfer_( aSubCtx,
1475 xChild,
1476 xNew,
1477 xChildRow );
1480 while ( xResultSet->next() );
1483 catch ( sdbc::SQLException const & )
1488 try {
1489 uno::Reference< ucb::XCommandProcessor > xcp(
1490 xTarget, uno::UNO_QUERY );
1492 uno::Any aAny;
1493 uno::Reference< ucb::XCommandInfo > xci;
1494 if(xcp.is())
1495 aAny =
1496 xcp->execute(
1497 ucb::Command(
1498 "getCommandInfo",
1500 uno::Any()),
1502 rContext.xEnv );
1504 static const OUStringLiteral cmdName(u"flush");
1505 if((aAny >>= xci) && xci->hasCommandByName(cmdName))
1506 xcp->execute(
1507 ucb::Command(
1508 cmdName,
1510 uno::Any()) ,
1512 rContext.xEnv );
1514 catch( uno::Exception const & )
1519 } /* namespace */
1522 // UniversalContentBroker implementation ( XCommandProcessor commands ).
1525 uno::Reference< ucb::XCommandInfo >
1526 UniversalContentBroker::getCommandInfo()
1528 return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1532 void UniversalContentBroker::globalTransfer(
1533 const ucb::GlobalTransferCommandArgument2 & rArg,
1534 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1536 // Use own command environment with own interaction handler intercepting
1537 // some interaction requests that shall not be handled by the user-supplied
1538 // interaction handler.
1539 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1540 if (xEnv.is())
1542 xLocalEnv.set( ucb::CommandEnvironment::create(
1543 m_xContext,
1544 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1545 xEnv->getProgressHandler() ) );
1549 // (1) Try to transfer the content using 'transfer' command.
1552 uno::Reference< ucb::XContent > xTarget;
1553 uno::Reference< ucb::XContentIdentifier > xId
1554 = createContentIdentifier( rArg.TargetURL );
1555 if ( xId.is() )
1559 xTarget = queryContent( xId );
1561 catch ( ucb::IllegalIdentifierException const & )
1566 if ( !xTarget.is() )
1568 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1570 {"Uri", uno::Any(rArg.TargetURL)}
1571 }));
1572 ucbhelper::cancelCommandExecution(
1573 ucb::IOErrorCode_CANT_READ,
1574 aArgs,
1575 xEnv,
1576 "Can't instantiate target object!",
1577 this );
1578 // Unreachable
1581 if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) ||
1582 ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) )
1584 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1585 xTarget, uno::UNO_QUERY );
1586 if ( !xCommandProcessor.is() )
1588 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1590 {"Uri", uno::Any(rArg.TargetURL)}
1591 }));
1592 ucbhelper::cancelCommandExecution(
1593 ucb::IOErrorCode_CANT_READ,
1594 aArgs,
1595 xEnv,
1596 "Target content is not a XCommandProcessor!",
1597 this );
1598 // Unreachable
1601 ucb::TransferInfo2 aTransferArg(
1602 ( rArg.Operation
1603 == ucb::TransferCommandOperation_MOVE ), // MoveData
1604 rArg.SourceURL,
1605 rArg.NewTitle,
1606 rArg.NameClash,
1607 rArg.MimeType );
1609 bool bRetry;
1612 bRetry = false;
1616 ucb::Command aCommand(
1617 "transfer", // Name
1618 -1, // Handle
1619 uno::Any( aTransferArg ) ); // Argument
1621 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1623 // Command succeeded. We're done.
1624 return;
1626 catch ( ucb::InteractiveBadTransferURLException const & )
1628 // Source URL is not supported by target. Try to transfer
1629 // the content "manually".
1631 catch ( ucb::UnsupportedCommandException const & )
1633 // 'transfer' command is not supported by commandprocessor.
1634 // Try to transfer manually.
1636 catch ( ucb::UnsupportedNameClashException const & exc )
1638 OSL_ENSURE( aTransferArg.NameClash == exc.NameClash,
1639 "nameclash mismatch!" );
1640 if ( exc.NameClash == ucb::NameClash::ASK )
1642 // Try to detect a name clash by invoking "transfer" with
1643 // NameClash::ERROR.
1646 ucb::TransferInfo2 aTransferArg1(
1647 aTransferArg.MoveData,
1648 aTransferArg.SourceURL,
1649 aTransferArg.NewTitle,
1650 ucb::NameClash::ERROR,
1651 aTransferArg.MimeType );
1653 ucb::Command aCommand1(
1654 "transfer",
1656 uno::Any( aTransferArg1 ) );
1658 xCommandProcessor->execute( aCommand1, 0, xLocalEnv );
1660 // Command succeeded. We're done.
1661 return;
1663 catch ( ucb::UnsupportedNameClashException const & )
1665 // No chance to solve name clashes, because I'm not
1666 // able to detect whether there is one.
1667 throw exc; // Not just 'throw;'!
1669 catch ( ucb::NameClashException const & )
1671 // There's a clash. Use interaction handler to solve it.
1673 uno::Any aExc;
1674 OUString aNewTitle;
1675 NameClashContinuation eCont
1676 = interactiveNameClashResolve(
1677 xEnv, // always use original environment!
1678 rArg.TargetURL, // target folder URL
1679 createDesiredName(
1680 aTransferArg ), // clashing name
1681 aExc,
1682 aNewTitle );
1684 switch ( eCont )
1686 case NOT_HANDLED:
1687 // Not handled.
1688 cppu::throwException( aExc );
1689 [[fallthrough]]; // break;
1691 case UNKNOWN:
1692 // Handled, but not clear, how...
1693 // fall through intended.
1695 case ABORT:
1696 throw ucb::CommandFailedException(
1697 "abort requested via interaction "
1698 "handler",
1699 uno::Reference< uno::XInterface >(),
1700 aExc );
1701 // break;
1703 case OVERWRITE:
1704 aTransferArg.NameClash
1705 = ucb::NameClash::OVERWRITE;
1706 bRetry = true;
1707 break;
1709 case NEW_NAME:
1710 aTransferArg.NewTitle = aNewTitle;
1711 bRetry = true;
1712 break;
1715 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1718 else
1720 throw;
1724 while ( bRetry );
1728 // (2) Try to transfer the content "manually".
1731 uno::Reference< ucb::XContent > xSource;
1734 uno::Reference< ucb::XContentIdentifier > xId2
1735 = createContentIdentifier( rArg.SourceURL );
1736 if ( xId2.is() )
1737 xSource = queryContent( xId2 );
1739 catch ( ucb::IllegalIdentifierException const & )
1741 // Error handling via "if ( !xSource.is() )" below.
1744 if ( !xSource.is() )
1746 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1748 {"Uri", uno::Any(rArg.SourceURL)}
1749 }));
1750 ucbhelper::cancelCommandExecution(
1751 ucb::IOErrorCode_CANT_READ,
1752 aArgs,
1753 xEnv,
1754 "Can't instantiate source object!",
1755 this );
1756 // Unreachable
1759 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1760 xSource, uno::UNO_QUERY );
1761 if ( !xCommandProcessor.is() )
1763 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1765 {"Uri", uno::Any(rArg.SourceURL)}
1766 }));
1767 ucbhelper::cancelCommandExecution(
1768 ucb::IOErrorCode_CANT_READ,
1769 aArgs,
1770 xEnv,
1771 "Source content is not a XCommandProcessor!",
1772 this );
1773 // Unreachable
1776 // Obtain interesting property values from source...
1778 uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */),
1779 makeProperty("IsDocument", -1 /* unknown */),
1780 makeProperty("TargetURL", -1 /* unknown */),
1781 makeProperty("BaseURI", -1 /* unknown */) };
1783 ucb::Command aGetPropsCommand(
1784 "getPropertyValues",
1786 uno::Any( aProps ) );
1788 uno::Reference< sdbc::XRow > xRow;
1789 xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow;
1791 if ( !xRow.is() )
1793 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1795 {"Uri", uno::Any(rArg.SourceURL)}
1796 }));
1797 ucbhelper::cancelCommandExecution(
1798 ucb::IOErrorCode_CANT_READ,
1799 aArgs,
1800 xEnv,
1801 "Unable to get properties from source object!",
1802 this );
1803 // Unreachable
1806 TransferCommandContext aTransferCtx(
1807 m_xContext, this, xLocalEnv, xEnv, rArg );
1809 if ( rArg.NewTitle.isEmpty() )
1811 // BaseURI: property is optional.
1812 OUString aBaseURI( xRow->getString( 4 ) );
1813 if ( !aBaseURI.isEmpty() )
1815 aTransferCtx.aArg.NewTitle
1816 = createDesiredName( aBaseURI, OUString() );
1820 // Do it!
1821 globalTransfer_( aTransferCtx, xSource, xTarget, xRow );
1824 // (3) Delete source, if operation is MOVE.
1827 if ( rArg.Operation != ucb::TransferCommandOperation_MOVE )
1828 return;
1832 ucb::Command aCommand(
1833 "delete", // Name
1834 -1, // Handle
1835 uno::Any( true ) ); // Argument
1837 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1839 catch ( uno::Exception const & )
1841 OSL_FAIL( "Cannot delete source object!" );
1842 throw;
1846 uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg,
1847 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1849 uno::Any aRet;
1850 // Use own command environment with own interaction handler intercepting
1851 // some interaction requests that shall not be handled by the user-supplied
1852 // interaction handler.
1853 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1854 if (xEnv.is())
1856 xLocalEnv.set( ucb::CommandEnvironment::create(
1857 m_xContext,
1858 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1859 xEnv->getProgressHandler() ) );
1862 uno::Reference< ucb::XContent > xTarget;
1863 uno::Reference< ucb::XContentIdentifier > xId
1864 = createContentIdentifier( rArg.TargetURL );
1865 if ( xId.is() )
1869 xTarget = queryContent( xId );
1871 catch ( ucb::IllegalIdentifierException const & )
1876 if ( !xTarget.is() )
1878 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1880 {"Uri", uno::Any(rArg.TargetURL)}
1881 }));
1882 ucbhelper::cancelCommandExecution(
1883 ucb::IOErrorCode_CANT_READ,
1884 aArgs,
1885 xEnv,
1886 "Can't instantiate target object!",
1887 this );
1888 // Unreachable
1891 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1892 xTarget, uno::UNO_QUERY );
1893 if ( !xCommandProcessor.is() )
1895 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1897 {"Uri", uno::Any(rArg.TargetURL)}
1898 }));
1899 ucbhelper::cancelCommandExecution(
1900 ucb::IOErrorCode_CANT_READ,
1901 aArgs,
1902 xEnv,
1903 "Target content is not a XCommandProcessor!",
1904 this );
1905 // Unreachable
1910 ucb::Command aCommand(
1911 "checkin", -1,
1912 uno::Any( rArg ) );
1914 aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1916 catch ( ucb::UnsupportedCommandException const & )
1918 // 'checkin' command is not supported by commandprocessor:
1919 // ignore.
1921 return aRet;
1924 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */