ios2html/ios2html

632 lines
24 KiB
Bash
Executable File

#!/usr/bin/env bash
#===============================================================================
# DIRECTORY:
# ---
#
# FILE:
# ./ios2html
#
# BASIC USAGE:
# $ ./ios2html [-b browser] -c case_no [-f folder] [-h] [-n] [-o officer] [-q] [-v] ios_backup
# OR
# $ bash ios2html ...
# AND MAYBE IN FUTURE ALSO:
# $ ksh ios2html ...
# $ zsh ios2html ...
# $ dash ios2html ...
# $ busybox ash ios2html ...
#
# OPTIONS:
# -b browser: The browser command to use to view the report.
# (default: firefox)
# -c case_no: The Case Number used in the organisation.
# (MANDOTORY!)
# -f folder : The output directory to use for all files.
# (default: report)
# -h : display usage and exit
# -n : disable logging (enabled by default)
# -o officer: The name of the Officer running the case.
# If this is not supplied it is the real name field
# for the current user in /etc/passwd or OpenDirectory.
# -q : disable stdout (enabled by default)
# -v : print version and exit
# ios_backup: source (folder) to process (MANDOTORY!)
#
# EXIT STATES:
# 0 = success
# 1 = (t)csh is not supported
# 2 = python2.7 is not installed
# 3 = functions library missing
# 4 = functions library could not be loaded
# 5 = OS and/or shell not supported
# 6 = additional library missing
# 7 = unrecognised option
# 8 = option requires an argument
# 9 = template_engine could not be loaded
# 10 = bc is not installed
# (for additional codes see libraries!)
#
# DESCRIPTION:
# This script generates multiple html files by processing an ios backup.
#
# REQUIREMENTS:
# date, ...
#
# BUGS:
# - macOS 10.13.4 + dash 0.5.10.2 does not work for any reason!?
#
# TESTS:
# Plaform and shell combinations:
# - OS X (10.13.4) + bash (from Homebrew, NOT the builtin sh)
# - ArchLinux + bash (4.4.19)
# - ArchLinux + busybox ash (1.28.4)
# - ArchLinux + ksh (93v- 2014-06-25) BUT WITH WARNINGS
# - FreeBSD 11.1 + bash 4.4.19
# ! (t)csh is NOT supported !
# Sources:
# - physical backup (jailbreak) of an iPhone 5s + iOS 8.1.2
# - empty backup (folder with just one empty file)
# Tools:
# - shellcheck -s bash -e SC2154,SC1090,SC2086,SC2034,SC2016 ./ios2html
# - shellcheck -s ksh -e SC2154,SC1090,SC2086,SC2034,SC2016 ./ios2html
# - shellcheck -s dash -e SC2154,SC1090,SC2086,SC2034,SC2016 ./ios2html
#
# AUTHOR:
# Patrick Neumann, patrick@neumannsland.de
#
# COAUTHOR(S):
# Odin Heitmann, odin.heitmann@gmail.com
#
# COMPANY:
# (privately)
#
# VERSION:
# 1.1
#
# LINK TO THE MOST CURRENT VERSION:
# (Sorry, we bet, I'm not allowed to publish it over GitHub!)
#
# CREATED:
# 2018-06-05
#
# COPYRIGHT (C):
# 2018 - Patrick Neumann & Odin Heitmann
#
# LICENSE:
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# WARRANTY:
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# NOTES:
# Followed styleguides:
# - http://lug.fh-swf.de/vim/vim-bash/StyleGuideShell.en.pdf
# - https://google.github.io/styleguide/shell.xml
#
# TODO:
# - wrap too long lines to have 80 chars at maximum
# - swith vars that have to be modified while execution to lowercase
# - different default application list for different devices
# - add other devices than iPhone to the hardware_map.tsv
# - add more pictures of iOS devices
# - add detection/support for logical (advanced) backups
# - add support for Debian, Bash on Ubuntu on Windows 10, ...
# - finish support for zsh, ksh, busybox ash & dash
#
# HISTORY:
# 1.0 - P. N. & O. H. - Initial (for the trainers eyes only) release
# 1.1 - P. N. - added network plugin
#
#===============================================================================
#-------------------------------------------------------------------------------
# (t)csh needs too many modifications!
# (different "if" syntax, no conditional command, "set" before var="val",...)
# Worth reading link: http://www.grymoire.com/unix/CshTop10.txt
#-------------------------------------------------------------------------------
# SC2154: ${shell} is referenced but not assigned
# (It's not a bug... it's a feature!)
test -n "${shell}" \
&& printf "\\n\\033[01;31;40mERROR: (t)csh is not supported... EXIT\\!\\!\\!\\033[00m\\n\\n" \
&& exit 1
#-------------------------------------------------------------------------------
# Control debugging manually: on or off (and everything else then "on").
#-------------------------------------------------------------------------------
readonly DEBUG="off"
#-------------------------------------------------------------------------------
# Start of script as a UNIX timestamp in microseconds.
#-------------------------------------------------------------------------------
# Long line because:
# SC1004: Break outside single quotes if you just want to break the line!
readonly MICRO_START="$( python2.7 -c 'from time import time; print int(round(time() * 1000000));' )"
if [ -z "${MICRO_START}" ] ; then
printf "\\n\\033[01;31;40mERROR: python2.7 not installed... EXIT!!!\\033[00m\\n\\n"
exit 2
fi
#-------------------------------------------------------------------------------
# Checking for and including libraries.
#-------------------------------------------------------------------------------
# To have "better" zsh support first import main functions standalone
readonly MAIN_FUNCTIONS="$( dirname "${0}" )/lib/functions.sh"
if ! [ -f "${MAIN_FUNCTIONS}" ] ; then
printf "\\n\\033[01;31;40mERROR: main functions library missing... EXIT!!!\\033[00m\\n\\n"
exit 3
fi
# In dash, 'source' in place of '.' is not supported!
. "${MAIN_FUNCTIONS}"
#-------------------------------------------------------------------------------
# Check if functions library is loaded first.
#-------------------------------------------------------------------------------
if [ "${FUNCTIONS_LOADED}" != "true" ] ; then
printf "\\n\\033[01;31;40mERROR: functions library not loaded... EXIT!!!\\033[00m\\n\\n"
exit 4
fi
#-------------------------------------------------------------------------------
# Check for unsupported OS + Shell combinations.
#-------------------------------------------------------------------------------
if [ "${OS_NAME}" = "Darwin" ] && [ "${CURRENT_SHELL}" != "bash" ] ; then
error_exit "Sorry, actually only bash is supported on macOS\\!" 5
fi
# dash doesn't support arrays!
LIBRARIES="template_engine.sh"
LIBRARIES="${LIBRARIES} hardware_plugin.sh"
LIBRARIES="${LIBRARIES} os_plugin.sh"
LIBRARIES="${LIBRARIES} network_plugin.sh"
LIBRARIES="${LIBRARIES} configuration_plugin.sh"
LIBRARIES="${LIBRARIES} accounts_plugin.sh"
LIBRARIES="${LIBRARIES} applications_plugin.sh"
LIBRARIES="${LIBRARIES} sms_plugin.sh"
LIBRARIES="${LIBRARIES} kik_plugin.sh"
readonly LIBRARIES="${LIBRARIES} addressbook_plugin.sh"
for library in ${LIBRARIES} ; do
# SC1090: "${0%/*}/${library}" is too generic for shellcheck!
if ! [ -f "$( ${BIN_DIRNAME} "${0}" )/lib/${library}" ] ; then
error_exit "\\n\\033[01;31;40mERROR: library ${library} missing... EXIT!!!\\033[00m\\n\\n" 6
fi
. "$( ${BIN_DIRNAME} "${0}" )/lib/${library}"
done
#=== CONFIGURATION (static) ====================================================
readonly VERSION="1.1"
readonly CREATED="2018-06-05"
FOLDER="report" # <- could be modified later!
assign_binary "date" # common
assign_binary "python2.7" # -> ${BIN_PYTHON27} # common
assign_binary "sed" # common
assign_binary "bc" # uncommon
assign_binary "mv" # common
#-------------------------------------------------------------------------------
# Check existence of uncommon binaries.
#-------------------------------------------------------------------------------
[ -z "${BIN_BC}" ] && error_exit "please install bc and retry" 10
# Change some defaults (functions.sh):
#ECHO_FUNC="display"
#-------------------------------------------------------------------------------
# Variables as "controllers" for the simple template engine.
#-------------------------------------------------------------------------------
readonly HEAD_TITLE="iOS to HTML"
readonly HEADER_H1="${HEAD_TITLE}"
readonly HEADER_SUBTITLE="... featured by bash, awk, pl(ist)util, sqlite3 &amp; co."
#=== CONFIGURATION (dynamic) ===================================================
# SC2086: The space(s) in ${DATE_DISPLAY} are important for splitting options!
readonly DATE="$( ${BIN_DATE} ${DATE_DISPLAY}${MICRO_START%??????} )"
readonly SCRIPTNAME="$( ${BIN_BASENAME} "${0}" )"
#=== FUNCTION ==================================================================
# NAME: usage
# DESCRIPTION: Display help.
# PARAMETERS: -
#===============================================================================
usage() {
${BIN_PRINTF} "BASIC USAGE...\\n"
${BIN_PRINTF} " bash ios2html [-b browser] -c case_no [-f folder] [-h] [-n] [-o officer] [-q] [-v] ios_backup\\n\\n"
${BIN_PRINTF} "OPTIONS:\\n\\n"
${BIN_PRINTF} " -b browser: The browser command to use to view the report.\\n"
${BIN_PRINTF} " (default: firefox)\\n"
${BIN_PRINTF} " -c case_no: The Case Number used in the organisation.\\n"
${BIN_PRINTF} " (MANDOTORY!)\\n"
${BIN_PRINTF} " -f folder : The output directory to use for all files.\\n"
${BIN_PRINTF} " (default: report)\\n"
${BIN_PRINTF} " -h : display usage and exit\\n"
${BIN_PRINTF} " -n : disable logging (enabled by default)\\n"
${BIN_PRINTF} " -o officer: The name of the Officer running the case.\\n"
${BIN_PRINTF} " If this is not supplied it is the real name field\\n"
${BIN_PRINTF} " for the current user in /etc/passwd or OpenDirectory.\\n"
${BIN_PRINTF} " -q : disable stdout (enabled by default)\\n"
${BIN_PRINTF} " -v : print version and exit\\n"
${BIN_PRINTF} " ios_backup: source (folder) to process (MANDOTORY!)\\n\\n"
}
#-------------------------------------------------------------------------------
# Try to reconstruct the script call.
#-------------------------------------------------------------------------------
CMD_LINE="${0}"
for part in "${@}" ; do
# Using "${@}" instead of ${*} should not lead in space problems!?
CMD_LINE="${CMD_LINE} ${part}"
done
#=== CONFIGURATION (input) =====================================================
# (-) GNU- and BSD-getopt behave differently
# (+) getopts is more POSIX and system-/shell-portable
while getopts ":b:c:f:hno:qv" opt ; do
case $opt in
b ) BROWSER="${OPTARG}" # <- could be modified later!
;;
c ) readonly CASE_NUMBER="${OPTARG}"
;;
f ) FOLDER="${OPTARG}" # <- could be modified later!
;;
h ) readonly GET_HELP="yes" ;;
n ) readonly DISABLE_LOGGING="yes" ;;
o ) OFFICER="${OPTARG}" # <- could be modified later!
;;
q ) readonly DISABLE_STDOUT="yes" ;;
v ) readonly GET_VERSION_ONLY="yes" ;;
\? ) error "unrecognised option (-%s)" "${OPTARG}"
usage
exit 7
;;
: ) error "option -%s requires an argument" "${OPTARG}"
usage
exit 8
;;
esac
done
LC_ALL="C" shift "$(( OPTIND - 1 ))"
#=== CONFIGURATION (output) ====================================================
if [ "${DISABLE_LOGGING}" = "yes" ] ; then
ECHO_FUNC="display"
fi
if [ "${DISABLE_STDOUT}" = "yes" ] ; then
ECHO_FUNC="log"
fi
if [ "${DISABLE_LOGGING}" = "yes" ] && [ "${DISABLE_STDOUT}" = "yes" ] ; then
ECHO_FUNC="quiet"
fi
#-------------------------------------------------------------------------------
# Just display version, if "-h" or "-v" is given and exit without error code
# if "-v" is given.
#-------------------------------------------------------------------------------
if [ "${GET_HELP}" = "yes" ] || [ "${GET_VERSION_ONLY}" = "yes" ] ; then
${BIN_PRINTF} "\\n${SCRIPTNAME} version: ${VERSION} (created: ${CREATED})\\n\\n"
else
#-------------------------------------------------------------------------------
# Some checks for folder and logfile.
#-------------------------------------------------------------------------------
check_folder
readonly LOG_FILE="${FOLDER}/ios2html.log"
if [ "${ECHO_FUNC}" = "display_and_log" ] || [ "${ECHO_FUNC}" = "log" ] ; then
check_logfile
# SC2034: all "unused" vars are verified!
readonly LOGFILE_LINK="<li><a href='ios2html.log' target='_blank'><code>ios2html.log</code></a></li>"
readonly LOGFILE_INFO="<tr><td>Logfile:</td><td>${LOG_FILE}</td></tr>"
fi
hint "${SCRIPTNAME} version:\\n ${VERSION} (created: ${CREATED})"
${ECHO_FUNC} "\\n"
fi
if [ "${GET_VERSION_ONLY}" = "yes" ] ; then
exit 0
fi
#-------------------------------------------------------------------------------
# Just display help, if "-h" is given and exit without error code.
#-------------------------------------------------------------------------------
# make only sence for stdout
if [ "${GET_HELP}" = "yes" ] ; then
usage
exit 0
fi
#-------------------------------------------------------------------------------
# Output cmdline.
#-------------------------------------------------------------------------------
hint "Cmdline:\\n ${CMD_LINE}\\n"
#-------------------------------------------------------------------------------
# Script started at.
#-------------------------------------------------------------------------------
hint "${SCRIPTNAME} started at:\\n ${DATE}\\n"
#-------------------------------------------------------------------------------
# Loaded libraries and plugins.
#-------------------------------------------------------------------------------
hint "Loaded libraries and plugins:"
hint " functions.sh"
for library in ${LIBRARIES} ; do
hint " ${library}"
done
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Display/log some output that we already have.
#-------------------------------------------------------------------------------
hint "System:\\n ${OS_NAME} + ${CURRENT_SHELL}\\n"
hint "Defined/supplied Target folder:\\n ${FOLDER}\\n"
hint "Defined log file:\\n ${LOG_FILE}\\n"
#-------------------------------------------------------------------------------
# Some more checks.
#-------------------------------------------------------------------------------
check_browser
check_case_number
check_officer
check_source "${1}"
#-------------------------------------------------------------------------------
# Check if temblate engine library is loaded first.
#-------------------------------------------------------------------------------
if [ "${TEMPLATE_ENGINE_LOADED}" != "true" ] ; then
error_exit "template_engine.sh not loaded" 9
fi
#-------------------------------------------------------------------------------
# Create and fill folder.
#-------------------------------------------------------------------------------
init_folder
#-------------------------------------------------------------------------------
# Create "index.html".
#-------------------------------------------------------------------------------
hint "Start creating index.html...\\n"
# If the HTMP page is in a subfolder you have to change the prefix to be able
# to load css, html, ... files.
prefix="./"
top_nav="$( ${BIN_CAT} <<'EOF'
<li class='active'><strong>Main</strong></li>
<li><a href='accounts.html'>Accounts</a></li>
<li><a href='applications.html'>Applications</a></li>
EOF
)"
render_template "${HEADER}" > "${FOLDER}/index.html"
render_template "${CASE_INFORMATION}" >> "${FOLDER}/index.html"
#-------------------------------------------------------------------------------
# Add hardware information to "index.html".
#-------------------------------------------------------------------------------
if [ "${HARDWARE_PLUGIN_LOADED}" = "true" ] ; then
hint "... adding hardware informations..."
get_hardware_info
render_template "${HARDWARE_INFORMATION}" >> "${FOLDER}/index.html"
success "hardware informations added"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Add operating system information to "index.html".
#-------------------------------------------------------------------------------
if [ "${OS_PLUGIN_LOADED}" = "true" ] ; then
hint "... adding os informations..."
get_os_info
render_template "${OS_INFORMATION}" >> "${FOLDER}/index.html"
success "operating system informations added"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Add network information to "index.html".
#-------------------------------------------------------------------------------
if [ "${NETWORK_PLUGIN_LOADED}" = "true" ] ; then
hint "... adding network informations..."
get_network_info
render_template "${NETWORK_INFORMATION}" >> "${FOLDER}/index.html"
success "network informations added"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Add configuration information to "index.html".
#-------------------------------------------------------------------------------
if [ "${CONFIGURATION_PLUGIN_LOADED}" = "true" ] ; then
hint "... adding configuration informations..."
get_configuration_info
render_template "${CONFIGURATION_INFORMATION}" >> "${FOLDER}/index.html"
success "configuration informations added"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Creating "accounts.html".
#-------------------------------------------------------------------------------
if [ "${ACCOUNTS_PLUGIN_LOADED}" = "true" ] ; then
hint "Start creating accounts.html..."
top_nav="$( ${BIN_CAT} <<'EOF'
<li><a href='index.html'>Main</a></li>
<li class='active'><strong>Accounts</strong></li>
<li><a href='applications.html'>Applications</a></li>
EOF
)"
render_template "${HEADER}" > "${FOLDER}/accounts.html"
get_accounts_list
render_template "${ACCOUNTS_INFORMATION}" >> "${FOLDER}/accounts.html"
render_template "${FOOTER}" >> "${FOLDER}/accounts.html"
success "accounts.html created"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Creating "applications.html".
#-------------------------------------------------------------------------------
if [ "${APPLICATIONS_PLUGIN_LOADED}" = "true" ] ; then
hint "Start creating applications.html..."
top_nav="$( ${BIN_CAT} <<'EOF'
<li><a href='index.html'>Main</a></li>
<li><a href='accounts.html'>Accounts</a></li>
<li class='active'><strong>Applications</strong></li>
EOF
)"
render_template "${HEADER}" > "${FOLDER}/applications.html"
get_applications_list
render_template "${APPLICATIONS_INFORMATION}" >> "${FOLDER}/applications.html"
render_template "${FOOTER}" >> "${FOLDER}/applications.html"
success "applications.html created"
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Creating "sms.html".
#-------------------------------------------------------------------------------
if [ "${SMS_PLUGIN_LOADED}" = "true" ] ; then
hint "Start creating sms.html..."
top_nav="$( ${BIN_CAT} <<'EOF'
<li><a href='index.html'>Main</a></li>
<li><a href='accounts.html'>Accounts</a></li>
<li><a href='applications.html'>Applications</a></li>
EOF
)"
render_template "${HEADER}" > "${FOLDER}/sms.html"
get_sms_list
render_template "${SMS_INFORMATION}" >> "${FOLDER}/sms.html"
render_template "${FOOTER}" >> "${FOLDER}/sms.html"
#-------------------------------------------------------------------------------
# Inject hyperlink to "applications.html"
#-------------------------------------------------------------------------------
${BIN_SED} "s|<li>Messages</li>|<li><a href='sms.html'>Messages</a></li>|" "${FOLDER}/applications.html" > /tmp/applications.html
${BIN_MV} /tmp/applications.html "${FOLDER}/applications.html"
success "sms.html created"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Creating "contacts.html".
#-------------------------------------------------------------------------------
if [ "${ADDRESSBOOK_PLUGIN_LOADED}" = "true" ] ; then
hint "Start creating contacts.html..."
top_nav="$( ${BIN_CAT} <<'EOF'
<li><a href='index.html'>Main</a></li>
<li><a href='accounts.html'>Accounts</a></li>
<li><a href='applications.html'>Applications</a></li>
EOF
)"
render_template "${HEADER}" > "${FOLDER}/contacts.html"
get_addressbook_list
render_template "${ADDRESSBOOK_INFORMATION}" >> "${FOLDER}/contacts.html"
render_template "${FOOTER}" >> "${FOLDER}/contacts.html"
#-------------------------------------------------------------------------------
# Inject hyperlink to "applications.html"
#-------------------------------------------------------------------------------
${BIN_SED} "s|<li>Contacts</li>|<li><a href='contacts.html'>Contacts</a></li>|" "${FOLDER}/applications.html" > /tmp/applications.html
${BIN_MV} /tmp/applications.html "${FOLDER}/applications.html"
success "contacts.html created"
fi
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Creating "kik.html".
#-------------------------------------------------------------------------------
if [ "${KIK_PLUGIN_LOADED}" = "true" ] ; then
hint "Start creating kik.html..."
top_nav="$( ${BIN_CAT} <<'EOF'
<li><a href='index.html'>Main</a></li>
<li><a href='accounts.html'>Accounts</a></li>
<li><a href='applications.html'>Applications</a></li>
EOF
)"
render_template "${HEADER}" > "${FOLDER}/kik.html"
get_kik_list
render_template "${KIK_INFORMATION}" >> "${FOLDER}/kik.html"
render_template "${FOOTER}" >> "${FOLDER}/kik.html"
#-------------------------------------------------------------------------------
# Inject hyperlink to "applications.html"
#-------------------------------------------------------------------------------
${BIN_SED} "s|<td>com.kik.chat</td>|<td><a href='kik.html'>com.kik.chat</a></td>|" "${FOLDER}/applications.html" > /tmp/applications.html
${BIN_MV} /tmp/applications.html "${FOLDER}/applications.html"
success "kik.html created"
fi
fi
${ECHO_FUNC} "\\n"
render_template "${FOOTER}" >> "${FOLDER}/index.html"
success "index.html created"
${ECHO_FUNC} "\\n"
#-------------------------------------------------------------------------------
# Open index.html file in a browser.
#-------------------------------------------------------------------------------
case "${OS_NAME}" in
Darwin) ${BIN_OPEN} -a "${BROWSER}" "${FOLDER}/index.html"
;;
Linux|FreeBSD) ${BROWSER} "${FOLDER}/index.html"
;;
esac
readonly MICRO_STOP="$( ${BIN_PYTHON27} -c 'from time import time; print int(round(time() * 1000000))' )"
readonly MICRO_DELTA="$(( MICRO_STOP - MICRO_START ))"
# printf does not work here
readonly BENCHMARK="$( echo "scale=6; x=${MICRO_DELTA}/1000000; if( x<1 && x>0 ) print 0; x" | ${BIN_BC} )"
hint "The report was rendered in ${BENCHMARK} seconds."
#-------------------------------------------------------------------------------
# Remove color codes in logfile.
#-------------------------------------------------------------------------------
# BSDs sed does not know "--in-place"!
# BSDs sed "-i" needs "" and Linux sed "-i" does not!
if [ "${DISABLE_LOGGING}" != "yes" ] ; then
${BIN_SED} 's/'"$( ${BIN_PRINTF} "\\033" )"'\[01;3[0-9];40m//g' "${LOG_FILE}" \
> /tmp/ios2html.log
${BIN_SED} 's/'"$( ${BIN_PRINTF} "\\033" )"'\[00m//g' /tmp/ios2html.log \
> "${LOG_FILE}"
${BIN_RM} /tmp/ios2html.log
fi
exit 0