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