1/* 2 * Copyright (c) 2009 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <string.h> 30#include <sys/types.h> 31#include <AssertMacros.h> 32 33#if !KERNEL 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include "kxld.h" 37 #include "kxld_types.h" 38#else 39 #include <libkern/libkern.h> 40 #include <libkern/kxld.h> 41 #include <libkern/kxld_types.h> 42#endif /* KERNEL */ 43 44#include "kxld_util.h" 45 46/****************************************************************************** 47* Macros 48******************************************************************************/ 49 50#define kCopyrightToken "Copyright © " 51#define kRightsToken " Apple Inc. All rights reserved." 52 53/****************************************************************************** 54* Globals 55******************************************************************************/ 56 57#if TEST 58 59#include <CoreFoundation/CoreFoundation.h> 60 61CFStringRef passes[] = { 62 CFSTR("Copyright © 2008 Apple Inc. All rights reserved."), 63 CFSTR("Copyright © 2004-2008 Apple Inc. All rights reserved."), 64 CFSTR("Copyright © 2004,2006 Apple Inc. All rights reserved."), 65 CFSTR("Copyright © 2004,2006-2008 Apple Inc. All rights reserved."), 66 CFSTR("Copyright © 2004 , 2006-2008 Apple Inc. All rights reserved."), 67 CFSTR("Copyright © 1998,2000-2002,2004,2006-2008 Apple Inc. All rights reserved."), 68 CFSTR("IOPCIFamily 2.1; Copyright © 2004,2006-2008 Apple Inc. All rights reserved."), 69 CFSTR("Copyright © 2004,2006-2008 Apple Inc. All rights reserved. The quick brown fox jumped over the lazy dog."), 70 CFSTR("IOPCIFamily 2.1; Copyright © 2004,2006-2008 Apple Inc. All rights reserved. The quick brown fox jumped over the lazy dog.") 71}; 72 73CFStringRef fails[] = { 74 CFSTR("Copyright © 2007-08 Apple Inc. All rights reserved."), 75 CFSTR("Copyright (c) 2007 Apple Inc. All rights reserved."), 76 CFSTR("Copyright © 2007- Apple Inc. All rights reserved."), 77 CFSTR("Copyright © 2007 - 2008 Apple Inc. All rights reserved.") 78}; 79 80extern char *createUTF8CStringForCFString(CFStringRef aString); 81 82#endif /* TEST */ 83 84/****************************************************************************** 85* Prototypes 86******************************************************************************/ 87 88static boolean_t is_space(const char c) 89 __attribute__((const)); 90static boolean_t is_token_delimiter(const char c) 91 __attribute__((const)); 92static boolean_t is_token_break(const char *str) 93 __attribute__((pure, nonnull)); 94static boolean_t token_is_year(const char *str) 95 __attribute__((pure, nonnull)); 96static boolean_t token_is_yearRange(const char *str) 97 __attribute__((pure, nonnull)); 98static boolean_t dates_are_valid(const char *str, const u_long len) 99 __attribute__((pure, nonnull)); 100 101/****************************************************************************** 102******************************************************************************/ 103static boolean_t 104is_space(const char c) 105{ 106 switch (c) { 107 case ' ': 108 case '\t': 109 case '\n': 110 case '\v': 111 case '\f': 112 case '\r': 113 return TRUE; 114 } 115 116 return FALSE; 117} 118 119/****************************************************************************** 120******************************************************************************/ 121static boolean_t 122is_token_delimiter(const char c) 123{ 124 return (is_space(c) || (',' == c) || ('\0' == c)); 125} 126 127/****************************************************************************** 128* A token break is defined to be the boundary where the current character is 129* not a token delimiter and the next character is a token delimiter. 130******************************************************************************/ 131static boolean_t 132is_token_break(const char *str) 133{ 134 /* This is safe because '\0' is a token delimiter, so the second check 135 * will not execute if we reach the end of the string. 136 */ 137 return (!is_token_delimiter(str[0]) && is_token_delimiter(str[1])); 138} 139 140/****************************************************************************** 141* A year is defined by the following regular expression: 142* /[0-9]{4}$/ 143******************************************************************************/ 144#define kYearLen 5 145static boolean_t 146token_is_year(const char *str) 147{ 148 boolean_t result = FALSE; 149 u_int i = 0; 150 151 for (i = 0; i < kYearLen - 1; ++i) { 152 if (str[i] < '0' || str[i] > '9') goto finish; 153 } 154 155 if (str[i] != '\0') goto finish; 156 157 result = TRUE; 158finish: 159 return result; 160} 161 162/****************************************************************************** 163* A year range is defined by the following regular expression: 164* /[0-9]{4}[-][0-9]{4}$/ 165******************************************************************************/ 166#define kYearRangeLen 10 167static boolean_t 168token_is_yearRange(const char *str) 169{ 170 boolean_t result = FALSE; 171 u_int i = 0; 172 173 for (i = 0; i < kYearLen - 1; ++i) { 174 if (str[i] < '0' || str[i] > '9') goto finish; 175 } 176 177 if (str[i] != '-') goto finish; 178 179 for (i = kYearLen; i < kYearRangeLen - 1; ++i) { 180 if (str[i] < '0' || str[i] > '9') goto finish; 181 } 182 183 if (str[i] != '\0') goto finish; 184 185 result = TRUE; 186finish: 187 return result; 188} 189 190/****************************************************************************** 191* The dates_are_valid function takes as input a comma-delimited list of years 192* and year ranges, and returns TRUE if all years and year ranges are valid 193* and well-formed. 194******************************************************************************/ 195static boolean_t 196dates_are_valid(const char *str, const u_long len) 197{ 198 boolean_t result = FALSE; 199 const char *token_ptr = NULL; 200 char token_buffer[kYearRangeLen]; 201 u_int token_index = 0; 202 203 token_index = 0; 204 for (token_ptr = str; token_ptr < str + len; ++token_ptr) { 205 if (is_token_delimiter(*token_ptr) && !token_index) continue; 206 207 /* If we exceed the length of a year range, the test will not succeed, 208 * so just fail now. This limits the length of the token buffer that 209 * we have to keep around. 210 */ 211 if (token_index == kYearRangeLen) goto finish; 212 213 token_buffer[token_index++] = *token_ptr; 214 if (is_token_break(token_ptr)) { 215 if (!token_index) continue; 216 217 token_buffer[token_index] = '\0'; 218 219 if (!token_is_year(token_buffer) && 220 !token_is_yearRange(token_buffer)) 221 { 222 goto finish; 223 } 224 225 token_index = 0; 226 } 227 } 228 229 result = TRUE; 230finish: 231 return result; 232} 233 234/****************************************************************************** 235* The copyright string is composed of three parts: 236* 1) A copyright notice, "Copyright ©" 237* 2) One or more years or year ranges, e.g., "2004,2006-2008" 238* 3) A rights reserved notice, "Apple Inc. All Rights Reserved." 239* We check the validity of the string by searching for both the copyright 240 241* notice and the rights reserved notice. If both are found, we then check that 242* the text between the two notices contains only valid years and year ranges. 243******************************************************************************/ 244boolean_t 245kxld_validate_copyright_string(const char *str) 246{ 247 boolean_t result = FALSE; 248 const char *copyright = NULL; 249 const char *rights = NULL; 250 char *date_str = NULL; 251 u_long len = 0; 252 253 copyright = kxld_strstr(str, kCopyrightToken); 254 rights = kxld_strstr(str, kRightsToken); 255 256 if (!copyright || !rights || copyright > rights) goto finish; 257 258 str = copyright + const_strlen(kCopyrightToken); 259 260 len = rights - str; 261 date_str = kxld_alloc(len+1); 262 if (!date_str) goto finish; 263 264 strncpy(date_str, str, len); 265 date_str[len] = '\0'; 266 267 if (!dates_are_valid(date_str, len)) goto finish; 268 269 result = TRUE; 270finish: 271 if (date_str) kxld_free(date_str, len+1); 272 return result; 273} 274 275#if TEST 276 277/****************************************************************************** 278******************************************************************************/ 279int 280main(int argc __unused, char *argv[] __unused) 281{ 282 int result = 1; 283 CFStringRef the_string = NULL; 284 const char *str = NULL; 285 u_int i = 0; 286 287 printf("The following %lu strings should pass\n", 288 const_array_len(passes)); 289 290 for (i = 0; i < const_array_len(passes); ++i) { 291 the_string = passes[i]; 292 str = createUTF8CStringForCFString(the_string); 293 if (!str) goto finish; 294 295 printf("%s: %s\n", 296 (kxld_validate_copyright_string(str)) ? "pass" : "fail", str); 297 } 298 299 printf("\nThe following %lu strings should fail\n", 300 const_array_len(fails)); 301 302 for (i = 0; i < const_array_len(fails); ++i) { 303 the_string = fails[i]; 304 str = createUTF8CStringForCFString(the_string); 305 if (!str) goto finish; 306 307 printf("%s: %s\n", 308 (kxld_validate_copyright_string(str)) ? "pass" : "fail", str); 309 } 310 311 result = 0; 312 313finish: 314 return result; 315} 316#endif /* TEST */ 317 318