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