Program List "bar"

#!/bin/bash

# bitmap archiver:
# bar Ver 0.003 (2009/03/01)
# Copyright (C) 2009 Adsaria

# This program is free software; you can redistribute it and/or modify it.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY.

### Default Value definitions
MAX_IMAGE_WIDTH=4096
BITMAP_FORMAT=png

### Constants
BAR_ID=BAR1
BAR_VER=1
ARCHIVE_TYPE=TGZ_
SIGNATURE_TYPE=RSA_
END_OF_ARCHIVE=EBAR

export LANG=en_US.UTF-8
CMD=`basename $0`
USAGE="$CMD -x|-c 
	[ -f|--file bitmap-archive ] 
	[ -s|--secret secret_key | -p|--private public_key ] 
	[ -b|--bitmap png|bmp ] 
	[ file... ] "
COLOR_DEPTH=24

### Option processing
GETOPT_FORM=`getopt -o cxvf:s:p:b: \
	--long create,extract,verbose,help,file:,secret:,public:,bitmap: \
	-n $CMD -- "$@"`
eval set -- "$GETOPT_FORM"
while true ; do
	case "$1" in
		-c|--create) OPER_C=Y ; OPERATION=create; shift ;;
		-x|--extract) OPER_X=Y ; OPERATION=extract; shift ;;
		-v|--verbose) TAR_OPT="-v" ; shift ;;
		-f|--file) ARCHIVE_FILE="$2" ; shift 2 ;;
		-s|--secret) PRIVATE_KEY="$2" ; shift 2 ;;
		-p|--public) PUBLIC_KEY="$2" ; shift 2 ;;
		-b|--bitmap) BITMAP_FORMAT_SPEC="$2" ; shift 2 ;;
		--help) echo "$USAGE" >&2 ; exit 1 ;;
		--) shift ; break ;;
		*) echo "Internal error!" >&2 ; exit 1 ;;
	esac
done
for arg do SRC_FILES="$SRC_FILES \"$arg\""; done

### Definition for temporally files
TAR_TEMP_FILE=`mktemp "$CMD.TAR_XXXXXXXXXX.tgz"`
SIGNATURE_TEMP_FILE=`mktemp "$CMD.SIG_XXXXXXXXXX.sig"`
BMP_Temp_File=`mktemp "$CMD.BMP_XXXXXXXXXX.bmp"`
PNG_Temp_File=`mktemp "$CMD.PNG_XXXXXXXXXX.png"`

trap 'clean_exit 2' 0 INT QUIT TRAP USR1 PIPE TERM

clean_exit ()
{
	trap - 0 INT QUIT TRAP USR1 PIPE TERM
	rm -rf "$TAR_TEMP_FILE"
	rm -rf "$SIGNATURE_TEMP_FILE"
	rm -rf "$BMP_Temp_File"
	rm -rf "$PNG_Temp_File"
	exit $1
}

tohex8 ()
{
	DECIMAL=$1
	UPPER=`expr \( $DECIMAL - \( $DECIMAL / 256 \* 256 \) \) / 16`
	LOWER=`expr $DECIMAL - \( $DECIMAL / 16 \* 16 \)`

	HEX_CHARS="0123456789ABCDEF"
	echo -n "\\x${HEX_CHARS:$UPPER:1}${HEX_CHARS:$LOWER:1}"
}

tohex16 ()
{
	DECIMAL=$1
	BYTE_0=`expr \( $DECIMAL - \( $DECIMAL / 65536 \* 65536 \) \) / 256`
	BYTE_1=`expr $DECIMAL - \( $DECIMAL / 256 \* 256 \)`

	echo -n `tohex8 $BYTE_1;tohex8 $BYTE_0`
}

tohex32 ()
{
	DECIMAL=$1
	BYTE_0=`expr \( $DECIMAL - \( $DECIMAL / 4294967296 \* 4294967296 \) \) / 16777216`
	BYTE_1=`expr \( $DECIMAL - \( $DECIMAL / 16777216 \* 16777216 \) \) / 65536`
	BYTE_2=`expr \( $DECIMAL - \( $DECIMAL / 65536 \* 65536 \) \) / 256`
	BYTE_3=`expr $DECIMAL - \( $DECIMAL / 256 \* 256 \)`

	echo -n `tohex8 $BYTE_3;tohex8 $BYTE_2;tohex8 $BYTE_1;tohex8 $BYTE_0`
}

exec_create ()
{
	eval tar zcf $TAR_TEMP_FILE "$TAR_OPT" $SRC_FILES 2> /dev/null
	if [ $? -ne 0 ]; then
		echo "${CMD}: arciving error." >&2
		clean_exit 1
	fi

	SIGNATURE_BYTES=0
	if [ -n "$PRIVATE_KEY" ]; then
		MD5SUM=`md5sum -b < "$TAR_TEMP_FILE" | cut -f 1 -d " "`
		echo -n "$MD5SUM" | openssl rsautl -out "$SIGNATURE_TEMP_FILE" -sign -inkey "$PRIVATE_KEY" 2> /dev/null
		if [ $? -ne 0 ]; then echo "${CMD}: could not generate the SIGNATURE_TEMP_FILE." >&2 ; fi
		# in the case of failure of generating signature, make null file as dummy.
		touch "$SIGNATURE_TEMP_FILE"
		SIGNATURE_BYTES=`wc -c "$SIGNATURE_TEMP_FILE" | cut -f 1 -d " "`
	fi

	IMAGE_WIDTH=$MAX_IMAGE_WIDTH
	TAR_TEMP_BYTES=`wc -c "$TAR_TEMP_FILE" | cut -f 1 -d " "`

	IMAGE_BYTES=`expr 4 + 4 + 4 + $TAR_TEMP_BYTES + 4 + 4 + $SIGNATURE_BYTES + 4 + 4`
	IMAGE_PIXS=`expr \( $IMAGE_BYTES \* 8 + $COLOR_DEPTH - 1 \) / $COLOR_DEPTH`

	# Deciding the image width and height, not in excess of MAX_IMAGE_WIDTH
	if [ $IMAGE_PIXS -lt `expr $MAX_IMAGE_WIDTH \* $MAX_IMAGE_WIDTH` ]; then
		for (( WIDTH=1 ; WIDTH<=$MAX_IMAGE_WIDTH ; WIDTH*=2 )); do
			SQUARE=`expr $WIDTH \* $WIDTH`
			if [ $IMAGE_PIXS -lt $SQUARE ]; then break; fi
		done
		if [ $WIDTH -gt $MAX_IMAGE_WIDTH ]; then WIDTH=$MAX_IMAGE_WIDTH; fi
		IMAGE_WIDTH=$WIDTH
	fi
	IMAGE_HEIGHT=`expr \( $IMAGE_PIXS + $IMAGE_WIDTH - 1 \) / $IMAGE_WIDTH`

	PADDING_BYTES=`expr \( $IMAGE_WIDTH \* $IMAGE_HEIGHT \) \* $COLOR_DEPTH / 8 - $IMAGE_BYTES`

	BF_TYPE=BM
	BF_SIZE=`expr 54 + $IMAGE_BYTES + $PADDING_BYTES`
	BF_RESERVED_1=0
	BF_RESERVED_2=0
	BF_OFFSET=54

	BI_HEADER_SIZE=40
	BI_WIDTH=$IMAGE_WIDTH
	BI_HEIGHT=$IMAGE_HEIGHT
	BI_PLANES=1
	BI_BITCOUNT=$COLOR_DEPTH
	BI_COMPRESSION=0
	BI_IMAGE_SIZE=`expr $IMAGE_BYTES + $PADDING_BYTES`
	# 2834 = 72 dpi, 3779 = 96 dpi
	BI_X_BPM=2834
	BI_Y_BPM=2834
	BI_COLOR_USED=0
	BI_COLOR_IMPORTANT=0

#	RGB_QUAD_1="\\x00\\x00\\x00\\xFF"
#	RGB_QUAD_2="\\xFF\\xFF\\xFF\\xFF"

	(
	exec 2> /dev/null
	exec > "$BMP_Temp_File"

	echo -n "$BF_TYPE"
	echo -n -e `tohex32 $BF_SIZE`
	echo -n -e `tohex16 $BF_RESERVED_1`
	echo -n -e `tohex16 $BF_RESERVED_2`
	echo -n -e `tohex32 $BF_OFFSET`

	echo -n -e `tohex32 $BI_HEADER_SIZE`
	echo -n -e `tohex32 $BI_WIDTH`
	echo -n -e `tohex32 $BI_HEIGHT`
	echo -n -e `tohex16 $BI_PLANES`
	echo -n -e `tohex16 $BI_BITCOUNT`
	echo -n -e `tohex32 $BI_COMPRESSION`
	echo -n -e `tohex32 $BI_IMAGE_SIZE`
	echo -n -e `tohex32 $BI_X_BPM`
	echo -n -e `tohex32 $BI_Y_BPM`
	echo -n -e `tohex32 $BI_COLOR_USED`
	echo -n -e `tohex32 $BI_COLOR_IMPORTANT`

#	echo -n -e $RGB_QUAD_1
#	echo -n -e $RGB_QUAD_2

	echo -n "$BAR_ID"
	echo -n -e `tohex32 4`
	echo -n -e `tohex32 $BAR_VER`

	echo -n "$ARCHIVE_TYPE"
	echo -n -e `tohex32 $TAR_TEMP_BYTES`
	cat $TAR_TEMP_FILE

	echo -n "$SIGNATURE_TYPE"
	echo -n -e `tohex32 $SIGNATURE_BYTES`
	if [ $SIGNATURE_BYTES -ne 0 ]; then
		cat "$SIGNATURE_TEMP_FILE"
	fi

	echo -n "$END_OF_ARCHIVE"
	echo -n -e `tohex32 0`

	head -c "$PADDING_BYTES" /dev/zero
	)

	SOURCE_TEMP_FILE="$BMP_Temp_File"
	if [ "$BITMAP_FORMAT" = png ]; then
		convert "$BMP_Temp_File" "$PNG_Temp_File"
		SOURCE_TEMP_FILE="$PNG_Temp_File"
	fi

	if [ -z "$ARCHIVE_FILE" ]; then
		cat "$SOURCE_TEMP_FILE"
	else
		cp "$SOURCE_TEMP_FILE" "$ARCHIVE_FILE"
	fi
}

exec_extract ()
{
	if [ -z "$ARCHIVE_FILE" ]; then
		cat > "$PNG_Temp_File"
	else
		cp -f "$ARCHIVE_FILE" "$PNG_Temp_File"
	fi

	FILE_TYPE=`file -b "$PNG_Temp_File" | cut -f 1 -d ","`
	case "$FILE_TYPE" in
	"PNG image data")
		convert "$PNG_Temp_File" "$BMP_Temp_File"
		;;
	"PC bitmap data")
		cp -f "$PNG_Temp_File" "$BMP_Temp_File"
		;;
	*)
		echo "${CMD}: input data type \"$FILE_TYPE\" is unknown." >&2
		clean_exit 1
		;;
	esac

	DEFAULT_OFFSET=54
	BF_TYPE=`hexdump -s 0 -n 2 -e "\"%c\"" "$BMP_Temp_File"`
	if [ "$BF_TYPE" != "BM" ]; then echo "${CMD}: Archive format error." >&2 ; clean_exit 1; fi
	BF_SIZE=`hexdump -s 2 -n 4 -e "\"%u\"" "$BMP_Temp_File"`
	BF_RESERVED_1=`hexdump -s 6 -n 2 -e "\"%u\"" "$BMP_Temp_File"`
	BF_RESERVED_2=`hexdump -s 8 -n 2 -e "\"%u\"" "$BMP_Temp_File"`
	BF_OFFSET=`hexdump -s 10 -n 4 -e "\"%u\"" "$BMP_Temp_File"`
	if [ "$BF_OFFSET" -ne "$DEFAULT_OFFSET" ]; then echo "${CMD}: Archive format error." >&2 ; clean_exit 1; fi

	TOTAL_NET_SIZE=0
	FRAME_OFFSET=`expr $BF_OFFSET`
	FRAME_NAME=`hexdump -s $FRAME_OFFSET -n 4 -e "\"%c\"" "$BMP_Temp_File"`
	FRAME_BYTES_OFFSET=`expr $FRAME_OFFSET + 4`
	FRAME_BYTES=`hexdump -s $FRAME_BYTES_OFFSET -n 4 -e "\"%u\"" "$BMP_Temp_File"`
	FRAME_BODY_OFFSET=`expr $FRAME_OFFSET + 8`
	TOTAL_NET_SIZE=`expr $TOTAL_NET_SIZE + 4 + 4 + $FRAME_BYTES`
	while [ "$FRAME_NAME" != "$END_OF_ARCHIVE" ]; do 
		case "$FRAME_NAME" in
		"$BAR_ID")
			BAR_VAR=`hexdump -s $FRAME_BODY_OFFSET -n $FRAME_BYTES -e "\"%u\"" "$BMP_Temp_File"`
			;;
		"$ARCHIVE_TYPE")
			tail -c +`expr $FRAME_BODY_OFFSET + 1` "$BMP_Temp_File" 2> /dev/null \
				| head -c "$FRAME_BYTES" 2> /dev/null \
				> "$TAR_TEMP_FILE"
			if [ $? -ne 0 ]; then echo "${CMD}: Extracting archive error." >&2 ; clean_exit 1; fi

			tar xf "$TAR_TEMP_FILE" "$TAR_OPT"
			if [ $? -ne 0 ]; then echo "${CMD}: Extracting file error." >&2 ; clean_exit 1; fi
			;;
		"$SIGNATURE_TYPE")
			if [ $FRAME_BYTES -ne 0 ]; then
				tail -c +`expr $FRAME_BODY_OFFSET + 1` "$BMP_Temp_File" 2> /dev/null \
					| head -c "$FRAME_BYTES" 2> /dev/null \
					> "$SIGNATURE_TEMP_FILE"
				if [ $? -ne 0 ]; then echo "${CMD}: WARNING, Extracting signature error." >&2 ; return 1; fi
				if [ -n "$PUBLIC_KEY" ]; then 
					SIGNATURE=`openssl rsautl -in "$SIGNATURE_TEMP_FILE" -verify -pubin -inkey "$PUBLIC_KEY" 2> /dev/null`
					if [ -n "$SIGNATURE" ]; then
						MD5SUM=`md5sum -b < "$TAR_TEMP_FILE" | cut -f 1 -d " "`
						if [ "$SIGNATURE" = "$MD5SUM" ]; then
							echo "${CMD}: SIGNATURE was verified." >&2
						else
							echo "${CMD}: WARNING, SIGNATURE was incorrect." >&2
						fi
					else
						echo "${CMD}: WARNING, unable to extract SIGNATURE." >&2
					fi
				else
					echo "${CMD}: WARNING, SIGNATURE was not verified, Public key was missing." >&2
				fi
			fi
			;;
		*)
			echo "${CMD}: Unknown frame error." >&2
			clean_exit 1
			;;
		esac
		FRAME_OFFSET=`expr $FRAME_OFFSET + 4 + 4 + $FRAME_BYTES`
		FRAME_NAME=`hexdump -s $FRAME_OFFSET -n 4 -e "\"%c\"" "$BMP_Temp_File"`
		FRAME_BYTES_OFFSET=`expr $FRAME_OFFSET + 4`
		FRAME_BYTES=`hexdump -s $FRAME_BYTES_OFFSET -n 4 -e "\"%u\"" "$BMP_Temp_File"`
		FRAME_BODY_OFFSET=`expr $FRAME_OFFSET + 8`
		TOTAL_NET_SIZE=`expr $TOTAL_NET_SIZE + 4 + 4 + $FRAME_BYTES`
	done
	if [ "$TOTAL_NET_SIZE" -gt "$BF_SIZE" ]; then echo "${CMD}: Format error." >&2 ; clean_exit 1; fi
}

### Now start main program

if [ "${OPER_C}${OPER_X}" != "Y" ];then echo "$USAGE" >&2 ; clean_exit 1; fi

case $OPERATION in
create)
	if [ -z "$SRC_FILES" ]; then
		echo "${CMD}: Cowardly refusing to create an empty archive" >&2
		clean_exit 1
	fi

	if [ -n "$ARCHIVE_FILE" ]; then
		if [  -e "$ARCHIVE_FILE" ]; then
			if [ ! -f "$ARCHIVE_FILE" ]; then
				echo "${CMD}: \"$ARCHIVE_FILE\": Cannot read: Is not a regular file" >&2
				clean_exit 1;
			fi
			if [ ! -w "$ARCHIVE_FILE" ]; then
				echo "${CMD}: \"$ARCHIVE_FILE\": Cannot open: Permission denied" >&2
				clean_exit 1;
			fi
		fi
		SUFFIX=`basename "$ARCHIVE_FILE" | tr "." "\n" | tail -1`
		case "$SUFFIX" in
		png|PNG) BITMAP_FORMAT=png ;;
		bmp|BMP) BITMAP_FORMAT=bmp ;;
		esac
	fi

	case "$BITMAP_FORMAT_SPEC" in
	png|PNG) BITMAP_FORMAT=png ;;
	bmp|BMP) BITMAP_FORMAT=bmp ;;
	"") ;;
	*)
		echo "${CMD}: Bitmap format \"$BITMAP_FORMAT\" is invalid." >&2
		clean_exit 1
		;;
	esac

	if [ -n "$PRIVATE_KEY" -a -e "$PRIVATE_KEY" ]; then
		if [ ! -f "$PRIVATE_KEY" ]; then
			echo "${CMD}: \"$PRIVATE_KEY\": Cannot read: Is not a regular file" >&2
			clean_exit 1;
		fi
		if [ ! -r "$PRIVATE_KEY" ]; then
			echo "${CMD}: \"$PRIVATE_KEY\": Cannot open: Permission denied" >&2
			clean_exit 1;
		fi
	fi
	if [ -n "$PUBLIC_KEY" ]; then
		echo "${CMD}: \"secret (private)\" key is needed." >&2
		clean_exit 1;
	fi

	exec_create
	;;
extract)
	if [ -n "$ARCHIVE_FILE" ]; then
		if [ ! -e "$ARCHIVE_FILE" ]; then
			echo "${CMD}: \"$ARCHIVE_FILE\": Cannot open: No such file or directory" >&2
			clean_exit 1;
		fi
		if [ ! -f "$ARCHIVE_FILE" ]; then
			echo "${CMD}: \"$ARCHIVE_FILE\": Cannot read: Is not a regular file" >&2
			clean_exit 1;
		fi
		if [ ! -r "$ARCHIVE_FILE" ]; then
			echo "${CMD}: \"$ARCHIVE_FILE\": Cannot open: Permission denied" >&2
			clean_exit 1;
		fi
	fi

	if [ -n "$PRIVATE_KEY" ]; then
		echo "${CMD}: \"public\" key is needed." >&2
		clean_exit 1;
	fi

	if [ -n "$BITMAP_FORMAT_SPEC" ]; then
		echo "${CMD}: Bitmap format specification is ignored." >&2
	fi

	exec_extract
	;;
esac

clean_exit 0