1148871Scperciva#!/bin/sh
2148871Scperciva
3148871Scperciva#-
4148871Scperciva# Copyright 2004-2005 Colin Percival
5148871Scperciva# All rights reserved
6148871Scperciva#
7148871Scperciva# Redistribution and use in source and binary forms, with or without
8148871Scperciva# modification, are permitted providing that the following conditions 
9148871Scperciva# are met:
10148871Scperciva# 1. Redistributions of source code must retain the above copyright
11148871Scperciva#    notice, this list of conditions and the following disclaimer.
12148871Scperciva# 2. Redistributions in binary form must reproduce the above copyright
13148871Scperciva#    notice, this list of conditions and the following disclaimer in the
14148871Scperciva#    documentation and/or other materials provided with the distribution.
15148871Scperciva#
16148871Scperciva# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17148871Scperciva# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18148871Scperciva# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19148871Scperciva# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20148871Scperciva# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21148871Scperciva# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22148871Scperciva# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23148871Scperciva# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24148871Scperciva# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25148871Scperciva# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26148871Scperciva# POSSIBILITY OF SUCH DAMAGE.
27148871Scperciva
28148871Scperciva# $FreeBSD: releng/9.3/usr.sbin/portsnap/portsnap/portsnap.sh 306942 2016-10-10 07:19:16Z delphij $
29148871Scperciva
30148871Scperciva#### Usage function -- called from command-line handling code.
31148871Scperciva
32148871Scperciva# Usage instructions.  Options not listed:
33148871Scperciva# --debug	-- don't filter output from utilities
34148871Scperciva# --no-stats	-- don't show progress statistics while fetching files
35148871Scpercivausage() {
36148871Scperciva	cat <<EOF
37149027Scpercivausage: `basename $0` [options] command ... [path]
38148871Scperciva
39148871ScpercivaOptions:
40148871Scperciva  -d workdir   -- Store working files in workdir
41148871Scperciva                  (default: /var/db/portsnap/)
42148871Scperciva  -f conffile  -- Read configuration options from conffile
43148871Scperciva                  (default: /etc/portsnap.conf)
44148871Scperciva  -I           -- Update INDEX only. (update command only)
45148871Scperciva  -k KEY       -- Trust an RSA key with SHA256 hash of KEY
46158523Scperciva  -l descfile  -- Merge the specified local describes file into the INDEX.
47148871Scperciva  -p portsdir  -- Location of uncompressed ports tree
48148871Scperciva                  (default: /usr/ports/)
49148871Scperciva  -s server    -- Server from which to fetch updates.
50148871Scperciva                  (default: portsnap.FreeBSD.org)
51148871Scperciva  path         -- Extract only parts of the tree starting with the given
52148871Scperciva                  string.  (extract command only)
53148871ScpercivaCommands:
54148871Scperciva  fetch        -- Fetch a compressed snapshot of the ports tree,
55148871Scperciva                  or update an existing snapshot.
56148871Scperciva  cron         -- Sleep rand(3600) seconds, and then fetch updates.
57148871Scperciva  extract      -- Extract snapshot of ports tree, replacing existing
58148871Scperciva                  files and directories.
59148871Scperciva  update       -- Update ports tree to match current snapshot, replacing
60148871Scperciva                  files and directories which have changed.
61148871ScpercivaEOF
62148871Scperciva	exit 0
63148871Scperciva}
64148871Scperciva
65148871Scperciva#### Parameter handling functions.
66148871Scperciva
67148871Scperciva# Initialize parameters to null, just in case they're
68148871Scperciva# set in the environment.
69148871Scpercivainit_params() {
70148871Scperciva	KEYPRINT=""
71148871Scperciva	EXTRACTPATH=""
72148871Scperciva	WORKDIR=""
73148871Scperciva	PORTSDIR=""
74148871Scperciva	CONFFILE=""
75148871Scperciva	COMMAND=""
76149027Scperciva	COMMANDS=""
77148871Scperciva	QUIETREDIR=""
78148871Scperciva	QUIETFLAG=""
79148871Scperciva	STATSREDIR=""
80148879Scperciva	XARGST=""
81148871Scperciva	NDEBUG=""
82148871Scperciva	DDSTATS=""
83148871Scperciva	INDEXONLY=""
84148871Scperciva	SERVERNAME=""
85149824Scperciva	REFUSE=""
86158523Scperciva	LOCALDESC=""
87148871Scperciva}
88148871Scperciva
89148871Scperciva# Parse the command line
90148871Scpercivaparse_cmdline() {
91148871Scperciva	while [ $# -gt 0 ]; do
92148871Scperciva		case "$1" in
93148871Scperciva		-d)
94148871Scperciva			if [ $# -eq 1 ]; then usage; fi
95148871Scperciva			if [ ! -z "${WORKDIR}" ]; then usage; fi
96148871Scperciva			shift; WORKDIR="$1"
97148871Scperciva			;;
98148871Scperciva		--debug)
99148871Scperciva			QUIETREDIR="/dev/stderr"
100148871Scperciva			STATSREDIR="/dev/stderr"
101148871Scperciva			QUIETFLAG=" "
102148871Scperciva			NDEBUG=" "
103148879Scperciva			XARGST="-t"
104148871Scperciva			DDSTATS=".."
105148871Scperciva			;;
106148871Scperciva		-f)
107148871Scperciva			if [ $# -eq 1 ]; then usage; fi
108148871Scperciva			if [ ! -z "${CONFFILE}" ]; then usage; fi
109148871Scperciva			shift; CONFFILE="$1"
110148871Scperciva			;;
111148871Scperciva		-h | --help | help)
112148871Scperciva			usage
113148871Scperciva			;;
114148871Scperciva		-I)
115148871Scperciva			INDEXONLY="YES"
116148871Scperciva			;;
117148871Scperciva		-k)
118148871Scperciva			if [ $# -eq 1 ]; then usage; fi
119148871Scperciva			if [ ! -z "${KEYPRINT}" ]; then usage; fi
120148871Scperciva			shift; KEYPRINT="$1"
121148871Scperciva			;;
122158523Scperciva		-l)
123158523Scperciva			if [ $# -eq 1 ]; then usage; fi
124158523Scperciva			if [ ! -z "${LOCALDESC}" ]; then usage; fi
125158523Scperciva			shift; LOCALDESC="$1"
126158523Scperciva			;;
127148871Scperciva		--no-stats)
128148871Scperciva			if [ -z "${STATSREDIR}" ]; then
129148871Scperciva				STATSREDIR="/dev/null"
130148871Scperciva				DDSTATS=".. "
131148871Scperciva			fi
132148871Scperciva			;;
133148871Scperciva		-p)
134148871Scperciva			if [ $# -eq 1 ]; then usage; fi
135148871Scperciva			if [ ! -z "${PORTSDIR}" ]; then usage; fi
136148871Scperciva			shift; PORTSDIR="$1"
137148871Scperciva			;;
138148871Scperciva		-s)
139148871Scperciva			if [ $# -eq 1 ]; then usage; fi
140148871Scperciva			if [ ! -z "${SERVERNAME}" ]; then usage; fi
141148871Scperciva			shift; SERVERNAME="$1"
142148871Scperciva			;;
143201251Scperciva		cron | extract | fetch | update | alfred)
144149027Scperciva			COMMANDS="${COMMANDS} $1"
145148871Scperciva			;;
146235310Seadler		up)
147235310Seadler			COMMANDS="${COMMANDS} update"
148235310Seadler			;;
149148871Scperciva		*)
150148871Scperciva			if [ $# -gt 1 ]; then usage; fi
151149027Scperciva			if echo ${COMMANDS} | grep -vq extract; then
152149027Scperciva				usage
153149027Scperciva			fi
154148871Scperciva			EXTRACTPATH="$1"
155148871Scperciva			;;
156148871Scperciva		esac
157148871Scperciva		shift
158148871Scperciva	done
159148871Scperciva
160149027Scperciva	if [ -z "${COMMANDS}" ]; then
161148871Scperciva		usage
162148871Scperciva	fi
163148871Scperciva}
164148871Scperciva
165148871Scperciva# If CONFFILE was specified at the command-line, make
166148871Scperciva# sure that it exists and is readable.
167148871Scpercivasanity_conffile() {
168148871Scperciva	if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
169148871Scperciva		echo -n "File does not exist "
170148871Scperciva		echo -n "or is not readable: "
171148871Scperciva		echo ${CONFFILE}
172148871Scperciva		exit 1
173148871Scperciva	fi
174148871Scperciva}
175148871Scperciva
176148871Scperciva# If a configuration file hasn't been specified, use
177148871Scperciva# the default value (/etc/portsnap.conf)
178148871Scpercivadefault_conffile() {
179148871Scperciva	if [ -z "${CONFFILE}" ]; then
180148871Scperciva		CONFFILE="/etc/portsnap.conf"
181148871Scperciva	fi
182148871Scperciva}
183148871Scperciva
184148871Scperciva# Read {KEYPRINT, SERVERNAME, WORKDIR, PORTSDIR} from the configuration
185148871Scperciva# file if they haven't already been set.  If the configuration
186148871Scperciva# file doesn't exist, do nothing.
187149824Scperciva# Also read REFUSE (which cannot be set via the command line) if it is
188149824Scperciva# present in the configuration file.
189148871Scpercivaparse_conffile() {
190148871Scperciva	if [ -r "${CONFFILE}" ]; then
191148871Scperciva		for X in KEYPRINT WORKDIR PORTSDIR SERVERNAME; do
192148871Scperciva			eval _=\$${X}
193148871Scperciva			if [ -z "${_}" ]; then
194148871Scperciva				eval ${X}=`grep "^${X}=" "${CONFFILE}" |
195148871Scperciva				    cut -f 2- -d '=' | tail -1`
196148871Scperciva			fi
197148871Scperciva		done
198149824Scperciva
199149824Scperciva		if grep -qE "^REFUSE[[:space:]]" ${CONFFILE}; then
200149824Scperciva			REFUSE="^(`
201149824Scperciva				grep -E "^REFUSE[[:space:]]" "${CONFFILE}" |
202149824Scperciva				    cut -c 7- | xargs echo | tr ' ' '|'
203149824Scperciva				`)"
204149824Scperciva		fi
205179073Scperciva
206179073Scperciva		if grep -qE "^INDEX[[:space:]]" ${CONFFILE}; then
207179073Scperciva			INDEXPAIRS="`
208179073Scperciva				grep -E "^INDEX[[:space:]]" "${CONFFILE}" |
209179073Scperciva				    cut -c 7- | tr ' ' '|' | xargs echo`"
210179073Scperciva		fi
211148871Scperciva	fi
212148871Scperciva}
213148871Scperciva
214148871Scperciva# If parameters have not been set, use default values
215148871Scpercivadefault_params() {
216148871Scperciva	_QUIETREDIR="/dev/null"
217148871Scperciva	_QUIETFLAG="-q"
218148871Scperciva	_STATSREDIR="/dev/stdout"
219148871Scperciva	_WORKDIR="/var/db/portsnap"
220148871Scperciva	_PORTSDIR="/usr/ports"
221148871Scperciva	_NDEBUG="-n"
222158523Scperciva	_LOCALDESC="/dev/null"
223158523Scperciva	for X in QUIETREDIR QUIETFLAG STATSREDIR WORKDIR PORTSDIR	\
224158523Scperciva	    NDEBUG LOCALDESC; do
225148871Scperciva		eval _=\$${X}
226148871Scperciva		eval __=\$_${X}
227148871Scperciva		if [ -z "${_}" ]; then
228148871Scperciva			eval ${X}=${__}
229148871Scperciva		fi
230148871Scperciva	done
231148871Scperciva}
232148871Scperciva
233148871Scperciva# Perform sanity checks and set some final parameters
234148871Scperciva# in preparation for fetching files.  Also chdir into
235148871Scperciva# the working directory.
236148871Scpercivafetch_check_params() {
237148871Scperciva	export HTTP_USER_AGENT="portsnap (${COMMAND}, `uname -r`)"
238148871Scperciva
239148871Scperciva	_SERVERNAME_z=\
240148871Scperciva"SERVERNAME must be given via command line or configuration file."
241148871Scperciva	_KEYPRINT_z="Key must be given via -k option or configuration file."
242148871Scperciva	_KEYPRINT_bad="Invalid key fingerprint: "
243148871Scperciva	_WORKDIR_bad="Directory does not exist or is not writable: "
244148871Scperciva
245148871Scperciva	if [ -z "${SERVERNAME}" ]; then
246148871Scperciva		echo -n "`basename $0`: "
247148871Scperciva		echo "${_SERVERNAME_z}"
248148871Scperciva		exit 1
249148871Scperciva	fi
250148871Scperciva	if [ -z "${KEYPRINT}" ]; then
251148871Scperciva		echo -n "`basename $0`: "
252148871Scperciva		echo "${_KEYPRINT_z}"
253148871Scperciva		exit 1
254148871Scperciva	fi
255148871Scperciva	if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
256148871Scperciva		echo -n "`basename $0`: "
257148871Scperciva		echo -n "${_KEYPRINT_bad}"
258148871Scperciva		echo ${KEYPRINT}
259148871Scperciva		exit 1
260148871Scperciva	fi
261148871Scperciva	if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
262148871Scperciva		echo -n "`basename $0`: "
263148871Scperciva		echo -n "${_WORKDIR_bad}"
264148871Scperciva		echo ${WORKDIR}
265148871Scperciva		exit 1
266148871Scperciva	fi
267148871Scperciva	cd ${WORKDIR} || exit 1
268148871Scperciva
269148871Scperciva	BSPATCH=/usr/bin/bspatch
270148871Scperciva	SHA256=/sbin/sha256
271148871Scperciva	PHTTPGET=/usr/libexec/phttpget
272148871Scperciva}
273148871Scperciva
274148871Scperciva# Perform sanity checks and set some final parameters
275148871Scperciva# in preparation for extracting or updating ${PORTSDIR}
276154088Scperciva# Complain if ${PORTSDIR} exists but is not writable,
277154088Scperciva# but don't complain if ${PORTSDIR} doesn't exist.
278148871Scpercivaextract_check_params() {
279148871Scperciva	_WORKDIR_bad="Directory does not exist: "
280154088Scperciva	_PORTSDIR_bad="Directory is not writable: "
281148871Scperciva
282148871Scperciva	if ! [ -d "${WORKDIR}" ]; then
283148871Scperciva		echo -n "`basename $0`: "
284148871Scperciva		echo -n "${_WORKDIR_bad}"
285148871Scperciva		echo ${WORKDIR}
286148871Scperciva		exit 1
287148871Scperciva	fi
288154088Scperciva	if [ -d "${PORTSDIR}" ] && ! [ -w "${PORTSDIR}" ]; then
289148871Scperciva		echo -n "`basename $0`: "
290148871Scperciva		echo -n "${_PORTSDIR_bad}"
291148871Scperciva		echo ${PORTSDIR}
292148871Scperciva		exit 1
293148871Scperciva	fi
294148871Scperciva
295148871Scperciva	if ! [ -d "${WORKDIR}/files" -a -r "${WORKDIR}/tag"	\
296148871Scperciva	    -a -r "${WORKDIR}/INDEX" -a -r "${WORKDIR}/tINDEX" ]; then
297148871Scperciva		echo "No snapshot available.  Try running"
298148871Scperciva		echo "# `basename $0` fetch"
299148871Scperciva		exit 1
300148871Scperciva	fi
301148871Scperciva
302148871Scperciva	MKINDEX=/usr/libexec/make_index
303148871Scperciva}
304148871Scperciva
305148871Scperciva# Perform sanity checks and set some final parameters
306148871Scperciva# in preparation for updating ${PORTSDIR}
307148871Scpercivaupdate_check_params() {
308148871Scperciva	extract_check_params
309148871Scperciva
310148871Scperciva	if ! [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
311148871Scperciva		echo "${PORTSDIR} was not created by portsnap."
312148871Scperciva		echo -n "You must run '`basename $0` extract' before "
313148871Scperciva		echo "running '`basename $0` update'."
314148871Scperciva		exit 1
315148871Scperciva	fi
316148871Scperciva
317148871Scperciva}
318148871Scperciva
319148871Scperciva#### Core functionality -- the actual work gets done here
320148871Scperciva
321148871Scperciva# Use an SRV query to pick a server.  If the SRV query doesn't provide
322148871Scperciva# a useful answer, use the server name specified by the user.
323148871Scperciva# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
324148871Scperciva# from that; or if no servers are returned, use ${SERVERNAME}.
325148871Scperciva# This allows a user to specify "portsnap.freebsd.org" (in which case
326148871Scperciva# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
327148871Scperciva# (in which case portsnap will use that particular server, since there
328148871Scperciva# won't be an SRV entry for that name).
329148871Scperciva#
330158273Scperciva# We ignore the Port field, since we are always going to use port 80.
331158273Scperciva
332158273Scperciva# Fetch the mirror list, but do not pick a mirror yet.  Returns 1 if
333158273Scperciva# no mirrors are available for any reason.
334158273Scpercivafetch_pick_server_init() {
335158273Scperciva	: > serverlist_tried
336158273Scperciva
337150159Scperciva# Check that host(1) exists (i.e., that the system wasn't built with the
338156813Sru# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
339150159Scperciva	if ! which -s host; then
340158273Scperciva		: > serverlist_full
341158273Scperciva		return 1
342150159Scperciva	fi
343150159Scperciva
344158273Scperciva	echo -n "Looking up ${SERVERNAME} mirrors... "
345148871Scperciva
346148871Scperciva# Issue the SRV query and pull out the Priority, Weight, and Target fields.
347158245Scperciva# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
348158245Scperciva# "$name server selection ..."; we allow either format.
349158245Scperciva	MLIST="_http._tcp.${SERVERNAME}"
350158245Scperciva	host -t srv "${MLIST}" |
351265753Sdelphij	    sed -nE "s/${MLIST} (has SRV record|server selection) //Ip" |
352158245Scperciva	    cut -f 1,2,4 -d ' ' |
353158273Scperciva	    sed -e 's/\.$//' |
354158273Scperciva	    sort > serverlist_full
355148871Scperciva
356148871Scperciva# If no records, give up -- we'll just use the server name we were given.
357158273Scperciva	if [ `wc -l < serverlist_full` -eq 0 ]; then
358158273Scperciva		echo "none found."
359158273Scperciva		return 1
360158273Scperciva	fi
361158273Scperciva
362158273Scperciva# Report how many mirrors we found.
363158273Scperciva	echo `wc -l < serverlist_full` "mirrors found."
364158274Scperciva
365158274Scperciva# Generate a random seed for use in picking mirrors.  If HTTP_PROXY
366158274Scperciva# is set, this will be used to generate the seed; otherwise, the seed
367158274Scperciva# will be random.
368158301Scperciva	if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
369158301Scperciva		RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
370158274Scperciva		    tr -d 'a-f' |
371158274Scperciva		    cut -c 1-9`
372158274Scperciva	else
373158274Scperciva		RANDVALUE=`jot -r 1 0 999999999`
374158274Scperciva	fi
375158273Scperciva}
376158273Scperciva
377158273Scperciva# Pick a mirror.  Returns 1 if we have run out of mirrors to try.
378158273Scpercivafetch_pick_server() {
379158273Scperciva# Generate a list of not-yet-tried mirrors
380158273Scperciva	sort serverlist_tried |
381158273Scperciva	    comm -23 serverlist_full - > serverlist
382158273Scperciva
383158273Scperciva# Have we run out of mirrors?
384148871Scperciva	if [ `wc -l < serverlist` -eq 0 ]; then
385158273Scperciva		echo "No mirrors remaining, giving up."
386158273Scperciva		return 1
387148871Scperciva	fi
388148871Scperciva
389148871Scperciva# Find the highest priority level (lowest numeric value).
390148871Scperciva	SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
391148871Scperciva
392148871Scperciva# Add up the weights of the response lines at that priority level.
393148871Scperciva	SRV_WSUM=0;
394148871Scperciva	while read X; do
395148871Scperciva		case "$X" in
396148871Scperciva		${SRV_PRIORITY}\ *)
397148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
398148871Scperciva			SRV_WSUM=$(($SRV_WSUM + $SRV_W))
399148871Scperciva			;;
400148871Scperciva		esac
401148871Scperciva	done < serverlist
402148871Scperciva
403148871Scperciva# If all the weights are 0, pretend that they are all 1 instead.
404148871Scperciva	if [ ${SRV_WSUM} -eq 0 ]; then
405148871Scperciva		SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
406148871Scperciva		SRV_W_ADD=1
407148871Scperciva	else
408148871Scperciva		SRV_W_ADD=0
409148871Scperciva	fi
410148871Scperciva
411158274Scperciva# Pick a value between 0 and the sum of the weights - 1
412158274Scperciva	SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
413148871Scperciva
414158273Scperciva# Read through the list of mirrors and set SERVERNAME.  Write the line
415158273Scperciva# corresponding to the mirror we selected into serverlist_tried so that
416158273Scperciva# we won't try it again.
417148871Scperciva	while read X; do
418148871Scperciva		case "$X" in
419148871Scperciva		${SRV_PRIORITY}\ *)
420148871Scperciva			SRV_W=`echo $X | cut -f 2 -d ' '`
421148871Scperciva			SRV_W=$(($SRV_W + $SRV_W_ADD))
422158274Scperciva			if [ $SRV_RND -lt $SRV_W ]; then
423148871Scperciva				SERVERNAME=`echo $X | cut -f 3 -d ' '`
424158273Scperciva				echo "$X" >> serverlist_tried
425148871Scperciva				break
426148871Scperciva			else
427148871Scperciva				SRV_RND=$(($SRV_RND - $SRV_W))
428148871Scperciva			fi
429148871Scperciva			;;
430148871Scperciva		esac
431148871Scperciva	done < serverlist
432148871Scperciva}
433148871Scperciva
434148871Scperciva# Check that we have a public key with an appropriate hash, or
435158273Scperciva# fetch the key if it doesn't exist.  Returns 1 if the key has
436158273Scperciva# not yet been fetched.
437148871Scpercivafetch_key() {
438148871Scperciva	if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
439158273Scperciva		return 0
440148871Scperciva	fi
441148871Scperciva
442158273Scperciva	echo -n "Fetching public key from ${SERVERNAME}... "
443148871Scperciva	rm -f pub.ssl
444148871Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
445148871Scperciva	    2>${QUIETREDIR} || true
446148871Scperciva	if ! [ -r pub.ssl ]; then
447148871Scperciva		echo "failed."
448148871Scperciva		return 1
449148871Scperciva	fi
450148871Scperciva	if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
451148871Scperciva		echo "key has incorrect hash."
452148871Scperciva		rm -f pub.ssl
453148871Scperciva		return 1
454148871Scperciva	fi
455148871Scperciva	echo "done."
456148871Scperciva}
457148871Scperciva
458148871Scperciva# Fetch a snapshot tag
459148871Scpercivafetch_tag() {
460148871Scperciva	rm -f snapshot.ssl tag.new
461148871Scperciva
462158273Scperciva	echo ${NDEBUG} "Fetching snapshot tag from ${SERVERNAME}... "
463158273Scperciva	fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl		\
464148871Scperciva	    2>${QUIETREDIR} || true
465148871Scperciva	if ! [ -r $1.ssl ]; then
466148871Scperciva		echo "failed."
467148871Scperciva		return 1
468148871Scperciva	fi
469148871Scperciva
470148871Scperciva	openssl rsautl -pubin -inkey pub.ssl -verify		\
471148871Scperciva	    < $1.ssl > tag.new 2>${QUIETREDIR} || true
472148871Scperciva	rm $1.ssl
473148871Scperciva
474148871Scperciva	if ! [ `wc -l < tag.new` = 1 ] ||
475148871Scperciva	    ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
476148871Scperciva		echo "invalid snapshot tag."
477148871Scperciva		return 1
478148871Scperciva	fi
479148871Scperciva
480148871Scperciva	echo "done."
481148871Scperciva
482148871Scperciva	SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
483148871Scperciva	SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
484148871Scperciva}
485148871Scperciva
486148871Scperciva# Sanity-check the date on a snapshot tag
487148871Scpercivafetch_snapshot_tagsanity() {
488148871Scperciva	if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
489148871Scperciva		echo "Snapshot appears to be more than a year old!"
490148871Scperciva		echo "(Is the system clock correct?)"
491149868Scperciva		echo "Cowardly refusing to proceed any further."
492148871Scperciva		return 1
493148871Scperciva	fi
494148871Scperciva	if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
495148871Scperciva		echo -n "Snapshot appears to have been created more than "
496148871Scperciva		echo "one day into the future!"
497148871Scperciva		echo "(Is the system clock correct?)"
498148871Scperciva		echo "Cowardly refusing to proceed any further."
499148871Scperciva		return 1
500148871Scperciva	fi
501148871Scperciva}
502148871Scperciva
503148871Scperciva# Sanity-check the date on a snapshot update tag
504148871Scpercivafetch_update_tagsanity() {
505148871Scperciva	fetch_snapshot_tagsanity || return 1
506148871Scperciva
507148871Scperciva	if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
508148871Scperciva		echo -n "Latest snapshot on server is "
509148871Scperciva		echo "older than what we already have!"
510148871Scperciva		echo -n "Cowardly refusing to downgrade from "
511148871Scperciva		date -r ${OLDSNAPSHOTDATE}
512148879Scperciva		echo "to `date -r ${SNAPSHOTDATE}`."
513148871Scperciva		return 1
514148871Scperciva	fi
515148871Scperciva}
516148871Scperciva
517148871Scperciva# Compare old and new tags; return 1 if update is unnecessary
518148871Scpercivafetch_update_neededp() {
519148871Scperciva	if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
520148871Scperciva		echo -n "Latest snapshot on server matches "
521148871Scperciva		echo "what we already have."
522148871Scperciva		echo "No updates needed."
523148871Scperciva		rm tag.new
524148871Scperciva		return 1
525148871Scperciva	fi
526148871Scperciva	if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
527148871Scperciva		echo -n "Ports tree hasn't changed since "
528148871Scperciva		echo "last snapshot."
529148871Scperciva		echo "No updates needed."
530148871Scperciva		rm tag.new
531148871Scperciva		return 1
532148871Scperciva	fi
533148871Scperciva
534148871Scperciva	return 0
535148871Scperciva}
536148871Scperciva
537148871Scperciva# Fetch snapshot metadata file
538148871Scpercivafetch_metadata() {
539148871Scperciva	rm -f ${SNAPSHOTHASH} tINDEX.new
540148871Scperciva
541148871Scperciva	echo ${NDEBUG} "Fetching snapshot metadata... "
542226312Sjilles	fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH} \
543148871Scperciva	    2>${QUIETREDIR} || return
544226312Sjilles	if [ "`${SHA256} -q ${SNAPSHOTHASH}`" != ${SNAPSHOTHASH} ]; then
545148871Scperciva		echo "snapshot metadata corrupt."
546148871Scperciva		return 1
547148871Scperciva	fi
548148871Scperciva	mv ${SNAPSHOTHASH} tINDEX.new
549148871Scperciva	echo "done."
550148871Scperciva}
551148871Scperciva
552148871Scperciva# Warn user about bogus metadata
553148871Scpercivafetch_metadata_freakout() {
554148871Scperciva	echo
555148871Scperciva	echo "Portsnap metadata is correctly signed, but contains"
556148871Scperciva	echo "at least one line which appears bogus."
557148871Scperciva	echo "Cowardly refusing to proceed any further."
558148871Scperciva}
559148871Scperciva
560148871Scperciva# Sanity-check a snapshot metadata file
561148871Scpercivafetch_metadata_sanity() {
562148871Scperciva	if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
563148871Scperciva		fetch_metadata_freakout
564148871Scperciva		return 1
565148871Scperciva	fi
566148871Scperciva	if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
567148871Scperciva		echo
568148871Scperciva		echo "Portsnap metadata appears bogus."
569148871Scperciva		echo "Cowardly refusing to proceed any further."
570148871Scperciva		return 1
571148871Scperciva	fi
572148871Scperciva}
573148871Scperciva
574148871Scperciva# Take a list of ${oldhash}|${newhash} and output a list of needed patches
575148871Scpercivafetch_make_patchlist() {
576148871Scperciva	grep -vE "^([0-9a-f]{64})\|\1$" | 
577148871Scperciva		while read LINE; do
578148871Scperciva			X=`echo ${LINE} | cut -f 1 -d '|'`
579148871Scperciva			Y=`echo ${LINE} | cut -f 2 -d '|'`
580148871Scperciva			if [ -f "files/${Y}.gz" ]; then continue; fi
581148871Scperciva			if [ ! -f "files/${X}.gz" ]; then continue; fi
582148871Scperciva			echo "${LINE}"
583148871Scperciva		done
584148871Scperciva}
585148871Scperciva
586148871Scperciva# Print user-friendly progress statistics
587148871Scpercivafetch_progress() {
588148871Scperciva	LNC=0
589148871Scperciva	while read x; do
590148871Scperciva		LNC=$(($LNC + 1))
591148871Scperciva		if [ $(($LNC % 10)) = 0 ]; then
592149023Scperciva			echo -n $LNC
593148871Scperciva		elif [ $(($LNC % 2)) = 0 ]; then
594148871Scperciva			echo -n .
595148871Scperciva		fi
596148871Scperciva	done
597148871Scperciva	echo -n " "
598148871Scperciva}
599148871Scperciva
600148871Scperciva# Sanity-check an index file
601148871Scpercivafetch_index_sanity() {
602148871Scperciva	if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
603148871Scperciva	    fgrep -q "./" INDEX.new; then
604148871Scperciva		fetch_metadata_freakout
605148871Scperciva		return 1
606148871Scperciva	fi
607148871Scperciva}
608148871Scperciva
609148871Scperciva# Verify a list of files
610148871Scpercivafetch_snapshot_verify() {
611148871Scperciva	while read F; do
612306942Sdelphij		if [ "`gunzip -c < snap/${F}.gz | ${SHA256} -q`" != ${F} ]; then
613148871Scperciva			echo "snapshot corrupt."
614148871Scperciva			return 1
615148871Scperciva		fi
616148871Scperciva	done
617148871Scperciva	return 0
618148871Scperciva}
619148871Scperciva
620148871Scperciva# Fetch a snapshot tarball, extract, and verify.
621148871Scpercivafetch_snapshot() {
622158273Scperciva	while ! fetch_tag snapshot; do
623158273Scperciva		fetch_pick_server || return 1
624158273Scperciva	done
625148871Scperciva	fetch_snapshot_tagsanity || return 1
626148871Scperciva	fetch_metadata || return 1
627148871Scperciva	fetch_metadata_sanity || return 1
628148871Scperciva
629148871Scperciva	rm -rf snap/
630148871Scperciva
631148871Scperciva# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
632148871Scperciva# probably take a while, so the progrees reports that fetch(1) generates
633148871Scperciva# will be useful for keeping the users' attention from drifting.
634148871Scperciva	echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
635154693Scperciva	fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
636148871Scperciva
637148871Scperciva	echo -n "Extracting snapshot... "
638216575Ssimon	tar -xz --numeric-owner -f ${SNAPSHOTHASH}.tgz snap/ || return 1
639148871Scperciva	rm ${SNAPSHOTHASH}.tgz
640148871Scperciva	echo "done."
641148871Scperciva
642148871Scperciva	echo -n "Verifying snapshot integrity... "
643148871Scperciva# Verify the metadata files
644148871Scperciva	cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
645148871Scperciva# Extract the index
646148871Scperciva	rm -f INDEX.new
647306942Sdelphij	gunzip -c < snap/`look INDEX tINDEX.new |
648148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
649148871Scperciva	fetch_index_sanity || return 1
650148871Scperciva# Verify the snapshot contents
651148871Scperciva	cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
652306942Sdelphij	cut -f 2 -d '|' tINDEX.new INDEX.new | sort -u > files.expected
653306942Sdelphij	find snap -mindepth 1 | sed -E 's^snap/(.*)\.gz^\1^' | sort > files.snap
654306942Sdelphij	if ! cmp -s files.expected files.snap; then
655306942Sdelphij		echo "unexpected files in snapshot."
656306942Sdelphij		return 1
657306942Sdelphij	fi
658306942Sdelphij	rm files.expected files.snap
659148871Scperciva	echo "done."
660148871Scperciva
661148871Scperciva# Move files into their proper locations
662148871Scperciva	rm -f tag INDEX tINDEX
663148871Scperciva	rm -rf files
664148871Scperciva	mv tag.new tag
665148871Scperciva	mv tINDEX.new tINDEX
666148871Scperciva	mv INDEX.new INDEX
667148871Scperciva	mv snap/ files/
668148871Scperciva
669148871Scperciva	return 0
670148871Scperciva}
671148871Scperciva
672148871Scperciva# Update a compressed snapshot
673148871Scpercivafetch_update() {
674148871Scperciva	rm -f patchlist diff OLD NEW filelist INDEX.new
675148871Scperciva
676148871Scperciva	OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
677148871Scperciva	OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
678148871Scperciva
679158273Scperciva	while ! fetch_tag latest; do
680158273Scperciva		fetch_pick_server || return 1
681158273Scperciva	done
682148871Scperciva	fetch_update_tagsanity || return 1
683148871Scperciva	fetch_update_neededp || return 0
684148871Scperciva	fetch_metadata || return 1
685148871Scperciva	fetch_metadata_sanity || return 1
686148871Scperciva
687148871Scperciva	echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
688148871Scperciva	echo "to `date -r ${SNAPSHOTDATE}`."
689148871Scperciva
690148871Scperciva# Generate a list of wanted metadata patches
691148871Scperciva	join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
692148871Scperciva	    fetch_make_patchlist > patchlist
693148871Scperciva
694148871Scperciva# Attempt to fetch metadata patches
695148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
696148871Scperciva	echo ${NDEBUG} "metadata patches.${DDSTATS}"
697148871Scperciva	tr '|' '-' < patchlist |
698148871Scperciva	    lam -s "tp/" - -s ".gz" |
699148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
700148871Scperciva	    2>${STATSREDIR} | fetch_progress
701148871Scperciva	echo "done."
702148871Scperciva
703148871Scperciva# Attempt to apply metadata patches
704148871Scperciva	echo -n "Applying metadata patches... "
705148871Scperciva	while read LINE; do
706148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
707148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
708148871Scperciva		if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
709148871Scperciva		gunzip -c < ${X}-${Y}.gz > diff
710148871Scperciva		gunzip -c < files/${X}.gz > OLD
711148871Scperciva		cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
712148871Scperciva		grep '^\+' diff | cut -c 2- |
713148871Scperciva		    sort -k 1,1 -t '|' -m - ptmp > NEW
714148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
715148871Scperciva			mv NEW files/${Y}
716148871Scperciva			gzip -n files/${Y}
717148871Scperciva		fi
718148871Scperciva		rm -f diff OLD NEW ${X}-${Y}.gz ptmp
719148871Scperciva	done < patchlist 2>${QUIETREDIR}
720148871Scperciva	echo "done."
721148871Scperciva
722148871Scperciva# Update metadata without patches
723148871Scperciva	join -t '|' -v 2 tINDEX tINDEX.new |
724148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
725148871Scperciva		while read Y; do
726148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
727148871Scperciva				echo ${Y};
728148871Scperciva			fi
729148871Scperciva		done > filelist
730148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
731148871Scperciva	echo ${NDEBUG} "metadata files... "
732148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
733148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
734148871Scperciva	    2>${QUIETREDIR}
735148871Scperciva
736148871Scperciva	while read Y; do
737148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
738148871Scperciva			mv ${Y}.gz files/${Y}.gz
739148871Scperciva		else
740148871Scperciva			echo "metadata is corrupt."
741148871Scperciva			return 1
742148871Scperciva		fi
743148871Scperciva	done < filelist
744148871Scperciva	echo "done."
745148871Scperciva
746148871Scperciva# Extract the index
747306942Sdelphij	gunzip -c < files/`look INDEX tINDEX.new |
748148871Scperciva	    cut -f 2 -d '|'`.gz > INDEX.new
749148871Scperciva	fetch_index_sanity || return 1
750148871Scperciva
751149824Scperciva# If we have decided to refuse certain updates, construct a hybrid index which
752149824Scperciva# is equal to the old index for parts of the tree which we don't want to
753149824Scperciva# update, and equal to the new index for parts of the tree which gets updates.
754149824Scperciva# This means that we should always have a "complete snapshot" of the ports
755149824Scperciva# tree -- with the caveat that it isn't actually a snapshot.
756149824Scperciva	if [ ! -z "${REFUSE}" ]; then
757149824Scperciva		echo "Refusing to download updates for ${REFUSE}"	\
758149824Scperciva		    >${QUIETREDIR}
759149824Scperciva
760149824Scperciva		grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
761149824Scperciva		grep -E "${REFUSE}" INDEX |
762149824Scperciva		    sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
763149824Scperciva		rm -f INDEX.tmp
764149824Scperciva	fi
765149824Scperciva
766148871Scperciva# Generate a list of wanted ports patches
767148871Scperciva	join -t '|' -o 1.2,2.2 INDEX INDEX.new |
768148871Scperciva	    fetch_make_patchlist > patchlist
769148871Scperciva
770148871Scperciva# Attempt to fetch ports patches
771148871Scperciva	echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
772148871Scperciva	echo ${NDEBUG} "patches.${DDSTATS}"
773148871Scperciva	tr '|' '-' < patchlist | lam -s "bp/" - |
774148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
775148871Scperciva	    2>${STATSREDIR} | fetch_progress
776148871Scperciva	echo "done."
777148871Scperciva
778148871Scperciva# Attempt to apply ports patches
779148871Scperciva	echo -n "Applying patches... "
780148871Scperciva	while read LINE; do
781148871Scperciva		X=`echo ${LINE} | cut -f 1 -d '|'`
782148871Scperciva		Y=`echo ${LINE} | cut -f 2 -d '|'`
783148871Scperciva		if [ ! -f "${X}-${Y}" ]; then continue; fi
784148871Scperciva		gunzip -c < files/${X}.gz > OLD
785148871Scperciva		${BSPATCH} OLD NEW ${X}-${Y}
786148871Scperciva		if [ `${SHA256} -q NEW` = ${Y} ]; then
787148871Scperciva			mv NEW files/${Y}
788148871Scperciva			gzip -n files/${Y}
789148871Scperciva		fi
790148871Scperciva		rm -f diff OLD NEW ${X}-${Y}
791148871Scperciva	done < patchlist 2>${QUIETREDIR}
792148871Scperciva	echo "done."
793148871Scperciva
794148871Scperciva# Update ports without patches
795148871Scperciva	join -t '|' -v 2 INDEX INDEX.new |
796148871Scperciva	    cut -f 2 -d '|' /dev/stdin patchlist |
797148871Scperciva		while read Y; do
798148871Scperciva			if [ ! -f "files/${Y}.gz" ]; then
799148871Scperciva				echo ${Y};
800148871Scperciva			fi
801148871Scperciva		done > filelist
802148871Scperciva	echo -n "Fetching `wc -l < filelist | tr -d ' '` "
803148871Scperciva	echo ${NDEBUG} "new ports or files... "
804148871Scperciva	lam -s "f/" - -s ".gz" < filelist |
805148879Scperciva	    xargs ${XARGST} ${PHTTPGET} ${SERVERNAME}	\
806148871Scperciva	    2>${QUIETREDIR}
807148871Scperciva
808148871Scperciva	while read Y; do
809148871Scperciva		if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
810148871Scperciva			mv ${Y}.gz files/${Y}.gz
811148871Scperciva		else
812148871Scperciva			echo "snapshot is corrupt."
813148871Scperciva			return 1
814148871Scperciva		fi
815148871Scperciva	done < filelist
816148871Scperciva	echo "done."
817148871Scperciva
818148871Scperciva# Remove files which are no longer needed
819253337Scperciva	cut -f 2 -d '|' tINDEX INDEX | sort -u > oldfiles
820253337Scperciva	cut -f 2 -d '|' tINDEX.new INDEX.new | sort -u | comm -13 - oldfiles |
821148871Scperciva	    lam -s "files/" - -s ".gz" | xargs rm -f
822148871Scperciva	rm patchlist filelist oldfiles
823148871Scperciva
824148871Scperciva# We're done!
825148871Scperciva	mv INDEX.new INDEX
826148871Scperciva	mv tINDEX.new tINDEX
827148871Scperciva	mv tag.new tag
828148871Scperciva
829148871Scperciva	return 0
830148871Scperciva}
831148871Scperciva
832148871Scperciva# Do the actual work involved in "fetch" / "cron".
833148871Scpercivafetch_run() {
834158273Scperciva	fetch_pick_server_init && fetch_pick_server
835148871Scperciva
836158273Scperciva	while ! fetch_key; do
837158273Scperciva		fetch_pick_server || return 1
838158273Scperciva	done
839148871Scperciva
840148871Scperciva	if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
841148871Scperciva		fetch_snapshot || return 1
842148871Scperciva	fi
843148871Scperciva	fetch_update || return 1
844148871Scperciva}
845148871Scperciva
846148871Scperciva# Build a ports INDEX file
847148871Scpercivaextract_make_index() {
848179073Scperciva	if ! look $1 ${WORKDIR}/tINDEX > /dev/null; then
849179073Scperciva		echo -n "$1 not provided by portsnap server; "
850179073Scperciva		echo "$2 not being generated."
851179073Scperciva	else
852306942Sdelphij	gunzip -c < "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
853158523Scperciva	    cut -f 2 -d '|'`.gz" |
854158523Scperciva	    cat - ${LOCALDESC} |
855158523Scperciva	    ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
856179073Scperciva	fi
857148871Scperciva}
858148871Scperciva
859148871Scperciva# Create INDEX, INDEX-5, INDEX-6
860148871Scpercivaextract_indices() {
861148871Scperciva	echo -n "Building new INDEX files... "
862179073Scperciva	for PAIR in ${INDEXPAIRS}; do
863179073Scperciva		INDEXFILE=`echo ${PAIR} | cut -f 1 -d '|'`
864179073Scperciva		DESCRIBEFILE=`echo ${PAIR} | cut -f 2 -d '|'`
865179073Scperciva		extract_make_index ${DESCRIBEFILE} ${INDEXFILE} || return 1
866179073Scperciva	done
867148871Scperciva	echo "done."
868148871Scperciva}
869148871Scperciva
870149824Scperciva# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
871149824Scperciva# merge the values from any exiting .portsnap.INDEX file.
872148871Scpercivaextract_metadata() {
873149824Scperciva	if [ -z "${REFUSE}" ]; then
874149984Scperciva		sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
875149824Scperciva	elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
876149824Scperciva		grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX	\
877149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX.tmp
878149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
879149824Scperciva		    sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp	\
880149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
881149824Scperciva		rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
882149824Scperciva	else
883149824Scperciva		grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
884149824Scperciva		    > ${PORTSDIR}/.portsnap.INDEX
885149824Scperciva	fi
886148871Scperciva}
887148871Scperciva
888148871Scperciva# Do the actual work involved in "extract"
889148871Scpercivaextract_run() {
890154088Scperciva	mkdir -p ${PORTSDIR} || return 1
891154088Scperciva
892149824Scperciva	if !
893149824Scperciva		if ! [ -z "${EXTRACTPATH}" ]; then
894149824Scperciva			grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
895149824Scperciva		elif ! [ -z "${REFUSE}" ]; then
896149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX
897149824Scperciva		else
898149824Scperciva			cat ${WORKDIR}/INDEX
899159062Scperciva		fi | tr '|' ' ' | while read FILE HASH; do
900148871Scperciva		echo ${PORTSDIR}/${FILE}
901148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
902148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
903148871Scperciva			return 1
904148871Scperciva		fi
905148871Scperciva		case ${FILE} in
906148871Scperciva		*/)
907159062Scperciva			rm -rf ${PORTSDIR}/${FILE%/}
908148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
909216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
910148871Scperciva			    -C ${PORTSDIR}/${FILE}
911148871Scperciva			;;
912148871Scperciva		*)
913148871Scperciva			rm -f ${PORTSDIR}/${FILE}
914216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
915148871Scperciva			    -C ${PORTSDIR} ${FILE}
916148871Scperciva			;;
917148871Scperciva		esac
918149041Scperciva	done; then
919149041Scperciva		return 1
920149041Scperciva	fi
921148871Scperciva	if [ ! -z "${EXTRACTPATH}" ]; then
922148871Scperciva		return 0;
923148871Scperciva	fi
924148871Scperciva
925148871Scperciva	extract_metadata
926148871Scperciva	extract_indices
927148871Scperciva}
928148871Scperciva
929148871Scperciva# Do the actual work involved in "update"
930148871Scpercivaupdate_run() {
931148871Scperciva	if ! [ -z "${INDEXONLY}" ]; then
932148871Scperciva		extract_indices >/dev/null || return 1
933148871Scperciva		return 0
934148871Scperciva	fi
935148871Scperciva
936148981Scperciva	if sort ${WORKDIR}/INDEX |
937149042Scperciva	    cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
938148958Scperciva		echo "Ports tree is already up to date."
939148958Scperciva		return 0
940148958Scperciva	fi
941148958Scperciva
942149824Scperciva# If we are REFUSEing to touch certain directories, don't remove files
943149824Scperciva# from those directories (even if they are out of date)
944148871Scperciva	echo -n "Removing old files and directories... "
945149824Scperciva	if ! [ -z "${REFUSE}" ]; then 
946149824Scperciva		sort ${WORKDIR}/INDEX |
947149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
948149824Scperciva		    grep -vE "${REFUSE}" |
949158473Scperciva		    lam -s "${PORTSDIR}/" - |
950158473Scperciva		    sed -e 's|/$||' | xargs rm -rf
951149824Scperciva	else
952149824Scperciva		sort ${WORKDIR}/INDEX |
953149824Scperciva		    comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
954158473Scperciva		    lam -s "${PORTSDIR}/" - |
955158473Scperciva		    sed -e 's|/$||' | xargs rm -rf
956149824Scperciva	fi
957148871Scperciva	echo "done."
958148871Scperciva
959148871Scperciva# Install new files
960148871Scperciva	echo "Extracting new files:"
961149824Scperciva	if !
962149824Scperciva		if ! [ -z "${REFUSE}" ]; then
963149824Scperciva			grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
964149824Scperciva		else
965149824Scperciva			sort ${WORKDIR}/INDEX
966149824Scperciva		fi |
967149041Scperciva	    comm -13 ${PORTSDIR}/.portsnap.INDEX - |
968148871Scperciva	    while read LINE; do
969148871Scperciva		FILE=`echo ${LINE} | cut -f 1 -d '|'`
970148871Scperciva		HASH=`echo ${LINE} | cut -f 2 -d '|'`
971148871Scperciva		echo ${PORTSDIR}/${FILE}
972148871Scperciva		if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
973148871Scperciva			echo "files/${HASH}.gz not found -- snapshot corrupt."
974148871Scperciva			return 1
975148871Scperciva		fi
976148871Scperciva		case ${FILE} in
977148871Scperciva		*/)
978148871Scperciva			mkdir -p ${PORTSDIR}/${FILE}
979216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
980148871Scperciva			    -C ${PORTSDIR}/${FILE}
981148871Scperciva			;;
982148871Scperciva		*)
983216575Ssimon			tar -xz --numeric-owner -f ${WORKDIR}/files/${HASH}.gz \
984148871Scperciva			    -C ${PORTSDIR} ${FILE}
985148871Scperciva			;;
986148871Scperciva		esac
987149041Scperciva	done; then
988149041Scperciva		return 1
989149041Scperciva	fi
990148871Scperciva
991148871Scperciva	extract_metadata
992148871Scperciva	extract_indices
993148871Scperciva}
994148871Scperciva
995148871Scperciva#### Main functions -- call parameter-handling and core functions
996148871Scperciva
997148871Scperciva# Using the command line, configuration file, and defaults,
998148871Scperciva# set all the parameters which are needed later.
999148871Scpercivaget_params() {
1000148871Scperciva	init_params
1001148871Scperciva	parse_cmdline $@
1002148871Scperciva	sanity_conffile
1003148871Scperciva	default_conffile
1004148871Scperciva	parse_conffile
1005148871Scperciva	default_params
1006148871Scperciva}
1007148871Scperciva
1008148871Scperciva# Fetch command.  Make sure that we're being called
1009148871Scperciva# interactively, then run fetch_check_params and fetch_run
1010148871Scpercivacmd_fetch() {
1011148871Scperciva	if [ ! -t 0 ]; then
1012148871Scperciva		echo -n "`basename $0` fetch should not "
1013148871Scperciva		echo "be run non-interactively."
1014148871Scperciva		echo "Run `basename $0` cron instead."
1015148871Scperciva		exit 1
1016148871Scperciva	fi
1017148871Scperciva	fetch_check_params
1018148871Scperciva	fetch_run || exit 1
1019148871Scperciva}
1020148871Scperciva
1021148871Scperciva# Cron command.  Make sure the parameters are sensible; wait
1022148871Scperciva# rand(3600) seconds; then fetch updates.  While fetching updates,
1023148871Scperciva# send output to a temporary file; only print that file if the
1024148871Scperciva# fetching failed.
1025148871Scpercivacmd_cron() {
1026148871Scperciva	fetch_check_params
1027148871Scperciva	sleep `jot -r 1 0 3600`
1028148871Scperciva
1029148871Scperciva	TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
1030148871Scperciva	if ! fetch_run >> ${TMPFILE}; then
1031148871Scperciva		cat ${TMPFILE}
1032148871Scperciva		rm ${TMPFILE}
1033148871Scperciva		exit 1
1034148871Scperciva	fi
1035148871Scperciva
1036148871Scperciva	rm ${TMPFILE}
1037148871Scperciva}
1038148871Scperciva
1039148871Scperciva# Extract command.  Make sure the parameters are sensible,
1040148871Scperciva# then extract the ports tree (or part thereof).
1041148871Scpercivacmd_extract() {
1042148871Scperciva	extract_check_params
1043148871Scperciva	extract_run || exit 1
1044148871Scperciva}
1045148871Scperciva
1046148871Scperciva# Update command.  Make sure the parameters are sensible,
1047148871Scperciva# then update the ports tree.
1048148871Scpercivacmd_update() {
1049148871Scperciva	update_check_params
1050148871Scperciva	update_run || exit 1
1051148871Scperciva}
1052148871Scperciva
1053201251Scperciva# Alfred command.  Run 'fetch' or 'cron' depending on
1054201251Scperciva# whether stdin is a terminal; then run 'update' or
1055201251Scperciva# 'extract' depending on whether ${PORTSDIR} exists.
1056201251Scpercivacmd_alfred() {
1057201251Scperciva	if [ -t 0 ]; then
1058201251Scperciva		cmd_fetch
1059201251Scperciva	else
1060201251Scperciva		cmd_cron
1061201251Scperciva	fi
1062201251Scperciva	if [ -d ${PORTSDIR} ]; then
1063201251Scperciva		cmd_update
1064201251Scperciva	else
1065201251Scperciva		cmd_extract
1066201251Scperciva	fi
1067201251Scperciva}
1068201251Scperciva
1069148871Scperciva#### Entry point
1070148871Scperciva
1071148871Scperciva# Make sure we find utilities from the base system
1072148871Scpercivaexport PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
1073148871Scperciva
1074163564Scperciva# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
1075163564Scpercivaexport LC_ALL=C
1076163564Scperciva
1077148871Scpercivaget_params $@
1078149027Scpercivafor COMMAND in ${COMMANDS}; do
1079149027Scperciva	cmd_${COMMAND}
1080149027Scpercivadone
1081