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 | |
61 | CFStringRef 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 | |
73 | CFStringRef 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 | |
80 | extern char *createUTF8CStringForCFString(CFStringRef aString); |
81 | |
82 | #endif /* TEST */ |
83 | |
84 | /****************************************************************************** |
85 | * Prototypes |
86 | ******************************************************************************/ |
87 | |
88 | static boolean_t is_space(const char c) |
89 | __attribute__((const)); |
90 | static boolean_t is_token_delimiter(const char c) |
91 | __attribute__((const)); |
92 | static boolean_t is_token_break(const char *str) |
93 | __attribute__((pure, nonnull)); |
94 | static boolean_t token_is_year(const char *str) |
95 | __attribute__((pure, nonnull)); |
96 | static boolean_t token_is_yearRange(const char *str) |
97 | __attribute__((pure, nonnull)); |
98 | static boolean_t dates_are_valid(const char *str, const u_long len) |
99 | __attribute__((pure, nonnull)); |
100 | |
101 | /****************************************************************************** |
102 | ******************************************************************************/ |
103 | static boolean_t |
104 | is_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 | ******************************************************************************/ |
121 | static boolean_t |
122 | is_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 | ******************************************************************************/ |
131 | static boolean_t |
132 | is_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 |
145 | static boolean_t |
146 | token_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; |
158 | finish: |
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 |
167 | static boolean_t |
168 | token_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; |
186 | finish: |
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 | ******************************************************************************/ |
195 | static boolean_t |
196 | dates_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; |
230 | finish: |
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 | ******************************************************************************/ |
244 | boolean_t |
245 | kxld_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; |
270 | finish: |
271 | if (date_str) kxld_free(date_str, len+1); |
272 | return result; |
273 | } |
274 | |
275 | #if TEST |
276 | |
277 | /****************************************************************************** |
278 | ******************************************************************************/ |
279 | int |
280 | main(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 | |
313 | finish: |
314 | return result; |
315 | } |
316 | #endif /* TEST */ |
317 | |
318 | |