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 
94 
95 class InteractionHandlerProxy :
96  public cppu::WeakImplHelper< task::XInteractionHandler >
97 {
98  uno::Reference< task::XInteractionHandler > m_xOrig;
99 
100 public:
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
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
177 void SAL_CALL ActiveDataSink::setInputStream(
178  const uno::Reference< io::XInputStream >& aStream )
179 {
180  m_xStream = aStream;
181 }
182 
183 
184 // virtual
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 
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
225  (*m_pInfo)[ 2 ]
226  = ucb::CommandInfo(
227  CHECKIN_NAME, // Name
228  CHECKIN_HANDLE, // Handle
230 }
231 
232 
233 // virtual
234 uno::Sequence< ucb::CommandInfo > SAL_CALL
235 CommandProcessorInfo::getCommands()
236 {
237  return *m_pInfo;
238 }
239 
240 
241 // virtual
242 ucb::CommandInfo SAL_CALL
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
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
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
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 
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 
333 OUString createDesiredName(
334  const ucb::GlobalTransferCommandArgument & rArg )
335 {
336  return createDesiredName( rArg.SourceURL, rArg.NewTitle );
337 }
338 
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 
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 {
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.get() );
370 
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 
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 
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  }));
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  }));
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  }));
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  {
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  }));
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 
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  }));
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  }));
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 
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 
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 
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  }));
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  {
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  }));
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  {
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 
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  {
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  {
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  }));
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));
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  }));
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  {
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  {
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));
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  }));
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  const OUString cmdName("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 >
1536 {
1537  return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() );
1538 }
1539 
1540 
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  }));
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  }));
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  }));
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  }));
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  }));
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  {
1844  try
1845  {
1846  ucb::Command aCommand(
1847  "delete", // Name
1848  -1, // Handle
1849  uno::makeAny( true ) ); // Argument
1850 
1851  xCommandProcessor->execute( aCommand, 0, xLocalEnv );
1852  }
1853  catch ( uno::Exception const & )
1854  {
1855  OSL_FAIL( "Cannot delete source object!" );
1856  throw;
1857  }
1858  }
1859 }
1860 
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  }));
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  }));
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: */
tools::SvRef< SvBaseLink > xSink
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:1541
ABORT
#define GLOBALTRANSFER_NAME
Definition: ucbcmds.hxx:30
Reference< XRow > xRow
UNKNOWN
#define CHECKIN_HANDLE
Definition: ucbcmds.hxx:34
NameClashContinuation
Definition: ucbcmds.cxx:346
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:1861
virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL createContentIdentifier(const OUString &ContentId) override
Definition: ucb.cxx:510
Reference< XStream > m_xStream
css::uno::Reference< css::uno::XComponentContext > m_xContext
Definition: ucb.hxx:160
OUString aName
#define GETCOMMANDINFO_NAME
Definition: ucbcmds.hxx:27
SQLHANDLE Handle
static css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo()
Definition: ucbcmds.cxx:1535
#define CHECKIN_NAME
Definition: ucbcmds.hxx:33
Reference< XContentIdentifier > xId
#define GETCOMMANDINFO_HANDLE
Definition: ucbcmds.hxx:28
OUString aCommand
Reference< XComponentContext > m_xContext
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier) override
Definition: ucb.cxx:462
sal_uInt16 nPos
::rtl::Reference< IEventProcessor > xProcessor