| 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; the |
| 42 | separator character determines the behaviour as shown below. The default is |
| 43 | \`+0'. |
| 44 | NAME=WIDTH Exact width: truncate the contents to fit if necessary. |
| 45 | NAME:WIDTH Minimum width: spill into the next column (breaking the |
| 46 | alignment) if necessary. |
| 47 | NAME+WIDTH Minimum width: if a value won't fit then make the entire |
| 48 | column wider. |
| 49 | |
| 50 | Columns names: |
| 51 | flags Various flags for the key. (Unset flags are shown as \`.') |
| 52 | R key has recovery information |
| 53 | ! key nub needs recovery |
| 54 | label The key's label (relative to its owner). |
| 55 | profile The key's profile name. |
| 56 | recov Recovery key labels, comma-separated. |
| 57 | HELP |
| 58 | |
| 59 | ###-------------------------------------------------------------------------- |
| 60 | ### Column types. |
| 61 | |
| 62 | ALLCOLS="" |
| 63 | defcol () { ALLCOLS=${ALLCOLS:+$ALLCOLS,}$1; } |
| 64 | |
| 65 | defcol flags |
| 66 | col_flags () { |
| 67 | label=$1 |
| 68 | flags="" |
| 69 | |
| 70 | rflag=. |
| 71 | for i in recov/*/current/$label.recov; do |
| 72 | if [ -f "$i" ]; then rflag=R; break; fi |
| 73 | done |
| 74 | flags=$flags$rflag |
| 75 | |
| 76 | bangflag=! |
| 77 | if [ -f nub/$label ]; then bangflag=.; fi |
| 78 | flags=$flags$bangflag |
| 79 | |
| 80 | echo "$flags" |
| 81 | } |
| 82 | |
| 83 | defcol label |
| 84 | col_label () { |
| 85 | label=$1 |
| 86 | |
| 87 | case $all,$label in |
| 88 | nil,$USERV_USER*) plabel=${label#*/} ;; |
| 89 | t,*) plabel=${label%%/*}:${label#*/} ;; |
| 90 | esac |
| 91 | echo "$plabel" |
| 92 | } |
| 93 | |
| 94 | defcol profile |
| 95 | col_profile () { |
| 96 | label=$1 |
| 97 | |
| 98 | readmeta store/$label |
| 99 | echo "$profile" |
| 100 | } |
| 101 | |
| 102 | defcol recov |
| 103 | col_recov () { |
| 104 | label=$1 |
| 105 | recov="" |
| 106 | |
| 107 | for i in recov/*; do |
| 108 | if [ -f "$i/current/$label.recov" ]; then |
| 109 | recov=${recov:+$recov,}${i#recov/} |
| 110 | fi |
| 111 | done |
| 112 | echo "$recov" |
| 113 | } |
| 114 | |
| 115 | ###-------------------------------------------------------------------------- |
| 116 | ### Main program. |
| 117 | |
| 118 | ## Parse the command-line options. Remaining arguments are glob patterns. |
| 119 | header=t |
| 120 | cols=$ALLCOLS |
| 121 | all=nil |
| 122 | user=$USERV_USER |
| 123 | while getopts "HaC:u:" opt; do |
| 124 | case "$opt" in |
| 125 | a) all=t ;; |
| 126 | H) header=nil ;; |
| 127 | C) cols=$OPTARG ;; |
| 128 | u) user=$OPTARG ;; |
| 129 | *) usage_err ;; |
| 130 | esac |
| 131 | done |
| 132 | shift $(( $OPTIND - 1 )) |
| 133 | case $# in 0) set "*" ;; esac |
| 134 | cd $KEYS |
| 135 | |
| 136 | ## First pass: validate the column specifications. Translate all bare column |
| 137 | ## names into explicit `NAME+0' forms. Decide whether we need a width- |
| 138 | ## measuring pass. |
| 139 | calcwd=nil |
| 140 | cc=$cols |
| 141 | wdcols="" |
| 142 | while :; do |
| 143 | |
| 144 | ## Pick off the next column name. If none are left, leave the loop. |
| 145 | case "$cc" in |
| 146 | *,*) col=${cc%%,*} cc=${cc#*,} ;; |
| 147 | ?*) col=$cc cc="" ;; |
| 148 | *) break ;; |
| 149 | esac |
| 150 | |
| 151 | ## Extract the column name for later. |
| 152 | name=${col%[:=+]*} |
| 153 | |
| 154 | ## If we have a minimum width or no width, we need a measuring pass. |
| 155 | case "$col" in *[:=]*) ;; *) calcwd=t ;; esac |
| 156 | |
| 157 | ## Check the column width is valid. Build the new column list with |
| 158 | ## explicit widths. |
| 159 | case "$col" in |
| 160 | *[:=+]*) |
| 161 | wd=${col#*[:=+]} |
| 162 | wdcols=${wdcols:+$wdcols,}$col |
| 163 | checknumber "column width" "$wd" |
| 164 | ;; |
| 165 | *) |
| 166 | wdcols=${wdcols:+$wdcols,}$col+0 |
| 167 | ;; |
| 168 | esac |
| 169 | |
| 170 | ## Check the column name. |
| 171 | case ,$ALLCOLS, in |
| 172 | *,"$name",*) ;; |
| 173 | *) echo >&2 "$quis: unknown column \`$name'"; exit 1 ;; |
| 174 | esac |
| 175 | done |
| 176 | |
| 177 | ## Second and third pass: find the keys, compute their properties and either |
| 178 | ## measure column widths or display the results. |
| 179 | while :; do |
| 180 | |
| 181 | ## Decide whether we need to display a header. (We need this on both |
| 182 | ## passes, because it may contribute to width.) |
| 183 | doheader=$header |
| 184 | |
| 185 | ## Find the metadata files. This tells us where the keys are. |
| 186 | case $all in |
| 187 | t) dir=store ;; |
| 188 | nil) dir=store/$user ;; |
| 189 | esac |
| 190 | metas=$(find $dir -type f -name meta | sort) |
| 191 | |
| 192 | ## Work through the keys we found. |
| 193 | while :; do |
| 194 | |
| 195 | ## If we're not doing a header line, read the next metadata file name. |
| 196 | ## Check that it matches at least one pattern. |
| 197 | case $doheader in |
| 198 | nil) |
| 199 | if ! read meta; then break; fi |
| 200 | label=${meta#store/}; label=${label%/meta} |
| 201 | matchp=nil |
| 202 | for pat in "$@"; do |
| 203 | case "$label" in $pat) matchp=t; break ;; esac |
| 204 | done |
| 205 | case $matchp in nil) continue ;; esac |
| 206 | ;; |
| 207 | esac |
| 208 | |
| 209 | ## Now iterate over the columns. If we're calculating widths, use the |
| 210 | ## ones we worked out last time, and clear the list so we can build a new |
| 211 | ## one as we go. |
| 212 | case $calcwd in t) cols=$wdcols ;; esac |
| 213 | cc=$cols wdcols="" sep="" |
| 214 | while :; do |
| 215 | |
| 216 | ## Pick off the next column spec. |
| 217 | case "$cc" in |
| 218 | *,*) col=${cc%%,*} cc=${cc#*,} ;; |
| 219 | ?*) col=$cc cc="" ;; |
| 220 | *) break ;; |
| 221 | esac |
| 222 | |
| 223 | ## Work out the column name. |
| 224 | name=${col%[:=+]*} wd=${col#*[:=+]} |
| 225 | |
| 226 | ## Work out the value. If this is a header line, then it's just the |
| 227 | ## column name in upper case; otherwise we have work to do. |
| 228 | case $doheader in |
| 229 | t) value=$(echo $name | tr a-z A-Z) ;; |
| 230 | nil) value=$(col_$name $label) ;; |
| 231 | esac |
| 232 | |
| 233 | ## Work out what to do about it. If we're measuring, then update our |
| 234 | ## idea of the column width. If we're printing, work out a format. |
| 235 | case $calcwd,$col in |
| 236 | t,*[:=]*) |
| 237 | wdcols=${wdcols:+$wdcols,}$col |
| 238 | ;; |
| 239 | t,*+*) |
| 240 | colwd=$(( $(echo "$value" | wc -c) - 1 )) |
| 241 | if [ $colwd -gt $wd ]; then wd=$colwd; fi |
| 242 | wdcols=${wdcols:+$wdcols,}$name+$wd |
| 243 | ;; |
| 244 | nil,*[=+]*) |
| 245 | fmt="%-${wd}.${wd}s" |
| 246 | ;; |
| 247 | nil,*:*) |
| 248 | fmt="%-${wd}s" |
| 249 | ;; |
| 250 | esac |
| 251 | |
| 252 | ## If we're printing, then print something. Leave space between the |
| 253 | ## columns. |
| 254 | case $calcwd in nil) printf "$sep$fmt" "$value"; sep=" " ;; esac |
| 255 | done |
| 256 | |
| 257 | ## The next line will certainly not be a header. |
| 258 | doheader=nil |
| 259 | |
| 260 | ## Start a new line if we're printing. |
| 261 | case $calcwd in nil) printf "\n" ;; esac |
| 262 | done <<EOF |
| 263 | $metas |
| 264 | EOF |
| 265 | |
| 266 | ## If we were measuring, go round again and print; otherwise we're done. |
| 267 | case $calcwd in |
| 268 | t) calcwd=nil ;; |
| 269 | nil) break ;; |
| 270 | esac |
| 271 | done |
| 272 | |
| 273 | ###----- That's all, folks -------------------------------------------------- |