aboutsummaryrefslogtreecommitdiff
path: root/background/page_actions_server.js
blob: 156a79fffbecb3ac18aef065e6b9632c1998dfcb (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/**
 * This file is part of Haketilo.
 *
 * Function: Serving page actions to content scripts.
 *
 * Copyright (C) 2021 Wojtek Kosior
 * Redistribution terms are gathered in the `copyright' file.
 */

/*
 * IMPORTS_START
 * IMPORT get_storage
 * IMPORT light_storage
 * IMPORT TYPE_PREFIX
 * IMPORT CONNECTION_TYPE
 * IMPORT browser
 * IMPORT listen_for_connection
 * IMPORT sha256
 * IMPORT query_best
 * IMPORT make_ajax_request
 * IMPORTS_END
 */

var storage;
var handler;
let policy_observable;

function send_actions(url, port)
{
    const [pattern, queried_settings] = query_best(storage, url);

    const settings = {allow: policy_observable && policy_observable.value};
    Object.assign(settings, queried_settings);
    if (settings.components)
	settings.allow = false;

    const repos = storage.get_all(TYPE_PREFIX.REPO);

    port.postMessage(["settings", [pattern, settings, repos]]);

    const components = settings.components;
    const processed_bags = new Set();

    if (components !== undefined)
	send_scripts([components], port, processed_bags);
}

// TODO: parallelize script fetching
async function send_scripts(components, port, processed_bags)
{
    for (let [prefix, name] of components) {
	if (prefix === TYPE_PREFIX.BAG) {
	    if (processed_bags.has(name)) {
		console.log(`preventing recursive inclusion of bag ${name}`);
		continue;
	    }

	    var bag = storage.get(TYPE_PREFIX.BAG, name);

	    if (bag === undefined) {
		console.log(`no bag in storage for key ${name}`);
		continue;
	    }

	    processed_bags.add(name);
	    await send_scripts(bag, port, processed_bags);

	    processed_bags.delete(name);
	} else {
	    let script_text = await get_script_text(name);
	    if (script_text === undefined)
		continue;

	    port.postMessage(["inject", [script_text]]);
	}
    }
}

async function get_script_text(script_name)
{
    try {
	let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name);
	if (script_data === undefined) {
	    console.log(`missing data for ${script_name}`);
	    return;
	}
	let script_text = script_data.text;
	if (!script_text)
	    script_text = await fetch_remote_script(script_data);
	return script_text;
    } catch (e) {
	console.log(e);
    }
}

async function fetch_remote_script(script_data)
{
    try {
	let xhttp = await make_ajax_request("GET", script_data.url);
	if (xhttp.status === 200) {
	    let computed_hash = sha256(xhttp.responseText);
	    if (computed_hash !== script_data.hash) {
		console.log(`Bad hash for ${script_data.url}\n    got ${computed_hash} instead of ${script_data.hash}`);
		return;
	    }
	    return xhttp.responseText;
	} else {
	    console.log("script not fetched: " + script_data.url);
	    return;
	}
    } catch (e) {
	console.log(e);
    }
}

function handle_message(port, message, handler)
{
    port.onMessage.removeListener(handler[0]);
    let url = message.url;
    console.log({url});
    send_actions(url, port);
}

function new_connection(port)
{
    console.log("new page actions connection!");
    let handler = [];
    handler.push(m => handle_message(port, m, handler));
    port.onMessage.addListener(handler[0]);
}

async function start_page_actions_server()
{
    storage = await get_storage();

    listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);

    policy_observable = await light_storage.observe_var("default_allow");
}

/*
 * EXPORTS_START
 * EXPORT start_page_actions_server
 * EXPORTS_END
 */