1 /*
2  * Copyright (c) 2000-2006 All rights reserved,
3  *       Alberto Reggiori <areggiori@webweaving.org>,
4  *       Dirk-Willem van Gulik <dirkx@webweaving.org>.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if any, must
18  * include the following acknowledgment: "This product includes software
19  * developed by Alberto Reggiori <areggiori@webweaving.org> and Dirk-Willem
20  * van Gulik <dirkx@webweaving.org>." Alternately, this acknowledgment may
21  * appear in the software itself, if and wherever such third-party
22  * acknowledgments normally appear.
23  *
24  * 4. All advertising materials mentioning features or use of this software must
25  * display the following acknowledgement: This product includes software
26  * developed by the University of California, Berkeley and its contributors.
27  *
28  * 5. Neither the name of the University nor the names of its contributors may
29  * be used to endorse or promote products derived from this software without
30  * specific prior written permission.
31  *
32  * 6. Products derived from this software may not be called "RDFStore" nor may
33  * "RDFStore" appear in their names without prior written permission.
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
42  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  * SUCH DAMAGE.
46  *
47  * ====================================================================
48  *
49  * This software consists of work developed by Alberto Reggiori and Dirk-Willem
50  * van Gulik. The RDF specific part is based based on public domain software
51  * written at the Stanford University Database Group by Sergey Melnik. For
52  * more information on the RDF API Draft work, please see
53  * <http://www-db.stanford.edu/~melnik/rdf/api.html> The DBMS TCP/IP server
54  * part is based on software originally written by Dirk-Willem van Gulik for
55  * Web Weaving Internet Engineering m/v Enschede, The Netherlands.
56  *
57  */
58 
59 #if !defined(WIN32)
60 #include <sys/param.h>
61 #endif
62 
63 #include <sys/types.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <errno.h>
67 #include <strings.h>
68 #include <fcntl.h>
69 
70 #include <time.h>
71 #include <sys/stat.h>
72 
73 #include "rdfstore_log.h"
74 #include "rdfstore_xsd.h"
75 
76 /*
77 #define MX	{ printf(" MX %s:%d - %p\n",__FILE__,__LINE__,me->nindex->free); }
78 */
79 
80 /*
81  * #define RDFSTORE_XSD_DEBUG
82  */
83 
84 time_t _rdfstore_xsd_mktime(const struct tm * t);
85 
rdfstore_xsd_serialize_decimal(const double value,char * result)86 void rdfstore_xsd_serialize_decimal( const double value, char * result ) {
87 	sprintf( result, RDFSTORE_XSD_DECIMAL_FORMAT, value );
88 	};
89 
rdfstore_xsd_deserialize_decimal(const char * string,double * val)90 int rdfstore_xsd_deserialize_decimal( const char * string, double * val ) {
91 	return rdfstore_xsd_deserialize_double( string, (double *) val );
92 	};
93 
rdfstore_xsd_serialize_float(const float value,char * result)94 void rdfstore_xsd_serialize_float( const float value, char * result ) {
95 	sprintf( result, RDFSTORE_XSD_FLOAT_FORMAT, value );
96 	};
97 
rdfstore_xsd_deserialize_float(const char * string,float * val)98 int rdfstore_xsd_deserialize_float( const char * string, float * val ) {
99 	return rdfstore_xsd_deserialize_double( string, (double *) val );
100 	};
101 
rdfstore_xsd_serialize_double(const double value,char * result)102 void rdfstore_xsd_serialize_double( const double value, char * result ) {
103 	sprintf( result, RDFSTORE_XSD_DOUBLE_FORMAT, value );
104 	};
105 
106 /*
107    parse a char string as double/float if possible and returns it
108 
109    NOTEs:
110 		strings like <foo:prop>   123.333344477 foo bar</foo:prop> are not considered numbers while
111 	 	strings like <foo:prop>
112 				123.333344477
113 					</foo:prop> are valid numbers
114 */
rdfstore_xsd_deserialize_double(const char * string,double * val)115 int rdfstore_xsd_deserialize_double( const char * string, double * val ) {
116 	char *endptr;
117 
118 	if (string == NULL) {
119 		return 0;
120 		};
121 
122 	*val = (double) strtod(string, &endptr);
123 
124 	if (endptr > string) { /* if a conversion was made */
125 		/* check if we really got a number or a literal/string... */
126 		while ( *endptr ) {
127 			if ( isspace(*endptr) == 0 )
128 				return 0;
129 			endptr++;
130 			};
131 
132 		if( errno == ERANGE )
133 			return 0;
134 
135 		return 1;
136 		};
137 
138 	return 0;
139 	};
140 
rdfstore_xsd_serialize_integer(const long value,char * result)141 void rdfstore_xsd_serialize_integer( const long value, char * result ) {
142 	sprintf( result, RDFSTORE_XSD_INTEGER_FORMAT, value );
143 	};
144 
rdfstore_xsd_deserialize_integer(const char * string,long * val)145 int rdfstore_xsd_deserialize_integer( const char * string, long * val ) {
146 	char *endptr;
147 
148 	if (string == NULL) {
149 		return 0;
150 		};
151 
152 	/* strtod should trim the string itself... */
153 	*val = (long) strtol(string, &endptr, 10); /* base 10 or should be any base '0' ? */
154 
155 	if (endptr > string) { /* if a conversion was made */
156 		/* check if we really got a number or a literal/string... */
157 		while ( *endptr ) {
158 			if ( isspace(*endptr) == 0 )
159 				return 0;
160 			endptr++;
161 			};
162 
163 		if( errno == ERANGE )
164 			return 0;
165 
166 		return 1;
167 		};
168 
169 	return 0;
170 	};
171 
rdfstore_xsd_serialize_date(const struct tm value,char * result)172 void rdfstore_xsd_serialize_date( const struct tm value, char * result ) {
173 	strftime( result, RDFSTORE_XSD_DATE_FORMAT_SIZE, "%Y-%m-%dZ", &value );
174 
175 #ifdef RDFSTORE_XSD_DEBUG
176 	printf("PROCESSED SUCCESSFULY DATE '%s'\n", result);
177 #endif
178 	};
179 
rdfstore_xsd_deserialize_date(const char * string,struct tm * val)180 int rdfstore_xsd_deserialize_date( const char * string, struct tm * val ) {
181 	char * ptr=(char*)string;
182 	char * ptr1=(char*)(string+strlen(string)-1);
183 	char * tzsign;
184 	char * temp;
185 	char * temp2;
186 	int status=0;
187         unsigned int len;
188 	time_t now;
189 	time_t timestamp;
190 	struct tm* ptm;
191 	struct tm t1;
192 	struct tm t2;
193 	time_t d;
194 
195 	bzero(val, sizeof( struct tm ) );
196 
197 	if (string == NULL) {
198 		return 0;
199 		};
200 
201 	time(&now);
202 
203 	ptm = gmtime(&now);
204 	memcpy(&t1, ptm, sizeof(struct tm));
205 
206 	ptm = localtime(&now);
207 	memcpy(&t2, ptm, sizeof(struct tm));
208 
209 	d = _rdfstore_xsd_mktime(&t1) - _rdfstore_xsd_mktime(&t2); /* carry out the difference in second between local and UTC */
210 
211         if (d == -1) {
212 		return 0;
213 		};
214 
215 	/* trim the value */
216 	while(	( ptr <= (string + strlen(string) ) ) &&
217 		( (*ptr == ' ' ) || ( *ptr == '\n' ) || ( *ptr == '\r' ) || ( *ptr == '\f' ) || ( *ptr == '\t' ) ) ) {
218 		ptr++;
219 		};
220 	while(	( ptr1 > ptr ) &&
221 		( (*ptr1 == ' ' ) || ( *ptr1 == '\n' ) || ( *ptr1 == '\r' ) || ( *ptr1 == '\f' ) || ( *ptr1 == '\t' ) ) ) {
222 		ptr1--;
223 		};
224 
225 	/* primitive date parsing... */
226 
227 	/* this expression should cover xsd:date and xsd:dateTime - see http://www.w3.org/TR/xmlschema-2/#date and http://www.w3.org/TR/xmlschema-2/#dateTime */
228 	/* date ::= '-'? yyyy '-' mm '-' dd ((('+' | '-') hh ':' mm) | 'Z')? */
229 
230 	if(	sscanf( ptr, "%d-%02d-%02d", &val->tm_year,
231 				&val->tm_mon, &val->tm_mday ) != 3 ) {
232 		return 0;
233                 };
234 
235 	val->tm_year -= 1900;
236         val->tm_mon--;
237 	val->tm_hour = 0;
238         val->tm_min = 0;
239         val->tm_sec = 0;
240         val->tm_isdst = -1;
241 #if !defined(WIN32) && !defined(AIX) && !defined( __OS400__ ) && !defined(__sun)
242 	val->tm_zone = NULL;
243 	val->tm_gmtoff = -1;
244 #endif
245 
246 	temp2 = strpbrk(ptr, ":");
247 
248         if( ( temp = strpbrk( ptr, "Z") ) != NULL ) { /* got canonical UTC date */
249 		time_t tt = _rdfstore_xsd_mktime(val);
250 		if( temp != ptr1 ) {
251 			return 0;
252 			};
253 
254 		if (tt == -1) {
255 			return 0;
256 			};
257 		ptm = localtime (&tt);
258 	} else if( temp2 != NULL ) { /* ok now we need to normalize +/-hh:mm timezone to UTC - hehheee! */
259             	int hours = 0;
260 		int minutes = 0;
261 		int secs;
262 		time_t t;
263 
264 		tzsign = strrchr(ptr, '+');
265 
266 		if (tzsign == NULL) {
267 			tzsign = strrchr(ptr, '-');
268 			};
269 
270 		if( *(tzsign-3) != '-' ) {
271 			return 0;
272 			};
273 
274 		timestamp = _rdfstore_xsd_mktime(val);
275 		if ( timestamp == -1 ) {
276 			return 0;
277             		};
278 
279 		if( sscanf( tzsign+1, "%02d:%02d", &hours, &minutes) != 2 ) {
280 			return 0;
281 			};
282 
283 		secs = hours * 60 * 60 + minutes * 60;
284 		if( (temp = strpbrk(tzsign, "+")) != NULL ) {
285 			timestamp += secs;
286 		} else {
287 			timestamp -= secs;
288 			};
289 
290 		ptm = localtime(&timestamp);
291 		memcpy(val, ptm, sizeof(struct tm));
292 		t = _rdfstore_xsd_mktime(val);
293 		if( t == -1 ) {
294 			return 0;
295 			};
296 
297 		t = labs(t - d);
298 		ptm = gmtime(&t);
299 	} else { /*else it is assumed that the sent time is localtime */
300 		if(	( *ptr1 < 48 ) ||
301 			( *ptr1 > 57 ) ||
302 			( *(ptr1-2) != '-' ) ) {
303 			return 0;
304 			};
305 
306 		timestamp = _rdfstore_xsd_mktime(val);
307 		if( timestamp == -1 ) {
308 			return 0;
309 			};
310 
311 		ptm = gmtime(&timestamp);
312 		};
313 
314 	if( ptm!= NULL ) {
315 #ifdef RDFSTORE_XSD_DEBUG
316 		printf("rdfstore_xsd_deserialize_date( '%s' ) is a valid date\n", ptr);
317 #endif
318 
319 		return 1;
320 	} else {
321 #ifdef RDFSTORE_XSD_DEBUG
322 		printf("rdfstore_xsd_deserialize_date( '%s' ) is NOT a valid date\n", ptr);
323 #endif
324 
325 		return 0;
326 		};
327 	};
328 
rdfstore_xsd_serialize_dateTime(const struct tm value,char * result)329 void rdfstore_xsd_serialize_dateTime( const struct tm value, char * result ) {
330 	strftime( result, RDFSTORE_XSD_DATETIME_FORMAT_SIZE, "%Y-%m-%dT%H:%M:%SZ", &value );
331 
332 #ifdef RDFSTORE_XSD_DEBUG
333 	printf("PROCESSED SUCCESSFULY DATETIME '%s'\n", result);
334 #endif
335 	};
336 
rdfstore_xsd_deserialize_dateTime(const char * string,struct tm * val)337 int rdfstore_xsd_deserialize_dateTime( const char * string, struct tm * val ) {
338 	char * ptr=(char*)string;
339 	char * ptr1=(char*)(string+strlen(string)-1);
340 	char * tzsign;
341 	char * temp;
342 	char * temp2;
343 	char * temp3;
344 	int status=0;
345         unsigned int len;
346 	time_t now;
347 	time_t timestamp;
348 	struct tm* ptm;
349 	struct tm t1;
350 	struct tm t2;
351 	time_t d;
352 
353 	bzero(val, sizeof( struct tm ) );
354 
355 	if (string == NULL) {
356 		return 0;
357 		};
358 
359 	time(&now);
360 
361 	ptm = gmtime(&now);
362 	memcpy(&t1, ptm, sizeof(struct tm));
363 
364 	ptm = localtime(&now);
365 	memcpy(&t2, ptm, sizeof(struct tm));
366 
367 	d = _rdfstore_xsd_mktime(&t1) - _rdfstore_xsd_mktime(&t2); /* carry out the difference in second between local and UTC */
368 
369         if (d == -1) {
370 		return 0;
371 		};
372 
373 	/* trim the value */
374 	while(	( ptr <= (string + strlen(string) ) ) &&
375 		( (*ptr == ' ' ) || ( *ptr == '\n' ) || ( *ptr == '\r' ) || ( *ptr == '\f' ) || ( *ptr == '\t' ) ) ) {
376 		ptr++;
377 		};
378 	while(	( ptr1 > ptr ) &&
379 		( (*ptr1 == ' ' ) || ( *ptr1 == '\n' ) || ( *ptr1 == '\r' ) || ( *ptr1 == '\f' ) || ( *ptr1 == '\t' ) ) ) {
380 		ptr1--;
381 		};
382 
383 	/* primitive date parsing... */
384 
385 	/* this expression should cover xsd:date and xsd:dateTime - see http://www.w3.org/TR/xmlschema-2/#date and http://www.w3.org/TR/xmlschema-2/#dateTime */
386 	/* date ::= '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? ((('+' | '-') hh ':' mm) | 'Z')? */
387 
388 	if(	sscanf( ptr, "%d-%02d-%02dT%02d:%02d:%02d", &val->tm_year,
389 				&val->tm_mon, &val->tm_mday, &val->tm_hour, &val->tm_min, &val->tm_sec) != 6 ) {
390 		return 0;
391                 };
392 
393 	val->tm_year -= 1900;
394         val->tm_mon--;
395         val->tm_isdst = -1;
396 #if !defined(WIN32) && !defined(AIX) && !defined( __OS400__ ) && !defined(__sun)
397 	val->tm_zone = NULL;
398 	val->tm_gmtoff = -1;
399 #endif
400 
401 	temp2 = strpbrk(ptr, "T");
402         temp3 = strrchr(temp2, ':');
403         temp3[0] = '\0';
404         len = strlen(temp2);
405         temp3[0] = ':';
406 
407         if( ( temp = strpbrk( ptr, "Z") ) != NULL ) { /* got canonical UTC date */
408 		time_t tt = _rdfstore_xsd_mktime(val);
409 		if( temp != ptr1 ) {
410 			return 0;
411 			};
412 
413 		if (tt == -1) {
414 			return 0;
415 			};
416 		ptm = localtime (&tt);
417 	} else if( len > (sizeof(char) * 6) ) { /* ok now we need to normalize +/-hh:mm timezone to UTC - hehheee! */
418             	int hours = 0;
419 		int minutes = 0;
420 		int secs;
421 		time_t t;
422 
423 		tzsign = strpbrk (temp2, "+");
424 
425 		if (tzsign == NULL) {
426 			tzsign = strpbrk (temp2, "-");
427 			};
428 
429 		timestamp = _rdfstore_xsd_mktime(val);
430 		if ( timestamp == -1 ) {
431 			return 0;
432             		};
433 
434 		if( sscanf( tzsign+1, "%02d:%02d", &hours, &minutes) != 2 ) {
435 			return 0;
436 			};
437 
438 		secs = hours * 60 * 60 + minutes * 60;
439 		if( (temp = strpbrk(tzsign, "+")) != NULL ) {
440 			timestamp += secs;
441 		} else {
442 			timestamp -= secs;
443 			};
444 
445 		ptm = localtime(&timestamp);
446 		memcpy(val, ptm, sizeof(struct tm));
447 		t = _rdfstore_xsd_mktime(val);
448 		if( t == -1 ) {
449 			return 0;
450 			};
451 
452 		t = labs(t - d);
453 		ptm = gmtime(&t);
454 	} else { /*else it is assumed that the sent time is localtime */
455 		if(	( *ptr1 < 48 ) ||
456 			( *ptr1 > 57 ) ||
457 			( *(ptr1-2) != ':' ) ) {
458 			return 0;
459 			};
460 
461 		timestamp = _rdfstore_xsd_mktime(val);
462 		if( timestamp == -1 ) {
463 			return 0;
464 			};
465 
466 		ptm = gmtime(&timestamp);
467 		};
468 
469 	if( ptm!= NULL ) {
470 #ifdef RDFSTORE_XSD_DEBUG
471 		printf("rdfstore_xsd_deserialize_dateTime( '%s' ) is a valid date\n", ptr);
472 #endif
473 
474 		return 1;
475 	} else {
476 #ifdef RDFSTORE_XSD_DEBUG
477 		printf("rdfstore_xsd_deserialize_dateTime( '%s' ) is NOT a valid date\n", ptr);
478 #endif
479 
480 		return 0;
481 		};
482 	};
483 
rdfstore_xsd_serialize_string(const char * value,char * result)484 void rdfstore_xsd_serialize_string( const char * value, char * result ) {
485 	};
486 
487 int rdfstore_xsd_deserialize_string( const char * string, char * val );
488 
489 /* this routine is faster than standard mktime() */
_rdfstore_xsd_mktime(const struct tm * t)490 time_t _rdfstore_xsd_mktime(const struct tm * t) {
491 	int year;
492 	time_t days;
493 	static const int dayoffset[12] = {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
494 
495 	year = t->tm_year;
496 
497 	if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138)))
498 		return RDFSTORE_XSD_BAD_DATE;
499 
500 	/* shift new year to 1st March in order to make leap year calc easy */
501 
502 	if (t->tm_mon < 2)
503 		year--;
504 
505 	/* Find number of days since 1st March 1900 (in the Gregorian calendar). */
506 
507 	days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
508 	days += dayoffset[t->tm_mon] + t->tm_mday - 1;
509 	days -= 25508;              /* 1 jan 1970 is 25508 days since 1 mar 1900 */
510 
511 	days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
512 
513 	if (days < 0)
514         	return RDFSTORE_XSD_BAD_DATE;        /* must have overflowed */
515     	else
516         	return days;            /* must be a valid time */
517 	};
518 
519