LibreOffice Module ucb (master) 1
webdavcontent.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
23#include <rtl/uri.hxx>
24#include <sal/log.hxx>
25#include <officecfg/Office/Common.hxx>
26#include <officecfg/Inet.hxx>
28#include <ucbhelper/macros.hxx>
33
34#include <com/sun/star/beans/IllegalTypeException.hpp>
35#include <com/sun/star/beans/NotRemoveableException.hpp>
36#include <com/sun/star/beans/PropertyAttribute.hpp>
37#include <com/sun/star/beans/PropertyExistException.hpp>
38#include <com/sun/star/beans/PropertySetInfoChange.hpp>
39#include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
40#include <com/sun/star/beans/PropertyValue.hpp>
41#include <com/sun/star/io/XActiveDataSink.hpp>
42#include <com/sun/star/io/XOutputStream.hpp>
43#include <com/sun/star/lang/IllegalAccessException.hpp>
44#include <com/sun/star/lang/IllegalArgumentException.hpp>
45#include <com/sun/star/sdbc/SQLException.hpp>
46#include <com/sun/star/task/PasswordContainerInteractionHandler.hpp>
47#include <com/sun/star/ucb/CommandEnvironment.hpp>
48#include <com/sun/star/ucb/CommandFailedException.hpp>
49#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
50#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
51#include <com/sun/star/ucb/InsertCommandArgument.hpp>
52#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
53#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
54#include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
55#include <com/sun/star/ucb/InteractiveLockingLockExpiredException.hpp>
56#include <com/sun/star/ucb/InteractiveLockingNotLockedException.hpp>
57#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
58#include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
59#include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
60#include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
61#include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
62#include <com/sun/star/ucb/MissingInputStreamException.hpp>
63#include <com/sun/star/ucb/MissingPropertiesException.hpp>
64#include <com/sun/star/ucb/NameClash.hpp>
65#include <com/sun/star/ucb/NameClashException.hpp>
66#include <com/sun/star/ucb/OpenCommandArgument3.hpp>
67#include <com/sun/star/ucb/OpenMode.hpp>
68#include <com/sun/star/ucb/PostCommandArgument2.hpp>
69#include <com/sun/star/ucb/PropertyCommandArgument.hpp>
70#include <com/sun/star/ucb/TransferInfo.hpp>
71#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
72#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
73#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
74#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
75#include <com/sun/star/ucb/XCommandInfo.hpp>
76#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
77#include <com/sun/star/uno/XComponentContext.hpp>
78
79#include "webdavcontent.hxx"
80#include "webdavprovider.hxx"
81#include "webdavresultset.hxx"
82#include "ContentProperties.hxx"
83#include "CurlUri.hxx"
85#include "DAVException.hxx"
86#include "DAVProperties.hxx"
87
88using namespace com::sun::star;
89using namespace http_dav_ucp;
90
91namespace
92{
93void lcl_sendPartialGETRequest( bool &bError,
94 DAVException &aLastException,
95 const std::vector< OUString >& rProps,
96 std::vector< OUString > &aHeaderNames,
97 const std::unique_ptr< DAVResourceAccess > &xResAccess,
98 std::unique_ptr< ContentProperties > &xProps,
99 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
100{
101 DAVResource aResource;
102 DAVRequestHeaders aPartialGet;
103 aPartialGet.push_back(
105 OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1>
106 OUString( "bytes=0-0" )));
107
108 bool bIsRequestSize = std::any_of(aHeaderNames.begin(), aHeaderNames.end(),
109 [](const OUString& rHeaderName) { return rHeaderName == "Content-Length"; });
110
111 if ( bIsRequestSize )
112 {
113 // we need to know if the server accepts range requests for a resource
114 // and the range unit it uses
115 aHeaderNames.push_back( OUString( "Accept-Ranges" ) ); // see <https://tools.ietf.org/html/rfc7233#section-2.3>
116 aHeaderNames.push_back( OUString( "Content-Range" ) ); // see <https://tools.ietf.org/html/rfc7233#section-4.2>
117 }
118 try
119 {
120 xResAccess->GET0( aPartialGet, aHeaderNames, aResource, xEnv );
121 bError = false;
122
123 if ( bIsRequestSize )
124 {
125 // the ContentProperties maps "Content-Length" to the UCB "Size" property
126 // This would have an unrealistic value of 1 byte because we did only a partial GET
127 // Solution: if "Content-Range" is present, map it with UCB "Size" property
128 OUString aAcceptRanges, aContentRange, aContentLength;
129 std::vector< DAVPropertyValue > &aResponseProps = aResource.properties;
130 for ( const auto& rResponseProp : aResponseProps )
131 {
132 if ( rResponseProp.Name == "Accept-Ranges" )
133 rResponseProp.Value >>= aAcceptRanges;
134 else if ( rResponseProp.Name == "Content-Range" )
135 rResponseProp.Value >>= aContentRange;
136 else if ( rResponseProp.Name == "Content-Length" )
137 rResponseProp.Value >>= aContentLength;
138 }
139
140 sal_Int64 nSize = 1;
141 if ( aContentLength.getLength() )
142 {
143 nSize = aContentLength.toInt64();
144 }
145
146 // according to http://tools.ietf.org/html/rfc2616#section-3.12
147 // the only range unit defined is "bytes" and implementations
148 // MAY ignore ranges specified using other units.
149 if ( nSize == 1 &&
150 aContentRange.getLength() &&
151 aAcceptRanges == "bytes" )
152 {
153 // Parse the Content-Range to get the size
154 // vid. http://tools.ietf.org/html/rfc2616#section-14.16
155 // Content-Range: <range unit> <bytes range>/<size>
156 sal_Int32 nSlash = aContentRange.lastIndexOf( '/' );
157 if ( nSlash != -1 )
158 {
159 OUString aSize = aContentRange.copy( nSlash + 1 );
160 // "*" means that the instance-length is unknown at the time when the response was generated
161 if ( aSize != "*" )
162 {
163 auto it = std::find_if(aResponseProps.begin(), aResponseProps.end(),
164 [](const DAVPropertyValue& rProp) { return rProp.Name == "Content-Length"; });
165 if (it != aResponseProps.end())
166 {
167 it->Value <<= aSize;
168 }
169 }
170 }
171 }
172 }
173
174 if (xProps)
175 xProps->addProperties(
176 rProps,
177 ContentProperties( aResource ) );
178 else
179 xProps.reset ( new ContentProperties( aResource ) );
180 }
181 catch ( DAVException const & ex )
182 {
183 aLastException = ex;
184 }
185}
186}
187
188// Static value, to manage a simple OPTIONS cache
189// Key is the URL, element is the DAVOptions resulting from an OPTIONS call.
190// Cached DAVOptions have a lifetime that depends on the errors received or not received
191// and on the value of received options.
193
194
195// Content Implementation.
196
197
198// ctr for content on an existing webdav resource
199Content::Content(
200 const uno::Reference< uno::XComponentContext >& rxContext,
201 ContentProvider* pProvider,
202 const uno::Reference< ucb::XContentIdentifier >& Identifier,
203 rtl::Reference< DAVSessionFactory > const & rSessionFactory )
204: ContentImplHelper( rxContext, pProvider, Identifier ),
205 m_eResourceType( UNKNOWN ),
206 m_eResourceTypeForLocks( UNKNOWN ),
207 m_pProvider( pProvider ),
208 m_bTransient( false ),
209 m_bCollection( false ),
210 m_bDidGetOrHead( false )
211{
212 try
213 {
214 initOptsCacheLifeTime();
215 m_xResAccess.reset( new DAVResourceAccess(
216 rxContext,
217 rSessionFactory,
218 Identifier->getContentIdentifier() ) );
219
220 CurlUri const aURI( Identifier->getContentIdentifier() );
221 m_aEscapedTitle = aURI.GetPathBaseName();
222 }
223 catch ( DAVException const & )
224 {
225 throw ucb::ContentCreationException();
226 }
227}
228
229
230// ctr for content on a non-existing webdav resource
231Content::Content(
232 const uno::Reference< uno::XComponentContext >& rxContext,
233 ContentProvider* pProvider,
234 const uno::Reference< ucb::XContentIdentifier >& Identifier,
235 rtl::Reference< DAVSessionFactory > const & rSessionFactory,
236 bool isCollection )
237: ContentImplHelper( rxContext, pProvider, Identifier ),
238 m_eResourceType( UNKNOWN ),
239 m_eResourceTypeForLocks( UNKNOWN ),
240 m_pProvider( pProvider ),
241 m_bTransient( true ),
242 m_bCollection( isCollection ),
243 m_bDidGetOrHead( false )
244{
245 try
246 {
247 initOptsCacheLifeTime();
248 m_xResAccess.reset( new DAVResourceAccess(
249 rxContext, rSessionFactory, Identifier->getContentIdentifier() ) );
250 }
251 catch ( DAVException const & )
252 {
253 throw ucb::ContentCreationException();
254 }
255
256 // Do not set m_aEscapedTitle here! Content::insert relays on this!!!
257}
258
259
260// virtual
261Content::~Content()
262{
263}
264
265
266// XInterface methods.
267
268
269// virtual
270void SAL_CALL Content::acquire() noexcept
271{
272 ContentImplHelper::acquire();
273}
274
275
276// virtual
277void SAL_CALL Content::release() noexcept
278{
279 ContentImplHelper::release();
280}
281
282
283// virtual
284uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
285{
286 // Note: isFolder may require network activities! So call it only
287 // if it is really necessary!!!
289 rType,
290 static_cast< ucb::XContentCreator * >( this ) );
291 if ( aRet.hasValue() )
292 {
293 try
294 {
295 uno::Reference< task::XInteractionHandler > xIH(
296 task::PasswordContainerInteractionHandler::create(m_xContext) );
297
298 // Supply a command env to isFolder() that contains an interaction
299 // handler that uses the password container service to obtain
300 // credentials without displaying a password gui.
301
302 uno::Reference< ucb::XCommandEnvironment > xCmdEnv(
303 ucb::CommandEnvironment::create(
305 xIH,
306 uno::Reference< ucb::XProgressHandler >() ) );
307
308 return isFolder( xCmdEnv ) ? aRet : uno::Any();
309 }
310 catch ( uno::RuntimeException const & )
311 {
312 throw;
313 }
314 catch ( uno::Exception const & )
315 {
316 return uno::Any();
317 }
318 }
319 return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
320}
321
322
323// XTypeProvider methods.
324
325
327
328
329// virtual
330uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
331{
332 bool bFolder = false;
333 try
334 {
335 bFolder
336 = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
337 }
338 catch ( uno::RuntimeException const & )
339 {
340 throw;
341 }
342 catch ( uno::Exception const & )
343 {
344 }
345
346 if ( bFolder )
347 {
348 static cppu::OTypeCollection s_aFolderTypes(
349 CPPU_TYPE_REF( lang::XTypeProvider ),
350 CPPU_TYPE_REF( lang::XServiceInfo ),
351 CPPU_TYPE_REF( lang::XComponent ),
352 CPPU_TYPE_REF( ucb::XContent ),
353 CPPU_TYPE_REF( ucb::XCommandProcessor ),
354 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
355 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
356 CPPU_TYPE_REF( beans::XPropertyContainer ),
357 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
358 CPPU_TYPE_REF( container::XChild ),
359 CPPU_TYPE_REF( ucb::XContentCreator ) );
360
361 return s_aFolderTypes.getTypes();
362 }
363 else
364 {
365 static cppu::OTypeCollection s_aDocumentTypes(
366 CPPU_TYPE_REF( lang::XTypeProvider ),
367 CPPU_TYPE_REF( lang::XServiceInfo ),
368 CPPU_TYPE_REF( lang::XComponent ),
369 CPPU_TYPE_REF( ucb::XContent ),
370 CPPU_TYPE_REF( ucb::XCommandProcessor ),
371 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
372 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
373 CPPU_TYPE_REF( beans::XPropertyContainer ),
374 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
375 CPPU_TYPE_REF( container::XChild ) );
376
377 return s_aDocumentTypes.getTypes();
378 }
379}
380
381
382// XServiceInfo methods.
383
384
385// virtual
387{
388 return "com.sun.star.comp.ucb.WebDAVContent";
389}
390
391
392// virtual
393uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
394{
395 uno::Sequence<OUString> aSNS { WEBDAV_CONTENT_SERVICE_NAME };
396 return aSNS;
397}
398
399
400// XContent methods.
401
402
403// virtual
404OUString SAL_CALL Content::getContentType()
405{
406 bool bFolder = false;
407 try
408 {
409 bFolder
410 = isFolder( uno::Reference< ucb::XCommandEnvironment >() );
411 }
412 catch ( uno::RuntimeException const & )
413 {
414 throw;
415 }
416 catch ( uno::Exception const & )
417 {
418 }
419
420 if ( bFolder )
422
423 return WEBDAV_CONTENT_TYPE;
424}
425
426
427// XCommandProcessor methods.
428
429
430// virtual
432 const ucb::Command& aCommand,
433 sal_Int32 /*CommandId*/,
434 const uno::Reference< ucb::XCommandEnvironment >& Environment )
435{
436 SAL_INFO("ucb.ucp.webdav", ">>>>> Content::execute: start: command: " << aCommand.Name
437 << ", env: " << (Environment.is() ? "present" : "missing") );
438
439 uno::Any aRet;
440
441 if ( aCommand.Name == "getPropertyValues" )
442 {
443
444 // getPropertyValues
445
446
447 uno::Sequence< beans::Property > Properties;
448 if ( !( aCommand.Argument >>= Properties ) )
449 {
451 uno::Any( lang::IllegalArgumentException(
452 "Wrong argument type!",
453 getXWeak(),
454 -1 ) ),
455 Environment );
456 // Unreachable
457 }
458
459 aRet <<= getPropertyValues( Properties, Environment );
460 }
461 else if ( aCommand.Name == "setPropertyValues" )
462 {
463
464 // setPropertyValues
465
466
467 uno::Sequence< beans::PropertyValue > aProperties;
468 if ( !( aCommand.Argument >>= aProperties ) )
469 {
471 uno::Any( lang::IllegalArgumentException(
472 "Wrong argument type!",
473 getXWeak(),
474 -1 ) ),
475 Environment );
476 // Unreachable
477 }
478
479 if ( !aProperties.getLength() )
480 {
482 uno::Any( lang::IllegalArgumentException(
483 "No properties!",
484 getXWeak(),
485 -1 ) ),
486 Environment );
487 // Unreachable
488 }
489
490 aRet <<= setPropertyValues( aProperties, Environment );
491 }
492 else if ( aCommand.Name == "getPropertySetInfo" )
493 {
494
495 // getPropertySetInfo
496
497
498 // Note: Implemented by base class.
499 aRet <<= getPropertySetInfo( Environment,
500 false /* don't cache data */ );
501 }
502 else if ( aCommand.Name == "getCommandInfo" )
503 {
504
505 // getCommandInfo
506
507
508 // Note: Implemented by base class.
509 aRet <<= getCommandInfo( Environment, false );
510 }
511 else if ( aCommand.Name == "open" )
512 {
513
514 // open
515
516
517 ucb::OpenCommandArgument3 aOpenCommand;
518 ucb::OpenCommandArgument2 aTmp;
519 if ( !( aCommand.Argument >>= aTmp ) )
520 {
522 uno::Any( lang::IllegalArgumentException(
523 "Wrong argument type!",
524 getXWeak(),
525 -1 ) ),
526 Environment );
527 // Unreachable
528 }
529 if ( !( aCommand.Argument >>= aOpenCommand ) )
530 {
531 // compat mode, extract Arg2 info into newer structure
532 aOpenCommand.Mode = aTmp.Mode;
533 aOpenCommand.Priority = aTmp.Priority;
534 aOpenCommand.Sink = aTmp.Sink;
535 aOpenCommand.Properties = aTmp.Properties;
536 aOpenCommand.SortingInfo = aTmp.SortingInfo;
537 }
538
539 aRet = open( aOpenCommand, Environment );
540
541 }
542 else if ( aCommand.Name == "insert" )
543 {
544
545 // insert
546
547
548 ucb::InsertCommandArgument arg;
549 if ( !( aCommand.Argument >>= arg ) )
550 {
552 uno::Any( lang::IllegalArgumentException(
553 "Wrong argument type!",
554 getXWeak(),
555 -1 ) ),
556 Environment );
557 // Unreachable
558 }
559
560 insert( arg.Data, arg.ReplaceExisting, Environment );
561 }
562 else if ( aCommand.Name == "delete" )
563 {
564
565 // delete
566
567
568 bool bDeletePhysical = false;
569 aCommand.Argument >>= bDeletePhysical;
570
571// KSO: Ignore parameter and destroy the content, if you don't support
572// putting objects into trashcan. ( Since we do not have a trash can
573// service yet (src603), you actually have no other choice. )
574// if ( bDeletePhysical )
575// {
576 try
577 {
578 std::unique_ptr< DAVResourceAccess > xResAccess;
579 {
580 osl::Guard< osl::Mutex > aGuard( m_aMutex );
581 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
582 }
583 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
584 // clean cached value of PROPFIND property names
585 removeCachedPropertyNames( xResAccess->getURL() );
586 xResAccess->DESTROY( Environment );
587 {
588 osl::Guard< osl::Mutex > aGuard( m_aMutex );
589 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
590 }
591 }
592 catch ( DAVException const & e )
593 {
594 cancelCommandExecution( e, Environment, true );
595 // Unreachable
596 }
597// }
598
599 // Propagate destruction.
600 destroy( bDeletePhysical );
601
602 // Remove own and all children's Additional Core Properties.
604 }
605 else if ( aCommand.Name == "transfer" && isFolder( Environment ) )
606 {
607
608 // transfer
609 // ( Not available at documents )
610
611
612 ucb::TransferInfo transferArgs;
613 if ( !( aCommand.Argument >>= transferArgs ) )
614 {
616 uno::Any( lang::IllegalArgumentException(
617 "Wrong argument type!",
618 getXWeak(),
619 -1 ) ),
620 Environment );
621 // Unreachable
622 }
623
624 transfer( transferArgs, Environment );
625 }
626 else if ( aCommand.Name == "post" )
627 {
628
629 // post
630
631
632 ucb::PostCommandArgument2 aArg;
633 if ( !( aCommand.Argument >>= aArg ) )
634 {
636 uno::Any( lang::IllegalArgumentException(
637 "Wrong argument type!",
638 getXWeak(),
639 -1 ) ),
640 Environment );
641 // Unreachable
642 }
643
644 post( aArg, Environment );
645 }
646 else if ( aCommand.Name == "lock" )
647 {
648
649 // lock
650
651 ResourceType eType = resourceTypeForLocks( Environment );
652 // when the resource is not yet present the lock is used to create it
653 // see: http://tools.ietf.org/html/rfc4918#section-7.3
654 // If the resource doesn't exists and the lock is not enabled (DAV with
655 // no lock or a simple web) the error will be dealt with inside lock() method
656 if ( eType == NOT_FOUND ||
657 eType == DAV )
658 {
659 lock( Environment );
660 if ( eType == NOT_FOUND )
661 {
662 m_eResourceType = UNKNOWN; // lock may have created it, need to check again
664 }
665 }
666 }
667 else if ( aCommand.Name == "unlock" )
668 {
669
670 // unlock
671 // do not check for a DAV resource
672 // the lock store will be checked before sending
673 unlock( Environment );
674 }
675 else if ( aCommand.Name == "createNewContent" &&
676 isFolder( Environment ) )
677 {
678
679 // createNewContent
680
681
682 ucb::ContentInfo aArg;
683 if ( !( aCommand.Argument >>= aArg ) )
684 {
686 uno::Any( lang::IllegalArgumentException(
687 "Wrong argument type!",
688 getXWeak(),
689 -1 ) ),
690 Environment );
691 // Unreachable
692 }
693
694 aRet <<= createNewContent( aArg );
695 }
696 else if ( aCommand.Name == "addProperty" )
697 {
698 ucb::PropertyCommandArgument aPropArg;
699 if ( !( aCommand.Argument >>= aPropArg ))
700 {
702 uno::Any( lang::IllegalArgumentException(
703 "Wrong argument type!",
704 getXWeak(),
705 -1 ) ),
706 Environment );
707 }
708
709 // TODO when/if XPropertyContainer is removed,
710 // the command execution can be canceled in addProperty
711 try
712 {
713 addProperty( aPropArg, Environment );
714 }
715 catch ( const beans::PropertyExistException &e )
716 {
717 ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
718 }
719 catch ( const beans::IllegalTypeException&e )
720 {
721 ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
722 }
723 catch ( const lang::IllegalArgumentException&e )
724 {
725 ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
726 }
727 }
728 else if ( aCommand.Name == "removeProperty" )
729 {
730 OUString sPropName;
731 if ( !( aCommand.Argument >>= sPropName ) )
732 {
734 uno::Any( lang::IllegalArgumentException(
735 "Wrong argument type!",
736 getXWeak(),
737 -1 ) ),
738 Environment );
739 }
740
741 // TODO when/if XPropertyContainer is removed,
742 // the command execution can be canceled in removeProperty
743 try
744 {
745 removeProperty( sPropName, Environment );
746 }
747 catch( const beans::UnknownPropertyException &e )
748 {
749 ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
750 }
751 catch( const beans::NotRemoveableException &e )
752 {
753 ucbhelper::cancelCommandExecution( uno::Any( e ), Environment );
754 }
755 }
756 else
757 {
758
759 // Unsupported command
760
761
763 uno::Any( ucb::UnsupportedCommandException(
764 aCommand.Name,
765 getXWeak() ) ),
766 Environment );
767 // Unreachable
768 }
769
770 SAL_INFO("ucb.ucp.webdav", "<<<<< Content::execute: end: command: " << aCommand.Name);
771
772 return aRet;
773}
774
775
776// virtual
777void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
778{
779 try
780 {
781 std::unique_ptr< DAVResourceAccess > xResAccess;
782 {
783 osl::MutexGuard aGuard( m_aMutex );
784 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
785 }
786 xResAccess->abort();
787 {
788 osl::Guard< osl::Mutex > aGuard( m_aMutex );
789 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
790 }
791 }
792 catch ( DAVException const & )
793 {
794 // abort failed!
795 }
796}
797
798
799// XPropertyContainer methods.
800
801
802void Content::addProperty( const css::ucb::PropertyCommandArgument &aCmdArg,
803 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
804{
805// if ( m_bTransient )
806// @@@ ???
807 const beans::Property aProperty = aCmdArg.Property;
808 const uno::Any aDefaultValue = aCmdArg.DefaultValue;
809
810 // check property Name
811 if ( !aProperty.Name.getLength() )
812 throw lang::IllegalArgumentException(
813 "\"addProperty\" with empty Property.Name",
814 getXWeak(),
815 -1 );
816
817 // Check property type.
818 if ( !UCBDeadPropertyValue::supportsType( aProperty.Type ) )
819 throw beans::IllegalTypeException(
820 "\"addProperty\" unsupported Property.Type",
821 getXWeak() );
822
823 // check default value
824 if ( aDefaultValue.hasValue() && aDefaultValue.getValueType() != aProperty.Type )
825 throw beans::IllegalTypeException(
826 "\"addProperty\" DefaultValue does not match Property.Type",
827 getXWeak() );
828
829
830 // Make sure a property with the requested name does not already
831 // exist in dynamic and static(!) properties.
832
833
834 // Take into account special properties with custom namespace
835 // using <prop:the_propname xmlns:prop="the_namespace">
836 OUString aSpecialName;
837 bool bIsSpecial = DAVProperties::isUCBSpecialProperty( aProperty.Name, aSpecialName );
838
839 // Note: This requires network access!
840 if ( getPropertySetInfo( xEnv, false /* don't cache data */ )
841 ->hasPropertyByName( bIsSpecial ? aSpecialName : aProperty.Name ) )
842 {
843 // Property does already exist.
844 throw beans::PropertyExistException();
845 }
846
847
848 // Add a new dynamic property.
849
850
851 ProppatchValue aValue( PROPSET, aProperty.Name, aDefaultValue );
852
853 std::vector< ProppatchValue > aProppatchValues;
854 aProppatchValues.push_back( aValue );
855
856 try
857 {
858 // Set property value at server.
859 std::unique_ptr< DAVResourceAccess > xResAccess;
860 {
861 osl::Guard< osl::Mutex > aGuard( m_aMutex );
862 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
863 }
864 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
865 // clean cached value of PROPFIND property names
866 // PROPPATCH can change them
867 removeCachedPropertyNames( xResAccess->getURL() );
868 xResAccess->PROPPATCH( aProppatchValues, xEnv );
869 {
870 osl::Guard< osl::Mutex > aGuard( m_aMutex );
871 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
872 }
873
874 // Notify propertyset info change listeners.
875 beans::PropertySetInfoChangeEvent evt(
876 getXWeak(),
877 bIsSpecial ? aSpecialName : aProperty.Name,
878 -1, // No handle available
879 beans::PropertySetInfoChange::PROPERTY_INSERTED );
881 }
882 catch ( DAVException const & e )
883 {
884 if ( e.getStatus() == SC_FORBIDDEN )
885 {
886 // Support for setting arbitrary dead properties is optional!
887
888 // Store property locally.
889 ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
890 aProperty.Attributes,
891 aDefaultValue );
892 }
893 else
894 {
896 {
897 try
898 {
899 const ResourceType eType = getResourceType( xEnv );
900 switch ( eType )
901 {
902 case UNKNOWN:
903 case DAV:
904 throw lang::IllegalArgumentException();
905
906 case NON_DAV:
907 // Store property locally.
908 ContentImplHelper::addProperty( bIsSpecial ? aSpecialName : aProperty.Name,
909 aProperty.Attributes,
910 aDefaultValue );
911 break;
912
913 default:
914 SAL_WARN( "ucb.ucp.webdav",
915 "Content::addProperty - "
916 "Unsupported resource type!" );
917 break;
918 }
919 }
920 catch ( uno::Exception const & )
921 {
922 SAL_WARN( "ucb.ucp.webdav",
923 "Content::addProperty - "
924 "Unable to determine resource type!" );
925 }
926 }
927 else
928 {
929 SAL_WARN( "ucb.ucp.webdav",
930 "Content::addProperty - "
931 "Unable to determine resource type!" );
932 }
933 }
934 }
935}
936
937void Content::removeProperty( const OUString& Name,
938 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
939{
940#if 0
941 // @@@ REMOVABLE at the moment not properly set in the PropSetInfo
942 try
943 {
944 beans::Property aProp
945 = getPropertySetInfo( xEnv, false /* don't cache data */ )
946 ->getPropertyByName( Name );
947
948 if ( !( aProp.Attributes & beans::PropertyAttribute::REMOVABLE ) )
949 {
950 // Not removable!
951 throw beans::NotRemoveableException();
952 }
953 }
954 catch ( beans::UnknownPropertyException const & )
955 {
956 //SAL_WARN( "ucb.ucp.webdav", "removeProperty - Unknown property!" );
957 throw;
958 }
959#endif
960
961 // Try to remove property from server.
962 try
963 {
964 std::vector< ProppatchValue > aProppatchValues;
965 ProppatchValue aValue( PROPREMOVE, Name, uno::Any() );
966 aProppatchValues.push_back( aValue );
967
968 // Remove property value from server.
969 std::unique_ptr< DAVResourceAccess > xResAccess;
970 {
971 osl::Guard< osl::Mutex > aGuard( m_aMutex );
972 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
973 }
974 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
975 // clean cached value of PROPFIND property names
976 // PROPPATCH can change them
977 removeCachedPropertyNames( xResAccess->getURL() );
978 xResAccess->PROPPATCH( aProppatchValues, xEnv );
979 {
980 osl::Guard< osl::Mutex > aGuard( m_aMutex );
981 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
982 }
983
984 // Notify propertyset info change listeners.
985 beans::PropertySetInfoChangeEvent evt(
986 getXWeak(),
987 Name,
988 -1, // No handle available
989 beans::PropertySetInfoChange::PROPERTY_REMOVED );
991 }
992 catch ( DAVException const & e )
993 {
994 if ( e.getStatus() == SC_FORBIDDEN )
995 {
996 // Support for setting arbitrary dead properties is optional!
997
998 // Try to remove property from local store.
999 ContentImplHelper::removeProperty( Name );
1000 }
1001 else
1002 {
1004 {
1005 try
1006 {
1007 const ResourceType eType = getResourceType( xEnv );
1008 switch ( eType )
1009 {
1010 case UNKNOWN:
1011 case DAV:
1012 throw beans::UnknownPropertyException(Name);
1013
1014 case NON_DAV:
1015 // Try to remove property from local store.
1016 ContentImplHelper::removeProperty( Name );
1017 break;
1018
1019 default:
1020 SAL_WARN( "ucb.ucp.webdav",
1021 "Content::removeProperty - "
1022 "Unsupported resource type!" );
1023 break;
1024 }
1025 }
1026 catch ( uno::Exception const & )
1027 {
1028 SAL_WARN( "ucb.ucp.webdav",
1029 "Content::removeProperty - "
1030 "Unable to determine resource type!" );
1031 }
1032 }
1033 else
1034 {
1035 SAL_WARN( "ucb.ucp.webdav",
1036 "Content::removeProperty - "
1037 "Unable to determine resource type!" );
1038// throw beans::UnknownPropertyException();
1039 }
1040 }
1041 }
1042}
1043
1044// virtual
1045void SAL_CALL Content::addProperty( const OUString& Name,
1046 sal_Int16 Attributes,
1047 const uno::Any& DefaultValue )
1048{
1049 beans::Property aProperty;
1050 aProperty.Name = Name;
1051 aProperty.Type = DefaultValue.getValueType();
1052 aProperty.Attributes = Attributes;
1053 aProperty.Handle = -1;
1054
1055 addProperty( ucb::PropertyCommandArgument( aProperty, DefaultValue ),
1056 uno::Reference< ucb::XCommandEnvironment >());
1057}
1058
1059// virtual
1060void SAL_CALL Content::removeProperty( const OUString& Name )
1061{
1063 uno::Reference< ucb::XCommandEnvironment >() );
1064}
1065
1066
1067// XContentCreator methods.
1068
1069
1070// virtual
1071uno::Sequence< ucb::ContentInfo > SAL_CALL
1073{
1074 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1075
1076 uno::Sequence< ucb::ContentInfo > aSeq( 2 );
1077
1078 // document.
1079 aSeq.getArray()[ 0 ].Type = WEBDAV_CONTENT_TYPE;
1080 aSeq.getArray()[ 0 ].Attributes
1081 = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
1082 | ucb::ContentInfoAttribute::KIND_DOCUMENT;
1083
1084 beans::Property aProp;
1085 m_pProvider->getProperty( "Title", aProp );
1086
1087 uno::Sequence< beans::Property > aDocProps( 1 );
1088 aDocProps.getArray()[ 0 ] = aProp;
1089 aSeq.getArray()[ 0 ].Properties = aDocProps;
1090
1091 // folder.
1092 aSeq.getArray()[ 1 ].Type = WEBDAV_COLLECTION_TYPE;
1093 aSeq.getArray()[ 1 ].Attributes
1094 = ucb::ContentInfoAttribute::KIND_FOLDER;
1095
1096 uno::Sequence< beans::Property > aFolderProps( 1 );
1097 aFolderProps.getArray()[ 0 ] = aProp;
1098 aSeq.getArray()[ 1 ].Properties = aFolderProps;
1099 return aSeq;
1100}
1101
1102
1103// virtual
1104uno::Reference< ucb::XContent > SAL_CALL
1105Content::createNewContent( const ucb::ContentInfo& Info )
1106{
1107 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1108
1109 if ( !Info.Type.getLength() )
1110 return uno::Reference< ucb::XContent >();
1111
1112 if ( ( Info.Type != WEBDAV_COLLECTION_TYPE )
1113 &&
1114 ( Info.Type != WEBDAV_CONTENT_TYPE ) )
1115 return uno::Reference< ucb::XContent >();
1116
1117 OUString aURL = m_xIdentifier->getContentIdentifier();
1118
1119 SAL_WARN_IF( aURL.isEmpty(), "ucb.ucp.webdav",
1120 "WebdavContent::createNewContent - empty identifier!" );
1121
1122 if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
1123 aURL += "/";
1124
1125 bool isCollection;
1126 if ( Info.Type == WEBDAV_COLLECTION_TYPE )
1127 {
1128 aURL += "New_Collection";
1129 isCollection = true;
1130 }
1131 else
1132 {
1133 aURL += "New_Content";
1134 isCollection = false;
1135 }
1136
1137 uno::Reference< ucb::XContentIdentifier > xId(
1138 new ::ucbhelper::ContentIdentifier( aURL ) );
1139
1140 // create the local content
1141 try
1142 {
1143 return new ::http_dav_ucp::Content( m_xContext,
1145 xId,
1146 m_xResAccess->getSessionFactory(),
1147 isCollection );
1148 }
1149 catch ( ucb::ContentCreationException & )
1150 {
1151 return uno::Reference< ucb::XContent >();
1152 }
1153}
1154
1155
1156// virtual
1158{
1159 // <scheme>:// -> ""
1160 // <scheme>://foo -> ""
1161 // <scheme>://foo/ -> ""
1162 // <scheme>://foo/bar -> <scheme>://foo/
1163 // <scheme>://foo/bar/ -> <scheme>://foo/
1164 // <scheme>://foo/bar/abc -> <scheme>://foo/bar/
1165
1166 OUString aURL = m_xIdentifier->getContentIdentifier();
1167
1168 sal_Int32 nPos = aURL.lastIndexOf( '/' );
1169 if ( nPos == ( aURL.getLength() - 1 ) )
1170 {
1171 // Trailing slash found. Skip.
1172 nPos = aURL.lastIndexOf( '/', nPos );
1173 }
1174
1175 sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
1176 if ( nPos1 != -1 )
1177 nPos1 = aURL.lastIndexOf( '/', nPos1 );
1178
1179 if ( nPos1 == -1 )
1180 return OUString();
1181
1182 return aURL.copy( 0, nPos + 1 );
1183}
1184
1185
1186// Non-interface methods.
1187
1188
1189// static
1190uno::Reference< sdbc::XRow > Content::getPropertyValues(
1191 const uno::Reference< uno::XComponentContext >& rxContext,
1192 const uno::Sequence< beans::Property >& rProperties,
1193 const ContentProperties& rData,
1195 const OUString& rContentId )
1196{
1197 // Note: Empty sequence means "get values of all supported properties".
1198
1200 = new ::ucbhelper::PropertyValueSet( rxContext );
1201
1202 sal_Int32 nCount = rProperties.getLength();
1203 if ( nCount )
1204 {
1205 uno::Reference< beans::XPropertySet > xAdditionalPropSet;
1206 bool bTriedToGetAdditionalPropSet = false;
1207
1208 const beans::Property* pProps = rProperties.getConstArray();
1209 for ( sal_Int32 n = 0; n < nCount; ++n )
1210 {
1211 const beans::Property& rProp = pProps[ n ];
1212
1213 // Process standard UCB, DAV and HTTP properties.
1214 const uno::Any & rValue = rData.getValue( rProp.Name );
1215 if ( rValue.hasValue() )
1216 {
1217 xRow->appendObject( rProp, rValue );
1218 }
1219 else
1220 {
1221 // Process local Additional Properties.
1222 if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
1223 {
1224 xAdditionalPropSet =
1225 rProvider->getAdditionalPropertySet( rContentId,
1226 false );
1227 bTriedToGetAdditionalPropSet = true;
1228 }
1229
1230 if ( !xAdditionalPropSet.is() ||
1231 !xRow->appendPropertySetValue(
1232 xAdditionalPropSet, rProp ) )
1233 {
1234 // Append empty entry.
1235 xRow->appendVoid( rProp );
1236 }
1237 }
1238 }
1239 }
1240 else
1241 {
1242 // Append all standard UCB, DAV and HTTP properties.
1243
1244 const std::unique_ptr< PropertyValueMap > & xProps = rData.getProperties();
1245
1246 ContentProvider * pProvider
1247 = static_cast< ContentProvider * >( rProvider.get() );
1248 beans::Property aProp;
1249
1250 for ( const auto& rProp : *xProps )
1251 {
1252 if ( pProvider->getProperty( rProp.first, aProp ) )
1253 xRow->appendObject( aProp, rProp.second.value() );
1254 }
1255
1256 // Append all local Additional Properties.
1257 uno::Reference< beans::XPropertySet > xSet =
1258 rProvider->getAdditionalPropertySet( rContentId, false );
1259 xRow->appendPropertySet( xSet );
1260 }
1261
1262 return uno::Reference<sdbc::XRow>(xRow);
1263}
1264
1265namespace {
1266void GetPropsUsingHeadRequest(DAVResource& resource,
1267 const std::unique_ptr< DAVResourceAccess >& xResAccess,
1268 const std::vector< OUString >& aHTTPNames,
1269 const uno::Reference< ucb::XCommandEnvironment >& xEnv)
1270{
1271 if (!aHTTPNames.empty())
1272 {
1273 DAVOptions aDAVOptions;
1274 OUString aTargetURL = xResAccess->getURL();
1275 // retrieve the cached options if any
1276 aStaticDAVOptionsCache.getDAVOptions(aTargetURL, aDAVOptions);
1277
1278 // clean cached value of PROPFIND property names
1279 // PROPPATCH can change them
1281 // test if HEAD allowed, if not, throw, should be caught immediately
1282 // SC_GONE used internally by us, see comment in Content::getPropertyValues
1283 // in the catch scope
1284 if (aDAVOptions.getHttpResponseStatusCode() != SC_GONE &&
1285 !aDAVOptions.isHeadAllowed())
1286 {
1288 }
1289 // if HEAD is enabled on this site
1290 // check if there is a relevant HTTP response status code cached
1291 if (aDAVOptions.getHttpResponseStatusCode() != SC_NONE)
1292 {
1293 // throws exception as if there was a server error, a DAV exception
1295 aDAVOptions.getHttpResponseStatusText(),
1296 aDAVOptions.getHttpResponseStatusCode());
1297 // Unreachable
1298 }
1299
1300 xResAccess->HEAD(aHTTPNames, resource, xEnv);
1301 }
1302}
1303}
1304
1305uno::Reference< sdbc::XRow > Content::getPropertyValues(
1306 const uno::Sequence< beans::Property >& rProperties,
1307 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1308{
1309 std::unique_ptr< ContentProperties > xProps;
1310 std::unique_ptr< ContentProperties > xCachedProps;
1311 std::unique_ptr< DAVResourceAccess > xResAccess;
1312 OUString aUnescapedTitle;
1313 bool bHasAll = false;
1314 uno::Reference< uno::XComponentContext > xContext;
1315 uno::Reference< ucb::XContentIdentifier > xIdentifier;
1317
1318 {
1319 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1320
1321 aUnescapedTitle = DecodeURI(m_aEscapedTitle);
1322 xContext.set( m_xContext );
1323 xIdentifier.set( m_xIdentifier );
1324 xProvider = m_xProvider;
1325 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
1326
1327 // First, ask cache...
1328 if (m_xCachedProps)
1329 {
1330 xCachedProps.reset( new ContentProperties( *m_xCachedProps ) );
1331
1332 std::vector< OUString > aMissingProps;
1333 if ( xCachedProps->containsAllNames( rProperties, aMissingProps ) )
1334 {
1335 // All properties are already in cache! No server access needed.
1336 bHasAll = true;
1337 }
1338
1339 // use the cached ContentProperties instance
1340 xProps.reset( new ContentProperties( *xCachedProps ) );
1341 }
1342 }
1343
1344 bool bNetworkAccessAllowed = true;
1345
1346 if ( !m_bTransient && !bHasAll )
1347 {
1348 // Obtain values from server...
1349
1350
1351 // First, identify whether resource is DAV or not
1353 xEnv, xResAccess, &bNetworkAccessAllowed );
1354
1355 if ( eType == DAV )
1356 {
1357 // cache lookup... getResourceType may fill the props cache via
1358 // PROPFIND!
1359 if (m_xCachedProps)
1360 {
1361 xCachedProps.reset(
1363
1364 std::vector< OUString > aMissingProps;
1365 if ( xCachedProps->containsAllNames(
1366 rProperties, aMissingProps ) )
1367 {
1368 // All properties are already in cache! No server access
1369 // needed.
1370 bHasAll = true;
1371 }
1372
1373 // use the cached ContentProperties instance
1374 xProps.reset( new ContentProperties( *xCachedProps ) );
1375 }
1376
1377 if ( !bHasAll )
1378 {
1379 // Only DAV resources support PROPFIND
1380 std::vector< OUString > aPropNames;
1381
1382 uno::Sequence< beans::Property > aProperties(rProperties);
1383
1384 if ( aProperties.getLength() > 0 )
1386 aProperties, aPropNames );
1387
1388 if ( !aPropNames.empty() )
1389 {
1390 std::vector< DAVResource > resources;
1391 try
1392 {
1393 xResAccess->PROPFIND(
1394 DAVZERO, aPropNames, resources, xEnv );
1395
1396 if ( 1 == resources.size() )
1397 {
1398#if defined SAL_LOG_INFO
1399 {//debug
1400 // print received resources
1401 std::vector< DAVPropertyValue >::const_iterator it = resources[0].properties.begin();
1402 std::vector< DAVPropertyValue >::const_iterator end = resources[0].properties.end();
1403 while ( it != end )
1404 {
1405 OUString aPropValue;
1406 bool bValue;
1407 uno::Sequence< ucb::LockEntry > aSupportedLocks;
1408 if( (*it).Value >>= aPropValue )
1409 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it).Name << ":" << aPropValue );
1410 else if( (*it).Value >>= bValue )
1411 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it).Name << ":" <<
1412 ( bValue ? "true" : "false" ) );
1413 else if( (*it).Value >>= aSupportedLocks )
1414 {
1415 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getPropertyValues) - returned property: " << (*it).Name << ":" );
1416 for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
1417 {
1418 SAL_INFO( "ucb.ucp.webdav"," scope: "
1419 << (aSupportedLocks[n].Scope == ucb::LockScope_SHARED ? "shared" : "exclusive")
1420 << ", type: "
1421 << (aSupportedLocks[n].Type != ucb::LockType_WRITE ? "" : "write") );
1422 }
1423 }
1424 ++it;
1425 }
1426 }
1427#endif
1428 if (xProps)
1429 xProps->addProperties(
1430 aPropNames,
1431 ContentProperties( resources[ 0 ] ));
1432 else
1433 xProps.reset(
1434 new ContentProperties( resources[ 0 ] ) );
1435 }
1436 }
1437 catch ( DAVException const & e )
1438 {
1439 bNetworkAccessAllowed = bNetworkAccessAllowed
1441
1442 if ( !bNetworkAccessAllowed )
1443 {
1444 cancelCommandExecution( e, xEnv );
1445 // unreachable
1446 }
1447 }
1448 }
1449 }
1450 }
1451
1452 if ( bNetworkAccessAllowed )
1453 {
1454 // All properties obtained already?
1455 std::vector< OUString > aMissingProps;
1456 if ( !( xProps
1457 && xProps->containsAllNames(rProperties, aMissingProps))
1458 // i#121922 for non-DAV, uncacheable properties must be fetched
1459 // regardless of m_bDidGetOrHead.
1460 // But SharePoint may do weird things on HEAD so for DAV
1461 // only do this if required.
1462 && (eType != DAV || !m_bDidGetOrHead))
1463 {
1464 // Possibly the missing props can be obtained using a HEAD
1465 // request.
1466
1467 std::vector< OUString > aHeaderNames;
1469 rProperties,
1470 aHeaderNames );
1471
1472 if( eType != DAV )
1473 {
1474 // in case of not DAV PROFIND (previously in program flow) failed
1475 // so we need to add the only prop that's common
1476 // to DAV and NON_DAV: MediaType, that maps to Content-Type
1477 aHeaderNames.push_back( "Content-Type" );
1478 }
1479
1480 if (!aHeaderNames.empty()) try
1481 {
1482 DAVResource resource;
1483 GetPropsUsingHeadRequest(resource, xResAccess, aHeaderNames, xEnv);
1484 m_bDidGetOrHead = true;
1485
1486 if (xProps)
1487 xProps->addProperties(
1488 aMissingProps,
1489 ContentProperties( resource ) );
1490 else
1491 xProps.reset ( new ContentProperties( resource ) );
1492
1493 if (m_eResourceType == NON_DAV)
1494 xProps->addProperties(aMissingProps,
1496 aUnescapedTitle,
1497 false));
1498 }
1499 catch ( DAVException const & e )
1500 {
1501 // non "general-purpose servers" may not support HEAD requests
1502 // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
1503 // In this case, perform a partial GET only to get the header info
1504 // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
1505 // WARNING if the server does not support partial GETs,
1506 // the GET will transfer the whole content
1507 bool bError = true;
1508 DAVException aLastException = e;
1509 OUString aTargetURL = xResAccess->getURL();
1510
1512 {
1513 // According to the spec. the origin server SHOULD return
1514 // * 405 (Method Not Allowed):
1515 // the method is known but not allowed for the requested resource
1516 // * 501 (Not Implemented):
1517 // the method is unrecognized or not implemented
1518 // * 404 (SC_NOT_FOUND)
1519 // is for google-code server and for MS IIS 10.0 Web server
1520 // when only GET is enabled
1521 if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED ||
1522 aLastException.getStatus() == SC_METHOD_NOT_ALLOWED ||
1523 aLastException.getStatus() == SC_NOT_FOUND )
1524 {
1525 SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
1526 aStaticDAVOptionsCache.setHeadAllowed( aTargetURL, false );
1527 lcl_sendPartialGETRequest( bError,
1528 aLastException,
1529 aMissingProps,
1530 aHeaderNames,
1531 xResAccess,
1532 xProps,
1533 xEnv );
1534 m_bDidGetOrHead = !bError;
1535 }
1536 }
1537
1538 if ( bError )
1539 {
1540 DAVOptions aDAVOptionsException;
1541
1542 aDAVOptionsException.setURL( aTargetURL );
1543 // check if the error was SC_NOT_FOUND, meaning that the
1544 // GET fall back didn't succeeded and the element is really missing
1545 // we will consider the resource SC_GONE (410) for some time
1546 // we use SC_GONE because has the same meaning of SC_NOT_FOUND (404)
1547 // see:
1548 // <https://tools.ietf.org/html/rfc7231#section-6.5.9> (retrieved 2016-10-09)
1549 // apparently it's not used to mark the missing HEAD method (so far...)
1550 sal_uInt16 ResponseStatusCode =
1551 ( aLastException.getStatus() == SC_NOT_FOUND ) ?
1552 SC_GONE :
1553 aLastException.getStatus();
1554 aDAVOptionsException.setHttpResponseStatusCode( ResponseStatusCode );
1555 aDAVOptionsException.setHttpResponseStatusText( aLastException.getData() );
1556 aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsException,
1558
1559 if ( !shouldAccessNetworkAfterException( aLastException ) )
1560 {
1561 cancelCommandExecution( aLastException, xEnv );
1562 // unreachable
1563 }
1564 }
1565 }
1566 }
1567 }
1568
1569 // might trigger HTTP redirect.
1570 // Therefore, title must be updated here.
1571 CurlUri const aUri( xResAccess->getURL() );
1572 aUnescapedTitle = aUri.GetPathBaseNameUnescaped();
1573
1574 if ( eType == UNKNOWN )
1575 {
1576 xProps.reset( new ContentProperties( aUnescapedTitle ) );
1577 }
1578
1579 // For DAV resources we only know the Title, for non-DAV
1580 // resources we additionally know that it is a document.
1581
1582 else if ( eType == DAV )
1583 {
1584 if (!xProps)
1585 xProps.reset(new ContentProperties(aUnescapedTitle));
1586 else
1587 xProps->addProperty("Title", uno::Any(aUnescapedTitle), true);
1588 }
1589 else
1590 {
1591 if (!xProps)
1592 xProps.reset( new ContentProperties( aUnescapedTitle, false ) );
1593 else
1594 xProps->addProperty(
1595 "Title",
1596 uno::Any( aUnescapedTitle ),
1597 true );
1598
1599 xProps->addProperty(
1600 "IsFolder",
1601 uno::Any( false ),
1602 true );
1603 xProps->addProperty(
1604 "IsDocument",
1605 uno::Any( true ),
1606 true );
1607 xProps->addProperty(
1608 "ContentType",
1609 uno::Any( OUString(WEBDAV_CONTENT_TYPE) ),
1610 true );
1611 }
1612 }
1613 else
1614 {
1615 // No server access for just created (not yet committed) objects.
1616 // Only a minimal set of properties supported at this stage.
1617 if (m_bTransient)
1618 xProps.reset( new ContentProperties( aUnescapedTitle,
1619 m_bCollection ) );
1620 }
1621
1622 // Add a default for the properties requested but not found.
1623 // Determine still missing properties, add a default.
1624 // Some client function doesn't expect a void uno::Any,
1625 // but instead wants some sort of default.
1626 std::vector< OUString > aMissingProps;
1627 if ( !xProps->containsAllNames(
1628 rProperties, aMissingProps ) )
1629 {
1630 //
1631 for ( std::vector< rtl::OUString >::const_iterator it = aMissingProps.begin();
1632 it != aMissingProps.end(); ++it )
1633 {
1634 // For the time being only a couple of properties need to be added
1635 if ( (*it) == "DateModified" || (*it) == "DateCreated" )
1636 {
1637 util::DateTime aDate;
1638 xProps->addProperty(
1639 (*it),
1640 uno::Any( aDate ),
1641 true );
1642 }
1643 else if (bNetworkAccessAllowed) // don't set these if connection failed
1644 {
1645 // If WebDAV didn't return the resource type, assume default
1646 // This happens e.g. for lists exported by SharePoint
1647 if ((*it) == "IsFolder")
1648 {
1649 xProps->addProperty(
1650 (*it),
1651 uno::Any( false ),
1652 true );
1653 }
1654 else if ((*it) == "IsDocument")
1655 {
1656 xProps->addProperty(
1657 (*it),
1658 uno::Any( true ),
1659 true );
1660 }
1661 }
1662 }
1663 }
1664
1665 sal_Int32 nCount = rProperties.getLength();
1666 for ( sal_Int32 n = 0; n < nCount; ++n )
1667 {
1668 const OUString rName = rProperties[ n ].Name;
1669 if ( rName == "BaseURI" )
1670 {
1671 // Add BaseURI property, if requested.
1672 xProps->addProperty(
1673 "BaseURI",
1674 uno::Any( getBaseURI( xResAccess ) ),
1675 true );
1676 }
1677 else if ( rName == "CreatableContentsInfo" )
1678 {
1679 // Add CreatableContentsInfo property, if requested.
1680 bool bFolder = false;
1681 xProps->getValue( "IsFolder" )
1682 >>= bFolder;
1683 xProps->addProperty(
1684 "CreatableContentsInfo",
1685 uno::Any( bFolder
1687 : uno::Sequence< ucb::ContentInfo >() ),
1688 true );
1689 }
1690 }
1691
1692 uno::Reference< sdbc::XRow > xResultRow
1693 = getPropertyValues( xContext,
1694 rProperties,
1695 *xProps,
1696 xProvider,
1697 xIdentifier->getContentIdentifier() );
1698
1699 {
1700 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1701
1702 if (!m_xCachedProps)
1703 m_xCachedProps.reset( new CachableContentProperties( *xProps ) );
1704 else
1705 m_xCachedProps->addProperties( *xProps );
1706
1707 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
1708 m_aEscapedTitle = EncodeSegment(aUnescapedTitle);
1709 }
1710
1711 return xResultRow;
1712}
1713
1714
1715uno::Sequence< uno::Any > Content::setPropertyValues(
1716 const uno::Sequence< beans::PropertyValue >& rValues,
1717 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1718{
1719 uno::Reference< ucb::XContentIdentifier > xIdentifier;
1721 bool bTransient;
1722 std::unique_ptr< DAVResourceAccess > xResAccess;
1723
1724 {
1725 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1726
1727 xProvider.set( m_pProvider );
1728 xIdentifier.set( m_xIdentifier );
1729 bTransient = m_bTransient;
1730 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
1731 }
1732
1733 uno::Sequence< uno::Any > aRet( rValues.getLength() );
1734 auto aRetRange = asNonConstRange(aRet);
1735 uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
1736 sal_Int32 nChanged = 0;
1737
1738 beans::PropertyChangeEvent aEvent;
1739 aEvent.Source = getXWeak();
1740 aEvent.Further = false;
1741 // aEvent.PropertyName =
1742 aEvent.PropertyHandle = -1;
1743 // aEvent.OldValue =
1744 // aEvent.NewValue =
1745
1746 std::vector< ProppatchValue > aProppatchValues;
1747 std::vector< sal_Int32 > aProppatchPropsPositions;
1748
1749 uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
1750 bool bTriedToGetAdditionalPropSet = false;
1751
1752 bool bExchange = false;
1753 OUString aNewTitle;
1754 OUString aOldTitle;
1755 sal_Int32 nTitlePos = -1;
1756
1757 uno::Reference< beans::XPropertySetInfo > xInfo;
1758
1759 const beans::PropertyValue* pValues = rValues.getConstArray();
1760 sal_Int32 nCount = rValues.getLength();
1761 for ( sal_Int32 n = 0; n < nCount; ++n )
1762 {
1763 const beans::PropertyValue& rValue = pValues[ n ];
1764 const OUString & rName = rValue.Name;
1765
1766 beans::Property aTmpProp;
1767 xProvider->getProperty( rName, aTmpProp );
1768
1769 if ( aTmpProp.Attributes & beans::PropertyAttribute::READONLY )
1770 {
1771 // Read-only property!
1772 aRetRange[ n ] <<= lang::IllegalAccessException(
1773 "Property is read-only!",
1774 getXWeak() );
1775 continue;
1776 }
1777
1778
1779 // Mandatory props.
1780
1781
1782 if ( rName == "ContentType" )
1783 {
1784 // Read-only property!
1785 aRetRange[ n ] <<= lang::IllegalAccessException(
1786 "Property is read-only!",
1787 getXWeak() );
1788 }
1789 else if ( rName == "IsDocument" )
1790 {
1791 // Read-only property!
1792 aRetRange[ n ] <<= lang::IllegalAccessException(
1793 "Property is read-only!",
1794 getXWeak() );
1795 }
1796 else if ( rName == "IsFolder" )
1797 {
1798 // Read-only property!
1799 aRetRange[ n ] <<= lang::IllegalAccessException(
1800 "Property is read-only!",
1801 getXWeak() );
1802 }
1803 else if ( rName == "Title" )
1804 {
1805 OUString aNewValue;
1806 if ( rValue.Value >>= aNewValue )
1807 {
1808 // No empty titles!
1809 if ( aNewValue.getLength() > 0 )
1810 {
1811 try
1812 {
1813 CurlUri const aURI(xIdentifier->getContentIdentifier());
1814 aOldTitle = aURI.GetPathBaseNameUnescaped();
1815
1816 if ( aNewValue != aOldTitle )
1817 {
1818 // modified title -> modified URL -> exchange !
1819 if ( !bTransient )
1820 bExchange = true;
1821
1822 // new value will be set later...
1823 aNewTitle = aNewValue;
1824
1825 // remember position within sequence of values (for
1826 // error handling).
1827 nTitlePos = n;
1828 }
1829 }
1830 catch ( DAVException const & )
1831 {
1832 aRetRange[ n ] <<= lang::IllegalArgumentException(
1833 "Invalid content identifier!",
1834 getXWeak(),
1835 -1 );
1836 }
1837 }
1838 else
1839 {
1840 aRetRange[ n ] <<= lang::IllegalArgumentException(
1841 "Empty title not allowed!",
1842 getXWeak(),
1843 -1 );
1844 }
1845 }
1846 else
1847 {
1848 aRetRange[ n ] <<= beans::IllegalTypeException(
1849 "Property value has wrong type!",
1850 getXWeak() );
1851 }
1852 }
1853 else
1854 {
1855
1856 // Optional props.
1857
1858
1859 OUString aSpecialName;
1860 bool bIsSpecial = DAVProperties::isUCBSpecialProperty( rName, aSpecialName );
1861
1862 if ( !xInfo.is() )
1863 xInfo = getPropertySetInfo( xEnv,
1864 false /* don't cache data */ );
1865
1866 if ( !xInfo->hasPropertyByName( bIsSpecial ? aSpecialName : rName ) )
1867 {
1868 // Check, whether property exists. Skip otherwise.
1869 // PROPPATCH::set would add the property automatically, which
1870 // is not allowed for "setPropertyValues" command!
1871 aRetRange[ n ] <<= beans::UnknownPropertyException(
1872 "Property is unknown!",
1873 getXWeak() );
1874 continue;
1875 }
1876
1877 if ( rName == "Size" )
1878 {
1879 // Read-only property!
1880 aRetRange[ n ] <<= lang::IllegalAccessException(
1881 "Property is read-only!",
1882 getXWeak() );
1883 }
1884 else if ( rName == "DateCreated" )
1885 {
1886 // Read-only property!
1887 aRetRange[ n ] <<= lang::IllegalAccessException(
1888 "Property is read-only!",
1889 getXWeak() );
1890 }
1891 else if ( rName == "DateModified" )
1892 {
1893 // Read-only property!
1894 aRetRange[ n ] <<= lang::IllegalAccessException(
1895 "Property is read-only!",
1896 getXWeak() );
1897 }
1898 else if ( rName == "MediaType" )
1899 {
1900 // Read-only property!
1901 // (but could be writable, if 'getcontenttype' would be)
1902 aRetRange[ n ] <<= lang::IllegalAccessException(
1903 "Property is read-only!",
1904 getXWeak() );
1905 }
1906 if ( rName == "CreatableContentsInfo" )
1907 {
1908 // Read-only property!
1909 aRetRange[ n ] <<= lang::IllegalAccessException(
1910 "Property is read-only!",
1911 getXWeak() );
1912 }
1913 else
1914 {
1915 if ( getResourceType( xEnv, xResAccess ) == DAV )
1916 {
1917 // Property value will be set on server.
1918 ProppatchValue aValue( PROPSET, rName, rValue.Value );
1919 aProppatchValues.push_back( aValue );
1920
1921 // remember position within sequence of values (for
1922 // error handling).
1923 aProppatchPropsPositions.push_back( n );
1924 }
1925 else
1926 {
1927 // Property value will be stored in local property store.
1928 if ( !bTriedToGetAdditionalPropSet &&
1929 !xAdditionalPropSet.is() )
1930 {
1931 xAdditionalPropSet
1932 = getAdditionalPropertySet( false );
1933 bTriedToGetAdditionalPropSet = true;
1934 }
1935
1936 if ( xAdditionalPropSet.is() )
1937 {
1938 try
1939 {
1940 uno::Any aOldValue
1941 = xAdditionalPropSet->getPropertyValue( rName );
1942 if ( aOldValue != rValue.Value )
1943 {
1944 xAdditionalPropSet->setPropertyValue(
1945 rName, rValue.Value );
1946
1947 aEvent.PropertyName = rName;
1948 aEvent.OldValue = aOldValue;
1949 aEvent.NewValue = rValue.Value;
1950
1951 aChanges.getArray()[ nChanged ] = aEvent;
1952 nChanged++;
1953 }
1954 }
1955 catch ( beans::UnknownPropertyException const & e )
1956 {
1957 aRetRange[ n ] <<= e;
1958 }
1959 catch ( lang::WrappedTargetException const & e )
1960 {
1961 aRetRange[ n ] <<= e;
1962 }
1963 catch ( beans::PropertyVetoException const & e )
1964 {
1965 aRetRange[ n ] <<= e;
1966 }
1967 catch ( lang::IllegalArgumentException const & e )
1968 {
1969 aRetRange[ n ] <<= e;
1970 }
1971 }
1972 else
1973 {
1974 aRetRange[ n ] <<= uno::Exception(
1975 "No property set for storing the value!",
1976 getXWeak() );
1977 }
1978 }
1979 }
1980 }
1981 } // for
1982
1983 if ( !bTransient && (!aProppatchValues.empty()) )
1984 {
1985 try
1986 {
1987 // clean cached value of PROPFIND property names
1988 // PROPPATCH can change them
1989 removeCachedPropertyNames( xResAccess->getURL() );
1990 // Set property values at server.
1991 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
1992 xResAccess->PROPPATCH( aProppatchValues, xEnv );
1993
1994 for ( const auto& rProppatchValue : aProppatchValues )
1995 {
1996 aEvent.PropertyName = rProppatchValue.name;
1997 aEvent.OldValue = uno::Any(); // @@@ too expensive to obtain!
1998 aEvent.NewValue = rProppatchValue.value;
1999
2000 aChanges.getArray()[ nChanged ] = aEvent;
2001 nChanged++;
2002 }
2003 }
2004 catch ( DAVException const & e )
2005 {
2006// SAL_WARN( "ucb.ucp.webdav",
2007// "Content::setPropertyValues - PROPPATCH failed!" );
2008
2009#if 1
2010 cancelCommandExecution( e, xEnv );
2011 // unreachable
2012#else
2013 // Note: PROPPATCH either sets ALL property values OR NOTHING.
2014
2015 std::vector< sal_Int32 >::const_iterator it
2016 = aProppatchPropsPositions.begin();
2017 std::vector< sal_Int32 >::const_iterator end
2018 = aProppatchPropsPositions.end();
2019
2020 while ( it != end )
2021 {
2022 // Set error.
2023 aRetRange[ (*it) ] <<= MapDAVException( e, true );
2024 ++it;
2025 }
2026#endif
2027 }
2028 }
2029
2030 if ( bExchange )
2031 {
2032 // Assemble new content identifier...
2033
2034 OUString aNewURL = getParentURL();
2035 if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
2036 aNewURL += "/";
2037
2038 aNewURL += EncodeSegment(aNewTitle);
2039
2040 uno::Reference< ucb::XContentIdentifier > xNewId
2041 = new ::ucbhelper::ContentIdentifier( aNewURL );
2042 uno::Reference< ucb::XContentIdentifier > xOldId = xIdentifier;
2043
2044 try
2045 {
2046 CurlUri const sourceURI( xOldId->getContentIdentifier() );
2047 CurlUri targetURI( xNewId->getContentIdentifier() );
2048
2049 targetURI.SetScheme( sourceURI.GetScheme() );
2050
2051 // clean cached value of PROPFIND property names
2052 removeCachedPropertyNames( sourceURI.GetURI() );
2053 removeCachedPropertyNames( targetURI.GetURI() );
2054 aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
2055 aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
2056 xResAccess->MOVE(
2057 sourceURI.GetRelativeReference(), targetURI.GetURI(), false, xEnv );
2058
2059 // @@@ Should check for resources that could not be moved
2060 // (due to source access or target overwrite) and send
2061 // this information through the interaction handler.
2062
2063 // @@@ Existing content should be checked to see if it needs
2064 // to be deleted at the source
2065
2066 // @@@ Existing content should be checked to see if it has
2067 // been overwritten at the target
2068
2069 if ( exchangeIdentity( xNewId ) )
2070 {
2071 xResAccess->setURL( aNewURL );
2072
2073// DAV resources store all additional props on server!
2074// // Adapt Additional Core Properties.
2075// renameAdditionalPropertySet( xOldId->getContentIdentifier(),
2076// xNewId->getContentIdentifier(),
2077// true );
2078 }
2079 else
2080 {
2081 // Do not set new title!
2082 aNewTitle.clear();
2083
2084 // Set error .
2085 aRetRange[ nTitlePos ] <<= uno::Exception(
2086 "Exchange failed!",
2087 getXWeak() );
2088 }
2089 }
2090 catch ( DAVException const & e )
2091 {
2092 // Do not set new title!
2093 aNewTitle.clear();
2094
2095 // Set error .
2096 aRetRange[ nTitlePos ] = MapDAVException( e, true );
2097 }
2098 }
2099
2100 if ( aNewTitle.getLength() )
2101 {
2102 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2103
2104 aEvent.PropertyName = "Title";
2105 aEvent.OldValue <<= aOldTitle;
2106 aEvent.NewValue <<= aNewTitle;
2107
2108 m_aEscapedTitle = EncodeSegment(aNewTitle);
2109
2110 aChanges.getArray()[ nChanged ] = aEvent;
2111 nChanged++;
2112 }
2113
2114 if ( nChanged > 0 )
2115 {
2116 aChanges.realloc( nChanged );
2117 notifyPropertiesChange( aChanges );
2118 }
2119
2120 {
2121 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2122 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2123 }
2124
2125 return aRet;
2126}
2127
2128
2130 const ucb::OpenCommandArgument3 & rArg,
2131 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2132{
2133 uno::Any aRet;
2134
2135 bool bOpenFolder = ( ( rArg.Mode == ucb::OpenMode::ALL ) ||
2136 ( rArg.Mode == ucb::OpenMode::FOLDERS ) ||
2137 ( rArg.Mode == ucb::OpenMode::DOCUMENTS ) );
2138 if ( bOpenFolder )
2139 {
2140 if ( isFolder( xEnv ) )
2141 {
2142 // Open collection.
2143
2144 uno::Reference< ucb::XDynamicResultSet > xSet
2145 = new DynamicResultSet( m_xContext, this, rArg, xEnv );
2146 aRet <<= xSet;
2147 }
2148 else
2149 {
2150 // Error: Not a folder!
2151
2153 uno::Any(
2154 lang::IllegalArgumentException(
2155 "Non-folder resource cannot be opened as folder! Wrong Open Mode!",
2156 getXWeak(),
2157 -1 ) ),
2158 xEnv );
2159 // Unreachable
2160 }
2161 }
2162
2163 if ( rArg.Sink.is() )
2164 {
2165 // Open document.
2166
2167 if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
2168 ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
2169 {
2170 // Currently(?) unsupported.
2172 uno::Any(
2173 ucb::UnsupportedOpenModeException(
2174 OUString(),
2175 getXWeak(),
2176 sal_Int16( rArg.Mode ) ) ),
2177 xEnv );
2178 // Unreachable
2179 }
2180
2181 uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
2182 if ( xOut.is() )
2183 {
2184 // PUSH: write data
2185 try
2186 {
2187 std::unique_ptr< DAVResourceAccess > xResAccess;
2188
2189 {
2190 osl::MutexGuard aGuard( m_aMutex );
2191
2192 xResAccess.reset(
2194 }
2195
2196 xResAccess->setFlags( rArg.OpeningFlags );
2197 DAVResource aResource;
2198 std::vector< OUString > aHeaders;
2199
2200 removeCachedPropertyNames( xResAccess->getURL() );
2201 xResAccess->GET( xOut, aHeaders, aResource, xEnv );
2202 m_bDidGetOrHead = true;
2203
2204 {
2205 osl::MutexGuard aGuard( m_aMutex );
2206
2207 // cache headers.
2208 if (!m_xCachedProps)
2209 m_xCachedProps.reset(
2210 new CachableContentProperties( ContentProperties( aResource ) ) );
2211 else
2212 m_xCachedProps->addProperties( ContentProperties( aResource ) );
2213
2214 m_xResAccess.reset(
2215 new DAVResourceAccess( *xResAccess ) );
2216 }
2217 }
2218 catch ( DAVException const & e )
2219 {
2220 cancelCommandExecution( e, xEnv );
2221 // Unreachable
2222 }
2223 }
2224 else
2225 {
2226 uno::Reference< io::XActiveDataSink > xDataSink( rArg.Sink, uno::UNO_QUERY );
2227 if ( xDataSink.is() )
2228 {
2229 // PULL: wait for client read
2230 OUString aTargetURL = m_xIdentifier->getContentIdentifier();
2231 try
2232 {
2233 std::unique_ptr< DAVResourceAccess > xResAccess;
2234 {
2235 osl::MutexGuard aGuard( m_aMutex );
2236
2237 xResAccess.reset(
2239 }
2240 xResAccess->setFlags( rArg.OpeningFlags );
2241
2242 // fill inputstream sync; return if all data present
2243 DAVResource aResource;
2244 std::vector< OUString > aHeaders;
2245
2246 aTargetURL = xResAccess->getURL();
2247 removeCachedPropertyNames( aTargetURL );
2248 // check if the resource was present on the server
2249 // first update it, if necessary
2250 // if the open is called directly, without the default open sequence,
2251 // e.g. the one used when opening a file looking for properties
2252 // first this call will have no effect, since OPTIONS would have already been called
2253 // as a consequence of getPropertyValues()
2254 DAVOptions aDAVOptions;
2255 getResourceOptions( xEnv, aDAVOptions, xResAccess );
2256
2257 if (aDAVOptions.getHttpResponseStatusCode() != SC_NONE
2258 // tdf#148426 fall back to GET in case of 500
2260 {
2261 // throws exception as if there was a server error, a DAV exception
2263 aDAVOptions.getHttpResponseStatusText(),
2264 aDAVOptions.getHttpResponseStatusCode() );
2265 }
2266 uno::Reference< io::XInputStream > xIn
2267 = xResAccess->GET( aHeaders, aResource, xEnv );
2268 m_bDidGetOrHead = true;
2269
2270 {
2271 osl::MutexGuard aGuard( m_aMutex );
2272
2273 // cache headers.
2274 if (!m_xCachedProps)
2275 m_xCachedProps.reset(
2276 new CachableContentProperties( ContentProperties( aResource ) ) );
2277 else
2278 m_xCachedProps->addProperties(
2279 aResource.properties );
2280
2281 m_xResAccess.reset(
2282 new DAVResourceAccess( *xResAccess ) );
2283 }
2284
2285 xDataSink->setInputStream( xIn );
2286 }
2287 catch ( DAVException const & e )
2288 {
2289 //TODO cache the http error if not yet cached
2290 cancelCommandExecution( e, xEnv );
2291 // Unreachable
2292 }
2293 }
2294 else
2295 {
2296 // Note: aOpenCommand.Sink may contain an XStream
2297 // implementation. Support for this type of
2298 // sink is optional...
2300 uno::Any(
2301 ucb::UnsupportedDataSinkException(
2302 OUString(),
2303 getXWeak(),
2304 rArg.Sink ) ),
2305 xEnv );
2306 // Unreachable
2307 }
2308 }
2309 }
2310
2311 return aRet;
2312}
2313
2314
2316 const ucb::PostCommandArgument2 & rArg,
2317 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2318{
2319 uno::Reference< io::XActiveDataSink > xSink( rArg.Sink, uno::UNO_QUERY );
2320 if ( xSink.is() )
2321 {
2322 try
2323 {
2324 std::unique_ptr< DAVResourceAccess > xResAccess;
2325 {
2326 osl::MutexGuard aGuard( m_aMutex );
2327 xResAccess.reset(
2329 }
2330
2331 removeCachedPropertyNames( xResAccess->getURL() );
2332 uno::Reference< io::XInputStream > xResult
2333 = xResAccess->POST( rArg.MediaType,
2334 rArg.Referer,
2335 rArg.Source,
2336 xEnv );
2337
2338 {
2339 osl::MutexGuard aGuard( m_aMutex );
2340 m_xResAccess.reset(
2341 new DAVResourceAccess( *xResAccess ) );
2342 }
2343
2344 xSink->setInputStream( xResult );
2345 }
2346 catch ( DAVException const & e )
2347 {
2348 cancelCommandExecution( e, xEnv, true );
2349 // Unreachable
2350 }
2351 }
2352 else
2353 {
2354 uno::Reference< io::XOutputStream > xResult( rArg.Sink, uno::UNO_QUERY );
2355 if ( xResult.is() )
2356 {
2357 try
2358 {
2359 std::unique_ptr< DAVResourceAccess > xResAccess;
2360 {
2361 osl::MutexGuard aGuard( m_aMutex );
2362 xResAccess.reset(
2364 }
2365
2366 removeCachedPropertyNames( xResAccess->getURL() );
2367 xResAccess->POST( rArg.MediaType,
2368 rArg.Referer,
2369 rArg.Source,
2370 xResult,
2371 xEnv );
2372
2373 {
2374 osl::MutexGuard aGuard( m_aMutex );
2375 m_xResAccess.reset(
2376 new DAVResourceAccess( *xResAccess ) );
2377 }
2378 }
2379 catch ( DAVException const & e )
2380 {
2381 cancelCommandExecution( e, xEnv, true );
2382 // Unreachable
2383 }
2384 }
2385 else
2386 {
2388 uno::Any(
2389 ucb::UnsupportedDataSinkException(
2390 OUString(),
2391 getXWeak(),
2392 rArg.Sink ) ),
2393 xEnv );
2394 // Unreachable
2395 }
2396 }
2397}
2398
2399
2401{
2402 // Obtain a list with a snapshot of all currently instantiated contents
2403 // from provider and extract the contents which are direct children
2404 // of this content.
2405
2406 ::ucbhelper::ContentRefList aAllContents;
2407 m_xProvider->queryExistingContents( aAllContents );
2408
2409 OUString aURL = m_xIdentifier->getContentIdentifier();
2410 sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
2411
2412 if ( nURLPos != ( aURL.getLength() - 1 ) )
2413 {
2414 // No trailing slash found. Append.
2415 aURL += "/";
2416 }
2417
2418 sal_Int32 nLen = aURL.getLength();
2419
2420 for ( const auto& rChild : aAllContents )
2421 {
2422 ::ucbhelper::ContentImplHelperRef xChild = rChild;
2423 OUString aChildURL
2424 = xChild->getIdentifier()->getContentIdentifier();
2425
2426 // Is aURL a prefix of aChildURL?
2427 if ( ( aChildURL.getLength() > nLen ) &&
2428 ( aChildURL.startsWith( aURL ) ) )
2429 {
2430 sal_Int32 nPos = nLen;
2431 nPos = aChildURL.indexOf( '/', nPos );
2432
2433 if ( ( nPos == -1 ) ||
2434 ( nPos == ( aChildURL.getLength() - 1 ) ) )
2435 {
2436 // No further slashes / only a final slash. It's a child!
2437 rChildren.push_back(
2439 static_cast< ::http_dav_ucp::Content * >(
2440 xChild.get() ) ) );
2441 }
2442 }
2443 }
2444}
2445
2446
2447void Content::insert(
2448 const uno::Reference< io::XInputStream > & xInputStream,
2449 bool bReplaceExisting,
2450 const uno::Reference< ucb::XCommandEnvironment >& Environment )
2451{
2452 bool bTransient, bCollection;
2453 OUString aEscapedTitle;
2454 std::unique_ptr< DAVResourceAccess > xResAccess;
2455
2456 {
2457 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2458
2459 bTransient = m_bTransient;
2460 bCollection = m_bCollection;
2461 aEscapedTitle = m_aEscapedTitle;
2462 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
2463 }
2464
2465 // Check, if all required properties are present.
2466
2467 if ( aEscapedTitle.isEmpty() )
2468 {
2469 SAL_WARN( "ucb.ucp.webdav", "Content::insert - Title missing!" );
2470
2471 uno::Sequence<OUString> aProps { "Title" };
2473 uno::Any( ucb::MissingPropertiesException(
2474 OUString(),
2475 getXWeak(),
2476 aProps ) ),
2477 Environment );
2478 // Unreachable
2479 }
2480
2481 if ( !bReplaceExisting )
2482 {
2483 /* [RFC 2616] - HTTP
2484
2485 The PUT method requests that the enclosed entity be stored under the
2486 supplied Request-URI. If the Request-URI refers to an already
2487 existing resource, the enclosed entity SHOULD be considered as a
2488 modified version of the one residing on the origin server.
2489 */
2490
2491 /* [RFC 2518] - WebDAV
2492
2493 MKCOL creates a new collection resource at the location specified by
2494 the Request-URI. If the resource identified by the Request-URI is
2495 non-null then the MKCOL MUST fail.
2496 */
2497
2498 // ==> Complain on PUT, continue on MKCOL.
2499 if ( !bTransient || !bCollection )
2500 {
2501#undef ERROR
2502 ucb::UnsupportedNameClashException aEx(
2503 "Unable to write without overwrite!",
2504 getXWeak(),
2505 ucb::NameClash::ERROR );
2506
2507 uno::Reference< task::XInteractionHandler > xIH;
2508
2509 if ( Environment.is() )
2510 xIH = Environment->getInteractionHandler();
2511
2512 if ( xIH.is() )
2513 {
2514 uno::Any aExAsAny( aEx );
2515
2518 aExAsAny,
2519 ContinuationFlags::Approve
2520 | ContinuationFlags::Disapprove );
2521 xIH->handle( xRequest );
2522
2523 const ContinuationFlags nResp = xRequest->getResponse();
2524
2525 switch ( nResp )
2526 {
2527 case ContinuationFlags::NONE:
2528 // Not handled; throw.
2529 throw aEx;
2530// break;
2531
2532 case ContinuationFlags::Approve:
2533 // Continue -> Overwrite.
2534 bReplaceExisting = true;
2535 break;
2536
2537 case ContinuationFlags::Disapprove:
2538 // Abort.
2539 throw ucb::CommandFailedException(
2540 OUString(),
2541 uno::Reference< uno::XInterface >(),
2542 aExAsAny );
2543// break;
2544
2545 default:
2546 SAL_WARN( "ucb.ucp.webdav",
2547 "Content::insert - "
2548 "Unknown interaction selection!" );
2549 throw ucb::CommandFailedException(
2550 "Unknown interaction selection!",
2551 uno::Reference< uno::XInterface >(),
2552 aExAsAny );
2553// break;
2554 }
2555 }
2556 else
2557 {
2558 // No IH; throw.
2559 throw aEx;
2560 }
2561 }
2562 }
2563
2564 if ( bTransient )
2565 {
2566 // Assemble new content identifier...
2567 OUString aURL = getParentURL();
2568 if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
2569 aURL += "/";
2570
2571 aURL += aEscapedTitle;
2572
2573 try
2574 {
2575 xResAccess->setURL( aURL );
2576
2577 if ( bCollection )
2578 {
2579 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
2580 removeCachedPropertyNames( xResAccess->getURL() );
2581 xResAccess->MKCOL( Environment );
2582 }
2583 else
2584 {
2585 // remove options from cache, PUT may change it
2586 // it will be refreshed when needed
2587 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
2588 removeCachedPropertyNames( xResAccess->getURL() );
2589 xResAccess->PUT( xInputStream, Environment );
2590 // clean cached value of PROPFIND properties names
2591 }
2592 // no error , set the resourcetype to unknown type
2593 // the resource may have transitioned from NOT FOUND or UNKNOWN to something else
2594 // depending on the server behaviour
2595 // this will force a recheck of the resource type
2598 }
2599 catch ( DAVException const & except )
2600 {
2601 if ( bCollection )
2602 {
2603 if ( except.getStatus() == SC_METHOD_NOT_ALLOWED )
2604 {
2605 // [RFC 2518] - WebDAV
2606 // 405 (Method Not Allowed) - MKCOL can only be
2607 // executed on a deleted/non-existent resource.
2608
2609 if ( bReplaceExisting )
2610 {
2611 // Destroy old resource.
2612 try
2613 {
2614 removeCachedPropertyNames( xResAccess->getURL() );
2615 xResAccess->DESTROY( Environment );
2616 }
2617 catch ( DAVException const & e )
2618 {
2619 cancelCommandExecution( e, Environment, true );
2620 // Unreachable
2621 }
2622
2623 // Insert (recursion!).
2624 insert( xInputStream, bReplaceExisting, Environment );
2625
2626 {
2627 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2628 m_xResAccess.reset(
2629 new DAVResourceAccess( *xResAccess ) );
2630 }
2631
2632 // Success!
2633 return;
2634 }
2635 else
2636 {
2637 OUString aTitle;
2638 try
2639 {
2640 CurlUri const aURI( aURL );
2641 aTitle = aURI.GetPathBaseNameUnescaped();
2642 }
2643 catch ( DAVException const & )
2644 {
2645 }
2646
2648 uno::Any(
2649 ucb::NameClashException(
2650 OUString(),
2651 getXWeak(),
2652 task::InteractionClassification_ERROR,
2653 aTitle ) ),
2654 Environment );
2655 // Unreachable
2656 }
2657 }
2658 }
2659
2660 cancelCommandExecution( except, Environment, true );
2661 // Unreachable
2662 }
2663
2664 {
2665 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2667 = new ::ucbhelper::ContentIdentifier( aURL );
2668 }
2669
2670 inserted();
2671
2672 {
2673 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2674 m_bTransient = false;
2675 }
2676 }
2677 else
2678 {
2679 if ( !xInputStream.is() )
2680 {
2682 uno::Any(
2683 ucb::MissingInputStreamException(
2684 OUString(),
2685 getXWeak() ) ),
2686 Environment );
2687 // Unreachable
2688 }
2689
2690 // save the URL since it may change due to redirection
2691 OUString aTargetUrl = xResAccess->getURL();
2692 try
2693 {
2694 removeCachedPropertyNames( xResAccess->getURL() );
2695 // remove options from cache, PUT may change it
2696 // it will be refreshed when needed
2698 xResAccess->PUT( xInputStream, Environment );
2699 }
2700 catch ( DAVException const & e )
2701 {
2702 cancelCommandExecution( e, Environment, true );
2703 // Unreachable
2704 }
2705 }
2706
2707 {
2708 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2709 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2710 }
2711}
2712
2713
2715 const ucb::TransferInfo & rArgs,
2716 const uno::Reference< ucb::XCommandEnvironment >& Environment )
2717{
2718 uno::Reference< uno::XComponentContext > xContext;
2719 uno::Reference< ucb::XContentIdentifier > xIdentifier;
2720 uno::Reference< ucb::XContentProvider > xProvider;
2721 std::unique_ptr< DAVResourceAccess > xResAccess;
2722
2723 {
2724 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2725
2726 xContext.set( m_xContext );
2727 xIdentifier.set( m_xIdentifier );
2728 xProvider.set( m_xProvider );
2729 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
2730 }
2731
2732 OUString aTargetURI;
2733 try
2734 {
2735 CurlUri sourceURI( rArgs.SourceURL );
2736 CurlUri targetURI( xIdentifier->getContentIdentifier() );
2737
2738 aTargetURI = targetURI.GetPathBaseNameUnescaped();
2739
2740 // Check source's and target's URL scheme
2741
2742 OUString aScheme = sourceURI.GetScheme().toAsciiLowerCase();
2743 if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
2744 {
2745 sourceURI.SetScheme( HTTP_URL_SCHEME );
2746 }
2747 else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
2748 {
2749 sourceURI.SetScheme( HTTPS_URL_SCHEME );
2750 }
2751 else if ( aScheme == DAV_URL_SCHEME )
2752 {
2753 sourceURI.SetScheme( HTTP_URL_SCHEME );
2754 }
2755 else if ( aScheme == DAVS_URL_SCHEME )
2756 {
2757 sourceURI.SetScheme( HTTPS_URL_SCHEME );
2758 }
2759 else if (aScheme == WEBDAV_URL_SCHEME)
2760 {
2761 sourceURI.SetScheme(HTTP_URL_SCHEME);
2762 }
2763 else if (aScheme == WEBDAVS_URL_SCHEME)
2764 {
2765 sourceURI.SetScheme(HTTPS_URL_SCHEME);
2766 }
2767 else
2768 {
2769 if ( aScheme != HTTP_URL_SCHEME && aScheme != HTTPS_URL_SCHEME )
2770 {
2772 uno::Any(
2773 ucb::InteractiveBadTransferURLException(
2774 "Unsupported URL scheme!",
2775 getXWeak() ) ),
2776 Environment );
2777 // Unreachable
2778 }
2779 }
2780
2781 aScheme = targetURI.GetScheme().toAsciiLowerCase();
2782 if ( aScheme == VNDSUNSTARWEBDAV_URL_SCHEME)
2783 targetURI.SetScheme( HTTP_URL_SCHEME );
2784 else if ( aScheme == VNDSUNSTARWEBDAVS_URL_SCHEME)
2785 targetURI.SetScheme( HTTPS_URL_SCHEME );
2786 else if ( aScheme == DAV_URL_SCHEME )
2787 targetURI.SetScheme( HTTP_URL_SCHEME );
2788 else if ( aScheme == DAVS_URL_SCHEME )
2789 targetURI.SetScheme( HTTPS_URL_SCHEME );
2790 else if (aScheme == WEBDAV_URL_SCHEME)
2791 targetURI.SetScheme(HTTP_URL_SCHEME);
2792 else if (aScheme == WEBDAVS_URL_SCHEME)
2793 targetURI.SetScheme(HTTPS_URL_SCHEME);
2794
2795 // @@@ This implementation of 'transfer' only works
2796 // if the source and target are located at same host.
2797 // (Neon does not support cross-server copy/move)
2798
2799 // Check for same host
2800
2801 if ( sourceURI.GetHost().getLength() &&
2802 ( sourceURI.GetHost() != targetURI.GetHost() ) )
2803 {
2805 uno::Any( ucb::InteractiveBadTransferURLException(
2806 "Different hosts!",
2807 getXWeak() ) ),
2808 Environment );
2809 // Unreachable
2810 }
2811
2812 OUString aTitle = rArgs.NewTitle;
2813
2814 if ( aTitle.isEmpty() )
2815 aTitle = sourceURI.GetPathBaseNameUnescaped();
2816
2817 if ( aTitle == "/" )
2818 {
2819 // kso: ???
2820 aTitle.clear();
2821 }
2822
2823 targetURI.AppendPath( aTitle );
2824
2825 OUString aTargetURL = xIdentifier->getContentIdentifier();
2826 if ( ( aTargetURL.lastIndexOf( '/' ) + 1 )
2827 != aTargetURL.getLength() )
2828 aTargetURL += "/";
2829
2830 aTargetURL += aTitle;
2831
2832 uno::Reference< ucb::XContentIdentifier > xTargetId
2833 = new ::ucbhelper::ContentIdentifier( aTargetURL );
2834
2835 DAVResourceAccess aSourceAccess( xContext,
2836 xResAccess->getSessionFactory(),
2837 sourceURI.GetURI() );
2838
2839 if ( rArgs.MoveData )
2840 {
2841 uno::Reference< ucb::XContentIdentifier > xId
2842 = new ::ucbhelper::ContentIdentifier( rArgs.SourceURL );
2843
2844 // Note: The static cast is okay here, because its sure that
2845 // xProvider is always the WebDAVContentProvider.
2847 = static_cast< Content * >(
2848 xProvider->queryContent( xId ).get() );
2849
2850 // [RFC 2518] - WebDAV
2851 // If a resource exists at the destination and the Overwrite
2852 // header is "T" then prior to performing the move the server
2853 // MUST perform a DELETE with "Depth: infinity" on the
2854 // destination resource. If the Overwrite header is set to
2855 // "F" then the operation will fail.
2856
2857 aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
2858 aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
2859 aSourceAccess.MOVE( sourceURI.GetRelativeReference(),
2860 targetURI.GetURI(),
2861 rArgs.NameClash
2862 == ucb::NameClash::OVERWRITE,
2863 Environment );
2864
2865 if ( xSource.is() )
2866 {
2867 // Propagate destruction to listeners.
2868 xSource->destroy( true );
2869 }
2870
2871// DAV resources store all additional props on server!
2872// // Rename own and all children's Additional Core Properties.
2873// renameAdditionalPropertySet( xId->getContentIdentifier(),
2874// xTargetId->getContentIdentifier(),
2875// true );
2876 }
2877 else
2878 {
2879 // [RFC 2518] - WebDAV
2880 // If a resource exists at the destination and the Overwrite
2881 // header is "T" then prior to performing the copy the server
2882 // MUST perform a DELETE with "Depth: infinity" on the
2883 // destination resource. If the Overwrite header is set to
2884 // "F" then the operation will fail.
2885
2886 aStaticDAVOptionsCache.removeDAVOptions( sourceURI.GetURI() );
2887 aStaticDAVOptionsCache.removeDAVOptions( targetURI.GetURI() );
2888 aSourceAccess.COPY( sourceURI.GetRelativeReference(),
2889 targetURI.GetURI(),
2890 rArgs.NameClash
2891 == ucb::NameClash::OVERWRITE,
2892 Environment );
2893
2894// DAV resources store all additional props on server!
2895// // Copy own and all children's Additional Core Properties.
2896// copyAdditionalPropertySet( xId->getContentIdentifier(),
2897// xTargetId->getContentIdentifier(),
2898// true );
2899 }
2900
2901 // Note: The static cast is okay here, because its sure that
2902 // xProvider is always the WebDAVContentProvider.
2904 = static_cast< Content * >(
2905 xProvider->queryContent( xTargetId ).get() );
2906
2907 // Announce transferred content in its new folder.
2908 xTarget->inserted();
2909 }
2910 catch ( ucb::IllegalIdentifierException const & )
2911 {
2912 // queryContent
2913 }
2914 catch ( DAVException const & e )
2915 {
2916 // [RFC 2518] - WebDAV
2917 // 412 (Precondition Failed) - The server was unable to maintain
2918 // the liveness of the properties listed in the propertybehavior
2919 // XML element or the Overwrite header is "F" and the state of
2920 // the destination resource is non-null.
2921
2922 if ( e.getStatus() == SC_PRECONDITION_FAILED )
2923 {
2924 switch ( rArgs.NameClash )
2925 {
2926 case 0/*ucb::NameClash::ERROR*/:
2927 {
2929 uno::Any(
2930 ucb::NameClashException(
2931 OUString(),
2932 getXWeak(),
2933 task::InteractionClassification_ERROR,
2934 aTargetURI ) ),
2935 Environment );
2936 // Unreachable
2937 }
2938 [[fallthrough]];
2939
2940 case ucb::NameClash::OVERWRITE:
2941 break;
2942
2943 case ucb::NameClash::KEEP: // deprecated
2944 case ucb::NameClash::RENAME:
2945 case ucb::NameClash::ASK:
2946 default:
2947 {
2949 uno::Any(
2950 ucb::UnsupportedNameClashException(
2951 OUString(),
2952 getXWeak(),
2953 rArgs.NameClash ) ),
2954 Environment );
2955 // Unreachable
2956 }
2957 }
2958 }
2959
2960 cancelCommandExecution( e, Environment, true );
2961 // Unreachable
2962 }
2963
2964 {
2965 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2966 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
2967 }
2968}
2969
2970
2971void Content::destroy( bool bDeletePhysical )
2972{
2973 // @@@ take care about bDeletePhysical -> trashcan support
2974
2975 uno::Reference< ucb::XContent > xThis = this;
2976
2977 deleted();
2978
2979 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2980
2981 // Process instantiated children...
2982
2984 queryChildren( aChildren );
2985
2986 for ( auto& rChild : aChildren )
2987 {
2988 rChild->destroy( bDeletePhysical );
2989 }
2990}
2991
2992// returns the resource type, to be checked for locks
2994 const uno::Reference< ucb::XCommandEnvironment >& Environment,
2995 const std::unique_ptr< DAVResourceAccess > & rResAccess)
2996{
2997 ResourceType eResourceTypeForLocks = UNKNOWN;
2998 {
2999 osl::MutexGuard g(m_aMutex);
3000 //check if cache contains what we need, usually the first PROPFIND on the URI has supported lock
3001 if (m_xCachedProps)
3002 {
3003 uno::Sequence< ucb::LockEntry > aSupportedLocks;
3005 >>= aSupportedLocks ) //get the cached value for supportedlock
3006 {
3007 for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
3008 {
3009 if ( aSupportedLocks[ n ].Scope
3010 == ucb::LockScope_EXCLUSIVE &&
3011 aSupportedLocks[ n ].Type
3012 == ucb::LockType_WRITE )
3013 eResourceTypeForLocks = DAV;
3014 }
3015 }
3016 }
3017 }
3018
3019 const OUString & rURL = m_xIdentifier->getContentIdentifier();
3020
3021 if ( eResourceTypeForLocks == UNKNOWN )
3022 {
3023 // resource type for lock/unlock operations still unknown, need to ask the server
3024
3025 //{
3026 DAVOptions aDAVOptions;
3027 getResourceOptions( Environment, aDAVOptions, rResAccess );
3028 if( aDAVOptions.isClass1() ||
3029 aDAVOptions.isClass2() ||
3030 aDAVOptions.isClass3() )
3031 {
3032 // this is at least a DAV, lock to be confirmed
3033 // class 2 is needed for full lock support
3034 // see
3035 // <https://tools.ietf.org/html/rfc4918#section-18.2>
3036 eResourceTypeForLocks = DAV_NOLOCK;
3037 if( aDAVOptions.isClass2() )
3038 {
3039 // ok, possible lock, check for it
3040 try
3041 {
3042 // we need only DAV:supportedlock
3043 std::vector< DAVResource > resources;
3044 std::vector< OUString > aPropNames;
3045 uno::Sequence< beans::Property > aProperties( 1 );
3046 aProperties.getArray()[ 0 ].Name = DAVProperties::SUPPORTEDLOCK;
3047
3048 ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
3049 rResAccess->PROPFIND( DAVZERO, aPropNames, resources, Environment );
3050
3051 bool wasSupportedlockFound = false;
3052
3053 // only one resource should be returned
3054 if ( resources.size() == 1 )
3055 {
3056 // we may have received a bunch of other properties
3057 // (some servers seems to do so)
3058 // but we need only supported lock for this check
3059 // all returned properties are in
3060 // resources.properties[n].Name/.Value
3061
3062 std::vector< DAVPropertyValue >::iterator it;
3063
3064 for ( it = resources[0].properties.begin();
3065 it != resources[0].properties.end(); ++it)
3066 {
3067 if ( (*it).Name == DAVProperties::SUPPORTEDLOCK )
3068 {
3069 wasSupportedlockFound = true;
3070 uno::Sequence< ucb::LockEntry > aSupportedLocks;
3071 if ( (*it).Value >>= aSupportedLocks )
3072 {
3073 for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
3074 {
3075 // TODO: if the lock type is changed from 'exclusive write' to 'shared write'
3076 // e.g. to implement 'Calc shared file feature', the ucb::LockScope_EXCLUSIVE
3077 // value should be checked as well, adaptation the code may be needed
3078 if ( aSupportedLocks[ n ].Scope == ucb::LockScope_EXCLUSIVE &&
3079 aSupportedLocks[ n ].Type == ucb::LockType_WRITE )
3080 {
3081 // requested locking mode is supported
3082 eResourceTypeForLocks = DAV;
3083 SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks - URL: <"
3084 << m_xIdentifier->getContentIdentifier() << ">, DAV lock/unlock supported");
3085 break;
3086 }
3087 }
3088 break;
3089 }
3090 }
3091 }
3092 }
3093 else
3094 {
3095 // PROPFIND failed; check if HEAD contains Content-Disposition: attachment (RFC1806, HTTP/1.1 19.5.1),
3096 // which supposedly means no lock for the resource (happens e.g. with SharePoint exported lists)
3097 OUString sContentDisposition;
3098 // First, check cached properties
3099 if (m_xCachedProps)
3100 {
3101 if ((m_xCachedProps->getValue("Content-Disposition") >>= sContentDisposition)
3102 && sContentDisposition.startsWithIgnoreAsciiCase("attachment"))
3103 {
3104 eResourceTypeForLocks = DAV_NOLOCK;
3105 wasSupportedlockFound = true;
3106 }
3107 }
3108 // If no data in cache, try HEAD request
3109 if (sContentDisposition.isEmpty() && !m_bDidGetOrHead) try
3110 {
3111 DAVResource resource;
3112 GetPropsUsingHeadRequest(resource, rResAccess, {"Content-Disposition"}, Environment);
3113 m_bDidGetOrHead = true;
3114 for (const auto& it : resource.properties)
3115 {
3116 if (it.Name.equalsIgnoreAsciiCase("Content-Disposition"))
3117 {
3118 if ((it.Value >>= sContentDisposition) && sContentDisposition.equalsIgnoreAsciiCase("attachment"))
3119 {
3120 eResourceTypeForLocks = DAV_NOLOCK;
3121 wasSupportedlockFound = true;
3122 }
3123 break;
3124 }
3125 }
3126 }
3127 catch (...){}
3128 }
3129 // check if this is still only a DAV_NOLOCK
3130 // a fallback for resources that do not have DAVProperties::SUPPORTEDLOCK property
3131 // we check for the returned OPTION if LOCK is allowed on the resource
3132 if ( !wasSupportedlockFound && eResourceTypeForLocks == DAV_NOLOCK )
3133 {
3134 SAL_INFO( "ucb.ucp.webdav", "This WebDAV server has no supportedlock property, check for allowed LOCK method in OPTIONS" );
3135 // ATTENTION: if the lock type is changed from 'exclusive write' to 'shared write'
3136 // e.g. to implement 'Calc shared file feature' on WebDAV directly, and we arrive to this fallback
3137 // and the LOCK is allowed, we should assume that only exclusive write lock is available
3138 // this is just a reminder...
3139 if ( aDAVOptions.isLockAllowed() )
3140 eResourceTypeForLocks = DAV;
3141 }
3142 }
3143 catch ( DAVException const & e )
3144 {
3145 rResAccess->resetUri();
3146 //grab the error code
3147 switch( e.getStatus() )
3148 {
3149 case SC_NOT_FOUND:
3150 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3151 << m_xIdentifier->getContentIdentifier() << "> was not found. ");
3152 eResourceTypeForLocks = NOT_FOUND;
3153 break;
3154 // some servers returns SC_FORBIDDEN, instead
3155 // the meaning of SC_FORBIDDEN is, according to <http://tools.ietf.org/html/rfc7231#section-6.5.3>:
3156 // The 403 (Forbidden) status code indicates that the server understood
3157 // the request but refuses to authorize it
3158 case SC_FORBIDDEN:
3159 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3160 // part of base http 1.1 RFCs
3161 case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3162 case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3163 // they all mean the resource is NON_DAV
3164 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException (SC_FORBIDDEN, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3165 << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3166 eResourceTypeForLocks = NON_DAV;
3167 break;
3168 default:
3169 //fallthrough
3170 SAL_WARN( "ucb.ucp.webdav", "resourceTypeForLocks() DAVException - URL: <"
3171 << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3172 eResourceTypeForLocks = UNKNOWN;
3173 }
3174 }
3175 }
3176 }
3177 else
3178 eResourceTypeForLocks = NON_DAV;
3179
3180 //}
3181 }
3182 osl::MutexGuard g(m_aMutex);
3184 {
3185 m_eResourceTypeForLocks = eResourceTypeForLocks;
3186 }
3187 else
3188 {
3190 eResourceTypeForLocks != m_eResourceTypeForLocks, "ucb.ucp.webdav",
3191 "different resource types for <" << rURL << ">: "
3192 << +eResourceTypeForLocks << " vs. " << +m_eResourceTypeForLocks);
3193 }
3194 SAL_INFO( "ucb.ucp.webdav", "resourceTypeForLocks() - URL: <"
3195 << m_xIdentifier->getContentIdentifier() << ">, m_eResourceTypeForLocks: " << m_eResourceTypeForLocks );
3197}
3198
3200 const uno::Reference< ucb::XCommandEnvironment >& Environment )
3201{
3202 std::unique_ptr< DAVResourceAccess > xResAccess;
3203 {
3204 osl::MutexGuard aGuard( m_aMutex );
3205 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
3206 }
3207 Content::ResourceType ret = resourceTypeForLocks( Environment, xResAccess );
3208 {
3209 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3210 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
3211 }
3212 return ret;
3213}
3214
3215void Content::lock(
3216 const uno::Reference< ucb::XCommandEnvironment >& Environment )
3217{
3218// prepare aURL to be used in exception, see below
3219 OUString aURL;
3220 if ( m_bTransient )
3221 {
3222 aURL = getParentURL();
3223 if ( aURL.lastIndexOf('/') != ( aURL.getLength() - 1 ) )
3224 aURL += "/";
3225
3227 }
3228 else
3229 {
3230 aURL = m_xIdentifier->getContentIdentifier();
3231 }
3232
3233 try
3234 {
3235 std::unique_ptr< DAVResourceAccess > xResAccess;
3236 {
3237 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3238 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
3239 }
3240
3241 uno::Any aOwnerAny;
3242 OUString const user(officecfg::Office::Common::Save::Document::UseUserData::get()
3244 : OUString());
3245 aOwnerAny <<= OUString("LibreOffice" + user);
3246
3247 ucb::Lock aLock(
3248 ucb::LockScope_EXCLUSIVE,
3249 ucb::LockType_WRITE,
3250 ucb::LockDepth_ZERO,
3251 aOwnerAny,
3252 180, // lock timeout in secs
3253 //-1, // infinite lock
3254 uno::Sequence< OUString >() );
3255
3256 // OPTIONS may change as a consequence of the lock operation
3257 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
3258 removeCachedPropertyNames( xResAccess->getURL() );
3259 xResAccess->LOCK( aLock, Environment );
3260
3261 {
3262 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3263 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
3264 }
3265 }
3266 catch ( DAVException const & e )
3267 {
3268 // check if the exception thrown is 'already locked'
3269 // this exception is mapped directly to the ucb correct one, without
3270 // going into the cancelCommandExecution() user interaction
3271 // this exception should be managed by the issuer of 'lock' command
3272 switch( e.getError() )
3273 {
3275 {
3276 SAL_WARN( "ucb.ucp.webdav", "lock(): resource already locked - URL: <"
3277 << m_xIdentifier->getContentIdentifier() << ">");
3278 throw
3279 ucb::InteractiveLockingLockedException(
3280 "Locked!",
3281 getXWeak(),
3282 task::InteractionClassification_ERROR,
3283 aURL,
3284 false );
3285 }
3286 break;
3289 {
3290 SAL_WARN( "ucb.ucp.webdav", "lock(): DAVException Authentication error - URL: <"
3291 << m_xIdentifier->getContentIdentifier() << ">" );
3292 // DAVException::DAV_HTTP_AUTH exception can mean:
3293 // - interaction handler for credential management not present (happens, depending
3294 // on the LO framework processing)
3295 // - the remote site is a WebDAV with special configuration: read/only for read operations
3296 // and read/write for write operations, the user is not allowed to lock/write and
3297 // she cancelled the credentials request.
3298 // this is not actually an error, but the exception is sent directly from here, avoiding the automatic
3299 // management that takes part in cancelCommandExecution() below
3300 // Unfortunately there is no InteractiveNetwork*Exception available to signal this
3301 // since it mostly happens on read/only part of webdav, this appears to be the most correct exception available
3302 throw
3303 ucb::InteractiveNetworkWriteException(
3304 "Authentication error while trying to lock! Write only WebDAV perhaps?",
3305 getXWeak(),
3306 task::InteractionClassification_ERROR,
3307 e.getData() );
3308 }
3309 break;
3311 //grab the error code
3312 switch( e.getStatus() )
3313 {
3314 // The 'case SC_NOT_FOUND' just below tries to solve a problem in eXo Platform
3315 // WebDAV connector which apparently fail on resource first creation
3316 // rfc4918 section-7.3 (see link below)
3317 case SC_NOT_FOUND: // <http://tools.ietf.org/html/rfc7231#section-6.5.4>
3318 // The 'case SC_PRECONDITION_FAILED' just below tries to solve a problem
3319 // in SharePoint when locking the resource on first creation fails due to this:
3320 // <https://msdn.microsoft.com/en-us/library/jj575265%28v=office.12%29.aspx#id15>
3321 // (retrieved on 2015-08-14)
3322 case SC_PRECONDITION_FAILED: // <http://tools.ietf.org/html/rfc7232#section-4.2>
3323 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3324 // part of base http 1.1 RFCs
3325 case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3326 case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3327 SAL_WARN( "ucb.ucp.webdav", "lock() DAVException (SC_NOT_FOUND, SC_PRECONDITION_FAILED, SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3328 << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3329 // act as nothing happened
3330 // that's because when a resource is first created
3331 // the lock is sent before the put, so the resource
3332 // is actually created by LOCK, locking it before
3333 // the first PUT, but if LOCK is not supported
3334 // (simple web or DAV with lock disabled) we end with one of these http
3335 // errors.
3336 // These same errors may be reported when the LOCK on an unmapped
3337 // (i.e. non existent) resource is not implemented.
3338 // Detailed specification in:
3339 // <http://tools.ietf.org/html/rfc4918#section-7.3>
3340 return;
3341 default:
3342 //fallthrough
3343 ;
3344 }
3345 break;
3347 // we already hold the lock and it is in our internal lockstore
3348 // just return as if the lock was successful
3349 return;
3350 default:
3351 //fallthrough
3352 ;
3353 }
3354
3355 SAL_WARN( "ucb.ucp.webdav","lock() DAVException - URL: <"
3356 << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3357 cancelCommandExecution( e, Environment, false );
3358 // Unreachable
3359 }
3360}
3361
3362
3363void Content::unlock(
3364 const uno::Reference< ucb::XCommandEnvironment >& Environment )
3365{
3366
3367 try
3368 {
3369 std::unique_ptr< DAVResourceAccess > xResAccess;
3370 {
3371 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3372 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
3373 }
3374
3375 // check if the target URL is a Class1 DAV
3376 DAVOptions aDAVOptions;
3377 getResourceOptions( Environment, aDAVOptions, xResAccess );
3378
3379 // at least class one is needed
3380 if( aDAVOptions.isClass1() )
3381 {
3382 // remove options from cache, unlock may change it
3383 // it will be refreshed when needed
3384 aStaticDAVOptionsCache.removeDAVOptions( xResAccess->getURL() );
3385 // clean cached value of PROPFIND properties names
3386 removeCachedPropertyNames( xResAccess->getURL() );
3387 xResAccess->UNLOCK( Environment );
3388 }
3389
3390 {
3391 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3392 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
3393 }
3394 }
3395 catch ( DAVException const & e )
3396 {
3397 switch( e.getError() )
3398 {
3400 SAL_WARN( "ucb.ucp.webdav", "unlock(): DAVException::DAV_NOT_LOCKED - URL: <"
3401 << m_xIdentifier->getContentIdentifier() << ">");
3402 // means that we don't own any lock on this resource
3403 // intercepted here to remove a confusing indication to the user
3404 // unfortunately this happens in some WebDAV server configuration
3405 // acting as WebDAV and having lock/unlock enabled only
3406 // for authorized user.
3407 return;
3409 //grab the error code
3410 switch( e.getStatus() )
3411 {
3412 // Errors SC_NOT_IMPLEMENTED and SC_METHOD_NOT_ALLOWED are
3413 // part of base http 1.1 RFCs
3414 case SC_NOT_IMPLEMENTED: // <http://tools.ietf.org/html/rfc7231#section-6.6.2>
3415 case SC_METHOD_NOT_ALLOWED: // <http://tools.ietf.org/html/rfc7231#section-6.5.5>
3416 SAL_WARN( "ucb.ucp.webdav", "unlock() DAVException (SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED) - URL: <"
3417 << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3418 return;
3419 default:
3420 //fallthrough
3421 ;
3422 }
3423 break;
3424 default:
3425 //fallthrough
3426 ;
3427 }
3428 SAL_WARN( "ucb.ucp.webdav","unlock() DAVException - URL: <"
3429 << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3430 cancelCommandExecution( e, Environment, false );
3431 // Unreachable
3432 }
3433}
3434
3435
3437 const uno::Reference< ucb::XContentIdentifier >& xNewId )
3438{
3439 if ( !xNewId.is() )
3440 return false;
3441
3442 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
3443
3444 uno::Reference< ucb::XContent > xThis = this;
3445
3446 // Already persistent?
3447 if ( m_bTransient )
3448 {
3449 SAL_WARN( "ucb.ucp.webdav", "Content::exchangeIdentity - Not persistent!" );
3450 return false;
3451 }
3452
3453 // Exchange own identity.
3454
3455 // Fail, if a content with given id already exists.
3456// if ( !hasData( xNewId ) )
3457 {
3458 OUString aOldURL = m_xIdentifier->getContentIdentifier();
3459
3460 aGuard.clear();
3461 if ( exchange( xNewId ) )
3462 {
3463 // Process instantiated children...
3464
3465 ContentRefList aChildren;
3466 queryChildren( aChildren );
3467
3468 for ( const auto& rChild : aChildren )
3469 {
3470 ContentRef xChild = rChild;
3471
3472 // Create new content identifier for the child...
3473 uno::Reference< ucb::XContentIdentifier >
3474 xOldChildId = xChild->getIdentifier();
3475 OUString aOldChildURL
3476 = xOldChildId->getContentIdentifier();
3477 OUString aNewChildURL
3478 = aOldChildURL.replaceAt(
3479 0,
3480 aOldURL.getLength(),
3481 xNewId->getContentIdentifier() );
3482 uno::Reference< ucb::XContentIdentifier > xNewChildId
3483 = new ::ucbhelper::ContentIdentifier( aNewChildURL );
3484
3485 if ( !xChild->exchangeIdentity( xNewChildId ) )
3486 return false;
3487 }
3488 return true;
3489 }
3490 }
3491
3492 SAL_WARN( "ucb.ucp.webdav",
3493 "Content::exchangeIdentity - "
3494 "Panic! Cannot exchange identity!" );
3495 return false;
3496}
3497
3498
3500 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
3501{
3502 {
3503 osl::MutexGuard aGuard( m_aMutex );
3504
3505 if ( m_bTransient )
3506 return m_bCollection;
3507 }
3508
3509 uno::Sequence< beans::Property > aProperties( 1 );
3510 auto pProperties = aProperties.getArray();
3511 pProperties[ 0 ].Name = "IsFolder";
3512 pProperties[ 0 ].Handle = -1;
3513 uno::Reference< sdbc::XRow > xRow( getPropertyValues( aProperties, xEnv ) );
3514 if ( xRow.is() )
3515 {
3516 try
3517 {
3518 return xRow->getBoolean( 1 );
3519 }
3520 catch ( sdbc::SQLException const & )
3521 {
3522 }
3523 }
3524
3525 return false;
3526}
3527
3528
3530{
3531 // Map DAVException...
3532 uno::Any aException;
3533
3534 OUString aURL;
3535 if ( m_bTransient )
3536 {
3537 aURL = getParentURL();
3538 if ( aURL.lastIndexOf( '/' ) != ( aURL.getLength() - 1 ) )
3539 aURL += "/";
3540
3542 }
3543 else
3544 {
3545 aURL = m_xIdentifier->getContentIdentifier();
3546 }
3547
3548 switch ( e.getStatus() )
3549 {
3550 case SC_NOT_FOUND:
3551 {
3552 uno::Sequence<uno::Any> aArgs{ uno::Any(beans::PropertyValue(
3553 "Uri", -1, uno::Any(aURL), beans::PropertyState_DIRECT_VALUE)) };
3554
3555 aException <<=
3556 ucb::InteractiveAugmentedIOException(
3557 "Not found!",
3558 getXWeak(),
3559 task::InteractionClassification_ERROR,
3560 ucb::IOErrorCode_NOT_EXISTING,
3561 aArgs );
3562 return aException;
3563 }
3564 default:
3565 break;
3566 }
3567
3568 switch ( e.getError() )
3569 {
3571 {
3572 if ( bWrite )
3573 aException <<=
3574 ucb::InteractiveNetworkWriteException(
3575 e.getData(),
3576 getXWeak(),
3577 task::InteractionClassification_ERROR,
3578 e.getData() );
3579 else
3580 aException <<=
3581 ucb::InteractiveNetworkReadException(
3582 e.getData(),
3583 getXWeak(),
3584 task::InteractionClassification_ERROR,
3585 e.getData() );
3586 break;
3587 }
3588
3590 aException <<=
3591 ucb::InteractiveNetworkResolveNameException(
3592 OUString(),
3593 getXWeak(),
3594 task::InteractionClassification_ERROR,
3595 e.getData() );
3596 break;
3597
3598// @@@ No matching InteractiveNetwork*Exception
3599// case DAVException::DAV_HTTP_AUTH:
3600// break;
3601
3602// @@@ No matching InteractiveNetwork*Exception
3603// case DAVException::DAV_HTTP_AUTHPROXY:
3604// break;
3605
3608 aException <<=
3609 ucb::InteractiveNetworkConnectException(
3610 OUString(),
3611 getXWeak(),
3612 task::InteractionClassification_ERROR,
3613 e.getData() );
3614 break;
3615
3616// @@@ No matching InteractiveNetwork*Exception
3617// case DAVException::DAV_HTTP_REDIRECT:
3618// break;
3619
3620// @@@ No matching InteractiveNetwork*Exception
3621// case DAVException::DAV_SESSION_CREATE:
3622// break;
3623
3625 aException <<=
3626 lang::IllegalArgumentException(
3627 OUString(),
3628 getXWeak(),
3629 -1 );
3630 break;
3631
3633#if 1
3634 aException <<=
3635 ucb::InteractiveLockingLockedException(
3636 "Locked!",
3637 getXWeak(),
3638 task::InteractionClassification_ERROR,
3639 aURL,
3640 false ); // not SelfOwned
3641#else
3642 {
3643 uno::Sequence< uno::Any > aArgs( 1 );
3644 aArgs[ 0 ] <<= beans::PropertyValue(
3645 OUString("Uri"), -1,
3646 uno::makeAny(aURL),
3647 beans::PropertyState_DIRECT_VALUE);
3648
3649 aException <<=
3650 ucb::InteractiveAugmentedIOException(
3651 OUString( "Locked!" ),
3652 getXWeak(),
3653 task::InteractionClassification_ERROR,
3654 ucb::IOErrorCode_LOCKING_VIOLATION,
3655 aArgs );
3656 }
3657#endif
3658 break;
3659
3661 aException <<=
3662 ucb::InteractiveLockingLockedException(
3663 "Locked (self)!",
3664 getXWeak(),
3665 task::InteractionClassification_ERROR,
3666 aURL,
3667 true ); // SelfOwned
3668 break;
3669
3671 aException <<=
3672 ucb::InteractiveLockingNotLockedException(
3673 "Not locked!",
3674 getXWeak(),
3675 task::InteractionClassification_ERROR,
3676 aURL );
3677 break;
3678
3680 aException <<=
3681 ucb::InteractiveLockingLockExpiredException(
3682 "Lock expired!",
3683 getXWeak(),
3684 task::InteractionClassification_ERROR,
3685 aURL );
3686 break;
3687
3688 default:
3689 aException <<=
3690 ucb::InteractiveNetworkGeneralException(
3691 OUString(),
3692 getXWeak(),
3693 task::InteractionClassification_ERROR );
3694 break;
3695 }
3696
3697 return aException;
3698}
3699
3700
3701// static
3703{
3704 if ( ( e.getStatus() == SC_NOT_FOUND ) ||
3705 ( e.getStatus() == SC_GONE ) ||
3712 return false;
3713
3714 return true;
3715}
3716
3717
3719 const DAVException & e,
3720 const uno::Reference< ucb::XCommandEnvironment > & xEnv,
3721 bool bWrite /* = false */ )
3722{
3724 // Unreachable
3725}
3726
3727
3728OUString
3729Content::getBaseURI( const std::unique_ptr< DAVResourceAccess > & rResAccess )
3730{
3731 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3732
3733 // First, try to obtain value of response header "Content-Location".
3734 if (m_xCachedProps)
3735 {
3736 OUString aLocation;
3737 m_xCachedProps->getValue( "Content-Location" ) >>= aLocation;
3738 if ( aLocation.getLength() )
3739 {
3740 try
3741 {
3742 // Do not use m_xIdentifier->getContentIdentifier() because it
3743 // for example does not reflect redirects applied to requests
3744 // done using the original URI but m_xResAccess' URI does.
3745 return rtl::Uri::convertRelToAbs( rResAccess->getURL(),
3746 aLocation );
3747 }
3748 catch ( rtl::MalformedUriException const & )
3749 {
3750 }
3751 }
3752 }
3753
3754 return rResAccess->getURL();
3755}
3756
3757// resource type is the type of the WebDAV resource
3759 const uno::Reference< ucb::XCommandEnvironment >& xEnv,
3760 const std::unique_ptr< DAVResourceAccess > & rResAccess,
3761 bool * networkAccessAllowed )
3762{
3763 {
3764 osl::MutexGuard g(m_aMutex);
3765 if (m_eResourceType != UNKNOWN) {
3766 return m_eResourceType;
3767 }
3768 }
3769
3770 ResourceType eResourceType = UNKNOWN;
3771 DAVOptions aDAVOptions;
3772
3773 {
3774 getResourceOptions( xEnv, aDAVOptions, rResAccess, networkAccessAllowed );
3775
3776 // at least class one is needed
3777 if( aDAVOptions.isClass1() )
3778 {
3779 try
3780 {
3781 // Try to fetch some frequently used property value, e.g. those
3782 // used when loading documents... along with identifying whether
3783 // this is a DAV resource.
3784 std::vector< DAVResource > resources;
3785 std::vector< OUString > aPropNames;
3786 uno::Sequence< beans::Property > aProperties( 5 );
3787 auto pProperties = aProperties.getArray();
3788 pProperties[ 0 ].Name = "IsFolder";
3789 pProperties[ 1 ].Name = "IsDocument";
3790 pProperties[ 2 ].Name = "IsReadOnly";
3791 pProperties[ 3 ].Name = "MediaType";
3792 pProperties[ 4 ].Name = DAVProperties::SUPPORTEDLOCK;
3793
3794 ContentProperties::UCBNamesToDAVNames( aProperties, aPropNames );
3795
3796 rResAccess->PROPFIND( DAVZERO, aPropNames, resources, xEnv );
3797
3798 if ( resources.size() == 1 )
3799 {
3800#if defined SAL_LOG_INFO
3801 {//debug
3802 // print received resources
3803 std::vector< DAVPropertyValue >::const_iterator it = resources[0].properties.begin();
3804 std::vector< DAVPropertyValue >::const_iterator end = resources[0].properties.end();
3805 while ( it != end )
3806 {
3807 OUString aPropValue;
3808 bool bValue;
3809 uno::Sequence< ucb::LockEntry > aSupportedLocks;
3810 if((*it).Value >>= aPropValue )
3811 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it).Name << ":" << aPropValue );
3812 else if( (*it).Value >>= bValue )
3813 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it).Name << ":" <<
3814 ( bValue ? "true" : "false" ) );
3815 else if( (*it).Value >>= aSupportedLocks )
3816 {
3817 SAL_INFO( "ucb.ucp.webdav", "PROPFIND (getResourceType) - ret'd prop: " << (*it).Name << ":" );
3818 for ( sal_Int32 n = 0; n < aSupportedLocks.getLength(); ++n )
3819 {
3820 SAL_INFO( "ucb.ucp.webdav","PROPFIND (getResourceType) - supportedlock[" << n <<"]: scope: "
3821 << (aSupportedLocks[n].Scope == ucb::LockScope_SHARED ? "shared" : "exclusive")
3822 << ", type: "
3823 << (aSupportedLocks[n].Type != ucb::LockType_WRITE ? "" : "write") );
3824 }
3825 }
3826 ++it;
3827 }
3828 }
3829#endif
3830 osl::MutexGuard g(m_aMutex);
3831 m_xCachedProps.reset(
3832 new CachableContentProperties( ContentProperties( resources[ 0 ] ) ) );
3833 m_xCachedProps->containsAllNames(
3834 aProperties, m_aFailedPropNames );
3835 }
3836 eResourceType = DAV;
3837 }
3838 catch ( DAVException const & e )
3839 {
3840 rResAccess->resetUri();
3841
3842 SAL_WARN( "ucb.ucp.webdav", "Content::getResourceType returned errors, DAV ExceptionCode: " << e.getError() << ", HTTP error: " << e.getStatus() );
3843
3844 if ( e.getStatus() == SC_METHOD_NOT_ALLOWED )
3845 {
3846 // Status SC_METHOD_NOT_ALLOWED is a safe indicator that the
3847 // resource is NON_DAV
3848 eResourceType = NON_DAV;
3849 }
3850 else if (networkAccessAllowed != nullptr)
3851 {
3852 *networkAccessAllowed = *networkAccessAllowed
3854 }
3855 if ( e.getStatus() == SC_NOT_FOUND )
3856 {
3857 // arrives here if OPTIONS is still cached for a resource previously available
3858 // operate on the OPTIONS cache:
3859 // if OPTIONS was not found, do nothing
3860 // else OPTIONS returned on a resource not existent (example a server that allows lock on null resource) set
3861 // not found and adjust lifetime accordingly
3862 DAVOptions aDAVOptionsInner;
3863 if (aStaticDAVOptionsCache.getDAVOptions(rResAccess->getURL(), aDAVOptionsInner))
3864 {
3865 // TODO? get redirected url
3866 aDAVOptionsInner.setHttpResponseStatusCode( e.getStatus() );
3867 aDAVOptionsInner.setHttpResponseStatusText( e.getData() );
3868 aStaticDAVOptionsCache.addDAVOptions( aDAVOptionsInner,
3870 }
3871 }
3872 // if the two net events below happen, something
3873 // is going on to the connection so break the command flow
3874 if ( ( e.getError() == DAVException::DAV_HTTP_TIMEOUT ) ||
3876 {
3877 cancelCommandExecution( e, xEnv );
3878 // unreachable
3879 }
3880
3881 // cancel command execution is case that no user authentication data has been provided.
3883 {
3884 cancelCommandExecution( e, uno::Reference< ucb::XCommandEnvironment >() );
3885 }
3886 }
3887 }
3888 else
3889 {
3890 rResAccess->resetUri();
3891
3892 // first check if the cached error can be mapped to DAVException::DAV_HTTP_TIMEOUT or mapped to DAVException::DAV_HTTP_CONNECT
3894 // can't get any reliable info without auth => cancel request
3895 || aDAVOptions.getHttpResponseStatusCode() == USC_AUTH_FAILED
3897 {
3898 // behave same as DAVException::DAV_HTTP_TIMEOUT or DAVException::DAV_HTTP_CONNECT was thrown
3899 try
3900 {
3901 // extract host name and connection port
3902 CurlUri theUri( rResAccess->getURL() );
3903 OUString aHostName = theUri.GetHost();
3904 sal_Int32 nPort = theUri.GetPort();
3906 switch (aDAVOptions.getHttpResponseStatusCode())
3907 {
3910 break;
3911 case USC_AUTH_FAILED:
3913 break;
3916 break;
3917 default:
3918 assert(false);
3919 }
3920 throw DAVException( e,
3921 ConnectionEndPointString(aHostName, nPort) );
3922 }
3923 catch ( DAVException& exp )
3924 {
3925 cancelCommandExecution( exp, xEnv );
3926 }
3927 }
3928
3929 if ( aDAVOptions.getHttpResponseStatusCode() != SC_NOT_FOUND &&
3930 aDAVOptions.getHttpResponseStatusCode() != SC_GONE ) // the cached OPTIONS can have SC_GONE
3931 {
3932 eResourceType = NON_DAV;
3933 }
3934 else
3935 {
3936 //resource doesn't exist
3937 if ( networkAccessAllowed != nullptr )
3938 *networkAccessAllowed = false;
3939 }
3940 }
3941 }
3942
3943 osl::MutexGuard g(m_aMutex);
3944 if (m_eResourceType == UNKNOWN) {
3945 m_eResourceType = eResourceType;
3946 } else {
3948 eResourceType != m_eResourceType, "ucb.ucp.webdav",
3949 "different resource types for <" << rResAccess->getURL() << ">: "
3950 << +eResourceType << " vs. " << +m_eResourceType);
3951 }
3952 SAL_INFO( "ucb.ucp.webdav", "m_eResourceType for <" << rResAccess->getURL() << ">: " << m_eResourceType );
3953 return m_eResourceType;
3954}
3955
3956
3958 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
3959{
3960 std::unique_ptr< DAVResourceAccess > xResAccess;
3961 {
3962 osl::MutexGuard aGuard( m_aMutex );
3963 xResAccess.reset( new DAVResourceAccess( *m_xResAccess ) );
3964 }
3965 const Content::ResourceType & ret = getResourceType( xEnv, xResAccess );
3966 {
3967 osl::Guard< osl::Mutex > aGuard( m_aMutex );
3968 m_xResAccess.reset( new DAVResourceAccess( *xResAccess ) );
3969 }
3970 return ret;
3971}
3972
3973
3975{
3976 // see description in
3977 // officecfg/registry/schema/org/openoffice/Inet.xcs
3978 // for use of these field values.
3979 sal_uInt32 nAtime;
3980 nAtime = officecfg::Inet::Settings::OptsCacheLifeImplWeb::get();
3981 m_nOptsCacheLifeImplWeb = std::max( sal_uInt32( 0 ),
3982 std::min( nAtime, sal_uInt32( 3600 ) ) );
3983
3984 nAtime = officecfg::Inet::Settings::OptsCacheLifeDAV::get();
3985 m_nOptsCacheLifeDAV = std::max( sal_uInt32( 0 ),
3986 std::min( nAtime, sal_uInt32( 3600 ) ) );
3987
3988 nAtime = officecfg::Inet::Settings::OptsCacheLifeDAVLocked::get();
3989 m_nOptsCacheLifeDAVLocked = std::max( sal_uInt32( 0 ),
3990 std::min( nAtime, sal_uInt32( 3600 ) ) );
3991
3992 nAtime = officecfg::Inet::Settings::OptsCacheLifeNotImpl::get();
3993 m_nOptsCacheLifeNotImpl = std::max( sal_uInt32( 0 ),
3994 std::min( nAtime, sal_uInt32( 43200 ) ) );
3995
3996 nAtime = officecfg::Inet::Settings::OptsCacheLifeNotFound::get();
3997 m_nOptsCacheLifeNotFound = std::max( sal_uInt32( 0 ),
3998 std::min( nAtime, sal_uInt32( 30 ) ) );
3999}
4000
4001
4003 const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
4004 DAVOptions& rDAVOptions,
4005 const std::unique_ptr< DAVResourceAccess > & rResAccess,
4006 bool * networkAccessAllowed )
4007{
4008 OUString aRedirURL;
4009 OUString aTargetURL = rResAccess->getURL();
4010 DAVOptions aDAVOptions;
4011 // first check if in cache, if not, then send method to server
4012 if ( !aStaticDAVOptionsCache.getDAVOptions( aTargetURL, aDAVOptions ) )
4013 {
4014 try
4015 {
4016 rResAccess->OPTIONS( aDAVOptions, xEnv );
4017 // IMPORTANT:the correctly implemented server will answer without errors, even if the resource is not present
4018 sal_uInt32 nLifeTime = ( aDAVOptions.isClass1() ||
4019 aDAVOptions.isClass2() ||
4020 aDAVOptions.isClass3() ) ?
4021 m_nOptsCacheLifeDAV : // a WebDAV site
4022 m_nOptsCacheLifeImplWeb; // a site implementing OPTIONS but
4023 // it's not DAV
4024 // if resource is locked, will use a
4025 // different lifetime
4026 if( aDAVOptions.isLocked() )
4027 nLifeTime = m_nOptsCacheLifeDAVLocked;
4028
4029 // check if redirected
4030 aRedirURL = rResAccess->getURL();
4031 if( aRedirURL == aTargetURL)
4032 { // no redirection
4033 aRedirURL.clear();
4034 }
4035 // cache this URL's option
4036 aDAVOptions.setURL( aTargetURL );
4037 aDAVOptions.setRedirectedURL( aRedirURL );
4039 nLifeTime );
4040 }
4041 catch ( DAVException const & e )
4042 {
4043 // first, remove from cache, will be added if needed, depending on the error received
4045 rResAccess->resetUri();
4046
4047 aDAVOptions.setURL( aTargetURL );
4048 aDAVOptions.setRedirectedURL( aRedirURL );
4049 switch( e.getError() )
4050 {
4053 {
4054 // something bad happened to the connection
4055 // not same as not found, this instead happens when the server doesn't exist or doesn't answer at all
4056 // probably a new bit stating 'timed out' should be added to opts var?
4057 // in any case abort the command
4058 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_TIMEOUT or DAV_HTTP_CONNECT for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4059 // cache the internal unofficial status code
4060
4062 // used only internally, so the text doesn't really matter..
4065 if ( networkAccessAllowed != nullptr )
4066 {
4067 *networkAccessAllowed = *networkAccessAllowed
4069 }
4070 }
4071 break;
4073 {
4074 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_LOOKUP for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4076 // used only internally, so the text doesn't really matter..
4079 if ( networkAccessAllowed != nullptr )
4080 {
4081 *networkAccessAllowed = *networkAccessAllowed
4083 }
4084 }
4085 break;
4088 {
4089 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTH for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4090 // - the remote site is a WebDAV with special configuration: read/only for read operations
4091 // and read/write for write operations, the user is not allowed to lock/write and
4092 // she cancelled the credentials request.
4093 // this is not actually an error, it means only that for current user this is a standard web,
4094 // though possibly DAV enabled
4096 // used only internally, so the text doesn't really matter..
4097 if (xEnv && xEnv->getInteractionHandler())
4098 { // only cache if there actually was a chance to request auth
4101 }
4102 if ( networkAccessAllowed != nullptr )
4103 {
4104 *networkAccessAllowed = *networkAccessAllowed
4106 }
4107 }
4108 break;
4110 {
4111 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAVException: DAV_HTTP_AUTHPROXY for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4113 // used only internally, so the text doesn't really matter..
4116 if ( networkAccessAllowed != nullptr )
4117 {
4118 *networkAccessAllowed = *networkAccessAllowed
4120 }
4121 }
4122 break;
4124 {
4125 switch( e.getStatus() )
4126 {
4127 case SC_FORBIDDEN:
4128 {
4129 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_FORBIDDEN for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4130 // cache it, so OPTIONS won't be called again, this URL does not support it
4133 }
4134 break;
4135 case SC_BAD_REQUEST:
4137 {
4138 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_BAD_REQUEST or SC_INTERNAL_SERVER_ERROR for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
4139 << ", '" << e.getData() << "'" );
4140 // cache it, so OPTIONS won't be called again, this URL detect some problem while answering the method
4141 aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
4142 aDAVOptions.setHttpResponseStatusText( e.getData() );
4145 }
4146 break;
4147 case SC_NOT_IMPLEMENTED:
4149 {
4150 // OPTIONS method must be implemented in DAV
4151 // resource is NON_DAV, or not advertising it
4152 SAL_WARN( "ucb.ucp.webdav","OPTIONS - SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
4153 << ", '" << e.getData() << "'" );
4154 // cache it, so OPTIONS won't be called again, this URL does not support it
4157 }
4158 break;
4159 case SC_NOT_FOUND:
4160 {
4161 // Apparently on IIS 10.0, if you disabled OPTIONS method, this error is the one reported,
4162 // instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED.
4163 // So check if this is an available resource, or a real 'Not Found' event.
4164 sal_uInt32 nLifeTime = m_nOptsCacheLifeNotFound;
4165 if( isResourceAvailable( xEnv, rResAccess, aDAVOptions ) )
4166 {
4167 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - Got an SC_NOT_FOUND, but the URL <" << m_xIdentifier->getContentIdentifier() << "> resource exists" );
4168 nLifeTime = m_nOptsCacheLifeNotImpl;
4169 }
4170 else
4171 {
4172 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - SC_NOT_FOUND for URL <" << m_xIdentifier->getContentIdentifier() << ">" );
4173 if ( networkAccessAllowed != nullptr )
4174 {
4175 *networkAccessAllowed = *networkAccessAllowed
4177 }
4178 }
4180 nLifeTime );
4181 }
4182 break;
4183 default:
4184 {
4185 SAL_WARN( "ucb.ucp.webdav", "OPTIONS - DAV_HTTP_ERROR, for URL <" << m_xIdentifier->getContentIdentifier() << ">, HTTP error: "<< e.getStatus()
4186 << ", '" << e.getData() << "'" );
4187 aDAVOptions.setHttpResponseStatusCode( e.getStatus() );
4188 aDAVOptions.setHttpResponseStatusText( e.getData() );
4189 // cache it, so OPTIONS won't be called again, this URL does not support it
4192 }
4193 break;
4194 }
4195 }
4196 break;
4197 // The 'DAVException::DAV_HTTP_REDIRECT' means we reached the maximum
4198 // number of redirections, consider the resource type as UNKNOWN
4199 // possibly a normal web site, not DAV
4201 default:
4202 {
4203 SAL_WARN( "ucb.ucp.webdav","OPTIONS - General DAVException (or max DAV_HTTP_REDIRECT reached) for URL <" << m_xIdentifier->getContentIdentifier() << ">, DAV ExceptionCode: "
4204 << e.getError() << ", HTTP error: "<< e.getStatus() );
4207 }
4208 break;
4209 }
4210 }
4211 }
4212 else
4213 {
4214 // check current response status code, perhaps we need to set networkAccessAllowed
4215 sal_uInt16 CachedResponseStatusCode = aDAVOptions.getHttpResponseStatusCode();
4216 if ( networkAccessAllowed != nullptr &&
4217 ( ( CachedResponseStatusCode == SC_NOT_FOUND ) ||
4218 ( CachedResponseStatusCode == SC_GONE ) ||
4219 ( CachedResponseStatusCode == USC_CONNECTION_TIMED_OUT ) ||
4220 ( CachedResponseStatusCode == USC_LOOKUP_FAILED ) ||
4221 ( CachedResponseStatusCode == USC_AUTH_FAILED ) ||
4222 ( CachedResponseStatusCode == USC_AUTHPROXY_FAILED )
4223 )
4224 )
4225 {
4226 *networkAccessAllowed = *networkAccessAllowed && false;
4227 }
4228 }
4229 rDAVOptions = aDAVOptions;
4230}
4231
4232//static
4233bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
4234 const std::unique_ptr< DAVResourceAccess > & rResAccess,
4235 DAVOptions& rDAVOptions )
4236{
4237 std::vector< rtl::OUString > aHeaderNames;
4238 DAVResource aResource;
4239
4240 try
4241 {
4242 // To check for the physical URL resource availability, first
4243 // try using a simple HEAD command
4244 // if HEAD is successful, set element found.
4245 rResAccess->HEAD( aHeaderNames, aResource, xEnv );
4246 rDAVOptions.setHttpResponseStatusCode( 0 );
4247 rDAVOptions.setHttpResponseStatusText("");
4248 return true;
4249 }
4250 catch ( DAVException const & e )
4251 {
4253 {
4254 if ( e.getStatus() == SC_NOT_IMPLEMENTED ||
4256 e.getStatus() == SC_NOT_FOUND )
4257 {
4258 SAL_WARN( "ucb.ucp.webdav", "HEAD probably not implemented: fall back to a partial GET" );
4259 // set in cached OPTIONS "HEAD not implemented"
4260 // so it won't be used again on this resource
4261 rDAVOptions.setHeadAllowed( false );
4262 try
4263 {
4264 // do a GET with a payload of 0, the server does not
4265 // support HEAD (or has HEAD disabled)
4266 DAVRequestHeaders aPartialGet;
4267 aPartialGet.push_back(
4269 OUString( "Range" ),
4270 OUString( "bytes=0-0" )));
4271
4272 rResAccess->GET0( aPartialGet,
4273 aHeaderNames,
4274 aResource,
4275 xEnv );
4276 return true;
4277 }
4278 catch ( DAVException const & ex )
4279 {
4280 if ( ex.getError() == DAVException::DAV_HTTP_ERROR )
4281 {
4282 rDAVOptions.setHttpResponseStatusCode( ex.getStatus() );
4283 rDAVOptions.setHttpResponseStatusText( ex.getData() );
4284 }
4285 }
4286 }
4287 else
4288 {
4289 rDAVOptions.setHttpResponseStatusCode( e.getStatus() );
4290 rDAVOptions.setHttpResponseStatusText( e.getData() );
4291 }
4292 }
4293 return false;
4294 }
4295 catch ( ... )
4296 {
4297 }
4298 // set SC_NOT_IMPLEMENTED since at a minimum GET must be implemented in a basic Web server
4300 rDAVOptions.setHttpResponseStatusText("");
4301 return false;
4302}
4303
4304
4305/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const PropertyValue * pValues
PropertiesInfo aProperties
AnyEventRef aEvent
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
const std::unique_ptr< PropertyValueMap > & getProperties() const
static void UCBNamesToDAVNames(const css::uno::Sequence< css::beans::Property > &rProps, std::vector< OUString > &resources)
const css::uno::Any & getValue(const OUString &rName) const
static void UCBNamesToHTTPNames(const css::uno::Sequence< css::beans::Property > &rProps, std::vector< OUString > &resources)
bool getProperty(const OUString &rPropName, css::beans::Property &rProp)
bool isFolder(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
OUString getBaseURI(const std::unique_ptr< DAVResourceAccess > &rResAccess)
ResourceType m_eResourceTypeForLocks
void cancelCommandExecution(const DAVException &e, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bWrite=false)
static void removeCachedPropertyNames(const OUString &rURL)
static bool isResourceAvailable(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, const std::unique_ptr< DAVResourceAccess > &rResAccess, DAVOptions &rDAVOptions)
sal_uInt32 m_nOptsCacheLifeDAVLocked
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL createNewContent(const css::ucb::ContentInfo &Info) override
sal_uInt32 m_nOptsCacheLifeImplWeb
rtl::Reference< Content > ContentRef
virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL queryCreatableContentsInfo() override
sal_uInt32 m_nOptsCacheLifeNotImpl
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
static bool shouldAccessNetworkAfterException(const DAVException &e)
sal_uInt32 m_nOptsCacheLifeDAV
css::uno::Reference< css::sdbc::XRow > getPropertyValues(const css::uno::Sequence< css::beans::Property > &rProperties, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
std::vector< ContentRef > ContentRefList
bool exchangeIdentity(const css::uno::Reference< css::ucb::XContentIdentifier > &xNewId)
void getResourceOptions(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, DAVOptions &rDAVOptions, const std::unique_ptr< DAVResourceAccess > &rResAccess, bool *networkAccessAllowed=nullptr)
Use OPTIONS method to retrieve the type of the Web resource.
void lock(const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
void unlock(const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
void removeProperty(const OUString &Name, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
css::uno::Sequence< css::uno::Any > setPropertyValues(const css::uno::Sequence< css::beans::PropertyValue > &rValues, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void insert(const css::uno::Reference< css::io::XInputStream > &xInputStream, bool bReplaceExisting, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
virtual css::uno::Any SAL_CALL execute(const css::ucb::Command &aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment) override
std::vector< OUString > m_aFailedPropNames
virtual OUString SAL_CALL getImplementationName() override
ResourceType m_eResourceType
virtual void SAL_CALL acquire() noexcept override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL release() noexcept override
ResourceType resourceTypeForLocks(const css::uno::Reference< css::ucb::XCommandEnvironment > &rEnvironment, const std::unique_ptr< DAVResourceAccess > &rResAccess)
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
void transfer(const css::ucb::TransferInfo &rArgs, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
css::uno::Any open(const css::ucb::OpenCommandArgument3 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
sal_uInt32 m_nOptsCacheLifeNotFound
void queryChildren(ContentRefList &rChildren)
std::unique_ptr< DAVResourceAccess > m_xResAccess
virtual OUString SAL_CALL getContentType() override
virtual void SAL_CALL abort(sal_Int32 CommandId) override
virtual OUString getParentURL() override
void addProperty(const css::ucb::PropertyCommandArgument &aCmdArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment)
ResourceType getResourceType(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
std::unique_ptr< CachableContentProperties > m_xCachedProps
css::uno::Any MapDAVException(const DAVException &e, bool bWrite)
void destroy(bool bDeletePhysical)
ContentProvider * m_pProvider
void post(const css::ucb::PostCommandArgument2 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
OUString GetPathBaseName() const
Definition: CurlUri.cxx:182
const OUString & getData() const
const ExceptionCode & getError() const
sal_uInt16 getStatus() const
bool getDAVOptions(const OUString &rURL, DAVOptions &rDAVOptions)
Definition: DAVTypes.cxx:105
void setHeadAllowed(const OUString &rURL, bool HeadAllowed=true)
Definition: DAVTypes.cxx:184
void removeDAVOptions(const OUString &rURL)
Definition: DAVTypes.cxx:133
void addDAVOptions(DAVOptions &rDAVOptions, const sal_uInt32 nLifeTime)
Definition: DAVTypes.cxx:147
bool isClass1() const
Definition: DAVTypes.hxx:96
bool isClass2() const
Definition: DAVTypes.hxx:99
const OUString & getHttpResponseStatusText() const
Definition: DAVTypes.hxx:130
void setURL(const OUString &sURL)
Definition: DAVTypes.hxx:115
void setHeadAllowed(bool HeadAllowed=true)
Definition: DAVTypes.hxx:106
bool isHeadAllowed() const
Definition: DAVTypes.hxx:105
bool isLockAllowed() const
Definition: DAVTypes.hxx:122
void setRedirectedURL(const OUString &sRedirectedURL)
Definition: DAVTypes.hxx:118
void setHttpResponseStatusCode(const sal_uInt16 nHttpResponseStatusCode)
Definition: DAVTypes.hxx:128
sal_uInt16 getHttpResponseStatusCode() const
Definition: DAVTypes.hxx:127
void setHttpResponseStatusText(const OUString &rHttpResponseStatusText)
Definition: DAVTypes.hxx:131
static bool supportsType(const css::uno::Type &rType)
static OUString GetOOOUserName()
css::uno::Reference< css::ucb::XPersistentPropertySet > getAdditionalPropertySet(bool bCreate)
void notifyPropertySetInfoChange(const css::beans::PropertySetInfoChangeEvent &evt) const
css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
rtl::Reference< ContentProviderImplHelper > m_xProvider
bool exchange(const css::uno::Reference< css::ucb::XContentIdentifier > &rNewId)
css::uno::Reference< css::beans::XPropertySetInfo > getPropertySetInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
css::uno::Reference< css::ucb::XContentIdentifier > m_xIdentifier
css::uno::Reference< css::uno::XComponentContext > m_xContext
void notifyPropertiesChange(const css::uno::Sequence< css::beans::PropertyChangeEvent > &evt) const
int nCount
URL aURL
Reference< XInterface > xTarget
UNKNOWN
DocumentType eType
sal_Int64 n
tools::SvRef< SvBaseLink > xSink
sal_uInt16 nPos
Sequence< sal_Int8 > aSeq
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
#define CPPU_TYPE_REF(T)
Type
css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType, Interface1 *p1)
Info
const sal_uInt16 SC_NOT_IMPLEMENTED
const sal_uInt16 USC_AUTHPROXY_FAILED
OUString DecodeURI(OUString const &rURI)
Definition: CurlUri.cxx:302
const sal_uInt16 USC_LOOKUP_FAILED
const sal_uInt16 SC_GONE
const sal_uInt16 SC_FORBIDDEN
const sal_uInt16 USC_CONNECTION_TIMED_OUT
constexpr OUStringLiteral WEBDAV_COLLECTION_TYPE
const sal_uInt16 SC_PRECONDITION_FAILED
const sal_uInt16 SC_NONE
OUString EncodeSegment(OUString const &rSegment)
Definition: CurlUri.cxx:296
OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
Definition: CurlUri.cxx:307
const sal_uInt16 SC_BAD_REQUEST
const sal_uInt16 SC_INTERNAL_SERVER_ERROR
const sal_uInt16 SC_NOT_FOUND
const sal_uInt16 SC_METHOD_NOT_ALLOWED
const sal_uInt16 USC_AUTH_FAILED
std::vector< DAVRequestHeader > DAVRequestHeaders
std::pair< OUString, OUString > DAVRequestHeader
constexpr OUStringLiteral WEBDAV_CONTENT_SERVICE_NAME
end
std::vector< ContentImplHelperRef > ContentRefList
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
ResourceType
const PropertyStruct aPropNames[]
ContinuationFlags
static constexpr OUStringLiteral SUPPORTEDLOCK
static bool isUCBSpecialProperty(std::u16string_view rFullName, OUString &rParsedName)
std::vector< DAVPropertyValue > properties
Definition: DAVResource.hxx:43
bool hasValue()
OUString aCommand
OUString Name
OUString aTargetURL
static DAVOptionsCache aStaticDAVOptionsCache
XTYPEPROVIDER_COMMON_IMPL(Content)
#define WEBDAV_CONTENT_TYPE
#define HTTP_URL_SCHEME
#define DAV_URL_SCHEME
#define DAVS_URL_SCHEME
#define WEBDAV_URL_SCHEME
#define HTTPS_URL_SCHEME
#define WEBDAVS_URL_SCHEME
#define VNDSUNSTARWEBDAV_URL_SCHEME
#define VNDSUNSTARWEBDAVS_URL_SCHEME