1# Copyright (c) 2017-2021 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
2# This program is free software: you can redistribute it and/or modify
3# it under the terms of the GNU General Public License as published by
4# the Free Software Foundation, either version 3 of the License, or
5# (at your option) any later version.
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9# GNU General Public License for more details.
10# You should have received a copy of the GNU General Public License
11# along with this program. If not, see <http://www.gnu.org/licenses/>.
12
13from gi.repository import Gio
14
15import json
16
17from eolie.define import EOLIE_DATA_PATH
18from eolie.logger import Logger
19
20
21class ContentBlockerExceptions:
22    """
23        Exception handler
24    """
25    __JSON_PATH = "%s/content_blocker_json" % EOLIE_DATA_PATH
26
27    def __init__(self, name):
28        """
29            Init constructor
30        """
31        try:
32            self.__name = name
33            self.__rules = []
34            f = Gio.File.new_for_path(
35                "%s/exceptions_%s.json" % (self.__JSON_PATH, self.__name))
36            if f.query_exists():
37                (status, contents, tag) = f.load_contents(None)
38                if status:
39                    self.__rules = json.loads(contents.decode("utf-8"))
40        except Exception as e:
41            Logger.error("AdblockExceptions::__init__(): %s", e)
42
43    def save(self):
44        """
45            Save rules to disk
46        """
47        try:
48            f = Gio.File.new_for_path(
49                "%s/exceptions_%s.json" % (self.__JSON_PATH, self.__name))
50            content = json.dumps(self.__rules)
51            f.replace_contents(content.encode("utf-8"),
52                               None,
53                               False,
54                               Gio.FileCreateFlags.REPLACE_DESTINATION,
55                               None)
56        except Exception as e:
57            Logger.error("AdblockExceptions::save(): %s", e)
58
59    def add_domain_exception(self, domain, url_filter=".*", internal=False):
60        """
61            Add an exception for domain
62            @param domain as str
63            @param url_filter as str
64            @param internal as bool
65        """
66        if internal:
67            rule = self.__get_rule_for_internal_domain(domain, url_filter)
68            if rule in self.__rules:
69                self.__rules.remove(rule)
70        else:
71            rule = self.__get_rule_for_domain(domain, url_filter)
72            self.__rules.append(rule)
73
74    def remove_domain_exception(self, domain, url_filter=".*", internal=False):
75        """
76            Remove an exception for domain
77            @param domain as str
78            @param url_filter as str
79            @param internal as bool
80        """
81        if internal:
82            rule = self.__get_rule_for_internal_domain(domain, url_filter)
83            self.__rules.append(rule)
84        else:
85            rule = self.__get_rule_for_domain(domain, url_filter)
86            if rule in self.__rules:
87                self.__rules.remove(rule)
88
89    def remove_all_domain_exceptions(self, domain):
90        """
91            Remove all exceptions for a domain
92            @param domain as str
93        """
94        for rule in list(self.__rules):
95            if rule["trigger"]["if-domain"] == ["*%s" % domain]:
96                self.remove_domain_exception(
97                    domain, rule["trigger"]["url-filter"])
98
99    def is_domain_exception(self, domain, url_filter=".*", internal=False):
100        """
101            True if domain exception exists
102            @parma domain as str
103            @param url_filter as str
104            @param internal as bool
105            @return bool
106        """
107        if internal:
108            rule = self.__get_rule_for_internal_domain(domain, url_filter)
109            return rule not in self.__rules
110        else:
111            rule = self.__get_rule_for_domain(domain, url_filter)
112            return rule in self.__rules
113
114    @property
115    def rules(self):
116        """
117            Get rules
118            @return []
119        """
120        return self.__rules
121
122#######################
123# PRIVATE             #
124#######################
125    def __get_rule_for_domain(self, domain, url_filter):
126        """
127            Return rule for domain
128            @param domain as str
129            @param url_filter as str
130            @return {}
131        """
132        value = "*%s" % domain
133        return {
134            "trigger": {
135                "url-filter": url_filter,
136                "if-domain": [value]
137            },
138            "action": {
139                "type": "ignore-previous-rules"
140            }
141        }
142
143    def __get_rule_for_internal_domain(self, domain, url_filter):
144        """
145            Return rule for domain
146            @param domain as str
147            @param url_filter as str
148            @return {}
149        """
150        value = "*%s" % domain
151        return {
152            "trigger": {
153                "url-filter": url_filter,
154                "if-domain": [value]
155            },
156            "action": {
157                "type": "block"
158            }
159        }
160