summaryrefslogtreecommitdiff
path: root/src/lib/blacklist.sh
blob: edea28df7aeb4372c3a98a924621fed1fa9a64df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env bash
# This may be included with or without `set -euE`

# Copyright (C) 2013-2014, 2016-2018  Luke Shumaker <lukeshu@parabola.nu>
# Copyright (C) 2017                  Isaac David <isacdaavid@isacdaavid.info>
# Copyright (C) 2020,2023             bill-auger <bill-auger@programmer.net>
#
# License: GNU GPLv2+
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

# make sure XDG_CACHE_HOME is set
source "$(librelib conf)"


## constants ##

# CACHE_DIR allows running (eg: `db-import`) via CLI (eg: manually as 'repo')
readonly CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}"
readonly BLACKLIST_CACHED="$CACHE_DIR/libretools/blacklist.txt"
readonly PACKAGE_BLACKLIST=/usr/share/doc/your-freedom/blacklist.txt
readonly BLACKLIST_VCSDIR=/srv/git/blacklist.git
readonly BLACKLIST_PKGNAME=your-freedom


## helpers ##

is_valid_blacklist() { # (blacklist_file)
	local blacklist_file="$1"

	if [[ -e "$blacklist_file" ]]                && \
	   grep -q ^base:base:     "$blacklist_file" && \
	   grep -q ^pacman:pacman: "$blacklist_file"    ; then
		return 0
	else
		return 1
	fi
}


## utilities ##

# Usage: blacklist-normalize <$file
# Normalizes the syntax of the blacklist on stdin.
blacklist-normalize() {
	# dynamically build sed expression based on number of fields
	local -a expr
	local fields=5 sep=: i
	for (( i = $fields - 2; i >= 0; --i )); do
		expr+=('-e' "s/^[^:]*(:[^:]*){${i}}$/&${sep}/")
		sep+=${sep:0:1}
	done
	sed -r -e '/^#/d' "${expr[@]}"
}

# Usage: blacklist-cat
# Prints the blacklist.
# Uses the cache, but downloads it if it doesn't exist.
# Also normalizes the blacklist for easier parsing.
blacklist-cat() {
	if ! is_valid_blacklist "$BLACKLIST_CACHED"; then
		blacklist-update
	fi

	blacklist-normalize < "$BLACKLIST_CACHED"
}

# Usage: blacklist-update
# Updates (or creates) the cached copy of the blacklist.
blacklist-update() (
	source "$(librelib messages)"
	load_conf libretools.conf BLACKLIST || :

	local remote_blacklist="$BLACKLIST"
	local prevwd=$(pwd)
	local tmp_vcs_dir   ; tmp_vcs_dir="$(  mktemp -t -d "blacklist.git.XXXXXXXXXX"       )" ;
	local tmp_blacklist ; tmp_blacklist="$(mktemp -t    "blacklist.txt.XXXXXXXXXX"       )" ;
	local tmp_log       ; tmp_log="$(      mktemp -t    "blacklist-update.log.XXXXXXXXXX")" ;

	stat_busy "Synchronizing '$BLACKLIST_PKGNAME' blacklist"

	# First, try downloading blacklist via HTTP.
	#   * requires: active internet connection
	# Next, try cloning from a local 'blacklist.git' VCS.
	#   * requires: the `git` program and blacklist.git cloned at $BLACKLIST_VCSDIR
	# Lastly, fallback on installed package blacklist.
	#   * requires: the '$BLACKLIST_PKGNAME' package to be installed
	# Of these, the first acquired valid blacklist file is used.
	mkdir -p "$CACHE_DIR"
	cd $tmp_vcs_dir
	if [[ -n "$remote_blacklist" ]]                                  && \
	   curl -v -o "$tmp_blacklist" "$remote_blacklist" &> "$tmp_log"    ; then
		stat_done ; print "Loaded remote blacklist" ;
	elif [[ -d "$BLACKLIST_VCSDIR" ]]                               && \
	     git clone --depth=1 file://$BLACKLIST_VCSDIR &> "$tmp_log" && \
	     cp blacklist/blacklist.txt "$tmp_blacklist"                   ; then
		stat_done ; print "Loaded local VCS blacklist" ;
	else
		stat_done ; warning "Unable to synchronize blacklist" ;
		warning "The blacklist data may be out-dated"
		sed 's/^/    /' <"$tmp_log" >&2
	fi

	# fall-back on packaged blacklist, if necessary
	if is_valid_blacklist "$tmp_blacklist"; then
		mv -fT -- "$tmp_blacklist" "$BLACKLIST_CACHED"
	else
		mv -fT -- "$tmp_blacklist" "$BLACKLIST_CACHED".$(date -I).invalid

		local installed_ver=$(expac -Q %v $BLACKLIST_PKGNAME)
		local repo_ver=$(     expac -S %v $BLACKLIST_PKGNAME)

		if [[ -n "$repo_ver" ]]; then
			warning "The '$BLACKLIST_PKGNAME' package is not in the repos"
		fi

		if [[ -n "$installed_ver" ]]; then
			cp "${PACKAGE_BLACKLIST}" "$BLACKLIST_CACHED"
			print "Loaded installed blacklist ($installed_ver)"
			if [[ "${installed_ver%%-*}" != "${repo_ver%%-*}" ]]; then
				warning "The installed '$BLACKLIST_PKGNAME' package is not the one in the repos"
				warning "The blacklist data may be out-dated"
			fi
		else
			warning "The '$BLACKLIST_PKGNAME' package is not installed"
			warning "The blacklist data may be out-dated"
		fi
	fi

	# cleanup
	cd $prevwd
	rm -rf -- "$tmp_vcs_dir"
	rm -f  -- "$tmp_blacklist"
	rm -f  -- "$tmp_log"

  # validate cached blacklist
	# yes, this is aggressive; but presumably,
	# inaccurate blacklist data is unacceptable to all clients
	if ! is_valid_blacklist "$BLACKLIST_CACHED"; then
		error "The blacklist data is missing or invalid - aborting"
		exit 1
	fi
)

# Usage: blacklist-cat | blacklist-lookup $pkgname
# Filters to obtain the line for $pkgname from the blacklist on stdin.
# Exits successfully whether a line is found or not.
blacklist-lookup() {
	local pkg=$1
	# we accept that $pkg contains no regex-nes
	blacklist-normalize | grep "^$pkg:" || true
}

# Usage: blacklist-cat | blacklist-get-pkg
# Prints only the package name field of the blacklist line(s) on stdin.
blacklist-get-pkg() {
	blacklist-normalize | cut -d: -f1
}

# Usage: blacklist-cat | blacklist-get-rep
# Prints only the replacement package field of the blacklist line(s) on stdin.
blacklist-get-rep() {
	local -a targets=($(blacklist-get-pkg))
	expac -Ss '%r/%n %n %P %R' | awk -v arr="${targets[*]}" '
	      {
	          gsub("[=<>]+[^[:blank:]]*", "", $0) # discard versioning
	          # build pkg -> providers table from pkg -> provides
	          for (provided = 2; provided <= NF; ++provided) {
	              if (! seen[$1 " " $provided]++) {
	                  providers[$provided] = providers[$provided] $1 " "
	              }
	          }
	      }
	      END {
	          split(arr, targets, " ")
	          for (pkg in targets) {
	              sub("[ \t]+$", "", providers[targets[pkg]])
	              print providers[targets[pkg]]
	          }
	      }'
}

# Usage: blacklist-cat | blacklist-get-url
# Prints URLs formed from the reference-id fields of the blacklist line(s) on stdin.
# Prints an empty line in the absence of reference.
blacklist-get-url() {
	blacklist-normalize | awk -F: '
	    BEGIN {
	        refs["debian"] = "http://bugs.debian.org/"
	        refs["fsf"] = "http://libreplanet.org/wiki/List_of_software_that_does_not_respect_the_Free_System_Distribution_Guidelines#"
	        refs["sv"] = "https://savannah.nongnu.org/bugs/?"
	        refs["fedora"] = "https://bugzilla.redhat.com/show_bug.cgi?id="
	        refs["parabola"] = "https://labs.parabola.nu/issues/"
	    }
	    refs[$3] { print refs[$3] $4 } !refs[$3] { print "" }'
}

# Usage: blacklist-cat | blacklist-get-reason
# Prints only the reason field of the blacklist line(s) on stdin.
blacklist-get-reason() {
	blacklist-normalize | cut -d: -f5-
}