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