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