/var/lib/sorcery/modules/libunpack
1 #!/bin/bash
2 #---------------------------------------------------------------------
3 ##
4 ## @Libgrimoire
5 ##
6 ## @Synopsis Set of functions containing the spell writing API.
7 ##
8 ##
9 ## These functions can be used in the PRE_BUILD, BUILD, POST_BUILD
10 ## and POST_INSTALL sections of spells.
11 ##
12 ## @Copyright
13 ## Original version Copyright 2001 by Kyle Sallee
14 ## Additions/Corrections Copyright 2002 by the Source Mage Team
15 ## New World libunpack Additions/Corrections by Seth Woolley (2005)
16 ##
17 #---------------------------------------------------------------------
18
19 #===================== libunpack common ==============================
20
21 #---------------------------------------------------------------------
22 ## @Type API
23 ## @param SOURCE suffix
24 ##
25 ## unpack_file takes the SOURCE suffix and figures out if it is supposed
26 ## to hash or gpg check it -- then it does its dirty work and runs unpack_hash
27 ## or unpack_gpg depending upon the circumstances. That's the only argument it
28 ## takes and needs: '' '2' '3', etc. It is run in default_pre_build for the
29 ## null argument only. Custom unpacking still requires a custom PRE_BUILD.
30 ##
31 ## valid formats: vendor-provided gpg, guru-provided gpg, any
32 ## hash-algorithm provided by gpg (currently md5, sha1, sha256, sha384,
33 ## sha512, ripemd160
34 ##
35 ## SOURCE=blah
36 ## SOURCE2=blah.asc
37 ## SOURCE_URL=http://blah.com/$SOURCE
38 ## SOURCE2_URL=http://blah.com/$SOURCE2
39 ## SOURCE_GPG=blah.gpg:$SOURCE2:UPSTREAM_KEY
40 ## SOURCE2_IGNORE=signature # for auditing purposes
41 ##
42 ## SOURCE=blah
43 ## SOURCE_URL=http://blah.com/$SOURCE
44 ## SOURCE_GPG=swoolley.gpg:$SOURCE.asc:WORKS_FOR_ME
45 ##
46 ## SOURCE=blah
47 ## SOURCE_URL=http://blah.com/$SOURCE
48 ## MD5[0]=d41d8cd98f00b204e9800998ecf8427e
49 ##
50 ## SOURCE=blah
51 ## SOURCE_URL=http://blah.com/$SOURCE
52 ## SOURCE_HASH=md5:d41d8cd98f00b204e9800998ecf8427e:WORKS_FOR_ME
53 ##
54 ## In GPG mode:
55 ## Validates the verification level (the third parameter) and the
56 ## hash algorithm against user defined lists.
57 ## It finds the public key and signature using locate_spell_file,
58 ## Then it validates it at the beginning.
59 ## see unpack_gpg()
60 ##
61 ## In HASH mode:
62 ## Validates the verification level (the third parameter) and the
63 ## hash algorithm against user defined lists.
64 ## It uses gpg to calculate the hash value except for md5 and sha1, which
65 ## coreutils provides.
66 ## see unpack_hash()
67 ##
68 ## In IGNORE mode:
69 ## It checks for the following text:
70 ## volatile (for cvs/svn/any-other-scm)
71 ## unversioned (the source file changes frequently, but not a direct scm)
72 ## signature (for gnupg signatures)
73 ## as reasons for ignoring the source code validation. Signatures
74 ## are silently ignored. Everything else respects MD5SUM_DL.
75 ## see unpack_ignore
76 ##
77 ## Otherwise, it falls back to MISSING mode, see unpack_missing
78 ## (or for now)
79 ## Otherwise, it falls back to old uncompressed md5sum check with MD5[n].
80 ## see real_unpack()
81 ##
82 ## The default verification level is "WORKS_FOR_ME"
83 ##
84 ## Verification levels are, these indicate how much effort was put into
85 ## validating the integrity of the source from the upstream vendor.
86 ## WORKS_FOR_ME No verification was done.
87 ## UPSTREAM_HASH Checked the upstream hash file
88 ## UPSTREAM_KEY Checked upstream (gpg) key, signature matched, but the
89 ## key was not validated
90 ## ESTABLISHED_UPSTREAM_KEY Upstream key was not validated against
91 ## multiple independent sources, but has been
92 ## in use for several years
93 ## VERIFIED_UPSTREAM_KEY Upstream key id was verified against multiple
94 ## independent sources.
95 ## ID_CHECK_UPSTREAM_KEY Key was verified in person with a photo id check.
96 ##
97 ## Also if you want to include more than one signature, hash, etc, just put
98 ## a 2, 3, 4, etc on the end of the variable like so:
99 ## SOURCE2_HASH2=...
100 ##
101 ## For cascading, currently it will still ask abort questions: a no abort
102 ## will make it fail over all cascades; a yes abort will have it skip to
103 ## the next cascades. Missing binaries or other failures like that (error 200
104 ## below) will silently fail over to the next check. The cascade order is:
105 ## GPG, HASH, IGNORE, MISSING
106 ##
107 ## The cascade setup allows you to place a higher bit checksum earlier
108 ## in the cascade and even if the binary doesn't work it will just print
109 ## out an abort query which can be said no to and it will continue to
110 ## fail over to the lower bit checksum that should be available in
111 ## coreutils (like sha1/md5). That's if you're not using gpg, which is
112 ## preferred. If multiple hashes are included of different ciphers, the
113 ## user can abort on either that go bad, so it can be considered a
114 ## security increase to have more than one, but only if the harder cipher
115 ## is first in the cascade order, as the first successful hash match will
116 ## go ahead and prompt an untarball. I may change it later, but for now I
117 ## think first successful match skipping the rest is least intrusive, and
118 ## I'd need to add an interface element to let the user choose to run all
119 ## checks on a single source.
120 ##
121 #---------------------------------------------------------------------
122 function real_unpack_file() {
123 debug "libgrimoire" "real_unpack_file - $*"
124
125 local FILENUM="$1"
126 local SVAR="SOURCE${FILENUM}"
127
128 real_verify_file "$@"
129 rc=$?
130 case "$rc" in
131 200) debug "libunpack" "unable to verify $SVAR ${!SVAR}" ;;
132 1) return 1 ;; # verification failed
133 0) uncompress_unpack ${!SVAR}; return $? ;;
134 esac
135
136 if false; then # <------ here's the switch to disable oldworld -------
137 debug "libgrimoire" "falling back to missing verification"
138 unpack_missing "${!SVAR}"
139 rc="$?"
140 case "$rc" in
141 0) uncompress_unpack "${!SVAR}"; return "$?" ;;
142 *) return "$rc" ;;
143 esac
144 else
145 debug "libgrimoire" "falling back to regular MD5[]"
146 local MD5NUM="$([ -z "$FILENUM" ] && echo 0 || echo "$(($FILENUM - 1))")"
147 real_unpack "${!SVAR}" "${MD5[$MD5NUM]}"
148 fi
149 }
150
151
152 #---------------------------------------------------------------------
153 ## @Type API
154 ## @param SOURCE suffix
155 ##
156 ## Does the work of verifying a file with the new-world verification
157 ## system.
158 #---------------------------------------------------------------------
159 function real_verify_file() {
160 debug "libunpack" "real_verify_file - $*"
161
162 local FILENUM="$1"
163 local SVAR="SOURCE${FILENUM}"
164
165 local crypto_func
166 for crypto_func in GPG HASH IGNORE; do
167 debug "libgrimoire" "checking $crypto_func verification"
168
169 local AVAR="SOURCE${FILENUM}_${crypto_func}"
170 [[ -n ${!AVAR} ]] || continue
171
172 local rc=""
173 local lcase_crypto_func="$(echo $crypto_func | tr 'A-Z' 'a-z')"
174 unpack_$lcase_crypto_func "${!SVAR}" "${!AVAR}"
175 rc="$?"
176
177 case "$rc" in
178 200) debug "libgrimoire" "unable to verify $AVAR with $crypto_func" ;;
179 *) return $rc ;;
180 esac
181
182 done
183 return 200
184 }
185
186
187 #---------------------------------------------------------------------
188 ## @param filename
189 ## @param compressor
190 ## @Stdout uncompressed
191 ##
192 ## Just uncompresses the file, but does not expand it. i.e. bunzip
193 ## it, but don't untar it. It dumps the expanded file to stdout.
194 ## Note: zip is a special case because it doesn't work with streams.
195 ##
196 #---------------------------------------------------------------------
197 function uncompress_core() {
198 debug "libgrimoire" "uncompress_core - $*"
199
200 case "$2" in
201 bzip2) bzip2 -cdf "$1" ;;
202 gzip) gzip -cdf "$1" ;;
203 compress*) gzip -cdf "$1" ;;
204 Zip) cat "$1" ;;
205 RPM) rpmunpack < "$1" | gzip -cd ;;
206 tar) cat "$1" ;;
207 xz|XZ) xz -cdf "$1" ;;
208 LZMA) xz -cdf "$1" ;;
209 7-zip) cat "$1" ;; # 7z supports stdout, but it unpacks at the same time
210 *) cat "$1" ;;
211 esac
212
213 }
214
215
216 #---------------------------------------------------------------------
217 ## @param filename
218 ## @param compressor
219 ## @Stdout uncompressed
220 ##
221 ## unpack_core takes the uncompressed stream and turns it into the
222 ## fully unarchived form.
223 ## Note: zip is a special case because it doesn't work with streams.
224 ##
225 #---------------------------------------------------------------------
226 function unpack_core() {
227 debug "libgrimoire" "unpack_core - $*"
228
229 case "$2" in
230 bzip2|gzip|compress*|tar|XZ|xz|LZMA)
231 if real_list_find "$3" same-permissions; then
232 tar -xf /dev/stdin 2> /dev/null
233 else
234 tar --no-same-owner --no-same-permissions -xf \
235 /dev/stdin 2> /dev/null
236 fi || cat > /dev/null ;;
237 Zip) cat /dev/stdin >/dev/null #get rid of unused output
238 unzip -q "$1" ;;
239 7-zip) cat /dev/stdin >/dev/null #get rid of unused output
240 7z e "$1" ;;
241 RPM) cpio -idm < /dev/stdin ;;
242 *) cat > /dev/null ;;
243 esac
244
245 }
246
247
248 #---------------------------------------------------------------------
249 ## @Type API
250 ## @param filename
251 ## @Stdout compressor
252 ##
253 ## Guesses what program was used to compress a file
254 ## Return value is always success due to `file' workings
255 ##
256 #---------------------------------------------------------------------
257 function real_guess_compressor() {
258 # NOTE: if the file doesn't exist, `file' still completes successfully
259 # the COMPRESSOR value in this case will be "can't"
260
261 local OUTPUT="$($FILEPROG -L -b "$1")"
262 local COMPRESSOR="$(echo "$OUTPUT" | cut -d ' ' -f1)"
263 [ "$COMPRESSOR" = "GNU" -o "$COMPRESSOR" = "POSIX" ] &&
264 COMPRESSOR="$(echo "$OUTPUT" | cut -d ' ' -f2)"
265 debug "libgrimoire" "guess_compressor() - guessed $1 compressor <$COMPRESSOR>"
266 echo "$COMPRESSOR"
267 }
268
269
270 #---------------------------------------------------------------------
271 ## @Type API
272 ##
273 ## Used to be uncompress_md5(), now it is uncompress_core()
274 ##
275 #---------------------------------------------------------------------
276 function real_uncompress() { uncompress_core "$@"; }
277
278
279 #---------------------------------------------------------------------
280 ## @param required spell
281 ##
282 ## Returns 200 if the user says not to Abort in the face, otherwise
283 ##
284 #---------------------------------------------------------------------
285 function unpack_spell_required() {
286 debug "libgrimoire" "Running unpack_spell_required -- $1"
287
288 local x
289 if ! spell_ok "$1" && ! smgl_which $2 x &> /dev/null; then
290 query "This spell has an option to check its integrity via spell "\
291 "${SPELL_COLOR}${1}${DEFAULT_COLOR}${QUERY_COLOR} with the $2 command for $3, you might consider installing it. "\
292 "Abort?" n &&
293 return 1 ||
294 return 200
295 else
296 return 0
297 fi
298
299 }
300
301
302 #===================== libunpack newworld ============================
303
304 #--------------------------------------------------------------------
305 ## @param the verification level
306 ##
307 ## returns 0 if the specified verification level is in the user's
308 ## list of allowed verification levels, or if they allow unknown
309 ## verification levels, 1 otherwise
310 ##
311 #--------------------------------------------------------------------
312 function is_allowed_verf_level() {
313 local rc=0
314 local VRFLEVEL=$1
315 message "${MESSAGE_COLOR}Checking spell level ${VRFLEVEL}${DEFAULT_COLOR}"
316 if list_find "${VRF_ALLOWED_LEVELS}" "${VRFLEVEL}:on"
317 then
318 message "${MESSAGE_COLOR}Spell level is an allowed level${DEFAULT_COLOR}"
319 elif list_find "${VRF_ALLOWED_LEVELS}" "${VRFLEVEL}:off"
320 then
321 message "${PROBLEM_COLOR}Spell level is not an allowed level${DEFAULT_COLOR}"
322 rc=1
323 else
324 if [[ "${VRF_ALLOW_NEW_LEVELS}" == "on" ]]
325 then
326 message "${MESSAGE_COLOR}Spell level is a new allowed level${DEFAULT_COLOR}"
327 else
328 message "${PROBLEM_COLOR}Spell level is not an allowed level${DEFAULT_COLOR}"
329 rc=1
330 fi
331 fi
332 return $rc
333 }
334
335 #--------------------------------------------------------------------
336 ## @param hash used
337 ## @param spells verification level
338 ##
339 ## first checks if the hash is in the user specified list in an on state then
340 ## checks if the hash is there in an off state, if it can't find either then
341 ## it checks the state of VRF_ALLOW_NEW_HASHES to see if we should succeed or
342 ## not
343 ## Returns 0 if the hash is allowed or (VRF_ALLOW_NEW_HASHES is on and the hash
344 ## is not present in the hash list)
345 ##
346 #--------------------------------------------------------------------
347 function is_allowed_hash() {
348 local rc=0
349 local hash=$1
350 local HASHLEVEL=$2
351 message "${MESSAGE_COLOR}Algorithm used: ${hash}${DEFAULT_COLOR}" &&
352 if list_find "$VRF_ALLOWED_HASHES" "${hash}:on"
353 then
354 message "${MESSAGE_COLOR}Algorithm checks out${DEFAULT_COLOR}"
355 if is_allowed_verf_level $HASHLEVEL ; then rc=0 ; else rc=1 ; fi
356 elif list_find "$VRF_ALLOWED_HASHES" "${hash}:off"
357 then
358 message "${PROBLEM_COLOR}Algorithm is not in user selected list${DEFAULT_COLOR}"
359 rc=1
360 elif [[ "$VRF_ALLOW_NEW_HASHES" == "on" ]]
361 then
362 message "${MESSAGE_COLOR}Allowing new hash ${hash}${DEFAULT_COLOR}"
363 if is_allowed_verf_level $HASHLEVEL ; then rc=0 ; else rc=1; fi
364 else
365 message "${PROBLEM_COLOR}Disallowing new hash ${hash}${DEFAULT_COLOR}"
366 rc=1
367 fi
368 return $rc
369 }
370
371 #---------------------------------------------------------------------
372 ## @param file to unpack
373 ## @param gpg public key file (.gpg) ":" gpg signature file (.asc)
374 ##
375 ## Given a file, unpack checks the gpg signature for that file, and, if
376 ## appropriate, runs the decompression program for that file, as well as
377 ## untar'ing the file. Note: zip is a special case because it doesn't
378 ## work with streams.
379 ##
380 #---------------------------------------------------------------------
381 function unpack_gpg() {
382 debug "libgrimoire" "Running unpack_gpg -- $*"
383
384 local FILENAME="$( guess_filename "$SOURCE_CACHE/$1" )"
385 local PFNAME="$( echo "$2" | cut -d: -f1 )"
386 local SFNAME="$( echo "$2" | cut -d: -f2 )"
387 local GPGLEVEL="$( echo "$2" | cut -d: -f3 )"
388 if [[ -z $GPGLEVEL ]]
389 then
390 GPGLEVEL=$DEFAULT_SPELL_VRF_LEVEL
391 elif ! list_find "${VERIFY_SPELL_LEVELS}" "${GPGLEVEL}"
392 then
393 message "${PROBLEM_COLOR}This is probably a spell bug ${GPGLEVEL} is not in ${VERIFY_SPELL_LEVELS}${DEFAULT_COLOR}"
394 return 1
395 fi
396 local GPGALGO_USED=""
397 local message_file=""
398
399 message "${MESSAGE_COLOR}GPG checking source file $1...${DEFAULT_COLOR}"
400
401 unpack_spell_required gnupg gpg || return "$?"
402
403 gpg_verify_signature "$( locate_spell_file "$SFNAME" )" \
404 "$FILENAME" \
405 "$( locate_spell_file "$PFNAME" securely)" GPGALGO_USED
406 rc="$?"
407 case "$rc" in
408 0)
409 local algo
410 rc=1
411 for algo in $GPGALGO_USED; do
412 if is_allowed_hash "$algo" "$GPGLEVEL"; then
413 rc=0
414 break
415 fi
416 done
417 ;;
418 3) message_file="Signature" ;;
419 4) message_file="Source" ;;
420 5) message_file="Keyring" ;;
421 esac
422 if [[ $message_file ]]
423 then
424 message "${PROBLEM_COLOR}AHHH!!! ${message_file} file not found${DEFAULT_COLOR}"
425 fi
426 if [ "$rc" -eq 200 ]; then
427 return 200
428 fi
429
430 gpg_user_query $rc $SPELL spell || return 1
431 return 0
432
433 }
434
435
436 #---------------------------------------------------------------------
437 ## @param file to unpack
438 ## @param algorithm ":" hashsum
439 ##
440 ## Given a file, unpack checks the hash for that file, and, if
441 ## appropriate, runs the decompression program for that file, as well as
442 ## untar'ing the file. Note: zip is a special case because it doesn't
443 ## work with streams.
444 ##
445 #---------------------------------------------------------------------
446 function unpack_hash() {
447 debug "libgrimoire" "Running unpack_hash() on $1"
448
449 local FILENAME="$( guess_filename "$SOURCE_CACHE/$1" )"
450 local ALGORITHM="$( echo "$2" | cut -d: -f1 )"
451 local HASHSUM="$( echo "$2" | cut -d: -f2 )"
452 local HLEVEL="$( echo "$2" | cut -d: -f3 )"
453 local rc=0
454 if [[ -z "$HLEVEL" ]]
455 then
456 HLEVEL=$DEFAULT_SPELL_VRF_LEVEL
457 fi
458
459 message "${MESSAGE_COLOR}Hash checking source file $1...${DEFAULT_COLOR}"
460 local HASH
461 if [ "$MD5SUM_DL" != "off" ]; then
462
463 if [[ "$ALGORITHM" == md5 ]] || [[ "$ALGORITHM" == sha1 ]] ; then
464 unpack_spell_required coreutils "${ALGORITHM}sum" "$ALGORITHM" ||
465 return "$?"
466 HASH="$(${ALGORITHM}sum "$FILENAME" | cut -d' ' -f1)"
467 else
468 unpack_spell_required gnupg gpg "$ALGORITHM" || return "$?"
469 if list_find "$(gpg_get_hashes)" $ALGORITHM; then
470 HASH="$(gpg_hashsum "${ALGORITHM}" "$FILENAME" | cut -d' ' -f1)"
471 else
472 message "${PROBLEM_COLOR}Algorithm $ALGORITHM is not"\
473 "known!${DEFAULT_COLOR}"
474 return 200
475 fi
476 fi
477 local rc=$?
478
479 if [[ "$HASH" != "$HASHSUM" ]] || [[ $rc != 0 ]]
480 then
481 error_message "${PROBLEM_COLOR}$ALGORITHM check failed!" &&
482 error_message "$HASH (computed) != $HASHSUM (expected)!$DEFAULT_COLOR" &&
483 hash_user_query 1 "$SPELL" spell || return 1
484 else
485 is_allowed_hash "$ALGORITHM" "$HLEVEL"
486 rc=$?
487 hash_user_query $rc "$SPELL" spell || return 1
488 fi
489 else
490 message "${PROBLEM_COLOR}Continuing!${DEFAULT_COLOR}"
491 fi
492 return 0
493 }
494
495 #--------------------------------------------------------------------
496 ## @param return code from unpack_hash
497 ## @param spell name
498 ##
499 ## Does some basic output to tell the user what failed and how then calls
500 ## unpack_file_user_query
501 ## Returns 0 if hash succeeded otherwise returns 1 if unpack_file_user_query
502 ## fails
503 ##
504 #--------------------------------------------------------------------
505 function hash_user_query() {
506 local rc=$1
507 local spell=$2
508 case "$rc" in
509 0)
510 message "${MESSAGE_COLOR}Hash verification succeeded${DEFAULT_COLOR}"
511 ;;
512 *)
513 message "${PROBLEM_COLOR}Hash verification failure${DEFAULT_COLOR}"
514 unpack_file_user_query $rc || return 1
515 ;;
516 esac
517 return 0
518 }
519
520 #--------------------------------------------------------------------
521 ## @param return code from the unpack_gpg or unpack_hash
522 ##
523 ## checks MD5SUM_DL to abort or not
524 ## Returns what query returns if it's called
525 ##
526 #--------------------------------------------------------------------
527 function unpack_file_user_query() {
528 local rc=$1
529 case "$rc" in
530 0)
531 ;;
532 *)
533 case "$MD5SUM_DL" in
534 ask_ignore) query "Abort?" "n" && return 1 ;;
535 ask_risky|ask_abort) query "Abort?" "y" && return 1 ;;
536 on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
537 esac
538 ;;
539 esac
540 return 0
541 }
542
543 #---------------------------------------------------------------------
544 ## @param file to unpack
545 ## @param reason to ignore it, one of: volatile unversioned signature
546 ##
547 ## Given a file, unpack checks the ignore rules for that file, and, if
548 ## appropriate, runs the decompression program for that file, as well as
549 ## untar'ing the file. Note: zip is a special case because it doesn't
550 ## work with streams.
551 ##
552 #---------------------------------------------------------------------
553 function unpack_ignore() {
554 debug "libgrimoire" "Running unpack_ignore() on $1"
555
556 REASON="$2"
557
558 message "${MESSAGE_COLOR}Not checking ${2} source file $1...${DEFAULT_COLOR}"
559
560 if [ "$MD5SUM_DL" != "off" ]; then
561
562 [ "$REASON" == "signature" ] ||
563 case "$MD5SUM_DL" in
564 ask_risky|ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
565 abort_all) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
566 ask_abort|on|*) query "Abort?" "y" && return 1 || return 0 ;;
567 esac
568
569 else
570 message "${RED}Continuing!${DEFAULT_COLOR}"
571 return 0
572 fi
573
574 }
575
576
577 #---------------------------------------------------------------------
578 ## @param file to unpack
579 ## @param reason to ignore it, one of: volatile unversioned signature
580 ##
581 ## Given a file, unpack checks the ignore rules for that file, and, if
582 ## appropriate, runs the decompression program for that file, as well as
583 ## untar'ing the file. Note: zip is a special case because it doesn't
584 ## work with streams.
585 ##
586 #---------------------------------------------------------------------
587 function unpack_missing() {
588 debug "libgrimoire" "Running unpack_missing() on $1"
589
590 message "${PROBLEM_COLOR}Missing check for source file $1!${DEFAULT_COLOR}"
591
592 if [ "$MD5SUM_DL" != "off" ]; then
593
594 case "$MD5SUM_DL" in
595 ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
596 ask_risky|ask_abort) query "Abort?" "y" && return 1 || return 0 ;;
597 on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
598 esac
599
600 else
601 message "${RED}Continuing!${DEFAULT_COLOR}"
602 return 0
603 fi
604
605 }
606
607 #---------------------------------------------------------------------
608 ## @param file to unpack
609 ##
610 ## Given a cache file, runs the decompression and the unarchival
611 ## program on it. A wrapper around uncompress_unpack
612 ##
613 #---------------------------------------------------------------------
614 function uncompress_unpack_cache() {
615 uncompress_unpack "$1" root cache same-permissions
616 }
617
618 #---------------------------------------------------------------------
619 ## @param file to unpack
620 ## @param (optional) dirname of the file (default: $SOURCE_CACHE)
621 ## 'root' if you already have an absolute path
622 ## @param (optional) type (default: source)
623 ## @param (optional) hints - used for passing hints to unpack/uncompress
624 ##
625 ## Given a file, runs the decompression program for that file, as well as
626 ## untar'ing the file.
627 ##
628 #---------------------------------------------------------------------
629 function uncompress_unpack() {
630 debug "libgrimoire" "Running uncompress_unpack() on $@"
631
632 local basedir
633 [[ $2 == root ]] || basedir=$SOURCE_CACHE
634 local filename=$(guess_filename "$basedir/$1") &&
635 local compressor=$(guess_compressor "$filename")
636 local type=${3:-source}
637 local hints="$4"
638
639 if [[ $type != cache ]]; then
640 message "${MESSAGE_COLOR}Unpacking $type file ${SPELL_COLOR}${1}${DEFAULT_COLOR}" \
641 "${MESSAGE_COLOR}for spell" \
642 "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}${MESSAGE_COLOR}.${DEFAULT_COLOR}"
643 fi
644
645 if [[ ! -f $filename ]]; then
646 error_message "$PROBLEM_COLOR$type file not found!$DEFAULT_COLOR"
647 return 1
648 fi
649
650 uncompress_core "$filename" "$compressor" "$hints" |
651 unpack_core "$filename" "$compressor" "$hints"
652 }
653
654 #---------------------------------------------------------------------
655 ## @param file to unpack
656 ##
657 ## Interface to unpack a file without any verification.
658 ##
659 #---------------------------------------------------------------------
660 function real_unpack_file_simple() { uncompress_unpack "$@"; }
661
662
663 #---------------------------------------------------------------------
664 ## @param absolute or relative file path
665 ## @param empty or 'securely', which would skip SOURCE_CACHE
666 ## @Stdout the real path of the file (sometimes relative to CWD)
667 ##
668 ## Given a file, locate_spell_file finds out where it really is within
669 ## the spell hierarchy down to the grimoire root, and then tries cwd and
670 ## then the source cache.
671 ##
672 #---------------------------------------------------------------------
673 function locate_spell_file() {
674 debug "libgrimoire" "Running locate_spell_file() $2 on $1"
675
676 # checks in any case
677 [ -f "$SPELL_DIRECTORY/$1" ] && echo "$SPELL_DIRECTORY/$1" && return 0
678 [ -f "$SECTION_DIRECTORY/$1" ] && echo "$SECTION_DIRECTORY/$1" && return 0
679 [ -f "$GRIMOIRE/$1" ] && echo "$GRIMOIRE/$1" && return 0
680 [ -f "$1" ] && echo "$1" && return 0
681
682 [ "$2" != "securely" ] && # checks in "secure" mode
683 [ -f "$SOURCE_CACHE/$1" ] && echo "$SOURCE_CACHE/$1" && return 0
684
685 message "${MESSAGE_COLOR}" \
686 "Problem: $1: file not found in spell hierarchy.${DEFAULT_COLOR}" \
687 > /dev/stderr
688 echo "$1"
689 return 1
690
691 }
692
693
694 #===================== libunpack oldworld ============================
695
696 #---------------------------------------------------------------------
697 ## @Type API
698 ## @param file to unpack
699 ## @param md5sum
700 ##
701 ## Given a file, unpack runs the decompression program for that file,
702 ## as well as untar'ing the file if appropriate and if the MD5
703 ## matches.
704 ## Note: zip is a special case because it doesn't work with streams.
705 ##
706 #---------------------------------------------------------------------
707 function real_unpack() {
708 debug "libgrimoire" "Running unpack -- $*"
709
710 message "${MESSAGE_COLOR}Unpacking source file ${SPELL_COLOR}${1}${DEFAULT_COLOR}" \
711 "${MESSAGE_COLOR}for spell" \
712 "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}${MESSAGE_COLOR}.${DEFAULT_COLOR}"
713
714 FILENAME="$(guess_filename "$SOURCE_CACHE/$1")" &&
715 COMPRESSOR="$(guess_compressor "$FILENAME")"
716
717 if [[ ! $FILENAME ]] || ! test -f "$FILENAME" ; then
718 message "${PROBLEM_COLOR}Source file not found.${DEFAULT_COLOR}"
719 return 1
720 fi
721
722 uncompress_md5 "$FILENAME" "$COMPRESSOR" "$2" |
723 unpack_core "$FILENAME" "$COMPRESSOR" &&
724 {
725
726 # This section takes care of what happens if the md5sum doesn't match.
727 # $TMP_DIR/libgrimoire.uncompress.$$ is set in uncompress. It's the only
728 # way to get the return value since it's in a pipe.
729 if ! [[ $2 ]] ; then
730
731 rm "$TMP_DIR/libgrimoire.uncompress.$$"
732
733 message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}:" \
734 "${QUERY_COLOR}doesn't have an MD5 sum for the uncompressed $1."
735
736 case "$MD5SUM_DL" in
737 off) message "${RED}Continuing!${DEFAULT_COLOR}"; return 0 ;;
738 ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
739 ask_risky|ask_abort) query "Abort?" "y" && return 1 || return 0 ;;
740 on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
741 esac
742
743 elif [[ $2 == "IGNORE" ]] ; then
744
745 rm "$TMP_DIR/libgrimoire.uncompress.$$"
746
747 message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}: ${QUERY_COLOR}MD5 sum was" \
748 "purposefully left out for the uncompressed $1."
749 message "${QUERY_COLOR}Would you like to abort so you can validate" \
750 "the source yourself via some alternate method?"
751
752 case "$MD5SUM_DL" in
753 off) message "${RED}Continuing!${DEFAULT_COLOR}"; return 0 ;;
754 ask_risky|ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
755 abort_all) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
756 ask_abort|on|*) query "Abort?" "y" && return 1 || return 0 ;;
757 esac
758
759 elif [[ "$(cat $TMP_DIR/libgrimoire.uncompress.$$)" != 0 ]] ; then
760
761 rm "$TMP_DIR/libgrimoire.uncompress.$$"
762
763 message "${SPELL_COLOR}${SPELL}${DEFAULT_COLOR}:" \
764 "${QUERY_COLOR}MD5 sum is different for uncompressed $1."
765
766 case "$MD5SUM_DL" in
767 off) message "${RED}Continuing!${DEFAULT_COLOR}"; return 0 ;;
768 ask_ignore) query "Abort?" "n" && return 1 || return 0 ;;
769 ask_risky|ask_abort) query "Abort?" "y" && return 1 || return 0 ;;
770 on|abort_all|*) message "${RED}Aborting.${DEFAULT_COLOR}" ; return 1 ;;
771 esac
772
773 fi
774
775 rm "$TMP_DIR/libgrimoire.uncompress.$$"
776
777 }
778
779 #By this point, the archive is unarchived, and we know the MD5 check was good.
780 return 0
781
782 }
783
784
785 #---------------------------------------------------------------------
786 ## @param filename
787 ## @param compressor
788 ## @param md5
789 ## @Stdout uncompressed
790 ##
791 ## Uncompress_md5 dumps the expanded file via tee to md5_tar_check where it
792 ## is gobbled up by the bitbucket. It also dumps the main">main">main">main">main stream out to
793 ## stdout.
794 ##
795 #---------------------------------------------------------------------
796 function uncompress_md5() {
797 debug "libgrimoire" "uncompress_md5 - $*"
798
799 # This is here so Duff's super debugging info doesn't screw the next step up
800 set +x
801
802 # Outer subshell is necessary to redirect stderr to stdout
803 (
804 uncompress_core "$1" "$2" |
805 tee /dev/stderr |
806 md5_tar_check "$3" 2>&1 1>/dev/null #we must avoid this printing
807 ) 2>&1
808
809 # This temp file is here because this function MUST NOT send
810 # anything to stdout or stderr, and upack needs a way to get the success or
811 # failure of this function.
812
813 local a="$?"
814 [[ $SUPER_DEBUG ]] && set -x #turn this back on as soon as possible
815 echo "$a" > "$TMP_DIR/libgrimoire.uncompress.$$"
816 return "$a"
817
818 }
819
820
821 #---------------------------------------------------------------------
822 ## @param md5
823 ##
824 ## Checks that the stdin matches the argument.
825 ## Note that DEBUG output may dissapear if it's /dev/stderr due to
826 ## uncompress' 2>/dev/null.
827 ##
828 #---------------------------------------------------------------------
829 function md5_tar_check() {
830 debug "libgrimoire" "md5_tar_check() - Checking MD5 sum"
831
832 local md5
833
834 #Do the md5
835 md5="$(md5sum /dev/stdin | awk '{print $1}')"
836 debug "libgrimoire" "md5_tar_check() - MD5 of tarball is $md5."
837 debug "libgrimoire" "md5_tar_check() - argument received is $1."
838
839 #See if they match
840 if [[ $1 == $md5 ]] ; then
841 debug "libgrimoire" "md5_tar_check() - MD5 Sum Success ( $1 == $md5 )"
842 return 0
843 fi
844
845 #See of we need to md5sum it at all
846 if [[ ${MD5SUM_DL:-on} == off ]] || ! [[ $1 ]] ; then
847 debug "libgrimoire" "md5_tar_check() - Skipping check"
848 return 0
849 fi
850
851 #If we get here, the md5's don't match, but should.
852 debug "libgrimoire" "md5_tar_check() - bad md5"
853 return 1
854
855 }
856
857
858 #---------------------------------------------------------------------
859 ## @License
860 ##
861 ## This software is free software; you can redistribute it and/or modify
862 ## it under the terms of the GNU General Public License as published by
863 ## the Free Software Foundation; either version 2 of the License, or
864 ## (at your option) any later version.
865 ##
866 ## This software is distributed in the hope that it will be useful,
867 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
868 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
869 ## GNU General Public License for more details.
870 ##
871 ## You should have received a copy of the GNU General Public License
872 ## along with this software; if not, write to the Free Software
873 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
874 ##
875 #---------------------------------------------------------------------