Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / ucb / source / core / ucbcmds.cxx
blobc53890d6bd842873ce3c295c9a51059314caf9a6
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 <memory>
21 #include <osl/diagnose.h>
22 #include <comphelper/propertysequence.hxx>
23 #include <cppuhelper/implbase.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
25 #include <rtl/ustring.hxx>
26 #include <com/sun/star/uno/XInterface.hpp>
27 #include <com/sun/star/beans/PropertyState.hpp>
28 #include <com/sun/star/beans/PropertyValue.hpp>
29 #include <com/sun/star/beans/XPropertySetInfo.hpp>
30 #include <com/sun/star/io/IOException.hpp>
31 #include <com/sun/star/io/Pipe.hpp>
32 #include <com/sun/star/io/XActiveDataSink.hpp>
33 #include <com/sun/star/io/XOutputStream.hpp>
34 #include <com/sun/star/io/XSeekable.hpp>
35 #include <com/sun/star/lang/IllegalArgumentException.hpp>
36 #include <com/sun/star/sdbc/SQLException.hpp>
37 #include <com/sun/star/sdbc/XRow.hpp>
38 #include <com/sun/star/task/XInteractionHandler.hpp>
39 #include <com/sun/star/ucb/CommandEnvironment.hpp>
40 #include <com/sun/star/ucb/CommandFailedException.hpp>
41 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
42 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
43 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
44 #include <com/sun/star/ucb/InsertCommandArgument2.hpp>
45 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
46 #include <com/sun/star/ucb/NameClash.hpp>
47 #include <com/sun/star/ucb/NameClashException.hpp>
48 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
49 #include <com/sun/star/ucb/OpenMode.hpp>
50 #include <com/sun/star/ucb/TransferInfo2.hpp>
51 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
52 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
53 #include <com/sun/star/ucb/XCommandInfo.hpp>
54 #include <com/sun/star/ucb/XContentAccess.hpp>
55 #include <com/sun/star/ucb/XContentCreator.hpp>
56 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
57 #include <com/sun/star/ucb/XInteractionSupplyName.hpp>
58 #include <com/sun/star/uno/Any.hxx>
59 #include <com/sun/star/uno/Sequence.hxx>
60 #include <ucbhelper/cancelcommandexecution.hxx>
61 #include <ucbhelper/simplenameclashresolverequest.hxx>
62 #include "ucbcmds.hxx"
63 #include "ucb.hxx"
65 using namespace com::sun::star;
67 namespace
71 // struct TransferCommandContext.
74 struct TransferCommandContext
76 uno::Reference< uno::XComponentContext > m_xContext;
77 uno::Reference< ucb::XCommandProcessor > xProcessor;
78 uno::Reference< ucb::XCommandEnvironment > xEnv;
79 uno::Reference< ucb::XCommandEnvironment > xOrigEnv;
80 ucb::GlobalTransferCommandArgument2 aArg;
82 TransferCommandContext(
83 const uno::Reference< uno::XComponentContext > & xContext,
84 const uno::Reference< ucb::XCommandProcessor > & rxProcessor,
85 const uno::Reference< ucb::XCommandEnvironment > & rxEnv,
86 const uno::Reference< ucb::XCommandEnvironment > & rxOrigEnv,
87 const ucb::GlobalTransferCommandArgument2 & rArg )
88 : m_xContext( xContext ), xProcessor( rxProcessor ), xEnv( rxEnv ),
89 xOrigEnv( rxOrigEnv ), aArg( rArg ) {}
93 // class InteractionHandlerProxy.
96 class InteractionHandlerProxy :
97 public cppu::WeakImplHelper< task::XInteractionHandler >
99 uno::Reference< task::XInteractionHandler > m_xOrig;
101 public:
102 explicit InteractionHandlerProxy(
103 const uno::Reference< task::XInteractionHandler > & xOrig )
104 : m_xOrig( xOrig ) {}
106 // XInteractionHandler methods.
107 virtual void SAL_CALL handle(
108 const uno::Reference< task::XInteractionRequest >& Request ) override;
112 // virtual
113 void SAL_CALL InteractionHandlerProxy::handle(
114 const uno::Reference< task::XInteractionRequest >& Request )
116 if ( !m_xOrig.is() )
117 return;
119 // Filter unwanted requests by just not handling them.
120 uno::Any aRequest = Request->getRequest();
122 // "transfer"
123 ucb::InteractiveBadTransferURLException aBadTransferURLEx;
124 if ( aRequest >>= aBadTransferURLEx )
126 return;
128 else
130 // "transfer"
131 ucb::UnsupportedNameClashException aUnsupportedNameClashEx;
132 if ( aRequest >>= aUnsupportedNameClashEx )
134 if ( aUnsupportedNameClashEx.NameClash
135 != ucb::NameClash::ERROR )
136 return;
138 else
140 // "insert"
141 ucb::NameClashException aNameClashEx;
142 if ( aRequest >>= aNameClashEx )
144 return;
146 else
148 // "transfer"
149 ucb::UnsupportedCommandException aUnsupportedCommandEx;
150 if ( aRequest >>= aUnsupportedCommandEx )
152 return;
158 // not filtered; let the original handler do the work.
159 m_xOrig->handle( Request );
163 // class ActiveDataSink.
166 class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink >
168 uno::Reference< io::XInputStream > m_xStream;
170 public:
171 // XActiveDataSink methods.
172 virtual void SAL_CALL setInputStream(
173 const uno::Reference< io::XInputStream >& aStream ) override;
174 virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override;
178 // virtual
179 void SAL_CALL ActiveDataSink::setInputStream(
180 const uno::Reference< io::XInputStream >& aStream )
182 m_xStream = aStream;
186 // virtual
187 uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream()
189 return m_xStream;
193 // class CommandProcessorInfo.
196 class CommandProcessorInfo :
197 public cppu::WeakImplHelper< ucb::XCommandInfo >
199 std::unique_ptr< uno::Sequence< ucb::CommandInfo > > m_pInfo;
201 public:
202 CommandProcessorInfo();
204 // XCommandInfo methods
205 virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() override;
206 virtual ucb::CommandInfo SAL_CALL
207 getCommandInfoByName( const OUString& Name ) override;
208 virtual ucb::CommandInfo SAL_CALL
209 getCommandInfoByHandle( sal_Int32 Handle ) override;
210 virtual sal_Bool SAL_CALL hasCommandByName( const OUString& Name ) override;
211 virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) override;
215 CommandProcessorInfo::CommandProcessorInfo()
216 : m_pInfo( new uno::Sequence< ucb::CommandInfo >( 3 ) )
218 (*m_pInfo)[ 0 ]
219 = ucb::CommandInfo(
220 GETCOMMANDINFO_NAME, // Name
221 GETCOMMANDINFO_HANDLE, // Handle
222 cppu::UnoType<void>::get() ); // ArgType
223 (*m_pInfo)[ 1 ]
224 = ucb::CommandInfo(
225 GLOBALTRANSFER_NAME, // Name
226 GLOBALTRANSFER_HANDLE, // Handle
227 cppu::UnoType<ucb::GlobalTransferCommandArgument>::get() ); // ArgType
228 (*m_pInfo)[ 2 ]
229 = ucb::CommandInfo(
230 CHECKIN_NAME, // Name
231 CHECKIN_HANDLE, // Handle
232 cppu::UnoType<ucb::CheckinArgument>::get() ); // ArgType
236 // virtual
237 uno::Sequence< ucb::CommandInfo > SAL_CALL
238 CommandProcessorInfo::getCommands()
240 return *m_pInfo;
244 // virtual
245 ucb::CommandInfo SAL_CALL
246 CommandProcessorInfo::getCommandInfoByName( const OUString& Name )
248 auto pInfo = std::find_if(m_pInfo->begin(), m_pInfo->end(),
249 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
250 if (pInfo != m_pInfo->end())
251 return *pInfo;
253 throw ucb::UnsupportedCommandException();
257 // virtual
258 ucb::CommandInfo SAL_CALL
259 CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle )
261 auto pInfo = std::find_if(m_pInfo->begin(), m_pInfo->end(),
262 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
263 if (pInfo != m_pInfo->end())
264 return *pInfo;
266 throw ucb::UnsupportedCommandException();
270 // virtual
271 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName(
272 const OUString& Name )
274 return std::any_of(m_pInfo->begin(), m_pInfo->end(),
275 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
279 // virtual
280 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle )
282 return std::any_of(m_pInfo->begin(), m_pInfo->end(),
283 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
287 OUString createDesiredName(
288 const OUString & rSourceURL, const OUString & rNewTitle )
290 OUString aName( rNewTitle );
291 if ( aName.isEmpty() )
293 // calculate name using source URL
295 // @@@ It's not guaranteed that slashes contained in the URL are
296 // actually path separators. This depends on the fact whether the
297 // URL is hierarchical. Only then the slashes are path separators.
298 // Therefore this algorithm is not guaranteed to work! But, ATM
299 // I don't know a better solution. It would have been better to
300 // have a member for the clashing name in
301 // UnsupportedNameClashException...
303 sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' );
304 bool bTrailingSlash = false;
305 if ( nLastSlash == rSourceURL.getLength() - 1 )
307 nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash );
308 bTrailingSlash = true;
311 if ( nLastSlash != -1 )
313 if ( bTrailingSlash )
314 aName = rSourceURL.copy(
315 nLastSlash + 1,
316 rSourceURL.getLength() - nLastSlash - 2 );
317 else
318 aName = rSourceURL.copy( nLastSlash + 1 );
320 else
322 aName = rSourceURL;
325 // query, fragment present?
326 sal_Int32 nPos = aName.indexOf( '?' );
327 if ( nPos == -1 )
328 nPos = aName.indexOf( '#' );
330 if ( nPos != -1 )
331 aName = aName.copy( 0, nPos );
333 return aName;
336 OUString createDesiredName(
337 const ucb::GlobalTransferCommandArgument & rArg )
339 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
342 OUString createDesiredName(
343 const ucb::TransferInfo & rArg )
345 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
349 enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN };
351 NameClashContinuation interactiveNameClashResolve(
352 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
353 const OUString & rTargetURL,
354 const OUString & rClashingName,
355 /* [out] */ uno::Any & rException,
356 /* [out] */ OUString & rNewName )
358 rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest(
359 new ucbhelper::SimpleNameClashResolveRequest(
360 rTargetURL, // target folder URL
361 rClashingName
362 ) );
364 rException = xRequest->getRequest();
365 if ( xEnv.is() )
367 uno::Reference< task::XInteractionHandler > xIH
368 = xEnv->getInteractionHandler();
369 if ( xIH.is() )
372 xIH->handle( xRequest.get() );
374 rtl::Reference< ucbhelper::InteractionContinuation >
375 xSelection( xRequest->getSelection() );
377 if ( xSelection.is() )
379 // Handler handled the request.
380 uno::Reference< task::XInteractionAbort > xAbort(
381 xSelection.get(), uno::UNO_QUERY );
382 if ( xAbort.is() )
384 // Abort.
385 return ABORT;
387 else
389 uno::Reference<
390 ucb::XInteractionReplaceExistingData >
391 xReplace(
392 xSelection.get(), uno::UNO_QUERY );
393 if ( xReplace.is() )
395 // Try again: Replace existing data.
396 return OVERWRITE;
398 else
400 uno::Reference<
401 ucb::XInteractionSupplyName >
402 xSupplyName(
403 xSelection.get(), uno::UNO_QUERY );
404 if ( xSupplyName.is() )
406 // Try again: Use new name.
407 rNewName = xRequest->getNewName();
408 return NEW_NAME;
410 else
412 OSL_FAIL( "Unknown interaction continuation!" );
413 return UNKNOWN;
420 return NOT_HANDLED;
423 /// @throws uno::RuntimeException
424 bool setTitle(
425 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor,
426 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
427 const OUString & rNewTitle )
431 uno::Sequence< beans::PropertyValue > aPropValues( 1 );
432 aPropValues[ 0 ].Name = "Title";
433 aPropValues[ 0 ].Handle = -1;
434 aPropValues[ 0 ].Value <<= rNewTitle;
436 ucb::Command aSetPropsCommand(
437 "setPropertyValues",
439 uno::makeAny( aPropValues ) );
441 uno::Any aResult
442 = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv );
444 uno::Sequence< uno::Any > aErrors;
445 aResult >>= aErrors;
447 OSL_ENSURE( aErrors.getLength() == 1,
448 "getPropertyValues return value invalid!" );
450 if ( aErrors[ 0 ].hasValue() )
452 // error occurred.
453 OSL_FAIL( "error setting Title property!" );
454 return false;
457 catch ( uno::RuntimeException const & )
459 throw;
461 catch ( uno::Exception const & )
463 return false;
466 return true;
469 /// @throws uno::Exception
470 uno::Reference< ucb::XContent > createNew(
471 const TransferCommandContext & rContext,
472 const uno::Reference< ucb::XContent > & xTarget,
473 bool bSourceIsFolder,
474 bool bSourceIsDocument,
475 bool bSourceIsLink )
479 // (1) Obtain creatable types from target.
482 // First, try it using "CreatabeleContentsInfo" property and
483 // "createNewContent" command -> the "new" way.
485 uno::Reference< ucb::XCommandProcessor > xCommandProcessorT(
486 xTarget, uno::UNO_QUERY );
487 if ( !xCommandProcessorT.is() )
489 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
491 {"Folder", uno::Any(rContext.aArg.TargetURL)}
492 }));
493 ucbhelper::cancelCommandExecution(
494 ucb::IOErrorCode_CANT_CREATE,
495 aArgs,
496 rContext.xOrigEnv,
497 "Target is no XCommandProcessor!",
498 rContext.xProcessor );
499 // Unreachable
502 uno::Sequence< beans::Property > aPropsToObtain( 1 );
503 aPropsToObtain[ 0 ].Name = "CreatableContentsInfo";
504 aPropsToObtain[ 0 ].Handle = -1;
506 ucb::Command aGetPropsCommand(
507 "getPropertyValues",
509 uno::makeAny( aPropsToObtain ) );
511 uno::Reference< sdbc::XRow > xRow;
512 xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
514 uno::Sequence< ucb::ContentInfo > aTypesInfo;
515 bool bGotTypesInfo = false;
517 if ( xRow.is() )
519 uno::Any aValue = xRow->getObject(
520 1, uno::Reference< container::XNameAccess >() );
521 if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) )
523 bGotTypesInfo = true;
527 uno::Reference< ucb::XContentCreator > xCreator;
529 if ( !bGotTypesInfo )
531 // Second, try it using XContentCreator interface -> the "old" way (not
532 // providing the chance to supply an XCommandEnvironment.
534 xCreator.set( xTarget, uno::UNO_QUERY );
536 if ( !xCreator.is() )
538 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
540 {"Folder", uno::Any(rContext.aArg.TargetURL)}
541 }));
542 ucbhelper::cancelCommandExecution(
543 ucb::IOErrorCode_CANT_CREATE,
544 aArgs,
545 rContext.xOrigEnv,
546 "Target is no XContentCreator!",
547 rContext.xProcessor );
548 // Unreachable
551 aTypesInfo = xCreator->queryCreatableContentsInfo();
554 if ( !aTypesInfo.hasElements() )
556 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
558 {"Folder", uno::Any(rContext.aArg.TargetURL)}
559 }));
560 ucbhelper::cancelCommandExecution(
561 ucb::IOErrorCode_CANT_CREATE,
562 aArgs,
563 rContext.xOrigEnv,
564 "No types creatable!",
565 rContext.xProcessor );
566 // Unreachable
569 // (2) Try to find a matching target type for the source object.
571 std::function<bool(const sal_Int32)> lCompare;
573 if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK )
575 // Create link
576 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
578 else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) ||
579 ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) )
581 // Copy / Move
582 // Is source a link? Create link in target folder then.
583 if ( bSourceIsLink )
585 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
587 else
589 // (not a and not b) or (a and b)
590 // not( a or b) or (a and b)
591 lCompare = [bSourceIsFolder, bSourceIsDocument](const sal_Int32 nAttribs) {
592 return ( bSourceIsFolder == !!( nAttribs & ucb::ContentInfoAttribute::KIND_FOLDER ) )
593 && ( bSourceIsDocument == !!( nAttribs & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) ;
597 else
599 ucbhelper::cancelCommandExecution(
600 uno::makeAny( lang::IllegalArgumentException(
601 "Unknown transfer operation!",
602 rContext.xProcessor,
603 -1 ) ),
604 rContext.xOrigEnv );
605 // Unreachable
608 uno::Reference< ucb::XContent > xNew;
609 auto pTypeInfo = std::find_if(aTypesInfo.begin(), aTypesInfo.end(),
610 [&lCompare](const ucb::ContentInfo& rTypeInfo) { return lCompare(rTypeInfo.Attributes); });
611 if (pTypeInfo != aTypesInfo.end())
613 // (3) Create a new, empty object of matched type.
615 if ( !xCreator.is() )
617 // First, try it using "CreatabeleContentsInfo" property and
618 // "createNewContent" command -> the "new" way.
619 ucb::Command aCreateNewCommand(
620 "createNewContent",
622 uno::makeAny( *pTypeInfo ) );
624 xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv )
625 >>= xNew;
627 else
629 // Second, try it using XContentCreator interface -> the "old"
630 // way (not providing the chance to supply an XCommandEnvironment.
632 xNew = xCreator->createNewContent( *pTypeInfo );
635 if ( !xNew.is() )
637 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
639 {"Folder", uno::Any(rContext.aArg.TargetURL)}
640 }));
641 ucbhelper::cancelCommandExecution(
642 ucb::IOErrorCode_CANT_CREATE,
643 aArgs,
644 rContext.xOrigEnv,
645 "createNewContent failed!",
646 rContext.xProcessor );
647 // Unreachable
651 return xNew;
654 /// @throws uno::Exception
655 void transferProperties(
656 const TransferCommandContext & rContext,
657 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS,
658 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN )
660 ucb::Command aGetPropertySetInfoCommand(
661 "getPropertySetInfo",
663 uno::Any() );
665 uno::Reference< beans::XPropertySetInfo > xInfo;
666 xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv )
667 >>= xInfo;
669 if ( !xInfo.is() )
671 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
673 {"Uri", uno::Any(rContext.aArg.SourceURL)}
674 }));
675 ucbhelper::cancelCommandExecution(
676 ucb::IOErrorCode_CANT_READ,
677 aArgs,
678 rContext.xOrigEnv,
679 "Unable to get propertyset info from source object!",
680 rContext.xProcessor );
681 // Unreachable
684 uno::Sequence< beans::Property > aAllProps = xInfo->getProperties();
686 ucb::Command aGetPropsCommand1(
687 "getPropertyValues",
689 uno::makeAny( aAllProps ) );
691 uno::Reference< sdbc::XRow > xRow1;
692 xCommandProcessorS->execute(
693 aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1;
695 if ( !xRow1.is() )
697 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
699 {"Uri", uno::Any(rContext.aArg.SourceURL)}
700 }));
701 ucbhelper::cancelCommandExecution(
702 ucb::IOErrorCode_CANT_READ,
703 aArgs,
704 rContext.xOrigEnv,
705 "Unable to get properties from source object!",
706 rContext.xProcessor );
707 // Unreachable
710 // Assemble data structure for setPropertyValues command.
712 // Note: Make room for additional Title and TargetURL too. -> + 2
713 uno::Sequence< beans::PropertyValue > aPropValues(
714 aAllProps.getLength() + 2 );
716 bool bHasTitle = rContext.aArg.NewTitle.isEmpty();
717 bool bHasTargetURL = ( rContext.aArg.Operation
718 != ucb::TransferCommandOperation_LINK );
720 sal_Int32 nWritePos = 0;
721 for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m )
723 const beans::Property & rCurrProp = aAllProps[ m ];
724 beans::PropertyValue & rCurrValue = aPropValues[ nWritePos ];
726 uno::Any aValue;
728 if ( rCurrProp.Name == "Title" )
730 // Supply new title, if given.
731 if ( !bHasTitle )
733 bHasTitle = true;
734 aValue <<= rContext.aArg.NewTitle;
737 else if ( rCurrProp.Name == "TargetURL" )
739 // Supply source URL as link target for the new link to create.
740 if ( !bHasTargetURL )
742 bHasTargetURL = true;
743 aValue <<= rContext.aArg.SourceURL;
747 if ( !aValue.hasValue() )
751 aValue = xRow1->getObject(
752 m + 1, uno::Reference< container::XNameAccess >() );
754 catch ( sdbc::SQLException const & )
756 // Argh! But try to bring things to an end. Perhaps the
757 // mad property is not really important...
761 if ( aValue.hasValue() )
763 rCurrValue.Name = rCurrProp.Name;
764 rCurrValue.Handle = rCurrProp.Handle;
765 rCurrValue.Value = aValue;
766 // rCurrValue.State =
768 nWritePos++;
772 // Title needed, but not set yet?
773 if ( !bHasTitle && !rContext.aArg.NewTitle.isEmpty() )
775 aPropValues[ nWritePos ].Name = "Title";
776 aPropValues[ nWritePos ].Handle = -1;
777 aPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle;
779 nWritePos++;
782 // TargetURL needed, but not set yet?
783 if ( !bHasTargetURL && ( rContext.aArg.Operation
784 == ucb::TransferCommandOperation_LINK ) )
786 aPropValues[ nWritePos ].Name = "TargetURL";
787 aPropValues[ nWritePos ].Handle = -1;
788 aPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL;
790 nWritePos++;
793 aPropValues.realloc( nWritePos );
795 // Set properties at new object.
797 ucb::Command aSetPropsCommand(
798 "setPropertyValues",
800 uno::makeAny( aPropValues ) );
802 xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv );
804 // @@@ What to do with source props that are not supported by the
805 // new object? addProperty ???
808 /// @throws uno::Exception
809 uno::Reference< io::XInputStream > getInputStream(
810 const TransferCommandContext & rContext,
811 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
813 uno::Reference< io::XInputStream > xInputStream;
816 // (1) Try to get data as XInputStream via XActiveDataSink.
821 uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink;
823 ucb::OpenCommandArgument2 aArg;
824 aArg.Mode = ucb::OpenMode::DOCUMENT;
825 aArg.Priority = 0; // unused
826 aArg.Sink = xSink;
827 aArg.Properties = uno::Sequence< beans::Property >( 0 ); // unused
829 ucb::Command aOpenCommand(
830 "open",
832 uno::makeAny( aArg ) );
834 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
835 xInputStream = xSink->getInputStream();
837 catch ( uno::RuntimeException const & )
839 throw;
841 catch ( uno::Exception const & )
843 // will be handled below.
846 if ( !xInputStream.is() )
850 // (2) Try to get data via XOutputStream.
855 uno::Reference< io::XOutputStream > xOutputStream( io::Pipe::create(rContext.m_xContext), uno::UNO_QUERY_THROW );
857 ucb::OpenCommandArgument2 aArg;
858 aArg.Mode = ucb::OpenMode::DOCUMENT;
859 aArg.Priority = 0; // unused
860 aArg.Sink = xOutputStream;
861 aArg.Properties = uno::Sequence< beans::Property >( 0 );
863 ucb::Command aOpenCommand(
864 "open",
866 uno::makeAny( aArg ) );
868 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
870 xInputStream.set( xOutputStream, uno::UNO_QUERY );
872 catch ( uno::RuntimeException const & )
874 throw;
876 catch ( uno::Exception const & )
878 OSL_FAIL( "unable to get input stream from document!" );
882 return xInputStream;
885 /// @throws uno::Exception
886 uno::Reference< sdbc::XResultSet > getResultSet(
887 const TransferCommandContext & rContext,
888 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
890 uno::Reference< sdbc::XResultSet > xResultSet;
892 uno::Sequence< beans::Property > aProps( 3 );
894 aProps[ 0 ].Name = "IsFolder";
895 aProps[ 0 ].Handle = -1; /* unknown */
896 aProps[ 1 ].Name = "IsDocument";
897 aProps[ 1 ].Handle = -1; /* unknown */
898 aProps[ 2 ].Name = "TargetURL";
899 aProps[ 2 ].Handle = -1; /* unknown */
901 ucb::OpenCommandArgument2 aArg;
902 aArg.Mode = ucb::OpenMode::ALL;
903 aArg.Priority = 0; // unused
904 aArg.Sink = nullptr;
905 aArg.Properties = aProps;
907 ucb::Command aOpenCommand( "open",
909 uno::makeAny( aArg ) );
912 uno::Reference< ucb::XDynamicResultSet > xSet;
913 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet;
915 if ( xSet.is() )
916 xResultSet = xSet->getStaticResultSet();
918 catch ( uno::RuntimeException const & )
920 throw;
922 catch ( uno::Exception const & )
924 OSL_FAIL( "unable to get result set from folder!" );
927 return xResultSet;
930 /// @throws uno::Exception
931 void handleNameClashRename(
932 const TransferCommandContext & rContext,
933 const uno::Reference< ucb::XContent > & xNew,
934 const uno::Reference<
935 ucb::XCommandProcessor > & xCommandProcessorN,
936 const uno::Reference<
937 ucb::XCommandProcessor > & xCommandProcessorS,
938 /* [inout] */ uno::Reference< io::XInputStream > & xInputStream )
940 sal_Int32 nTry = 0;
942 // Obtain old title.
943 uno::Sequence< beans::Property > aProps( 1 );
944 aProps[ 0 ].Name = "Title";
945 aProps[ 0 ].Handle = -1;
947 ucb::Command aGetPropsCommand(
948 "getPropertyValues",
950 uno::makeAny( aProps ) );
952 uno::Reference< sdbc::XRow > xRow;
953 xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
955 if ( !xRow.is() )
957 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
959 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
960 }));
961 ucbhelper::cancelCommandExecution(
962 ucb::IOErrorCode_CANT_READ,
963 aArgs,
964 rContext.xOrigEnv,
965 "Unable to get properties from new object!",
966 rContext.xProcessor );
967 // Unreachable
970 OUString aOldTitle = xRow->getString( 1 );
971 if ( aOldTitle.isEmpty() )
973 ucbhelper::cancelCommandExecution(
974 uno::makeAny( beans::UnknownPropertyException(
975 "Unable to get property 'Title' from new object!",
976 rContext.xProcessor ) ),
977 rContext.xOrigEnv );
978 // Unreachable
981 // Some pseudo-intelligence for not destroying file extensions.
982 OUString aOldTitlePre;
983 OUString aOldTitlePost;
984 sal_Int32 nPos = aOldTitle.lastIndexOf( '.' );
985 if ( nPos != -1 )
987 aOldTitlePre = aOldTitle.copy( 0, nPos );
988 aOldTitlePost = aOldTitle.copy( nPos );
990 else
991 aOldTitlePre = aOldTitle;
993 if ( nPos > 0 )
994 aOldTitlePre += "_";
996 bool bContinue = true;
999 nTry++;
1001 OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) +
1002 aOldTitlePost;
1004 // Set new title
1005 setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle );
1007 // Retry inserting the content.
1010 // Previous try may have read from stream. Seek to begin (if
1011 // optional interface XSeekable is supported) or get a new stream.
1012 if ( xInputStream.is() )
1014 uno::Reference< io::XSeekable > xSeekable(
1015 xInputStream, uno::UNO_QUERY );
1016 if ( xSeekable.is() )
1020 xSeekable->seek( 0 );
1022 catch ( lang::IllegalArgumentException const & )
1024 xInputStream.clear();
1026 catch ( io::IOException const & )
1028 xInputStream.clear();
1031 else
1032 xInputStream.clear();
1034 if ( !xInputStream.is() )
1036 xInputStream
1037 = getInputStream( rContext, xCommandProcessorS );
1038 if ( !xInputStream.is() )
1040 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1042 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
1043 }));
1044 ucbhelper::cancelCommandExecution(
1045 ucb::IOErrorCode_CANT_READ,
1046 aArgs,
1047 rContext.xOrigEnv,
1048 "Got no data stream from source!",
1049 rContext.xProcessor );
1050 // Unreachable
1055 ucb::InsertCommandArgument2 aArg;
1056 aArg.Data = xInputStream;
1057 aArg.ReplaceExisting = false;
1059 ucb::Command aInsertCommand(
1060 "insert",
1062 uno::makeAny( aArg ) );
1064 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1066 // Success!
1067 bContinue = false;
1069 catch ( uno::RuntimeException const & )
1071 throw;
1073 catch ( uno::Exception const & )
1077 while ( bContinue && ( nTry < 50 ) );
1079 if ( nTry == 50 )
1081 ucbhelper::cancelCommandExecution(
1082 uno::makeAny(
1083 ucb::UnsupportedNameClashException(
1084 "Unable to resolve name clash!",
1085 rContext.xProcessor,
1086 ucb::NameClash::RENAME ) ),
1087 rContext.xOrigEnv );
1088 // Unreachable
1092 /// @throws uno::Exception
1093 void globalTransfer_(
1094 const TransferCommandContext & rContext,
1095 const uno::Reference< ucb::XContent > & xSource,
1096 const uno::Reference< ucb::XContent > & xTarget,
1097 const uno::Reference< sdbc::XRow > & xSourceProps )
1099 // IsFolder: property is required.
1100 bool bSourceIsFolder = xSourceProps->getBoolean( 1 );
1101 if ( !bSourceIsFolder && xSourceProps->wasNull() )
1103 ucbhelper::cancelCommandExecution(
1104 uno::makeAny( beans::UnknownPropertyException(
1105 "Unable to get property 'IsFolder' from source object!",
1106 rContext.xProcessor ) ),
1107 rContext.xOrigEnv );
1108 // Unreachable
1111 // IsDocument: property is required.
1112 bool bSourceIsDocument = xSourceProps->getBoolean( 2 );
1113 if ( !bSourceIsDocument && xSourceProps->wasNull() )
1115 ucbhelper::cancelCommandExecution(
1116 uno::makeAny( beans::UnknownPropertyException(
1117 "Unable to get property 'IsDocument' from source object!",
1118 rContext.xProcessor ) ),
1119 rContext.xOrigEnv );
1120 // Unreachable
1123 // TargetURL: property is optional.
1124 bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty();
1127 // (1) Try to find a matching target type for the source object and
1128 // create a new, empty object of that type.
1131 uno::Reference< ucb::XContent > xNew = createNew( rContext,
1132 xTarget,
1133 bSourceIsFolder,
1134 bSourceIsDocument,
1135 bSourceIsLink );
1136 if ( !xNew.is() )
1138 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1140 {"Folder", uno::Any(rContext.aArg.TargetURL)}
1141 }));
1142 ucbhelper::cancelCommandExecution(
1143 ucb::IOErrorCode_CANT_CREATE,
1144 aArgs,
1145 rContext.xOrigEnv,
1146 "No matching content type at target!",
1147 rContext.xProcessor );
1148 // Unreachable
1152 // (2) Transfer property values from source to new object.
1155 uno::Reference< ucb::XCommandProcessor > xCommandProcessorN(
1156 xNew, uno::UNO_QUERY );
1157 if ( !xCommandProcessorN.is() )
1159 uno::Any aProps
1160 = uno::makeAny(beans::PropertyValue(
1161 "Uri",
1163 uno::makeAny(
1164 xNew->getIdentifier()->
1165 getContentIdentifier()),
1166 beans::PropertyState_DIRECT_VALUE));
1167 ucbhelper::cancelCommandExecution(
1168 ucb::IOErrorCode_CANT_WRITE,
1169 uno::Sequence< uno::Any >(&aProps, 1),
1170 rContext.xOrigEnv,
1171 "New content is not a XCommandProcessor!",
1172 rContext.xProcessor );
1173 // Unreachable
1176 // Obtain all properties from source.
1178 uno::Reference< ucb::XCommandProcessor > xCommandProcessorS(
1179 xSource, uno::UNO_QUERY );
1180 if ( !xCommandProcessorS.is() )
1182 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1184 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1185 }));
1186 ucbhelper::cancelCommandExecution(
1187 ucb::IOErrorCode_CANT_READ,
1188 aArgs,
1189 rContext.xOrigEnv,
1190 "Source content is not a XCommandProcessor!",
1191 rContext.xProcessor );
1192 // Unreachable
1195 transferProperties( rContext, xCommandProcessorS, xCommandProcessorN );
1198 // (3) Try to obtain a data stream from source.
1201 uno::Reference< io::XInputStream > xInputStream;
1203 if ( bSourceIsDocument && ( rContext.aArg.Operation
1204 != ucb::TransferCommandOperation_LINK ) )
1205 xInputStream = getInputStream( rContext, xCommandProcessorS );
1208 // (4) Try to obtain a resultset (children) from source.
1211 uno::Reference< sdbc::XResultSet > xResultSet;
1213 if ( bSourceIsFolder && ( rContext.aArg.Operation
1214 != ucb::TransferCommandOperation_LINK ) )
1215 xResultSet = getResultSet( rContext, xCommandProcessorS );
1218 // (5) Insert (store) new content.
1221 ucb::InsertCommandArgument2 aArg;
1222 aArg.Data = xInputStream;
1223 aArg.MimeType = rContext.aArg.MimeType;
1224 aArg.DocumentId = rContext.aArg.DocumentId;
1226 switch ( rContext.aArg.NameClash )
1228 case ucb::NameClash::OVERWRITE:
1229 aArg.ReplaceExisting = true;
1230 break;
1232 case ucb::NameClash::ERROR:
1233 case ucb::NameClash::RENAME:
1234 case ucb::NameClash::KEEP: // deprecated
1235 case ucb::NameClash::ASK:
1236 aArg.ReplaceExisting = false;
1237 break;
1239 default:
1240 aArg.ReplaceExisting = false;
1241 OSL_FAIL( "Unknown nameclash directive!" );
1242 break;
1245 OUString aDesiredName = createDesiredName( rContext.aArg );
1247 bool bRetry;
1250 bRetry = false;
1254 ucb::Command aInsertCommand(
1255 "insert",
1257 uno::makeAny( aArg ) );
1259 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1261 catch ( ucb::UnsupportedNameClashException const & exc )
1263 OSL_ENSURE( !aArg.ReplaceExisting,
1264 "BUG: UnsupportedNameClashException not allowed here!" );
1266 if (exc.NameClash != ucb::NameClash::ERROR) {
1267 OSL_FAIL( "BUG: NameClash::ERROR expected!" );
1270 // No chance to solve name clashes, because I'm not able to detect
1271 // whether there is one.
1272 throw ucb::UnsupportedNameClashException(
1273 "Unable to resolve name clashes, no chance to detect "
1274 "that there is one!",
1275 rContext.xProcessor,
1276 rContext.aArg.NameClash );
1278 catch ( ucb::NameClashException const & )
1280 // The 'insert' command throws a NameClashException if the parameter
1281 // ReplaceExisting of the command's argument was set to false and
1282 // there exists a resource with a clashing name in the target folder
1283 // of the operation.
1285 // 'insert' command has no direct support for name clashes other
1286 // than ERROR ( ReplaceExisting == false ) and OVERWRITE
1287 // ( ReplaceExisting == true ). So we have to implement the
1288 // other name clash handling directives on top of the content.
1290 // @@@ 'insert' command should be extended that it accepts a
1291 // name clash handling directive, exactly like 'transfer' command.
1293 switch ( rContext.aArg.NameClash )
1295 case ucb::NameClash::OVERWRITE:
1297 ucbhelper::cancelCommandExecution(
1298 uno::makeAny(
1299 ucb::UnsupportedNameClashException(
1300 "BUG: insert + replace == true MUST NOT "
1301 "throw NameClashException.",
1302 rContext.xProcessor,
1303 rContext.aArg.NameClash ) ),
1304 rContext.xOrigEnv );
1305 [[fallthrough]]; // Unreachable
1308 case ucb::NameClash::ERROR:
1309 throw;
1311 case ucb::NameClash::RENAME:
1313 // "invent" a new valid title.
1314 handleNameClashRename( rContext,
1315 xNew,
1316 xCommandProcessorN,
1317 xCommandProcessorS,
1318 xInputStream );
1319 break;
1322 case ucb::NameClash::ASK:
1324 uno::Any aExc;
1325 OUString aNewTitle;
1326 NameClashContinuation eCont
1327 = interactiveNameClashResolve(
1328 rContext.xOrigEnv, // always use original environment!
1329 rContext.aArg.TargetURL, // target folder URL
1330 aDesiredName,
1331 aExc,
1332 aNewTitle );
1334 switch ( eCont )
1336 case NOT_HANDLED:
1337 // Not handled.
1338 cppu::throwException( aExc );
1339 [[fallthrough]]; // break;
1341 case UNKNOWN:
1342 // Handled, but not clear, how...
1343 // fall through intended.
1345 case ABORT:
1346 throw ucb::CommandFailedException(
1347 "abort requested via interaction "
1348 "handler",
1349 uno::Reference< uno::XInterface >(),
1350 aExc );
1351 // break;
1353 case OVERWRITE:
1354 OSL_ENSURE( !aArg.ReplaceExisting,
1355 "Hu? ReplaceExisting already true?"
1357 aArg.ReplaceExisting = true;
1358 bRetry = true;
1359 break;
1361 case NEW_NAME:
1363 // set new name -> set "Title" property...
1364 if ( setTitle( xCommandProcessorN,
1365 rContext.xEnv,
1366 aNewTitle ) )
1368 // remember suggested title...
1369 aDesiredName = aNewTitle;
1371 // ... and try again.
1372 bRetry = true;
1374 else
1376 // error setting title. Abort.
1377 throw ucb::CommandFailedException(
1378 "error setting Title property!",
1379 uno::Reference< uno::XInterface >(),
1380 aExc );
1382 break;
1386 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1388 break;
1390 case ucb::NameClash::KEEP: // deprecated
1391 default:
1393 ucbhelper::cancelCommandExecution(
1394 uno::makeAny(
1395 ucb::UnsupportedNameClashException(
1396 "default action, don't know how to "
1397 "handle name clash",
1398 rContext.xProcessor,
1399 rContext.aArg.NameClash ) ),
1400 rContext.xOrigEnv );
1401 // Unreachable
1406 while ( bRetry );
1409 // (6) Process children of source.
1412 if ( xResultSet.is() )
1416 // Iterate over children...
1418 uno::Reference< sdbc::XRow > xChildRow(
1419 xResultSet, uno::UNO_QUERY );
1421 if ( !xChildRow.is() )
1423 uno::Any aProps
1424 = uno::makeAny(
1425 beans::PropertyValue(
1426 "Uri",
1428 uno::makeAny(rContext.aArg.SourceURL),
1429 beans::PropertyState_DIRECT_VALUE));
1430 ucbhelper::cancelCommandExecution(
1431 ucb::IOErrorCode_CANT_READ,
1432 uno::Sequence< uno::Any >(&aProps, 1),
1433 rContext.xOrigEnv,
1434 "Unable to get properties from children of source!",
1435 rContext.xProcessor );
1436 // Unreachable
1439 uno::Reference< ucb::XContentAccess > xChildAccess(
1440 xResultSet, uno::UNO_QUERY );
1442 if ( !xChildAccess.is() )
1444 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1446 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1447 }));
1448 ucbhelper::cancelCommandExecution(
1449 ucb::IOErrorCode_CANT_READ,
1450 aArgs,
1451 rContext.xOrigEnv,
1452 "Unable to get children of source!",
1453 rContext.xProcessor );
1454 // Unreachable
1457 if ( xResultSet->first() )
1459 ucb::GlobalTransferCommandArgument2 aTransArg(
1460 rContext.aArg.Operation,
1461 OUString(), // SourceURL; filled later
1462 xNew->getIdentifier()
1463 ->getContentIdentifier(), // TargetURL
1464 OUString(), // NewTitle;
1465 rContext.aArg.NameClash,
1466 rContext.aArg.MimeType,
1467 rContext.aArg.DocumentId);
1469 TransferCommandContext aSubCtx(
1470 rContext.m_xContext,
1471 rContext.xProcessor,
1472 rContext.xEnv,
1473 rContext.xOrigEnv,
1474 aTransArg );
1477 uno::Reference< ucb::XContent > xChild
1478 = xChildAccess->queryContent();
1479 if ( xChild.is() )
1481 // Recursion!
1483 aSubCtx.aArg.SourceURL
1484 = xChild->getIdentifier()->getContentIdentifier();
1486 globalTransfer_( aSubCtx,
1487 xChild,
1488 xNew,
1489 xChildRow );
1492 while ( xResultSet->next() );
1495 catch ( sdbc::SQLException const & )
1500 try {
1501 uno::Reference< ucb::XCommandProcessor > xcp(
1502 xTarget, uno::UNO_QUERY );
1504 uno::Any aAny;
1505 uno::Reference< ucb::XCommandInfo > xci;
1506 if(xcp.is())
1507 aAny =
1508 xcp->execute(
1509 ucb::Command(
1510 "getCommandInfo",
1512 uno::Any()),
1514 rContext.xEnv );
1516 const OUString cmdName("flush");
1517 if((aAny >>= xci) && xci->hasCommandByName(cmdName))
1518 xcp->execute(
1519 ucb::Command(
1520 cmdName,
1522 uno::Any()) ,
1524 rContext.xEnv );
1526 catch( uno::Exception const & )
1531 } /* namespace */
1534 // UniversalContentBroker implementation ( XCommandProcessor commands ).
1537 uno::Reference< ucb::XCommandInfo >
1538 UniversalContentBroker::getCommandInfo()
1540 return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1544 void UniversalContentBroker::globalTransfer(
1545 const ucb::GlobalTransferCommandArgument2 & rArg,
1546 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1548 // Use own command environment with own interaction handler intercepting
1549 // some interaction requests that shall not be handled by the user-supplied
1550 // interaction handler.
1551 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1552 if (xEnv.is())
1554 xLocalEnv.set( ucb::CommandEnvironment::create(
1555 m_xContext,
1556 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1557 xEnv->getProgressHandler() ) );
1561 // (1) Try to transfer the content using 'transfer' command.
1564 uno::Reference< ucb::XContent > xTarget;
1565 uno::Reference< ucb::XContentIdentifier > xId
1566 = createContentIdentifier( rArg.TargetURL );
1567 if ( xId.is() )
1571 xTarget = queryContent( xId );
1573 catch ( ucb::IllegalIdentifierException const & )
1578 if ( !xTarget.is() )
1580 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1582 {"Uri", uno::Any(rArg.TargetURL)}
1583 }));
1584 ucbhelper::cancelCommandExecution(
1585 ucb::IOErrorCode_CANT_READ,
1586 aArgs,
1587 xEnv,
1588 "Can't instantiate target object!",
1589 this );
1590 // Unreachable
1593 if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) ||
1594 ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) )
1596 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1597 xTarget, uno::UNO_QUERY );
1598 if ( !xCommandProcessor.is() )
1600 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1602 {"Uri", uno::Any(rArg.TargetURL)}
1603 }));
1604 ucbhelper::cancelCommandExecution(
1605 ucb::IOErrorCode_CANT_READ,
1606 aArgs,
1607 xEnv,
1608 "Target content is not a XCommandProcessor!",
1609 this );
1610 // Unreachable
1613 ucb::TransferInfo2 aTransferArg(
1614 ( rArg.Operation
1615 == ucb::TransferCommandOperation_MOVE ), // MoveData
1616 rArg.SourceURL,
1617 rArg.NewTitle,
1618 rArg.NameClash,
1619 rArg.MimeType );
1621 bool bRetry;
1624 bRetry = false;
1628 ucb::Command aCommand(
1629 "transfer", // Name
1630 -1, // Handle
1631 uno::makeAny( aTransferArg ) ); // Argument
1633 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1635 // Command succeeded. We're done.
1636 return;
1638 catch ( ucb::InteractiveBadTransferURLException const & )
1640 // Source URL is not supported by target. Try to transfer
1641 // the content "manually".
1643 catch ( ucb::UnsupportedCommandException const & )
1645 // 'transfer' command is not supported by commandprocessor.
1646 // Try to transfer manually.
1648 catch ( ucb::UnsupportedNameClashException const & exc )
1650 OSL_ENSURE( aTransferArg.NameClash == exc.NameClash,
1651 "nameclash mismatch!" );
1652 if ( exc.NameClash == ucb::NameClash::ASK )
1654 // Try to detect a name clash by invoking "transfer" with
1655 // NameClash::ERROR.
1658 ucb::TransferInfo2 aTransferArg1(
1659 aTransferArg.MoveData,
1660 aTransferArg.SourceURL,
1661 aTransferArg.NewTitle,
1662 ucb::NameClash::ERROR,
1663 aTransferArg.MimeType );
1665 ucb::Command aCommand1(
1666 "transfer",
1668 uno::makeAny( aTransferArg1 ) );
1670 xCommandProcessor->execute( aCommand1, 0, xLocalEnv );
1672 // Command succeeded. We're done.
1673 return;
1675 catch ( ucb::UnsupportedNameClashException const & )
1677 // No chance to solve name clashes, because I'm not
1678 // able to detect whether there is one.
1679 throw exc; // Not just 'throw;'!
1681 catch ( ucb::NameClashException const & )
1683 // There's a clash. Use interaction handler to solve it.
1685 uno::Any aExc;
1686 OUString aNewTitle;
1687 NameClashContinuation eCont
1688 = interactiveNameClashResolve(
1689 xEnv, // always use original environment!
1690 rArg.TargetURL, // target folder URL
1691 createDesiredName(
1692 aTransferArg ), // clashing name
1693 aExc,
1694 aNewTitle );
1696 switch ( eCont )
1698 case NOT_HANDLED:
1699 // Not handled.
1700 cppu::throwException( aExc );
1701 [[fallthrough]]; // break;
1703 case UNKNOWN:
1704 // Handled, but not clear, how...
1705 // fall through intended.
1707 case ABORT:
1708 throw ucb::CommandFailedException(
1709 "abort requested via interaction "
1710 "handler",
1711 uno::Reference< uno::XInterface >(),
1712 aExc );
1713 // break;
1715 case OVERWRITE:
1716 aTransferArg.NameClash
1717 = ucb::NameClash::OVERWRITE;
1718 bRetry = true;
1719 break;
1721 case NEW_NAME:
1722 aTransferArg.NewTitle = aNewTitle;
1723 bRetry = true;
1724 break;
1727 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1730 else
1732 throw;
1736 while ( bRetry );
1740 // (2) Try to transfer the content "manually".
1743 uno::Reference< ucb::XContent > xSource;
1746 uno::Reference< ucb::XContentIdentifier > xId2
1747 = createContentIdentifier( rArg.SourceURL );
1748 if ( xId2.is() )
1749 xSource = queryContent( xId2 );
1751 catch ( ucb::IllegalIdentifierException const & )
1753 // Error handling via "if ( !xSource.is() )" below.
1756 if ( !xSource.is() )
1758 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1760 {"Uri", uno::Any(rArg.SourceURL)}
1761 }));
1762 ucbhelper::cancelCommandExecution(
1763 ucb::IOErrorCode_CANT_READ,
1764 aArgs,
1765 xEnv,
1766 "Can't instantiate source object!",
1767 this );
1768 // Unreachable
1771 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1772 xSource, uno::UNO_QUERY );
1773 if ( !xCommandProcessor.is() )
1775 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1777 {"Uri", uno::Any(rArg.SourceURL)}
1778 }));
1779 ucbhelper::cancelCommandExecution(
1780 ucb::IOErrorCode_CANT_READ,
1781 aArgs,
1782 xEnv,
1783 "Source content is not a XCommandProcessor!",
1784 this );
1785 // Unreachable
1788 // Obtain interesting property values from source...
1790 uno::Sequence< beans::Property > aProps( 4 );
1792 aProps[ 0 ].Name = "IsFolder";
1793 aProps[ 0 ].Handle = -1; /* unknown */
1794 aProps[ 1 ].Name = "IsDocument";
1795 aProps[ 1 ].Handle = -1; /* unknown */
1796 aProps[ 2 ].Name = "TargetURL";
1797 aProps[ 2 ].Handle = -1; /* unknown */
1798 aProps[ 3 ].Name = "BaseURI";
1799 aProps[ 3 ].Handle = -1; /* unknown */
1801 ucb::Command aGetPropsCommand(
1802 "getPropertyValues",
1804 uno::makeAny( aProps ) );
1806 uno::Reference< sdbc::XRow > xRow;
1807 xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow;
1809 if ( !xRow.is() )
1811 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1813 {"Uri", uno::Any(rArg.SourceURL)}
1814 }));
1815 ucbhelper::cancelCommandExecution(
1816 ucb::IOErrorCode_CANT_READ,
1817 aArgs,
1818 xEnv,
1819 "Unable to get properties from source object!",
1820 this );
1821 // Unreachable
1824 TransferCommandContext aTransferCtx(
1825 m_xContext, this, xLocalEnv, xEnv, rArg );
1827 if ( rArg.NewTitle.isEmpty() )
1829 // BaseURI: property is optional.
1830 OUString aBaseURI( xRow->getString( 4 ) );
1831 if ( !aBaseURI.isEmpty() )
1833 aTransferCtx.aArg.NewTitle
1834 = createDesiredName( aBaseURI, OUString() );
1838 // Do it!
1839 globalTransfer_( aTransferCtx, xSource, xTarget, xRow );
1842 // (3) Delete source, if operation is MOVE.
1845 if ( rArg.Operation == ucb::TransferCommandOperation_MOVE )
1849 ucb::Command aCommand(
1850 "delete", // Name
1851 -1, // Handle
1852 uno::makeAny( true ) ); // Argument
1854 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1856 catch ( uno::Exception const & )
1858 OSL_FAIL( "Cannot delete source object!" );
1859 throw;
1864 uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg,
1865 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1867 uno::Any aRet;
1868 // Use own command environment with own interaction handler intercepting
1869 // some interaction requests that shall not be handled by the user-supplied
1870 // interaction handler.
1871 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1872 if (xEnv.is())
1874 xLocalEnv.set( ucb::CommandEnvironment::create(
1875 m_xContext,
1876 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1877 xEnv->getProgressHandler() ) );
1880 uno::Reference< ucb::XContent > xTarget;
1881 uno::Reference< ucb::XContentIdentifier > xId
1882 = createContentIdentifier( rArg.TargetURL );
1883 if ( xId.is() )
1887 xTarget = queryContent( xId );
1889 catch ( ucb::IllegalIdentifierException const & )
1894 if ( !xTarget.is() )
1896 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1898 {"Uri", uno::Any(rArg.TargetURL)}
1899 }));
1900 ucbhelper::cancelCommandExecution(
1901 ucb::IOErrorCode_CANT_READ,
1902 aArgs,
1903 xEnv,
1904 "Can't instantiate target object!",
1905 this );
1906 // Unreachable
1909 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1910 xTarget, uno::UNO_QUERY );
1911 if ( !xCommandProcessor.is() )
1913 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1915 {"Uri", uno::Any(rArg.TargetURL)}
1916 }));
1917 ucbhelper::cancelCommandExecution(
1918 ucb::IOErrorCode_CANT_READ,
1919 aArgs,
1920 xEnv,
1921 "Target content is not a XCommandProcessor!",
1922 this );
1923 // Unreachable
1928 ucb::Command aCommand(
1929 "checkin", -1,
1930 uno::makeAny( rArg ) );
1932 aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1934 catch ( ucb::UnsupportedCommandException const & )
1936 // 'checkin' command is not supported by commandprocessor:
1937 // ignore.
1939 return aRet;
1942 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */