#!/usr/bin/env bash # -*- tab-width: 4 ; sh-basic-offset: 4 -*- # pkgbuild-check-nonfree # Copyright (C) 2011 Joseph Graham (Xylon) # Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) # Copyright (C) 2010-2012 Nicolás Reynolds # Copyright (C) 2012-2013, 2017 Luke Shumaker # # License: GNU GPLv3+ # # This file is part of Parabola. # # Parabola is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Parabola is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Parabola. If not, see . # I appologize that this program got *huge*. # It's not complicated, just long. . "$(librelib messages)" . "$(librelib conf)" . "$(librelib blacklist)" usage() { print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" print "Analyzes a PKGBUILD for freedom issues" echo prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.' echo print "Exit status (add them for combinations):" print " 0: Everything OK, no freedom issues" print " 1: Ran with error" print "Warning-level freedom issues:" print " 2: Uses unrecognized licenses, check them" print " 4: Uses GPL-incompatible licenses" print "Error-level freedom issues:" print " 8: Uses known unacceptable licenses" print " 16: Has nonfree dependencies" print " 32: Is a known nonfree package" echo print "Options:" flag '-c' 'Use the cached blacklist, do not try downloading' flag '-f' 'Allow running as root user' echo flag '-q' 'Be quiet' flag '-v' 'Be verbose' echo flag '-h' 'Show this message' } # Make sure these match pkgbuild-summarize-nonfree declare -ri _E_OK=0 declare -ri _E_ERROR=1 declare -ri _E_LIC_UNKNOWN=2 declare -ri _E_LIC_NOGPL=4 declare -ri _E_LIC_NONFREE=8 declare -ri _E_DEP_NONFREE=16 declare -ri _E_PKG_NONFREE=32 main() { # Parse flags local cache=false local asroot=false local v=1 while getopts 'cfqvh' arg; do case "$arg" in c) cache=true;; f) asroot=true;; q) v=0;; v) v=2;; h) usage; return $_E_OK;; *) usage >&2; return $_E_ERROR;; esac done shift $((OPTIND - 1)) if [[ $# -lt 1 ]]; then pkgbuilds=("$PWD/PKGBUILD") else pkgbuilds=("$@") fi # Do a check to see if we are running as root if [[ -w / ]] && ! $asroot; then error "Run as normal user, or use the -f option to run as root." return $_E_ERROR fi # Adjust the verbosity if [[ $v == 0 ]]; then error() { :; } warning() { :; } plain() { :; } info() { :; } elif [[ $v == 1 ]]; then info() { :; } elif [[ $v == 2 ]]; then info() { plain "$@"; } fi # Update the blacklist $cache || blacklist-update || return $_E_ERROR # Do the work declare -i ret=0 local pkgbuild for pkgbuild in "${pkgbuilds[@]}"; do pkgbuild_check "$pkgbuild" || ret=$((ret|$?)) done return $ret } # Helper functions ############################################################# # These should maybe be moved into lib/conf.sh # Usage: var="$(pkgbuild_get_pkg_str ${pkgname} ${varname})" # Gets a package-level string for a split-package pkgbuild_get_pkg_str() { [[ $# == 2 ]] || panic 'malformed call to pkgbuild_get_pkg_str' local pkg=$1 local var=$2 local indirect=${!var} eval "$(declare -f "package_$pkg" | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p")" printf '%s' "${indirect}" } # Usage: eval $(pkgbuild_get_pkg_ary ${pkgname} ${varname} [$variable_name_to_set]) # Gets a package-level array for a split-package pkgbuild_get_pkg_ary() { [[ $# == 2 ]] || [[ $# == 3 ]] || panic 'malformed call to pkgbuild_get_pkg_ary' local pkg=$1 local var=$2 local out="${3:-$var}" local ary="${var}[@]" local indirect=("${!ary}") eval "$(declare -f "package_$pkg" | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p")" declare -p indirect|sed "s/ indirect=/ ${out}=/" } # Checker functions ############################################################ # Usage: check_lic "${licence}" # Check a license name to see if it is OK check_lic() { [[ $# == 1 ]] || panic 'malformed call to check_license' local license=$1 info 'Checking license: %s' "$license" if [[ -e "/usr/share/licenses/common/$license" ]]; then return $_E_OK else case "${license#custom:}" in WTFPL) # accept as common, I think it should be in the licenses package return $_E_OK;; BSD1|BSD2|BSD3|MIT|X11) # accept these as common; they can't be included in the # 'licenses' package because some text must be customized return $_E_OK;; BSD4) warning "The 4-clause BSD license is free but has practical problems." return $_E_LIC_NOGPL;; BSD) warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses." return $_E_LIC_UNKNOWN;; JSON) error "License '%s' is a known non-free license." "$license" return $_E_LIC_NONFREE;; *) warning "License '%s' is not a common (recognized) license." "$license" return $_E_LIC_UNKNOWN;; esac fi panic 'code should never be reached' } # Usage: check_dep "${dependency}" # Checks for ${dependency} in the blacklist check_dep() { [[ $# == 1 ]] || panic 'malformed call to check_dep' local pkg=$1 local line rep line="$(blacklist-cat|blacklist-lookup "$pkg")" rep="$(blacklist-get-rep <<<"$line")" if [[ -z $line ]]; then # not mentioned in blacklist; free info '%s: not blacklisted' "$pkg" return $_E_OK elif [[ -z $rep ]]; then # non-free with no replacement plain '%s: blacklisted' "$pkg" return $_E_DEP_NONFREE else # non-free with free replacement if [[ "$rep" == "$pkg" ]]; then info '%s: repackaged with the same name' "$pkg" else info '%s: replaced by %s' "$pkg" "$rep" fi return $_E_OK fi panic 'code should never be reached' } # Usage: check_pkg "${pkgname}" # Checks for ${pkgname} in the blacklist check_pkg() { [[ $# == 1 ]] || panic 'malformed call to check_pkg' check_dep "$@" case $? in $_E_OK) return $_E_OK;; $_E_DEP_NONFREE) return $_E_PKG_NONFREE;; *) panic 'unexpected return code from check_dep';; esac panic 'code should never be reached' } # Usage: pkgbuild_ckec $pkgbuild # Check whether a PKGBUILD has any issues (using the above) pkgbuild_check() ( [[ $# == 1 ]] || panic 'malformed call to pkgbuild_check' local pkgbuild=$1 load_PKGBUILD "$pkgbuild" if [[ -z $pkgname ]]; then return $_E_ERROR # not a PKGBUILD fi declare -i ret=0 # the return status local dep lic # iterators for us in `for` loops local ck_deps ck_lics # lists of deps and licenses that have been checked if [[ ${#pkgname[@]} == 1 ]]; then msg2 'Inspecting package pkgname=%q (%s)' "$pkgname" "$(get_full_version)" else msg2 'Inspecting split package pkgbase=%q (%s)' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" fi # Check if this is blacklisted check_pkg "${pkgbase:-${pkgname[0]}}" || ret=$((ret|$?)) # Check if dependencies are blacklisted for dep in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" \ "${optdepends[@]%%:*}" "${mkdepends[@]}" do check_dep "$dep" || ret=$((ret|$?)) ck_deps+=("$dep") done # Check the licenses for lic in "${license[@]}"; do check_lic "$lic" || ret=$((ret|$?)) ck_lics+=("$lic") done if [[ ${#pkgname[@]} == 1 ]]; then # Non-split package # Make sure a license is set if [[ ${#ck_lics[@]} == 0 ]]; then error "The license array is empty" ret=$((ret|_E_ERROR)) fi else # Split package # Check the individual split packages local _pkgname _license _depends _optdepends for _pkgname in "${pkgname[@]}"; do msg2 'Inspecting split package pkgname=%q (%s)' "$_pkgname" "$(get_full_version "$_pkgname")" eval "$(pkgbuild_get_pkg_ary "$_pkgname" license _license)" eval "$(pkgbuild_get_pkg_ary "$_pkgname" depends _depends)" eval "$(pkgbuild_get_pkg_ary "$_pkgname" optdepends _optdepends)" # Check if this is blacklisted check_pkg "$_pkgname" || ret=$((ret|$?)) # Check if dependencies are blacklisted for dep in "${_depends[@]}" "${_optdepends[@]%%:*}"; do if ! in_array "$dep" "${ck_deps[@]}"; then check_dep "$dep" || ret=$((ret|$?)) fi done # Check the licenses for lic in "${_license[@]}"; do if ! in_array "$lic" "${ck_lics[@]}"; then check_lic "$lic" || ret=$((ret|$?)) fi done if [[ ${#_license[@]} == 0 ]]; then error "The license array is empty" ret=$((ret|_E_ERROR)) fi done fi return $ret ) main "$@"