1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_ucb.hxx"
26 
27 /**************************************************************************
28                                 TODO
29  **************************************************************************
30 
31  *************************************************************************/
32 #include <osl/diagnose.h>
33 #include <com/sun/star/util/DateTime.hpp>
34 #include "NeonUri.hxx"
35 #include "DAVResource.hxx"
36 #include "DAVProperties.hxx"
37 #include "DateTimeHelper.hxx"
38 #include "webdavprovider.hxx"
39 #include "ContentProperties.hxx"
40 
41 using namespace com::sun::star;
42 using namespace webdav_ucp;
43 
44 /*
45 =============================================================================
46 
47                             Property Mapping
48 
49 =============================================================================
50 HTTP (entity header)    WebDAV (property)   UCB (property)
51 =============================================================================
52 
53 Allow
54 Content-Encoding
55 Content-Language        getcontentlanguage
56 Content-Length          getcontentlength    Size
57 Content-Location
58 Content-MD5
59 Content-Range
60 Content-Type            getcontenttype      MediaType
61 Expires
62 Last-Modified           getlastmodified     DateModified
63                         creationdate        DateCreated
64                         resourcetype        IsFolder,IsDocument,ContentType
65                         displayname
66 ETag (actually          getetag
67 a response header )
68                         lockdiscovery
69                         supportedlock
70                         source
71                                             Title (always taken from URI)
72 
73 =============================================================================
74 
75 Important: HTTP headers will not be mapped to DAV properties; only to UCB
76            properties. (Content-Length,Content-Type,Last-Modified)
77 */
78 
79 //=========================================================================
80 //=========================================================================
81 //
82 // ContentProperties Implementation.
83 //
84 //=========================================================================
85 //=========================================================================
86 
87 // static member!
88 uno::Any ContentProperties::m_aEmptyAny;
89 
90 ContentProperties::ContentProperties( const DAVResource& rResource )
91 : m_xProps( new PropertyValueMap ),
92   m_bTrailingSlash( false )
93 {
94     OSL_ENSURE( rResource.uri.getLength(),
95                 "ContentProperties ctor - Empty resource URI!" );
96 
97     // Title
98     try
99     {
100         NeonUri aURI( rResource.uri );
101         m_aEscapedTitle = aURI.GetPathBaseName();
102 
103         (*m_xProps)[ rtl::OUString::createFromAscii( "Title" ) ]
104             = PropertyValue(
105                 uno::makeAny( aURI.GetPathBaseNameUnescaped() ), true );
106     }
107     catch ( DAVException const & )
108     {
109         (*m_xProps)[ rtl::OUString::createFromAscii( "Title" ) ]
110             = PropertyValue(
111                 uno::makeAny(
112                     rtl::OUString(
113                         RTL_CONSTASCII_USTRINGPARAM( "*** unknown ***" ) ) ),
114                 true );
115     }
116 
117     std::vector< DAVPropertyValue >::const_iterator it
118         = rResource.properties.begin();
119     std::vector< DAVPropertyValue >::const_iterator end
120         = rResource.properties.end();
121 
122     while ( it != end )
123     {
124         addProperty( (*it) );
125         ++it;
126     }
127 
128     if ( rResource.uri.getStr()[ rResource.uri.getLength() - 1 ]
129         == sal_Unicode( '/' ) )
130         m_bTrailingSlash = sal_True;
131 }
132 
133 //=========================================================================
134 ContentProperties::ContentProperties(
135                         const rtl::OUString & rTitle, sal_Bool bFolder )
136 : m_xProps( new PropertyValueMap ),
137   m_bTrailingSlash( sal_False )
138 {
139     (*m_xProps)[ rtl::OUString::createFromAscii( "Title" ) ]
140         = PropertyValue( uno::makeAny( rTitle ), true );
141     (*m_xProps)[ rtl::OUString::createFromAscii( "IsFolder" ) ]
142         = PropertyValue( uno::makeAny( bFolder ), true );
143     (*m_xProps)[ rtl::OUString::createFromAscii( "IsDocument" ) ]
144         = PropertyValue( uno::makeAny( sal_Bool( !bFolder ) ), true );
145 }
146 
147 //=========================================================================
148 ContentProperties::ContentProperties( const rtl::OUString & rTitle )
149 : m_xProps( new PropertyValueMap ),
150   m_bTrailingSlash( sal_False )
151 {
152     (*m_xProps)[ rtl::OUString::createFromAscii( "Title" ) ]
153         = PropertyValue( uno::makeAny( rTitle ), true );
154 }
155 
156 //=========================================================================
157 ContentProperties::ContentProperties()
158 : m_xProps( new PropertyValueMap ),
159   m_bTrailingSlash( sal_False )
160 {
161 }
162 
163 //=========================================================================
164 ContentProperties::ContentProperties( const ContentProperties & rOther )
165 : m_aEscapedTitle( rOther.m_aEscapedTitle ),
166   m_xProps( rOther.m_xProps.get()
167             ? new PropertyValueMap( *rOther.m_xProps )
168             : new PropertyValueMap ),
169   m_bTrailingSlash( rOther.m_bTrailingSlash )
170 {
171 }
172 
173 //=========================================================================
174 bool ContentProperties::contains( const rtl::OUString & rName ) const
175 {
176     if ( get( rName ) )
177         return true;
178     else
179         return false;
180 }
181 
182 //=========================================================================
183 const uno::Any & ContentProperties::getValue(
184                                     const rtl::OUString & rName ) const
185 {
186     const PropertyValue * pProp = get( rName );
187     if ( pProp )
188         return pProp->value();
189     else
190         return m_aEmptyAny;
191 }
192 
193 //=========================================================================
194 const PropertyValue * ContentProperties::get(
195                                     const rtl::OUString & rName ) const
196 {
197     PropertyValueMap::const_iterator it = m_xProps->find( rName );
198     const PropertyValueMap::const_iterator end = m_xProps->end();
199 
200     if ( it == end )
201     {
202         it  = m_xProps->begin();
203         while ( it != end )
204         {
205             if ( (*it).first.equalsIgnoreAsciiCase( rName ) )
206                 return &(*it).second;
207 
208             ++it;
209         }
210         return 0;
211     }
212     else
213         return &(*it).second;
214 }
215 
216 //=========================================================================
217 // static
218 void ContentProperties::UCBNamesToDAVNames(
219                             const uno::Sequence< beans::Property > & rProps,
220                             std::vector< rtl::OUString > & propertyNames,
221                             bool bIncludeUnmatched /* = true */ )
222 {
223     //////////////////////////////////////////////////////////////
224     // Assemble list of DAV properties to obtain from server.
225     // Append DAV properties needed to obtain requested UCB props.
226     //////////////////////////////////////////////////////////////
227 
228     //       DAV              UCB
229     // creationdate     <- DateCreated
230     // getlastmodified  <- DateModified
231     // getcontenttype   <- MediaType
232     // getcontentlength <- Size
233     // resourcetype     <- IsFolder, IsDocument, ContentType
234     // (taken from URI) <- Title
235 
236     sal_Bool bCreationDate  = sal_False;
237     sal_Bool bLastModified  = sal_False;
238     sal_Bool bContentType   = sal_False;
239     sal_Bool bContentLength = sal_False;
240     sal_Bool bResourceType  = sal_False;
241 
242     sal_Int32 nCount = rProps.getLength();
243     for ( sal_Int32 n = 0; n < nCount; ++n )
244     {
245         const beans::Property & rProp = rProps[ n ];
246 
247         if ( rProp.Name.equalsAsciiL(
248                     RTL_CONSTASCII_STRINGPARAM( "Title" ) ) )
249         {
250             // Title is always obtained from resource's URI.
251             continue;
252         }
253         else if ( rProp.Name.equalsAsciiL(
254                     RTL_CONSTASCII_STRINGPARAM( "DateCreated" ) )
255                   ||
256                   ( rProp.Name == DAVProperties::CREATIONDATE ) )
257         {
258             if ( !bCreationDate )
259             {
260                     propertyNames.push_back( DAVProperties::CREATIONDATE );
261                 bCreationDate = sal_True;
262             }
263         }
264         else if ( rProp.Name.equalsAsciiL(
265                     RTL_CONSTASCII_STRINGPARAM( "DateModified" ) )
266                   ||
267                   ( rProp.Name == DAVProperties::GETLASTMODIFIED ) )
268         {
269             if ( !bLastModified )
270             {
271                     propertyNames.push_back(
272                     DAVProperties::GETLASTMODIFIED );
273                 bLastModified = sal_True;
274             }
275         }
276         else if ( rProp.Name.equalsAsciiL(
277                     RTL_CONSTASCII_STRINGPARAM( "MediaType" ) )
278                   ||
279                   ( rProp.Name == DAVProperties::GETCONTENTTYPE ) )
280         {
281             if ( !bContentType )
282             {
283                     propertyNames.push_back(
284                         DAVProperties::GETCONTENTTYPE );
285                 bContentType = sal_True;
286             }
287         }
288         else if ( rProp.Name.equalsAsciiL(
289                     RTL_CONSTASCII_STRINGPARAM( "Size" ) )
290                   ||
291                   ( rProp.Name == DAVProperties::GETCONTENTLENGTH ) )
292         {
293             if ( !bContentLength )
294             {
295                     propertyNames.push_back(
296                     DAVProperties::GETCONTENTLENGTH );
297                 bContentLength = sal_True;
298             }
299         }
300         else if ( rProp.Name.equalsAsciiL(
301                     RTL_CONSTASCII_STRINGPARAM( "ContentType" ) )
302                   ||
303                   rProp.Name.equalsAsciiL(
304                     RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) )
305                   ||
306                   rProp.Name.equalsAsciiL(
307                     RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) )
308                   ||
309                   ( rProp.Name == DAVProperties::RESOURCETYPE ) )
310         {
311             if ( !bResourceType )
312             {
313                     propertyNames.push_back( DAVProperties::RESOURCETYPE );
314                 bResourceType = sal_True;
315             }
316         }
317         else
318         {
319             if ( bIncludeUnmatched )
320                 propertyNames.push_back( rProp.Name );
321         }
322     }
323 }
324 
325 //=========================================================================
326 // static
327 void ContentProperties::UCBNamesToHTTPNames(
328                             const uno::Sequence< beans::Property > & rProps,
329                             std::vector< rtl::OUString > & propertyNames,
330                             bool bIncludeUnmatched /* = true */ )
331 {
332     //////////////////////////////////////////////////////////////
333     // Assemble list of HTTP header names to obtain from server.
334     // Append HTTP headers needed to obtain requested UCB props.
335     //////////////////////////////////////////////////////////////
336 
337     //       HTTP              UCB
338     // Last-Modified  <- DateModified
339     // Content-Type   <- MediaType
340     // Content-Length <- Size
341 
342     sal_Int32 nCount = rProps.getLength();
343     for ( sal_Int32 n = 0; n < nCount; ++n )
344     {
345         const beans::Property & rProp = rProps[ n ];
346 
347         if ( rProp.Name.equalsAsciiL(
348                     RTL_CONSTASCII_STRINGPARAM( "DateModified" ) ) )
349         {
350             propertyNames.push_back(
351                 rtl::OUString::createFromAscii( "Last-Modified" ) );
352         }
353         else if ( rProp.Name.equalsAsciiL(
354                     RTL_CONSTASCII_STRINGPARAM( "MediaType" ) ) )
355         {
356             propertyNames.push_back(
357                 rtl::OUString::createFromAscii( "Content-Type" ) );
358         }
359         else if ( rProp.Name.equalsAsciiL(
360                     RTL_CONSTASCII_STRINGPARAM( "Size" ) ) )
361         {
362             propertyNames.push_back(
363                 rtl::OUString::createFromAscii( "Content-Length" ) );
364         }
365         else
366         {
367             if ( bIncludeUnmatched )
368                 propertyNames.push_back( rProp.Name );
369         }
370     }
371 }
372 
373 //=========================================================================
374 bool ContentProperties::containsAllNames(
375                     const uno::Sequence< beans::Property >& rProps,
376                     std::vector< rtl::OUString > & rNamesNotContained ) const
377 {
378     rNamesNotContained.clear();
379 
380     sal_Int32 nCount = rProps.getLength();
381     for ( sal_Int32 n = 0; n < nCount; ++n )
382     {
383         const rtl::OUString & rName = rProps[ n ].Name;
384         if ( !contains( rName ) )
385         {
386             // Not found.
387             rNamesNotContained.push_back( rName );
388         }
389     }
390 
391     return ( rNamesNotContained.size() == 0 );
392 }
393 
394 //=========================================================================
395 void ContentProperties::addProperties(
396                                 const std::vector< rtl::OUString > & rProps,
397                                 const ContentProperties & rContentProps )
398 {
399     std::vector< rtl::OUString >::const_iterator it  = rProps.begin();
400     std::vector< rtl::OUString >::const_iterator end = rProps.end();
401 
402     while ( it != end )
403     {
404         const rtl::OUString & rName = (*it);
405 
406         if ( !contains( rName ) ) // ignore duplicates
407         {
408             const PropertyValue * pProp = rContentProps.get( rName );
409             if ( pProp )
410             {
411                 // Add it.
412                 addProperty( rName, pProp->value(), pProp->isCaseSensitive() );
413             }
414             else
415             {
416                 addProperty( rName, uno::Any(), false );
417             }
418         }
419         ++it;
420     }
421 }
422 
423 //=========================================================================
424 void ContentProperties::addProperties( const ContentProperties & rProps )
425 {
426     PropertyValueMap::const_iterator it = rProps.m_xProps->begin();
427     const PropertyValueMap::const_iterator end = rProps.m_xProps->end();
428 
429     while ( it != end )
430     {
431         addProperty(
432             (*it).first, (*it).second.value(), (*it).second.isCaseSensitive() );
433         ++it;
434     }
435 }
436 
437 //=========================================================================
438 void ContentProperties::addProperties(
439     const std::vector< DAVPropertyValue > & rProps )
440 {
441     std::vector< DAVPropertyValue >::const_iterator it  = rProps.begin();
442     const std::vector< DAVPropertyValue >::const_iterator end = rProps.end();
443 
444     while ( it != end )
445     {
446         addProperty( (*it) );
447         ++it;
448     }
449 }
450 
451 //=========================================================================
452 void ContentProperties::addProperty( const DAVPropertyValue & rProp )
453 {
454     addProperty( rProp.Name, rProp.Value, rProp.IsCaseSensitive );
455 }
456 
457 //=========================================================================
458 void ContentProperties::addProperty( const rtl::OUString & rName,
459                                      const com::sun::star::uno::Any & rValue,
460                                      bool bIsCaseSensitive )
461 {
462     if ( rName.equals( DAVProperties::CREATIONDATE ) )
463     {
464         // Map DAV:creationdate to UCP:DateCreated
465         rtl::OUString aValue;
466         rValue >>= aValue;
467         util::DateTime aDate;
468         DateTimeHelper::convert( aValue, aDate );
469 
470         (*m_xProps)[ rtl::OUString::createFromAscii( "DateCreated" ) ]
471             = PropertyValue( uno::makeAny( aDate ), true );
472     }
473     //  else if ( rName.equals( DAVProperties::DISPLAYNAME ) )
474     //  {
475     //  }
476     //  else if ( rName.equals( DAVProperties::GETCONTENTLANGUAGE ) )
477     //  {
478     //  }
479     else if ( rName.equals( DAVProperties::GETCONTENTLENGTH ) )
480     {
481         // Map DAV:getcontentlength to UCP:Size
482         rtl::OUString aValue;
483         rValue >>= aValue;
484 
485         (*m_xProps)[ rtl::OUString::createFromAscii( "Size" ) ]
486             = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
487     }
488     else if ( rName.equalsAsciiL(
489                 RTL_CONSTASCII_STRINGPARAM( "Content-Length" ) ) )
490     {
491         // Do NOT map Content-Lenght entity header to DAV:getcontentlength!
492         // Only DAV resources have this property.
493 
494         // Map Content-Length entity header to UCP:Size
495         rtl::OUString aValue;
496         rValue >>= aValue;
497 
498         (*m_xProps)[ rtl::OUString::createFromAscii( "Size" ) ]
499             = PropertyValue( uno::makeAny( aValue.toInt64() ), true );
500     }
501     else if ( rName.equals( DAVProperties::GETCONTENTTYPE ) )
502     {
503         // Map DAV:getcontenttype to UCP:MediaType (1:1)
504         (*m_xProps)[ rtl::OUString::createFromAscii( "MediaType" ) ]
505             = PropertyValue( rValue, true );
506     }
507     else if ( rName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Content-Type" ) ) )
508     {
509         // Do NOT map Content-Type entity header to DAV:getcontenttype!
510         // Only DAV resources have this property.
511 
512         // Map DAV:getcontenttype to UCP:MediaType (1:1)
513         (*m_xProps)[ rtl::OUString::createFromAscii( "MediaType" ) ]
514             = PropertyValue( rValue, true );
515     }
516     //  else if ( rName.equals( DAVProperties::GETETAG ) )
517     //  {
518     //  }
519     else if ( rName.equals( DAVProperties::GETLASTMODIFIED ) )
520     {
521         // Map the DAV:getlastmodified entity header to UCP:DateModified
522         rtl::OUString aValue;
523         rValue >>= aValue;
524         util::DateTime aDate;
525         DateTimeHelper::convert( aValue, aDate );
526 
527         (*m_xProps)[ rtl::OUString::createFromAscii( "DateModified" ) ]
528             = PropertyValue( uno::makeAny( aDate ), true );
529     }
530     else if ( rName.equalsAsciiL(
531                 RTL_CONSTASCII_STRINGPARAM( "Last-Modified" ) ) )
532     {
533         // Do not map Last-Modified entity header to DAV:getlastmodified!
534         // Only DAV resources have this property.
535 
536         // Map the Last-Modified entity header to UCP:DateModified
537         rtl::OUString aValue;
538         rValue >>= aValue;
539         util::DateTime aDate;
540         DateTimeHelper::convert( aValue, aDate );
541 
542         (*m_xProps)[ rtl::OUString::createFromAscii( "DateModified" ) ]
543             = PropertyValue( uno::makeAny( aDate ), true );
544     }
545     //  else if ( rName.equals( DAVProperties::LOCKDISCOVERY ) )
546     //  {
547     //  }
548     else if ( rName.equals( DAVProperties::RESOURCETYPE ) )
549     {
550         rtl::OUString aValue;
551         rValue >>= aValue;
552 
553         // Map DAV:resourceype to UCP:IsFolder, UCP:IsDocument, UCP:ContentType
554         sal_Bool bFolder =
555             aValue.equalsIgnoreAsciiCaseAsciiL(
556                 RTL_CONSTASCII_STRINGPARAM( "collection" ) );
557 
558         (*m_xProps)[ rtl::OUString::createFromAscii( "IsFolder" ) ]
559             = PropertyValue( uno::makeAny( bFolder ), true );
560         (*m_xProps)[ rtl::OUString::createFromAscii( "IsDocument" ) ]
561             = PropertyValue( uno::makeAny( sal_Bool( !bFolder ) ), true );
562         (*m_xProps)[ rtl::OUString::createFromAscii( "ContentType" ) ]
563             = PropertyValue( uno::makeAny( bFolder
564                 ? rtl::OUString::createFromAscii( WEBDAV_COLLECTION_TYPE )
565                 : rtl::OUString::createFromAscii( WEBDAV_CONTENT_TYPE ) ), true );
566     }
567     //  else if ( rName.equals( DAVProperties::SOURCE ) )
568     //  {
569     //  }
570     //  else if ( rName.equals( DAVProperties::SUPPORTEDLOCK ) )
571     //  {
572     //  }
573 
574     // Save property.
575     (*m_xProps)[ rName ] = PropertyValue( rValue, bIsCaseSensitive );
576 }
577 
578 //=========================================================================
579 //=========================================================================
580 //
581 // CachableContentProperties Implementation.
582 //
583 //=========================================================================
584 //=========================================================================
585 
586 namespace
587 {
588     bool isCachable( rtl::OUString const & rName,
589                      bool isCaseSensitive )
590     {
591         static rtl::OUString aNonCachableProps [] =
592         {
593             DAVProperties::LOCKDISCOVERY,
594 
595             DAVProperties::GETETAG,
596             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ETag" ) ),
597 
598             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DateModified" ) ),
599             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Last-Modified" ) ),
600             DAVProperties::GETLASTMODIFIED,
601 
602             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Size" ) ),
603             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Content-Length" ) ),
604             DAVProperties::GETCONTENTLENGTH,
605 
606             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Date" ) )
607         };
608 
609         for ( sal_uInt32 n = 0;
610               n <  ( sizeof( aNonCachableProps )
611                      / sizeof( aNonCachableProps[ 0 ] ) );
612               ++n )
613         {
614             if ( isCaseSensitive )
615             {
616                 if ( rName.equals( aNonCachableProps[ n ] ) )
617                     return false;
618             }
619             else
620                 if ( rName.equalsIgnoreAsciiCase( aNonCachableProps[ n ] ) )
621                     return false;
622         }
623         return true;
624     }
625 
626 } // namespace
627 
628 //=========================================================================
629 CachableContentProperties::CachableContentProperties(
630     const ContentProperties & rProps )
631 {
632     addProperties( rProps );
633 }
634 
635 //=========================================================================
636 void CachableContentProperties::addProperties(
637     const ContentProperties & rProps )
638 {
639     const std::auto_ptr< PropertyValueMap > & props = rProps.getProperties();
640 
641     PropertyValueMap::const_iterator it = props->begin();
642     const PropertyValueMap::const_iterator end = props->end();
643 
644     while ( it != end )
645     {
646         if ( isCachable( (*it).first, (*it).second.isCaseSensitive() ) )
647             m_aProps.addProperty( (*it).first,
648                                   (*it).second.value(),
649                                   (*it).second.isCaseSensitive() );
650 
651         ++it;
652     }
653 }
654 
655 //=========================================================================
656 void CachableContentProperties::addProperties(
657     const std::vector< DAVPropertyValue > & rProps )
658 {
659     std::vector< DAVPropertyValue >::const_iterator it  = rProps.begin();
660     const std::vector< DAVPropertyValue >::const_iterator end = rProps.end();
661 
662     while ( it != end )
663     {
664         if ( isCachable( (*it).Name, (*it).IsCaseSensitive ) )
665             m_aProps.addProperty( (*it) );
666 
667         ++it;
668      }
669 }
670