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