| 1 | #!/bin/sh
 | 
  
    | 2 | 
 | 
  
    | 3 | # Copyright (C) 2021 Wojtek Kosior
 | 
  
    | 4 | # Redistribution terms are gathered in the `copyright' file.
 | 
  
    | 5 | 
 | 
  
    | 6 | . ./shell_utils.sh
 | 
  
    | 7 | 
 | 
  
    | 8 | handle_export_line() {
 | 
  
    | 9 |     if [ "x$1" = "xEXPORTS_START" ]; then
 | 
  
    | 10 | 	if [ "$STATE" = "before_block" ]; then
 | 
  
    | 11 | 	    STATE="in_block"
 | 
  
    | 12 | 	fi
 | 
  
    | 13 |     elif [ "x$1" = "xEXPORT" ]; then
 | 
  
    | 14 | 	if [ "$STATE" != "in_block" ]; then
 | 
  
    | 15 | 	    return
 | 
  
    | 16 | 	fi
 | 
  
    | 17 | 
 | 
  
    | 18 | 	EXPORTCODE="${EXPORTCODE}window.killtheweb.$2 = $2;$ENDL"
 | 
  
    | 19 | 
 | 
  
    | 20 | 	PREVIOUS_FILE="$(map_get EXPORTS $2)"
 | 
  
    | 21 | 	if [ "x$PREVIOUS_FILE" != "x" ]; then
 | 
  
    | 22 | 	    errcho "export $2 present in both $PREVIOUS_FILE and $FILE"
 | 
  
    | 23 | 	    return 1
 | 
  
    | 24 | 	fi
 | 
  
    | 25 | 
 | 
  
    | 26 | 	map_set_instr EXPORTS $2 "$FILE"
 | 
  
    | 27 | 
 | 
  
    | 28 |     elif [ "x$1" = "xEXPORTS_END" ]; then
 | 
  
    | 29 | 	if [ "$STATE" = "in_block" ]; then
 | 
  
    | 30 | 	    STATE="after_block"
 | 
  
    | 31 | 	fi
 | 
  
    | 32 |     fi
 | 
  
    | 33 | }
 | 
  
    | 34 | 
 | 
  
    | 35 | translate_exports() {
 | 
  
    | 36 |     STATE="before_block"
 | 
  
    | 37 |     EXPORTCODE=''
 | 
  
    | 38 | 
 | 
  
    | 39 |     while read EXPORT_LINE; do
 | 
  
    | 40 | 	handle_export_line $EXPORT_LINE || return 1
 | 
  
    | 41 |     done
 | 
  
    | 42 | 
 | 
  
    | 43 |     map_set_instr EXPORTCODES $FILEKEY "$EXPORTCODE"
 | 
  
    | 44 | }
 | 
  
    | 45 | 
 | 
  
    | 46 | add_exports() {
 | 
  
    | 47 |     FILE="$1"
 | 
  
    | 48 |     FILEKEY="$(sanitize "$FILE")"
 | 
  
    | 49 | 
 | 
  
    | 50 |     eval "$(grep -o 'EXPORT.\+' "$1" | translate_exports || exit 1)"
 | 
  
    | 51 | }
 | 
  
    | 52 | 
 | 
  
    | 53 | handle_import_line() {
 | 
  
    | 54 |     if [ "x$1" = "xIMPORTS_START" ]; then
 | 
  
    | 55 | 	if [ "$STATE" = "before_block" ]; then
 | 
  
    | 56 | 	    STATE="in_block"
 | 
  
    | 57 | 	fi
 | 
  
    | 58 |     elif [ "x$1" = "xIMPORT" ]; then
 | 
  
    | 59 | 	if [ "$STATE" != "in_block" ]; then
 | 
  
    | 60 | 	    return
 | 
  
    | 61 | 	fi
 | 
  
    | 62 | 
 | 
  
    | 63 | 	IMPORTCODE="${IMPORTCODE}const $2 = window.killtheweb.$2;$ENDL"
 | 
  
    | 64 | 
 | 
  
    | 65 | 	IMPORTS="$IMPORTS $2"
 | 
  
    | 66 | 
 | 
  
    | 67 |     elif [ "x$1" = "xIMPORTS_END" ]; then
 | 
  
    | 68 | 	if [ "$STATE" = "in_block" ]; then
 | 
  
    | 69 | 	    STATE="after_block"
 | 
  
    | 70 | 	fi
 | 
  
    | 71 |     fi
 | 
  
    | 72 | }
 | 
  
    | 73 | 
 | 
  
    | 74 | translate_imports() {
 | 
  
    | 75 |     STATE="before_block"
 | 
  
    | 76 |     IMPORTCODE=''
 | 
  
    | 77 |     IMPORTS=''
 | 
  
    | 78 | 
 | 
  
    | 79 |     while read IMPORT_LINE; do
 | 
  
    | 80 | 	handle_import_line $IMPORT_LINE || return 1
 | 
  
    | 81 |     done
 | 
  
    | 82 | 
 | 
  
    | 83 |     map_set_instr IMPORTCODES $FILEKEY "$IMPORTCODE"
 | 
  
    | 84 |     map_set_instr IMPORTS $FILEKEY "$IMPORTS"
 | 
  
    | 85 | }
 | 
  
    | 86 | 
 | 
  
    | 87 | add_imports() {
 | 
  
    | 88 |     FILE="$1"
 | 
  
    | 89 |     FILEKEY="$(sanitize "$FILE")"
 | 
  
    | 90 | 
 | 
  
    | 91 |     eval "$(grep -o 'IMPORT.\+' "$1" | translate_imports || exit 1)"
 | 
  
    | 92 | }
 | 
  
    | 93 | 
 | 
  
    | 94 | compute_scripts_list_rec() {
 | 
  
    | 95 |     local FILE="$1"
 | 
  
    | 96 |     local FILEKEY=$(sanitize "$1")
 | 
  
    | 97 | 
 | 
  
    | 98 |     local FILESTATE="$(map_get FILESTATES $FILEKEY)"
 | 
  
    | 99 |     if [ "xprocessed" = "x$FILESTATE" ]; then
 | 
  
    | 100 | 	return
 | 
  
    | 101 |     fi
 | 
  
    | 102 |     if [ "xprocessing" = "x$FILESTATE" ]; then
 | 
  
    | 103 | 	errcho "import loop on $FILE"
 | 
  
    | 104 | 	return 1
 | 
  
    | 105 |     fi
 | 
  
    | 106 | 
 | 
  
    | 107 |     USED="$USED $FILEKEY"
 | 
  
    | 108 | 
 | 
  
    | 109 |     map_set FILESTATES $FILEKEY "processing"
 | 
  
    | 110 | 
 | 
  
    | 111 |     local IMPORT
 | 
  
    | 112 |     for IMPORT in $(map_get IMPORTS $FILEKEY); do
 | 
  
    | 113 | 	NEXT_FILE="$(map_get EXPORTS $IMPORT)"
 | 
  
    | 114 | 	if [ "x" = "x$NEXT_FILE" ]; then
 | 
  
    | 115 | 	    errcho "nothing exports $IMPORT, required by $FILE"
 | 
  
    | 116 | 	    return 1
 | 
  
    | 117 | 	fi
 | 
  
    | 118 | 	if ! compute_scripts_list_rec "$NEXT_FILE"; then
 | 
  
    | 119 | 	    errcho "when satisfying $IMPORT for $FILE"
 | 
  
    | 120 | 	    return 1
 | 
  
    | 121 | 	fi
 | 
  
    | 122 |     done
 | 
  
    | 123 | 
 | 
  
    | 124 |     [ "x$FILE" = "xexports_init.js" ] || echo $FILE # exports_init.js is hardcoded to load first; the entire export system depends on it
 | 
  
    | 125 |     map_set FILESTATES $FILEKEY "processed"
 | 
  
    | 126 | }
 | 
  
    | 127 | 
 | 
  
    | 128 | compute_scripts_list() {
 | 
  
    | 129 |     USED=''
 | 
  
    | 130 |     echo COMPUTED_SCRIPTS=\"exports_init.js
 | 
  
    | 131 |     compute_scripts_list_rec "$1"
 | 
  
    | 132 |     echo \"
 | 
  
    | 133 | 
 | 
  
    | 134 |     for FILEKEY in $USED; do
 | 
  
    | 135 | 	map_set_instr USED $FILEKEY yes
 | 
  
    | 136 |     done
 | 
  
    | 137 | }
 | 
  
    | 138 | 
 | 
  
    | 139 | as_json_list() {
 | 
  
    | 140 |     while true; do
 | 
  
    | 141 | 	if [ "x" = "x$2" ]; then
 | 
  
    | 142 | 	    echo -n '\\n'"\t\t\"$1\""'\\n\t'
 | 
  
    | 143 | 	    return
 | 
  
    | 144 | 	fi
 | 
  
    | 145 | 	echo -n '\\n'"\t\t\"$1\","
 | 
  
    | 146 | 	shift
 | 
  
    | 147 |     done
 | 
  
    | 148 | }
 | 
  
    | 149 | 
 | 
  
    | 150 | as_html_list() {
 | 
  
    | 151 |     while [ "x" != "x$1" ]; do
 | 
  
    | 152 | 	echo -n '\\n'"    <script src=\"/$1\"></script>"
 | 
  
    | 153 | 	shift
 | 
  
    | 154 |     done
 | 
  
    | 155 | }
 | 
  
    | 156 | 
 | 
  
    | 157 | set_browser() {
 | 
  
    | 158 |     if [ "x$1" = "xmozilla" -o "x$1" = "xchromium" ]; then
 | 
  
    | 159 | 	BROWSER="$1"
 | 
  
    | 160 |     else
 | 
  
    | 161 | 	errcho "usage:  $0 mozilla|chromium"
 | 
  
    | 162 | 	exit 1
 | 
  
    | 163 |     fi
 | 
  
    | 164 | }
 | 
  
    | 165 | 
 | 
  
    | 166 | main() {
 | 
  
    | 167 |     set_browser "$1"
 | 
  
    | 168 | 
 | 
  
    | 169 |     # placate importers of these, as they are exported by the yet-to-be-created exports_init.js
 | 
  
    | 170 |     EXPORTS__browser=exports_init.js
 | 
  
    | 171 |     EXPORTS__is_chrome=exports_init.js
 | 
  
    | 172 |     EXPORTS__is_mozilla=exports_init.js
 | 
  
    | 173 | 
 | 
  
    | 174 |     SCRIPTDIRS='background html common content'
 | 
  
    | 175 | 
 | 
  
    | 176 |     SCRIPTS=$(find $SCRIPTDIRS -name '[^.#]*.js')
 | 
  
    | 177 | 
 | 
  
    | 178 |     for SCRIPT in $SCRIPTS; do
 | 
  
    | 179 | 	add_exports $SCRIPT
 | 
  
    | 180 | 	add_imports $SCRIPT
 | 
  
    | 181 |     done
 | 
  
    | 182 | 
 | 
  
    | 183 |     eval "$(compute_scripts_list background/main.js || exit 1)"
 | 
  
    | 184 |     BGSCRIPTS="$(as_json_list $COMPUTED_SCRIPTS)"
 | 
  
    | 185 |     eval "$(compute_scripts_list content/main.js || exit 1)"
 | 
  
    | 186 |     CONTENTSCRIPTS="$(as_json_list $COMPUTED_SCRIPTS)"
 | 
  
    | 187 |     eval "$(compute_scripts_list html/display-panel.js || exit 1)"
 | 
  
    | 188 |     POPUPSCRIPTS="$(as_html_list $COMPUTED_SCRIPTS)"
 | 
  
    | 189 |     eval "$(compute_scripts_list html/options_main.js || exit 1)"
 | 
  
    | 190 |     OPTIONSSCRIPTS="$(as_html_list $COMPUTED_SCRIPTS)"
 | 
  
    | 191 | 
 | 
  
    | 192 |     BUILDDIR=build_$BROWSER
 | 
  
    | 193 |     rm -rf $BUILDDIR
 | 
  
    | 194 |     mkdir $BUILDDIR
 | 
  
    | 195 |     for DIR in $(find $SCRIPTDIRS -type d); do
 | 
  
    | 196 | 	mkdir -p $BUILDDIR/$DIR
 | 
  
    | 197 |     done
 | 
  
    | 198 | 
 | 
  
    | 199 |     CHROMIUM_KEY=''
 | 
  
    | 200 |     GECKO_APPLICATIONS=''
 | 
  
    | 201 | 
 | 
  
    | 202 |     if [ "$BROWSER" = "chromium" ]; then
 | 
  
    | 203 | 	CHROMIUM_KEY="$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64)"
 | 
  
    | 204 | 	echo "chromium key is" $CHROMIUM_KEY
 | 
  
    | 205 | 	CHROMIUM_KEY="chromium-key-dummy-file-$CHROMIUM_KEY"
 | 
  
    | 206 | 	CHROMIUM_KEY=$(echo $CHROMIUM_KEY | tr / -);
 | 
  
    | 207 | 	touch $BUILDDIR/$CHROMIUM_KEY
 | 
  
    | 208 | 
 | 
  
    | 209 | 	CHROMIUM_KEY="\n\
 | 
  
    | 210 | 	// WARNING!!!\n\
 | 
  
    | 211 | 	// EACH USER SHOULD REPLACE DUMMY FILE's VALUE WITH A UNIQUE ONE!!!\n\
 | 
  
    | 212 | 	// OTHERWISE, SECURITY CAN BE TRIVIALLY COMPROMISED!\n\
 | 
  
    | 213 | 	// Only relevant to users of chrome-based browsers.\n\
 | 
  
    | 214 | 	// Users of Firefox forks are safe.\n\
 | 
  
    | 215 | 	\"$CHROMIUM_KEY\"\
 | 
  
    | 216 | "
 | 
  
    | 217 |     else
 | 
  
    | 218 | 	GECKO_APPLICATIONS="\n\
 | 
  
    | 219 |     \"applications\": {\n\
 | 
  
    | 220 | 	\"gecko\": {\n\
 | 
  
    | 221 | 	    \"id\": \"{6fe13369-88e9-440f-b837-5012fb3bedec}\",\n\
 | 
  
    | 222 | 	    \"strict_min_version\": \"60.0\"\n\
 | 
  
    | 223 | 	}\n\
 | 
  
    | 224 |     },"
 | 
  
    | 225 |     fi
 | 
  
    | 226 | 
 | 
  
    | 227 |     sed "\
 | 
  
    | 228 | s^_GECKO_APPLICATIONS_^$GECKO_APPLICATIONS^
 | 
  
    | 229 | s^_CHROMIUM_KEY_^$CHROMIUM_KEY^
 | 
  
    | 230 | s^_BGSCRIPTS_^$BGSCRIPTS^
 | 
  
    | 231 | s^_CONTENTSCRIPTS_^$CONTENTSCRIPTS^" \
 | 
  
    | 232 | 	< manifest.json > $BUILDDIR/manifest.json
 | 
  
    | 233 | 
 | 
  
    | 234 |     ./process_html_file.sh html/display-panel.html |
 | 
  
    | 235 | 	sed "s^_POPUPSCRIPTS_^$POPUPSCRIPTS^" \
 | 
  
    | 236 | 	    > $BUILDDIR/html/display-panel.html
 | 
  
    | 237 | 
 | 
  
    | 238 |     ./process_html_file.sh html/options.html |
 | 
  
    | 239 | 	sed "s^_OPTIONSSCRIPTS_^$OPTIONSSCRIPTS^" \
 | 
  
    | 240 | 	    > $BUILDDIR/html/options.html
 | 
  
    | 241 | 
 | 
  
    | 242 |     for FILE in $SCRIPTS; do
 | 
  
    | 243 | 	FILEKEY=$(sanitize "$FILE")
 | 
  
    | 244 | 	if [ "xyes" != "x$(map_get USED $FILEKEY)" ]; then
 | 
  
    | 245 | 	    errcho "WARNING! $FILE not used"
 | 
  
    | 246 | 	else
 | 
  
    | 247 | 	    (echo "\
 | 
  
    | 248 | \"use strict\";
 | 
  
    | 249 | 
 | 
  
    | 250 | ({fun: (function() {
 | 
  
    | 251 | $(map_get IMPORTCODES $FILEKEY)
 | 
  
    | 252 | 
 | 
  
    | 253 | ";
 | 
  
    | 254 | 
 | 
  
    | 255 | # A hack to insert the contents of default_settings.json at the appropriate location in background/main.js
 | 
  
    | 256 | if [ "$FILE" = "background/main.js" ]; then
 | 
  
    | 257 |     # Uses an internal sed expression to escape and indent the JSON file for use in the external sed expression
 | 
  
    | 258 |     sed 's/^        `DEFAULT SETTINGS`$/'"$(sed -E 's/([\\\&\/])/\\\1/g; s/^/        /; s/$/\\/' < default_settings.json) "/g < "$FILE"
 | 
  
    | 259 | else
 | 
  
    | 260 |     cat $FILE
 | 
  
    | 261 | fi
 | 
  
    | 262 | 
 | 
  
    | 263 | echo "
 | 
  
    | 264 | 
 | 
  
    | 265 | $(map_get EXPORTCODES $FILEKEY)
 | 
  
    | 266 | })}).fun();") > $BUILDDIR/$FILE
 | 
  
    | 267 | 	fi
 | 
  
    | 268 |     done
 | 
  
    | 269 | 
 | 
  
    | 270 |     if [ "$BROWSER" = "chromium" ]; then
 | 
  
    | 271 | 	cat > $BUILDDIR/exports_init.js <<EOF
 | 
  
    | 272 | window.killtheweb={is_chrome: true, browser: window.chrome};
 | 
  
    | 273 | EOF
 | 
  
    | 274 |     else
 | 
  
    | 275 | 	cat > $BUILDDIR/exports_init.js <<EOF
 | 
  
    | 276 | /* Polyfill for IceCat 60. */
 | 
  
    | 277 | String.prototype.matchAll = String.prototype.matchAll || function(regex) {
 | 
  
    | 278 |     if (regex.flags.search("g") === -1)
 | 
  
    | 279 |         throw new TypeError("String.prototype.matchAll called with a non-global RegExp argument");
 | 
  
    | 280 | 
 | 
  
    | 281 |     for (const matches = [];;) {
 | 
  
    | 282 |         if (matches[matches.push(regex.exec(this)) - 1] === null)
 | 
  
    | 283 | 	    return matches.splice(0, matches.length - 1);
 | 
  
    | 284 |     }
 | 
  
    | 285 | }
 | 
  
    | 286 | 
 | 
  
    | 287 | window.killtheweb={is_mozilla: true, browser: this.browser};
 | 
  
    | 288 | EOF
 | 
  
    | 289 |     fi
 | 
  
    | 290 | 
 | 
  
    | 291 |     cp -r copyright licenses/ $BUILDDIR
 | 
  
    | 292 |     cp html/*.css $BUILDDIR/html
 | 
  
    | 293 |     mkdir $BUILDDIR/icons
 | 
  
    | 294 |     cp icons/*.png $BUILDDIR/icons
 | 
  
    | 295 | 
 | 
  
    | 296 |     if [ "$BROWSER" = "chromium" ]; then
 | 
  
    | 297 | 	for MOZILLA_FILE in $(find $BUILDDIR -name "MOZILLA_*"); do
 | 
  
    | 298 | 	    echo > "$MOZILLA_FILE"
 | 
  
    | 299 | 	done
 | 
  
    | 300 |     fi
 | 
  
    | 301 |     if [ "$BROWSER" = "mozilla" ]; then
 | 
  
    | 302 | 	for CHROMIUM_FILE in $(find $BUILDDIR -name "CHROMIUM_*"); do
 | 
  
    | 303 | 	    echo > "$CHROMIUM_FILE"
 | 
  
    | 304 | 	done
 | 
  
    | 305 |     fi
 | 
  
    | 306 | }
 | 
  
    | 307 | 
 | 
  
    | 308 | main "$@"
 |