1#!/bin/sh 2# Sign files and upload them. 3 4scriptversion=2018-05-19.18; # UTC 5 6# Copyright (C) 2004-2020 Free Software Foundation, Inc. 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 3, or (at your option) 11# any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program. If not, see <https://www.gnu.org/licenses/>. 20 21# Originally written by Alexandre Duret-Lutz <adl@gnu.org>. 22# The master copy of this file is maintained in the gnulib Git repository. 23# Please send bug reports and feature requests to bug-gnulib@gnu.org. 24 25set -e 26 27GPG=gpg 28# Choose the proper version of gpg, so as to avoid a 29# "gpg-agent is not available in this session" error 30# when gpg-agent is version 3 but gpg is still version 1. 31# FIXME-2020: remove, once all major distros ship gpg version 3 as /usr/bin/gpg 32gpg_agent_version=`(gpg-agent --version) 2>/dev/null | sed -e '2,$d' -e 's/^[^0-9]*//'` 33case "$gpg_agent_version" in 34 2.*) 35 gpg_version=`(gpg --version) 2>/dev/null | sed -e '2,$d' -e 's/^[^0-9]*//'` 36 case "$gpg_version" in 37 1.*) 38 if (type gpg2) >/dev/null 2>/dev/null; then 39 # gpg2 is present. 40 GPG=gpg2 41 else 42 # gpg2 is missing. Ubuntu users should install the package 'gnupg2'. 43 echo "WARNING: Using 'gpg', which is too old. You should install 'gpg2'." 1>&2 44 fi 45 ;; 46 esac 47 ;; 48esac 49 50GPG="${GPG} --batch --no-tty" 51conffile=.gnuploadrc 52to= 53dry_run=false 54replace= 55symlink_files= 56delete_files= 57delete_symlinks= 58collect_var= 59dbg= 60nl=' 61' 62 63usage="Usage: $0 [OPTION]... [CMD] FILE... [[CMD] FILE...] 64 65Sign all FILES, and process them at the destinations specified with --to. 66If CMD is not given, it defaults to uploading. See examples below. 67 68Commands: 69 --delete delete FILES from destination 70 --symlink create symbolic links 71 --rmsymlink remove symbolic links 72 -- treat the remaining arguments as files to upload 73 74Options: 75 --to DEST specify a destination DEST for FILES 76 (multiple --to options are allowed) 77 --user NAME sign with key NAME 78 --replace allow replacements of existing files 79 --symlink-regex[=EXPR] use sed script EXPR to compute symbolic link names 80 -n, --dry-run do nothing, show what would have been done 81 (including the constructed directive file) 82 --version output version information and exit 83 -h, --help print this help text and exit 84 85If --symlink-regex is given without EXPR, then the link target name 86is created by replacing the version information with '-latest', e.g.: 87 foo-1.3.4.tar.gz -> foo-latest.tar.gz 88 89Recognized destinations are: 90 alpha.gnu.org:DIRECTORY 91 savannah.gnu.org:DIRECTORY 92 savannah.nongnu.org:DIRECTORY 93 ftp.gnu.org:DIRECTORY 94 build directive files and upload files by FTP 95 download.gnu.org.ua:{alpha|ftp}/DIRECTORY 96 build directive files and upload files by SFTP 97 [user@]host:DIRECTORY upload files with scp 98 99Options and commands are applied in order. If the file $conffile exists 100in the current working directory, its contents are prepended to the 101actual command line options. Use this to keep your defaults. Comments 102(#) and empty lines in $conffile are allowed. 103 104<https://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html> 105gives some further background. 106 107Examples: 1081. Upload foobar-1.0.tar.gz to ftp.gnu.org: 109 gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz 110 1112. Upload foobar-1.0.tar.gz and foobar-1.0.tar.xz to ftp.gnu.org: 112 gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz foobar-1.0.tar.xz 113 1143. Same as above, and also create symbolic links to foobar-latest.tar.*: 115 gnupload --to ftp.gnu.org:foobar \\ 116 --symlink-regex \\ 117 foobar-1.0.tar.gz foobar-1.0.tar.xz 118 1194. Create a symbolic link foobar-latest.tar.gz -> foobar-1.0.tar.gz 120 and likewise for the corresponding .sig file: 121 gnupload --to ftp.gnu.org:foobar \\ 122 --symlink foobar-1.0.tar.gz foobar-latest.tar.gz \\ 123 foobar-1.0.tar.gz.sig foobar-latest.tar.gz.sig 124 or (equivalent): 125 gnupload --to ftp.gnu.org:foobar \\ 126 --symlink foobar-1.0.tar.gz foobar-latest.tar.gz \\ 127 --symlink foobar-1.0.tar.gz.sig foobar-latest.tar.gz.sig 128 1295. Upload foobar-0.9.90.tar.gz to two sites: 130 gnupload --to alpha.gnu.org:foobar \\ 131 --to sources.redhat.com:~ftp/pub/foobar \\ 132 foobar-0.9.90.tar.gz 133 1346. Delete oopsbar-0.9.91.tar.gz and upload foobar-0.9.91.tar.gz 135 (the -- terminates the list of files to delete): 136 gnupload --to alpha.gnu.org:foobar \\ 137 --to sources.redhat.com:~ftp/pub/foobar \\ 138 --delete oopsbar-0.9.91.tar.gz \\ 139 -- foobar-0.9.91.tar.gz 140 141gnupload executes a program ncftpput to do the transfers; if you don't 142happen to have an ncftp package installed, the ncftpput-ftp script in 143the build-aux/ directory of the gnulib package 144(https://savannah.gnu.org/projects/gnulib) may serve as a replacement. 145 146Send patches and bug reports to <bug-gnulib@gnu.org>." 147 148# Read local configuration file 149if test -r "$conffile"; then 150 echo "$0: Reading configuration file $conffile" 151 conf=`sed 's/#.*$//;/^$/d' "$conffile" | tr "\015$nl" ' '` 152 eval set x "$conf \"\$@\"" 153 shift 154fi 155 156while test -n "$1"; do 157 case $1 in 158 -*) 159 collect_var= 160 case $1 in 161 -h | --help) 162 echo "$usage" 163 exit $? 164 ;; 165 --to) 166 if test -z "$2"; then 167 echo "$0: Missing argument for --to" 1>&2 168 exit 1 169 elif echo "$2" | grep 'ftp-upload\.gnu\.org' >/dev/null; then 170 echo "$0: Use ftp.gnu.org:PKGNAME or alpha.gnu.org:PKGNAME" >&2 171 echo "$0: for the destination, not ftp-upload.gnu.org (which" >&2 172 echo "$0: is used for direct ftp uploads, not with gnupload)." >&2 173 echo "$0: See --help and its examples if need be." >&2 174 exit 1 175 else 176 to="$to $2" 177 shift 178 fi 179 ;; 180 --user) 181 if test -z "$2"; then 182 echo "$0: Missing argument for --user" 1>&2 183 exit 1 184 else 185 GPG="$GPG --local-user $2" 186 shift 187 fi 188 ;; 189 --delete) 190 collect_var=delete_files 191 ;; 192 --replace) 193 replace="replace: true" 194 ;; 195 --rmsymlink) 196 collect_var=delete_symlinks 197 ;; 198 --symlink-regex=*) 199 symlink_expr=`expr "$1" : '[^=]*=\(.*\)'` 200 ;; 201 --symlink-regex) 202 symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|' 203 ;; 204 --symlink) 205 collect_var=symlink_files 206 ;; 207 -n | --dry-run) 208 dry_run=: 209 ;; 210 --version) 211 echo "gnupload $scriptversion" 212 exit $? 213 ;; 214 --) 215 shift 216 break 217 ;; 218 -*) 219 echo "$0: Unknown option '$1', try '$0 --help'" 1>&2 220 exit 1 221 ;; 222 esac 223 ;; 224 *) 225 if test -z "$collect_var"; then 226 break 227 else 228 eval "$collect_var=\"\$$collect_var $1\"" 229 fi 230 ;; 231 esac 232 shift 233done 234 235dprint() 236{ 237 echo "Running $* ..." 238} 239 240if $dry_run; then 241 dbg=dprint 242fi 243 244if test -z "$to"; then 245 echo "$0: Missing destination sites" >&2 246 exit 1 247fi 248 249if test -n "$symlink_files"; then 250 x=`echo "$symlink_files" | sed 's/[^ ]//g;s/ //g'` 251 if test -n "$x"; then 252 echo "$0: Odd number of symlink arguments" >&2 253 exit 1 254 fi 255fi 256 257if test $# = 0; then 258 if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then 259 echo "$0: No file to upload" 1>&2 260 exit 1 261 fi 262else 263 # Make sure all files exist. We don't want to ask 264 # for the passphrase if the script will fail. 265 for file 266 do 267 if test ! -f $file; then 268 echo "$0: Cannot find '$file'" 1>&2 269 exit 1 270 elif test -n "$symlink_expr"; then 271 linkname=`echo $file | sed "$symlink_expr"` 272 if test -z "$linkname"; then 273 echo "$0: symlink expression produces empty results" >&2 274 exit 1 275 elif test "$linkname" = $file; then 276 echo "$0: symlink expression does not alter file name" >&2 277 exit 1 278 fi 279 fi 280 done 281fi 282 283# Make sure passphrase is not exported in the environment. 284unset passphrase 285unset passphrase_fd_0 286GNUPGHOME=${GNUPGHOME:-$HOME/.gnupg} 287 288# Reset PATH to be sure that echo is a built-in. We will later use 289# 'echo $passphrase' to output the passphrase, so it is important that 290# it is a built-in (third-party programs tend to appear in 'ps' 291# listings with their arguments...). 292# Remember this script runs with 'set -e', so if echo is not built-in 293# it will exit now. 294if $dry_run || grep -q "^use-agent" $GNUPGHOME/gpg.conf; then :; else 295 PATH=/empty echo -n "Enter GPG passphrase: " 296 stty -echo 297 read -r passphrase 298 stty echo 299 echo 300 passphrase_fd_0="--passphrase-fd 0" 301fi 302 303if test $# -ne 0; then 304 for file 305 do 306 echo "Signing $file ..." 307 rm -f $file.sig 308 echo "$passphrase" | $dbg $GPG $passphrase_fd_0 -ba -o $file.sig $file 309 done 310fi 311 312 313# mkdirective DESTDIR BASE FILE STMT 314# Arguments: See upload, below 315mkdirective () 316{ 317 stmt="$4" 318 if test -n "$3"; then 319 stmt=" 320filename: $3$stmt" 321 fi 322 323 cat >${2}.directive<<EOF 324version: 1.2 325directory: $1 326comment: gnupload v. $scriptversion$stmt 327EOF 328 if $dry_run; then 329 echo "File ${2}.directive:" 330 cat ${2}.directive 331 echo "File ${2}.directive:" | sed 's/./-/g' 332 fi 333} 334 335mksymlink () 336{ 337 while test $# -ne 0 338 do 339 echo "symlink: $1 $2" 340 shift 341 shift 342 done 343} 344 345# upload DEST DESTDIR BASE FILE STMT FILES 346# Arguments: 347# DEST Destination site; 348# DESTDIR Destination directory; 349# BASE Base name for the directive file; 350# FILE Name of the file to distribute (may be empty); 351# STMT Additional statements for the directive file; 352# FILES List of files to upload. 353upload () 354{ 355 dest=$1 356 destdir=$2 357 base=$3 358 file=$4 359 stmt=$5 360 files=$6 361 362 rm -f $base.directive $base.directive.asc 363 case $dest in 364 alpha.gnu.org:*) 365 mkdirective "$destdir" "$base" "$file" "$stmt" 366 echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive 367 $dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc 368 ;; 369 ftp.gnu.org:*) 370 mkdirective "$destdir" "$base" "$file" "$stmt" 371 echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive 372 $dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc 373 ;; 374 savannah.gnu.org:*) 375 if test -z "$files"; then 376 echo "$0: warning: standalone directives not applicable for $dest" >&2 377 fi 378 $dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files 379 ;; 380 savannah.nongnu.org:*) 381 if test -z "$files"; then 382 echo "$0: warning: standalone directives not applicable for $dest" >&2 383 fi 384 $dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files 385 ;; 386 download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*) 387 destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'` 388 destdir_topdir=`echo "$destdir" | sed 's,/.*,,'` 389 mkdirective "$destdir_p1" "$base" "$file" "$stmt" 390 echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive 391 for f in $files $base.directive.asc 392 do 393 echo put $f 394 done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir 395 ;; 396 /*) 397 dest_host=`echo "$dest" | sed 's,:.*,,'` 398 mkdirective "$destdir" "$base" "$file" "$stmt" 399 echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive 400 $dbg cp $files $base.directive.asc $dest_host 401 ;; 402 *) 403 if test -z "$files"; then 404 echo "$0: warning: standalone directives not applicable for $dest" >&2 405 fi 406 $dbg scp $files $dest 407 ;; 408 esac 409 rm -f $base.directive $base.directive.asc 410} 411 412##### 413# Process any standalone directives 414stmt= 415if test -n "$symlink_files"; then 416 stmt="$stmt 417`mksymlink $symlink_files`" 418fi 419 420for file in $delete_files 421do 422 stmt="$stmt 423archive: $file" 424done 425 426for file in $delete_symlinks 427do 428 stmt="$stmt 429rmsymlink: $file" 430done 431 432if test -n "$stmt"; then 433 for dest in $to 434 do 435 destdir=`echo $dest | sed 's/[^:]*://'` 436 upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt" 437 done 438fi 439 440# Process actual uploads 441for dest in $to 442do 443 for file 444 do 445 echo "Uploading $file to $dest ..." 446 stmt= 447 # 448 # allowing file replacement is all or nothing. 449 if test -n "$replace"; then stmt="$stmt 450$replace" 451 fi 452 # 453 files="$file $file.sig" 454 destdir=`echo $dest | sed 's/[^:]*://'` 455 if test -n "$symlink_expr"; then 456 linkname=`echo $file | sed "$symlink_expr"` 457 stmt="$stmt 458symlink: $file $linkname 459symlink: $file.sig $linkname.sig" 460 fi 461 upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files" 462 done 463done 464 465exit 0 466 467# Local variables: 468# eval: (add-hook 'before-save-hook 'time-stamp) 469# time-stamp-start: "scriptversion=" 470# time-stamp-format: "%:y-%02m-%02d.%02H" 471# time-stamp-time-zone: "UTC0" 472# time-stamp-end: "; # UTC" 473# End: 474