addrutil: Re-indent.
[wk-misc.git] / manifest-tool
1 #!/bin/sh
2 # Process GnuPG based Manifest files
3 # Copyright (C) 2003 g10 Code GmbH
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation; either version 2.1 of
8 # the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 # Default values
20 PGM="manifest-tool"
21 PGM_VERSION="0.1"
22 keyid=${MANIFEST_KEYID:-"0x37D92FFB"}
23 mode=check
24 manifest="Manifest"
25 tmp_manifest=".#Manifest.$$"
26 tmp_allnames=
27 tmp_localnames=
28 LC_ALL=C
29
30 # Helper constants
31 tick='`'
32
33 # Parse the options
34 prev=
35 while [ $# -gt 0 ]; do
36   arg="$1"
37   case $arg in
38       -*=*) optarg=$(echo "X$arg" | sed -e '1s/^X//' -e 's/[-_a-zA-Z0-9]*=//')
39             ;;
40          *) optarg=
41             ;;
42   esac
43   if [ -n "$prev" ]; then
44     eval "$prev=\$arg"
45     prev=
46     shift
47     continue
48   fi
49   case $arg in
50       --version)
51           echo "$PGM $PGM_VERSION"
52           exit 0
53           ;;
54
55       --help|-h)
56           cat <<EOF
57 Usage: $PGM [options] [arguments]
58 Update or check a Manifest file
59
60 Options:
61   -c, --check            Verify the Manifest file [default]
62   -u, --update           Update a file's signature
63   --update-names         Update the signature of all names
64   --manifest-file=FILE   Use FILE instead of ${tick}Manifest'
65   --keyid=ID             Use key ID for signing (default=$keyid)
66
67 Environment:
68
69 MANIFEST_KEYID=ID        Set the default for the signing keyid to ID
70
71 Example:
72
73   $PGM --update foo.c
74
75 This updates and replaces the signature of the file ${tick}foo.c'
76 in the Manifest file.  ${tick}foo.c' is expected to be in Manifest.
77 EOF
78           exit 0
79           ;;
80     
81       --update|-u)
82           mode=update
83           ;;
84
85       --update-names)
86           mode=update-names
87           ;;
88
89       --check|-c)
90           mode=check
91           ;;
92
93       --manifest-file)  
94           prev=manifest
95           ;;
96       --manifest-file=*)
97           manifest="$optarg"
98           ;;
99
100       -*)
101           echo "$PGM: invalid option $tick$arg'" >&2
102           exit 1
103           ;;
104
105       *)
106           break
107           ;;
108   esac
109   shift
110 done
111 if [ -n "$prev" ]; then
112   echo "$PGM: argument missing for option $tick$prev'" >&2
113   exit 1
114 fi
115
116
117 # Check mode
118 do_check ()
119 {
120     tmp=$(gawk '
121 /^[ \t]*(\#|$)/ {next}
122     {if (length ($1) > maxlen) maxlen=length($1)}
123 END { print maxlen+1 }' $manifest)
124
125     gawk -v maxfilelen=$tmp '
126 /^[ \t]*(\#|$)/ {next}
127 $1 == "$names$"   { allnames_sig = $2; next }
128                   { check_sig($1, $1, $2); allnames = (allnames $1 "\n") }
129
130 END {
131   tmpfile = ("/tmp/check-manifest." PROCINFO["pid"]);
132   command = ("sort > " tmpfile);
133   printf "%s", allnames | command;
134   close(command);
135   check_sig(tmpfile, "$names$", allnames_sig);
136   system("rm " tmpfile);
137   if (ANYERR)
138     printf "warning: some files not checked\n" > "/dev/stderr";
139   if (ANYBAD)
140     printf "warning: bad signatures found\n" > "/dev/stderr";
141   exit (ANYERR || ANYBAD)? 1:0;
142 }
143
144
145 function check_sig(file, fileprint, a,    command, pos, len, n, line, arr, any)
146 {
147   command = ("gpg --verify --logger-fd 1 --status-fd 1 - " file);
148   print "-----BEGIN PGP SIGNATURE-----\n" |& command;
149   len = index(a, "=");
150   if (substr (a, len+1, 1) == "=" && substr (a, len+2, 1) == "=")
151     len++;
152   for (pos=1; len > 0; len -= n) {
153     n = len;
154     if (n > 72)
155       n = 72;
156     print substr (a, pos, n) |& command;
157     pos += n;
158   }
159   print substr (a, pos) |& command;
160   print "-----END PGP SIGNATURE-----" |& command;
161   close(command, "to");
162   any = 0
163   while ((command |& getline line) > 0) {
164     split(line, arr);
165     if( arr[1] != "[GNUPG:]")
166       continue;
167     if( arr[2] == "VALIDSIG" ) {
168       printf ("! %-" maxfilelen "s %s %s\n"), fileprint, arr[3], arr[4];
169       any = 1;
170       continue;
171     }
172     if( arr[2] == "BADSIG" ) {
173       printf ("- %-" maxfilelen "s %s\n"), fileprint, arr[3];
174       any = 1;
175       ANYBAD=1
176       continue;
177     }
178   }      
179   close(command);
180   if( !any ) {
181       printf "? %s\n", fileprint;
182       ANYERR=1;
183   }
184 }
185           ' $manifest  
186     save_exit=$?
187
188     # Now check whether files are not included in the Manifest
189     tmp_allnames="/tmp/check-manifest-1.$$"
190     awk '/^[ \t]*(\#|$)/ {next}; $1 != "$names$" {print $1}' \
191         $manifest | sort > $tmp_allnames
192     tmp_localnames="/tmp/check-manifest-2.$$"
193     ls *.c *.h *am *.S 2>/dev/null | sort > $tmp_localnames
194     tmp=`comm -3 $tmp_allnames $tmp_localnames`
195     rm $tmp_allnames $tmp_localnames
196     tmp_allnames=
197     tmp_localnames=
198     if [ -n "$tmp" ]; then
199         echo "warning: files not included:" $tmp
200     fi
201
202     exit $save_exit
203 }
204
205
206
207 # Update mode
208 do_update ()
209 {
210     filename="$1"
211
212     if echo "$filename" | grep '[       / ]' >/dev/null; then
213         echo "$PGM $tick$filename' may not contain directory separators or spaces" >&2
214         exit 1
215     fi
216
217     if awk -v fn="$filename" '$1 == fn {rc=1;exit}; END {exit rc}' $manifest
218     then
219         echo "$PGM: $tick$filename' is not present in $tick$manifest'" >&2
220         exit 1
221     fi
222
223     if [ "$filename" == '$names$' ]; then
224         tmp_allnames="/tmp/update-manifest.$$"
225         awk '/^[ \t]*(\#|$)/ {next}; $1 != "$names$" {print $1}' \
226             $manifest | sort > $tmp_allnames
227         realfilename="$tmp_allnames"
228     else
229         if [ ! -f "$filename" ]; then
230             echo "$PGM: $tick$filename' not found" >&2
231             exit 1
232         fi
233         realfilename="$filename"
234     fi
235
236
237     # Copy the first part, sign and then copy the second part to the temp file
238     (   
239         awk -v fn="$filename" '$1 == fn {exit 0}; {print $0}' $manifest
240         echo -n "$filename "
241         gpg2 -sabu $keyid -o - $realfilename | awk '
242 /^-----BEGIN/ {hdr=1; next}
243 hdr && /^[ \t]*$/ {hdr=0; next}
244 hdr {next}
245 /^-----END/ {print ""; exit 0}
246    {printf "%s", $0; next}
247 '
248         if [ $? != 0 ]; then
249             echo "$PGM: signing failed" >&2
250             exit 1
251         fi
252         awk -v fn="$filename" '$1 == fn {ok=1;next}; ok==1 {print $0}' $manifest
253     ) > $tmp_manifest
254
255     # Update the orginal file
256     if ! mv "$tmp_manifest" "$manifest"; then
257         echo "$PGM: failed to update manifest file" >&2
258         exit 1
259     fi
260 }
261
262
263 # Cleanup on exit
264 cleanup ()
265 {
266     [ -f "$tmp_manifest" ] && rm "$tmp_manifest"
267     [ -n "$tmp_allnames" -a -f "$tmp_allnames" ] && rm "$tmp_allnames"
268     [ -n "$tmp_localnames" -a -f "$tmp_localnames" ] && rm "$tmp_localnames"
269 }
270
271 trap cleanup EXIT SIGINT SIGHUP SIGPIPE
272
273
274 # Check that the Manifest file is there
275 if [ ! -f "$manifest" ]; then
276     echo "$PGM: $tick$manifest' not found" >&2
277     exit 1
278 fi
279
280
281 # Main function dispatcher
282 case $mode in 
283     check)
284         do_check
285         ;;
286  
287     update)
288         if [ $# != 1 ]; then
289             echo "usage: $PGM --update <filename>" >&2
290             exit 1
291         fi
292         do_update $1
293         ;;
294
295     update-names)
296         if [ $# != 0 ]; then
297             echo "usage: $PGM --update-names" >&2
298             exit 1
299         fi
300         do_update '$names$'
301         ;;
302
303     *)
304         echo "$PGM: invalid mode $tick$mode'" >&2
305         exit 1
306         ''
307 esac
308 cleanup