...
 
Commits (6)
......@@ -34,7 +34,6 @@ libhttpseverywhere_version = '0.4.8'
glib = dependency('glib-2.0')
gobject = dependency('gobject-2.0')
json_glib = dependency('json-glib-1.0')
libxml = dependency('libxml-2.0')
soup = dependency('libsoup-2.4')
gio = dependency('gio-2.0')
......@@ -54,7 +53,7 @@ httpseverywhere_test_source = [
valagir = 'HTTPSEverywhere-' + api + '.gir'
httpseverywhere_lib = shared_library('httpseverywhere-'+api, httpseverywhere_lib_source,
dependencies: [glib, gobject, json_glib, libxml, soup, gio, gee, archive],
dependencies: [glib, gobject, libxml, soup, gio, gee, archive],
vala_gir: valagir,
vala_header: 'httpseverywhere.h',
install: true,
......@@ -107,5 +106,5 @@ httpseverywhere_vapi = meson.current_build_dir()+'/httpseverywhere-'+api+'.vapi'
run_command ('touch', httpseverywhere_vapi)
install_data(httpseverywhere_vapi, install_dir: get_option('datadir') + '/vala/vapi')
install_data('data/rulesets.json', install_dir: get_option('datadir') + '/libhttpseverywhere')
install_data('data/default.rulesets', install_dir: get_option('datadir') + '/libhttpseverywhere')
install_data('httpseverywhere-'+api+'.deps', install_dir: get_option('datadir') + '/vala/vapi')
......@@ -29,14 +29,14 @@ namespace HTTPSEverywhere {
NOT_IMPLEMENTED
}
private const string rulesets_file = "rulesets.json";
private const string rulesets_file = "default.rulesets";
/**
* The library context object. Most applications will only need to create a
* single context.
*/
public class Context : GLib.Object {
private Json.Parser parser;
private Xml.Doc* ruleset_xml;
private RewriteResult last_rewrite_state;
private Gee.HashMap<Target, Gee.ArrayList<uint>> targets;
......@@ -80,61 +80,77 @@ namespace HTTPSEverywhere {
* Create a new library context object.
*/
public Context() {
try {
new Thread<int>.try("rulesets", this.init);
} catch (Error e) {
warning("Could not initialize HTTPSEverywhere: %s", e.message);
}
}
/**
* Initialization finished
*
* This signal is being triggered when all ruesets have
* been loaded into memory and the context can subsequently
* be asked to perform queries on HTTPSEverywheres data.
*/
public signal void rdy(Error? e);
/**
* This function initializes HTTPSEverywhere by loading
* the rulesets from the filesystem.
*/
public async void init(Cancellable? cancellable = null) throws IOError {
initialized = false;
private int init() {
lock (this.initialized) {
IOError e;
try {
initialized = false;
targets = new Gee.HashMap<Target,Gee.ArrayList<uint>>();
rulesets = new Gee.HashMap<int, Ruleset>();
cache = new Gee.ArrayList<Target>();
targets = new Gee.HashMap<Target,Gee.ArrayList<uint>>();
rulesets = new Gee.HashMap<int, Ruleset>();
cache = new Gee.ArrayList<Target>();
ignore_list = new Gee.ArrayList<uint>();
ignore_list = new Gee.ArrayList<uint>();
var datapaths = new Gee.ArrayList<string>();
var datapaths = new Gee.ArrayList<string>();
// Specify the paths to search for rules in
datapaths.add(Path.build_filename(Environment.get_user_data_dir(),
"libhttpseverywhere", rulesets_file));
foreach (string dp in Environment.get_system_data_dirs())
datapaths.add(Path.build_filename(dp, "libhttpseverywhere", rulesets_file));
// Specify the paths to search for rules in
datapaths.add(Path.build_filename(Environment.get_user_data_dir(),
"libhttpseverywhere", rulesets_file));
foreach (string dp in Environment.get_system_data_dirs())
datapaths.add(Path.build_filename(dp, "libhttpseverywhere", rulesets_file));
// local rules in repo dir to test data without installation
datapaths.add(Path.build_filename(Environment.get_current_dir(), "..", "data", rulesets_file));
// local rules in repo dir to test data without installation
datapaths.add(Path.build_filename(Environment.get_current_dir(), "..", "data", rulesets_file));
parser = new Json.Parser();
bool success = false;
bool success = false;
foreach (string dp in datapaths) {
try {
File f = File.new_for_path(dp);
FileInputStream fis = yield f.read_async(Priority.DEFAULT, cancellable);
DataInputStream dis = new DataInputStream(fis);
yield parser.load_from_stream_async(dis, cancellable);
} catch (Error e) {
if (e is IOError.CANCELLED) {
throw (IOError) e;
foreach (string dp in datapaths) {
if (!FileUtils.test(dp, FileTest.EXISTS))
continue;
this.ruleset_xml = Xml.Parser.parse_file(dp);
if (this.ruleset_xml == null)
continue;
success = true;
break;
}
continue;
if (!success) {
string locations = "\n";
foreach (string location in datapaths)
locations += "%s\n".printf(location);
critical("Could not find any suitable database in the following locations:%s",
locations);
return 1;
}
this.load_rulesets();
initialized = true;
} catch (IOError e) {
GLib.Idle.add(()=>{this.rdy(e); return false;});
return 1;
}
success = true;
break;
}
if (!success) {
string locations = "\n";
foreach (string location in datapaths)
locations += "%s\n".printf(location);
critical("Could not find any suitable database in the following locations:%s",
locations);
return;
}
load_targets();
initialized = true;
GLib.Idle.add(()=>{this.rdy(null); return false;});
return 0;
}
/**
......@@ -169,8 +185,8 @@ namespace HTTPSEverywhere {
if (ruleset_id in this.ignore_list)
continue;
if (!rulesets.has_key(ruleset_id))
load_ruleset(ruleset_id);
/*if (!rulesets.has_key(ruleset_id))
load_ruleset(ruleset_id);*/
rs = rulesets.get(ruleset_id);
}
......@@ -185,8 +201,8 @@ namespace HTTPSEverywhere {
if (ruleset_id in this.ignore_list)
continue;
if (!rulesets.has_key(ruleset_id))
load_ruleset(ruleset_id);
/*if (!rulesets.has_key(ruleset_id))
load_ruleset(ruleset_id);*/
rs = rulesets.get(ruleset_id);
}
......@@ -255,81 +271,33 @@ namespace HTTPSEverywhere {
throw new ContextError.NOT_IMPLEMENTED("Context.unignore_host ist not implemented yet.");
}
/**
* Loads all possible targets into memory
*/
private void load_targets() {
Json.Node root = parser.get_root();
if (root.get_node_type() != Json.NodeType.OBJECT) {
error("Need an object as the rootnode of rulesets.");
}
var rootobj = root.get_object();
if (!rootobj.has_member("targets") ||
rootobj.get_member("targets").get_node_type() != Json.NodeType.OBJECT) {
error("The root object must have an object with the name 'targets'.");
}
rootobj.get_member("targets").get_object().foreach_member((obj, host, member) => {
if (member.get_node_type() != Json.NodeType.ARRAY) {
error("Targets must supply their ruleset IDs as arrays of integers.");
}
var id_list = new Gee.ArrayList<uint>();
member.get_array().foreach_element((arr,index,element) => {
if (element.get_node_type() != Json.NodeType.VALUE)
error ("RulesetIDs must be supplied as integer values");
id_list.add((uint)element.get_int());
});
targets.set(new Target(host), id_list);
});
}
/**
* Loads a ruleset from the database and stores it in the ram cache
*/
private void load_ruleset(uint ruleset_id) {
Json.Node root = parser.get_root();
if (root.get_node_type() != Json.NodeType.OBJECT) {
error("Need an object as the rootnode of rulesets.");
}
var rootobj = root.get_object();
if (!rootobj.has_member("rulesetStrings")) {
error("The root object must have an array with the name 'rulesetStrings'.");
}
Json.Node arrnode = rootobj.get_member("rulesetStrings");
if (arrnode.get_node_type() != Json.NodeType.ARRAY) {
error("rulesetStrings must be supplied as array of string");
}
var arr = arrnode.get_array();
parse_ruleset(ruleset_id,arr.get_string_element(ruleset_id));
}
/**
* Causes a new {@link HTTPSEverywhere.Ruleset} to be created from the
* file at rulepath and to be stored in this libs memory
*/
private void parse_ruleset(uint id, string ruledata) {
Xml.Doc* doc = Xml.Parser.parse_doc(ruledata);
if (doc == null) {
warning("Could not parse rule with id %u".printf(id));
return;
private void load_rulesets() {
Xml.Node* root = this.ruleset_xml->get_root_element();
if (root->name != "rulesetlibrary") {
error("The root element of a ruleset-library must be named 'rulesetlibrary'");
}
Xml.Node* root = doc->get_root_element();
if (root != null) {
uint id = 0;
Xml.Node* cn;
for (cn = root->children; cn != null; cn = cn->next) {
if (cn->type != Xml.ElementType.ELEMENT_NODE || cn->name != "ruleset")
continue;
try {
var rs = new Ruleset.from_xml(root);
rulesets.set(id, rs);
var rs = new Ruleset.from_xml(cn);
rulesets.@set(++id, rs);
foreach (Target target in rs.targets) {
if (this.targets.has_key(target)) {
this.targets.@get(target).add(id);
} else {
var id_list = new Gee.ArrayList<uint>();
id_list.add(id);
this.targets.@set(target, id_list);
}
}
} catch (RulesetError e) {
}
} else {
warning("No Root element in rule with id %u".printf(id));
}
delete doc;
delete cn;
delete root;
}
}
}
......@@ -189,7 +189,7 @@ namespace HTTPSEverywhere {
string json = "";
unowned Archive.Entry e = null;
while (zipreader.next_header(out e) == Archive.Result.OK) {
if (e != null && e.pathname() == "chrome/content/rulesets.json") {
if (e != null && e.pathname() == "webextension/rules/default.rulesets") {
uint8[] jsonblock = new uint8[1024*1024];
while (true) {
var r = zipreader.read_data(jsonblock, 1024*1024);
......
......@@ -42,9 +42,11 @@ namespace HTTPSEverywhereTest {
Test.add_func("/httpseverywhere/context/rewrite", () => {
var context = new Context();
var m = new MainLoop();
context.init.begin(null, (obj, res) => {
context.rdy.connect((e) => {
if (e != null) {
GLib.assert_not_reached();
}
try {
context.init.end(res);
var result = context.rewrite("http://example.com");
assert(result == "http://example.com/" || result == "https://example.com/");
assert(context.has_https("http://example.com") == result.has_prefix("https://"));
......@@ -56,26 +58,6 @@ namespace HTTPSEverywhereTest {
m.run();
});
Test.add_func("/httpseverywhere/context/cancel_init", () => {
var loop = new MainLoop();
var context = new Context();
var cancellable = new Cancellable();
context.init.begin(cancellable, (obj, res) => {
try {
context.init.end(res);
assert_not_reached();
} catch (Error e) {
assert(e is IOError.CANCELLED);
assert(cancellable.is_cancelled());
loop.quit();
}
});
cancellable.cancel();
loop.run();
});
Test.add_func("/httpseverywhere/context/rewrite_before_init", () => {
if (Test.subprocess()) {
/* Should emit a critical since init has not been called. */
......@@ -143,28 +125,6 @@ namespace HTTPSEverywhereTest {
url = "http://www.dl.ed.gov/";
assert (ruleset.rewrite(url) == "https://www.dl.ed.gov/");
});
Test.add_func("/httpseverywhere/context/ignore", () => {
var context = new Context();
var m = new MainLoop();
context.init.begin(null, (obj, res) => {
try {
context.init.end(res);
var result = context.rewrite("http://forums.lemonde.fr");
assert(result.has_prefix("https://"));
context.ignore_ruleset(9204);
result = context.rewrite("http://forums.lemonde.fr");
assert(result.has_prefix("http://"));
context.unignore_ruleset(9204);
result = context.rewrite("http://forums.lemonde.fr");
assert(result.has_prefix("https://"));
m.quit();
} catch (Error e) {
GLib.assert_not_reached();
}
});
m.run();
});
}
}
}