xref: /netbsd-src/external/bsd/pkg_install/dist/lib/license.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: license.c,v 1.7 2018/03/25 04:04:36 sevan 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     "cc0-1.0-universal "
55     "cddl-1.0 "
56     "cecill-2.1 "
57     "cpl-1.0 "
58     "epl-v1.0 "
59     "eupl-v1.1 "
60     "gfsl "
61     "gnu-fdl-v1.1 gnu-fdl-v1.2 gnu-fdl-v1.3 "
62     "gnu-gpl-v1 "
63     "gnu-gpl-v2 gnu-lgpl-v2 gnu-lgpl-v2.1 "
64     "gnu-gpl-v3 gnu-lgpl-v3 "
65     "happy "
66     "hpnd "
67     "info-zip "
68     "ipafont "
69     "ipl-1.0 "
70     "isc "
71     "lppl-1.0 lppl-1.2 lppl-1.3c "
72     "lucent "
73     "miros "
74     "mit "
75     "mpl-1.0 mpl-1.1 mpl-2.0 "
76     "mplusfont "
77     "ofl-v1.0 ofl-v1.1 "
78     "openssl "
79     "original-bsd modified-bsd 2-clause-bsd "
80     "paratype "
81     "php "
82     "png-license "
83     "postgresql-license "
84     "public-domain "
85     "python-software-foundation "
86     "qpl-v1.0 "
87     "sgi-free-software-b-v2.0 "
88     "sissl-1.1 "
89     "sleepycat-public "
90     "unicode "
91     "unlicense "
92     "vera-ttf-license "
93     "w3c "
94     "x11 "
95     "zlib "
96     "zpl-2.0 zpl-2.1 "
97     "zsh";
98 
99 #ifdef DEBUG
100 static size_t hash_collisions;
101 #endif
102 
103 static char **license_hash[HASH_SIZE];
104 static const char license_spaces[] = " \t\n";
105 static const char license_chars[] =
106     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.";
107 
108 static size_t
109 hash_license(const char *license, size_t len)
110 {
111 	size_t hash;
112 
113 	for (hash = 0; *license && len; ++license, --len)
114 		hash = *license + hash * 32;
115 	return hash % HASH_SIZE;
116 }
117 
118 static void
119 add_license_internal(const char *license, size_t len)
120 {
121 	char *new_license;
122 	size_t slot, i;
123 
124 	slot = hash_license(license, len);
125 
126 	new_license = malloc(len + 1);
127 	memcpy(new_license, license, len);
128 	new_license[len] = '\0';
129 
130 	if (license_hash[slot] == NULL) {
131 		license_hash[slot] = calloc(sizeof(char *), 2);
132 		license_hash[slot][0] = new_license;
133 	} else {
134 		for (i = 0; license_hash[slot][i]; ++i) {
135 			if (!memcmp(license_hash[slot][i], license, len) &&
136 			    license_hash[slot][i][len] == '\0') {
137 				free(new_license);
138 				return;
139 			}
140 		}
141 
142 #ifdef DEBUG
143 		++hash_collisions;
144 #endif
145 
146 		license_hash[slot] = realloc(license_hash[slot],
147 		    sizeof(char *) * (i + 2));
148 		license_hash[slot][i] = new_license;
149 		license_hash[slot][i + 1] = NULL;
150 	}
151 }
152 
153 int
154 add_licenses(const char *line)
155 {
156 	const char *next;
157 
158 	if (line == NULL)
159 		return 0;
160 
161 	for (line += strspn(line, license_spaces); line; ) {
162 		next = line + strspn(line, license_chars);
163 		if (next == line)
164 			return *line ? -1 : 0;
165 		add_license_internal(line, next - line);
166 		line = next + strspn(next, license_spaces);
167 		if (next == line)
168 			return *line ? -1 : 0;
169 	}
170 	return 0;
171 }
172 
173 static int
174 acceptable_license_internal(const char *license, size_t len)
175 {
176 	size_t slot, i;
177 
178 	slot = hash_license(license, len);
179 
180 	if (license_hash[slot] == NULL)
181 		return 0;
182 
183 	for (i = 0; license_hash[slot][i]; ++i) {
184 		if (strncmp(license_hash[slot][i], license, len) == 0 &&
185 		    license_hash[slot][i][len] == '\0')
186 			return 1;
187 	}
188 
189 	return 0;
190 }
191 
192 int
193 acceptable_license(const char *license)
194 {
195 	size_t len;
196 
197 	len = strlen(license);
198 	if (strspn(license, license_chars) != len) {
199 		warnx("Invalid character in license name at position %" PRIzu, len);
200 		return -1;
201 	}
202 
203 	return acceptable_license_internal(license, len);
204 }
205 
206 static int
207 acceptable_pkg_license_internal(const char **licensep, int toplevel, const char *start)
208 {
209 	const char *license = *licensep;
210 	int need_parenthesis, is_true = 0;
211 	int expr_type = 0; /* 0: unset, 1: or, 2: and */
212 	size_t len;
213 
214 	license += strspn(license, license_spaces);
215 
216 	if (*license == '(' && !toplevel) {
217 		need_parenthesis = 1;
218 		++license;
219 		license += strspn(license, license_spaces);
220 	} else {
221 		need_parenthesis = 0;
222 	}
223 
224 	for (;;) {
225 		if (*license == '(') {
226 			switch (acceptable_pkg_license_internal(&license, 0, start)) {
227 			case -1:
228 				return -1;
229 			case 0:
230 				if (expr_type == 2)
231 					is_true = 0;
232 				break;
233 			case 1:
234 				is_true = 1;
235 				break;
236 			}
237 			license += strspn(license, license_spaces);
238 		} else {
239 			len = strspn(license, license_chars);
240 			if (len == 0) {
241 				warnx("Invalid character in license name at position %" PRIzu, license - start + 1);
242 				return -1;
243 			}
244 
245 			if (acceptable_license_internal(license, len)) {
246 				if (expr_type != 2)
247 					is_true = 1;
248 			} else if (expr_type == 2) {
249 				is_true = 0;
250 			}
251 
252 			license += len;
253 
254 			len = strspn(license, license_spaces);
255 			if (len == 0 && *license && *license  != ')') {
256 				warnx("Missing space at position %" PRIzu, license - start + 1);
257 				return -1;
258 			}
259 			license += len;
260 		}
261 
262 		if (*license == ')') {
263 			if (!need_parenthesis) {
264 				warnx("Missing open parenthesis at position %" PRIzu, license - start + 1);
265 				return -1;
266 			}
267 			*licensep = license + 1;
268 			return is_true;
269 		}
270 		if (*license == '\0') {
271 			if (need_parenthesis) {
272 				warnx("Unbalanced parenthesis at position %" PRIzu, license - start + 1);
273 				return -1;
274 			}
275 			*licensep = license;
276 			return is_true;
277 		}
278 
279 		if (strncmp(license, "AND", 3) == 0) {
280 			if (expr_type == 1) {
281 				warnx("Invalid operator in OR expression at position %" PRIzu, license - start + 1);
282 				return -1;
283 			}
284 			expr_type = 2;
285 			license += 3;
286 		} else if (strncmp(license, "OR", 2) == 0) {
287 			if (expr_type == 2) {
288 				warnx("Invalid operator in AND expression at position %" PRIzu, license - start + 1);
289 				return -1;
290 			}
291 			expr_type = 1;
292 			license += 2;
293 		} else {
294 			warnx("Invalid operator at position %" PRIzu, license - start + 1);
295 			return -1;
296 		}
297 		len = strspn(license, license_spaces);
298 		if (len == 0 && *license != '(') {
299 			warnx("Missing space at position %" PRIzu, license - start + 1);
300 			return -1;
301 		}
302 		license += len;
303 	}
304 }
305 
306 int
307 acceptable_pkg_license(const char *license)
308 {
309 	int ret;
310 
311 	ret = acceptable_pkg_license_internal(&license, 1, license);
312 	if (ret == -1)
313 		return -1;
314 	license += strspn(license, license_spaces);
315 	if (*license) {
316 		warnx("Trailing garbage in license specification");
317 		return -1;
318 	}
319 	return ret;
320 }
321 
322 void
323 load_license_lists(void)
324 {
325 	if (add_licenses(getenv("PKGSRC_ACCEPTABLE_LICENSES")))
326 		errx(EXIT_FAILURE, "syntax error in PKGSRC_ACCEPTABLE_LICENSES");
327 	if (add_licenses(acceptable_licenses))
328 		errx(EXIT_FAILURE, "syntax error in ACCEPTABLE_LICENSES");
329 	if (add_licenses(getenv("PKGSRC_DEFAULT_ACCEPTABLE_LICENSES")))
330 		errx(EXIT_FAILURE, "syntax error in PKGSRC_DEFAULT_ACCEPTABLE_LICENSES");
331 	if (add_licenses(default_acceptable_licenses))
332 		errx(EXIT_FAILURE, "syntax error in DEFAULT_ACCEPTABLE_LICENSES");
333 }
334