blob: 01d7a736e17bf9bbd1d0b62b574ca67a07f4db61 [file] [log] [blame]
vlmfa67ddc2004-06-03 03:38:44 +00001/*-
2 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
4 */
5#include <GeneralizedTime.h>
6#include <time.h>
7#include <errno.h>
8#ifndef __NO_ASSERT_H__
9#include <assert.h>
10#endif /* __NO_ASSERT_H__ */
11
12#ifndef __NO_ASN_TABLE__
13
14/*
15 * GeneralizedTime basic type description.
16 */
17static ber_tlv_tag_t asn1_DEF_GeneralizedTime_tags[] = {
18 (ASN_TAG_CLASS_UNIVERSAL | (24 << 2))
19};
20asn1_TYPE_descriptor_t asn1_DEF_GeneralizedTime = {
21 "GeneralizedTime",
22 GeneralizedTime_constraint, /* Check validity of time */
23 OCTET_STRING_decode_ber, /* Implemented in terms of OCTET STRING */
24 OCTET_STRING_encode_der, /* Implemented in terms of OCTET STRING */
25 GeneralizedTime_print,
26 OCTET_STRING_free,
27 0, /* Use generic outmost tag fetcher */
28 asn1_DEF_GeneralizedTime_tags,
29 sizeof(asn1_DEF_GeneralizedTime_tags)
30 / sizeof(asn1_DEF_GeneralizedTime_tags[0]),
31 1, /* Single UNIVERSAL tag may be implicitly overriden */
32 -1, /* Both ways are fine */
33};
34
35#endif /* __NO_ASN_TABLE__ */
36
37/*
38 * Check that the time looks like the time.
39 */
40int
41GeneralizedTime_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,
42 asn_app_consume_bytes_f *app_errlog, void *app_key) {
43 const GeneralizedTime_t *st = sptr;
44 time_t tloc;
45
46 errno = EPERM; /* Just an unlikely error code */
47 tloc = asn_GT2time(st, 0);
48 if(tloc == -1 && errno != EPERM) {
49 _ASN_ERRLOG("%s: Invalid time format: %s",
50 td->name, strerror(errno));
51 return -1;
52 }
53
54 return 0;
55}
56
57int
58GeneralizedTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
59 asn_app_consume_bytes_f *cb, void *app_key) {
60 const GeneralizedTime_t *st = sptr;
61
62 if(st && st->buf) {
63 char buf[32];
64 struct tm tm;
65 int ret;
66
67 errno = EPERM;
68 if(asn_GT2time(st, &tm) == -1 && errno != EPERM)
69 return cb("<bad-value>", 11, app_key);
70
71 ret = snprintf(buf, sizeof(buf),
72 "%04d-%02d-%02d %02d:%02d%02d",
73 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
74 tm.tm_hour, tm.tm_min, tm.tm_sec);
75 assert(ret > 0 && ret < sizeof(buf));
76 return cb(buf, ret, app_key);
77 } else {
78 return cb("<absent>", 8, app_key);
79 }
80}
81
82/*
83 * Where to look for offset from GMT, Phase I.
84 * Several platforms are known.
85 */
86#if defined(__FreeBSD__) || (defined(__GNUC__) && defined(__APPLE_CC__))
87#undef HAVE_TM_ZONE
88#define HAVE_TM_ZONE
89#endif /* BSDs */
90
91/*
92 * Where to look for offset from GMT, Phase II.
93 */
94#ifdef HAVE_TM_ZONE
95#define GMTOFF (tm_s.tm_gmtoff)
96#else /* HAVE_TM_ZONE */
97#define GMTOFF (-timezone)
98#endif /* HAVE_TM_ZONE */
99
100time_t
101asn_GT2time(const GeneralizedTime_t *st, struct tm *_tm) {
102 struct tm tm_s;
103 uint8_t *buf;
104 uint8_t *end;
105 int tm_gmtoff_h = 0;
106 int tm_gmtoff_m = 0;
107 int tm_gmtoff = 0; /* h + m */
108 int offset_specified = 0;
109 time_t tloc;
110
111 if(!st || !st->buf) {
112 errno = EINVAL;
113 return -1;
114 } else {
115 buf = st->buf;
116 end = buf + st->size;
117 }
118
119 if(st->size < 10) {
120 errno = EINVAL;
121 return -1;
122 }
123
124 /*
125 * Decode first 10 bytes: "AAAAMMJJhh"
126 */
127 memset(&tm_s, 0, sizeof(tm_s));
128#undef B2F
129#undef B2T
130#define B2F(var) do { \
131 unsigned ch = *buf; \
132 if(ch < 0x30 && ch > 0x39) { \
133 errno = EINVAL; \
134 return -1; \
135 } else { \
136 var = var * 10 + (ch - 0x30); \
137 buf++; \
138 } \
139 } while(0)
140#define B2T(var) B2F(tm_s.var)
141
142 B2T(tm_year); /* 1: A */
143 B2T(tm_year); /* 2: A */
144 B2T(tm_year); /* 3: A */
145 B2T(tm_year); /* 4: A */
146 B2T(tm_mon); /* 5: M */
147 B2T(tm_mon); /* 6: M */
148 B2T(tm_mday); /* 7: J */
149 B2T(tm_mday); /* 8: J */
150 B2T(tm_hour); /* 9: h */
151 B2T(tm_hour); /* 0: h */
152
153 if(buf == end) goto local_finish;
154
155 /*
156 * Parse [mm[ss[(.|,)ffff]]]
157 * ^^
158 */
159 switch(*buf) {
160 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
161 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
162 tm_s.tm_min = (*buf++) - 0x30;
163 if(buf == end) { errno = EINVAL; return -1; }
164 B2T(tm_min);
165 break;
166 case 0x2B: case 0x2D: /* +, - */
167 goto offset;
168 case 0x5A: /* Z */
169 goto utc_finish;
170 default:
171 errno = EINVAL;
172 return -1;
173 }
174
175 if(buf == end) goto local_finish;
176
177 /*
178 * Parse [mm[ss[(.|,)ffff]]]
179 * ^^
180 */
181 switch(*buf) {
182 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
183 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
184 tm_s.tm_sec = (*buf++) - 0x30;
185 if(buf == end) { errno = EINVAL; return -1; }
186 B2T(tm_sec);
187 break;
188 case 0x2B: case 0x2D: /* +, - */
189 goto offset;
190 case 0x5A: /* Z */
191 goto utc_finish;
192 default:
193 errno = EINVAL;
194 return -1;
195 }
196
197 if(buf == end) goto local_finish;
198
199 /*
200 * Parse [mm[ss[(.|,)ffff]]]
201 * ^ ^
202 */
203 switch(*buf) {
204 case 0x2C: case 0x2E: /* (.|,) */
205 /* Fractions of seconds are not supported
206 * by time_t or struct tm. Skip them */
207 for(buf++; buf < end; buf++) {
208 switch(*buf) {
209 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
210 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
211 continue;
212 default:
213 break;
214 }
215 break;
216 }
217 }
218
219 if(buf == end) goto local_finish;
220
221 switch(*buf) {
222 case 0x2B: case 0x2D: /* +, - */
223 goto offset;
224 case 0x5A: /* Z */
225 goto utc_finish;
226 default:
227 errno = EINVAL;
228 return -1;
229 }
230
231
232offset:
233
234 if(end - buf < 3) {
235 errno = EINVAL;
236 return -1;
237 }
238 buf++;
239 B2F(tm_gmtoff_h);
240 B2F(tm_gmtoff_h);
241 if(buf[-3] == 0x2D) /* Negative */
242 tm_gmtoff = -1;
243 else
244 tm_gmtoff = 1;
245
246 if((end - buf) == 2) {
247 B2F(tm_gmtoff_m);
248 B2F(tm_gmtoff_m);
249 } else if(end != buf) {
250 errno = EINVAL;
251 return -1;
252 }
253
254 tm_gmtoff = tm_gmtoff * (3600 * tm_gmtoff_h + 60 * tm_gmtoff_m);
255
256 /* Fall through */
257utc_finish:
258
259 offset_specified = 1;
260
261 /* Fall through */
262local_finish:
263
264 /*
265 * Validation.
266 */
267 if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
268 || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
269 || (tm_s.tm_hour > 23)
270 || (tm_s.tm_sec > 60)
271 ) {
272 errno = EINVAL;
273 return -1;
274 }
275
276 /* Canonicalize */
277 tm_s.tm_mon -= 1; /* 0 - 11 */
278 tm_s.tm_year -= 1900;
279 tm_s.tm_isdst = -1;
280
281 tloc = mktime(&tm_s);
282 if(tloc == -1) {
283 errno = EINVAL;
284 return -1;
285 }
286
287 if(offset_specified) {
288 /*
289 * Offset from GMT is specified in the time expression.
290 */
291 tloc += GMTOFF - tm_gmtoff;
292 if(_tm && (localtime_r(&tloc, &tm_s) == NULL)) {
293 /* Could not reconstruct the time */
294 return -1;
295 }
296 }
297
298 if(_tm) memcpy(_tm, &tm_s, sizeof(struct tm));
299
300 return tloc;
301}
302