1/*
2 Copyright (C) 2009-2018 Christian Dywan <christian@twotoats.de>
3 Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 See the file COPYING for the full license text.
11*/
12
13namespace Adblock {
14    public class Button : Gtk.Button {
15        public string icon_name { get; protected set; }
16        Settings settings = Settings.get_default ();
17
18        construct {
19            action_name = "win.adblock-status";
20            tooltip_text = _("Advertisement blocker");
21            var image = new Gtk.Image.from_icon_name (icon_name, Gtk.IconSize.BUTTON);
22            bind_property ("icon-name", image, "icon-name");
23            image.use_fallback = true;
24            image.show ();
25            add (image);
26            settings.notify["enabled"].connect (update_icon);
27            update_icon ();
28            show ();
29        }
30
31        void update_icon () {
32            icon_name = "security-%s-symbolic".printf (settings.enabled ? "high" : "low");
33        }
34
35        public Button (Midori.Browser browser) {
36            var action = new SimpleAction ("adblock-status", null);
37            action.activate.connect (() => {
38                settings.enabled = !settings.enabled;
39                browser.tab.reload ();
40            });
41            browser.notify["uri"].connect (() => {
42                action.set_enabled (browser.uri.has_prefix ("http"));
43            });
44            browser.add_action (action);
45            browser.application.set_accels_for_action ("win.adblock-status", { });
46        }
47    }
48
49    public class Frontend : Object, Midori.BrowserActivatable {
50        public Midori.Browser browser { owned get; set; }
51
52        public void activate () {
53            var button = new Button (browser);
54            browser.add_button (button);
55            deactivate.connect (() => {
56                button.destroy ();
57            });
58
59            browser.web_context.register_uri_scheme ("abp", (request) => {
60                if (request.get_uri ().has_prefix ("abp:subscribe?location=")) {
61                    // abp://subscripe?location=http://example.com&title=foo
62                    var sub = new Subscription (request.get_uri ().substring (23, -1));
63                    debug ("Adding %s to filters\n", sub.uri);
64                    Settings.get_default ().add (sub);
65                    sub.active = true;
66                    request.get_web_view ().stop_loading ();
67                } else {
68                    request.finish_error (new FileError.NOENT (_("Invalid URI")));
69                }
70            });
71        }
72    }
73
74    public class RequestFilter : Peas.ExtensionBase, Peas.Activatable {
75        public Object object { owned get; construct; }
76
77        public void activate () {
78            string? page_uri;
79            object.get ("uri", out page_uri);
80            object.connect ("signal::send-request", handle_request, page_uri);
81        }
82
83        bool handle_request (Object request, Object? response, string? page_uri) {
84            string? uri;
85            request.get ("uri", out uri);
86            if (page_uri == null) {
87                // Note: 'this' is the WebKit.WebPage object in this context
88                get ("uri", out page_uri);
89            }
90            return get_directive_for_uri (uri, page_uri) == Directive.BLOCK;
91        }
92
93        Directive get_directive_for_uri (string request_uri, string page_uri) {
94            var settings = Settings.get_default ();
95
96            if (!settings.enabled)
97                return Directive.ALLOW;
98
99            // Always allow the main page
100            if (request_uri == page_uri)
101                return Directive.ALLOW;
102
103            // No adblock on non-http(s) schemes
104            if (!request_uri.has_prefix ("http"))
105                return Directive.ALLOW;
106
107            Directive? directive = null;
108            foreach (var sub in settings) {
109                directive = sub.get_directive (request_uri, page_uri);
110                if (directive != null)
111                    break;
112            }
113
114            if (directive == null) {
115                directive = Directive.ALLOW;
116            }
117            return directive;
118        }
119
120        public void deactivate () {
121        }
122
123        public void update_state () {
124        }
125    }
126
127    public class Preferences : Object, Midori.PreferencesActivatable {
128        public Midori.Preferences preferences { owned get; set; }
129
130        public void activate () {
131            var box = new Midori.LabelWidget (_("Configure Advertisement filters"));
132            var listbox = new Gtk.ListBox ();
133            listbox.selection_mode = Gtk.SelectionMode.NONE;
134            var settings = Settings.get_default ();
135            foreach (var sub in settings) {
136                var row = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4);
137                var button = new Gtk.CheckButton.with_label (sub.title);
138                button.tooltip_text = sub.uri;
139                sub.bind_property ("active", button, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
140                row.pack_start (button);
141                if (!settings.default_filters.contains (sub.uri.split ("&")[0])) {
142                    var remove = new Gtk.Button.from_icon_name ("list-remove-symbolic");
143                    remove.relief = Gtk.ReliefStyle.NONE;
144                    remove.clicked.connect (() => {
145                        settings.remove (sub);
146                        row.destroy ();
147                    });
148                    row.pack_end (remove, false);
149                }
150                listbox.insert (row, -1);
151            }
152
153            // Provide guidance to adding additional filters via the abp: scheme
154            var label = new Gtk.Label (
155                _("You can find more lists by visiting following sites:\n %s, %s\n").printf (
156                "<a href=\"https://adblockplus.org/en/subscriptions\">AdblockPlus</a>",
157                "<a href=\"https://easylist.to\">EasyList</a>"));
158            label.use_markup = true;
159            label.activate_link.connect ((uri) => {
160                var files = new File[1];
161                files[0] = File.new_for_uri (uri);
162                Application.get_default ().open (files, "");
163                return true;
164            });
165            listbox.insert (label, -1);
166
167            box.add (listbox);
168            box.show_all ();
169            preferences.add (_("Privacy"), box);
170            deactivate.connect (() => {
171                box.destroy ();
172            });
173        }
174    }
175}
176
177[ModuleInit]
178public void peas_register_types(TypeModule module) {
179    ((Peas.ObjectModule)module).register_extension_type (
180        typeof (Midori.BrowserActivatable), typeof (Adblock.Frontend));
181    ((Peas.ObjectModule)module).register_extension_type (
182        typeof (Peas.Activatable), typeof (Adblock.RequestFilter));
183    ((Peas.ObjectModule)module).register_extension_type (
184        typeof (Midori.PreferencesActivatable), typeof (Adblock.Preferences));
185}
186