cryptop.list: New tool for listing keys.
[distorted-keys] / cryptop.list
1 #! /bin/sh
2 ###
3 ### List a user's keys
4 ###
5 ### (c) 2011 Mark Wooding
6 ###
7
8 ###----- Licensing notice ---------------------------------------------------
9 ###
10 ### This file is part of the distorted.org.uk key management suite.
11 ###
12 ### distorted-keys is free software; you can redistribute it and/or modify
13 ### it under the terms of the GNU General Public License as published by
14 ### the Free Software Foundation; either version 2 of the License, or
15 ### (at your option) any later version.
16 ###
17 ### distorted-keys is distributed in the hope that it will be useful,
18 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ### GNU General Public License for more details.
21 ###
22 ### You should have received a copy of the GNU General Public License
23 ### along with distorted-keys; if not, write to the Free Software Foundation,
24 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 set -e
27 case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
28 . "$KEYSLIB"/keyfunc.sh
29
30 defhelp <<HELP
31 [-Ha] [-C COLUMN,...] [-u USER] [PATTERN ...]
32 List stored keys. If PATTERNs are given, only list keys whose labels match
33 at least one PATTERN.
34
35 Options:
36 -H Don't show the column headings (useful for scripts).
37 -C COLUMN,... Select the columns to show.
38 -a Show keys owned by all users.
39 -u USER Show keys owned by USER.
40
41 A COLUMN spec consists of a column name and an optional column width,
42 separated by a colon. The widths of omitted columns are computed
43 automatically.
44
45 Columns:
46 flags Various flags for the key. (Unset flags are shown as \`.')
47 R key has recovery information
48 ! key nub needs recovery
49 label The key's label (relative to its owner).
50 profile The key's profile name.
51 recov Recovery key labels, comma-separated.
52 HELP
53
54 ###--------------------------------------------------------------------------
55 ### Column types.
56
57 ALLCOLS=""
58 defcol () { ALLCOLS=${ALLCOLS:+$ALLCOLS,}$1; }
59
60 defcol flags
61 col_flags () {
62 label=$1
63 flags=""
64
65 rflag=.
66 for i in recov/*/current/$label.recov; do
67 if [ -f "$i" ]; then rflag=R; break; fi
68 done
69 flags=$flags$rflag
70
71 bangflag=!
72 if [ -f nub/$label ]; then bangflag=.; fi
73 flags=$flags$bangflag
74
75 echo "$flags"
76 }
77
78 defcol label
79 col_label () {
80 label=$1
81
82 case $all,$label in
83 nil,$USERV_USER*) plabel=${label#*/} ;;
84 t,*) plabel=${label%%/*}:${label#*/} ;;
85 esac
86 echo "$plabel"
87 }
88
89 defcol profile
90 col_profile () {
91 label=$1
92
93 readmeta store/$label
94 echo "$profile"
95 }
96
97 defcol recov
98 col_recov () {
99 label=$1
100 recov=""
101
102 for i in recov/*; do
103 if [ -f "$i/current/$label.recov" ]; then
104 recov=${recov:+$recov,}${i#recov/}
105 fi
106 done
107 echo "$recov"
108 }
109
110 ###--------------------------------------------------------------------------
111 ### Main program.
112
113 ## Parse the command-line options. Remaining arguments are glob patterns.
114 header=t
115 cols=$ALLCOLS
116 all=nil
117 user=$USERV_USER
118 while getopts "HaC:u:" opt; do
119 case "$opt" in
120 a) all=t ;;
121 H) header=nil ;;
122 C) cols=$OPTARG ;;
123 u) user=$OPTARG ;;
124 *) usage_err ;;
125 esac
126 done
127 shift $(( $OPTIND - 1 ))
128 case $# in 0) set "*" ;; esac
129 cd $KEYS
130
131 ## First pass: validate the column specifications. Translate all bare column
132 ## names into explicit `NAME+0' forms. Decide whether we need a width-
133 ## measuring pass.
134 calcwd=nil
135 cc=$cols
136 wdcols=""
137 while :; do
138
139 ## Pick off the next column name. If none are left, leave the loop.
140 case "$cc" in
141 *,*) col=${cc%%,*} cc=${cc#*,} ;;
142 ?*) col=$cc cc="" ;;
143 *) break ;;
144 esac
145
146 ## Extract the column name for later.
147 name=${col%[:=+]*}
148
149 ## If we have a minimum width or no width, we need a measuring pass.
150 case "$col" in *[:=]*) ;; *) calcwd=t ;; esac
151
152 ## Check the column width is valid. Build the new column list with
153 ## explicit widths.
154 case "$col" in
155 *[:=+]*)
156 wd=${col#*[:=+]}
157 wdcols=${wdcols:+$wdcols,}$col
158 checknumber "column width" "$wd"
159 ;;
160 *)
161 wdcols=${wdcols:+$wdcols,}$col+0
162 ;;
163 esac
164
165 ## Check the column name.
166 case ,$ALLCOLS, in
167 *,"$name",*) ;;
168 *) echo >&2 "$quis: unknown column \`$name'"; exit 1 ;;
169 esac
170 done
171
172 ## Second and third pass: find the keys, compute their properties and either
173 ## measure column widths or display the results.
174 while :; do
175
176 ## Decide whether we need to display a header. (We need this on both
177 ## passes, because it may contribute to width.)
178 doheader=$header
179
180 ## Find the metadata files. This tells us where the keys are.
181 case $all in
182 t) dir=store ;;
183 nil) dir=store/$user ;;
184 esac
185 metas=$(find store -type f -name meta)
186
187 ## Work through the keys we found.
188 while :; do
189
190 ## If we're not doing a header line, read the next metadata file name.
191 ## Check that it matches at least one pattern.
192 case $doheader in
193 nil)
194 if ! read meta; then break; fi
195 label=${meta#store/}; label=${label%/meta}
196 matchp=nil
197 for pat in "$@"; do
198 case "$label" in $pat) matchp=t; break ;; esac
199 done
200 case $matchp in nil) continue ;; esac
201 ;;
202 esac
203
204 ## Now iterate over the columns. If we're calculating widths, use the
205 ## ones we worked out last time, and clear the list so we can build a new
206 ## one as we go.
207 case $calcwd in t) cols=$wdcols ;; esac
208 cc=$cols wdcols="" sep=""
209 while :; do
210
211 ## Pick off the next column spec.
212 case "$cc" in
213 *,*) col=${cc%%,*} cc=${cc#*,} ;;
214 ?*) col=$cc cc="" ;;
215 *) break ;;
216 esac
217
218 ## Work out the column name.
219 name=${col%[:=+]*} wd=${col#*[:=+]}
220
221 ## Work out the value. If this is a header line, then it's just the
222 ## column name in upper case; otherwise we have work to do.
223 case $doheader in
224 t) value=$(echo $name | tr a-z A-Z) ;;
225 nil) value=$(col_$name $label) ;;
226 esac
227
228 ## Work out what to do about it. If we're measuring, then update our
229 ## idea of the column width. If we're printing, work out a format.
230 case $calcwd,$col in
231 t,*[:=]*)
232 wdcols=${wdcols:+$wdcols,}$col
233 ;;
234 t,*+*)
235 colwd=$(( $(echo "$value" | wc -c) - 1 ))
236 if [ $colwd -gt $wd ]; then wd=$colwd; fi
237 wdcols=${wdcols:+$wdcols,}$name+$wd
238 ;;
239 nil,*[=+]*)
240 fmt="%-${wd}.${wd}s"
241 ;;
242 nil,*:*)
243 fmt="%-${wd}s"
244 ;;
245 esac
246
247 ## If we're printing, then print something. Leave space between the
248 ## columns.
249 case $calcwd in nil) printf "$sep$fmt" "$value"; sep=" " ;; esac
250 done
251
252 ## The next line will certainly not be a header.
253 doheader=nil
254
255 ## Start a new line if we're printing.
256 case $calcwd in nil) printf "\n" ;; esac
257 done <<EOF
258 $metas
259 EOF
260
261 ## If we were measuring, go round again and print; otherwise we're done.
262 case $calcwd in
263 t) calcwd=nil ;;
264 nil) break ;;
265 esac
266 done
267
268 ###----- That's all, folks --------------------------------------------------