Project

General

Profile

Download (5.12 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / background / page_actions_server.js @ 263d03d5

1
/**
2
 * This file is part of Haketilo.
3
 *
4
 * Function: Serving page actions to content scripts.
5
 *
6
 * Copyright (C) 2021 Wojtek Kosior
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * As additional permission under GNU GPL version 3 section 7, you
19
 * may distribute forms of that code without the copy of the GNU
20
 * GPL normally required by section 4, provided you include this
21
 * license notice and, in case of non-source distribution, a URL
22
 * through which recipients can access the Corresponding Source.
23
 * If you modify file(s) with this exception, you may extend this
24
 * exception to your version of the file(s), but you are not
25
 * obligated to do so. If you do not wish to do so, delete this
26
 * exception statement from your version.
27
 *
28
 * As a special exception to the GPL, any HTML file which merely
29
 * makes function calls to this code, and for that purpose
30
 * includes it by reference shall be deemed a separate work for
31
 * copyright law purposes. If you modify this code, you may extend
32
 * this exception to your version of the code, but you are not
33
 * obligated to do so. If you do not wish to do so, delete this
34
 * exception statement from your version.
35
 *
36
 * You should have received a copy of the GNU General Public License
37
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
38
 *
39
 * I, Wojtek Kosior, thereby promise not to sue for violation of this file's
40
 * license. Although I request that you do not make use this code in a
41
 * proprietary program, I am not going to enforce this in court.
42
 */
43

    
44
/*
45
 * IMPORTS_START
46
 * IMPORT get_storage
47
 * IMPORT light_storage
48
 * IMPORT TYPE_PREFIX
49
 * IMPORT CONNECTION_TYPE
50
 * IMPORT browser
51
 * IMPORT listen_for_connection
52
 * IMPORT sha256
53
 * IMPORT query_best
54
 * IMPORT make_ajax_request
55
 * IMPORTS_END
56
 */
57

    
58
var storage;
59
var handler;
60
let policy_observable;
61

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

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

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

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

    
75
    const components = settings.components;
76
    const processed_bags = new Set();
77

    
78
    if (components !== undefined)
79
	send_scripts([components], port, processed_bags);
80
}
81

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

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

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

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

    
102
	    processed_bags.delete(name);
103
	} else {
104
	    let script_text = await get_script_text(name);
105
	    if (script_text === undefined)
106
		continue;
107

    
108
	    port.postMessage(["inject", [script_text]]);
109
	}
110
    }
111
}
112

    
113
async function get_script_text(script_name)
114
{
115
    try {
116
	let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name);
117
	if (script_data === undefined) {
118
	    console.log(`missing data for ${script_name}`);
119
	    return;
120
	}
121
	let script_text = script_data.text;
122
	if (!script_text)
123
	    script_text = await fetch_remote_script(script_data);
124
	return script_text;
125
    } catch (e) {
126
	console.log(e);
127
    }
128
}
129

    
130
async function fetch_remote_script(script_data)
131
{
132
    try {
133
	let xhttp = await make_ajax_request("GET", script_data.url);
134
	if (xhttp.status === 200) {
135
	    let computed_hash = sha256(xhttp.responseText);
136
	    if (computed_hash !== script_data.hash) {
137
		console.log(`Bad hash for ${script_data.url}\n    got ${computed_hash} instead of ${script_data.hash}`);
138
		return;
139
	    }
140
	    return xhttp.responseText;
141
	} else {
142
	    console.log("script not fetched: " + script_data.url);
143
	    return;
144
	}
145
    } catch (e) {
146
	console.log(e);
147
    }
148
}
149

    
150
function handle_message(port, message, handler)
151
{
152
    port.onMessage.removeListener(handler[0]);
153
    let url = message.url;
154
    console.log({url});
155
    send_actions(url, port);
156
}
157

    
158
function new_connection(port)
159
{
160
    console.log("new page actions connection!");
161
    let handler = [];
162
    handler.push(m => handle_message(port, m, handler));
163
    port.onMessage.addListener(handler[0]);
164
}
165

    
166
async function start_page_actions_server()
167
{
168
    storage = await get_storage();
169

    
170
    listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);
171

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

    
175
/*
176
 * EXPORTS_START
177
 * EXPORT start_page_actions_server
178
 * EXPORTS_END
179
 */
(3-3/7)