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