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