LibreOffice Module ucb (master) 1
ucbcmds.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <osl/diagnose.h>
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>
61#include <utility>
62#include "ucbcmds.hxx"
63#include "ucb.hxx"
64
65using namespace com::sun::star;
66
67namespace
68{
69// Helper to provide defaults for type and attributes (save some typing)
70beans::Property makeProperty(const OUString& n, sal_Int32 h, uno::Type t = {}, sal_Int16 a = {})
71{
72 return { n, h, t, a };
73}
74
75// struct TransferCommandContext.
76
77
78struct TransferCommandContext
79{
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;
85
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 )) {}
94};
95
96
97
98
99class InteractionHandlerProxy :
100 public cppu::WeakImplHelper< task::XInteractionHandler >
101{
102 uno::Reference< task::XInteractionHandler > m_xOrig;
103
104public:
105 explicit InteractionHandlerProxy(
106 uno::Reference< task::XInteractionHandler > xOrig )
107 : m_xOrig(std::move( xOrig )) {}
108
109 // XInteractionHandler methods.
110 virtual void SAL_CALL handle(
111 const uno::Reference< task::XInteractionRequest >& Request ) override;
112};
113
114
115// virtual
116void SAL_CALL InteractionHandlerProxy::handle(
117 const uno::Reference< task::XInteractionRequest >& Request )
118{
119 if ( !m_xOrig.is() )
120 return;
121
122 // Filter unwanted requests by just not handling them.
123 uno::Any aRequest = Request->getRequest();
124
125 // "transfer"
126 ucb::InteractiveBadTransferURLException aBadTransferURLEx;
127 if ( aRequest >>= aBadTransferURLEx )
128 {
129 return;
130 }
131 else
132 {
133 // "transfer"
134 ucb::UnsupportedNameClashException aUnsupportedNameClashEx;
135 if ( aRequest >>= aUnsupportedNameClashEx )
136 {
137 if ( aUnsupportedNameClashEx.NameClash
138 != ucb::NameClash::ERROR )
139 return;
140 }
141 else
142 {
143 // "insert"
144 ucb::NameClashException aNameClashEx;
145 if ( aRequest >>= aNameClashEx )
146 {
147 return;
148 }
149 else
150 {
151 // "transfer"
152 ucb::UnsupportedCommandException aUnsupportedCommandEx;
153 if ( aRequest >>= aUnsupportedCommandEx )
154 {
155 return;
156 }
157 }
158 }
159 }
160
161 // not filtered; let the original handler do the work.
162 m_xOrig->handle( Request );
163}
164
165
166
167
168class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink >
169{
170 uno::Reference< io::XInputStream > m_xStream;
171
172public:
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;
177};
178
179
180// virtual
181void SAL_CALL ActiveDataSink::setInputStream(
182 const uno::Reference< io::XInputStream >& aStream )
183{
184 m_xStream = aStream;
185}
186
187
188// virtual
189uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream()
190{
191 return m_xStream;
192}
193
194
195
196
197class CommandProcessorInfo :
198 public cppu::WeakImplHelper< ucb::XCommandInfo >
199{
200 uno::Sequence< ucb::CommandInfo > m_xInfo;
201
202public:
203 CommandProcessorInfo();
204
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;
213};
214
215
216CommandProcessorInfo::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
230{
231}
232
233
234// virtual
235uno::Sequence< ucb::CommandInfo > SAL_CALL
236CommandProcessorInfo::getCommands()
237{
238 return m_xInfo;
239}
240
241
242// virtual
243ucb::CommandInfo SAL_CALL
244CommandProcessorInfo::getCommandInfoByName( const OUString& Name )
245{
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;
250
251 throw ucb::UnsupportedCommandException();
252}
253
254
255// virtual
256ucb::CommandInfo SAL_CALL
257CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle )
258{
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;
263
264 throw ucb::UnsupportedCommandException();
265}
266
267
268// virtual
269sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName(
270 const OUString& Name )
271{
272 return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo),
273 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
274}
275
276
277// virtual
278sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle )
279{
280 return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo),
281 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
282}
283
284
285OUString createDesiredName(
286 const OUString & rSourceURL, const OUString & rNewTitle )
287{
288 OUString aName( rNewTitle );
289 if ( aName.isEmpty() )
290 {
291 // calculate name using source URL
292
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...
300
301 sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' );
302 bool bTrailingSlash = false;
303 if ( nLastSlash == rSourceURL.getLength() - 1 )
304 {
305 nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash );
306 bTrailingSlash = true;
307 }
308
309 if ( nLastSlash != -1 )
310 {
311 if ( bTrailingSlash )
312 aName = rSourceURL.copy(
313 nLastSlash + 1,
314 rSourceURL.getLength() - nLastSlash - 2 );
315 else
316 aName = rSourceURL.copy( nLastSlash + 1 );
317 }
318 else
319 {
320 aName = rSourceURL;
321 }
322
323 // query, fragment present?
324 sal_Int32 nPos = aName.indexOf( '?' );
325 if ( nPos == -1 )
326 nPos = aName.indexOf( '#' );
327
328 if ( nPos != -1 )
329 aName = aName.copy( 0, nPos );
330 }
331 return aName;
332}
333
334OUString createDesiredName(
335 const ucb::GlobalTransferCommandArgument & rArg )
336{
337 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
338}
339
340OUString createDesiredName(
341 const ucb::TransferInfo & rArg )
342{
343 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
344}
345
346
347enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN };
348
349NameClashContinuation 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 )
355{
357 new ucbhelper::SimpleNameClashResolveRequest(
358 rTargetURL, // target folder URL
359 rClashingName
360 ) );
361
362 rException = xRequest->getRequest();
363 if ( xEnv.is() )
364 {
365 uno::Reference< task::XInteractionHandler > xIH
366 = xEnv->getInteractionHandler();
367 if ( xIH.is() )
368 {
369
370 xIH->handle( xRequest );
371
373 xSelection( xRequest->getSelection() );
374
375 if ( xSelection.is() )
376 {
377 // Handler handled the request.
378 uno::Reference< task::XInteractionAbort > xAbort(
379 xSelection.get(), uno::UNO_QUERY );
380 if ( xAbort.is() )
381 {
382 // Abort.
383 return ABORT;
384 }
385 else
386 {
387 uno::Reference<
388 ucb::XInteractionReplaceExistingData >
389 xReplace(
390 xSelection.get(), uno::UNO_QUERY );
391 if ( xReplace.is() )
392 {
393 // Try again: Replace existing data.
394 return OVERWRITE;
395 }
396 else
397 {
398 uno::Reference<
399 ucb::XInteractionSupplyName >
400 xSupplyName(
401 xSelection.get(), uno::UNO_QUERY );
402 if ( xSupplyName.is() )
403 {
404 // Try again: Use new name.
405 rNewName = xRequest->getNewName();
406 return NEW_NAME;
407 }
408 else
409 {
410 OSL_FAIL( "Unknown interaction continuation!" );
411 return UNKNOWN;
412 }
413 }
414 }
415 }
416 }
417 }
418 return NOT_HANDLED;
419}
420
422bool setTitle(
423 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor,
424 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
425 const OUString & rNewTitle )
426{
427 try
428 {
429 uno::Sequence< beans::PropertyValue > aPropValues{ { /* Name */ "Title",
430 /* Handle */ -1,
431 /* Value */ uno::Any(rNewTitle),
432 /* State */ {} } };
433
434 ucb::Command aSetPropsCommand(
435 "setPropertyValues",
436 -1,
437 uno::Any( aPropValues ) );
438
439 uno::Any aResult
440 = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv );
441
442 uno::Sequence< uno::Any > aErrors;
443 aResult >>= aErrors;
444
445 OSL_ENSURE( aErrors.getLength() == 1,
446 "getPropertyValues return value invalid!" );
447
448 if ( aErrors[ 0 ].hasValue() )
449 {
450 // error occurred.
451 OSL_FAIL( "error setting Title property!" );
452 return false;
453 }
454 }
455 catch ( uno::RuntimeException const & )
456 {
457 throw;
458 }
459 catch ( uno::Exception const & )
460 {
461 return false;
462 }
463
464 return true;
465}
466
468uno::Reference< ucb::XContent > createNew(
469 const TransferCommandContext & rContext,
470 const uno::Reference< ucb::XContent > & xTarget,
471 bool bSourceIsFolder,
472 bool bSourceIsDocument,
473 bool bSourceIsLink )
474{
475
476
477 // (1) Obtain creatable types from target.
478
479
480 // First, try it using "CreatabeleContentsInfo" property and
481 // "createNewContent" command -> the "new" way.
482
483 uno::Reference< ucb::XCommandProcessor > xCommandProcessorT(
484 xTarget, uno::UNO_QUERY );
485 if ( !xCommandProcessorT.is() )
486 {
487 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
488 {
489 {"Folder", uno::Any(rContext.aArg.TargetURL)}
490 }));
492 ucb::IOErrorCode_CANT_CREATE,
493 aArgs,
494 rContext.xOrigEnv,
495 "Target is no XCommandProcessor!",
496 rContext.xProcessor );
497 // Unreachable
498 }
499
500 uno::Sequence< beans::Property > aPropsToObtain{ makeProperty("CreatableContentsInfo", -1) };
501
502 ucb::Command aGetPropsCommand(
503 "getPropertyValues",
504 -1,
505 uno::Any( aPropsToObtain ) );
506
507 uno::Reference< sdbc::XRow > xRow;
508 xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
509
510 uno::Sequence< ucb::ContentInfo > aTypesInfo;
511 bool bGotTypesInfo = false;
512
513 if ( xRow.is() )
514 {
515 uno::Any aValue = xRow->getObject(
516 1, uno::Reference< container::XNameAccess >() );
517 if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) )
518 {
519 bGotTypesInfo = true;
520 }
521 }
522
523 uno::Reference< ucb::XContentCreator > xCreator;
524
525 if ( !bGotTypesInfo )
526 {
527 // Second, try it using XContentCreator interface -> the "old" way (not
528 // providing the chance to supply an XCommandEnvironment.
529
530 xCreator.set( xTarget, uno::UNO_QUERY );
531
532 if ( !xCreator.is() )
533 {
534 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
535 {
536 {"Folder", uno::Any(rContext.aArg.TargetURL)}
537 }));
539 ucb::IOErrorCode_CANT_CREATE,
540 aArgs,
541 rContext.xOrigEnv,
542 "Target is no XContentCreator!",
543 rContext.xProcessor );
544 // Unreachable
545 }
546
547 aTypesInfo = xCreator->queryCreatableContentsInfo();
548 }
549
550 if ( !aTypesInfo.hasElements() )
551 {
552 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
553 {
554 {"Folder", uno::Any(rContext.aArg.TargetURL)}
555 }));
557 ucb::IOErrorCode_CANT_CREATE,
558 aArgs,
559 rContext.xOrigEnv,
560 "No types creatable!",
561 rContext.xProcessor );
562 // Unreachable
563 }
564
565 // (2) Try to find a matching target type for the source object.
566
567 std::function<bool(const sal_Int32)> lCompare;
568
569 if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK )
570 {
571 // Create link
572 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
573 }
574 else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) ||
575 ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) )
576 {
577 // Copy / Move
578 // Is source a link? Create link in target folder then.
579 if ( bSourceIsLink )
580 {
581 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
582 }
583 else
584 {
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 ) ) ;
590 };
591 }
592 }
593 else
594 {
596 uno::Any( lang::IllegalArgumentException(
597 "Unknown transfer operation!",
598 rContext.xProcessor,
599 -1 ) ),
600 rContext.xOrigEnv );
601 // Unreachable
602 }
603
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))
608 {
609 // (3) Create a new, empty object of matched type.
610
611 if ( !xCreator.is() )
612 {
613 // First, try it using "CreatabeleContentsInfo" property and
614 // "createNewContent" command -> the "new" way.
615 ucb::Command aCreateNewCommand(
616 "createNewContent",
617 -1,
618 uno::Any( *pTypeInfo ) );
619
620 xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv )
621 >>= xNew;
622 }
623 else
624 {
625 // Second, try it using XContentCreator interface -> the "old"
626 // way (not providing the chance to supply an XCommandEnvironment.
627
628 xNew = xCreator->createNewContent( *pTypeInfo );
629 }
630
631 if ( !xNew.is() )
632 {
633 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
634 {
635 {"Folder", uno::Any(rContext.aArg.TargetURL)}
636 }));
638 ucb::IOErrorCode_CANT_CREATE,
639 aArgs,
640 rContext.xOrigEnv,
641 "createNewContent failed!",
642 rContext.xProcessor );
643 // Unreachable
644 }
645 }
646
647 return xNew;
648}
649
651void transferProperties(
652 const TransferCommandContext & rContext,
653 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS,
654 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN )
655{
656 ucb::Command aGetPropertySetInfoCommand(
657 "getPropertySetInfo",
658 -1,
659 uno::Any() );
660
661 uno::Reference< beans::XPropertySetInfo > xInfo;
662 xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv )
663 >>= xInfo;
664
665 if ( !xInfo.is() )
666 {
667 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
668 {
669 {"Uri", uno::Any(rContext.aArg.SourceURL)}
670 }));
672 ucb::IOErrorCode_CANT_READ,
673 aArgs,
674 rContext.xOrigEnv,
675 "Unable to get propertyset info from source object!",
676 rContext.xProcessor );
677 // Unreachable
678 }
679
680 uno::Sequence< beans::Property > aAllProps = xInfo->getProperties();
681
682 ucb::Command aGetPropsCommand1(
683 "getPropertyValues",
684 -1,
685 uno::Any( aAllProps ) );
686
687 uno::Reference< sdbc::XRow > xRow1;
688 xCommandProcessorS->execute(
689 aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1;
690
691 if ( !xRow1.is() )
692 {
693 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
694 {
695 {"Uri", uno::Any(rContext.aArg.SourceURL)}
696 }));
698 ucb::IOErrorCode_CANT_READ,
699 aArgs,
700 rContext.xOrigEnv,
701 "Unable to get properties from source object!",
702 rContext.xProcessor );
703 // Unreachable
704 }
705
706 // Assemble data structure for setPropertyValues command.
707
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();
712
713 bool bHasTitle = rContext.aArg.NewTitle.isEmpty();
714 bool bHasTargetURL = ( rContext.aArg.Operation
715 != ucb::TransferCommandOperation_LINK );
716
717 sal_Int32 nWritePos = 0;
718 for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m )
719 {
720 const beans::Property & rCurrProp = aAllProps[ m ];
721 beans::PropertyValue & rCurrValue = pPropValues[ nWritePos ];
722
723 uno::Any aValue;
724
725 if ( rCurrProp.Name == "Title" )
726 {
727 // Supply new title, if given.
728 if ( !bHasTitle )
729 {
730 bHasTitle = true;
731 aValue <<= rContext.aArg.NewTitle;
732 }
733 }
734 else if ( rCurrProp.Name == "TargetURL" )
735 {
736 // Supply source URL as link target for the new link to create.
737 if ( !bHasTargetURL )
738 {
739 bHasTargetURL = true;
740 aValue <<= rContext.aArg.SourceURL;
741 }
742 }
743
744 if ( !aValue.hasValue() )
745 {
746 try
747 {
748 aValue = xRow1->getObject(
749 m + 1, uno::Reference< container::XNameAccess >() );
750 }
751 catch ( sdbc::SQLException const & )
752 {
753 // Argh! But try to bring things to an end. Perhaps the
754 // mad property is not really important...
755 }
756 }
757
758 if ( aValue.hasValue() )
759 {
760 rCurrValue.Name = rCurrProp.Name;
761 rCurrValue.Handle = rCurrProp.Handle;
762 rCurrValue.Value = aValue;
763// rCurrValue.State =
764
765 nWritePos++;
766 }
767 }
768
769 // Title needed, but not set yet?
770 if ( !bHasTitle && !rContext.aArg.NewTitle.isEmpty() )
771 {
772 pPropValues[ nWritePos ].Name = "Title";
773 pPropValues[ nWritePos ].Handle = -1;
774 pPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle;
775
776 nWritePos++;
777 }
778
779 // TargetURL needed, but not set yet?
780 if ( !bHasTargetURL && ( rContext.aArg.Operation
781 == ucb::TransferCommandOperation_LINK ) )
782 {
783 pPropValues[ nWritePos ].Name = "TargetURL";
784 pPropValues[ nWritePos ].Handle = -1;
785 pPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL;
786
787 nWritePos++;
788 }
789
790 aPropValues.realloc( nWritePos );
791
792 // Set properties at new object.
793
794 ucb::Command aSetPropsCommand(
795 "setPropertyValues",
796 -1,
797 uno::Any( aPropValues ) );
798
799 xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv );
800
801 // @@@ What to do with source props that are not supported by the
802 // new object? addProperty ???
803}
804
806uno::Reference< io::XInputStream > getInputStream(
807 const TransferCommandContext & rContext,
808 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
809{
810 uno::Reference< io::XInputStream > xInputStream;
811
812
813 // (1) Try to get data as XInputStream via XActiveDataSink.
814
815
816 try
817 {
818 uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink;
819
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
825
826 ucb::Command aOpenCommand(
827 "open",
828 -1,
829 uno::Any( aArg ) );
830
831 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
832 xInputStream = xSink->getInputStream();
833 }
834 catch ( uno::RuntimeException const & )
835 {
836 throw;
837 }
838 catch ( uno::Exception const & )
839 {
840 // will be handled below.
841 }
842
843 if ( !xInputStream.is() )
844 {
845
846
847 // (2) Try to get data via XOutputStream.
848
849
850 try
851 {
852 uno::Reference< io::XOutputStream > xOutputStream( io::Pipe::create(rContext.m_xContext), uno::UNO_QUERY_THROW );
853
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 );
859
860 ucb::Command aOpenCommand(
861 "open",
862 -1,
863 uno::Any( aArg ) );
864
865 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv );
866
867 xInputStream.set( xOutputStream, uno::UNO_QUERY );
868 }
869 catch ( uno::RuntimeException const & )
870 {
871 throw;
872 }
873 catch ( uno::Exception const & )
874 {
875 OSL_FAIL( "unable to get input stream from document!" );
876 }
877 }
878
879 return xInputStream;
880}
881
883uno::Reference< sdbc::XResultSet > getResultSet(
884 const TransferCommandContext & rContext,
885 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS )
886{
887 uno::Reference< sdbc::XResultSet > xResultSet;
888
889 uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */),
890 makeProperty("IsDocument", -1 /* unknown */),
891 makeProperty("TargetURL", -1 /* unknown */) };
892
893 ucb::OpenCommandArgument2 aArg;
894 aArg.Mode = ucb::OpenMode::ALL;
895 aArg.Priority = 0; // unused
896 aArg.Sink = nullptr;
897 aArg.Properties = aProps;
898
899 ucb::Command aOpenCommand( "open",
900 -1,
901 uno::Any( aArg ) );
902 try
903 {
904 uno::Reference< ucb::XDynamicResultSet > xSet;
905 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet;
906
907 if ( xSet.is() )
908 xResultSet = xSet->getStaticResultSet();
909 }
910 catch ( uno::RuntimeException const & )
911 {
912 throw;
913 }
914 catch ( uno::Exception const & )
915 {
916 OSL_FAIL( "unable to get result set from folder!" );
917 }
918
919 return xResultSet;
920}
921
923void 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 )
931{
932 sal_Int32 nTry = 0;
933
934 // Obtain old title.
935 uno::Sequence< beans::Property > aProps{ makeProperty("Title", -1) };
936
937 ucb::Command aGetPropsCommand(
938 "getPropertyValues",
939 -1,
940 uno::Any( aProps ) );
941
942 uno::Reference< sdbc::XRow > xRow;
943 xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
944
945 if ( !xRow.is() )
946 {
947 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
948 {
949 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
950 }));
952 ucb::IOErrorCode_CANT_READ,
953 aArgs,
954 rContext.xOrigEnv,
955 "Unable to get properties from new object!",
956 rContext.xProcessor );
957 // Unreachable
958 }
959
960 OUString aOldTitle = xRow->getString( 1 );
961 if ( aOldTitle.isEmpty() )
962 {
964 uno::Any( beans::UnknownPropertyException(
965 "Unable to get property 'Title' from new object!",
966 rContext.xProcessor ) ),
967 rContext.xOrigEnv );
968 // Unreachable
969 }
970
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 )
976 {
977 aOldTitlePre = aOldTitle.copy( 0, nPos );
978 aOldTitlePost = aOldTitle.copy( nPos );
979 }
980 else
981 aOldTitlePre = aOldTitle;
982
983 if ( nPos > 0 )
984 aOldTitlePre += "_";
985
986 bool bContinue = true;
987 do
988 {
989 nTry++;
990
991 OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) +
992 aOldTitlePost;
993
994 // Set new title
995 setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle );
996
997 // Retry inserting the content.
998 try
999 {
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() )
1003 {
1004 uno::Reference< io::XSeekable > xSeekable(
1005 xInputStream, uno::UNO_QUERY );
1006 if ( xSeekable.is() )
1007 {
1008 try
1009 {
1010 xSeekable->seek( 0 );
1011 }
1012 catch ( lang::IllegalArgumentException const & )
1013 {
1014 xInputStream.clear();
1015 }
1016 catch ( io::IOException const & )
1017 {
1018 xInputStream.clear();
1019 }
1020 }
1021 else
1022 xInputStream.clear();
1023
1024 if ( !xInputStream.is() )
1025 {
1026 xInputStream
1027 = getInputStream( rContext, xCommandProcessorS );
1028 if ( !xInputStream.is() )
1029 {
1030 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1031 {
1032 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
1033 }));
1035 ucb::IOErrorCode_CANT_READ,
1036 aArgs,
1037 rContext.xOrigEnv,
1038 "Got no data stream from source!",
1039 rContext.xProcessor );
1040 // Unreachable
1041 }
1042 }
1043 }
1044
1045 ucb::InsertCommandArgument2 aArg;
1046 aArg.Data = xInputStream;
1047 aArg.ReplaceExisting = false;
1048
1049 ucb::Command aInsertCommand(
1050 "insert",
1051 -1,
1052 uno::Any( aArg ) );
1053
1054 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1055
1056 // Success!
1057 bContinue = false;
1058 }
1059 catch ( uno::RuntimeException const & )
1060 {
1061 throw;
1062 }
1063 catch ( uno::Exception const & )
1064 {
1065 }
1066 }
1067 while ( bContinue && ( nTry < 50 ) );
1068
1069 if ( nTry == 50 )
1070 {
1072 uno::Any(
1073 ucb::UnsupportedNameClashException(
1074 "Unable to resolve name clash!",
1075 rContext.xProcessor,
1076 ucb::NameClash::RENAME ) ),
1077 rContext.xOrigEnv );
1078 // Unreachable
1079 }
1080}
1081
1083void 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 )
1088{
1089 // IsFolder: property is required.
1090 bool bSourceIsFolder = xSourceProps->getBoolean( 1 );
1091 if ( !bSourceIsFolder && xSourceProps->wasNull() )
1092 {
1094 uno::Any( beans::UnknownPropertyException(
1095 "Unable to get property 'IsFolder' from source object!",
1096 rContext.xProcessor ) ),
1097 rContext.xOrigEnv );
1098 // Unreachable
1099 }
1100
1101 // IsDocument: property is required.
1102 bool bSourceIsDocument = xSourceProps->getBoolean( 2 );
1103 if ( !bSourceIsDocument && xSourceProps->wasNull() )
1104 {
1106 uno::Any( beans::UnknownPropertyException(
1107 "Unable to get property 'IsDocument' from source object!",
1108 rContext.xProcessor ) ),
1109 rContext.xOrigEnv );
1110 // Unreachable
1111 }
1112
1113 // TargetURL: property is optional.
1114 bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty();
1115
1116
1117 // (1) Try to find a matching target type for the source object and
1118 // create a new, empty object of that type.
1119
1120
1121 uno::Reference< ucb::XContent > xNew = createNew( rContext,
1122 xTarget,
1123 bSourceIsFolder,
1124 bSourceIsDocument,
1125 bSourceIsLink );
1126 if ( !xNew.is() )
1127 {
1128 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1129 {
1130 {"Folder", uno::Any(rContext.aArg.TargetURL)}
1131 }));
1133 ucb::IOErrorCode_CANT_CREATE,
1134 aArgs,
1135 rContext.xOrigEnv,
1136 "No matching content type at target!",
1137 rContext.xProcessor );
1138 // Unreachable
1139 }
1140
1141
1142 // (2) Transfer property values from source to new object.
1143
1144
1145 uno::Reference< ucb::XCommandProcessor > xCommandProcessorN(
1146 xNew, uno::UNO_QUERY );
1147 if ( !xCommandProcessorN.is() )
1148 {
1149 uno::Any aProps(beans::PropertyValue(
1150 "Uri",
1151 -1,
1152 uno::Any(
1153 xNew->getIdentifier()->
1154 getContentIdentifier()),
1155 beans::PropertyState_DIRECT_VALUE));
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
1163 }
1164
1165 // Obtain all properties from source.
1166
1167 uno::Reference< ucb::XCommandProcessor > xCommandProcessorS(
1168 xSource, uno::UNO_QUERY );
1169 if ( !xCommandProcessorS.is() )
1170 {
1171 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1172 {
1173 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1174 }));
1176 ucb::IOErrorCode_CANT_READ,
1177 aArgs,
1178 rContext.xOrigEnv,
1179 "Source content is not a XCommandProcessor!",
1180 rContext.xProcessor );
1181 // Unreachable
1182 }
1183
1184 transferProperties( rContext, xCommandProcessorS, xCommandProcessorN );
1185
1186
1187 // (3) Try to obtain a data stream from source.
1188
1189
1190 uno::Reference< io::XInputStream > xInputStream;
1191
1192 if ( bSourceIsDocument && ( rContext.aArg.Operation
1193 != ucb::TransferCommandOperation_LINK ) )
1194 xInputStream = getInputStream( rContext, xCommandProcessorS );
1195
1196
1197 // (4) Try to obtain a resultset (children) from source.
1198
1199
1200 uno::Reference< sdbc::XResultSet > xResultSet;
1201
1202 if ( bSourceIsFolder && ( rContext.aArg.Operation
1203 != ucb::TransferCommandOperation_LINK ) )
1204 xResultSet = getResultSet( rContext, xCommandProcessorS );
1205
1206
1207 // (5) Insert (store) new content.
1208
1209
1210 ucb::InsertCommandArgument2 aArg;
1211 aArg.Data = xInputStream;
1212 aArg.MimeType = rContext.aArg.MimeType;
1213 aArg.DocumentId = rContext.aArg.DocumentId;
1214
1215 switch ( rContext.aArg.NameClash )
1216 {
1217 case ucb::NameClash::OVERWRITE:
1218 aArg.ReplaceExisting = true;
1219 break;
1220
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;
1227
1228 default:
1229 aArg.ReplaceExisting = false;
1230 OSL_FAIL( "Unknown nameclash directive!" );
1231 break;
1232 }
1233
1234 OUString aDesiredName = createDesiredName( rContext.aArg );
1235
1236 bool bRetry;
1237 do
1238 {
1239 bRetry = false;
1240
1241 try
1242 {
1243 ucb::Command aInsertCommand(
1244 "insert",
1245 -1,
1246 uno::Any( aArg ) );
1247
1248 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1249 }
1250 catch ( ucb::UnsupportedNameClashException const & exc )
1251 {
1252 OSL_ENSURE( !aArg.ReplaceExisting,
1253 "BUG: UnsupportedNameClashException not allowed here!" );
1254
1255 if (exc.NameClash != ucb::NameClash::ERROR) {
1256 OSL_FAIL( "BUG: NameClash::ERROR expected!" );
1257 }
1258
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 );
1266 }
1267 catch ( ucb::NameClashException const & )
1268 {
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.
1273
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.
1278
1279 // @@@ 'insert' command should be extended that it accepts a
1280 // name clash handling directive, exactly like 'transfer' command.
1281
1282 switch ( rContext.aArg.NameClash )
1283 {
1284 case ucb::NameClash::OVERWRITE:
1285 {
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
1295 }
1296
1297 case ucb::NameClash::ERROR:
1298 throw;
1299
1300 case ucb::NameClash::RENAME:
1301 {
1302 // "invent" a new valid title.
1303 handleNameClashRename( rContext,
1304 xNew,
1305 xCommandProcessorN,
1306 xCommandProcessorS,
1307 xInputStream );
1308 break;
1309 }
1310
1311 case ucb::NameClash::ASK:
1312 {
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 );
1322
1323 switch ( eCont )
1324 {
1325 case NOT_HANDLED:
1326 // Not handled.
1327 cppu::throwException( aExc );
1328 [[fallthrough]]; // break;
1329
1330 case UNKNOWN:
1331 // Handled, but not clear, how...
1332 // fall through intended.
1333
1334 case ABORT:
1335 throw ucb::CommandFailedException(
1336 "abort requested via interaction "
1337 "handler",
1338 uno::Reference< uno::XInterface >(),
1339 aExc );
1340 // break;
1341
1342 case OVERWRITE:
1343 OSL_ENSURE( !aArg.ReplaceExisting,
1344 "Hu? ReplaceExisting already true?"
1345 );
1346 aArg.ReplaceExisting = true;
1347 bRetry = true;
1348 break;
1349
1350 case NEW_NAME:
1351 {
1352 // set new name -> set "Title" property...
1353 if ( setTitle( xCommandProcessorN,
1354 rContext.xEnv,
1355 aNewTitle ) )
1356 {
1357 // remember suggested title...
1358 aDesiredName = aNewTitle;
1359
1360 // ... and try again.
1361 bRetry = true;
1362 }
1363 else
1364 {
1365 // error setting title. Abort.
1366 throw ucb::CommandFailedException(
1367 "error setting Title property!",
1368 uno::Reference< uno::XInterface >(),
1369 aExc );
1370 }
1371 break;
1372 }
1373 }
1374
1375 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1376 }
1377 break;
1378
1379 case ucb::NameClash::KEEP: // deprecated
1380 default:
1381 {
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
1391 }
1392 }
1393 }
1394 }
1395 while ( bRetry );
1396
1397
1398 // (6) Process children of source.
1399
1400
1401 if ( xResultSet.is() )
1402 {
1403 try
1404 {
1405 // Iterate over children...
1406
1407 uno::Reference< sdbc::XRow > xChildRow(
1408 xResultSet, uno::UNO_QUERY );
1409
1410 if ( !xChildRow.is() )
1411 {
1412 uno::Any aProps(
1413 beans::PropertyValue(
1414 "Uri",
1415 -1,
1416 uno::Any(rContext.aArg.SourceURL),
1417 beans::PropertyState_DIRECT_VALUE));
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
1425 }
1426
1427 uno::Reference< ucb::XContentAccess > xChildAccess(
1428 xResultSet, uno::UNO_QUERY );
1429
1430 if ( !xChildAccess.is() )
1431 {
1432 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1433 {
1434 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1435 }));
1437 ucb::IOErrorCode_CANT_READ,
1438 aArgs,
1439 rContext.xOrigEnv,
1440 "Unable to get children of source!",
1441 rContext.xProcessor );
1442 // Unreachable
1443 }
1444
1445 if ( xResultSet->first() )
1446 {
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);
1456
1457 TransferCommandContext aSubCtx(
1458 rContext.m_xContext,
1459 rContext.xProcessor,
1460 rContext.xEnv,
1461 rContext.xOrigEnv,
1462 std::move(aTransArg) );
1463 do
1464 {
1465 uno::Reference< ucb::XContent > xChild
1466 = xChildAccess->queryContent();
1467 if ( xChild.is() )
1468 {
1469 // Recursion!
1470
1471 aSubCtx.aArg.SourceURL
1472 = xChild->getIdentifier()->getContentIdentifier();
1473
1474 globalTransfer_( aSubCtx,
1475 xChild,
1476 xNew,
1477 xChildRow );
1478 }
1479 }
1480 while ( xResultSet->next() );
1481 }
1482 }
1483 catch ( sdbc::SQLException const & )
1484 {
1485 }
1486 }
1487
1488 try {
1489 uno::Reference< ucb::XCommandProcessor > xcp(
1490 xTarget, uno::UNO_QUERY );
1491
1492 uno::Any aAny;
1493 uno::Reference< ucb::XCommandInfo > xci;
1494 if(xcp.is())
1495 aAny =
1496 xcp->execute(
1497 ucb::Command(
1498 "getCommandInfo",
1499 -1,
1500 uno::Any()),
1501 0,
1502 rContext.xEnv );
1503
1504 static const OUStringLiteral cmdName(u"flush");
1505 if((aAny >>= xci) && xci->hasCommandByName(cmdName))
1506 xcp->execute(
1507 ucb::Command(
1508 cmdName,
1509 -1,
1510 uno::Any()) ,
1511 0,
1512 rContext.xEnv );
1513 }
1514 catch( uno::Exception const & )
1515 {
1516 }
1517}
1518
1519} /* namespace */
1520
1521
1522// UniversalContentBroker implementation ( XCommandProcessor commands ).
1523
1524
1525uno::Reference< ucb::XCommandInfo >
1527{
1528 return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1529}
1530
1531
1533 const ucb::GlobalTransferCommandArgument2 & rArg,
1534 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1535{
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())
1541 {
1542 xLocalEnv.set( ucb::CommandEnvironment::create(
1543 m_xContext,
1544 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1545 xEnv->getProgressHandler() ) );
1546 }
1547
1548
1549 // (1) Try to transfer the content using 'transfer' command.
1550
1551
1552 uno::Reference< ucb::XContent > xTarget;
1553 uno::Reference< ucb::XContentIdentifier > xId
1554 = createContentIdentifier( rArg.TargetURL );
1555 if ( xId.is() )
1556 {
1557 try
1558 {
1559 xTarget = queryContent( xId );
1560 }
1561 catch ( ucb::IllegalIdentifierException const & )
1562 {
1563 }
1564 }
1565
1566 if ( !xTarget.is() )
1567 {
1568 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1569 {
1570 {"Uri", uno::Any(rArg.TargetURL)}
1571 }));
1573 ucb::IOErrorCode_CANT_READ,
1574 aArgs,
1575 xEnv,
1576 "Can't instantiate target object!",
1577 this );
1578 // Unreachable
1579 }
1580
1581 if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) ||
1582 ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) )
1583 {
1584 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1585 xTarget, uno::UNO_QUERY );
1586 if ( !xCommandProcessor.is() )
1587 {
1588 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1589 {
1590 {"Uri", uno::Any(rArg.TargetURL)}
1591 }));
1593 ucb::IOErrorCode_CANT_READ,
1594 aArgs,
1595 xEnv,
1596 "Target content is not a XCommandProcessor!",
1597 this );
1598 // Unreachable
1599 }
1600
1601 ucb::TransferInfo2 aTransferArg(
1602 ( rArg.Operation
1603 == ucb::TransferCommandOperation_MOVE ), // MoveData
1604 rArg.SourceURL,
1605 rArg.NewTitle,
1606 rArg.NameClash,
1607 rArg.MimeType );
1608
1609 bool bRetry;
1610 do
1611 {
1612 bRetry = false;
1613
1614 try
1615 {
1616 ucb::Command aCommand(
1617 "transfer", // Name
1618 -1, // Handle
1619 uno::Any( aTransferArg ) ); // Argument
1620
1621 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1622
1623 // Command succeeded. We're done.
1624 return;
1625 }
1626 catch ( ucb::InteractiveBadTransferURLException const & )
1627 {
1628 // Source URL is not supported by target. Try to transfer
1629 // the content "manually".
1630 }
1631 catch ( ucb::UnsupportedCommandException const & )
1632 {
1633 // 'transfer' command is not supported by commandprocessor.
1634 // Try to transfer manually.
1635 }
1636 catch ( ucb::UnsupportedNameClashException const & exc )
1637 {
1638 OSL_ENSURE( aTransferArg.NameClash == exc.NameClash,
1639 "nameclash mismatch!" );
1640 if ( exc.NameClash == ucb::NameClash::ASK )
1641 {
1642 // Try to detect a name clash by invoking "transfer" with
1643 // NameClash::ERROR.
1644 try
1645 {
1646 ucb::TransferInfo2 aTransferArg1(
1647 aTransferArg.MoveData,
1648 aTransferArg.SourceURL,
1649 aTransferArg.NewTitle,
1650 ucb::NameClash::ERROR,
1651 aTransferArg.MimeType );
1652
1653 ucb::Command aCommand1(
1654 "transfer",
1655 -1,
1656 uno::Any( aTransferArg1 ) );
1657
1658 xCommandProcessor->execute( aCommand1, 0, xLocalEnv );
1659
1660 // Command succeeded. We're done.
1661 return;
1662 }
1663 catch ( ucb::UnsupportedNameClashException const & )
1664 {
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;'!
1668 }
1669 catch ( ucb::NameClashException const & )
1670 {
1671 // There's a clash. Use interaction handler to solve it.
1672
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 );
1683
1684 switch ( eCont )
1685 {
1686 case NOT_HANDLED:
1687 // Not handled.
1688 cppu::throwException( aExc );
1689 [[fallthrough]]; // break;
1690
1691 case UNKNOWN:
1692 // Handled, but not clear, how...
1693 // fall through intended.
1694
1695 case ABORT:
1696 throw ucb::CommandFailedException(
1697 "abort requested via interaction "
1698 "handler",
1699 uno::Reference< uno::XInterface >(),
1700 aExc );
1701// break;
1702
1703 case OVERWRITE:
1704 aTransferArg.NameClash
1705 = ucb::NameClash::OVERWRITE;
1706 bRetry = true;
1707 break;
1708
1709 case NEW_NAME:
1710 aTransferArg.NewTitle = aNewTitle;
1711 bRetry = true;
1712 break;
1713 }
1714
1715 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1716 }
1717 }
1718 else
1719 {
1720 throw;
1721 }
1722 }
1723 }
1724 while ( bRetry );
1725 }
1726
1727
1728 // (2) Try to transfer the content "manually".
1729
1730
1731 uno::Reference< ucb::XContent > xSource;
1732 try
1733 {
1734 uno::Reference< ucb::XContentIdentifier > xId2
1735 = createContentIdentifier( rArg.SourceURL );
1736 if ( xId2.is() )
1737 xSource = queryContent( xId2 );
1738 }
1739 catch ( ucb::IllegalIdentifierException const & )
1740 {
1741 // Error handling via "if ( !xSource.is() )" below.
1742 }
1743
1744 if ( !xSource.is() )
1745 {
1746 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1747 {
1748 {"Uri", uno::Any(rArg.SourceURL)}
1749 }));
1751 ucb::IOErrorCode_CANT_READ,
1752 aArgs,
1753 xEnv,
1754 "Can't instantiate source object!",
1755 this );
1756 // Unreachable
1757 }
1758
1759 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1760 xSource, uno::UNO_QUERY );
1761 if ( !xCommandProcessor.is() )
1762 {
1763 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1764 {
1765 {"Uri", uno::Any(rArg.SourceURL)}
1766 }));
1768 ucb::IOErrorCode_CANT_READ,
1769 aArgs,
1770 xEnv,
1771 "Source content is not a XCommandProcessor!",
1772 this );
1773 // Unreachable
1774 }
1775
1776 // Obtain interesting property values from source...
1777
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 */) };
1782
1783 ucb::Command aGetPropsCommand(
1784 "getPropertyValues",
1785 -1,
1786 uno::Any( aProps ) );
1787
1788 uno::Reference< sdbc::XRow > xRow;
1789 xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow;
1790
1791 if ( !xRow.is() )
1792 {
1793 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1794 {
1795 {"Uri", uno::Any(rArg.SourceURL)}
1796 }));
1798 ucb::IOErrorCode_CANT_READ,
1799 aArgs,
1800 xEnv,
1801 "Unable to get properties from source object!",
1802 this );
1803 // Unreachable
1804 }
1805
1806 TransferCommandContext aTransferCtx(
1807 m_xContext, this, xLocalEnv, xEnv, rArg );
1808
1809 if ( rArg.NewTitle.isEmpty() )
1810 {
1811 // BaseURI: property is optional.
1812 OUString aBaseURI( xRow->getString( 4 ) );
1813 if ( !aBaseURI.isEmpty() )
1814 {
1815 aTransferCtx.aArg.NewTitle
1816 = createDesiredName( aBaseURI, OUString() );
1817 }
1818 }
1819
1820 // Do it!
1821 globalTransfer_( aTransferCtx, xSource, xTarget, xRow );
1822
1823
1824 // (3) Delete source, if operation is MOVE.
1825
1826
1827 if ( rArg.Operation != ucb::TransferCommandOperation_MOVE )
1828 return;
1829
1830 try
1831 {
1832 ucb::Command aCommand(
1833 "delete", // Name
1834 -1, // Handle
1835 uno::Any( true ) ); // Argument
1836
1837 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1838 }
1839 catch ( uno::Exception const & )
1840 {
1841 OSL_FAIL( "Cannot delete source object!" );
1842 throw;
1843 }
1844}
1845
1846uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg,
1847 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1848{
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())
1855 {
1856 xLocalEnv.set( ucb::CommandEnvironment::create(
1857 m_xContext,
1858 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1859 xEnv->getProgressHandler() ) );
1860 }
1861
1862 uno::Reference< ucb::XContent > xTarget;
1863 uno::Reference< ucb::XContentIdentifier > xId
1864 = createContentIdentifier( rArg.TargetURL );
1865 if ( xId.is() )
1866 {
1867 try
1868 {
1869 xTarget = queryContent( xId );
1870 }
1871 catch ( ucb::IllegalIdentifierException const & )
1872 {
1873 }
1874 }
1875
1876 if ( !xTarget.is() )
1877 {
1878 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1879 {
1880 {"Uri", uno::Any(rArg.TargetURL)}
1881 }));
1883 ucb::IOErrorCode_CANT_READ,
1884 aArgs,
1885 xEnv,
1886 "Can't instantiate target object!",
1887 this );
1888 // Unreachable
1889 }
1890
1891 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1892 xTarget, uno::UNO_QUERY );
1893 if ( !xCommandProcessor.is() )
1894 {
1895 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1896 {
1897 {"Uri", uno::Any(rArg.TargetURL)}
1898 }));
1900 ucb::IOErrorCode_CANT_READ,
1901 aArgs,
1902 xEnv,
1903 "Target content is not a XCommandProcessor!",
1904 this );
1905 // Unreachable
1906 }
1907
1908 try
1909 {
1910 ucb::Command aCommand(
1911 "checkin", -1,
1912 uno::Any( rArg ) );
1913
1914 aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1915 }
1916 catch ( ucb::UnsupportedCommandException const & )
1917 {
1918 // 'checkin' command is not supported by commandprocessor:
1919 // ignore.
1920 }
1921 return aRet;
1922}
1923
1924/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
XPropertyListType t
::rtl::Reference< IEventProcessor > xProcessor
static css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo()
Definition: ucbcmds.cxx:1526
css::uno::Reference< css::uno::XComponentContext > m_xContext
Definition: ucb.hxx:146
void globalTransfer(const css::ucb::GlobalTransferCommandArgument2 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
Definition: ucbcmds.cxx:1532
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier) override
Definition: ucb.cxx:463
css::uno::Any checkIn(const css::ucb::CheckinArgument &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
Definition: ucbcmds.cxx:1846
virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL createContentIdentifier(const OUString &ContentId) override
Definition: ucb.cxx:511
Reference< XInterface > xTarget
Reference< beans::XPropertySetInfo > m_xInfo
UNKNOWN
OUString aName
sal_Int64 n
uno_Any a
tools::SvRef< SvBaseLink > xSink
sal_uInt16 nPos
OVERWRITE
ABORT
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
void SAL_CALL throwException(Any const &exc)
m
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
sal_Int32 h
bool hasValue()
Object Value
OUString aCommand
OUString Name
unsigned char sal_Bool
constexpr OUStringLiteral GLOBALTRANSFER_NAME
Definition: ucbcmds.hxx:30
constexpr OUStringLiteral CHECKIN_NAME
Definition: ucbcmds.hxx:33
#define GLOBALTRANSFER_HANDLE
Definition: ucbcmds.hxx:31
#define CHECKIN_HANDLE
Definition: ucbcmds.hxx:34
constexpr OUStringLiteral GETCOMMANDINFO_NAME
Definition: ucbcmds.hxx:27
#define GETCOMMANDINFO_HANDLE
Definition: ucbcmds.hxx:28
Reference< XStream > m_xStream