--- /dev/null
+/*
+ * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * cssmdatetime.cpp -- CSSM date and time utilities for the Mac
+ */
+
+#ifdef __MWERKS__
+#define _CPP_CSSM_DATE_TIME_UTILS
+#endif
+
+#include "cssmdatetime.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <security_utilities/errors.h>
+#include <CoreFoundation/CFDate.h>
+#include <CoreFoundation/CFTimeZone.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <SecBase.h>
+namespace Security
+{
+
+namespace CSSMDateTimeUtils
+{
+
+#define UTC_TIME_NOSEC_LEN 11
+#define UTC_TIME_STRLEN 13
+#define GENERALIZED_TIME_STRLEN 15
+#define LOCALIZED_UTC_TIME_STRLEN 17
+#define LOCALIZED_TIME_STRLEN 19
+#define MAX_TIME_STR_LEN 30
+
+
+void
+GetCurrentMacLongDateTime(sint64 &outMacDate)
+{
+ CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
+ CFAbsoluteTime absTime = CFAbsoluteTimeGetCurrent();
+ absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
+ CFRelease(timeZone);
+ outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
+}
+
+void
+TimeStringToMacSeconds (const CSSM_DATA &inUTCTime, uint32 &ioMacDate)
+{
+ sint64 ldt;
+ TimeStringToMacLongDateTime(inUTCTime, ldt);
+ ioMacDate = uint32(ldt);
+}
+
+/*
+ * Given a CSSM_DATA containing either a UTC-style or "generalized time"
+ * time string, convert to 32-bit Mac time in seconds.
+ * Returns nonzero on error.
+ */
+void
+TimeStringToMacLongDateTime (const CSSM_DATA &inUTCTime, sint64 &outMacDate)
+{
+ char szTemp[5];
+ size_t len;
+ int isUtc;
+ sint32 x;
+ sint32 i;
+ char *cp;
+
+ CFGregorianDate date;
+ ::memset( &date, 0, sizeof(date) );
+
+ if ((inUTCTime.Data == NULL) || (inUTCTime.Length == 0))
+ {
+ MacOSError::throwMe(errSecParam);
+ }
+
+ /* tolerate NULL terminated or not */
+ len = inUTCTime.Length;
+ if (inUTCTime.Data[len - 1] == '\0')
+ len--;
+
+ switch(len)
+ {
+ case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
+ isUtc = 1;
+ break;
+ case GENERALIZED_TIME_STRLEN: // 4-digit year
+ isUtc = 0;
+ break;
+ default: // unknown format
+ MacOSError::throwMe(errSecParam);
+ }
+
+ cp = (char *)inUTCTime.Data;
+
+ /* check that all characters except last are digits */
+ for(i=0; i<(sint32)(len - 1); i++) {
+ if ( !(isdigit(cp[i])) ) {
+ MacOSError::throwMe(errSecParam);
+ }
+ }
+
+ /* check last character is a 'Z' */
+ if(cp[len - 1] != 'Z' ) {
+ MacOSError::throwMe(errSecParam);
+ }
+
+ /* YEAR */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ if(!isUtc) {
+ /* two more digits */
+ szTemp[2] = *cp++;
+ szTemp[3] = *cp++;
+ szTemp[4] = '\0';
+ }
+ else {
+ szTemp[2] = '\0';
+ }
+ x = atoi( szTemp );
+ if(isUtc) {
+ /*
+ * 2-digit year.
+ * 0 <= year <= 50 : assume century 21
+ * 50 < year < 70 : illegal per PKIX
+ * 70 < year <= 99 : assume century 20
+ */
+ if(x <= 50) {
+ x += 100;
+ }
+ else if(x < 70) {
+ MacOSError::throwMe(errSecParam);
+ }
+ /* else century 20, OK */
+
+ /* bug fix... we need to end up with a 4-digit year! */
+ x += 1900;
+ }
+ /* by definition - tm_year is year - 1900 */
+ //tmp->tm_year = x - 1900;
+ date.year = x;
+
+ /* MONTH */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ /* in the string, months are from 1 to 12 */
+ if((x > 12) || (x <= 0)) {
+ MacOSError::throwMe(errSecParam);
+ }
+ /* in a tm, 0 to 11 */
+ //tmp->tm_mon = x - 1;
+ date.month = x;
+
+ /* DAY */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ /* 1..31 in both formats */
+ if((x > 31) || (x <= 0)) {
+ MacOSError::throwMe(errSecParam);
+ }
+ //tmp->tm_mday = x;
+ date.day = x;
+
+ /* HOUR */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 23) || (x < 0)) {
+ MacOSError::throwMe(errSecParam);
+ }
+ //tmp->tm_hour = x;
+ date.hour = x;
+
+ /* MINUTE */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 59) || (x < 0)) {
+ MacOSError::throwMe(errSecParam);
+ }
+ //tmp->tm_min = x;
+ date.minute = x;
+
+ /* SECOND */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 59) || (x < 0)) {
+ MacOSError::throwMe(errSecParam);
+ }
+ //tmp->tm_sec = x;
+ date.second = x;
+
+ CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+ CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
+ CFRelease(timeZone);
+
+ // Adjust abstime to local timezone
+ timeZone = CFTimeZoneCopyDefault();
+ absTime += CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
+ CFRelease(timeZone);
+
+ outMacDate = sint64(double(absTime + kCFAbsoluteTimeIntervalSince1904));
+}
+
+void MacSecondsToTimeString(uint32 inMacDate, uint32 inLength, void *outData)
+{
+ sint64 ldt = sint64(uint64(inMacDate));
+ MacLongDateTimeToTimeString(ldt, inLength, outData);
+}
+
+void MacLongDateTimeToTimeString(const sint64 &inMacDate,
+ uint32 inLength, void *outData)
+{
+ // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
+ CFAbsoluteTime absTime = inMacDate - kCFAbsoluteTimeIntervalSince1904;
+
+ // Remove local timezone component from absTime
+ CFTimeZoneRef timeZone = CFTimeZoneCopyDefault();
+ absTime -= CFTimeZoneGetSecondsFromGMT(timeZone, absTime);
+ CFRelease(timeZone);
+
+ timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+ CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absTime, timeZone);
+ CFRelease(timeZone);
+
+ if (inLength == 16)
+ {
+ sprintf((char *)(outData), "%04d%02d%02d%02d%02d%02dZ",
+ int(date.year % 10000), date.month, date.day,
+ date.hour, date.minute, int(date.second));
+ }
+ else if (inLength == 14)
+ {
+ /* UTC - 2 year digits - code which parses this assumes that
+ * (2-digit) years between 0 and 49 are in century 21 */
+ sprintf((char *)(outData), "%02d%02d%02d%02d%02d%02dZ",
+ int(date.year % 100), date.month, date.day,
+ date.hour, date.minute, int(date.second));
+ }
+ else
+ MacOSError::throwMe(errSecParam);
+}
+
+void
+CFDateToCssmDate(CFDateRef date, char *outCssmDate)
+{
+ CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+ CFGregorianDate gd = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date), timeZone);
+ sprintf(outCssmDate, "%04d%02d%02d%02d%02d%02dZ", (int)gd.year, gd.month, gd.day, gd.hour, gd.minute, (unsigned int)gd.second);
+ CFRelease(timeZone);
+}
+
+void
+CssmDateToCFDate(const char *cssmDate, CFDateRef *outCFDate)
+{
+ CFTimeZoneRef timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
+ CFGregorianDate gd;
+ unsigned int year, month, day, hour, minute, second;
+ sscanf(cssmDate, "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second);
+ gd.year = year;
+ gd.month = month;
+ gd.day = day;
+ gd.hour = hour;
+ gd.minute = minute;
+ gd.second = second;
+ *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
+ CFRelease(timeZone);
+}
+
+int
+CssmDateStringToCFDate(const char *cssmDate, unsigned int len, CFDateRef *outCFDate)
+{
+ CFTimeZoneRef timeZone;
+ CFGregorianDate gd;
+ CFTimeInterval ti=0;
+ char szTemp[5];
+ unsigned isUtc=0, isLocal=0, i;
+ int x;
+ unsigned noSeconds=0;
+ char *cp;
+
+ if((cssmDate == NULL) || (len == 0) || (outCFDate == NULL))
+ return 1;
+
+ /* tolerate NULL terminated or not */
+ if(cssmDate[len - 1] == '\0')
+ len--;
+
+ switch(len) {
+ case UTC_TIME_NOSEC_LEN: // 2-digit year, no seconds, not y2K compliant
+ isUtc = 1;
+ noSeconds = 1;
+ break;
+ case UTC_TIME_STRLEN: // 2-digit year, not Y2K compliant
+ isUtc = 1;
+ break;
+ case GENERALIZED_TIME_STRLEN: // 4-digit year
+ //isUtc = 0;
+ break;
+ case LOCALIZED_UTC_TIME_STRLEN: // "YYMMDDhhmmssThhmm" (where T=[+,-])
+ isUtc = 1;
+ // deliberate fallthrough
+ case LOCALIZED_TIME_STRLEN: // "YYYYMMDDhhmmssThhmm" (where T=[+,-])
+ isLocal = 1;
+ break;
+ default: // unknown format
+ return 1;
+ }
+
+ cp = (char *)cssmDate;
+
+ /* check that all characters except last (or timezone indicator, if localized) are digits */
+ for(i=0; i<(len - 1); i++) {
+ if ( !(isdigit(cp[i])) )
+ if ( !isLocal || !(cp[i]=='+' || cp[i]=='-') )
+ return 1;
+ }
+ /* check last character is a 'Z', unless localized */
+ if(!isLocal && cp[len - 1] != 'Z' ) {
+ return 1;
+ }
+
+ /* YEAR */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ if(!isUtc) {
+ /* two more digits */
+ szTemp[2] = *cp++;
+ szTemp[3] = *cp++;
+ szTemp[4] = '\0';
+ }
+ else {
+ szTemp[2] = '\0';
+ }
+ x = atoi( szTemp );
+ if(isUtc) {
+ /*
+ * 2-digit year.
+ * 0 <= year < 50 : assume century 21
+ * 50 <= year < 70 : illegal per PKIX
+ * 70 < year <= 99 : assume century 20
+ */
+ if(x < 50) {
+ x += 2000;
+ }
+ else if(x < 70) {
+ return 1;
+ }
+ else {
+ /* century 20 */
+ x += 1900;
+ }
+ }
+ gd.year = x;
+
+ /* MONTH */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ /* in the string, months are from 1 to 12 */
+ if((x > 12) || (x <= 0)) {
+ return 1;
+ }
+ gd.month = x;
+
+ /* DAY */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ /* 1..31 in both formats */
+ if((x > 31) || (x <= 0)) {
+ return 1;
+ }
+ gd.day = x;
+
+ /* HOUR */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 23) || (x < 0)) {
+ return 1;
+ }
+ gd.hour = x;
+
+ /* MINUTE */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 59) || (x < 0)) {
+ return 1;
+ }
+ gd.minute = x;
+
+ /* SECOND */
+ if(noSeconds) {
+ gd.second = 0;
+ }
+ else {
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp );
+ if((x > 59) || (x < 0)) {
+ return 1;
+ }
+ gd.second = x;
+ }
+
+ if (isLocal) {
+ /* ZONE INDICATOR */
+ ti = (*cp++ == '+') ? 1 : -1;
+ /* ZONE HH OFFSET */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp ) * 60 * 60;
+ ti *= x;
+ /* ZONE MM OFFSET */
+ szTemp[0] = *cp++;
+ szTemp[1] = *cp++;
+ szTemp[2] = '\0';
+ x = atoi( szTemp ) * 60;
+ ti += ((ti < 0) ? (x*-1) : x);
+ }
+ timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, ti);
+ if (!timeZone) return 1;
+ *outCFDate = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gd, timeZone));
+ CFRelease(timeZone);
+
+ return 0;
+}
+
+}; // end namespace CSSMDateTimeUtils
+
+} // end namespace Security