1 /*	$NetBSD: license.c,v 1.4 2013/04/20 15:29:23 wiz Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #if HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 #include <nbcompat.h>
37 
38 #if HAVE_ERR_H
39 #include <err.h>
40 #endif
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "lib.h"
45 
46 #define	HASH_SIZE	521
47 
48 const char *default_acceptable_licenses =
49     "apache-1.1 apache-2.0 "
50     "arphic-public "
51     "artistic artistic-2.0 "
52     "boost-license "
53     "cc-by-sa-v3.0 "
54     "cddl-1.0 "
55     "cpl-1.0 "
56     "epl-v1.0 "
57     "gnu-fdl-v1.1 gnu-fdl-v1.2 gnu-fdl-v1.3 "
58     "gnu-gpl-v1 "
59     "gnu-gpl-v2 gnu-lgpl-v2 gnu-lgpl-v2.1 "
60     "gnu-gpl-v3 gnu-lgpl-v3 "
61     "ibm-public-license-1.0 "
62     "ipafont "
63     "isc "
64     "lppl-1.3c "
65     "lucent "
66     "miros "
67     "mit "
68     "mpl-1.0 mpl-1.1 mpl-2.0 "
69     "mplusfont "
70     "ofl-v1.0 ofl-v1.1 "
71     "original-bsd modified-bsd 2-clause-bsd "
72     "php "
73     "png-license "
74     "postgresql-license "
75     "public-domain "
76     "python-software-foundation "
77     "qpl-v1.0 "
78     "sleepycat-public "
79     "unlicense "
80     "x11 "
81     "zlib "
82     "zpl";
83 
84 #ifdef DEBUG
85 static size_t hash_collisions;
86 #endif
87 
88 static char **license_hash[HASH_SIZE];
89 static const char license_spaces[] = " \t\n";
90 static const char license_chars[] =
91     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.";
92 
93 static size_t
94 hash_license(const char *license, size_t len)
95 {
96 	size_t hash;
97 
98 	for (hash = 0; *license && len; ++license, --len)
99 		hash = *license + hash * 32;
100 	return hash % HASH_SIZE;
101 }
102 
103 static void
104 add_license_internal(const char *license, size_t len)
105 {
106 	char *new_license;
107 	size_t slot, i;
108 
109 	slot = hash_license(license, len);
110 
111 	new_license = malloc(len + 1);
112 	memcpy(new_license, license, len);
113 	new_license[len] = '\0';
114 
115 	if (license_hash[slot] == NULL) {
116 		license_hash[slot] = calloc(sizeof(char *), 2);
117 		license_hash[slot][0] = new_license;
118 	} else {
119 		for (i = 0; license_hash[slot][i]; ++i) {
120 			if (!memcmp(license_hash[slot][i], license, len) &&
121 			    license_hash[slot][i][len] == '\0') {
122 				free(new_license);
123 				return;
124 			}
125 		}
126 
127 #ifdef DEBUG
128 		++hash_collisions;
129 #endif
130 
131 		license_hash[slot] = realloc(license_hash[slot],
132 		    sizeof(char *) * (i + 2));
133 		license_hash[slot][i] = new_license;
134 		license_hash[slot][i + 1] = NULL;
135 	}
136 }
137 
138 int
139 add_licenses(const char *line)
140 {
141 	const char *next;
142 
143 	if (line == NULL)
144 		return 0;
145 
146 	for (line += strspn(line, license_spaces); line; ) {
147 		next = line + strspn(line, license_chars);
148 		if (next == line)
149 			return *line ? -1 : 0;
150 		add_license_internal(line, next - line);
151 		line = next + strspn(next, license_spaces);
152 		if (next == line)
153 			return *line ? -1 : 0;
154 	}
155 	return 0;
156 }
157 
158 static int
159 acceptable_license_internal(const char *license, size_t len)
160 {
161 	size_t slot, i;
162 
163 	slot = hash_license(license, len);
164 
165 	if (license_hash[slot] == NULL)
166 		return 0;
167 
168 	for (i = 0; license_hash[slot][i]; ++i) {
169 		if (strncmp(license_hash[slot][i], license, len) == 0 &&
170 		    license_hash[slot][i][len] == '\0')
171 			return 1;
172 	}
173 
174 	return 0;
175 }
176 
177 int
178 acceptable_license(const char *license)
179 {
180 	size_t len;
181 
182 	len = strlen(license);
183 	if (strspn(license, license_chars) != len) {
184 		warnx("Invalid character in license name at position %" PRIzu, len);
185 		return -1;
186 	}
187 
188 	return acceptable_license_internal(license, len);
189 }
190 
191 static int
192 acceptable_pkg_license_internal(const char **licensep, int toplevel, const char *start)
193 {
194 	const char *license = *licensep;
195 	int need_parenthesis, is_true = 0;
196 	int expr_type = 0; /* 0: unset, 1: or, 2: and */
197 	size_t len;
198 
199 	license += strspn(license, license_spaces);
200 
201 	if (*license == '(' && !toplevel) {
202 		need_parenthesis = 1;
203 		++license;
204 		license += strspn(license, license_spaces);
205 	} else {
206 		need_parenthesis = 0;
207 	}
208 
209 	for (;;) {
210 		if (*license == '(') {
211 			switch (acceptable_pkg_license_internal(&license, 0, start)) {
212 			case -1:
213 				return -1;
214 			case 0:
215 				if (expr_type == 2)
216 					is_true = 0;
217 				break;
218 			case 1:
219 				is_true = 1;
220 				break;
221 			}
222 			license += strspn(license, license_spaces);
223 		} else {
224 			len = strspn(license, license_chars);
225 			if (len == 0) {
226 				warnx("Invalid character in license name at position %" PRIzu, license - start + 1);
227 				return -1;
228 			}
229 
230 			if (acceptable_license_internal(license, len)) {
231 				if (expr_type != 2)
232 					is_true = 1;
233 			} else if (expr_type == 2) {
234 				is_true = 0;
235 			}
236 
237 			license += len;
238 
239 			len = strspn(license, license_spaces);
240 			if (len == 0 && *license && *license  != ')') {
241 				warnx("Missing space at position %" PRIzu, license - start + 1);
242 				return -1;
243 			}
244 			license += len;
245 		}
246 
247 		if (*license == ')') {
248 			if (!need_parenthesis) {
249 				warnx("Missing open parenthesis at position %" PRIzu, license - start + 1);
250 				return -1;
251 			}
252 			*licensep = license + 1;
253 			return is_true;
254 		}
255 		if (*license == '\0') {
256 			if (need_parenthesis) {
257 				warnx("Unbalanced parenthesis at position %" PRIzu, license - start + 1);
258 				return -1;
259 			}
260 			*licensep = license;
261 			return is_true;
262 		}
263 
264 		if (strncmp(license, "AND", 3) == 0) {
265 			if (expr_type == 1) {
266 				warnx("Invalid operator in OR expression at position %" PRIzu, license - start + 1);
267 				return -1;
268 			}
269 			expr_type = 2;
270 			license += 3;
271 		} else if (strncmp(license, "OR", 2) == 0) {
272 			if (expr_type == 2) {
273 				warnx("Invalid operator in AND expression at position %" PRIzu, license - start + 1);
274 				return -1;
275 			}
276 			expr_type = 1;
277 			license += 2;
278 		} else {
279 			warnx("Invalid operator at position %" PRIzu, license - start + 1);
280 			return -1;
281 		}
282 		len = strspn(license, license_spaces);
283 		if (len == 0 && *license != '(') {
284 			warnx("Missing space at position %" PRIzu, license - start + 1);
285 			return -1;
286 		}
287 		license += len;
288 	}
289 }
290 
291 int
292 acceptable_pkg_license(const char *license)
293 {
294 	int ret;
295 
296 	ret = acceptable_pkg_license_internal(&license, 1, license);
297 	if (ret == -1)
298 		return -1;
299 	license += strspn(license, license_spaces);
300 	if (*license) {
301 		warnx("Trailing garbage in license specification");
302 		return -1;
303 	}
304 	return ret;
305 }
306 
307 void
308 load_license_lists(void)
309 {
310 	if (add_licenses(getenv("PKGSRC_ACCEPTABLE_LICENSES")))
311 		errx(EXIT_FAILURE, "syntax error in PKGSRC_ACCEPTABLE_LICENSES");
312 	if (add_licenses(acceptable_licenses))
313 		errx(EXIT_FAILURE, "syntax error in ACCEPTABLE_LICENSES");
314 	if (add_licenses(getenv("PKGSRC_DEFAULT_ACCEPTABLE_LICENSES")))
315 		errx(EXIT_FAILURE, "syntax error in PKGSRC_DEFAULT_ACCEPTABLE_LICENSES");
316 	if (add_licenses(default_acceptable_licenses))
317 		errx(EXIT_FAILURE, "syntax error in DEFAULT_ACCEPTABLE_LICENSES");
318 }
319