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 <memory>
21 #include <osl/diagnose.h>
22 #include <comphelper/propertysequence.hxx>
23 #include <cppuhelper/implbase.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
25 #include <rtl/ustring.hxx>
26 #include <com/sun/star/uno/XInterface.hpp>
27 #include <com/sun/star/beans/PropertyState.hpp>
28 #include <com/sun/star/beans/PropertyValue.hpp>
29 #include <com/sun/star/beans/XPropertySetInfo.hpp>
30 #include <com/sun/star/io/IOException.hpp>
31 #include <com/sun/star/io/Pipe.hpp>
32 #include <com/sun/star/io/XActiveDataSink.hpp>
33 #include <com/sun/star/io/XOutputStream.hpp>
34 #include <com/sun/star/io/XSeekable.hpp>
35 #include <com/sun/star/lang/IllegalArgumentException.hpp>
36 #include <com/sun/star/sdbc/SQLException.hpp>
37 #include <com/sun/star/sdbc/XRow.hpp>
38 #include <com/sun/star/task/XInteractionHandler.hpp>
39 #include <com/sun/star/ucb/CommandEnvironment.hpp>
40 #include <com/sun/star/ucb/CommandFailedException.hpp>
41 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
42 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
43 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
44 #include <com/sun/star/ucb/InsertCommandArgument2.hpp>
45 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
46 #include <com/sun/star/ucb/NameClash.hpp>
47 #include <com/sun/star/ucb/NameClashException.hpp>
48 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
49 #include <com/sun/star/ucb/OpenMode.hpp>
50 #include <com/sun/star/ucb/TransferInfo2.hpp>
51 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
52 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
53 #include <com/sun/star/ucb/XCommandInfo.hpp>
54 #include <com/sun/star/ucb/XContentAccess.hpp>
55 #include <com/sun/star/ucb/XContentCreator.hpp>
56 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
57 #include <com/sun/star/ucb/XInteractionSupplyName.hpp>
58 #include <com/sun/star/uno/Any.hxx>
59 #include <com/sun/star/uno/Sequence.hxx>
60 #include <ucbhelper/cancelcommandexecution.hxx>
61 #include <ucbhelper/simplenameclashresolverequest.hxx>
62 #include "ucbcmds.hxx"
63 #include "ucb.hxx"
64
65 using namespace com::sun::star;
66
67 namespace
68 {
69
70
71 // struct TransferCommandContext.
72
73
74 struct TransferCommandContext
75 {
76 uno::Reference< uno::XComponentContext > m_xContext;
77 uno::Reference< ucb::XCommandProcessor > xProcessor;
78 uno::Reference< ucb::XCommandEnvironment > xEnv;
79 uno::Reference< ucb::XCommandEnvironment > xOrigEnv;
80 ucb::GlobalTransferCommandArgument2 aArg;
81
TransferCommandContext__anonbad44a700111::TransferCommandContext82 TransferCommandContext(
83 const uno::Reference< uno::XComponentContext > & xContext,
84 const uno::Reference< ucb::XCommandProcessor > & rxProcessor,
85 const uno::Reference< ucb::XCommandEnvironment > & rxEnv,
86 const uno::Reference< ucb::XCommandEnvironment > & rxOrigEnv,
87 const ucb::GlobalTransferCommandArgument2 & rArg )
88 : m_xContext( xContext ), xProcessor( rxProcessor ), xEnv( rxEnv ),
89 xOrigEnv( rxOrigEnv ), aArg( rArg ) {}
90 };
91
92
93
94
95 class InteractionHandlerProxy :
96 public cppu::WeakImplHelper< task::XInteractionHandler >
97 {
98 uno::Reference< task::XInteractionHandler > m_xOrig;
99
100 public:
InteractionHandlerProxy(const uno::Reference<task::XInteractionHandler> & xOrig)101 explicit InteractionHandlerProxy(
102 const uno::Reference< task::XInteractionHandler > & xOrig )
103 : m_xOrig( xOrig ) {}
104
105 // XInteractionHandler methods.
106 virtual void SAL_CALL handle(
107 const uno::Reference< task::XInteractionRequest >& Request ) override;
108 };
109
110
111 // virtual
handle(const uno::Reference<task::XInteractionRequest> & Request)112 void SAL_CALL InteractionHandlerProxy::handle(
113 const uno::Reference< task::XInteractionRequest >& Request )
114 {
115 if ( !m_xOrig.is() )
116 return;
117
118 // Filter unwanted requests by just not handling them.
119 uno::Any aRequest = Request->getRequest();
120
121 // "transfer"
122 ucb::InteractiveBadTransferURLException aBadTransferURLEx;
123 if ( aRequest >>= aBadTransferURLEx )
124 {
125 return;
126 }
127 else
128 {
129 // "transfer"
130 ucb::UnsupportedNameClashException aUnsupportedNameClashEx;
131 if ( aRequest >>= aUnsupportedNameClashEx )
132 {
133 if ( aUnsupportedNameClashEx.NameClash
134 != ucb::NameClash::ERROR )
135 return;
136 }
137 else
138 {
139 // "insert"
140 ucb::NameClashException aNameClashEx;
141 if ( aRequest >>= aNameClashEx )
142 {
143 return;
144 }
145 else
146 {
147 // "transfer"
148 ucb::UnsupportedCommandException aUnsupportedCommandEx;
149 if ( aRequest >>= aUnsupportedCommandEx )
150 {
151 return;
152 }
153 }
154 }
155 }
156
157 // not filtered; let the original handler do the work.
158 m_xOrig->handle( Request );
159 }
160
161
162
163
164 class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink >
165 {
166 uno::Reference< io::XInputStream > m_xStream;
167
168 public:
169 // XActiveDataSink methods.
170 virtual void SAL_CALL setInputStream(
171 const uno::Reference< io::XInputStream >& aStream ) override;
172 virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override;
173 };
174
175
176 // virtual
setInputStream(const uno::Reference<io::XInputStream> & aStream)177 void SAL_CALL ActiveDataSink::setInputStream(
178 const uno::Reference< io::XInputStream >& aStream )
179 {
180 m_xStream = aStream;
181 }
182
183
184 // virtual
getInputStream()185 uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream()
186 {
187 return m_xStream;
188 }
189
190
191
192
193 class CommandProcessorInfo :
194 public cppu::WeakImplHelper< ucb::XCommandInfo >
195 {
196 std::unique_ptr< uno::Sequence< ucb::CommandInfo > > m_pInfo;
197
198 public:
199 CommandProcessorInfo();
200
201 // XCommandInfo methods
202 virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() override;
203 virtual ucb::CommandInfo SAL_CALL
204 getCommandInfoByName( const OUString& Name ) override;
205 virtual ucb::CommandInfo SAL_CALL
206 getCommandInfoByHandle( sal_Int32 Handle ) override;
207 virtual sal_Bool SAL_CALL hasCommandByName( const OUString& Name ) override;
208 virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) override;
209 };
210
211
CommandProcessorInfo()212 CommandProcessorInfo::CommandProcessorInfo()
213 : m_pInfo( new uno::Sequence< ucb::CommandInfo >( 3 ) )
214 {
215 (*m_pInfo)[ 0 ]
216 = ucb::CommandInfo(
217 GETCOMMANDINFO_NAME, // Name
218 GETCOMMANDINFO_HANDLE, // Handle
219 cppu::UnoType<void>::get() ); // ArgType
220 (*m_pInfo)[ 1 ]
221 = ucb::CommandInfo(
222 GLOBALTRANSFER_NAME, // Name
223 GLOBALTRANSFER_HANDLE, // Handle
224 cppu::UnoType<ucb::GlobalTransferCommandArgument>::get() ); // ArgType
225 (*m_pInfo)[ 2 ]
226 = ucb::CommandInfo(
227 CHECKIN_NAME, // Name
228 CHECKIN_HANDLE, // Handle
229 cppu::UnoType<ucb::CheckinArgument>::get() ); // ArgType
230 }
231
232
233 // virtual
234 uno::Sequence< ucb::CommandInfo > SAL_CALL
getCommands()235 CommandProcessorInfo::getCommands()
236 {
237 return *m_pInfo;
238 }
239
240
241 // virtual
242 ucb::CommandInfo SAL_CALL
getCommandInfoByName(const OUString & Name)243 CommandProcessorInfo::getCommandInfoByName( const OUString& Name )
244 {
245 auto pInfo = std::find_if(m_pInfo->begin(), m_pInfo->end(),
246 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
247 if (pInfo != m_pInfo->end())
248 return *pInfo;
249
250 throw ucb::UnsupportedCommandException();
251 }
252
253
254 // virtual
255 ucb::CommandInfo SAL_CALL
getCommandInfoByHandle(sal_Int32 Handle)256 CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle )
257 {
258 auto pInfo = std::find_if(m_pInfo->begin(), m_pInfo->end(),
259 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
260 if (pInfo != m_pInfo->end())
261 return *pInfo;
262
263 throw ucb::UnsupportedCommandException();
264 }
265
266
267 // virtual
hasCommandByName(const OUString & Name)268 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName(
269 const OUString& Name )
270 {
271 return std::any_of(m_pInfo->begin(), m_pInfo->end(),
272 [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; });
273 }
274
275
276 // virtual
hasCommandByHandle(sal_Int32 Handle)277 sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle )
278 {
279 return std::any_of(m_pInfo->begin(), m_pInfo->end(),
280 [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; });
281 }
282
283
createDesiredName(const OUString & rSourceURL,const OUString & rNewTitle)284 OUString 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
createDesiredName(const ucb::GlobalTransferCommandArgument & rArg)333 OUString createDesiredName(
334 const ucb::GlobalTransferCommandArgument & rArg )
335 {
336 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
337 }
338
createDesiredName(const ucb::TransferInfo & rArg)339 OUString createDesiredName(
340 const ucb::TransferInfo & rArg )
341 {
342 return createDesiredName( rArg.SourceURL, rArg.NewTitle );
343 }
344
345
346 enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN };
347
interactiveNameClashResolve(const uno::Reference<ucb::XCommandEnvironment> & xEnv,const OUString & rTargetURL,const OUString & rClashingName,uno::Any & rException,OUString & rNewName)348 NameClashContinuation 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 {
355 rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest(
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
371 rtl::Reference< ucbhelper::InteractionContinuation >
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
420 /// @throws uno::RuntimeException
setTitle(const uno::Reference<ucb::XCommandProcessor> & xCommandProcessor,const uno::Reference<ucb::XCommandEnvironment> & xEnv,const OUString & rNewTitle)421 bool 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( 1 );
429 aPropValues[ 0 ].Name = "Title";
430 aPropValues[ 0 ].Handle = -1;
431 aPropValues[ 0 ].Value <<= rNewTitle;
432
433 ucb::Command aSetPropsCommand(
434 "setPropertyValues",
435 -1,
436 uno::makeAny( 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
466 /// @throws uno::Exception
createNew(const TransferCommandContext & rContext,const uno::Reference<ucb::XContent> & xTarget,bool bSourceIsFolder,bool bSourceIsDocument,bool bSourceIsLink)467 uno::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 }));
490 ucbhelper::cancelCommandExecution(
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( 1 );
500 aPropsToObtain[ 0 ].Name = "CreatableContentsInfo";
501 aPropsToObtain[ 0 ].Handle = -1;
502
503 ucb::Command aGetPropsCommand(
504 "getPropertyValues",
505 -1,
506 uno::makeAny( aPropsToObtain ) );
507
508 uno::Reference< sdbc::XRow > xRow;
509 xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
510
511 uno::Sequence< ucb::ContentInfo > aTypesInfo;
512 bool bGotTypesInfo = false;
513
514 if ( xRow.is() )
515 {
516 uno::Any aValue = xRow->getObject(
517 1, uno::Reference< container::XNameAccess >() );
518 if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) )
519 {
520 bGotTypesInfo = true;
521 }
522 }
523
524 uno::Reference< ucb::XContentCreator > xCreator;
525
526 if ( !bGotTypesInfo )
527 {
528 // Second, try it using XContentCreator interface -> the "old" way (not
529 // providing the chance to supply an XCommandEnvironment.
530
531 xCreator.set( xTarget, uno::UNO_QUERY );
532
533 if ( !xCreator.is() )
534 {
535 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
536 {
537 {"Folder", uno::Any(rContext.aArg.TargetURL)}
538 }));
539 ucbhelper::cancelCommandExecution(
540 ucb::IOErrorCode_CANT_CREATE,
541 aArgs,
542 rContext.xOrigEnv,
543 "Target is no XContentCreator!",
544 rContext.xProcessor );
545 // Unreachable
546 }
547
548 aTypesInfo = xCreator->queryCreatableContentsInfo();
549 }
550
551 if ( !aTypesInfo.hasElements() )
552 {
553 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
554 {
555 {"Folder", uno::Any(rContext.aArg.TargetURL)}
556 }));
557 ucbhelper::cancelCommandExecution(
558 ucb::IOErrorCode_CANT_CREATE,
559 aArgs,
560 rContext.xOrigEnv,
561 "No types creatable!",
562 rContext.xProcessor );
563 // Unreachable
564 }
565
566 // (2) Try to find a matching target type for the source object.
567
568 std::function<bool(const sal_Int32)> lCompare;
569
570 if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK )
571 {
572 // Create link
573 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
574 }
575 else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) ||
576 ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) )
577 {
578 // Copy / Move
579 // Is source a link? Create link in target folder then.
580 if ( bSourceIsLink )
581 {
582 lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); };
583 }
584 else
585 {
586 // (not a and not b) or (a and b)
587 // not( a or b) or (a and b)
588 lCompare = [bSourceIsFolder, bSourceIsDocument](const sal_Int32 nAttribs) {
589 return ( bSourceIsFolder == !!( nAttribs & ucb::ContentInfoAttribute::KIND_FOLDER ) )
590 && ( bSourceIsDocument == !!( nAttribs & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) ;
591 };
592 }
593 }
594 else
595 {
596 ucbhelper::cancelCommandExecution(
597 uno::makeAny( lang::IllegalArgumentException(
598 "Unknown transfer operation!",
599 rContext.xProcessor,
600 -1 ) ),
601 rContext.xOrigEnv );
602 // Unreachable
603 }
604
605 uno::Reference< ucb::XContent > xNew;
606 auto pTypeInfo = std::find_if(aTypesInfo.begin(), aTypesInfo.end(),
607 [&lCompare](const ucb::ContentInfo& rTypeInfo) { return lCompare(rTypeInfo.Attributes); });
608 if (pTypeInfo != aTypesInfo.end())
609 {
610 // (3) Create a new, empty object of matched type.
611
612 if ( !xCreator.is() )
613 {
614 // First, try it using "CreatabeleContentsInfo" property and
615 // "createNewContent" command -> the "new" way.
616 ucb::Command aCreateNewCommand(
617 "createNewContent",
618 -1,
619 uno::makeAny( *pTypeInfo ) );
620
621 xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv )
622 >>= xNew;
623 }
624 else
625 {
626 // Second, try it using XContentCreator interface -> the "old"
627 // way (not providing the chance to supply an XCommandEnvironment.
628
629 xNew = xCreator->createNewContent( *pTypeInfo );
630 }
631
632 if ( !xNew.is() )
633 {
634 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
635 {
636 {"Folder", uno::Any(rContext.aArg.TargetURL)}
637 }));
638 ucbhelper::cancelCommandExecution(
639 ucb::IOErrorCode_CANT_CREATE,
640 aArgs,
641 rContext.xOrigEnv,
642 "createNewContent failed!",
643 rContext.xProcessor );
644 // Unreachable
645 }
646 }
647
648 return xNew;
649 }
650
651 /// @throws uno::Exception
transferProperties(const TransferCommandContext & rContext,const uno::Reference<ucb::XCommandProcessor> & xCommandProcessorS,const uno::Reference<ucb::XCommandProcessor> & xCommandProcessorN)652 void transferProperties(
653 const TransferCommandContext & rContext,
654 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS,
655 const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN )
656 {
657 ucb::Command aGetPropertySetInfoCommand(
658 "getPropertySetInfo",
659 -1,
660 uno::Any() );
661
662 uno::Reference< beans::XPropertySetInfo > xInfo;
663 xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv )
664 >>= xInfo;
665
666 if ( !xInfo.is() )
667 {
668 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
669 {
670 {"Uri", uno::Any(rContext.aArg.SourceURL)}
671 }));
672 ucbhelper::cancelCommandExecution(
673 ucb::IOErrorCode_CANT_READ,
674 aArgs,
675 rContext.xOrigEnv,
676 "Unable to get propertyset info from source object!",
677 rContext.xProcessor );
678 // Unreachable
679 }
680
681 uno::Sequence< beans::Property > aAllProps = xInfo->getProperties();
682
683 ucb::Command aGetPropsCommand1(
684 "getPropertyValues",
685 -1,
686 uno::makeAny( aAllProps ) );
687
688 uno::Reference< sdbc::XRow > xRow1;
689 xCommandProcessorS->execute(
690 aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1;
691
692 if ( !xRow1.is() )
693 {
694 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
695 {
696 {"Uri", uno::Any(rContext.aArg.SourceURL)}
697 }));
698 ucbhelper::cancelCommandExecution(
699 ucb::IOErrorCode_CANT_READ,
700 aArgs,
701 rContext.xOrigEnv,
702 "Unable to get properties from source object!",
703 rContext.xProcessor );
704 // Unreachable
705 }
706
707 // Assemble data structure for setPropertyValues command.
708
709 // Note: Make room for additional Title and TargetURL too. -> + 2
710 uno::Sequence< beans::PropertyValue > aPropValues(
711 aAllProps.getLength() + 2 );
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 = aPropValues[ 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 aPropValues[ nWritePos ].Name = "Title";
773 aPropValues[ nWritePos ].Handle = -1;
774 aPropValues[ 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 aPropValues[ nWritePos ].Name = "TargetURL";
784 aPropValues[ nWritePos ].Handle = -1;
785 aPropValues[ 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::makeAny( 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
805 /// @throws uno::Exception
getInputStream(const TransferCommandContext & rContext,const uno::Reference<ucb::XCommandProcessor> & xCommandProcessorS)806 uno::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::makeAny( 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::makeAny( 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
882 /// @throws uno::Exception
getResultSet(const TransferCommandContext & rContext,const uno::Reference<ucb::XCommandProcessor> & xCommandProcessorS)883 uno::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( 3 );
890
891 aProps[ 0 ].Name = "IsFolder";
892 aProps[ 0 ].Handle = -1; /* unknown */
893 aProps[ 1 ].Name = "IsDocument";
894 aProps[ 1 ].Handle = -1; /* unknown */
895 aProps[ 2 ].Name = "TargetURL";
896 aProps[ 2 ].Handle = -1; /* unknown */
897
898 ucb::OpenCommandArgument2 aArg;
899 aArg.Mode = ucb::OpenMode::ALL;
900 aArg.Priority = 0; // unused
901 aArg.Sink = nullptr;
902 aArg.Properties = aProps;
903
904 ucb::Command aOpenCommand( "open",
905 -1,
906 uno::makeAny( aArg ) );
907 try
908 {
909 uno::Reference< ucb::XDynamicResultSet > xSet;
910 xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet;
911
912 if ( xSet.is() )
913 xResultSet = xSet->getStaticResultSet();
914 }
915 catch ( uno::RuntimeException const & )
916 {
917 throw;
918 }
919 catch ( uno::Exception const & )
920 {
921 OSL_FAIL( "unable to get result set from folder!" );
922 }
923
924 return xResultSet;
925 }
926
927 /// @throws uno::Exception
handleNameClashRename(const TransferCommandContext & rContext,const uno::Reference<ucb::XContent> & xNew,const uno::Reference<ucb::XCommandProcessor> & xCommandProcessorN,const uno::Reference<ucb::XCommandProcessor> & xCommandProcessorS,uno::Reference<io::XInputStream> & xInputStream)928 void handleNameClashRename(
929 const TransferCommandContext & rContext,
930 const uno::Reference< ucb::XContent > & xNew,
931 const uno::Reference<
932 ucb::XCommandProcessor > & xCommandProcessorN,
933 const uno::Reference<
934 ucb::XCommandProcessor > & xCommandProcessorS,
935 /* [inout] */ uno::Reference< io::XInputStream > & xInputStream )
936 {
937 sal_Int32 nTry = 0;
938
939 // Obtain old title.
940 uno::Sequence< beans::Property > aProps( 1 );
941 aProps[ 0 ].Name = "Title";
942 aProps[ 0 ].Handle = -1;
943
944 ucb::Command aGetPropsCommand(
945 "getPropertyValues",
946 -1,
947 uno::makeAny( aProps ) );
948
949 uno::Reference< sdbc::XRow > xRow;
950 xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow;
951
952 if ( !xRow.is() )
953 {
954 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
955 {
956 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
957 }));
958 ucbhelper::cancelCommandExecution(
959 ucb::IOErrorCode_CANT_READ,
960 aArgs,
961 rContext.xOrigEnv,
962 "Unable to get properties from new object!",
963 rContext.xProcessor );
964 // Unreachable
965 }
966
967 OUString aOldTitle = xRow->getString( 1 );
968 if ( aOldTitle.isEmpty() )
969 {
970 ucbhelper::cancelCommandExecution(
971 uno::makeAny( beans::UnknownPropertyException(
972 "Unable to get property 'Title' from new object!",
973 rContext.xProcessor ) ),
974 rContext.xOrigEnv );
975 // Unreachable
976 }
977
978 // Some pseudo-intelligence for not destroying file extensions.
979 OUString aOldTitlePre;
980 OUString aOldTitlePost;
981 sal_Int32 nPos = aOldTitle.lastIndexOf( '.' );
982 if ( nPos != -1 )
983 {
984 aOldTitlePre = aOldTitle.copy( 0, nPos );
985 aOldTitlePost = aOldTitle.copy( nPos );
986 }
987 else
988 aOldTitlePre = aOldTitle;
989
990 if ( nPos > 0 )
991 aOldTitlePre += "_";
992
993 bool bContinue = true;
994 do
995 {
996 nTry++;
997
998 OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) +
999 aOldTitlePost;
1000
1001 // Set new title
1002 setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle );
1003
1004 // Retry inserting the content.
1005 try
1006 {
1007 // Previous try may have read from stream. Seek to begin (if
1008 // optional interface XSeekable is supported) or get a new stream.
1009 if ( xInputStream.is() )
1010 {
1011 uno::Reference< io::XSeekable > xSeekable(
1012 xInputStream, uno::UNO_QUERY );
1013 if ( xSeekable.is() )
1014 {
1015 try
1016 {
1017 xSeekable->seek( 0 );
1018 }
1019 catch ( lang::IllegalArgumentException const & )
1020 {
1021 xInputStream.clear();
1022 }
1023 catch ( io::IOException const & )
1024 {
1025 xInputStream.clear();
1026 }
1027 }
1028 else
1029 xInputStream.clear();
1030
1031 if ( !xInputStream.is() )
1032 {
1033 xInputStream
1034 = getInputStream( rContext, xCommandProcessorS );
1035 if ( !xInputStream.is() )
1036 {
1037 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1038 {
1039 {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())}
1040 }));
1041 ucbhelper::cancelCommandExecution(
1042 ucb::IOErrorCode_CANT_READ,
1043 aArgs,
1044 rContext.xOrigEnv,
1045 "Got no data stream from source!",
1046 rContext.xProcessor );
1047 // Unreachable
1048 }
1049 }
1050 }
1051
1052 ucb::InsertCommandArgument2 aArg;
1053 aArg.Data = xInputStream;
1054 aArg.ReplaceExisting = false;
1055
1056 ucb::Command aInsertCommand(
1057 "insert",
1058 -1,
1059 uno::makeAny( aArg ) );
1060
1061 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1062
1063 // Success!
1064 bContinue = false;
1065 }
1066 catch ( uno::RuntimeException const & )
1067 {
1068 throw;
1069 }
1070 catch ( uno::Exception const & )
1071 {
1072 }
1073 }
1074 while ( bContinue && ( nTry < 50 ) );
1075
1076 if ( nTry == 50 )
1077 {
1078 ucbhelper::cancelCommandExecution(
1079 uno::makeAny(
1080 ucb::UnsupportedNameClashException(
1081 "Unable to resolve name clash!",
1082 rContext.xProcessor,
1083 ucb::NameClash::RENAME ) ),
1084 rContext.xOrigEnv );
1085 // Unreachable
1086 }
1087 }
1088
1089 /// @throws uno::Exception
globalTransfer_(const TransferCommandContext & rContext,const uno::Reference<ucb::XContent> & xSource,const uno::Reference<ucb::XContent> & xTarget,const uno::Reference<sdbc::XRow> & xSourceProps)1090 void globalTransfer_(
1091 const TransferCommandContext & rContext,
1092 const uno::Reference< ucb::XContent > & xSource,
1093 const uno::Reference< ucb::XContent > & xTarget,
1094 const uno::Reference< sdbc::XRow > & xSourceProps )
1095 {
1096 // IsFolder: property is required.
1097 bool bSourceIsFolder = xSourceProps->getBoolean( 1 );
1098 if ( !bSourceIsFolder && xSourceProps->wasNull() )
1099 {
1100 ucbhelper::cancelCommandExecution(
1101 uno::makeAny( beans::UnknownPropertyException(
1102 "Unable to get property 'IsFolder' from source object!",
1103 rContext.xProcessor ) ),
1104 rContext.xOrigEnv );
1105 // Unreachable
1106 }
1107
1108 // IsDocument: property is required.
1109 bool bSourceIsDocument = xSourceProps->getBoolean( 2 );
1110 if ( !bSourceIsDocument && xSourceProps->wasNull() )
1111 {
1112 ucbhelper::cancelCommandExecution(
1113 uno::makeAny( beans::UnknownPropertyException(
1114 "Unable to get property 'IsDocument' from source object!",
1115 rContext.xProcessor ) ),
1116 rContext.xOrigEnv );
1117 // Unreachable
1118 }
1119
1120 // TargetURL: property is optional.
1121 bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty();
1122
1123
1124 // (1) Try to find a matching target type for the source object and
1125 // create a new, empty object of that type.
1126
1127
1128 uno::Reference< ucb::XContent > xNew = createNew( rContext,
1129 xTarget,
1130 bSourceIsFolder,
1131 bSourceIsDocument,
1132 bSourceIsLink );
1133 if ( !xNew.is() )
1134 {
1135 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1136 {
1137 {"Folder", uno::Any(rContext.aArg.TargetURL)}
1138 }));
1139 ucbhelper::cancelCommandExecution(
1140 ucb::IOErrorCode_CANT_CREATE,
1141 aArgs,
1142 rContext.xOrigEnv,
1143 "No matching content type at target!",
1144 rContext.xProcessor );
1145 // Unreachable
1146 }
1147
1148
1149 // (2) Transfer property values from source to new object.
1150
1151
1152 uno::Reference< ucb::XCommandProcessor > xCommandProcessorN(
1153 xNew, uno::UNO_QUERY );
1154 if ( !xCommandProcessorN.is() )
1155 {
1156 uno::Any aProps
1157 = uno::makeAny(beans::PropertyValue(
1158 "Uri",
1159 -1,
1160 uno::makeAny(
1161 xNew->getIdentifier()->
1162 getContentIdentifier()),
1163 beans::PropertyState_DIRECT_VALUE));
1164 ucbhelper::cancelCommandExecution(
1165 ucb::IOErrorCode_CANT_WRITE,
1166 uno::Sequence< uno::Any >(&aProps, 1),
1167 rContext.xOrigEnv,
1168 "New content is not a XCommandProcessor!",
1169 rContext.xProcessor );
1170 // Unreachable
1171 }
1172
1173 // Obtain all properties from source.
1174
1175 uno::Reference< ucb::XCommandProcessor > xCommandProcessorS(
1176 xSource, uno::UNO_QUERY );
1177 if ( !xCommandProcessorS.is() )
1178 {
1179 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1180 {
1181 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1182 }));
1183 ucbhelper::cancelCommandExecution(
1184 ucb::IOErrorCode_CANT_READ,
1185 aArgs,
1186 rContext.xOrigEnv,
1187 "Source content is not a XCommandProcessor!",
1188 rContext.xProcessor );
1189 // Unreachable
1190 }
1191
1192 transferProperties( rContext, xCommandProcessorS, xCommandProcessorN );
1193
1194
1195 // (3) Try to obtain a data stream from source.
1196
1197
1198 uno::Reference< io::XInputStream > xInputStream;
1199
1200 if ( bSourceIsDocument && ( rContext.aArg.Operation
1201 != ucb::TransferCommandOperation_LINK ) )
1202 xInputStream = getInputStream( rContext, xCommandProcessorS );
1203
1204
1205 // (4) Try to obtain a resultset (children) from source.
1206
1207
1208 uno::Reference< sdbc::XResultSet > xResultSet;
1209
1210 if ( bSourceIsFolder && ( rContext.aArg.Operation
1211 != ucb::TransferCommandOperation_LINK ) )
1212 xResultSet = getResultSet( rContext, xCommandProcessorS );
1213
1214
1215 // (5) Insert (store) new content.
1216
1217
1218 ucb::InsertCommandArgument2 aArg;
1219 aArg.Data = xInputStream;
1220 aArg.MimeType = rContext.aArg.MimeType;
1221 aArg.DocumentId = rContext.aArg.DocumentId;
1222
1223 switch ( rContext.aArg.NameClash )
1224 {
1225 case ucb::NameClash::OVERWRITE:
1226 aArg.ReplaceExisting = true;
1227 break;
1228
1229 case ucb::NameClash::ERROR:
1230 case ucb::NameClash::RENAME:
1231 case ucb::NameClash::KEEP: // deprecated
1232 case ucb::NameClash::ASK:
1233 aArg.ReplaceExisting = false;
1234 break;
1235
1236 default:
1237 aArg.ReplaceExisting = false;
1238 OSL_FAIL( "Unknown nameclash directive!" );
1239 break;
1240 }
1241
1242 OUString aDesiredName = createDesiredName( rContext.aArg );
1243
1244 bool bRetry;
1245 do
1246 {
1247 bRetry = false;
1248
1249 try
1250 {
1251 ucb::Command aInsertCommand(
1252 "insert",
1253 -1,
1254 uno::makeAny( aArg ) );
1255
1256 xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv );
1257 }
1258 catch ( ucb::UnsupportedNameClashException const & exc )
1259 {
1260 OSL_ENSURE( !aArg.ReplaceExisting,
1261 "BUG: UnsupportedNameClashException not allowed here!" );
1262
1263 if (exc.NameClash != ucb::NameClash::ERROR) {
1264 OSL_FAIL( "BUG: NameClash::ERROR expected!" );
1265 }
1266
1267 // No chance to solve name clashes, because I'm not able to detect
1268 // whether there is one.
1269 throw ucb::UnsupportedNameClashException(
1270 "Unable to resolve name clashes, no chance to detect "
1271 "that there is one!",
1272 rContext.xProcessor,
1273 rContext.aArg.NameClash );
1274 }
1275 catch ( ucb::NameClashException const & )
1276 {
1277 // The 'insert' command throws a NameClashException if the parameter
1278 // ReplaceExisting of the command's argument was set to false and
1279 // there exists a resource with a clashing name in the target folder
1280 // of the operation.
1281
1282 // 'insert' command has no direct support for name clashes other
1283 // than ERROR ( ReplaceExisting == false ) and OVERWRITE
1284 // ( ReplaceExisting == true ). So we have to implement the
1285 // other name clash handling directives on top of the content.
1286
1287 // @@@ 'insert' command should be extended that it accepts a
1288 // name clash handling directive, exactly like 'transfer' command.
1289
1290 switch ( rContext.aArg.NameClash )
1291 {
1292 case ucb::NameClash::OVERWRITE:
1293 {
1294 ucbhelper::cancelCommandExecution(
1295 uno::makeAny(
1296 ucb::UnsupportedNameClashException(
1297 "BUG: insert + replace == true MUST NOT "
1298 "throw NameClashException.",
1299 rContext.xProcessor,
1300 rContext.aArg.NameClash ) ),
1301 rContext.xOrigEnv );
1302 [[fallthrough]]; // Unreachable
1303 }
1304
1305 case ucb::NameClash::ERROR:
1306 throw;
1307
1308 case ucb::NameClash::RENAME:
1309 {
1310 // "invent" a new valid title.
1311 handleNameClashRename( rContext,
1312 xNew,
1313 xCommandProcessorN,
1314 xCommandProcessorS,
1315 xInputStream );
1316 break;
1317 }
1318
1319 case ucb::NameClash::ASK:
1320 {
1321 uno::Any aExc;
1322 OUString aNewTitle;
1323 NameClashContinuation eCont
1324 = interactiveNameClashResolve(
1325 rContext.xOrigEnv, // always use original environment!
1326 rContext.aArg.TargetURL, // target folder URL
1327 aDesiredName,
1328 aExc,
1329 aNewTitle );
1330
1331 switch ( eCont )
1332 {
1333 case NOT_HANDLED:
1334 // Not handled.
1335 cppu::throwException( aExc );
1336 [[fallthrough]]; // break;
1337
1338 case UNKNOWN:
1339 // Handled, but not clear, how...
1340 // fall through intended.
1341
1342 case ABORT:
1343 throw ucb::CommandFailedException(
1344 "abort requested via interaction "
1345 "handler",
1346 uno::Reference< uno::XInterface >(),
1347 aExc );
1348 // break;
1349
1350 case OVERWRITE:
1351 OSL_ENSURE( !aArg.ReplaceExisting,
1352 "Hu? ReplaceExisting already true?"
1353 );
1354 aArg.ReplaceExisting = true;
1355 bRetry = true;
1356 break;
1357
1358 case NEW_NAME:
1359 {
1360 // set new name -> set "Title" property...
1361 if ( setTitle( xCommandProcessorN,
1362 rContext.xEnv,
1363 aNewTitle ) )
1364 {
1365 // remember suggested title...
1366 aDesiredName = aNewTitle;
1367
1368 // ... and try again.
1369 bRetry = true;
1370 }
1371 else
1372 {
1373 // error setting title. Abort.
1374 throw ucb::CommandFailedException(
1375 "error setting Title property!",
1376 uno::Reference< uno::XInterface >(),
1377 aExc );
1378 }
1379 break;
1380 }
1381 }
1382
1383 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1384 }
1385 break;
1386
1387 case ucb::NameClash::KEEP: // deprecated
1388 default:
1389 {
1390 ucbhelper::cancelCommandExecution(
1391 uno::makeAny(
1392 ucb::UnsupportedNameClashException(
1393 "default action, don't know how to "
1394 "handle name clash",
1395 rContext.xProcessor,
1396 rContext.aArg.NameClash ) ),
1397 rContext.xOrigEnv );
1398 // Unreachable
1399 }
1400 }
1401 }
1402 }
1403 while ( bRetry );
1404
1405
1406 // (6) Process children of source.
1407
1408
1409 if ( xResultSet.is() )
1410 {
1411 try
1412 {
1413 // Iterate over children...
1414
1415 uno::Reference< sdbc::XRow > xChildRow(
1416 xResultSet, uno::UNO_QUERY );
1417
1418 if ( !xChildRow.is() )
1419 {
1420 uno::Any aProps
1421 = uno::makeAny(
1422 beans::PropertyValue(
1423 "Uri",
1424 -1,
1425 uno::makeAny(rContext.aArg.SourceURL),
1426 beans::PropertyState_DIRECT_VALUE));
1427 ucbhelper::cancelCommandExecution(
1428 ucb::IOErrorCode_CANT_READ,
1429 uno::Sequence< uno::Any >(&aProps, 1),
1430 rContext.xOrigEnv,
1431 "Unable to get properties from children of source!",
1432 rContext.xProcessor );
1433 // Unreachable
1434 }
1435
1436 uno::Reference< ucb::XContentAccess > xChildAccess(
1437 xResultSet, uno::UNO_QUERY );
1438
1439 if ( !xChildAccess.is() )
1440 {
1441 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1442 {
1443 {"Uri", uno::Any(rContext.aArg.SourceURL)}
1444 }));
1445 ucbhelper::cancelCommandExecution(
1446 ucb::IOErrorCode_CANT_READ,
1447 aArgs,
1448 rContext.xOrigEnv,
1449 "Unable to get children of source!",
1450 rContext.xProcessor );
1451 // Unreachable
1452 }
1453
1454 if ( xResultSet->first() )
1455 {
1456 ucb::GlobalTransferCommandArgument2 aTransArg(
1457 rContext.aArg.Operation,
1458 OUString(), // SourceURL; filled later
1459 xNew->getIdentifier()
1460 ->getContentIdentifier(), // TargetURL
1461 OUString(), // NewTitle;
1462 rContext.aArg.NameClash,
1463 rContext.aArg.MimeType,
1464 rContext.aArg.DocumentId);
1465
1466 TransferCommandContext aSubCtx(
1467 rContext.m_xContext,
1468 rContext.xProcessor,
1469 rContext.xEnv,
1470 rContext.xOrigEnv,
1471 aTransArg );
1472 do
1473 {
1474 uno::Reference< ucb::XContent > xChild
1475 = xChildAccess->queryContent();
1476 if ( xChild.is() )
1477 {
1478 // Recursion!
1479
1480 aSubCtx.aArg.SourceURL
1481 = xChild->getIdentifier()->getContentIdentifier();
1482
1483 globalTransfer_( aSubCtx,
1484 xChild,
1485 xNew,
1486 xChildRow );
1487 }
1488 }
1489 while ( xResultSet->next() );
1490 }
1491 }
1492 catch ( sdbc::SQLException const & )
1493 {
1494 }
1495 }
1496
1497 try {
1498 uno::Reference< ucb::XCommandProcessor > xcp(
1499 xTarget, uno::UNO_QUERY );
1500
1501 uno::Any aAny;
1502 uno::Reference< ucb::XCommandInfo > xci;
1503 if(xcp.is())
1504 aAny =
1505 xcp->execute(
1506 ucb::Command(
1507 "getCommandInfo",
1508 -1,
1509 uno::Any()),
1510 0,
1511 rContext.xEnv );
1512
1513 static const OUStringLiteral cmdName(u"flush");
1514 if((aAny >>= xci) && xci->hasCommandByName(cmdName))
1515 xcp->execute(
1516 ucb::Command(
1517 cmdName,
1518 -1,
1519 uno::Any()) ,
1520 0,
1521 rContext.xEnv );
1522 }
1523 catch( uno::Exception const & )
1524 {
1525 }
1526 }
1527
1528 } /* namespace */
1529
1530
1531 // UniversalContentBroker implementation ( XCommandProcessor commands ).
1532
1533
1534 uno::Reference< ucb::XCommandInfo >
getCommandInfo()1535 UniversalContentBroker::getCommandInfo()
1536 {
1537 return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1538 }
1539
1540
globalTransfer(const ucb::GlobalTransferCommandArgument2 & rArg,const uno::Reference<ucb::XCommandEnvironment> & xEnv)1541 void UniversalContentBroker::globalTransfer(
1542 const ucb::GlobalTransferCommandArgument2 & rArg,
1543 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1544 {
1545 // Use own command environment with own interaction handler intercepting
1546 // some interaction requests that shall not be handled by the user-supplied
1547 // interaction handler.
1548 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1549 if (xEnv.is())
1550 {
1551 xLocalEnv.set( ucb::CommandEnvironment::create(
1552 m_xContext,
1553 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1554 xEnv->getProgressHandler() ) );
1555 }
1556
1557
1558 // (1) Try to transfer the content using 'transfer' command.
1559
1560
1561 uno::Reference< ucb::XContent > xTarget;
1562 uno::Reference< ucb::XContentIdentifier > xId
1563 = createContentIdentifier( rArg.TargetURL );
1564 if ( xId.is() )
1565 {
1566 try
1567 {
1568 xTarget = queryContent( xId );
1569 }
1570 catch ( ucb::IllegalIdentifierException const & )
1571 {
1572 }
1573 }
1574
1575 if ( !xTarget.is() )
1576 {
1577 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1578 {
1579 {"Uri", uno::Any(rArg.TargetURL)}
1580 }));
1581 ucbhelper::cancelCommandExecution(
1582 ucb::IOErrorCode_CANT_READ,
1583 aArgs,
1584 xEnv,
1585 "Can't instantiate target object!",
1586 this );
1587 // Unreachable
1588 }
1589
1590 if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) ||
1591 ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) )
1592 {
1593 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1594 xTarget, uno::UNO_QUERY );
1595 if ( !xCommandProcessor.is() )
1596 {
1597 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1598 {
1599 {"Uri", uno::Any(rArg.TargetURL)}
1600 }));
1601 ucbhelper::cancelCommandExecution(
1602 ucb::IOErrorCode_CANT_READ,
1603 aArgs,
1604 xEnv,
1605 "Target content is not a XCommandProcessor!",
1606 this );
1607 // Unreachable
1608 }
1609
1610 ucb::TransferInfo2 aTransferArg(
1611 ( rArg.Operation
1612 == ucb::TransferCommandOperation_MOVE ), // MoveData
1613 rArg.SourceURL,
1614 rArg.NewTitle,
1615 rArg.NameClash,
1616 rArg.MimeType );
1617
1618 bool bRetry;
1619 do
1620 {
1621 bRetry = false;
1622
1623 try
1624 {
1625 ucb::Command aCommand(
1626 "transfer", // Name
1627 -1, // Handle
1628 uno::makeAny( aTransferArg ) ); // Argument
1629
1630 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1631
1632 // Command succeeded. We're done.
1633 return;
1634 }
1635 catch ( ucb::InteractiveBadTransferURLException const & )
1636 {
1637 // Source URL is not supported by target. Try to transfer
1638 // the content "manually".
1639 }
1640 catch ( ucb::UnsupportedCommandException const & )
1641 {
1642 // 'transfer' command is not supported by commandprocessor.
1643 // Try to transfer manually.
1644 }
1645 catch ( ucb::UnsupportedNameClashException const & exc )
1646 {
1647 OSL_ENSURE( aTransferArg.NameClash == exc.NameClash,
1648 "nameclash mismatch!" );
1649 if ( exc.NameClash == ucb::NameClash::ASK )
1650 {
1651 // Try to detect a name clash by invoking "transfer" with
1652 // NameClash::ERROR.
1653 try
1654 {
1655 ucb::TransferInfo2 aTransferArg1(
1656 aTransferArg.MoveData,
1657 aTransferArg.SourceURL,
1658 aTransferArg.NewTitle,
1659 ucb::NameClash::ERROR,
1660 aTransferArg.MimeType );
1661
1662 ucb::Command aCommand1(
1663 "transfer",
1664 -1,
1665 uno::makeAny( aTransferArg1 ) );
1666
1667 xCommandProcessor->execute( aCommand1, 0, xLocalEnv );
1668
1669 // Command succeeded. We're done.
1670 return;
1671 }
1672 catch ( ucb::UnsupportedNameClashException const & )
1673 {
1674 // No chance to solve name clashes, because I'm not
1675 // able to detect whether there is one.
1676 throw exc; // Not just 'throw;'!
1677 }
1678 catch ( ucb::NameClashException const & )
1679 {
1680 // There's a clash. Use interaction handler to solve it.
1681
1682 uno::Any aExc;
1683 OUString aNewTitle;
1684 NameClashContinuation eCont
1685 = interactiveNameClashResolve(
1686 xEnv, // always use original environment!
1687 rArg.TargetURL, // target folder URL
1688 createDesiredName(
1689 aTransferArg ), // clashing name
1690 aExc,
1691 aNewTitle );
1692
1693 switch ( eCont )
1694 {
1695 case NOT_HANDLED:
1696 // Not handled.
1697 cppu::throwException( aExc );
1698 [[fallthrough]]; // break;
1699
1700 case UNKNOWN:
1701 // Handled, but not clear, how...
1702 // fall through intended.
1703
1704 case ABORT:
1705 throw ucb::CommandFailedException(
1706 "abort requested via interaction "
1707 "handler",
1708 uno::Reference< uno::XInterface >(),
1709 aExc );
1710 // break;
1711
1712 case OVERWRITE:
1713 aTransferArg.NameClash
1714 = ucb::NameClash::OVERWRITE;
1715 bRetry = true;
1716 break;
1717
1718 case NEW_NAME:
1719 aTransferArg.NewTitle = aNewTitle;
1720 bRetry = true;
1721 break;
1722 }
1723
1724 OSL_ENSURE( bRetry, "bRetry must be true here!!!" );
1725 }
1726 }
1727 else
1728 {
1729 throw;
1730 }
1731 }
1732 }
1733 while ( bRetry );
1734 }
1735
1736
1737 // (2) Try to transfer the content "manually".
1738
1739
1740 uno::Reference< ucb::XContent > xSource;
1741 try
1742 {
1743 uno::Reference< ucb::XContentIdentifier > xId2
1744 = createContentIdentifier( rArg.SourceURL );
1745 if ( xId2.is() )
1746 xSource = queryContent( xId2 );
1747 }
1748 catch ( ucb::IllegalIdentifierException const & )
1749 {
1750 // Error handling via "if ( !xSource.is() )" below.
1751 }
1752
1753 if ( !xSource.is() )
1754 {
1755 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1756 {
1757 {"Uri", uno::Any(rArg.SourceURL)}
1758 }));
1759 ucbhelper::cancelCommandExecution(
1760 ucb::IOErrorCode_CANT_READ,
1761 aArgs,
1762 xEnv,
1763 "Can't instantiate source object!",
1764 this );
1765 // Unreachable
1766 }
1767
1768 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1769 xSource, uno::UNO_QUERY );
1770 if ( !xCommandProcessor.is() )
1771 {
1772 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1773 {
1774 {"Uri", uno::Any(rArg.SourceURL)}
1775 }));
1776 ucbhelper::cancelCommandExecution(
1777 ucb::IOErrorCode_CANT_READ,
1778 aArgs,
1779 xEnv,
1780 "Source content is not a XCommandProcessor!",
1781 this );
1782 // Unreachable
1783 }
1784
1785 // Obtain interesting property values from source...
1786
1787 uno::Sequence< beans::Property > aProps( 4 );
1788
1789 aProps[ 0 ].Name = "IsFolder";
1790 aProps[ 0 ].Handle = -1; /* unknown */
1791 aProps[ 1 ].Name = "IsDocument";
1792 aProps[ 1 ].Handle = -1; /* unknown */
1793 aProps[ 2 ].Name = "TargetURL";
1794 aProps[ 2 ].Handle = -1; /* unknown */
1795 aProps[ 3 ].Name = "BaseURI";
1796 aProps[ 3 ].Handle = -1; /* unknown */
1797
1798 ucb::Command aGetPropsCommand(
1799 "getPropertyValues",
1800 -1,
1801 uno::makeAny( aProps ) );
1802
1803 uno::Reference< sdbc::XRow > xRow;
1804 xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow;
1805
1806 if ( !xRow.is() )
1807 {
1808 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1809 {
1810 {"Uri", uno::Any(rArg.SourceURL)}
1811 }));
1812 ucbhelper::cancelCommandExecution(
1813 ucb::IOErrorCode_CANT_READ,
1814 aArgs,
1815 xEnv,
1816 "Unable to get properties from source object!",
1817 this );
1818 // Unreachable
1819 }
1820
1821 TransferCommandContext aTransferCtx(
1822 m_xContext, this, xLocalEnv, xEnv, rArg );
1823
1824 if ( rArg.NewTitle.isEmpty() )
1825 {
1826 // BaseURI: property is optional.
1827 OUString aBaseURI( xRow->getString( 4 ) );
1828 if ( !aBaseURI.isEmpty() )
1829 {
1830 aTransferCtx.aArg.NewTitle
1831 = createDesiredName( aBaseURI, OUString() );
1832 }
1833 }
1834
1835 // Do it!
1836 globalTransfer_( aTransferCtx, xSource, xTarget, xRow );
1837
1838
1839 // (3) Delete source, if operation is MOVE.
1840
1841
1842 if ( rArg.Operation != ucb::TransferCommandOperation_MOVE )
1843 return;
1844
1845 try
1846 {
1847 ucb::Command aCommand(
1848 "delete", // Name
1849 -1, // Handle
1850 uno::makeAny( true ) ); // Argument
1851
1852 xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1853 }
1854 catch ( uno::Exception const & )
1855 {
1856 OSL_FAIL( "Cannot delete source object!" );
1857 throw;
1858 }
1859 }
1860
checkIn(const ucb::CheckinArgument & rArg,const uno::Reference<ucb::XCommandEnvironment> & xEnv)1861 uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg,
1862 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1863 {
1864 uno::Any aRet;
1865 // Use own command environment with own interaction handler intercepting
1866 // some interaction requests that shall not be handled by the user-supplied
1867 // interaction handler.
1868 uno::Reference< ucb::XCommandEnvironment > xLocalEnv;
1869 if (xEnv.is())
1870 {
1871 xLocalEnv.set( ucb::CommandEnvironment::create(
1872 m_xContext,
1873 new InteractionHandlerProxy( xEnv->getInteractionHandler() ),
1874 xEnv->getProgressHandler() ) );
1875 }
1876
1877 uno::Reference< ucb::XContent > xTarget;
1878 uno::Reference< ucb::XContentIdentifier > xId
1879 = createContentIdentifier( rArg.TargetURL );
1880 if ( xId.is() )
1881 {
1882 try
1883 {
1884 xTarget = queryContent( xId );
1885 }
1886 catch ( ucb::IllegalIdentifierException const & )
1887 {
1888 }
1889 }
1890
1891 if ( !xTarget.is() )
1892 {
1893 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1894 {
1895 {"Uri", uno::Any(rArg.TargetURL)}
1896 }));
1897 ucbhelper::cancelCommandExecution(
1898 ucb::IOErrorCode_CANT_READ,
1899 aArgs,
1900 xEnv,
1901 "Can't instantiate target object!",
1902 this );
1903 // Unreachable
1904 }
1905
1906 uno::Reference< ucb::XCommandProcessor > xCommandProcessor(
1907 xTarget, uno::UNO_QUERY );
1908 if ( !xCommandProcessor.is() )
1909 {
1910 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1911 {
1912 {"Uri", uno::Any(rArg.TargetURL)}
1913 }));
1914 ucbhelper::cancelCommandExecution(
1915 ucb::IOErrorCode_CANT_READ,
1916 aArgs,
1917 xEnv,
1918 "Target content is not a XCommandProcessor!",
1919 this );
1920 // Unreachable
1921 }
1922
1923 try
1924 {
1925 ucb::Command aCommand(
1926 "checkin", -1,
1927 uno::makeAny( rArg ) );
1928
1929 aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1930 }
1931 catch ( ucb::UnsupportedCommandException const & )
1932 {
1933 // 'checkin' command is not supported by commandprocessor:
1934 // ignore.
1935 }
1936 return aRet;
1937 }
1938
1939 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1940