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