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