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 #include <memory>
36 #include <com/sun/star/util/DateTime.hpp>
37 #include "NeonUri.hxx"
38 #include "DAVResource.hxx"
39 #include "DAVProperties.hxx"
40 #include "DateTimeHelper.hxx"
41 #include "webdavprovider.hxx"
42 #include "ContentProperties.hxx"
43 
44 using namespace com::sun::star;
45 using namespace webdav_ucp;
46 
47 /*
48 =============================================================================
49 
50                             Property Mapping
51 
52 =============================================================================
53 HTTP (entity header)    WebDAV (property)   UCB (property)
54 =============================================================================
55 
56 Allow
57 Content-Encoding
58 Content-Language        getcontentlanguage
59 Content-Length          getcontentlength    Size
60 Content-Location
61 Content-MD5
62 Content-Range
63 Content-Type            getcontenttype      MediaType
64 Expires
65 Last-Modified           getlastmodified     DateModified
66                         creationdate        DateCreated
67                         resourcetype        IsFolder,IsDocument,ContentType
68                         displayname
69 ETag (actually          getetag
70 a response header )
71                         lockdiscovery
72                         supportedlock
73                         source
74                                             Title (always taken from URI)
75 
76 =============================================================================
77 
78 Important: HTTP headers will not be mapped to DAV properties; only to UCB
79            properties. (Content-Length,Content-Type,Last-Modified)
80 */
81 
82 
83 // ContentProperties Implementation.
84 
85 
86 // static member!
87 uno::Any ContentProperties::m_aEmptyAny;
88 
ContentProperties(const DAVResource & rResource)89 ContentProperties::ContentProperties( const DAVResource& rResource )
90 : m_xProps( new PropertyValueMap ),
91   m_bTrailingSlash( false )
92 {
93     assert( !rResource.uri.isEmpty() && "ContentProperties ctor - Empty resource URI!" );
94 
95     // Title
96     try
97     {
98         NeonUri aURI( rResource.uri );
99         m_aEscapedTitle = aURI.GetPathBaseName();
100 
101         (*m_xProps)[ OUString("Title") ]
102             = PropertyValue(
103                 uno::makeAny( aURI.GetPathBaseNameUnescaped() ), true );
104     }
105     catch ( DAVException const & )
106     {
107         (*m_xProps)[ OUString("Title") ]
108             = PropertyValue(
109                 uno::makeAny(
110                     OUString(
111                          "*** unknown ***"  ) ),
112                 true );
113     }
114 
115     for ( const auto& rProp : rResource.properties )
116     {
117         addProperty( rProp );
118     }
119 
120     if ( rResource.uri.endsWith("/") )
121         m_bTrailingSlash = true;
122 }
123 
124 
ContentProperties(const OUString & rTitle,bool bFolder)125 ContentProperties::ContentProperties(
126                         const OUString & rTitle, bool bFolder )
127 : m_xProps( new PropertyValueMap ),
128   m_bTrailingSlash( false )
129 {
130     (*m_xProps)[ OUString("Title") ]
131         = PropertyValue( uno::makeAny( rTitle ), true );
132     (*m_xProps)[ OUString("IsFolder") ]
133         = PropertyValue( uno::makeAny( bFolder ), true );
134     (*m_xProps)[ OUString("IsDocument") ]
135         = PropertyValue( uno::makeAny( !bFolder ), true );
136 }
137 
138 
ContentProperties(const OUString & rTitle)139 ContentProperties::ContentProperties( const OUString & rTitle )
140 : m_xProps( new PropertyValueMap ),
141   m_bTrailingSlash( false )
142 {
143     (*m_xProps)[ OUString("Title") ]
144         = PropertyValue( uno::makeAny( rTitle ), true );
145 }
146 
147 
ContentProperties()148 ContentProperties::ContentProperties()
149 : m_xProps( new PropertyValueMap ),
150   m_bTrailingSlash( false )
151 {
152 }
153 
ContentProperties(const ContentProperties & rOther)154 ContentProperties::ContentProperties(const ContentProperties& rOther)
155     : m_aEscapedTitle(rOther.m_aEscapedTitle)
156     , m_xProps(rOther.m_xProps ? new PropertyValueMap(*rOther.m_xProps) : new PropertyValueMap)
157     , m_bTrailingSlash(rOther.m_bTrailingSlash)
158 {
159 }
160 
161 
contains(const OUString & rName) const162 bool ContentProperties::contains( const OUString & rName ) const
163 {
164     return get( rName ) != nullptr;
165 }
166 
167 
getValue(const OUString & rName) const168 const uno::Any & ContentProperties::getValue(
169                                     const OUString & rName ) const
170 {
171     const PropertyValue * pProp = get( rName );
172     if ( pProp )
173         return pProp->value();
174     else
175         return m_aEmptyAny;
176 }
177 
178 
get(const OUString & rName) const179 const PropertyValue * ContentProperties::get(
180                                     const OUString & rName ) const
181 {
182     PropertyValueMap::const_iterator it = m_xProps->find( rName );
183     const PropertyValueMap::const_iterator end = m_xProps->end();
184 
185     if ( it == end )
186     {
187         it = std::find_if(m_xProps->cbegin(), end,
188             [&rName](const PropertyValueMap::value_type& rEntry) {
189                 return rEntry.first.equalsIgnoreAsciiCase( rName );
190             });
191         if ( it != end )
192             return &(*it).second;
193 
194         return nullptr;
195     }
196     else
197         return &(*it).second;
198 }
199 
200 
201 // static
UCBNamesToDAVNames(const uno::Sequence<beans::Property> & rProps,std::vector<OUString> & propertyNames)202 void ContentProperties::UCBNamesToDAVNames(
203                             const uno::Sequence< beans::Property > & rProps,
204                             std::vector< OUString > & propertyNames )
205 {
206 
207     // Assemble list of DAV properties to obtain from server.
208     // Append DAV properties needed to obtain requested UCB props.
209 
210 
211     //       DAV              UCB
212     // creationdate     <- DateCreated
213     // getlastmodified  <- DateModified
214     // getcontenttype   <- MediaType
215     // getcontentlength <- Size
216     // resourcetype     <- IsFolder, IsDocument, ContentType
217     // (taken from URI) <- Title
218 
219     bool bCreationDate  = false;
220     bool bLastModified  = false;
221     bool bContentType   = false;
222     bool bContentLength = false;
223     bool bResourceType  = false;
224 
225     for ( const beans::Property & rProp : rProps )
226     {
227         if ( rProp.Name == "Title" )
228         {
229             // Title is always obtained from resource's URI.
230             continue;
231         }
232         else if ( rProp.Name == "DateCreated" || rProp.Name == DAVProperties::CREATIONDATE )
233         {
234             if ( !bCreationDate )
235             {
236                 propertyNames.push_back( DAVProperties::CREATIONDATE );
237                 bCreationDate = true;
238             }
239         }
240         else if ( rProp.Name == "DateModified" || rProp.Name == DAVProperties::GETLASTMODIFIED )
241         {
242             if ( !bLastModified )
243             {
244                 propertyNames.push_back( DAVProperties::GETLASTMODIFIED );
245                 bLastModified = true;
246             }
247         }
248         else if ( rProp.Name == "MediaType" || rProp.Name == DAVProperties::GETCONTENTTYPE )
249         {
250             if ( !bContentType )
251             {
252                 propertyNames.push_back( DAVProperties::GETCONTENTTYPE );
253                 bContentType = true;
254             }
255         }
256         else if ( rProp.Name == "Size" || rProp.Name == DAVProperties::GETCONTENTLENGTH )
257         {
258             if ( !bContentLength )
259             {
260                 propertyNames.push_back( DAVProperties::GETCONTENTLENGTH );
261                 bContentLength = true;
262             }
263         }
264         else if ( rProp.Name == "ContentType" || rProp.Name == "IsDocument" || rProp.Name == "IsFolder" || rProp.Name == DAVProperties::RESOURCETYPE )
265         {
266             if ( !bResourceType )
267             {
268                 propertyNames.push_back( DAVProperties::RESOURCETYPE );
269                 bResourceType = true;
270             }
271         }
272         else
273         {
274             propertyNames.push_back( rProp.Name );
275         }
276     }
277 }
278 
279 
280 // static
UCBNamesToHTTPNames(const uno::Sequence<beans::Property> & rProps,std::vector<OUString> & propertyNames)281 void ContentProperties::UCBNamesToHTTPNames(
282                             const uno::Sequence< beans::Property > & rProps,
283                             std::vector< OUString > & propertyNames )
284 {
285 
286     // Assemble list of HTTP header names to obtain from server.
287     // Append HTTP headers needed to obtain requested UCB props.
288 
289 
290     //       HTTP              UCB
291     // Last-Modified  <- DateModified
292     // Content-Type   <- MediaType
293     // Content-Length <- Size
294 
295     for ( const beans::Property & rProp : rProps )
296     {
297         if ( rProp.Name == "DateModified" )
298         {
299             propertyNames.emplace_back("Last-Modified" );
300         }
301         else if ( rProp.Name == "MediaType" )
302         {
303             propertyNames.emplace_back("Content-Type" );
304         }
305         else if ( rProp.Name == "Size" )
306         {
307             propertyNames.emplace_back("Content-Length" );
308         }
309         else
310         {
311             propertyNames.push_back( rProp.Name );
312         }
313     }
314 }
315 
316 
containsAllNames(const uno::Sequence<beans::Property> & rProps,std::vector<OUString> & rNamesNotContained) const317 bool ContentProperties::containsAllNames(
318                     const uno::Sequence< beans::Property >& rProps,
319                     std::vector< OUString > & rNamesNotContained ) const
320 {
321     rNamesNotContained.clear();
322 
323     for ( const auto& rProp : rProps )
324     {
325         const OUString & rName = rProp.Name;
326         if ( !contains( rName ) )
327         {
328             // Not found.
329             rNamesNotContained.push_back( rName );
330         }
331     }
332 
333     return rNamesNotContained.empty();
334 }
335 
336 
addProperties(const std::vector<OUString> & rProps,const ContentProperties & rContentProps)337 void ContentProperties::addProperties(
338                                 const std::vector< OUString > & rProps,
339                                 const ContentProperties & rContentProps )
340 {
341     for ( const OUString & rName : rProps )
342     {
343         if ( !contains( rName ) ) // ignore duplicates
344         {
345             const PropertyValue * pProp = rContentProps.get( rName );
346             if ( pProp )
347             {
348                 // Add it.
349                 addProperty( rName, pProp->value(), pProp->isCaseSensitive() );
350             }
351             else
352             {
353                 addProperty( rName, uno::Any(), false );
354             }
355         }
356     }
357 }
358 
359 
addProperty(const DAVPropertyValue & rProp)360 void ContentProperties::addProperty( const DAVPropertyValue & rProp )
361 {
362     addProperty( rProp.Name, rProp.Value, rProp.IsCaseSensitive );
363 }
364 
365 
addProperty(const OUString & rName,const css::uno::Any & rValue,bool bIsCaseSensitive)366 void ContentProperties::addProperty( const OUString & rName,
367                                      const css::uno::Any & rValue,
368                                      bool bIsCaseSensitive )
369 {
370     if ( rName == DAVProperties::CREATIONDATE )
371     {
372         // Map DAV:creationdate to UCP:DateCreated
373         OUString aValue;
374         rValue >>= aValue;
375         util::DateTime aDate;
376         DateTimeHelper::convert( aValue, aDate );
377 
378         (*m_xProps)[ OUString("DateCreated") ]
379             = PropertyValue( uno::makeAny( aDate ), true );
380     }
381     //  else if ( rName.equals( DAVProperties::DISPLAYNAME ) )
382     //  {
383     //  }
384     //  else if ( rName.equals( DAVProperties::GETCONTENTLANGUAGE ) )
385     //  {
386     //  }
387     else if ( rName == DAVProperties::GETCONTENTLENGTH )
388     {
389         // Map DAV:getcontentlength to UCP:Size
390         OUString aValue;
391         rValue >>= aValue;
392 
393         (*m_xProps)[ OUString("Size") ]
394             = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
395     }
396     else if ( rName.equalsIgnoreAsciiCase( "Content-Length" ) )
397     {
398         // Do NOT map Content-length entity header to DAV:getcontentlength!
399         // Only DAV resources have this property.
400 
401         // Map Content-Length entity header to UCP:Size
402         OUString aValue;
403         rValue >>= aValue;
404 
405         (*m_xProps)[ OUString("Size") ]
406             = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
407     }
408     else if ( rName == DAVProperties::GETCONTENTTYPE )
409     {
410         // Map DAV:getcontenttype to UCP:MediaType (1:1)
411         (*m_xProps)[ OUString("MediaType") ]
412             = PropertyValue( rValue, true );
413     }
414     else if ( rName.equalsIgnoreAsciiCase( "Content-Type" ) )
415     {
416         // Do NOT map Content-Type entity header to DAV:getcontenttype!
417         // Only DAV resources have this property.
418 
419         // Map DAV:getcontenttype to UCP:MediaType (1:1)
420         (*m_xProps)[ OUString("MediaType") ]
421             = PropertyValue( rValue, true );
422     }
423     //  else if ( rName.equals( DAVProperties::GETETAG ) )
424     //  {
425     //  }
426     else if ( rName == DAVProperties::GETLASTMODIFIED )
427     {
428         // Map the DAV:getlastmodified entity header to UCP:DateModified
429         OUString aValue;
430         rValue >>= aValue;
431         util::DateTime aDate;
432         DateTimeHelper::convert( aValue, aDate );
433 
434         (*m_xProps)[ OUString("DateModified") ]
435             = PropertyValue( uno::makeAny( aDate ), true );
436     }
437     else if ( rName.equalsIgnoreAsciiCase( "Last-Modified" ) )
438     {
439         // Do not map Last-Modified entity header to DAV:getlastmodified!
440         // Only DAV resources have this property.
441 
442         // Map the Last-Modified entity header to UCP:DateModified
443         OUString aValue;
444         rValue >>= aValue;
445         util::DateTime aDate;
446         DateTimeHelper::convert( aValue, aDate );
447 
448         (*m_xProps)[ OUString("DateModified") ]
449             = PropertyValue( uno::makeAny( aDate ), true );
450     }
451     //  else if ( rName.equals( DAVProperties::LOCKDISCOVERY ) )
452     //  {
453     //  }
454     else if ( rName == DAVProperties::RESOURCETYPE )
455     {
456         OUString aValue;
457         rValue >>= aValue;
458 
459         // Map DAV:resourcetype to UCP:IsFolder, UCP:IsDocument, UCP:ContentType
460         bool bFolder = aValue.equalsIgnoreAsciiCase( "collection" );
461 
462         (*m_xProps)[ OUString("IsFolder") ]
463             = PropertyValue( uno::makeAny( bFolder ), true );
464         (*m_xProps)[ OUString("IsDocument") ]
465             = PropertyValue( uno::makeAny( !bFolder ), true );
466         (*m_xProps)[ OUString("ContentType") ]
467             = PropertyValue( uno::makeAny( bFolder
468                 ? OUString( WEBDAV_COLLECTION_TYPE )
469                 : OUString( WEBDAV_CONTENT_TYPE ) ), true );
470     }
471     //  else if ( rName.equals( DAVProperties::SOURCE ) )
472     //  {
473     //  }
474     //  else if ( rName.equals( DAVProperties::SUPPORTEDLOCK ) )
475     //  {
476     //  }
477 
478     // Save property.
479     (*m_xProps)[ rName ] = PropertyValue( rValue, bIsCaseSensitive );
480 }
481 
482 
483 // CachableContentProperties Implementation.
484 
485 
486 namespace
487 {
isCachable(OUString const & rName,bool isCaseSensitive)488     bool isCachable( OUString const & rName,
489                      bool isCaseSensitive )
490     {
491         static const OUString aNonCachableProps [] =
492         {
493             DAVProperties::LOCKDISCOVERY,
494 
495             DAVProperties::GETETAG,
496             OUString(  "ETag"  ),
497 
498             OUString(  "DateModified"  ),
499             OUString(  "Last-Modified"  ),
500             DAVProperties::GETLASTMODIFIED,
501 
502             OUString(  "Size"  ),
503             OUString(  "Content-Length"  ),
504             DAVProperties::GETCONTENTLENGTH,
505 
506             OUString(  "Date"  )
507         };
508 
509         for (const auto & rNonCachableProp : aNonCachableProps)
510         {
511             if ( isCaseSensitive )
512             {
513                 if ( rName == rNonCachableProp )
514                     return false;
515             }
516             else
517                 if ( rName.equalsIgnoreAsciiCase( rNonCachableProp ) )
518                     return false;
519         }
520         return true;
521     }
522 
523 } // namespace
524 
525 
CachableContentProperties(const ContentProperties & rProps)526 CachableContentProperties::CachableContentProperties(
527     const ContentProperties & rProps )
528 {
529     addProperties( rProps );
530 }
531 
532 
addProperties(const ContentProperties & rProps)533 void CachableContentProperties::addProperties(
534     const ContentProperties & rProps )
535 {
536     const std::unique_ptr< PropertyValueMap > & props = rProps.getProperties();
537 
538     for ( const auto& rProp : *props )
539     {
540         if ( isCachable( rProp.first, rProp.second.isCaseSensitive() ) )
541             m_aProps.addProperty( rProp.first,
542                                   rProp.second.value(),
543                                   rProp.second.isCaseSensitive() );
544     }
545 }
546 
547 
addProperties(const std::vector<DAVPropertyValue> & rProps)548 void CachableContentProperties::addProperties(
549     const std::vector< DAVPropertyValue > & rProps )
550 {
551     for ( const auto& rProp : rProps )
552     {
553         if ( isCachable( rProp.Name, rProp.IsCaseSensitive ) )
554             m_aProps.addProperty( rProp );
555      }
556 }
557 
558 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
559