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