1/*
2 * Copyright (c) 2000-2004,2006,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//
26// Manage the Tower of Babel of CSSM dates and times
27//
28#include <security_cdsa_utilities/cssmdates.h>
29#include <security_cdsa_utilities/cssmerrors.h>
30#include <Security/cssm.h>
31#include <string>
32
33
34//
35// A (private) PODwrapper for CFGregorianDate
36//
37struct Gregorian : public PodWrapper<Gregorian, CFGregorianDate> {
38    Gregorian() { }
39
40    Gregorian(int y, int m, int d, int h = 0, int min = 0, double sec = 0)
41    {
42        year = y; month = m; day = d;
43        hour = h; minute = min; second = sec;
44    }
45
46    Gregorian(CFAbsoluteTime ref)
47    { static_cast<CFGregorianDate &>(*this) = CFAbsoluteTimeGetGregorianDate(ref, NULL); }
48
49    operator CFAbsoluteTime () const
50    { return CFGregorianDateGetAbsoluteTime(*this, NULL); }
51};
52
53
54//
55// The CssmDate PODwrapper
56//
57CssmDate::CssmDate(const char *y, const char *m, const char *d)
58{
59    assign(years(), 4, y);
60    assign(months(), 2, m);
61    assign(days(), 2, d);
62}
63
64CssmDate::CssmDate(int y, int m, int d)
65{
66    // internal format is "yyyymmdd" (no null termination)
67    char str[9];
68    if (8 != snprintf(str, 9, "%4.4d%2.2d%2.2d", y, m, d))
69        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
70    memcpy(this, str, 8);
71}
72
73int CssmDate::year() const
74{ return atoi(string(years(), 4).c_str()); }
75
76int CssmDate::month() const
77{ return atoi(string(months(), 2).c_str()); }
78
79int CssmDate::day() const
80{ return atoi(string(days(), 2).c_str()); }
81
82// right-adjust fill
83void CssmDate::assign(char *dest, int width, const char *src)
84{
85    // pick last width characters of src at most
86    size_t len = strlen(src);
87    if (len > width)
88        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
89    memset(dest, '0', width - len);
90    memcpy(dest + width - len, src, len);
91}
92
93
94//
95// CssmUniformDate core functions
96//
97
98
99//
100// Uniform conversions with CFDateRef
101//
102CssmUniformDate::CssmUniformDate(CFDateRef ref)
103{
104    mTime = CFDateGetAbsoluteTime(ref);
105}
106
107CssmUniformDate::operator CFDateRef() const
108{
109    return CFDateCreate(NULL, mTime);
110}
111
112
113//
114// Uniform conversions with CssmDates
115//
116CssmUniformDate::CssmUniformDate(const CssmDate &date)
117{
118    mTime = CFGregorianDateGetAbsoluteTime(Gregorian(date.year(), date.month(), date.day()),
119        NULL);
120}
121
122CssmUniformDate::operator CssmDate () const
123{
124    Gregorian greg(mTime);
125    return CssmDate(greg.year, greg.month, greg.day);
126}
127
128
129//
130// Uniform conversions with CssmData (1999-06-30_15:05:39 form)
131//
132CssmUniformDate::CssmUniformDate(const CSSM_DATA &inData)
133{
134    const CssmData &data = CssmData::overlay(inData);
135    if (data.length() != 19)
136        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
137    setFromString(reinterpret_cast<const char *>(inData.Data), "%ld-%d-%d_%d:%d:%lf", 19);
138}
139
140void CssmUniformDate::convertTo(CssmOwnedData &data) const
141{
142    Gregorian greg(mTime);
143    char str[20];
144    if (19 != snprintf(str, 20, "%4.4d-%2.2d-%2.2d_%2.2d:%2.2d:%2.2d",
145        int(greg.year), greg.month, greg.day, greg.hour, greg.minute, int(greg.second)))
146        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
147    data = CssmData(str, 19);
148}
149
150
151//
152// Uniform conversions with CSSM_TIMESTRING (19990630150539 form)
153//
154CssmUniformDate::CssmUniformDate(const char *src)
155{
156    setFromString(src, "%4ld%2d%2d%2d%2d%2lf", 14);
157}
158
159void CssmUniformDate::convertTo(char *dst, size_t length) const
160{
161    if (length < 14)
162        CssmError::throwMe(CSSMERR_CSSM_BUFFER_TOO_SMALL);
163    Gregorian greg(mTime);
164    char str[15];
165    if (14 != snprintf(str, 15, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d",
166        int(greg.year), greg.month, greg.day, greg.hour, greg.minute, int(greg.second)))
167        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
168    memcpy(dst, str, length == 14 ? 14 : 15);	// null terminate if there's room
169}
170
171
172//
173// Generalized parse-from-string setup
174//
175void CssmUniformDate::setFromString(const char *src, const char *format, size_t fieldWidth)
176{
177    // use a stack buffer
178    char str[20];
179    assert(fieldWidth < sizeof(str));
180
181    // make a copy with proper null terminator
182    memcpy(str, src, fieldWidth);
183    str[fieldWidth] = '\0';
184
185    // parse (with limited checks for bad field formats)
186    long year;
187    int month, day, hour, minute;
188    double second;
189    if (6 != sscanf(str, format,
190        &year, &month, &day, &hour, &minute, &second))
191        CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
192
193    // success
194    mTime = Gregorian((int)year, month, day, hour, minute, second);
195}
196