aboutsummaryrefslogtreecommitdiffstats
path: root/src/GeneralizedTime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/GeneralizedTime.c')
-rw-r--r--src/GeneralizedTime.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/src/GeneralizedTime.c b/src/GeneralizedTime.c
new file mode 100644
index 0000000..f864d83
--- /dev/null
+++ b/src/GeneralizedTime.c
@@ -0,0 +1,683 @@
+/*-
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#define _POSIX_PTHREAD_SEMANTICS /* for Sun */
+#define _REENTRANT /* for Sun */
+#include <asn_internal.h>
+#include <GeneralizedTime.h>
+#include <errno.h>
+
+#ifdef __CYGWIN__
+#include "/usr/include/time.h"
+#else
+#include <time.h>
+#endif /* __CYGWIN__ */
+
+#if defined(WIN32)
+#pragma message( "PLEASE STOP AND READ!")
+#pragma message( " localtime_r is implemented via localtime(), which may be not thread-safe.")
+#pragma message( " gmtime_r is implemented via gmtime(), which may be not thread-safe.")
+#pragma message( " ")
+#pragma message( " You must fix the code by inserting appropriate locking")
+#pragma message( " if you want to use asn_GT2time() or asn_UT2time().")
+#pragma message( "PLEASE STOP AND READ!")
+
+static struct tm *localtime_r(const time_t *tloc, struct tm *result) {
+ struct tm *tm;
+ if((tm = localtime(tloc)))
+ return memcpy(result, tm, sizeof(struct tm));
+ return 0;
+}
+
+static struct tm *gmtime_r(const time_t *tloc, struct tm *result) {
+ struct tm *tm;
+ if((tm = gmtime(tloc)))
+ return memcpy(result, tm, sizeof(struct tm));
+ return 0;
+}
+
+#define tzset() _tzset()
+#define putenv(c) _putenv(c)
+#define _EMULATE_TIMEGM
+
+#endif /* WIN32 */
+
+#if defined(sun) || defined(_sun_) || defined(__solaris__)
+#define _EMULATE_TIMEGM
+#endif
+
+/*
+ * Where to look for offset from GMT, Phase I.
+ * Several platforms are known.
+ */
+#if defined(__FreeBSD__) \
+ || (defined(__GNUC__) && defined(__APPLE_CC__)) \
+ || (defined __GLIBC__ && __GLIBC__ >= 2)
+#undef HAVE_TM_GMTOFF
+#define HAVE_TM_GMTOFF
+#endif /* BSDs and newer glibc */
+
+/*
+ * Where to look for offset from GMT, Phase II.
+ */
+#ifdef HAVE_TM_GMTOFF
+#define GMTOFF(tm) ((tm).tm_gmtoff)
+#else /* HAVE_TM_GMTOFF */
+#define GMTOFF(tm) (-timezone)
+#endif /* HAVE_TM_GMTOFF */
+
+#if (defined(_EMULATE_TIMEGM) || !defined(HAVE_TM_GMTOFF))
+#warning "PLEASE STOP AND READ!"
+#warning " timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe."
+#warning " "
+#warning " You must fix the code by inserting appropriate locking"
+#warning " if you want to use asn_GT2time() or asn_UT2time()."
+#warning "PLEASE STOP AND READ!"
+#endif /* _EMULATE_TIMEGM */
+
+/*
+ * Override our GMTOFF decision for other known platforms.
+ */
+#ifdef __CYGWIN__
+#undef GMTOFF
+static long GMTOFF(struct tm a){
+ struct tm *lt;
+ time_t local_time, gmt_time;
+ long zone;
+
+ tzset();
+ gmt_time = time (NULL);
+
+ lt = gmtime(&gmt_time);
+
+ local_time = mktime(lt);
+ return (gmt_time - local_time);
+}
+#define _EMULATE_TIMEGM
+
+#endif /* __CYGWIN__ */
+
+#define ATZVARS do { \
+ char tzoldbuf[64]; \
+ char *tzold
+#define ATZSAVETZ do { \
+ tzold = getenv("TZ"); \
+ if(tzold) { \
+ size_t tzlen = strlen(tzold); \
+ if(tzlen < sizeof(tzoldbuf)) \
+ tzold = memcpy(tzoldbuf, tzold, tzlen + 1); \
+ else \
+ tzold = strdup(tzold); /* Ignore error */ \
+ setenv("TZ", "UTC", 1); \
+ } \
+ tzset(); \
+} while(0)
+#define ATZOLDTZ do { \
+ if (tzold) { \
+ setenv("TZ", tzold, 1); \
+ *tzoldbuf = 0; \
+ if(tzold != tzoldbuf) \
+ FREEMEM(tzold); \
+ } else { \
+ unsetenv("TZ"); \
+ } \
+ tzset(); \
+} while(0); } while(0);
+
+#ifdef _EMULATE_TIMEGM
+static time_t timegm(struct tm *tm) {
+ time_t tloc;
+ ATZVARS;
+ ATZSAVETZ;
+ tloc = mktime(tm);
+ ATZOLDTZ;
+ return tloc;
+}
+#endif /* _EMULATE_TIMEGM */
+
+
+#ifndef __ASN_INTERNAL_TEST_MODE__
+
+/*
+ * GeneralizedTime basic type description.
+ */
+static ber_tlv_tag_t asn_DEF_GeneralizedTime_tags[] = {
+ (ASN_TAG_CLASS_UNIVERSAL | (24 << 2)), /* [UNIVERSAL 24] IMPLICIT ...*/
+ (ASN_TAG_CLASS_UNIVERSAL | (26 << 2)), /* [UNIVERSAL 26] IMPLICIT ...*/
+ (ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) /* ... OCTET STRING */
+};
+asn_TYPE_descriptor_t asn_DEF_GeneralizedTime = {
+ "GeneralizedTime",
+ "GeneralizedTime",
+ OCTET_STRING_free,
+ GeneralizedTime_print,
+ GeneralizedTime_constraint, /* Check validity of time */
+ OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */
+ GeneralizedTime_encode_der,
+ OCTET_STRING_decode_xer_utf8,
+ GeneralizedTime_encode_xer,
+ 0, 0,
+ 0, /* Use generic outmost tag fetcher */
+ asn_DEF_GeneralizedTime_tags,
+ sizeof(asn_DEF_GeneralizedTime_tags)
+ / sizeof(asn_DEF_GeneralizedTime_tags[0]) - 2,
+ asn_DEF_GeneralizedTime_tags,
+ sizeof(asn_DEF_GeneralizedTime_tags)
+ / sizeof(asn_DEF_GeneralizedTime_tags[0]),
+ 0, /* No PER visible constraints */
+ 0, 0, /* No members */
+ 0 /* No specifics */
+};
+
+#endif /* __ASN_INTERNAL_TEST_MODE__ */
+
+/*
+ * Check that the time looks like the time.
+ */
+int
+GeneralizedTime_constraint(asn_TYPE_descriptor_t *td, const void *sptr,
+ asn_app_constraint_failed_f *ctfailcb, void *app_key) {
+ const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
+ time_t tloc;
+
+ errno = EPERM; /* Just an unlikely error code */
+ tloc = asn_GT2time(st, 0, 0);
+ if(tloc == -1 && errno != EPERM) {
+ _ASN_CTFAIL(app_key, td,
+ "%s: Invalid time format: %s (%s:%d)",
+ td->name, strerror(errno), __FILE__, __LINE__);
+ return -1;
+ }
+
+ return 0;
+}
+
+asn_enc_rval_t
+GeneralizedTime_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
+ int tag_mode, ber_tlv_tag_t tag,
+ asn_app_consume_bytes_f *cb, void *app_key) {
+ GeneralizedTime_t *st = (GeneralizedTime_t *)sptr;
+ asn_enc_rval_t erval;
+ int fv, fd; /* seconds fraction value and number of digits */
+ struct tm tm;
+ time_t tloc;
+
+ /*
+ * Encode as a canonical DER.
+ */
+ errno = EPERM;
+ tloc = asn_GT2time_frac(st, &fv, &fd, &tm, 1); /* Recognize time */
+ if(tloc == -1 && errno != EPERM)
+ /* Failed to recognize time. Fail completely. */
+ _ASN_ENCODE_FAILED;
+
+ st = asn_time2GT_frac(0, &tm, fv, fd, 1); /* Save time canonically */
+ if(!st) _ASN_ENCODE_FAILED; /* Memory allocation failure. */
+
+ erval = OCTET_STRING_encode_der(td, st, tag_mode, tag, cb, app_key);
+
+ FREEMEM(st->buf);
+ FREEMEM(st);
+
+ return erval;
+}
+
+#ifndef __ASN_INTERNAL_TEST_MODE__
+
+asn_enc_rval_t
+GeneralizedTime_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
+ int ilevel, enum xer_encoder_flags_e flags,
+ asn_app_consume_bytes_f *cb, void *app_key) {
+
+ if(flags & XER_F_CANONICAL) {
+ GeneralizedTime_t *gt;
+ asn_enc_rval_t rv;
+ int fv, fd; /* fractional parts */
+ struct tm tm;
+
+ errno = EPERM;
+ if(asn_GT2time_frac((GeneralizedTime_t *)sptr,
+ &fv, &fd, &tm, 1) == -1
+ && errno != EPERM)
+ _ASN_ENCODE_FAILED;
+
+ gt = asn_time2GT_frac(0, &tm, fv, fd, 1);
+ if(!gt) _ASN_ENCODE_FAILED;
+
+ rv = OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
+ cb, app_key);
+ ASN_STRUCT_FREE(asn_DEF_GeneralizedTime, gt);
+ return rv;
+ } else {
+ return OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
+ cb, app_key);
+ }
+}
+
+#endif /* __ASN_INTERNAL_TEST_MODE__ */
+
+int
+GeneralizedTime_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+ asn_app_consume_bytes_f *cb, void *app_key) {
+ const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
+
+ (void)td; /* Unused argument */
+ (void)ilevel; /* Unused argument */
+
+ if(st && st->buf) {
+ char buf[32];
+ struct tm tm;
+ int ret;
+
+ errno = EPERM;
+ if(asn_GT2time(st, &tm, 1) == -1 && errno != EPERM)
+ return (cb("<bad-value>", 11, app_key) < 0) ? -1 : 0;
+
+ ret = snprintf(buf, sizeof(buf),
+ "%04d-%02d-%02d %02d:%02d:%02d (GMT)",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ assert(ret > 0 && ret < (int)sizeof(buf));
+ return (cb(buf, ret, app_key) < 0) ? -1 : 0;
+ } else {
+ return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+ }
+}
+
+time_t
+asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm, int as_gmt) {
+ return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
+}
+
+time_t
+asn_GT2time_prec(const GeneralizedTime_t *st, int *frac_value, int frac_digits, struct tm *ret_tm, int as_gmt) {
+ time_t tloc;
+ int fv, fd = 0;
+
+ if(frac_value)
+ tloc = asn_GT2time_frac(st, &fv, &fd, ret_tm, as_gmt);
+ else
+ return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
+ if(fd == 0 || frac_digits <= 0) {
+ *frac_value = 0;
+ } else {
+ while(fd > frac_digits)
+ fv /= 10, fd--;
+ while(fd < frac_digits) {
+ int new_fv = fv * 10;
+ if(new_fv / 10 != fv) {
+ /* Too long precision request */
+ fv = 0;
+ break;
+ }
+ fv = new_fv, fd++;
+ }
+
+ *frac_value = fv;
+ }
+
+ return tloc;
+}
+
+time_t
+asn_GT2time_frac(const GeneralizedTime_t *st, int *frac_value, int *frac_digits, struct tm *ret_tm, int as_gmt) {
+ struct tm tm_s;
+ uint8_t *buf;
+ uint8_t *end;
+ int gmtoff_h = 0;
+ int gmtoff_m = 0;
+ int gmtoff = 0; /* h + m */
+ int offset_specified = 0;
+ int fvalue = 0;
+ int fdigits = 0;
+ time_t tloc;
+
+ if(!st || !st->buf) {
+ errno = EINVAL;
+ return -1;
+ } else {
+ buf = st->buf;
+ end = buf + st->size;
+ }
+
+ if(st->size < 10) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Decode first 10 bytes: "AAAAMMJJhh"
+ */
+ memset(&tm_s, 0, sizeof(tm_s));
+#undef B2F
+#undef B2T
+#define B2F(var) do { \
+ unsigned ch = *buf; \
+ if(ch < 0x30 || ch > 0x39) { \
+ errno = EINVAL; \
+ return -1; \
+ } else { \
+ var = var * 10 + (ch - 0x30); \
+ buf++; \
+ } \
+ } while(0)
+#define B2T(var) B2F(tm_s.var)
+
+ B2T(tm_year); /* 1: A */
+ B2T(tm_year); /* 2: A */
+ B2T(tm_year); /* 3: A */
+ B2T(tm_year); /* 4: A */
+ B2T(tm_mon); /* 5: M */
+ B2T(tm_mon); /* 6: M */
+ B2T(tm_mday); /* 7: J */
+ B2T(tm_mday); /* 8: J */
+ B2T(tm_hour); /* 9: h */
+ B2T(tm_hour); /* 0: h */
+
+ if(buf == end) goto local_finish;
+
+ /*
+ * Parse [mm[ss[(.|,)ffff]]]
+ * ^^
+ */
+ switch(*buf) {
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+ case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+ tm_s.tm_min = (*buf++) - 0x30;
+ if(buf == end) { errno = EINVAL; return -1; }
+ B2T(tm_min);
+ break;
+ case 0x2B: case 0x2D: /* +, - */
+ goto offset;
+ case 0x5A: /* Z */
+ goto utc_finish;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if(buf == end) goto local_finish;
+
+ /*
+ * Parse [mm[ss[(.|,)ffff]]]
+ * ^^
+ */
+ switch(*buf) {
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+ case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+ tm_s.tm_sec = (*buf++) - 0x30;
+ if(buf == end) { errno = EINVAL; return -1; }
+ B2T(tm_sec);
+ break;
+ case 0x2B: case 0x2D: /* +, - */
+ goto offset;
+ case 0x5A: /* Z */
+ goto utc_finish;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if(buf == end) goto local_finish;
+
+ /*
+ * Parse [mm[ss[(.|,)ffff]]]
+ * ^ ^
+ */
+ switch(*buf) {
+ case 0x2C: case 0x2E: /* (.|,) */
+ /*
+ * Process fractions of seconds.
+ */
+ for(buf++; buf < end; buf++) {
+ int v = *buf;
+ int new_fvalue;
+ switch(v) {
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+ case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+ new_fvalue = fvalue * 10 + (v - 0x30);
+ if(new_fvalue / 10 != fvalue) {
+ /* Not enough precision, ignore */
+ } else {
+ fvalue = new_fvalue;
+ fdigits++;
+ }
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ if(buf == end) goto local_finish;
+
+ switch(*buf) {
+ case 0x2B: case 0x2D: /* +, - */
+ goto offset;
+ case 0x5A: /* Z */
+ goto utc_finish;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+
+offset:
+
+ if(end - buf < 3) {
+ errno = EINVAL;
+ return -1;
+ }
+ buf++;
+ B2F(gmtoff_h);
+ B2F(gmtoff_h);
+ if(buf[-3] == 0x2D) /* Negative */
+ gmtoff = -1;
+ else
+ gmtoff = 1;
+
+ if((end - buf) == 2) {
+ B2F(gmtoff_m);
+ B2F(gmtoff_m);
+ } else if(end != buf) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ gmtoff = gmtoff * (3600 * gmtoff_h + 60 * gmtoff_m);
+
+ /* Fall through */
+utc_finish:
+
+ offset_specified = 1;
+
+ /* Fall through */
+local_finish:
+
+ /*
+ * Validation.
+ */
+ if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
+ || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
+ || (tm_s.tm_hour > 23)
+ || (tm_s.tm_sec > 60)
+ ) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Canonicalize */
+ tm_s.tm_mon -= 1; /* 0 - 11 */
+ tm_s.tm_year -= 1900;
+ tm_s.tm_isdst = -1;
+
+ tm_s.tm_sec -= gmtoff;
+
+ /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
+
+ if(offset_specified) {
+ tloc = timegm(&tm_s);
+ } else {
+ /*
+ * Without an offset (or "Z"),
+ * we can only guess that it is a local zone.
+ * Interpret it in this fashion.
+ */
+ tloc = mktime(&tm_s);
+ }
+ if(tloc == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if(ret_tm) {
+ if(as_gmt) {
+ if(offset_specified) {
+ *ret_tm = tm_s;
+ } else {
+ if(gmtime_r(&tloc, ret_tm) == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ } else {
+ if(localtime_r(&tloc, ret_tm) == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ }
+
+ /* Fractions of seconds */
+ if(frac_value) *frac_value = fvalue;
+ if(frac_digits) *frac_digits = fdigits;
+
+ return tloc;
+}
+
+GeneralizedTime_t *
+asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm, int force_gmt) {
+ return asn_time2GT_frac(opt_gt, tm, 0, 0, force_gmt);
+}
+
+GeneralizedTime_t *
+asn_time2GT_frac(GeneralizedTime_t *opt_gt, const struct tm *tm, int frac_value, int frac_digits, int force_gmt) {
+ struct tm tm_s;
+ long gmtoff;
+ const unsigned int buf_size =
+ 4 + 2 + 2 /* yyyymmdd */
+ + 2 + 2 + 2 /* hhmmss */
+ + 1 + 6 /* .ffffff */
+ + 1 + 4 /* +hhmm */
+ + 1 /* '\0' */
+ ;
+ char *buf;
+ char *p;
+ int size;
+
+ /* Check arguments */
+ if(!tm) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ /* Pre-allocate a buffer of sufficient yet small length */
+ buf = (char *)MALLOC(buf_size);
+ if(!buf) return 0;
+
+ gmtoff = GMTOFF(*tm);
+
+ if(force_gmt && gmtoff) {
+ tm_s = *tm;
+ tm_s.tm_sec -= gmtoff;
+ timegm(&tm_s); /* Fix the time */
+ tm = &tm_s;
+#ifdef HAVE_TM_GMTOFF
+ assert(!GMTOFF(tm_s)); /* Will fix itself */
+#else /* !HAVE_TM_GMTOFF */
+ gmtoff = 0;
+#endif
+ }
+
+ size = snprintf(buf, buf_size, "%04d%02d%02d%02d%02d%02d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec
+ );
+ if(size != 14) {
+ /* Could be assert(size == 14); */
+ FREEMEM(buf);
+ errno = EINVAL;
+ return 0;
+ }
+
+ p = buf + size;
+
+ /*
+ * Deal with fractions.
+ */
+ if(frac_value > 0 && frac_digits > 0) {
+ char *end = p + 1 + 6; /* '.' + maximum 6 digits */
+ char *z = p;
+ long fbase;
+ *z++ = '.';
+
+ /* Place bounds on precision */
+ while(frac_digits-- > 6)
+ frac_value /= 10;
+
+ /* emulate fbase = pow(10, frac_digits) */
+ for(fbase = 1; frac_digits--;)
+ fbase *= 10;
+
+ do {
+ int digit = frac_value / fbase;
+ if(digit > 9) { z = 0; break; }
+ *z++ = digit + 0x30;
+ frac_value %= fbase;
+ fbase /= 10;
+ } while(fbase > 0 && frac_value > 0 && z < end);
+ if(z) {
+ for(--z; *z == 0x30; --z); /* Strip zeroes */
+ p = z + (*z != '.');
+ size = p - buf;
+ }
+ }
+
+ if(force_gmt) {
+ *p++ = 0x5a; /* "Z" */
+ *p++ = 0;
+ size++;
+ } else {
+ int ret;
+ gmtoff %= 86400;
+ ret = snprintf(p, buf_size - size, "%+03ld%02ld",
+ gmtoff / 3600, labs(gmtoff % 3600));
+ if(ret != 5) {
+ FREEMEM(buf);
+ errno = EINVAL;
+ return 0;
+ }
+ size += ret;
+ }
+
+ if(opt_gt) {
+ if(opt_gt->buf)
+ FREEMEM(opt_gt->buf);
+ } else {
+ opt_gt = (GeneralizedTime_t *)CALLOC(1, sizeof *opt_gt);
+ if(!opt_gt) { FREEMEM(buf); return 0; }
+ }
+
+ opt_gt->buf = (unsigned char *)buf;
+ opt_gt->size = size;
+
+ return opt_gt;
+}
+
+