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 #include <string.h>
32 #include <ne_xml.h>
33 #include <osl/diagnose.h>
34 #include <rtl/ustrbuf.hxx>
35 #include "UCBDeadPropertyValue.hxx"
36 
37 using namespace webdav_ucp;
38 using namespace com::sun::star;
39 
40 //////////////////////////////////////////////////////////////////////////
41 
42 struct UCBDeadPropertyValueParseContext
43 {
44     rtl::OUString * pType;
45     rtl::OUString * pValue;
46 
47     UCBDeadPropertyValueParseContext() : pType( 0 ), pValue( 0 ) {}
48     ~UCBDeadPropertyValueParseContext() { delete pType; delete pValue; }
49 };
50 
51 // static
52 const rtl::OUString UCBDeadPropertyValue::aTypeString
53     = rtl::OUString::createFromAscii( "string" );
54 const rtl::OUString UCBDeadPropertyValue::aTypeLong
55     = rtl::OUString::createFromAscii( "long" );
56 const rtl::OUString UCBDeadPropertyValue::aTypeShort
57     = rtl::OUString::createFromAscii( "short" );
58 const rtl::OUString UCBDeadPropertyValue::aTypeBoolean
59     = rtl::OUString::createFromAscii( "boolean" );
60 const rtl::OUString UCBDeadPropertyValue::aTypeChar
61     = rtl::OUString::createFromAscii( "char" );
62 const rtl::OUString UCBDeadPropertyValue::aTypeByte
63     = rtl::OUString::createFromAscii( "byte" );
64 const rtl::OUString UCBDeadPropertyValue::aTypeHyper
65     = rtl::OUString::createFromAscii( "hyper" );
66 const rtl::OUString UCBDeadPropertyValue::aTypeFloat
67     = rtl::OUString::createFromAscii( "float" );
68 const rtl::OUString UCBDeadPropertyValue::aTypeDouble
69     = rtl::OUString::createFromAscii( "double" );
70 
71 // static
72 const rtl::OUString UCBDeadPropertyValue::aXMLPre
73     = rtl::OUString::createFromAscii( "<ucbprop><type>" );
74 const rtl::OUString UCBDeadPropertyValue::aXMLMid
75     = rtl::OUString::createFromAscii( "</type><value>" );
76 const rtl::OUString UCBDeadPropertyValue::aXMLEnd
77     = rtl::OUString::createFromAscii( "</value></ucbprop>" );
78 
79 #define STATE_TOP (1)
80 
81 #define STATE_UCBPROP   (STATE_TOP)
82 #define STATE_TYPE      (STATE_TOP + 1)
83 #define STATE_VALUE     (STATE_TOP + 2)
84 
85 //////////////////////////////////////////////////////////////////////////
86 extern "C" int UCBDeadPropertyValue_startelement_callback(
87     void *,
88     int parent,
89     const char * /*nspace*/,
90     const char *name,
91     const char ** )
92 {
93     if ( name != 0 )
94     {
95         switch ( parent )
96         {
97             case NE_XML_STATEROOT:
98                 if ( strcmp( name, "ucbprop" ) == 0 )
99                     return STATE_UCBPROP;
100                 break;
101 
102             case STATE_UCBPROP:
103                 if ( strcmp( name, "type" ) == 0 )
104                     return STATE_TYPE;
105                 else if ( strcmp( name, "value" ) == 0 )
106                     return STATE_VALUE;
107                 break;
108         }
109     }
110     return NE_XML_DECLINE;
111 }
112 
113 //////////////////////////////////////////////////////////////////////////
114 extern "C" int UCBDeadPropertyValue_chardata_callback(
115     void *userdata,
116     int state,
117     const char *buf,
118     size_t len )
119 {
120     UCBDeadPropertyValueParseContext * pCtx
121             = static_cast< UCBDeadPropertyValueParseContext * >( userdata );
122 
123     switch ( state )
124     {
125         case STATE_TYPE:
126             OSL_ENSURE( !pCtx->pType,
127                         "UCBDeadPropertyValue_endelement_callback - "
128                         "Type already set!" );
129             pCtx->pType
130                 = new rtl::OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
131             break;
132 
133         case STATE_VALUE:
134             OSL_ENSURE( !pCtx->pValue,
135                         "UCBDeadPropertyValue_endelement_callback - "
136                         "Value already set!" );
137             pCtx->pValue
138                 = new rtl::OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
139             break;
140     }
141     return 0; // zero to continue, non-zero to abort parsing
142 }
143 
144 //////////////////////////////////////////////////////////////////////////
145 extern "C" int UCBDeadPropertyValue_endelement_callback(
146     void *userdata,
147     int state,
148     const char *,
149     const char * )
150 {
151     UCBDeadPropertyValueParseContext * pCtx
152             = static_cast< UCBDeadPropertyValueParseContext * >( userdata );
153 
154     switch ( state )
155     {
156         case STATE_TYPE:
157             if ( !pCtx->pType )
158                 return 1; // abort
159             break;
160 
161         case STATE_VALUE:
162             if ( !pCtx->pValue )
163                 return 1; // abort
164             break;
165 
166         case STATE_UCBPROP:
167             if ( !pCtx->pType || ! pCtx->pValue )
168                 return 1; // abort
169             break;
170     }
171     return 0; // zero to continue, non-zero to abort parsing
172 }
173 
174 //////////////////////////////////////////////////////////////////////////
175 static rtl::OUString encodeValue( const rtl::OUString & rValue )
176 {
177     // Note: I do not use the usual &amp; + &lt; + &gt; encoding, because
178     //       I want to prevent any XML parser from trying to 'understand'
179     //       the value. This caused problems:
180     //
181     //       Example:
182     //       - Unencoded property value: x<z
183     //       PROPPATCH:
184     //       - Encoded property value: x&lt;z
185     //       - UCBDeadPropertyValue::toXML result:
186     //              <ucbprop><type>string</type><value>x&lt;z</value></ucbprop>
187     //       PROPFIND:
188     //       - parser replaces &lt; by > ==> error (not well formed)
189 
190     rtl::OUStringBuffer aResult;
191     const sal_Unicode * pValue = rValue.getStr();
192 
193     sal_Int32 nCount = rValue.getLength();
194     for ( sal_Int32 n = 0; n < nCount; ++n )
195     {
196         const sal_Unicode c = pValue[ n ];
197 
198         if ( '%' == c )
199             aResult.appendAscii( "%per;" );
200         else if ( '<' == c )
201             aResult.appendAscii( "%lt;" );
202         else if ( '>' == c )
203             aResult.appendAscii( "%gt;" );
204         else
205             aResult.append( c );
206     }
207     return rtl::OUString( aResult );
208 }
209 
210 //////////////////////////////////////////////////////////////////////////
211 static rtl::OUString decodeValue( const rtl::OUString & rValue )
212 {
213     rtl::OUStringBuffer aResult;
214     const sal_Unicode * pValue = rValue.getStr();
215 
216     sal_Int32 nPos = 0;
217     sal_Int32 nEnd = rValue.getLength();
218 
219     while ( nPos < nEnd )
220     {
221         sal_Unicode c = pValue[ nPos ];
222 
223         if ( '%' == c )
224         {
225             nPos++;
226 
227             if ( nPos == nEnd )
228             {
229                 OSL_ENSURE( sal_False,
230                     "UCBDeadPropertyValue::decodeValue - syntax error!" );
231                 return rtl::OUString();
232             }
233 
234             c = pValue[ nPos ];
235 
236             if ( 'p' == c )
237             {
238                 // %per;
239 
240                 if ( nPos > nEnd - 4 )
241                 {
242                     OSL_ENSURE( sal_False,
243                         "UCBDeadPropertyValue::decodeValue - syntax error!" );
244                     return rtl::OUString();
245                 }
246 
247                 if ( ( 'e' == pValue[ nPos + 1 ] )
248                      &&
249                      ( 'r' == pValue[ nPos + 2 ] )
250                      &&
251                      ( ';' == pValue[ nPos + 3 ] ) )
252                 {
253                     aResult.append( sal_Unicode( '%' ) );
254                     nPos += 3;
255                 }
256                 else
257                 {
258                     OSL_ENSURE( sal_False,
259                         "UCBDeadPropertyValue::decodeValue - syntax error!" );
260                     return rtl::OUString();
261                 }
262             }
263             else if ( 'l' == c )
264             {
265                 // %lt;
266 
267                 if ( nPos > nEnd - 3 )
268                 {
269                     OSL_ENSURE( sal_False,
270                         "UCBDeadPropertyValue::decodeValue - syntax error!" );
271                     return rtl::OUString();
272                 }
273 
274                 if ( ( 't' == pValue[ nPos + 1 ] )
275                      &&
276                      ( ';' == pValue[ nPos + 2 ] ) )
277                 {
278                     aResult.append( sal_Unicode( '<' ) );
279                     nPos += 2;
280                 }
281                 else
282                 {
283                     OSL_ENSURE( sal_False,
284                         "UCBDeadPropertyValue::decodeValue - syntax error!" );
285                     return rtl::OUString();
286                 }
287             }
288             else if ( 'g' == c )
289             {
290                 // %gt;
291 
292                 if ( nPos > nEnd - 3 )
293                 {
294                     OSL_ENSURE( sal_False,
295                         "UCBDeadPropertyValue::decodeValue - syntax error!" );
296                     return rtl::OUString();
297                 }
298 
299                 if ( ( 't' == pValue[ nPos + 1 ] )
300                      &&
301                      ( ';' == pValue[ nPos + 2 ] ) )
302                 {
303                     aResult.append( sal_Unicode( '>' ) );
304                     nPos += 2;
305                 }
306                 else
307                 {
308                     OSL_ENSURE( sal_False,
309                         "UCBDeadPropertyValue::decodeValue - syntax error!" );
310                     return rtl::OUString();
311                 }
312             }
313             else
314             {
315                 OSL_ENSURE( sal_False,
316                     "UCBDeadPropertyValue::decodeValue - syntax error!" );
317                 return rtl::OUString();
318             }
319         }
320         else
321             aResult.append( c );
322 
323         nPos++;
324     }
325 
326     return rtl::OUString( aResult );
327 }
328 
329 //////////////////////////////////////////////////////////////////////////
330 // static
331 bool UCBDeadPropertyValue::supportsType( const uno::Type & rType )
332 {
333     if ( ( rType != getCppuType( static_cast< const rtl::OUString * >( 0 ) ) )
334          &&
335          ( rType != getCppuType( static_cast< const sal_Int32 * >( 0 ) ) )
336          &&
337          ( rType != getCppuType( static_cast< const sal_Int16 * >( 0 ) ) )
338          &&
339          ( rType != getCppuBooleanType() )
340          &&
341          ( rType != getCppuCharType() )
342          &&
343          ( rType != getCppuType( static_cast< const sal_Int8 * >( 0 ) ) )
344          &&
345          ( rType != getCppuType( static_cast< const sal_Int64 * >( 0 ) ) )
346          &&
347          ( rType != getCppuType( static_cast< const float * >( 0 ) ) )
348          &&
349          ( rType != getCppuType( static_cast< const double * >( 0 ) ) ) )
350     {
351         return false;
352     }
353 
354     return true;
355 }
356 
357 //////////////////////////////////////////////////////////////////////////
358 // static
359 bool UCBDeadPropertyValue::createFromXML( const rtl::OString & rInData,
360                                           uno::Any & rOutData )
361 {
362     bool success = false;
363 
364     ne_xml_parser * parser = ne_xml_create();
365     if ( parser )
366     {
367         UCBDeadPropertyValueParseContext aCtx;
368         ne_xml_push_handler( parser,
369                              UCBDeadPropertyValue_startelement_callback,
370                              UCBDeadPropertyValue_chardata_callback,
371                              UCBDeadPropertyValue_endelement_callback,
372                              &aCtx );
373 
374         ne_xml_parse( parser, rInData.getStr(), rInData.getLength() );
375 
376         success = !ne_xml_failed( parser );
377 
378         ne_xml_destroy( parser );
379 
380         if ( success )
381         {
382             if ( aCtx.pType && aCtx.pValue )
383             {
384                 // Decode aCtx.pValue! It may contain XML reserved chars.
385                 rtl::OUString aStringValue = decodeValue( *aCtx.pValue );
386                 if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeString ) )
387                 {
388                     rOutData <<= aStringValue;
389                 }
390                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeLong ) )
391                 {
392                     rOutData <<= aStringValue.toInt32();
393                 }
394                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeShort ) )
395                 {
396                     rOutData <<= sal_Int16( aStringValue.toInt32() );
397                 }
398                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeBoolean ) )
399                 {
400                     if ( aStringValue.equalsIgnoreAsciiCase(
401                             rtl::OUString::createFromAscii( "true" ) ) )
402                         rOutData <<= sal_Bool( sal_True );
403                     else
404                         rOutData <<= sal_Bool( sal_False );
405                 }
406                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeChar ) )
407                 {
408                     rOutData <<= aStringValue.toChar();
409                 }
410                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeByte ) )
411                 {
412                     rOutData <<= sal_Int8( aStringValue.toChar() );
413                 }
414                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeHyper ) )
415                 {
416                     rOutData <<= aStringValue.toInt64();
417                 }
418                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeFloat ) )
419                 {
420                     rOutData <<= aStringValue.toFloat();
421                 }
422                 else if ( aCtx.pType->equalsIgnoreAsciiCase( aTypeDouble ) )
423                 {
424                     rOutData <<= aStringValue.toDouble();
425                 }
426                 else
427                 {
428                     OSL_ENSURE( sal_False,
429                                 "UCBDeadPropertyValue::createFromXML - "
430                                 "Unsupported property type!" );
431                     success = false;
432                 }
433             }
434             else
435                 success = false;
436         }
437     }
438 
439     return success;
440 }
441 
442 //////////////////////////////////////////////////////////////////////////
443 // static
444 bool UCBDeadPropertyValue::toXML( const uno::Any & rInData,
445                                   rtl::OUString & rOutData )
446 {
447     // <ucbprop><type>the_type</type><value>the_value</value></ucbprop>
448 
449     // Check property type. Extract type and value as string.
450 
451     const uno::Type& rType = rInData.getValueType();
452     rtl::OUString aStringValue;
453     rtl::OUString aStringType;
454 
455     if ( rType == getCppuType( static_cast< const rtl::OUString * >( 0 ) ) )
456     {
457         // string
458         rInData >>= aStringValue;
459         aStringType = aTypeString;
460     }
461     else if ( rType == getCppuType( static_cast< const sal_Int32 * >( 0 ) ) )
462     {
463         // long
464         sal_Int32 nValue = 0;
465         rInData >>= nValue;
466         aStringValue = rtl::OUString::valueOf( nValue );
467         aStringType = aTypeLong;
468     }
469     else if ( rType == getCppuType( static_cast< const sal_Int16 * >( 0 ) ) )
470     {
471         // short
472         sal_Int32 nValue = 0;
473         rInData >>= nValue;
474         aStringValue = rtl::OUString::valueOf( nValue );
475         aStringType = aTypeShort;
476     }
477     else if ( rType == getCppuBooleanType() )
478     {
479         // boolean
480         sal_Bool bValue = false;
481         rInData >>= bValue;
482         aStringValue = rtl::OUString::valueOf( bValue );
483         aStringType = aTypeBoolean;
484     }
485     else if ( rType == getCppuCharType() )
486     {
487         // char
488         sal_Unicode cValue = 0;
489         rInData >>= cValue;
490         aStringValue = rtl::OUString::valueOf( cValue );
491         aStringType = aTypeChar;
492     }
493     else if ( rType == getCppuType( static_cast< const sal_Int8 * >( 0 ) ) )
494     {
495         // byte
496         sal_Int8 nValue = 0;
497         rInData >>= nValue;
498         aStringValue = rtl::OUString::valueOf( sal_Unicode( nValue ) );
499         aStringType = aTypeByte;
500     }
501     else if ( rType == getCppuType( static_cast< const sal_Int64 * >( 0 ) ) )
502     {
503         // hyper
504         sal_Int64 nValue = 0;
505         rInData >>= nValue;
506         aStringValue = rtl::OUString::valueOf( nValue );
507         aStringType = aTypeHyper;
508     }
509     else if ( rType == getCppuType( static_cast< const float * >( 0 ) ) )
510     {
511         // float
512         float nValue = 0;
513         rInData >>= nValue;
514         aStringValue = rtl::OUString::valueOf( nValue );
515         aStringType = aTypeFloat;
516     }
517     else if ( rType == getCppuType( static_cast< const double * >( 0 ) ) )
518     {
519         // double
520         double nValue = 0;
521         rInData >>= nValue;
522         aStringValue = rtl::OUString::valueOf( nValue );
523         aStringType = aTypeDouble;
524     }
525     else
526     {
527         OSL_ENSURE( sal_False,
528                     "UCBDeadPropertyValue::toXML - "
529                     "Unsupported property type!" );
530         return false;
531     }
532 
533     // Encode value! It must not contain XML reserved chars!
534     aStringValue = encodeValue( aStringValue );
535 
536         rOutData =  aXMLPre;
537     rOutData += aStringType;
538     rOutData += aXMLMid;
539     rOutData += aStringValue;
540     rOutData += aXMLEnd;
541 
542     return true;
543 }
544