LibreOffice Module ucb (master)  1
ucbcmds.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <osl/diagnose.h>
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>
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 
82  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:
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
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
179 void SAL_CALL ActiveDataSink::setInputStream(
180  const uno::Reference< io::XInputStream >& aStream )
181 {
182  m_xStream = aStream;
183 }
184 
185 
186 // virtual
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 
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
228  (*m_pInfo)[ 2 ]
229  = ucb::CommandInfo(
230  CHECKIN_NAME, // Name
231  CHECKIN_HANDLE, // Handle
233 }
234 
235 
236 // virtual
237 uno::Sequence< ucb::CommandInfo > SAL_CALL
238 CommandProcessorInfo::getCommands()
239 {
240  return *m_pInfo;
241 }
242 
243 
244 // virtual
245 ucb::CommandInfo SAL_CALL
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
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
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
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 
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 
336 OUString createDesiredName(
337  const ucb::GlobalTransferCommandArgument & rArg )
338 {
339  return createDesiredName( rArg.SourceURL, rArg.NewTitle );
340 }
341 
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 
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 {
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 
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 
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 
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  }));
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  }));
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  }));
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  {
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  }));
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 
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  }));
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  }));
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 
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 
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 
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  }));
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  {
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  }));
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  {
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 
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  {
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  {
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  }));
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));
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  }));
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  {
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  {
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));
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  }));
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 >
1539 {
1540  return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1541 }
1542 
1543 
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  }));
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  }));
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  }));
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  }));
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  }));
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 
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  }));
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  }));
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: */
bool hasValue()
OVERWRITE
void SAL_CALL throwException(Any const &exc)
#define GLOBALTRANSFER_HANDLE
Definition: ucbcmds.hxx:31
void globalTransfer(const css::ucb::GlobalTransferCommandArgument2 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
Definition: ucbcmds.cxx:1544
ABORT
#define GLOBALTRANSFER_NAME
Definition: ucbcmds.hxx:30
UNKNOWN
#define CHECKIN_HANDLE
Definition: ucbcmds.hxx:34
NameClashContinuation
Definition: ucbcmds.cxx:349
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
unsigned char sal_Bool
Object Value
css::uno::Any checkIn(const css::ucb::CheckinArgument &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
Definition: ucbcmds.cxx:1864
virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL createContentIdentifier(const OUString &ContentId) override
Definition: ucb.cxx:510
css::uno::Reference< css::uno::XComponentContext > m_xContext
Definition: ucb.hxx:160
#define GETCOMMANDINFO_NAME
Definition: ucbcmds.hxx:27
SQLHANDLE Handle
static css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo()
Definition: ucbcmds.cxx:1538
#define CHECKIN_NAME
Definition: ucbcmds.hxx:33
OString const aName
#define GETCOMMANDINFO_HANDLE
Definition: ucbcmds.hxx:28
sal_Int32 nPos
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier) override
Definition: ucb.cxx:462
const uno::Reference< uno::XComponentContext > m_xContext