2 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
5 #define _POSIX_PTHREAD_SEMANTICS /* for Sun */
6 #define _REENTRANT /* for Sun */
7 #include <asn_internal.h>
8 #include <GeneralizedTime.h>
12 #include "/usr/include/time.h"
15 #endif /* __CYGWIN__ */
18 #pragma message( "PLEASE STOP AND READ!")
19 #pragma message( " localtime_r is implemented via localtime(), which may be not thread-safe.")
20 #pragma message( " gmtime_r is implemented via gmtime(), which may be not thread-safe.")
22 #pragma message( " You must fix the code by inserting appropriate locking")
23 #pragma message( " if you want to use asn_GT2time() or asn_UT2time().")
24 #pragma message( "PLEASE STOP AND READ!")
26 static struct tm
*localtime_r(const time_t *tloc
, struct tm
*result
) {
28 if((tm
= localtime(tloc
)))
29 return memcpy(result
, tm
, sizeof(struct tm
));
33 static struct tm
*gmtime_r(const time_t *tloc
, struct tm
*result
) {
35 if((tm
= gmtime(tloc
)))
36 return memcpy(result
, tm
, sizeof(struct tm
));
40 #define tzset() _tzset()
41 #define putenv(c) _putenv(c)
42 #define _EMULATE_TIMEGM
46 #if defined(sun) || defined(_sun_) || defined(__solaris__)
47 #define _EMULATE_TIMEGM
51 * Where to look for offset from GMT, Phase I.
52 * Several platforms are known.
54 #if defined(__FreeBSD__) \
55 || (defined(__GNUC__) && defined(__APPLE_CC__)) \
56 || (defined __GLIBC__ && __GLIBC__ >= 2)
58 #define HAVE_TM_GMTOFF
59 #endif /* BSDs and newer glibc */
62 * Where to look for offset from GMT, Phase II.
65 #define GMTOFF(tm) ((tm).tm_gmtoff)
66 #else /* HAVE_TM_GMTOFF */
67 #define GMTOFF(tm) (-timezone)
68 #endif /* HAVE_TM_GMTOFF */
70 #if (defined(_EMULATE_TIMEGM) || !defined(HAVE_TM_GMTOFF))
71 #warning "PLEASE STOP AND READ!"
72 #warning " timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe."
74 #warning " You must fix the code by inserting appropriate locking"
75 #warning " if you want to use asn_GT2time() or asn_UT2time()."
76 #warning "PLEASE STOP AND READ!"
77 #endif /* _EMULATE_TIMEGM */
80 * Override our GMTOFF decision for other known platforms.
84 static long GMTOFF(struct tm a
){
86 time_t local_time
, gmt_time
;
90 gmt_time
= time (NULL
);
92 lt
= gmtime(&gmt_time
);
94 local_time
= mktime(lt
);
95 return (gmt_time
- local_time
);
97 #define _EMULATE_TIMEGM
99 #endif /* __CYGWIN__ */
101 #define ATZVARS do { \
104 #define ATZSAVETZ do { \
105 tzold = getenv("TZ"); \
107 size_t tzlen = strlen(tzold); \
108 if(tzlen < sizeof(tzoldbuf)) { \
109 tzold = memcpy(tzoldbuf, tzold, tzlen + 1); \
111 char *dupptr = tzold; \
112 tzold = MALLOC(tzlen + 1); \
113 if(tzold) memcpy(tzold, dupptr, tzlen + 1); \
115 setenv("TZ", "UTC", 1); \
119 #define ATZOLDTZ do { \
121 setenv("TZ", tzold, 1); \
123 if(tzold != tzoldbuf) \
129 } while(0); } while(0);
131 #ifdef _EMULATE_TIMEGM
132 static time_t timegm(struct tm
*tm
) {
140 #endif /* _EMULATE_TIMEGM */
143 #ifndef __ASN_INTERNAL_TEST_MODE__
146 * GeneralizedTime basic type description.
148 static ber_tlv_tag_t asn_DEF_GeneralizedTime_tags
[] = {
149 (ASN_TAG_CLASS_UNIVERSAL
| (24 << 2)), /* [UNIVERSAL 24] IMPLICIT ...*/
150 (ASN_TAG_CLASS_UNIVERSAL
| (26 << 2)), /* [UNIVERSAL 26] IMPLICIT ...*/
151 (ASN_TAG_CLASS_UNIVERSAL
| (4 << 2)) /* ... OCTET STRING */
153 static asn_per_constraints_t asn_DEF_GeneralizedTime_constraints
= {
154 { APC_CONSTRAINED
, 7, 7, 0x20, 0x7e }, /* Value */
155 { APC_SEMI_CONSTRAINED
, -1, -1, 0, 0 }, /* Size */
158 asn_TYPE_descriptor_t asn_DEF_GeneralizedTime
= {
162 GeneralizedTime_print
,
163 GeneralizedTime_constraint
, /* Check validity of time */
164 OCTET_STRING_decode_ber
, /* Implemented in terms of OCTET STRING */
165 GeneralizedTime_encode_der
,
166 OCTET_STRING_decode_xer_utf8
,
167 GeneralizedTime_encode_xer
,
168 OCTET_STRING_decode_uper
,
169 OCTET_STRING_encode_uper
,
170 0, /* Use generic outmost tag fetcher */
171 asn_DEF_GeneralizedTime_tags
,
172 sizeof(asn_DEF_GeneralizedTime_tags
)
173 / sizeof(asn_DEF_GeneralizedTime_tags
[0]) - 2,
174 asn_DEF_GeneralizedTime_tags
,
175 sizeof(asn_DEF_GeneralizedTime_tags
)
176 / sizeof(asn_DEF_GeneralizedTime_tags
[0]),
177 &asn_DEF_GeneralizedTime_constraints
,
178 0, 0, /* No members */
182 #endif /* __ASN_INTERNAL_TEST_MODE__ */
185 * Check that the time looks like the time.
188 GeneralizedTime_constraint(asn_TYPE_descriptor_t
*td
, const void *sptr
,
189 asn_app_constraint_failed_f
*ctfailcb
, void *app_key
) {
190 const GeneralizedTime_t
*st
= (const GeneralizedTime_t
*)sptr
;
193 errno
= EPERM
; /* Just an unlikely error code */
194 tloc
= asn_GT2time(st
, 0, 0);
195 if(tloc
== -1 && errno
!= EPERM
) {
196 _ASN_CTFAIL(app_key
, td
, sptr
,
197 "%s: Invalid time format: %s (%s:%d)",
198 td
->name
, strerror(errno
), __FILE__
, __LINE__
);
206 GeneralizedTime_encode_der(asn_TYPE_descriptor_t
*td
, void *sptr
,
207 int tag_mode
, ber_tlv_tag_t tag
,
208 asn_app_consume_bytes_f
*cb
, void *app_key
) {
209 GeneralizedTime_t
*st
= (GeneralizedTime_t
*)sptr
;
210 asn_enc_rval_t erval
;
211 int fv
, fd
; /* seconds fraction value and number of digits */
216 * Encode as a canonical DER.
219 tloc
= asn_GT2time_frac(st
, &fv
, &fd
, &tm
, 1); /* Recognize time */
220 if(tloc
== -1 && errno
!= EPERM
)
221 /* Failed to recognize time. Fail completely. */
224 st
= asn_time2GT_frac(0, &tm
, fv
, fd
, 1); /* Save time canonically */
225 if(!st
) _ASN_ENCODE_FAILED
; /* Memory allocation failure. */
227 erval
= OCTET_STRING_encode_der(td
, st
, tag_mode
, tag
, cb
, app_key
);
235 #ifndef __ASN_INTERNAL_TEST_MODE__
238 GeneralizedTime_encode_xer(asn_TYPE_descriptor_t
*td
, void *sptr
,
239 int ilevel
, enum xer_encoder_flags_e flags
,
240 asn_app_consume_bytes_f
*cb
, void *app_key
) {
242 if(flags
& XER_F_CANONICAL
) {
243 GeneralizedTime_t
*gt
;
245 int fv
, fd
; /* fractional parts */
249 if(asn_GT2time_frac((GeneralizedTime_t
*)sptr
,
250 &fv
, &fd
, &tm
, 1) == -1
254 gt
= asn_time2GT_frac(0, &tm
, fv
, fd
, 1);
255 if(!gt
) _ASN_ENCODE_FAILED
;
257 rv
= OCTET_STRING_encode_xer_utf8(td
, sptr
, ilevel
, flags
,
259 ASN_STRUCT_FREE(asn_DEF_GeneralizedTime
, gt
);
262 return OCTET_STRING_encode_xer_utf8(td
, sptr
, ilevel
, flags
,
267 #endif /* __ASN_INTERNAL_TEST_MODE__ */
270 GeneralizedTime_print(asn_TYPE_descriptor_t
*td
, const void *sptr
, int ilevel
,
271 asn_app_consume_bytes_f
*cb
, void *app_key
) {
272 const GeneralizedTime_t
*st
= (const GeneralizedTime_t
*)sptr
;
274 (void)td
; /* Unused argument */
275 (void)ilevel
; /* Unused argument */
283 if(asn_GT2time(st
, &tm
, 1) == -1 && errno
!= EPERM
)
284 return (cb("<bad-value>", 11, app_key
) < 0) ? -1 : 0;
286 ret
= snprintf(buf
, sizeof(buf
),
287 "%04d-%02d-%02d %02d:%02d:%02d (GMT)",
288 tm
.tm_year
+ 1900, tm
.tm_mon
+ 1, tm
.tm_mday
,
289 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
290 assert(ret
> 0 && ret
< (int)sizeof(buf
));
291 return (cb(buf
, ret
, app_key
) < 0) ? -1 : 0;
293 return (cb("<absent>", 8, app_key
) < 0) ? -1 : 0;
298 asn_GT2time(const GeneralizedTime_t
*st
, struct tm
*ret_tm
, int as_gmt
) {
299 return asn_GT2time_frac(st
, 0, 0, ret_tm
, as_gmt
);
303 asn_GT2time_prec(const GeneralizedTime_t
*st
, int *frac_value
, int frac_digits
, struct tm
*ret_tm
, int as_gmt
) {
308 tloc
= asn_GT2time_frac(st
, &fv
, &fd
, ret_tm
, as_gmt
);
310 return asn_GT2time_frac(st
, 0, 0, ret_tm
, as_gmt
);
311 if(fd
== 0 || frac_digits
<= 0) {
314 while(fd
> frac_digits
)
316 while(fd
< frac_digits
) {
317 int volatile new_fv
= fv
* 10;
318 /* GCC 4.x is being too smart without volatile */
319 if(new_fv
/ 10 != fv
) {
320 /* Too long precision request */
334 asn_GT2time_frac(const GeneralizedTime_t
*st
, int *frac_value
, int *frac_digits
, struct tm
*ret_tm
, int as_gmt
) {
340 int gmtoff
= 0; /* h + m */
341 int offset_specified
= 0;
346 if(!st
|| !st
->buf
) {
351 end
= buf
+ st
->size
;
360 * Decode first 10 bytes: "AAAAMMJJhh"
362 memset(&tm_s
, 0, sizeof(tm_s
));
365 #define B2F(var) do { \
366 unsigned ch = *buf; \
367 if(ch < 0x30 || ch > 0x39) { \
371 var = var * 10 + (ch - 0x30); \
375 #define B2T(var) B2F(tm_s.var)
377 B2T(tm_year
); /* 1: A */
378 B2T(tm_year
); /* 2: A */
379 B2T(tm_year
); /* 3: A */
380 B2T(tm_year
); /* 4: A */
381 B2T(tm_mon
); /* 5: M */
382 B2T(tm_mon
); /* 6: M */
383 B2T(tm_mday
); /* 7: J */
384 B2T(tm_mday
); /* 8: J */
385 B2T(tm_hour
); /* 9: h */
386 B2T(tm_hour
); /* 0: h */
388 if(buf
== end
) goto local_finish
;
391 * Parse [mm[ss[(.|,)ffff]]]
395 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
396 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
397 tm_s
.tm_min
= (*buf
++) - 0x30;
398 if(buf
== end
) { errno
= EINVAL
; return -1; }
401 case 0x2B: case 0x2D: /* +, - */
410 if(buf
== end
) goto local_finish
;
413 * Parse [mm[ss[(.|,)ffff]]]
417 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
418 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
419 tm_s
.tm_sec
= (*buf
++) - 0x30;
420 if(buf
== end
) { errno
= EINVAL
; return -1; }
423 case 0x2B: case 0x2D: /* +, - */
432 if(buf
== end
) goto local_finish
;
435 * Parse [mm[ss[(.|,)ffff]]]
439 case 0x2C: case 0x2E: /* (.|,) */
441 * Process fractions of seconds.
443 for(buf
++; buf
< end
; buf
++) {
445 int volatile new_fvalue
;
446 /* GCC 4.x is being too smart without volatile */
448 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
449 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
450 new_fvalue
= fvalue
* 10 + (v
- 0x30);
451 if(new_fvalue
/ 10 != fvalue
) {
452 /* Not enough precision, ignore */
465 if(buf
== end
) goto local_finish
;
468 case 0x2B: case 0x2D: /* +, - */
487 if(buf
[-3] == 0x2D) /* Negative */
492 if((end
- buf
) == 2) {
495 } else if(end
!= buf
) {
500 gmtoff
= gmtoff
* (3600 * gmtoff_h
+ 60 * gmtoff_m
);
505 offset_specified
= 1;
513 if((tm_s
.tm_mon
> 12 || tm_s
.tm_mon
< 1)
514 || (tm_s
.tm_mday
> 31 || tm_s
.tm_mday
< 1)
515 || (tm_s
.tm_hour
> 23)
516 || (tm_s
.tm_sec
> 60)
523 tm_s
.tm_mon
-= 1; /* 0 - 11 */
524 tm_s
.tm_year
-= 1900;
527 tm_s
.tm_sec
-= gmtoff
;
529 /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
531 if(offset_specified
) {
532 tloc
= timegm(&tm_s
);
535 * Without an offset (or "Z"),
536 * we can only guess that it is a local zone.
537 * Interpret it in this fashion.
539 tloc
= mktime(&tm_s
);
548 if(offset_specified
) {
551 if(gmtime_r(&tloc
, ret_tm
) == 0) {
557 if(localtime_r(&tloc
, ret_tm
) == 0) {
564 /* Fractions of seconds */
565 if(frac_value
) *frac_value
= fvalue
;
566 if(frac_digits
) *frac_digits
= fdigits
;
572 asn_time2GT(GeneralizedTime_t
*opt_gt
, const struct tm
*tm
, int force_gmt
) {
573 return asn_time2GT_frac(opt_gt
, tm
, 0, 0, force_gmt
);
577 asn_time2GT_frac(GeneralizedTime_t
*opt_gt
, const struct tm
*tm
, int frac_value
, int frac_digits
, int force_gmt
) {
580 const unsigned int buf_size
=
581 4 + 2 + 2 /* yyyymmdd */
582 + 2 + 2 + 2 /* hhmmss */
583 + 1 + 6 /* .ffffff */
591 /* Check arguments */
597 /* Pre-allocate a buffer of sufficient yet small length */
598 buf
= (char *)MALLOC(buf_size
);
601 gmtoff
= GMTOFF(*tm
);
603 if(force_gmt
&& gmtoff
) {
605 tm_s
.tm_sec
-= gmtoff
;
606 timegm(&tm_s
); /* Fix the time */
608 #ifdef HAVE_TM_GMTOFF
609 assert(!GMTOFF(tm_s
)); /* Will fix itself */
610 #else /* !HAVE_TM_GMTOFF */
615 size
= snprintf(buf
, buf_size
, "%04d%02d%02d%02d%02d%02d",
624 /* Could be assert(size == 14); */
633 * Deal with fractions.
635 if(frac_value
> 0 && frac_digits
> 0) {
636 char *end
= p
+ 1 + 6; /* '.' + maximum 6 digits */
641 /* Place bounds on precision */
642 while(frac_digits
-- > 6)
645 /* emulate fbase = pow(10, frac_digits) */
646 for(fbase
= 1; frac_digits
--;)
650 int digit
= frac_value
/ fbase
;
651 if(digit
> 9) { z
= 0; break; }
655 } while(fbase
> 0 && frac_value
> 0 && z
< end
);
657 for(--z
; *z
== 0x30; --z
); /* Strip zeroes */
664 *p
++ = 0x5a; /* "Z" */
670 ret
= snprintf(p
, buf_size
- size
, "%+03ld%02ld",
671 gmtoff
/ 3600, labs(gmtoff
% 3600) / 60);
682 FREEMEM(opt_gt
->buf
);
684 opt_gt
= (GeneralizedTime_t
*)CALLOC(1, sizeof *opt_gt
);
685 if(!opt_gt
) { FREEMEM(buf
); return 0; }
688 opt_gt
->buf
= (unsigned char *)buf
;