1#!/bin/sh
2# Sign files and upload them.
3
4scriptversion=2009-04-28.21; # UTC
5
6# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation
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 <http://www.gnu.org/licenses/>.
20
21# Originally written by Alexandre Duret-Lutz <adl@gnu.org>.
22
23set -e
24
25GPG='gpg --batch --no-tty'
26conffile=.gnuploadrc
27to=
28dry_run=false
29symlink_files=
30delete_files=
31delete_symlinks=
32collect_var=
33dbg=
34
35usage="Usage: $0 [OPTIONS]... [COMMAND] FILES... [[COMMAND] FILES...]
36
37Sign all FILES, and upload them to selected destinations, according to
38<http://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html>.
39
40Commands:
41  --delete                 delete FILES from destination
42  --symlink                create symbolic links
43  --rmsymlink              remove symbolic links
44  --                       treat the remaining arguments as files to upload
45
46Options:
47  --help                   print this help text and exit
48  --to DEST                specify one destination for FILES
49                           (multiple --to options are allowed)
50  --user NAME              sign with key NAME
51  --symlink-regex[=EXPR]   use sed script EXPR to compute symbolic link names
52  --dry-run                do nothing, show what would have been done
53  --version                output version information and exit
54
55If --symlink-regex is given without EXPR, then the link target name
56is created by replacing the version information with \`-latest', e.g.:
57
58  foo-1.3.4.tar.gz -> foo-latest.tar.gz
59
60Recognized destinations are:
61  alpha.gnu.org:DIRECTORY
62  savannah.gnu.org:DIRECTORY
63  savannah.nongnu.org:DIRECTORY
64  ftp.gnu.org:DIRECTORY
65                           build directive files and upload files by FTP
66  download.gnu.org.ua:{alpha|ftp}/DIRECTORY
67                           build directive files and upload files by SFTP
68  [user@]host:DIRECTORY    upload files with scp
69
70Options and commands are applied in order.  If the file $conffile exists
71in the current working directory, its contents are prepended to the
72actual command line options.  Use this to keep your defaults.  Comments
73(#) and empty lines in $conffile are allowed.
74
75Examples:
761. Upload automake-1.8.2b.tar.gz and automake-1.8.2b.tar.bz2 to two sites:
77  gnupload --to sources.redhat.com:~ftp/pub/automake \\
78           --to alpha.gnu.org:automake \\
79           automake-1.8.2b.tar.gz automake-1.8.2b.tar.bz2
80
812. Same as above, but also create symbolic links to automake-latest.tar.*:
82  gnupload --to sources.redhat.com:~ftp/pub/automake \\
83           --to alpha.gnu.org:automake \\
84           --symlink-regex \\
85           automake-1.8.2b.tar.gz automake-1.8.2b.tar.bz2
86
873. Symlink automake-1.8.2b.tar.gz to automake-latest.tar.gz and
88automake-1.8.2b.tar.bz2 to automake-latest.tar.bz2 on both sites:
89
90  gnupload --to sources.redhat.com:~ftp/pub/automake \\
91           --to alpha.gnu.org:automake \\
92           --symlink automake-1.8.2b.tar.gz automake-latest.tar.gz \\
93                     automake-1.8.2b.tar.bz2 automake-latest.tar.bz2
94
954. Delete automake-1.8.2a.tar.gz and .bz2, remove symlink
96automake-latest.tar.gz and upload automake-1.8.2b.tar.gz:
97
98  gnupload --to sources.redhat.com:~ftp/pub/automake \\
99           --to alpha.gnu.org:automake \\
100           --delete automake-1.8.2a.tar.gz automake-1.8.2a.tar.bz2 \\
101           --rmsymlink automake-latest.tar.gz \\
102           -- \\
103           automake-1.8.2b.tar.gz automake-1.8.2b.tar.bz2
104
105Report bugs to <bug-automake@gnu.org>.
106Send patches to <automake-patches@gnu.org>."
107
108# Read local configuration file
109if test -r "$conffile"; then
110  echo "$0: Reading configuration file $conffile"
111  eval set x "`sed 's/#.*$//;/^$/d' \"$conffile\" | tr '\012\015' '  '` \"\$@\""
112  shift
113fi
114
115while test -n "$1"; do
116  case $1 in
117  -*)
118    collect_var=
119    case $1 in
120    --help)
121      echo "$usage"
122      exit $?
123      ;;
124    --to)
125      if test -z "$2"; then
126        echo "$0: Missing argument for --to" 1>&2
127        exit 1
128      else
129        to="$to $2"
130        shift
131      fi
132      ;;
133    --user)
134      if test -z "$2"; then
135        echo "$0: Missing argument for --user" 1>&2
136        exit 1
137      else
138        GPG="$GPG --local-user $2"
139        shift
140      fi
141      ;;
142    --delete)
143      collect_var=delete_files
144      ;;
145    --rmsymlink)
146      collect_var=delete_symlinks
147      ;;
148    --symlink-regex=*)
149      symlink_expr=`expr "$1" : '[^=]*=\(.*\)'`
150      ;;
151    --symlink-regex)
152      symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|'
153      ;;
154    --symlink)
155      collect_var=symlink_files
156      ;;
157    --dry-run|-n)
158      dry_run=:
159      ;;
160    --version)
161      echo "gnupload $scriptversion"
162      exit $?
163      ;;
164    --)
165      shift
166      break
167      ;;
168    -*)
169      echo "$0: Unknown option \`$1', try \`$0 --help'" 1>&2
170      exit 1
171      ;;
172    esac
173    ;;
174  *)
175    if test -z "$collect_var"; then
176      break
177    else
178      eval "$collect_var=\"\$$collect_var $1\""
179    fi
180    ;;
181  esac
182  shift
183done
184
185dprint()
186{
187  echo "Running $*..."
188}
189
190if $dry_run; then
191  dbg=dprint
192fi
193
194if test -z "$to"; then
195  echo "$0: Missing destination sites" >&2
196  exit 1
197fi
198
199if test -n "$symlink_files"; then
200  x=`echo "$symlink_files" | sed 's/[^ ]//g;s/  //g'`
201  if test -n "$x"; then
202    echo "$0: Odd number of symlink arguments" >&2
203    exit 1
204  fi
205fi
206
207if test $# = 0; then
208  if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then
209    echo "$0: No file to upload" 1>&2
210    exit 1
211  fi
212else
213  # Make sure all files exist.  We don't want to ask
214  # for the passphrase if the script will fail.
215  for file
216  do
217    if test ! -f $file; then
218      echo "$0: Cannot find \`$file'" 1>&2
219      exit 1
220    elif test -n "$symlink_expr"; then
221      linkname=`echo $file | sed "$symlink_expr"`
222      if test -z "$linkname"; then
223        echo "$0: symlink expression produces empty results" >&2
224        exit 1
225      elif test "$linkname" = $file; then
226        echo "$0: symlink expression does not alter file name" >&2
227        exit 1
228      fi
229    fi
230  done
231fi
232
233# Make sure passphrase is not exported in the environment.
234unset passphrase
235
236# Reset PATH to be sure that echo is a built-in.  We will later use
237# `echo $passphrase' to output the passphrase, so it is important that
238# it is a built-in (third-party programs tend to appear in `ps'
239# listings with their arguments...).
240# Remember this script runs with `set -e', so if echo is not built-in
241# it will exit now.
242PATH=/empty echo -n "Enter GPG passphrase: "
243stty -echo
244read -r passphrase
245stty echo
246echo
247
248if test $# -ne 0; then
249  for file
250  do
251    echo "Signing $file..."
252    rm -f $file.sig
253    echo "$passphrase" | $dbg $GPG --passphrase-fd 0 -ba -o $file.sig $file
254  done
255fi
256
257
258# mkdirective DESTDIR BASE FILE STMT
259# Arguments: See upload, below
260mkdirective ()
261{
262  stmt="$4"
263  if test -n "$3"; then
264    stmt="
265filename: $3$stmt"
266  fi
267
268  cat >${2}.directive<<EOF
269version: 1.1
270directory: $1
271comment: gnupload v. $scriptversion$stmt
272EOF
273  if $dry_run; then
274    echo "File ${2}.directive:"
275    cat ${2}.directive
276    echo "File ${2}.directive:" | sed 's/./-/g'
277  fi
278}
279
280mksymlink ()
281{
282  while test $# -ne 0
283  do
284    echo "symlink: $1 $2"
285    shift
286    shift
287  done
288}
289
290# upload DEST DESTDIR BASE FILE STMT FILES
291# Arguments:
292#  DEST     Destination site;
293#  DESTDIR  Destination directory;
294#  BASE     Base name for the directive file;
295#  FILE     Name of the file to distribute (may be empty);
296#  STMT     Additional statements for the directive file;
297#  FILES    List of files to upload.
298upload ()
299{
300  dest=$1
301  destdir=$2
302  base=$3
303  file=$4
304  stmt=$5
305  files=$6
306
307  rm -f $base.directive $base.directive.asc
308  case $dest in
309    alpha.gnu.org:*)
310      mkdirective "$destdir" "$base" "$file" "$stmt"
311      echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
312      $dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc
313      ;;
314    ftp.gnu.org:*)
315      mkdirective "$destdir" "$base" "$file" "$stmt"
316      echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
317      $dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc
318      ;;
319    savannah.gnu.org:*)
320      if test -z "$files"; then
321        echo "$0: warning: standalone directives not applicable for $dest" >&2
322      fi
323      $dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files
324      ;;
325    savannah.nongnu.org:*)
326      if test -z "$files"; then
327        echo "$0: warning: standalone directives not applicable for $dest" >&2
328      fi
329      $dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files
330      ;;
331    download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*)
332      destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'`
333      destdir_topdir=`echo "$destdir" | sed 's,/.*,,'`
334      mkdirective "$destdir_p1" "$base" "$file" "$stmt"
335      echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
336      for f in $files $base.directive.asc
337      do
338        echo put $f
339      done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir
340      ;;
341    /*)
342      dest_host=`echo "$dest" | sed 's,:.*,,'`
343      mkdirective "$destdir" "$base" "$file" "$stmt"
344      echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
345      $dbg cp $files $base.directive.asc $dest_host
346      ;;
347    *)
348      if test -z "$files"; then
349        echo "$0: warning: standalone directives not applicable for $dest" >&2
350      fi
351      $dbg scp $files $dest
352      ;;
353  esac
354  rm -f $base.directive $base.directive.asc
355}
356
357#####
358# Process any standalone directives
359stmt=
360if test -n "$symlink_files"; then
361  stmt="$stmt
362`mksymlink $symlink_files`"
363fi
364
365for file in $delete_files
366do
367  stmt="$stmt
368archive: $file"
369done
370
371for file in $delete_symlinks
372do
373  stmt="$stmt
374rmsymlink: $file"
375done
376
377if test -n "$stmt"; then
378  for dest in $to
379  do
380    destdir=`echo $dest | sed 's/[^:]*://'`
381    upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt"
382  done
383fi
384
385# Process actual uploads
386for dest in $to
387do
388  for file
389  do
390    echo "Uploading $file to $dest..."
391    stmt=
392    files="$file $file.sig"
393    destdir=`echo $dest | sed 's/[^:]*://'`
394    if test -n "$symlink_expr"; then
395      linkname=`echo $file | sed "$symlink_expr"`
396      stmt="$stmt
397symlink: $file $linkname
398symlink: $file.sig $linkname.sig"
399    fi
400    upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files"
401  done
402done
403
404exit 0
405
406# Local variables:
407# eval: (add-hook 'write-file-hooks 'time-stamp)
408# time-stamp-start: "scriptversion="
409# time-stamp-format: "%:y-%02m-%02d.%02H"
410# time-stamp-time-zone: "UTC"
411# time-stamp-end: "; # UTC"
412# End:
413