Status: | Merged |
---|---|
Approved by: | Paweł Forysiuk |
Approved revision: | 6325 |
Merged at revision: | 6558 |
Proposed branch: | lp:~midori/midori/wk2ab |
Merge into: | lp:midori |
Diff against target: |
3520 lines (+1443/-1934) 18 files modified
data/adblock.list (+54/-0) extensions/CMakeLists.txt (+18/-2) extensions/adblock.c (+0/-1930) extensions/adblock/config.vala (+107/-0) extensions/adblock/element.vala (+36/-0) extensions/adblock/extension.vala (+545/-0) extensions/adblock/filter.vala (+52/-0) extensions/adblock/keys.vala (+47/-0) extensions/adblock/options.vala (+32/-0) extensions/adblock/pattern.vala (+26/-0) extensions/adblock/subscriptions.vala (+328/-0) extensions/adblock/updater.vala (+139/-0) extensions/adblock/whitelist.vala (+30/-0) katze/midori-paths.vala (+1/-0) midori/midori-app.c (+1/-1) midori/midori.vapi (+1/-0) midori/webkit2gtk-3.0.vapi (+16/-0) po/POTFILES.in (+10/-1) |
To merge this branch: | bzr merge lp:~midori/midori/wk2ab |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paweł Forysiuk | Approve | ||
Review via email: mp+178349@code.launchpad.net |
Commit message
Rewrite Adblock more modularly, add Whitelist support
Description of the change
TODO:
- Expose filter metadata to user (name, last updated, time to next update)
- Check checksum hash of the subscription after downloading
- Remove subscriptions
- Open local files in editor
- Ability to disable adblock for site/for all sites via icon
- Save pre-parsed pre filtered ruleset into file as .preparsed
- Save matched/ cached uris from previous runs
[https:/
enabled=1
expires=
retries=1
homepage=https:/
title=EasyList
Test cases https:/
To post a comment you must log in.
lp:~midori/midori/wk2ab
updated
- 6325. By Cris Dywan
-
Rename adblock2 to adblock
Revision history for this message
Paweł Forysiuk (tuxator) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'data/adblock.list' |
2 | --- data/adblock.list 1970-01-01 00:00:00 +0000 |
3 | +++ data/adblock.list 2014-02-20 20:31:17 +0000 |
4 | @@ -0,0 +1,54 @@ |
5 | +[Adblock Plus 2.0] |
6 | +! Version: 201402142200 |
7 | +! Title: Exercise |
8 | +! Last modified: 11 Feb 2014 22:00 UTC |
9 | +! Expires: 3 days (update frequency) |
10 | +! Homepage: http://www.midori-browser.org |
11 | +! Licence: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt |
12 | +! Copyright (C) 2014 Christian Dywan <christian@twotoasts.de> |
13 | +! |
14 | +! Yadayada http://example.com/ e-mail (somebody@example.com). |
15 | +! |
16 | +!-----Spam eggs--------! |
17 | +! *** la:le/lu_foo_bar.txt *** |
18 | +|/http://ads.blub.boing/*$domain=xxx.com |
19 | +|/http://ads.blub.boing/*$domain=xxx.com,foo.fr,coco.at |
20 | +/market.php?$domain=adf.ly|foo.com |
21 | +/?placement=$script,domain=putlocker.com|sockshare.com |
22 | + |
23 | +! Some basic filters |
24 | +*ads.foo.bar* |
25 | +*ads.bogus.name* |
26 | +||^http://ads.bla.blub/* |
27 | +engine.adct.ru/*? |
28 | +/addyn|*|adtech; |
29 | +doubleclick.net/pfadx/*.mtvi |
30 | +objects.tremormedia.com/embed/xml/*.xml?r= |
31 | +videostrip.com^*/admatcherclient. |
32 | +test.dom/test?var |
33 | +/adpage. |
34 | +br.gcl.ru/cgi-bin/br/ |
35 | +_300x600. |
36 | +_rectangle_ads. |
37 | ++adverts/ |
38 | +-2/ads/ |
39 | + |
40 | +! CSS elements |
41 | +old.tv,delicio.us,longc.at###box |
42 | +##.zRightAdNote |
43 | +###advertisingModule160x600 |
44 | +##a[href$="/vghd.shtml"] |
45 | +imagetwist.com###left[align="center"] > center > a[target="_blank"] |
46 | + |
47 | +! Options |
48 | +||videobox.com/?tid=$popup |
49 | +||sexsearchcom.com^$popup,third-party |
50 | +||206.217.206.137^$third-party |
51 | +/spopunder^$popup |
52 | +||putlocker.com^*.php?*title$subdocument |
53 | +://ads.$popup |
54 | + |
55 | +! Whitelist |
56 | +@@||hortifor.com/images/*120x60$~third-party |
57 | +@@||stickam.com/wb/www/category/300x250/$image |
58 | +@@||adultadworld.com/adhandler/$subdocument |
59 | |
60 | === modified file 'extensions/CMakeLists.txt' |
61 | --- extensions/CMakeLists.txt 2013-09-08 19:56:46 +0000 |
62 | +++ extensions/CMakeLists.txt 2014-02-20 20:31:17 +0000 |
63 | @@ -15,7 +15,6 @@ |
64 | file(GLOB EXTENSIONS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *) |
65 | if (HALF_BRO_INCOM_WEBKIT2) |
66 | list(REMOVE_ITEM EXTENSIONS |
67 | - "adblock.c" |
68 | "cookie-permissions" |
69 | "addons.c" |
70 | "formhistory" |
71 | @@ -46,13 +45,30 @@ |
72 | string(FIND ${UNIT_SRC} "." UNIT_EXTENSION) |
73 | if (UNIT_EXTENSION EQUAL -1) |
74 | file(GLOB UNIT_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${UNIT_SRC}/*.c") |
75 | + file(GLOB UNIT_FILES_VALA RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${UNIT_SRC}/*.vala") |
76 | + if (UNIT_FILES_VALA) |
77 | + include(ValaPrecompile) |
78 | + vala_precompile(UNIT_SRC_C ${UNIT_SRC} |
79 | + ${UNIT_FILES_VALA} |
80 | + PACKAGES |
81 | + ${PKGS} |
82 | + OPTIONS |
83 | + ${VALAFLAGS} |
84 | + CUSTOM_VAPIS |
85 | + ${EXTRA_VAPIS} |
86 | + "${CMAKE_SOURCE_DIR}/midori/midori.vapi" |
87 | + "${CMAKE_SOURCE_DIR}/katze/katze.vapi" |
88 | + "${CMAKE_BINARY_DIR}/midori/${LIBMIDORI}.vapi" |
89 | + ) |
90 | + set(UNIT_FILES ${UNIT_FILES} ${UNIT_SRC_C}) |
91 | + endif () |
92 | if (UNIT_FILES) |
93 | add_library(${UNIT_SRC} MODULE ${UNIT_FILES}) |
94 | target_link_libraries(${UNIT_SRC} |
95 | ${LIBMIDORI} |
96 | ) |
97 | set_target_properties(${UNIT_SRC} PROPERTIES |
98 | - COMPILE_FLAGS ${CFLAGS} |
99 | + COMPILE_FLAGS ${VALA_CFLAGS} |
100 | ) |
101 | install(TARGETS ${UNIT_SRC} |
102 | LIBRARY DESTINATION ${EXTENSIONDIR} |
103 | |
104 | === added directory 'extensions/adblock' |
105 | === removed file 'extensions/adblock.c' |
106 | --- extensions/adblock.c 2013-11-09 17:56:58 +0000 |
107 | +++ extensions/adblock.c 1970-01-01 00:00:00 +0000 |
108 | @@ -1,1930 +0,0 @@ |
109 | -/* |
110 | - Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de> |
111 | - Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com> |
112 | - |
113 | - This library is free software; you can redistribute it and/or |
114 | - modify it under the terms of the GNU Lesser General Public |
115 | - License as published by the Free Software Foundation; either |
116 | - version 2.1 of the License, or (at your option) any later version. |
117 | - |
118 | - See the file COPYING for the full license text. |
119 | -*/ |
120 | -#include <midori/midori.h> |
121 | -#include "midori-core.h" |
122 | -#include <glib/gstdio.h> |
123 | - |
124 | -#include "config.h" |
125 | -#if HAVE_UNISTD_H |
126 | - #include <unistd.h> |
127 | -#endif |
128 | - |
129 | -#define SIGNATURE_SIZE 8 |
130 | -#define USE_PATTERN_MATCHING 1 |
131 | -#define CUSTOM_LIST_NAME "custom.list" |
132 | -#define ADBLOCK_FILTER_VALID(__filter) \ |
133 | - (__filter && (g_str_has_prefix (__filter, "http") \ |
134 | - || g_str_has_prefix (__filter, "file"))) |
135 | -#define ADBLOCK_FILTER_SET(__filter,__active) \ |
136 | - __filter[4] = __active ? (__filter[5] == ':' ? 's' : ':') : '-' |
137 | -#define ADBLOCK_FILTER_IS_SET(__filter) \ |
138 | - (__filter[4] != '-' && __filter[5] != '-') |
139 | - #define adblock_debug(dmsg, darg1, darg2) \ |
140 | - do { if (midori_debug ("adblock:match")) g_debug (dmsg, darg1, darg2); } while (0) |
141 | - |
142 | -static GHashTable* pattern = NULL; |
143 | -static GHashTable* keys = NULL; |
144 | -static GHashTable* optslist = NULL; |
145 | -static GHashTable* urlcache = NULL; |
146 | -static GHashTable* blockcssprivate = NULL; |
147 | -static GHashTable* navigationwhitelist = NULL; |
148 | -static GString* blockcss = NULL; |
149 | -static GList* update_list = NULL; |
150 | -static gboolean update_done = FALSE; |
151 | - |
152 | -static void |
153 | -adblock_parse_file (gchar* path); |
154 | - |
155 | -static gboolean |
156 | -adblock_file_is_up_to_date (gchar* path); |
157 | - |
158 | -static void |
159 | -adblock_reload_rules (MidoriExtension* extension, |
160 | - gboolean custom_only); |
161 | - |
162 | -static gchar* |
163 | -adblock_build_js (const gchar* uri) |
164 | -{ |
165 | - gchar* domain; |
166 | - const gchar* style; |
167 | - GString* subdomain; |
168 | - GString* code; |
169 | - int cnt = 0, blockscnt = 0; |
170 | - gchar** subdomains; |
171 | - |
172 | - domain = midori_uri_parse_hostname (uri, NULL); |
173 | - subdomains = g_strsplit (domain, ".", -1); |
174 | - g_free (domain); |
175 | - if (!subdomains) |
176 | - return NULL; |
177 | - |
178 | - code = g_string_new ( |
179 | - "window.addEventListener ('DOMContentLoaded'," |
180 | - "function () {" |
181 | - " if (document.getElementById('madblock'))" |
182 | - " return;" |
183 | - " public = '"); |
184 | - |
185 | - cnt = g_strv_length (subdomains) - 1; |
186 | - subdomain = g_string_new (subdomains [cnt]); |
187 | - g_string_prepend_c (subdomain, '.'); |
188 | - cnt--; |
189 | - while (cnt >= 0) |
190 | - { |
191 | - g_string_prepend (subdomain, subdomains[cnt]); |
192 | - if ((style = g_hash_table_lookup (blockcssprivate, subdomain->str))) |
193 | - { |
194 | - g_string_append (code, style); |
195 | - g_string_append_c (code, ','); |
196 | - blockscnt++; |
197 | - } |
198 | - g_string_prepend_c (subdomain, '.'); |
199 | - cnt--; |
200 | - } |
201 | - g_string_free (subdomain, TRUE); |
202 | - g_strfreev (subdomains); |
203 | - |
204 | - if (blockscnt == 0) |
205 | - return g_string_free (code, TRUE); |
206 | - |
207 | - g_string_append (code, |
208 | - " zz-non-existent {display: none !important}';" |
209 | - " var mystyle = document.createElement('style');" |
210 | - " mystyle.setAttribute('type', 'text/css');" |
211 | - " mystyle.setAttribute('id', 'madblock');" |
212 | - " mystyle.appendChild(document.createTextNode(public));" |
213 | - " var head = document.getElementsByTagName('head')[0];" |
214 | - " if (head) head.appendChild(mystyle);" |
215 | - "}, true);"); |
216 | - return g_string_free (code, FALSE); |
217 | -} |
218 | - |
219 | -static GString* |
220 | -adblock_fixup_regexp (const gchar* prefix, |
221 | - gchar* src); |
222 | - |
223 | -static void |
224 | -adblock_destroy_db () |
225 | -{ |
226 | - g_string_free (blockcss, TRUE); |
227 | - blockcss = NULL; |
228 | - |
229 | - g_hash_table_destroy (pattern); |
230 | - pattern = NULL; |
231 | - g_hash_table_destroy (optslist); |
232 | - optslist = NULL; |
233 | - g_hash_table_destroy (urlcache); |
234 | - urlcache = NULL; |
235 | - g_hash_table_destroy (blockcssprivate); |
236 | - blockcssprivate = NULL; |
237 | - g_hash_table_destroy (navigationwhitelist); |
238 | - navigationwhitelist = NULL; |
239 | -} |
240 | - |
241 | -static void |
242 | -adblock_init_db () |
243 | -{ |
244 | - pattern = g_hash_table_new_full (g_str_hash, g_str_equal, |
245 | - (GDestroyNotify)g_free, |
246 | - (GDestroyNotify)g_regex_unref); |
247 | - keys = g_hash_table_new_full (g_str_hash, g_str_equal, |
248 | - (GDestroyNotify)g_free, |
249 | - (GDestroyNotify)g_regex_unref); |
250 | - optslist = g_hash_table_new_full (g_str_hash, g_str_equal, |
251 | - NULL, |
252 | - (GDestroyNotify)g_free); |
253 | - urlcache = g_hash_table_new_full (g_str_hash, g_str_equal, |
254 | - (GDestroyNotify)g_free, |
255 | - (GDestroyNotify)g_free); |
256 | - blockcssprivate = g_hash_table_new_full (g_str_hash, g_str_equal, |
257 | - (GDestroyNotify)g_free, |
258 | - (GDestroyNotify)g_free); |
259 | - navigationwhitelist = g_hash_table_new_full (g_direct_hash, g_str_equal, |
260 | - NULL, |
261 | - (GDestroyNotify)g_free); |
262 | - |
263 | - blockcss = g_string_new ("z-non-exist"); |
264 | -} |
265 | - |
266 | -static void |
267 | -adblock_download_notify_status_cb (WebKitDownload* download, |
268 | - GParamSpec* pspec, |
269 | - MidoriExtension* extension) |
270 | -{ |
271 | - if (update_done) |
272 | - return; |
273 | - |
274 | - if (webkit_download_get_status (download) == WEBKIT_DOWNLOAD_STATUS_FINISHED) |
275 | - { |
276 | - GList* li = NULL; |
277 | - for (li = update_list; li != NULL; li = g_list_next (li)) |
278 | - { |
279 | - gchar* uri = g_strdup (webkit_download_get_destination_uri (download) + 7); |
280 | - if (g_strcmp0 (li->data, uri)) |
281 | - update_list = g_list_remove (update_list, li->data); |
282 | - g_free (uri); |
283 | - } |
284 | - } |
285 | - |
286 | - if (g_list_length (update_list) == 0) |
287 | - { |
288 | - adblock_reload_rules (extension, FALSE); |
289 | - update_done = TRUE; |
290 | - } |
291 | -} |
292 | - |
293 | -static gchar* |
294 | -adblock_get_filename_for_uri (const gchar* uri) |
295 | -{ |
296 | - gchar* filename; |
297 | - gchar* folder; |
298 | - gchar* path; |
299 | - |
300 | - if (!ADBLOCK_FILTER_IS_SET (uri)) |
301 | - return NULL; |
302 | - |
303 | - if (!strncmp (uri, "file", 4)) |
304 | - return g_strndup (uri + 7, strlen (uri) - 7); |
305 | - |
306 | - folder = g_build_filename (midori_paths_get_cache_dir (), "adblock", NULL); |
307 | - katze_mkdir_with_parents (folder, 0700); |
308 | - |
309 | - filename = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1); |
310 | - path = g_build_filename (folder, filename, NULL); |
311 | - |
312 | - g_free (filename); |
313 | - g_free (folder); |
314 | - return path; |
315 | -} |
316 | - |
317 | -static void |
318 | -adblock_reload_rules (MidoriExtension* extension, |
319 | - gboolean custom_only) |
320 | -{ |
321 | - gchar* path; |
322 | - gchar* custom_list; |
323 | - gchar** filters; |
324 | - guint i = 0; |
325 | - MidoriApp* app = midori_extension_get_app (extension); |
326 | - MidoriWebSettings* settings = katze_object_get_object (app, "settings"); |
327 | - |
328 | - if (pattern) |
329 | - adblock_destroy_db (); |
330 | - adblock_init_db (); |
331 | - |
332 | - custom_list = g_build_filename (midori_extension_get_config_dir (extension), |
333 | - CUSTOM_LIST_NAME, NULL); |
334 | - adblock_parse_file (custom_list); |
335 | - g_free (custom_list); |
336 | - |
337 | - filters = midori_extension_get_string_list (extension, "filters", NULL); |
338 | - if (!custom_only && filters && *filters) |
339 | - { |
340 | - while (filters[i] != NULL) |
341 | - { |
342 | - path = adblock_get_filename_for_uri (filters[i]); |
343 | - if (!path) |
344 | - { |
345 | - i++; |
346 | - continue; |
347 | - } |
348 | - |
349 | - if (!adblock_file_is_up_to_date (path)) |
350 | - { |
351 | - WebKitNetworkRequest* request; |
352 | - WebKitDownload* download; |
353 | - gchar* destination = g_filename_to_uri (path, NULL, NULL); |
354 | - |
355 | - request = webkit_network_request_new (filters[i]); |
356 | - download = webkit_download_new (request); |
357 | - g_object_unref (request); |
358 | - webkit_download_set_destination_uri (download, destination); |
359 | - update_list = g_list_prepend (update_list, path); |
360 | - g_free (destination); |
361 | - g_signal_connect (download, "notify::status", |
362 | - G_CALLBACK (adblock_download_notify_status_cb), extension); |
363 | - webkit_download_start (download); |
364 | - } |
365 | - else |
366 | - adblock_parse_file (path); |
367 | - g_free (path); |
368 | - i++; |
369 | - } |
370 | - } |
371 | - g_strfreev (filters); |
372 | - g_string_append (blockcss, " {display: none !important}\n"); |
373 | - |
374 | - midori_web_settings_add_style (settings, "adblock-blockcss", blockcss->str); |
375 | - g_object_unref (settings); |
376 | -} |
377 | - |
378 | -static void |
379 | -adblock_preferences_render_tick_cb (GtkTreeViewColumn* column, |
380 | - GtkCellRenderer* renderer, |
381 | - GtkTreeModel* model, |
382 | - GtkTreeIter* iter, |
383 | - MidoriExtension* extension) |
384 | -{ |
385 | - gchar* filter; |
386 | - |
387 | - gtk_tree_model_get (model, iter, 0, &filter, -1); |
388 | - |
389 | - g_object_set (renderer, |
390 | - "activatable", ADBLOCK_FILTER_VALID (filter), |
391 | - "active", ADBLOCK_FILTER_VALID (filter) && ADBLOCK_FILTER_IS_SET (filter), |
392 | - NULL); |
393 | - |
394 | - g_free (filter); |
395 | -} |
396 | - |
397 | -static void |
398 | -adblock_preferences_renderer_text_edited_cb (GtkCellRenderer* renderer, |
399 | - const gchar* tree_path, |
400 | - const gchar* new_text, |
401 | - GtkTreeModel* model) |
402 | -{ |
403 | - GtkTreeIter iter; |
404 | - |
405 | - if (gtk_tree_model_get_iter_from_string (model, &iter, tree_path)) |
406 | - gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, new_text, -1); |
407 | -} |
408 | - |
409 | -static void |
410 | -adblock_preferences_renderer_toggle_toggled_cb (GtkCellRendererToggle* renderer, |
411 | - const gchar* path, |
412 | - GtkTreeModel* model) |
413 | -{ |
414 | - GtkTreeIter iter; |
415 | - |
416 | - if (gtk_tree_model_get_iter_from_string (model, &iter, path)) |
417 | - { |
418 | - gchar* filter; |
419 | - |
420 | - gtk_tree_model_get (model, &iter, 0, &filter, -1); |
421 | - |
422 | - if (ADBLOCK_FILTER_VALID (filter)) |
423 | - { |
424 | - ADBLOCK_FILTER_SET (filter, TRUE); |
425 | - if (gtk_cell_renderer_toggle_get_active (renderer)) |
426 | - { |
427 | - if (midori_uri_is_http (filter)) |
428 | - { |
429 | - gchar* filename = adblock_get_filename_for_uri (filter); |
430 | - g_unlink (filename); |
431 | - g_free (filename); |
432 | - } |
433 | - ADBLOCK_FILTER_SET (filter, FALSE); |
434 | - } |
435 | - |
436 | - gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, filter, -1); |
437 | - |
438 | - g_free (filter); |
439 | - } |
440 | - } |
441 | -} |
442 | - |
443 | -static void |
444 | -adblock_preferences_render_text_cb (GtkTreeViewColumn* column, |
445 | - GtkCellRenderer* renderer, |
446 | - GtkTreeModel* model, |
447 | - GtkTreeIter* iter, |
448 | - MidoriExtension* extension) |
449 | -{ |
450 | - gchar* filter; |
451 | - |
452 | - gtk_tree_model_get (model, iter, 0, &filter, -1); |
453 | - |
454 | - if (ADBLOCK_FILTER_VALID (filter)) |
455 | - ADBLOCK_FILTER_SET (filter, TRUE); |
456 | - |
457 | - g_object_set (renderer, |
458 | - "text", filter, |
459 | - NULL); |
460 | - |
461 | - g_free (filter); |
462 | -} |
463 | - |
464 | -static void |
465 | -adblock_preferences_model_row_changed_cb (GtkTreeModel* model, |
466 | - GtkTreePath* path, |
467 | - GtkTreeIter* iter, |
468 | - MidoriExtension* extension) |
469 | -{ |
470 | - gsize length = gtk_tree_model_iter_n_children (model, NULL); |
471 | - gchar** filters = g_new (gchar*, length + 1); |
472 | - guint i = 0; |
473 | - gboolean need_reload = FALSE; |
474 | - |
475 | - if (gtk_tree_model_iter_children (model, iter, NULL)) |
476 | - do |
477 | - { |
478 | - gchar* filter; |
479 | - gtk_tree_model_get (model, iter, 0, &filter, -1); |
480 | - if (filter && *filter) |
481 | - { |
482 | - filters[i++] = filter; |
483 | - need_reload = TRUE; |
484 | - } |
485 | - else |
486 | - g_free (filter); |
487 | - } |
488 | - while (gtk_tree_model_iter_next (model, iter)); |
489 | - filters[i] = NULL; |
490 | - midori_extension_set_string_list (extension, "filters", filters, i); |
491 | - g_free (filters); |
492 | - if (need_reload) |
493 | - adblock_reload_rules (extension, FALSE); |
494 | -} |
495 | - |
496 | -static void |
497 | -adblock_preferences_model_row_deleted_cb (GtkTreeModel* model, |
498 | - GtkTreePath* path, |
499 | - MidoriExtension* extension) |
500 | -{ |
501 | - GtkTreeIter iter; |
502 | - adblock_preferences_model_row_changed_cb (model, path, &iter, extension); |
503 | -} |
504 | - |
505 | -static void |
506 | -adblock_preferences_add_clicked_cb (GtkWidget* button, |
507 | - GtkTreeModel* model) |
508 | -{ |
509 | - GtkEntry* entry = GTK_IS_ENTRY (button) |
510 | - ? button : g_object_get_data (G_OBJECT (button), "entry"); |
511 | - gtk_list_store_insert_with_values (GTK_LIST_STORE (model), |
512 | - NULL, 0, 0, gtk_entry_get_text (entry), -1); |
513 | - gtk_entry_set_text (entry, ""); |
514 | -} |
515 | - |
516 | -static void |
517 | -adblock_preferences_edit_clicked_cb (GtkWidget* button, |
518 | - GtkTreeViewColumn* column) |
519 | -{ |
520 | - GdkEvent* event = gtk_get_current_event (); |
521 | - GtkTreeView* treeview = g_object_get_data (G_OBJECT (button), "treeview"); |
522 | - GtkTreeModel* model; |
523 | - GtkTreeIter iter; |
524 | - if (katze_tree_view_get_selected_iter (treeview, &model, &iter)) |
525 | - { |
526 | - gchar* path = gtk_tree_model_get_string_from_iter (model, &iter); |
527 | - GtkTreePath* tree_path = gtk_tree_path_new_from_string (path); |
528 | - /* gtk_cell_renderer_start_editing */ |
529 | - gtk_tree_view_set_cursor (treeview, tree_path, column, TRUE); |
530 | - gtk_tree_path_free (tree_path); |
531 | - g_free (path); |
532 | - } |
533 | - gdk_event_free (event); |
534 | -} |
535 | - |
536 | -static void |
537 | -adblock_preferences_remove_clicked_cb (GtkWidget* button, |
538 | - GtkTreeView* treeview) |
539 | -{ |
540 | - GtkTreeModel* model; |
541 | - GtkTreeIter iter; |
542 | - if (katze_tree_view_get_selected_iter (treeview, &model, &iter)) |
543 | - gtk_list_store_remove (GTK_LIST_STORE (model), &iter); |
544 | -} |
545 | - |
546 | -static gboolean |
547 | -adblock_activate_link_cb (GtkWidget* label, |
548 | - const gchar* uri) |
549 | -{ |
550 | - MidoriBrowser* browser = midori_browser_get_for_widget (label); |
551 | - GtkWidget* view = midori_browser_add_uri (browser, uri); |
552 | - midori_browser_set_current_tab (browser, view); |
553 | - return TRUE; |
554 | -} |
555 | - |
556 | -static GtkWidget* |
557 | -adblock_get_preferences_dialog (MidoriExtension* extension) |
558 | -{ |
559 | - MidoriApp* app; |
560 | - GtkWidget* browser; |
561 | - const gchar* dialog_title; |
562 | - GtkWidget* dialog; |
563 | - GtkWidget* content_area; |
564 | - gint width, height; |
565 | - GtkWidget* xfce_heading; |
566 | - GtkWidget* hbox; |
567 | - GtkListStore* liststore; |
568 | - GtkWidget* treeview; |
569 | - GtkTreeViewColumn* column; |
570 | - GtkCellRenderer* renderer_text; |
571 | - GtkCellRenderer* renderer_toggle; |
572 | - GtkWidget* scrolled; |
573 | - gchar** filters; |
574 | - GtkWidget* vbox; |
575 | - GtkWidget* button; |
576 | - gchar* description; |
577 | - GtkWidget* entry; |
578 | - #if HAVE_OSX |
579 | - GtkWidget* icon; |
580 | - #endif |
581 | - |
582 | - app = midori_extension_get_app (extension); |
583 | - browser = katze_object_get_object (app, "browser"); |
584 | - |
585 | - dialog_title = _("Configure Advertisement filters"); |
586 | - dialog = gtk_dialog_new_with_buttons (dialog_title, GTK_WINDOW (browser), |
587 | - GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, |
588 | - #if !HAVE_OSX |
589 | - GTK_STOCK_HELP, GTK_RESPONSE_HELP, |
590 | - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
591 | - #endif |
592 | - NULL); |
593 | - katze_widget_add_class (gtk_dialog_get_widget_for_response ( |
594 | - GTK_DIALOG (dialog), GTK_RESPONSE_HELP), "help_button"); |
595 | - content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); |
596 | - gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES); |
597 | - /* TODO: Implement some kind of help function */ |
598 | - gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), |
599 | - GTK_RESPONSE_HELP, FALSE); |
600 | - sokoke_widget_get_text_size (dialog, "M", &width, &height); |
601 | - gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, -1); |
602 | - g_signal_connect_swapped (dialog, "response", |
603 | - G_CALLBACK (gtk_widget_destroy), dialog); |
604 | - /* TODO: We need mnemonics */ |
605 | - if ((xfce_heading = sokoke_xfce_header_new ( |
606 | - gtk_window_get_icon_name (GTK_WINDOW (dialog)), dialog_title))) |
607 | - gtk_box_pack_start (GTK_BOX (content_area), xfce_heading, FALSE, FALSE, 0); |
608 | - hbox = gtk_hbox_new (FALSE, 0); |
609 | - gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 12); |
610 | - vbox = gtk_vbox_new (FALSE, 0); |
611 | - gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 4); |
612 | - button = gtk_label_new (NULL); |
613 | - description = g_strdup_printf (_( |
614 | - "Type the address of a preconfigured filter list in the text entry " |
615 | - "and click \"Add\" to add it to the list. " |
616 | - "You can find more lists at %s."), |
617 | - "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a> " |
618 | - "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>"); |
619 | - g_signal_connect (button, "activate-link", |
620 | - G_CALLBACK (adblock_activate_link_cb), NULL); |
621 | - gtk_label_set_markup (GTK_LABEL (button), description); |
622 | - g_free (description); |
623 | - gtk_label_set_line_wrap (GTK_LABEL (button), TRUE); |
624 | - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 4); |
625 | - entry = katze_uri_entry_new (NULL); |
626 | - gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 4); |
627 | - liststore = gtk_list_store_new (1, G_TYPE_STRING); |
628 | - treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore)); |
629 | - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE); |
630 | - column = gtk_tree_view_column_new (); |
631 | - renderer_toggle = gtk_cell_renderer_toggle_new (); |
632 | - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer_toggle, FALSE); |
633 | - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer_toggle, |
634 | - (GtkCellLayoutDataFunc)adblock_preferences_render_tick_cb, |
635 | - extension, NULL); |
636 | - g_signal_connect (renderer_toggle, "toggled", |
637 | - G_CALLBACK (adblock_preferences_renderer_toggle_toggled_cb), liststore); |
638 | - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); |
639 | - column = gtk_tree_view_column_new (); |
640 | - renderer_text = gtk_cell_renderer_text_new (); |
641 | - gtk_tree_view_column_pack_start (column, renderer_text, TRUE); |
642 | - g_object_set (renderer_text, "editable", TRUE, NULL); |
643 | - g_signal_connect (renderer_text, "edited", |
644 | - G_CALLBACK (adblock_preferences_renderer_text_edited_cb), liststore); |
645 | - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer_text, |
646 | - (GtkCellLayoutDataFunc)adblock_preferences_render_text_cb, |
647 | - extension, NULL); |
648 | - gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); |
649 | - scrolled = gtk_scrolled_window_new (NULL, NULL); |
650 | - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), |
651 | - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
652 | - gtk_container_add (GTK_CONTAINER (scrolled), treeview); |
653 | - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), |
654 | - GTK_SHADOW_IN); |
655 | - gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5); |
656 | - |
657 | - filters = midori_extension_get_string_list (extension, "filters", NULL); |
658 | - if (filters != NULL) |
659 | - { |
660 | - gsize i = 0; |
661 | - while (filters[i++] != NULL) |
662 | - gtk_list_store_insert_with_values (GTK_LIST_STORE (liststore), |
663 | - NULL, i - 1, 0, filters[i -1], -1); |
664 | - } |
665 | - g_strfreev (filters); |
666 | - g_object_connect (liststore, |
667 | - "signal::row-inserted", |
668 | - adblock_preferences_model_row_changed_cb, extension, |
669 | - "signal::row-changed", |
670 | - adblock_preferences_model_row_changed_cb, extension, |
671 | - "signal::row-deleted", |
672 | - adblock_preferences_model_row_deleted_cb, extension, |
673 | - NULL); |
674 | - |
675 | - g_object_unref (liststore); |
676 | - vbox = gtk_vbox_new (FALSE, 4); |
677 | - gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 4); |
678 | - button = gtk_button_new_from_stock (GTK_STOCK_ADD); |
679 | - g_object_set_data (G_OBJECT (dialog), "entry", entry); |
680 | - g_object_set_data (G_OBJECT (button), "entry", entry); |
681 | - g_signal_connect (button, "clicked", |
682 | - G_CALLBACK (adblock_preferences_add_clicked_cb), liststore); |
683 | - g_signal_connect (entry, "activate", |
684 | - G_CALLBACK (adblock_preferences_add_clicked_cb), liststore); |
685 | - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); |
686 | - button = gtk_button_new_from_stock (GTK_STOCK_EDIT); |
687 | - g_object_set_data (G_OBJECT (button), "treeview", treeview); |
688 | - g_signal_connect (button, "clicked", |
689 | - G_CALLBACK (adblock_preferences_edit_clicked_cb), column); |
690 | - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); |
691 | - button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); |
692 | - g_signal_connect (button, "clicked", |
693 | - G_CALLBACK (adblock_preferences_remove_clicked_cb), treeview); |
694 | - gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); |
695 | - button = gtk_label_new (""); /* This is an invisible separator */ |
696 | - gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 8); |
697 | - gtk_widget_set_sensitive (button, FALSE); |
698 | - button = gtk_label_new (""); /* This is an invisible separator */ |
699 | - gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 12); |
700 | - button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN); |
701 | - gtk_widget_set_sensitive (button, FALSE); |
702 | - gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); |
703 | - button = gtk_button_new_from_stock (GTK_STOCK_GO_UP); |
704 | - gtk_widget_set_sensitive (button, FALSE); |
705 | - gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0); |
706 | - |
707 | - #if HAVE_OSX |
708 | - hbox = gtk_hbox_new (FALSE, 0); |
709 | - button = gtk_button_new (); |
710 | - icon = gtk_image_new_from_stock (GTK_STOCK_HELP, GTK_ICON_SIZE_BUTTON); |
711 | - gtk_button_set_image (GTK_BUTTON (button), icon); |
712 | - /* TODO: Implement some kind of help function */ |
713 | - gtk_widget_set_sensitive (button, FALSE); |
714 | - /* g_signal_connect (button, "clicked", |
715 | - G_CALLBACK (adblock_preferences_help_clicked_cb), dialog); */ |
716 | - gtk_box_pack_end (GTK_BOX (hbox), |
717 | - button, FALSE, FALSE, 4); |
718 | - gtk_box_pack_end (GTK_BOX (content_area), |
719 | - hbox, FALSE, FALSE, 0); |
720 | - #endif |
721 | - gtk_widget_show_all (content_area); |
722 | - |
723 | - g_object_unref (browser); |
724 | - |
725 | - return dialog; |
726 | -} |
727 | - |
728 | -static GtkWidget* |
729 | -adblock_show_preferences_dialog (MidoriExtension* extension, |
730 | - const gchar* uri) |
731 | -{ |
732 | - static GtkWidget* dialog = NULL; |
733 | - |
734 | - if (!dialog) |
735 | - { |
736 | - dialog = adblock_get_preferences_dialog (extension); |
737 | - g_signal_connect (dialog, "destroy", |
738 | - G_CALLBACK (gtk_widget_destroyed), &dialog); |
739 | - gtk_widget_show (dialog); |
740 | - } |
741 | - else |
742 | - gtk_window_present (GTK_WINDOW (dialog)); |
743 | - |
744 | - if (uri != NULL) |
745 | - { |
746 | - GtkWidget* entry = g_object_get_data (G_OBJECT (dialog), "entry"); |
747 | - gtk_entry_set_text (GTK_ENTRY (entry), uri); |
748 | - } |
749 | - return dialog; |
750 | -} |
751 | - |
752 | -static void |
753 | -adblock_open_preferences_cb (MidoriExtension* extension) |
754 | -{ |
755 | - adblock_show_preferences_dialog (extension, NULL); |
756 | -} |
757 | - |
758 | -static gint |
759 | -adblock_check_rule (GRegex* regex, |
760 | - const gchar* patt, |
761 | - const gchar* req_uri, |
762 | - const gchar* page_uri) |
763 | -{ |
764 | - gchar* opts; |
765 | - |
766 | - if (!g_regex_match_full (regex, req_uri, -1, 0, 0, NULL, NULL)) |
767 | - return FALSE; |
768 | - |
769 | - opts = g_hash_table_lookup (optslist, patt); |
770 | - if (opts && g_regex_match_simple (",third-party", opts, |
771 | - G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY)) |
772 | - { |
773 | - if (page_uri && g_regex_match_full (regex, page_uri, -1, 0, 0, NULL, NULL)) |
774 | - return FALSE; |
775 | - } |
776 | - /* TODO: Domain opt check */ |
777 | - adblock_debug ("blocked by pattern regexp=%s -- %s", g_regex_get_pattern (regex), req_uri); |
778 | - return TRUE; |
779 | -} |
780 | - |
781 | -static gboolean |
782 | -adblock_is_matched_by_pattern (const gchar* req_uri, |
783 | - const gchar* page_uri) |
784 | -{ |
785 | - GHashTableIter iter; |
786 | - gpointer patt, regex; |
787 | - |
788 | - if (USE_PATTERN_MATCHING == 0) |
789 | - return FALSE; |
790 | - |
791 | - g_hash_table_iter_init (&iter, pattern); |
792 | - while (g_hash_table_iter_next (&iter, &patt, ®ex)) |
793 | - { |
794 | - if (adblock_check_rule (regex, patt, req_uri, page_uri)) |
795 | - return TRUE; |
796 | - } |
797 | - return FALSE; |
798 | -} |
799 | - |
800 | -static gboolean |
801 | -adblock_is_matched_by_key (const gchar* req_uri, |
802 | - const gchar* page_uri) |
803 | -{ |
804 | - gchar* uri; |
805 | - gint len; |
806 | - int pos = 0; |
807 | - GList* regex_bl = NULL; |
808 | - GString* guri; |
809 | - gboolean ret = FALSE; |
810 | - gchar sig[SIGNATURE_SIZE + 1]; |
811 | - |
812 | - memset (&sig[0], 0, sizeof (sig)); |
813 | - /* Signatures are made on pattern, so we need to convert url to a pattern as well */ |
814 | - guri = adblock_fixup_regexp ("", (gchar*)req_uri); |
815 | - uri = guri->str; |
816 | - len = guri->len; |
817 | - |
818 | - for (pos = len - SIGNATURE_SIZE; pos >= 0; pos--) |
819 | - { |
820 | - GRegex* regex; |
821 | - strncpy (sig, uri + pos, SIGNATURE_SIZE); |
822 | - regex = g_hash_table_lookup (keys, sig); |
823 | - |
824 | - /* Dont check if regex is already blacklisted */ |
825 | - if (!regex || g_list_find (regex_bl, regex)) |
826 | - continue; |
827 | - ret = adblock_check_rule (regex, sig, req_uri, page_uri); |
828 | - if (ret) |
829 | - break; |
830 | - regex_bl = g_list_prepend (regex_bl, regex); |
831 | - } |
832 | - g_string_free (guri, TRUE); |
833 | - g_list_free (regex_bl); |
834 | - return ret; |
835 | -} |
836 | - |
837 | -static gboolean |
838 | -adblock_is_matched (const gchar* req_uri, |
839 | - const gchar* page_uri) |
840 | -{ |
841 | - gchar* value; |
842 | - |
843 | - if ((value = g_hash_table_lookup (urlcache, req_uri))) |
844 | - { |
845 | - if (value[0] == '0') |
846 | - return FALSE; |
847 | - else |
848 | - return TRUE; |
849 | - } |
850 | - |
851 | - if (adblock_is_matched_by_key (req_uri, page_uri)) |
852 | - { |
853 | - g_hash_table_insert (urlcache, g_strdup (req_uri), g_strdup("1")); |
854 | - return TRUE; |
855 | - } |
856 | - |
857 | - if (adblock_is_matched_by_pattern (req_uri, page_uri)) |
858 | - { |
859 | - g_hash_table_insert (urlcache, g_strdup (req_uri), g_strdup("1")); |
860 | - return TRUE; |
861 | - } |
862 | - g_hash_table_insert (urlcache, g_strdup (req_uri), g_strdup("0")); |
863 | - return FALSE; |
864 | -} |
865 | - |
866 | -static gchar* |
867 | -adblock_prepare_urihider_js (GList* uris) |
868 | -{ |
869 | - GList* li = NULL; |
870 | - GString* js = g_string_new ( |
871 | - "(function() {" |
872 | - "function getElementsByAttribute (strTagName, strAttributeName, arrAttributeValue) {" |
873 | - " var arrElements = document.getElementsByTagName (strTagName);" |
874 | - " var arrReturnElements = new Array();" |
875 | - " for (var j=0; j<arrAttributeValue.length; j++) {" |
876 | - " var strAttributeValue = arrAttributeValue[j];" |
877 | - " for (var i=0; i<arrElements.length; i++) {" |
878 | - " var oCurrent = arrElements[i];" |
879 | - " var oAttribute = oCurrent.getAttribute && oCurrent.getAttribute (strAttributeName);" |
880 | - " if (oAttribute && oAttribute.length > 0 && strAttributeValue.indexOf (oAttribute) != -1)" |
881 | - " arrReturnElements.push (oCurrent);" |
882 | - " }" |
883 | - " }" |
884 | - " return arrReturnElements;" |
885 | - "};" |
886 | - "function hideElementBySrc (uris) {" |
887 | - " var oElements = getElementsByAttribute('img', 'src', uris);" |
888 | - " if (oElements.length == 0)" |
889 | - " oElements = getElementsByAttribute ('iframe', 'src', uris);" |
890 | - " for (var i=0; i<oElements.length; i++) {" |
891 | - " oElements[i].style.visibility = 'hidden !important';" |
892 | - " oElements[i].style.width = '0';" |
893 | - " oElements[i].style.height = '0';" |
894 | - " }" |
895 | - "};" |
896 | - "var uris=new Array ();"); |
897 | - |
898 | - for (li = uris; li != NULL; li = g_list_next (li)) |
899 | - g_string_append_printf (js, "uris.push ('%s');", (gchar*)li->data); |
900 | - |
901 | - g_string_append (js, "hideElementBySrc (uris);})();"); |
902 | - |
903 | - return g_string_free (js, FALSE); |
904 | -} |
905 | - |
906 | -static gboolean |
907 | -adblock_navigation_policy_decision_requested_cb (WebKitWebView* web_view, |
908 | - WebKitWebFrame* web_frame, |
909 | - WebKitNetworkRequest* request, |
910 | - WebKitWebNavigationAction* action, |
911 | - WebKitWebPolicyDecision* decision, |
912 | - MidoriExtension* extension) |
913 | -{ |
914 | - const gchar* uri = webkit_network_request_get_uri (request); |
915 | - if (g_str_has_prefix (uri, "abp:")) |
916 | - { |
917 | - gchar** parts; |
918 | - gchar* filter; |
919 | - if (g_str_has_prefix (uri, "abp:subscribe?location=")) |
920 | - uri = &uri[23]; |
921 | - else if (g_str_has_prefix (uri, "abp://subscribe?location=")) |
922 | - uri = &uri[25]; |
923 | - else |
924 | - return FALSE; |
925 | - |
926 | - parts = g_strsplit (uri, "&", 2); |
927 | - filter = soup_uri_decode (parts[0]); |
928 | - webkit_web_policy_decision_ignore (decision); |
929 | - adblock_show_preferences_dialog (extension, filter); |
930 | - g_free (filter); |
931 | - g_strfreev (parts); |
932 | - return TRUE; |
933 | - } |
934 | - |
935 | - if (web_frame == webkit_web_view_get_main_frame (web_view)) |
936 | - { |
937 | - const gchar* req_uri = webkit_network_request_get_uri (request); |
938 | - g_hash_table_replace (navigationwhitelist, web_view, g_strdup (req_uri)); |
939 | - } |
940 | - return false; |
941 | -} |
942 | - |
943 | - |
944 | -static void |
945 | -adblock_resource_request_starting_cb (WebKitWebView* web_view, |
946 | - WebKitWebFrame* web_frame, |
947 | - WebKitWebResource* web_resource, |
948 | - WebKitNetworkRequest* request, |
949 | - WebKitNetworkResponse* response, |
950 | - MidoriView* view) |
951 | -{ |
952 | - SoupMessage* msg; |
953 | - GList* blocked_uris; |
954 | - const gchar* req_uri; |
955 | - const char *page_uri; |
956 | - |
957 | - page_uri = webkit_web_view_get_uri (web_view); |
958 | - /* Skip checks on about: pages */ |
959 | - if (midori_uri_is_blank (page_uri)) |
960 | - return; |
961 | - |
962 | - req_uri = webkit_network_request_get_uri (request); |
963 | - |
964 | - if (!g_strcmp0 (req_uri, g_hash_table_lookup (navigationwhitelist, web_view))) |
965 | - return; |
966 | - |
967 | - if (!midori_uri_is_http (req_uri) |
968 | - || g_str_has_suffix (req_uri, "favicon.ico")) |
969 | - return; |
970 | - |
971 | - msg = webkit_network_request_get_message (request); |
972 | - if (!(msg && !g_strcmp0 (msg->method, "GET"))) |
973 | - return; |
974 | - |
975 | - if (response != NULL) /* request is caused by redirect */ |
976 | - { |
977 | - if (web_frame == webkit_web_view_get_main_frame (web_view)) |
978 | - { |
979 | - g_hash_table_replace (navigationwhitelist, web_view, g_strdup (req_uri)); |
980 | - return; |
981 | - } |
982 | - } |
983 | - |
984 | - if (midori_debug ("adblock:time")) |
985 | - g_test_timer_start (); |
986 | - if (adblock_is_matched (req_uri, page_uri)) |
987 | - { |
988 | - blocked_uris = g_object_get_data (G_OBJECT (web_view), "blocked-uris"); |
989 | - blocked_uris = g_list_prepend (blocked_uris, g_strdup (req_uri)); |
990 | - webkit_network_request_set_uri (request, "about:blank"); |
991 | - g_object_set_data (G_OBJECT (web_view), "blocked-uris", blocked_uris); |
992 | - } |
993 | - if (midori_debug ("adblock:time")) |
994 | - g_debug ("match: %f%s", g_test_timer_elapsed (), "seconds"); |
995 | -} |
996 | - |
997 | -static void |
998 | -adblock_custom_block_image_cb (GtkWidget* widget, |
999 | - MidoriExtension* extension) |
1000 | -{ |
1001 | - gchar* custom_list; |
1002 | - FILE* list; |
1003 | - MidoriApp* app; |
1004 | - GtkWidget* browser; |
1005 | - GtkWidget* dialog; |
1006 | - GtkWidget* content_area; |
1007 | - GtkSizeGroup* sizegroup; |
1008 | - GtkWidget* hbox; |
1009 | - GtkWidget* label; |
1010 | - GtkWidget* entry; |
1011 | - gchar* title; |
1012 | - |
1013 | - app = midori_extension_get_app (extension); |
1014 | - browser = katze_object_get_object (app, "browser"); |
1015 | - |
1016 | - title = _("Edit rule"); |
1017 | - dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (browser), |
1018 | - GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, |
1019 | - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
1020 | - GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, |
1021 | - NULL); |
1022 | - content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); |
1023 | - gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_ADD); |
1024 | - gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); |
1025 | - gtk_container_set_border_width (GTK_CONTAINER (content_area), 5); |
1026 | - sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); |
1027 | - |
1028 | - hbox = gtk_hbox_new (FALSE, 8); |
1029 | - gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); |
1030 | - label = gtk_label_new_with_mnemonic (_("_Rule:")); |
1031 | - gtk_size_group_add_widget (sizegroup, label); |
1032 | - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); |
1033 | - entry = gtk_entry_new (); |
1034 | - gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); |
1035 | - gtk_entry_set_text (GTK_ENTRY (entry), |
1036 | - g_object_get_data (G_OBJECT (widget), "uri")); |
1037 | - gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); |
1038 | - gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, TRUE, 0); |
1039 | - gtk_widget_show_all (hbox); |
1040 | - |
1041 | - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); |
1042 | - if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT) |
1043 | - { |
1044 | - gtk_widget_destroy (dialog); |
1045 | - return; |
1046 | - } |
1047 | - |
1048 | - custom_list = g_build_filename (midori_extension_get_config_dir (extension), |
1049 | - CUSTOM_LIST_NAME, NULL); |
1050 | - katze_mkdir_with_parents (midori_extension_get_config_dir (extension), 0700); |
1051 | - if ((list = g_fopen (custom_list, "a+"))) |
1052 | - { |
1053 | - g_fprintf (list, "%s\n", gtk_entry_get_text (GTK_ENTRY (entry))); |
1054 | - fclose (list); |
1055 | - adblock_reload_rules (extension, TRUE); |
1056 | - g_debug ("%s: Updated custom list\n", G_STRFUNC); |
1057 | - } |
1058 | - else |
1059 | - g_debug ("%s: Failed to open custom list %s\n", G_STRFUNC, custom_list); |
1060 | - g_free (custom_list); |
1061 | - gtk_widget_destroy (dialog); |
1062 | -} |
1063 | - |
1064 | -static void |
1065 | -adblock_populate_popup_cb (WebKitWebView* web_view, |
1066 | - GtkWidget* menu, |
1067 | - MidoriExtension* extension) |
1068 | -{ |
1069 | - GtkWidget* menuitem; |
1070 | - gchar *uri; |
1071 | - gint x, y; |
1072 | - GdkEventButton event; |
1073 | - WebKitHitTestResultContext context; |
1074 | - WebKitHitTestResult* hit_test; |
1075 | - |
1076 | - gdk_window_get_pointer (gtk_widget_get_window(GTK_WIDGET (web_view)), &x, &y, NULL); |
1077 | - event.x = x; |
1078 | - event.y = y; |
1079 | - hit_test = webkit_web_view_get_hit_test_result (web_view, &event); |
1080 | - context = katze_object_get_int (hit_test, "context"); |
1081 | - if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) |
1082 | - { |
1083 | - uri = katze_object_get_string (hit_test, "image-uri"); |
1084 | - menuitem = gtk_menu_item_new_with_mnemonic (_("Bl_ock image")); |
1085 | - } |
1086 | - else if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) |
1087 | - { |
1088 | - uri = katze_object_get_string (hit_test, "link-uri"); |
1089 | - menuitem = gtk_menu_item_new_with_mnemonic (_("Bl_ock link")); |
1090 | - } |
1091 | - else |
1092 | - return; |
1093 | - gtk_widget_show (menuitem); |
1094 | - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); |
1095 | - g_object_set_data_full (G_OBJECT (menuitem), "uri", uri, (GDestroyNotify)g_free); |
1096 | - g_signal_connect (menuitem, "activate", |
1097 | - G_CALLBACK (adblock_custom_block_image_cb), extension); |
1098 | -} |
1099 | - |
1100 | -static void |
1101 | -adblock_load_finished_cb (WebKitWebView *web_view, |
1102 | - WebKitWebFrame *web_frame, |
1103 | - gpointer user_data) |
1104 | -{ |
1105 | - GList* uris = g_object_get_data (G_OBJECT (web_view), "blocked-uris"); |
1106 | - gchar* script; |
1107 | - GList* li; |
1108 | - |
1109 | - if (g_list_nth_data (uris, 0) == NULL) |
1110 | - return; |
1111 | - |
1112 | - script = adblock_prepare_urihider_js (uris); |
1113 | - webkit_web_view_execute_script (web_view, script); |
1114 | - li = NULL; |
1115 | - for (li = uris; li != NULL; li = g_list_next (li)) |
1116 | - uris = g_list_remove (uris, li->data); |
1117 | - g_free (script); |
1118 | - g_object_set_data (G_OBJECT (web_view), "blocked-uris", uris); |
1119 | -} |
1120 | - |
1121 | -static void |
1122 | -adblock_window_object_cleared_cb (WebKitWebView* web_view, |
1123 | - WebKitWebFrame* web_frame, |
1124 | - JSContextRef js_context, |
1125 | - JSObjectRef js_window) |
1126 | -{ |
1127 | - const char *page_uri; |
1128 | - gchar* script; |
1129 | - |
1130 | - page_uri = webkit_web_frame_get_uri (web_frame); |
1131 | - /* Don't add adblock css into speeddial and about: pages */ |
1132 | - if (!midori_uri_is_http (page_uri)) |
1133 | - return; |
1134 | - |
1135 | - script = adblock_build_js (page_uri); |
1136 | - if (!script) |
1137 | - return; |
1138 | - |
1139 | - g_free (sokoke_js_script_eval (js_context, script, NULL)); |
1140 | - g_free (script); |
1141 | -} |
1142 | - |
1143 | -static void |
1144 | -adblock_add_tab_cb (MidoriBrowser* browser, |
1145 | - MidoriView* view, |
1146 | - MidoriExtension* extension) |
1147 | -{ |
1148 | - GtkWidget* web_view = midori_view_get_web_view (view); |
1149 | - |
1150 | - g_signal_connect (web_view, "window-object-cleared", |
1151 | - G_CALLBACK (adblock_window_object_cleared_cb), 0); |
1152 | - |
1153 | - g_signal_connect_after (web_view, "populate-popup", |
1154 | - G_CALLBACK (adblock_populate_popup_cb), extension); |
1155 | - g_signal_connect (web_view, "navigation-policy-decision-requested", |
1156 | - G_CALLBACK (adblock_navigation_policy_decision_requested_cb), extension); |
1157 | - g_signal_connect (web_view, "resource-request-starting", |
1158 | - G_CALLBACK (adblock_resource_request_starting_cb), view); |
1159 | - g_signal_connect (web_view, "load-finished", |
1160 | - G_CALLBACK (adblock_load_finished_cb), view); |
1161 | -} |
1162 | - |
1163 | -static void |
1164 | -adblock_remove_tab_cb (MidoriBrowser* browser, |
1165 | - MidoriView* view, |
1166 | - MidoriExtension* extension) |
1167 | -{ |
1168 | - GtkWidget* web_view = midori_view_get_web_view (view); |
1169 | - g_hash_table_remove (navigationwhitelist, web_view); |
1170 | -} |
1171 | - |
1172 | -static void |
1173 | -adblock_deactivate_cb (MidoriExtension* extension, |
1174 | - MidoriBrowser* browser); |
1175 | - |
1176 | -static void |
1177 | -adblock_app_add_browser_cb (MidoriApp* app, |
1178 | - MidoriBrowser* browser, |
1179 | - MidoriExtension* extension) |
1180 | -{ |
1181 | - GtkWidget* statusbar; |
1182 | - GtkWidget* image; |
1183 | - GList* children; |
1184 | - |
1185 | - statusbar = katze_object_get_object (browser, "statusbar"); |
1186 | - image = NULL; |
1187 | - /* image = gtk_image_new_from_stock (STOCK_IMAGE, GTK_ICON_SIZE_MENU); |
1188 | - gtk_widget_show (image); |
1189 | - gtk_box_pack_start (GTK_BOX (statusbar), image, FALSE, FALSE, 3); */ |
1190 | - g_object_set_data_full (G_OBJECT (browser), "status-image", image, |
1191 | - (GDestroyNotify)gtk_widget_destroy); |
1192 | - |
1193 | - children = midori_browser_get_tabs (MIDORI_BROWSER (browser)); |
1194 | - for (; children; children = g_list_next (children)) |
1195 | - adblock_add_tab_cb (browser, children->data, extension); |
1196 | - g_list_free (children); |
1197 | - |
1198 | - g_signal_connect (browser, "add-tab", |
1199 | - G_CALLBACK (adblock_add_tab_cb), extension); |
1200 | - g_signal_connect (browser, "remove-tab", |
1201 | - G_CALLBACK (adblock_remove_tab_cb), extension); |
1202 | - g_signal_connect (extension, "open-preferences", |
1203 | - G_CALLBACK (adblock_open_preferences_cb), extension); |
1204 | - g_signal_connect (extension, "deactivate", |
1205 | - G_CALLBACK (adblock_deactivate_cb), browser); |
1206 | - g_object_unref (statusbar); |
1207 | -} |
1208 | - |
1209 | -static GString* |
1210 | -adblock_fixup_regexp (const gchar* prefix, |
1211 | - gchar* src) |
1212 | -{ |
1213 | - GString* str; |
1214 | - int len = 0; |
1215 | - |
1216 | - if (!src) |
1217 | - return NULL; |
1218 | - |
1219 | - str = g_string_new (prefix); |
1220 | - |
1221 | - /* lets strip first .* */ |
1222 | - if (src[0] == '*') |
1223 | - { |
1224 | - (void)*src++; |
1225 | - } |
1226 | - |
1227 | - do |
1228 | - { |
1229 | - switch (*src) |
1230 | - { |
1231 | - case '*': |
1232 | - g_string_append (str, ".*"); |
1233 | - break; |
1234 | - /*case '.': |
1235 | - g_string_append (str, "\\."); |
1236 | - break;*/ |
1237 | - case '?': |
1238 | - g_string_append (str, "\\?"); |
1239 | - break; |
1240 | - case '|': |
1241 | - /* FIXME: We actually need to match :[0-9]+ or '/'. Sign means |
1242 | - "here could be port number or nothing". So bla.com^ will match |
1243 | - bla.com/ or bla.com:8080/ but not bla.com.au/ */ |
1244 | - case '^': |
1245 | - case '+': |
1246 | - break; |
1247 | - case '[': |
1248 | - g_string_append (str, "\\["); |
1249 | - break; |
1250 | - case ']': |
1251 | - g_string_append (str, "\\]"); |
1252 | - break; |
1253 | - case '(': |
1254 | - g_string_append (str, "\\("); |
1255 | - break; |
1256 | - case ')': |
1257 | - g_string_append (str, "\\)"); |
1258 | - break; |
1259 | - default: |
1260 | - g_string_append_printf (str,"%c", *src); |
1261 | - break; |
1262 | - } |
1263 | - src++; |
1264 | - } |
1265 | - while (*src); |
1266 | - |
1267 | - len = str->len; |
1268 | - /* We dont need .* in the end of url. Thats stupid */ |
1269 | - if (str->str && str->str[len-1] == '*' && str->str[len-2] == '.') |
1270 | - g_string_erase (str, len-2, 2); |
1271 | - |
1272 | - return str; |
1273 | -} |
1274 | - |
1275 | -static gboolean |
1276 | -adblock_compile_regexp (GString* gpatt, |
1277 | - gchar* opts) |
1278 | -{ |
1279 | - GRegex* regex; |
1280 | - GError* error = NULL; |
1281 | - int pos = 0; |
1282 | - gchar *sig; |
1283 | - gchar *patt; |
1284 | - int len; |
1285 | - |
1286 | - if (!gpatt) |
1287 | - return FALSE; |
1288 | - |
1289 | - patt = gpatt->str; |
1290 | - len = gpatt->len; |
1291 | - |
1292 | - /* TODO: Play with optimization flags */ |
1293 | - regex = g_regex_new (patt, G_REGEX_OPTIMIZE, |
1294 | - G_REGEX_MATCH_NOTEMPTY, &error); |
1295 | - if (error) |
1296 | - { |
1297 | - g_warning ("%s: %s", G_STRFUNC, error->message); |
1298 | - g_error_free (error); |
1299 | - return TRUE; |
1300 | - } |
1301 | - |
1302 | - if (!g_regex_match_simple ("^/.*[\\^\\$\\*].*/$", patt, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY)) |
1303 | - { |
1304 | - int signature_count = 0; |
1305 | - |
1306 | - for (pos = len - SIGNATURE_SIZE; pos >= 0; pos--) { |
1307 | - sig = g_strndup (patt + pos, SIGNATURE_SIZE); |
1308 | - if (!g_regex_match_simple ("[\\*]", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) && |
1309 | - !g_hash_table_lookup (keys, sig)) |
1310 | - { |
1311 | - adblock_debug ("sig: %s %s", sig, patt); |
1312 | - g_hash_table_insert (keys, sig, regex); |
1313 | - g_hash_table_insert (optslist, sig, g_strdup (opts)); |
1314 | - signature_count++; |
1315 | - } |
1316 | - else |
1317 | - { |
1318 | - if (g_regex_match_simple ("^\\*", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) && |
1319 | - !g_hash_table_lookup (pattern, patt)) |
1320 | - { |
1321 | - adblock_debug ("patt2: %s %s", sig, patt); |
1322 | - g_hash_table_insert (pattern, patt, regex); |
1323 | - g_hash_table_insert (optslist, patt, g_strdup (opts)); |
1324 | - } |
1325 | - g_free (sig); |
1326 | - } |
1327 | - } |
1328 | - if (signature_count > 1 && g_hash_table_lookup (pattern, patt)) |
1329 | - { |
1330 | - g_hash_table_steal (pattern, patt); |
1331 | - return TRUE; |
1332 | - } |
1333 | - return FALSE; |
1334 | - } |
1335 | - else |
1336 | - { |
1337 | - adblock_debug ("patt: %s%s", patt, ""); |
1338 | - /* Pattern is a regexp chars */ |
1339 | - g_hash_table_insert (pattern, patt, regex); |
1340 | - g_hash_table_insert (optslist, patt, g_strdup (opts)); |
1341 | - return FALSE; |
1342 | - } |
1343 | -} |
1344 | - |
1345 | -static gchar* |
1346 | -adblock_add_url_pattern (gchar* prefix, |
1347 | - gchar* type, |
1348 | - gchar* line) |
1349 | -{ |
1350 | - gchar** data; |
1351 | - gchar* patt; |
1352 | - GString* format_patt; |
1353 | - gchar* opts; |
1354 | - gboolean should_free; |
1355 | - |
1356 | - data = g_strsplit (line, "$", -1); |
1357 | - if (!data || !data[0]) |
1358 | - { |
1359 | - g_strfreev (data); |
1360 | - return NULL; |
1361 | - } |
1362 | - |
1363 | - if (data[1] && data[2]) |
1364 | - { |
1365 | - patt = g_strconcat (data[0], data[1], NULL); |
1366 | - opts = g_strconcat (type, ",", data[2], NULL); |
1367 | - } |
1368 | - else if (data[1]) |
1369 | - { |
1370 | - patt = data[0]; |
1371 | - opts = g_strconcat (type, ",", data[1], NULL); |
1372 | - } |
1373 | - else |
1374 | - { |
1375 | - patt = data[0]; |
1376 | - opts = type; |
1377 | - } |
1378 | - |
1379 | - if (g_regex_match_simple ("subdocument", opts, |
1380 | - G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY)) |
1381 | - { |
1382 | - if (data[1] && data[2]) |
1383 | - g_free (patt); |
1384 | - if (data[1]) |
1385 | - g_free (opts); |
1386 | - g_strfreev (data); |
1387 | - return NULL; |
1388 | - } |
1389 | - |
1390 | - format_patt = adblock_fixup_regexp (prefix, patt); |
1391 | - |
1392 | - adblock_debug ("got: %s opts %s", format_patt->str, opts); |
1393 | - should_free = adblock_compile_regexp (format_patt, opts); |
1394 | - |
1395 | - if (data[1] && data[2]) |
1396 | - g_free (patt); |
1397 | - if (data[1]) |
1398 | - g_free (opts); |
1399 | - g_strfreev (data); |
1400 | - |
1401 | - return g_string_free (format_patt, should_free); |
1402 | -} |
1403 | - |
1404 | -static void |
1405 | -adblock_frame_add (gchar* line) |
1406 | -{ |
1407 | - const gchar* separator = " , "; |
1408 | - |
1409 | - (void)*line++; |
1410 | - (void)*line++; |
1411 | - if (strchr (line, '\'') |
1412 | - || (strchr (line, ':') |
1413 | - && !g_regex_match_simple (".*\\[.*:.*\\].*", line, |
1414 | - G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))) |
1415 | - { |
1416 | - return; |
1417 | - } |
1418 | - g_string_append (blockcss, separator); |
1419 | - g_string_append (blockcss, line); |
1420 | -} |
1421 | - |
1422 | -static void |
1423 | -adblock_update_css_hash (gchar* domain, |
1424 | - gchar* value) |
1425 | -{ |
1426 | - const gchar* olddata; |
1427 | - gchar* newdata; |
1428 | - |
1429 | - if ((olddata = g_hash_table_lookup (blockcssprivate, domain))) |
1430 | - { |
1431 | - newdata = g_strconcat (olddata, " , ", value, NULL); |
1432 | - g_hash_table_replace (blockcssprivate, g_strdup (domain), newdata); |
1433 | - } |
1434 | - else |
1435 | - g_hash_table_insert (blockcssprivate, g_strdup (domain), g_strdup (value)); |
1436 | -} |
1437 | - |
1438 | -static void |
1439 | -adblock_frame_add_private (const gchar* line, |
1440 | - const gchar* sep) |
1441 | -{ |
1442 | - gchar** data; |
1443 | - data = g_strsplit (line, sep, 2); |
1444 | - |
1445 | - if (!(data[1] && *data[1]) |
1446 | - || strchr (data[1], '\'') |
1447 | - || (strchr (data[1], ':') |
1448 | - && !g_regex_match_simple (".*\\[.*:.*\\].*", data[1], |
1449 | - G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))) |
1450 | - { |
1451 | - g_strfreev (data); |
1452 | - return; |
1453 | - } |
1454 | - |
1455 | - if (strchr (data[0], ',')) |
1456 | - { |
1457 | - gchar** domains; |
1458 | - gint i; |
1459 | - |
1460 | - domains = g_strsplit (data[0], ",", -1); |
1461 | - for (i = 0; domains[i]; i++) |
1462 | - { |
1463 | - gchar* domain; |
1464 | - |
1465 | - domain = domains[i]; |
1466 | - /* Ignore Firefox-specific option */ |
1467 | - if (!g_strcmp0 (domain, "~pregecko2")) |
1468 | - continue; |
1469 | - /* FIXME: ~ should negate match */ |
1470 | - if (domain[0] == '~') |
1471 | - domain++; |
1472 | - adblock_update_css_hash (g_strstrip (domain), data[1]); |
1473 | - } |
1474 | - g_strfreev (domains); |
1475 | - } |
1476 | - else |
1477 | - { |
1478 | - adblock_update_css_hash (data[0], data[1]); |
1479 | - } |
1480 | - g_strfreev (data); |
1481 | -} |
1482 | - |
1483 | -static gchar* |
1484 | -adblock_parse_line (gchar* line) |
1485 | -{ |
1486 | - /* |
1487 | - * AdblockPlus rule reference based on http://adblockplus.org/en/filters |
1488 | - * Block URL: |
1489 | - * http://example.com/ads/banner123.gif |
1490 | - * http://example.com/ads/banner*.gif |
1491 | - * Partial match for "ad": |
1492 | - * *ad* |
1493 | - * ad |
1494 | - * Block example.com/annoyingflash.swf but not example.com/swf/: |
1495 | - * swf| |
1496 | - * Block bad.example/banner.gif but not good.example/analyze?http://bad.example: |
1497 | - * |http://baddomain.example/ |
1498 | - * Block http(s) example.com but not badexample.com or good.example/analyze?http://bad.example: |
1499 | - * ||example.com/banner.gif |
1500 | - * Block example.com/ and example.com:8000/ but not example.com.ar/: |
1501 | - * http://example.com^ |
1502 | - * A ^ matches anything that isn't A-Za-z0-0_-.% |
1503 | - * Block example.com:8000/foo.bar?a=12&b=%D1%82%D0%B5: |
1504 | - * ^example.com^ |
1505 | - * ^%D1%82%D0%B5^ |
1506 | - * ^foo.bar^ |
1507 | - * TODO: ^ is partially supported by Midori |
1508 | - * Block banner123 and banner321 with a regex: |
1509 | - * /banner\d+/ |
1510 | - * Never block URIs with "advice": |
1511 | - * @@advice |
1512 | - * No blocking at all: |
1513 | - * @@http://example.com |
1514 | - * @@|http://example.com |
1515 | - * TODO: @@ is currently ignored by Midori. |
1516 | - * Element hiding by class: |
1517 | - * ##textad |
1518 | - * ##div.textad |
1519 | - * Element hiding by id: |
1520 | - * ##div#sponsorad |
1521 | - * ##*#sponsorad |
1522 | - * Match example.com/ and something.example.com/ but not example.org/ |
1523 | - * example.com##*.sponsor |
1524 | - * Match multiple domains: |
1525 | - * domain1.example,domain2.example,domain3.example##*.sponsor |
1526 | - * Match on any domain but "example.com": |
1527 | - * ~example.com##*.sponsor |
1528 | - * Match on "example.com" except "foo.example.com": |
1529 | - * example.com,~foo.example.com##*.sponsor |
1530 | - * By design rules only apply to full domain names: |
1531 | - * "domain" is NOT equal to "domain.example,domain.test." |
1532 | - * In Firefox rules can apply to browser UI: |
1533 | - * browser##menuitem#javascriptConsole will hide the Console menuitem |
1534 | - * Hide tables with width attribute 80%: |
1535 | - * ##table[width="80%"] |
1536 | - * Hide all div with title attribute containing "adv": |
1537 | - * ##div[title*="adv"] |
1538 | - * Hide div with title starting with "adv" and ending with "ert": |
1539 | - * ##div[title^="adv"][title$="ert"] |
1540 | - * Match tables with width attribute 80% and bgcolor attribute white: |
1541 | - * table[width="80%"][bgcolor="white"] |
1542 | - * TODO: [] is currently ignored by Midori |
1543 | - * Hide anything following div with class "adheader": |
1544 | - * ##div.adheader + * |
1545 | - * Old CSS element hiding syntax, officially deprecated: |
1546 | - * #div(id=foo) |
1547 | - * Match anything but "example.com" |
1548 | - * ~example.com##*.sponsor |
1549 | - * TODO: ~ is currently ignored by Midori |
1550 | - * Match "example.com" domain except "foo.example.com": |
1551 | - * example.com,~foo.example.com##*.sponsor |
1552 | - * ! Comment |
1553 | - * Supported options after a trailing $: |
1554 | - * domain,third-party,~pregecko2 |
1555 | - * Official options (not all supported by Midori): |
1556 | - * script,image,stylesheet,object,xmlhttprequest,object-subrequest, |
1557 | - * subdocument,document,elemhide,popup,third-party,sitekey,match-case |
1558 | - * collapse,donottrack,pregecko2 |
1559 | - * Deprecated: |
1560 | - * background,xbl,ping,dtd |
1561 | - * Inverse options: |
1562 | - * ~script,~image,~stylesheet,~object,~xmlhttprequest,~collapse, |
1563 | - * ~object-subrequest,~subdocument,~document,~elemhide,~third-party, |
1564 | - * ~pregecko2 |
1565 | - **/ |
1566 | - |
1567 | - /* Skip invalid, empty and comment lines */ |
1568 | - if (!(line && line[0] != ' ' && line[0] != '!' && line[0])) |
1569 | - return NULL; |
1570 | - |
1571 | - /* FIXME: No support for whitelisting */ |
1572 | - if (line[0] == '@' && line[1] == '@') |
1573 | - return NULL; |
1574 | - /* FIXME: No support for [include] and [exclude] tags */ |
1575 | - if (line[0] == '[') |
1576 | - return NULL; |
1577 | - |
1578 | - g_strchomp (line); |
1579 | - |
1580 | - /* Got CSS block hider */ |
1581 | - if (line[0] == '#' && line[1] == '#' ) |
1582 | - { |
1583 | - adblock_frame_add (line); |
1584 | - return NULL; |
1585 | - } |
1586 | - /* Got CSS block hider. Workaround */ |
1587 | - if (line[0] == '#') |
1588 | - return NULL; |
1589 | - |
1590 | - /* Got per domain CSS hider rule */ |
1591 | - if (strstr (line, "##")) |
1592 | - { |
1593 | - adblock_frame_add_private (line, "##"); |
1594 | - return NULL; |
1595 | - } |
1596 | - /* Got per domain CSS hider rule. Workaround */ |
1597 | - if (strchr (line, '#')) |
1598 | - { |
1599 | - adblock_frame_add_private (line, "#"); |
1600 | - return NULL; |
1601 | - } |
1602 | - |
1603 | - /* Got URL blocker rule */ |
1604 | - if (line[0] == '|' && line[1] == '|' ) |
1605 | - { |
1606 | - (void)*line++; |
1607 | - (void)*line++; |
1608 | - return adblock_add_url_pattern ("", "fulluri", line); |
1609 | - } |
1610 | - if (line[0] == '|') |
1611 | - { |
1612 | - (void)*line++; |
1613 | - return adblock_add_url_pattern ("^", "fulluri", line); |
1614 | - } |
1615 | - return adblock_add_url_pattern ("", "uri", line); |
1616 | -} |
1617 | - |
1618 | -static GDateMonth |
1619 | -str_month_name_to_gdate (const gchar* month) |
1620 | -{ |
1621 | - guint i; |
1622 | - const gchar* months[] = { |
1623 | - "", "January", "February", "March", "April", "May", "June", |
1624 | - "July", "August", "September", "October", "November", "December" |
1625 | - }; |
1626 | - |
1627 | - for (i = 0; i < G_N_ELEMENTS (months); i++) |
1628 | - { |
1629 | - if (strncmp (month, months[i], 3) == 0) |
1630 | - return i; |
1631 | - } |
1632 | - return 0; |
1633 | -} |
1634 | - |
1635 | -static gboolean |
1636 | -adblock_file_is_up_to_date (gchar* path) |
1637 | -{ |
1638 | - FILE* file; |
1639 | - gchar line[2000]; |
1640 | - |
1641 | - /* Check a chunk of header for update info */ |
1642 | - if ((file = g_fopen (path, "r"))) |
1643 | - { |
1644 | - gint days_to_expire = 0; |
1645 | - gchar* timestamp = NULL; |
1646 | - guint i; |
1647 | - gboolean found_meta = FALSE; |
1648 | - gint fs_days_elapsed, days_elapsed = 0, least_days; |
1649 | - |
1650 | - for (i = 0; i <= 15; i++) |
1651 | - { |
1652 | - if (!fgets (line, 2000, file)) |
1653 | - break; |
1654 | - |
1655 | - if (strncmp ("! Expires", line, 9) == 0) |
1656 | - { |
1657 | - gchar** parts = g_strsplit (line, " ", 4); |
1658 | - days_to_expire = atoi (parts[2]); |
1659 | - g_strfreev (parts); |
1660 | - found_meta = TRUE; |
1661 | - } |
1662 | - if (strncmp ("! This list expires after", line, 25) == 0) |
1663 | - { |
1664 | - gchar** parts = g_strsplit (line, " ", 7); |
1665 | - |
1666 | - if (strncmp (parts[6], "days", 4) == 0) |
1667 | - days_to_expire = atoi (parts[5]); |
1668 | - if (strncmp (parts[6], "hours", 5) == 0) |
1669 | - days_to_expire = (atoi (parts[5])) / 24; |
1670 | - |
1671 | - g_strfreev (parts); |
1672 | - found_meta = TRUE; |
1673 | - } |
1674 | - |
1675 | - if (strncmp ("! Last mod", line, 10) == 0 |
1676 | - || strncmp ("! Updated", line, 9) == 0) |
1677 | - { |
1678 | - gchar** parts = g_strsplit (line, ":", 2); |
1679 | - timestamp = g_strdup (parts[1] + 1); |
1680 | - g_strchomp (timestamp); |
1681 | - g_strfreev (parts); |
1682 | - found_meta = TRUE; |
1683 | - } |
1684 | - } |
1685 | - |
1686 | - if (!found_meta) |
1687 | - { |
1688 | - g_print ("Adblock: no metadata found in %s (broken download?)\n", path); |
1689 | - return FALSE; |
1690 | - } |
1691 | - |
1692 | - /* query filesystem about file change, maybe there is no update yet |
1693 | - * or there is no "modified" metadata to check, otherwise we will repeatedly |
1694 | - * download files that have no new updates */ |
1695 | - { |
1696 | - GDate* current = g_date_new (); |
1697 | - GDate* fs_mod_date = g_date_new (); |
1698 | - GTimeVal mod_time; |
1699 | - GFile* filter_file = g_file_new_for_path (path); |
1700 | - GFileInfo* info = g_file_query_info (filter_file, "time:modified", 0, NULL, NULL); |
1701 | - |
1702 | - g_file_info_get_modification_time (info, &mod_time); |
1703 | - g_date_set_time_t (current, time (NULL)); |
1704 | - g_date_set_time_val (fs_mod_date, &mod_time); |
1705 | - |
1706 | - fs_days_elapsed = g_date_days_between (fs_mod_date, current); |
1707 | - |
1708 | - g_date_free (current); |
1709 | - g_date_free (fs_mod_date); |
1710 | - } |
1711 | - |
1712 | - /* If there is no update metadata but file is valid, assume one week */ |
1713 | - if ((!days_to_expire && !timestamp) && fs_days_elapsed < 7) |
1714 | - return TRUE; |
1715 | - |
1716 | - if (days_to_expire && timestamp != NULL) |
1717 | - { |
1718 | - GDate* current = g_date_new (); |
1719 | - GDate* mod_date = g_date_new (); |
1720 | - gchar** parts; |
1721 | - gboolean use_dots = FALSE; |
1722 | - |
1723 | - /* Common dates are 20 Mar 2012, 20.08.2012 */ |
1724 | - if (strrchr (timestamp, '.')) |
1725 | - { |
1726 | - use_dots = TRUE; |
1727 | - /* In case of date like '20.08.2012 12:34' |
1728 | - * we should also nuke the time part */ |
1729 | - if (strrchr (timestamp, ' ')) |
1730 | - { |
1731 | - gchar** part = g_strsplit (timestamp, " ", 2); |
1732 | - parts = g_strsplit (part[0], ".", 4); |
1733 | - g_strfreev (part); |
1734 | - } |
1735 | - else |
1736 | - parts = g_strsplit (timestamp, ".", 4); |
1737 | - } |
1738 | - else |
1739 | - parts = g_strsplit (timestamp, " ", 4); |
1740 | - |
1741 | - if (use_dots) |
1742 | - g_date_set_month (mod_date, atoi (parts[1])); |
1743 | - else |
1744 | - g_date_set_month (mod_date, str_month_name_to_gdate (parts[1])); |
1745 | - |
1746 | - /* check if first part is year 201(2) or day */ |
1747 | - if (strncmp (parts[0], "201", 3) == 0) |
1748 | - { |
1749 | - g_date_set_day (mod_date, atoi (parts[2])); |
1750 | - g_date_set_year (mod_date, atoi (parts[0])); |
1751 | - } |
1752 | - else |
1753 | - { |
1754 | - g_date_set_day (mod_date, atoi (parts[0])); |
1755 | - g_date_set_year (mod_date, atoi (parts[2])); |
1756 | - } |
1757 | - g_strfreev (parts); |
1758 | - |
1759 | - g_date_set_time_t (current, time (NULL)); |
1760 | - days_elapsed = g_date_days_between (mod_date, current); |
1761 | - |
1762 | - g_date_free (current); |
1763 | - g_date_free (mod_date); |
1764 | - g_free (timestamp); |
1765 | - } |
1766 | - |
1767 | - /* File from the future? Assume up to date */ |
1768 | - if (days_elapsed < 0) |
1769 | - { |
1770 | - g_print ("Adblock: file %s appears to be from the future," |
1771 | - "check your system clock!\n", path); |
1772 | - return TRUE; |
1773 | - } |
1774 | - |
1775 | - least_days = days_elapsed < fs_days_elapsed ? days_elapsed : fs_days_elapsed; |
1776 | - if (least_days < days_to_expire) |
1777 | - return TRUE; |
1778 | - else |
1779 | - return FALSE; |
1780 | - |
1781 | - return TRUE; |
1782 | - } |
1783 | - return FALSE; |
1784 | -} |
1785 | - |
1786 | -static void |
1787 | -adblock_parse_file (gchar* path) |
1788 | -{ |
1789 | - FILE* file; |
1790 | - gchar line[2000]; |
1791 | - |
1792 | - if ((file = g_fopen (path, "r"))) |
1793 | - { |
1794 | - while (fgets (line, 2000, file)) |
1795 | - adblock_parse_line (line); |
1796 | - fclose (file); |
1797 | - } |
1798 | -} |
1799 | - |
1800 | -static void |
1801 | -adblock_deactivate_tabs (MidoriView* view, |
1802 | - MidoriBrowser* browser, |
1803 | - MidoriExtension* extension) |
1804 | -{ |
1805 | - GtkWidget* web_view = midori_view_get_web_view (view); |
1806 | - |
1807 | - g_signal_handlers_disconnect_by_func ( |
1808 | - web_view, adblock_window_object_cleared_cb, 0); |
1809 | - g_signal_handlers_disconnect_by_func ( |
1810 | - web_view, adblock_populate_popup_cb, extension); |
1811 | - g_signal_handlers_disconnect_by_func ( |
1812 | - web_view, adblock_resource_request_starting_cb, view); |
1813 | - g_signal_handlers_disconnect_by_func ( |
1814 | - web_view, adblock_load_finished_cb, view); |
1815 | - g_signal_handlers_disconnect_by_func ( |
1816 | - web_view, adblock_navigation_policy_decision_requested_cb, extension); |
1817 | -} |
1818 | - |
1819 | -static void |
1820 | -adblock_deactivate_cb (MidoriExtension* extension, |
1821 | - MidoriBrowser* browser) |
1822 | -{ |
1823 | - GList* children; |
1824 | - MidoriApp* app = midori_extension_get_app (extension); |
1825 | - MidoriWebSettings* settings = katze_object_get_object (app, "settings"); |
1826 | - |
1827 | - g_signal_handlers_disconnect_by_func ( |
1828 | - browser, adblock_open_preferences_cb, extension); |
1829 | - g_signal_handlers_disconnect_by_func ( |
1830 | - extension, adblock_deactivate_cb, browser); |
1831 | - g_signal_handlers_disconnect_by_func ( |
1832 | - app, adblock_app_add_browser_cb, extension); |
1833 | - g_signal_handlers_disconnect_by_func ( |
1834 | - browser, adblock_add_tab_cb, extension); |
1835 | - g_signal_handlers_disconnect_by_func ( |
1836 | - browser, adblock_remove_tab_cb, extension); |
1837 | - |
1838 | - children = midori_browser_get_tabs (MIDORI_BROWSER (browser)); |
1839 | - for (; children; children = g_list_next (children)) |
1840 | - adblock_deactivate_tabs (children->data, browser, extension); |
1841 | - g_list_free (children); |
1842 | - |
1843 | - adblock_destroy_db (); |
1844 | - midori_web_settings_remove_style (settings, "adblock-blockcss"); |
1845 | - g_object_unref (settings); |
1846 | -} |
1847 | - |
1848 | -static void |
1849 | -adblock_activate_cb (MidoriExtension* extension, |
1850 | - MidoriApp* app) |
1851 | -{ |
1852 | - KatzeArray* browsers; |
1853 | - MidoriBrowser* browser; |
1854 | - |
1855 | - adblock_reload_rules (extension, FALSE); |
1856 | - |
1857 | - browsers = katze_object_get_object (app, "browsers"); |
1858 | - KATZE_ARRAY_FOREACH_ITEM (browser, browsers) |
1859 | - adblock_app_add_browser_cb (app, browser, extension); |
1860 | - g_signal_connect (app, "add-browser", |
1861 | - G_CALLBACK (adblock_app_add_browser_cb), extension); |
1862 | - |
1863 | - g_object_unref (browsers); |
1864 | -} |
1865 | - |
1866 | -static void |
1867 | -test_adblock_parse (void) |
1868 | -{ |
1869 | - adblock_init_db (); |
1870 | - g_assert (!adblock_parse_line (NULL)); |
1871 | - g_assert (!adblock_parse_line ("!")); |
1872 | - g_assert (!adblock_parse_line ("@@")); |
1873 | - g_assert (!adblock_parse_line ("##")); |
1874 | - g_assert (!adblock_parse_line ("[")); |
1875 | - |
1876 | - g_assert_cmpstr (adblock_parse_line ("+advert/"), ==, "advert/"); |
1877 | - g_assert_cmpstr (adblock_parse_line ("*foo"), ==, "foo"); |
1878 | - g_assert_cmpstr (adblock_parse_line ("f*oo"), ==, "f.*oo"); |
1879 | - g_assert_cmpstr (adblock_parse_line ("?foo"), ==, "\\?foo"); |
1880 | - g_assert_cmpstr (adblock_parse_line ("foo?"), ==, "foo\\?"); |
1881 | - |
1882 | - g_assert_cmpstr (adblock_parse_line (".*foo/bar"), ==, "..*foo/bar"); |
1883 | - g_assert_cmpstr (adblock_parse_line ("http://bla.blub/*"), ==, "http://bla.blub/"); |
1884 | - g_assert_cmpstr (adblock_parse_line ("bag?r[]=*cpa"), ==, "bag\\?r\\[\\]=.*cpa"); |
1885 | - g_assert_cmpstr (adblock_parse_line ("(facebookLike,"), ==, "\\(facebookLike,"); |
1886 | - adblock_destroy_db (); |
1887 | -} |
1888 | - |
1889 | -static void |
1890 | -test_subscription_update (void) |
1891 | -{ |
1892 | - gint temp; |
1893 | - gchar* filename; |
1894 | - |
1895 | - adblock_init_db (); |
1896 | - temp = g_file_open_tmp ("midori_adblock_update_test_XXXXXX", &filename, NULL); |
1897 | - close (temp); |
1898 | - |
1899 | - g_file_set_contents (filename, "", -1, NULL); |
1900 | - g_assert (!adblock_file_is_up_to_date (filename)); |
1901 | - |
1902 | - g_file_set_contents (filename, |
1903 | - "[Adblock Plus 1.1]\n" |
1904 | - "! Checksum: 48f6Qdo4PsNogsurLvQ71w\n" |
1905 | - "! Title: EasyList\n" |
1906 | - "! Last modified: 05 Sep 2010 11:00 UTC\n" |
1907 | - "! This list expires after 48 hours\n", |
1908 | - -1, NULL); |
1909 | - g_assert (!adblock_file_is_up_to_date (filename)); |
1910 | - |
1911 | - g_file_set_contents (filename, |
1912 | - "[Adblock Plus 1.1]\n" |
1913 | - "! Checksum: 48f6Qdo4PsNogsurLvQ71w\n" |
1914 | - "! Title: EasyList\n" |
1915 | - "! Last modified: 05.09.2010 11:00 UTC\n" |
1916 | - "! Expires: 2 days (update frequency)\n", |
1917 | - -1, NULL); |
1918 | - g_assert (!adblock_file_is_up_to_date (filename)); |
1919 | - |
1920 | - g_file_set_contents (filename, |
1921 | - "[Adblock Plus 1.1]\n" |
1922 | - "! Checksum: 48f6Qdo4PsNogsurLvQ71w\n" |
1923 | - "! Title: EasyList\n" |
1924 | - "! Updated: 05 Nov 2014 11:00 UTC\n" |
1925 | - "! Expires: 5 days (update frequency)\n", |
1926 | - -1, NULL); |
1927 | - g_assert (adblock_file_is_up_to_date (filename)); |
1928 | - |
1929 | - g_file_set_contents (filename, |
1930 | - "[Adblock]\n" |
1931 | - "! dutchblock v3\n" |
1932 | - "! This list expires after 14 days\n" |
1933 | - "|http://b*.mookie1.com/\n", |
1934 | - -1, NULL); |
1935 | - g_assert (adblock_file_is_up_to_date (filename)); |
1936 | - |
1937 | - g_file_set_contents (filename, |
1938 | - "[Adblock Plus 2.0]\n" |
1939 | - "! Checksum: S4reE8XaYTtAFxe2RjgBPg\n" |
1940 | - "! Last modification time (GMT): 2012.11.05 13:33\n" |
1941 | - "! Expires: 5 days (update frequency)\n", |
1942 | - -1, NULL); |
1943 | - g_assert (!adblock_file_is_up_to_date (filename)); |
1944 | - |
1945 | - g_file_set_contents (filename, |
1946 | - "[Adblock Plus 2.0]\n" |
1947 | - "! Checksum: S4reE8XaYTtAFxe2RjgBPg\n" |
1948 | - "! Last modification time (GMT): 2012.11.05 13:33\n", |
1949 | - -1, NULL); |
1950 | - g_assert (!adblock_file_is_up_to_date (filename)); |
1951 | - |
1952 | - g_unlink (filename); |
1953 | - g_free (filename); |
1954 | - adblock_destroy_db (); |
1955 | -} |
1956 | - |
1957 | -static void |
1958 | -test_adblock_pattern (void) |
1959 | -{ |
1960 | - gint temp; |
1961 | - gchar* filename; |
1962 | - |
1963 | - adblock_init_db (); |
1964 | - temp = g_file_open_tmp ("midori_adblock_match_test_XXXXXX", &filename, NULL); |
1965 | - |
1966 | - /* TODO: Update some tests and add new ones. */ |
1967 | - g_file_set_contents (filename, |
1968 | - "*ads.foo.bar*\n" |
1969 | - "*ads.bogus.name*\n" |
1970 | - "||^http://ads.bla.blub/*\n" |
1971 | - "|http://ads.blub.boing/*$domain=xxx.com\n" |
1972 | - "engine.adct.ru/*?\n" |
1973 | - "/addyn|*|adtech;\n" |
1974 | - "doubleclick.net/pfadx/*.mtvi\n" |
1975 | - "objects.tremormedia.com/embed/xml/*.xml?r=\n" |
1976 | - "videostrip.com^*/admatcherclient.\n" |
1977 | - "test.dom/test?var\n" |
1978 | - "/adpage.\n" |
1979 | - "br.gcl.ru/cgi-bin/br/", |
1980 | - -1, NULL); |
1981 | - |
1982 | - adblock_parse_file (filename); |
1983 | - |
1984 | - g_test_timer_start (); |
1985 | - g_assert (adblock_is_matched ("http://www.engadget.com/_uac/adpage.html", "")); |
1986 | - g_assert (adblock_is_matched ("http://test.dom/test?var=1", "")); |
1987 | - g_assert (adblock_is_matched ("http://ads.foo.bar/teddy", "")); |
1988 | - g_assert (!adblock_is_matched ("http://ads.fuu.bar/teddy", "")); |
1989 | - g_assert (adblock_is_matched ("https://ads.bogus.name/blub", "")); |
1990 | - g_assert (adblock_is_matched ("http://ads.bla.blub/kitty", "")); |
1991 | - g_assert (adblock_is_matched ("http://ads.blub.boing/soda", "")); |
1992 | - g_assert (!adblock_is_matched ("http://ads.foo.boing/beer", "")); |
1993 | - g_assert (adblock_is_matched ("https://testsub.engine.adct.ru/test?id=1", "")); |
1994 | - if (USE_PATTERN_MATCHING) |
1995 | - g_assert (adblock_is_matched ("http://test.ltd/addyn/test/test?var=adtech;&var2=1", "")); |
1996 | - g_assert (adblock_is_matched ("http://add.doubleclick.net/pfadx/aaaa.mtvi", "")); |
1997 | - g_assert (!adblock_is_matched ("http://add.doubleclick.net/pfadx/aaaa.mtv", "")); |
1998 | - g_assert (adblock_is_matched ("http://objects.tremormedia.com/embed/xml/list.xml?r=", "")); |
1999 | - g_assert (!adblock_is_matched ("http://qq.videostrip.c/sub/admatcherclient.php", "")); |
2000 | - g_assert (adblock_is_matched ("http://qq.videostrip.com/sub/admatcherclient.php", "")); |
2001 | - g_assert (adblock_is_matched ("http://qq.videostrip.com/sub/admatcherclient.php", "")); |
2002 | - g_assert (adblock_is_matched ("http://br.gcl.ru/cgi-bin/br/test", "")); |
2003 | - g_assert (!adblock_is_matched ("https://bugs.webkit.org/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&long_desc_type=substring&long_desc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&keywords_type=allwords&keywords=&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailassigned_to1=1&emailtype1=substring&email1=&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailtype2=substring&email2=&bugidtype=include&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&query_based_on=gtkport&field0-0-0=keywords&type0-0-0=anywordssubstr&value0-0-0=Gtk%20Cairo%20soup&field0-0-1=short_desc&type0-0-1=anywordssubstr&value0-0-1=Gtk%20Cairo%20soup%20autoconf%20automake%20autotool&field0-0-2=component&type0-0-2=equals&value0-0-2=WebKit%20Gtk", "")); |
2004 | - g_assert (!adblock_is_matched ("http://www.engadget.com/2009/09/24/google-hits-android-rom-modder-with-a-cease-and-desist-letter/", "")); |
2005 | - g_assert (!adblock_is_matched ("http://karibik-invest.com/es/bienes_raices/search.php?sqT=19&sqN=&sqMp=&sqL=0&qR=1&sqMb=&searchMode=1&action=B%FAsqueda", "")); |
2006 | - g_assert (!adblock_is_matched ("http://google.com", "")); |
2007 | - |
2008 | - g_print ("Search took %f seconds\n", g_test_timer_elapsed ()); |
2009 | - |
2010 | - close (temp); |
2011 | - g_unlink (filename); |
2012 | - adblock_destroy_db (); |
2013 | -} |
2014 | - |
2015 | -void |
2016 | -extension_test (void) |
2017 | -{ |
2018 | - g_test_add_func ("/extensions/adblock/parse", test_adblock_parse); |
2019 | - g_test_add_func ("/extensions/adblock/pattern", test_adblock_pattern); |
2020 | - g_test_add_func ("/extensions/adblock/update", test_subscription_update); |
2021 | -} |
2022 | - |
2023 | -MidoriExtension* |
2024 | -extension_init (void) |
2025 | -{ |
2026 | - MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION, |
2027 | - "name", _("Advertisement blocker"), |
2028 | - "description", _("Block advertisements according to a filter list"), |
2029 | - "version", "0.6" MIDORI_VERSION_SUFFIX, |
2030 | - "authors", "Christian Dywan <christian@twotoasts.de>", |
2031 | - NULL); |
2032 | - midori_extension_install_string_list (extension, "filters", NULL, G_MAXSIZE); |
2033 | - |
2034 | - g_signal_connect (extension, "activate", |
2035 | - G_CALLBACK (adblock_activate_cb), NULL); |
2036 | - |
2037 | - return extension; |
2038 | -} |
2039 | |
2040 | === added file 'extensions/adblock/config.vala' |
2041 | --- extensions/adblock/config.vala 1970-01-01 00:00:00 +0000 |
2042 | +++ extensions/adblock/config.vala 2014-02-20 20:31:17 +0000 |
2043 | @@ -0,0 +1,107 @@ |
2044 | +/* |
2045 | + Copyright (C) 2014 Christian Dywan <christian@twotoasts.de> |
2046 | + |
2047 | + This library is free software; you can redistribute it and/or |
2048 | + modify it under the terms of the GNU Lesser General Public |
2049 | + License as published by the Free Software Foundation; either |
2050 | + version 2.1 of the License, or (at your option) any later version. |
2051 | + |
2052 | + See the file COPYING for the full license text. |
2053 | +*/ |
2054 | + |
2055 | +namespace Adblock { |
2056 | + public class Config : GLib.Object { |
2057 | + List<Subscription> subscriptions; |
2058 | + string? path; |
2059 | + KeyFile keyfile; |
2060 | + Subscription? custom; |
2061 | + |
2062 | + public Config (string? path) { |
2063 | + subscriptions = new GLib.List<Subscription> (); |
2064 | + |
2065 | + this.path = path; |
2066 | + if (path == null) |
2067 | + return; |
2068 | + |
2069 | + string custom_list = GLib.Path.build_filename (path, "custom.list"); |
2070 | + try { |
2071 | + custom = new Subscription (Filename.to_uri (custom_list, null)); |
2072 | + subscriptions.append (custom); |
2073 | + } catch (Error error) { |
2074 | + custom = null; |
2075 | + warning ("Failed to add custom list %s: %s", custom_list, error.message); |
2076 | + } |
2077 | + |
2078 | + string filename = GLib.Path.build_filename (path, "config"); |
2079 | + keyfile = new GLib.KeyFile (); |
2080 | + try { |
2081 | + keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE); |
2082 | + string[] filters = keyfile.get_string_list ("settings", "filters"); |
2083 | + foreach (string filter in filters) { |
2084 | + bool active = false; |
2085 | + string uri = filter; |
2086 | + if (filter.has_prefix ("http-")) |
2087 | + uri = "http:" + filter.substring (6); |
2088 | + else if (filter.has_prefix ("file-")) |
2089 | + uri = "file:" + filter.substring (6); |
2090 | + else if (filter.has_prefix ("https-")) |
2091 | + uri = "https:" + filter.substring (7); |
2092 | + else |
2093 | + active = true; |
2094 | + Subscription sub = new Subscription (uri); |
2095 | + sub.active = active; |
2096 | + sub.add_feature (new Updater ()); |
2097 | + sub.notify["active"].connect (active_changed); |
2098 | + subscriptions.append (sub); |
2099 | + } |
2100 | + } catch (FileError.NOENT exist_error) { |
2101 | + /* It's no error if no config file exists */ |
2102 | + } catch (GLib.Error settings_error) { |
2103 | + warning ("Error reading settings from %s: %s\n", filename, settings_error.message); |
2104 | + } |
2105 | + |
2106 | + size = subscriptions.length (); |
2107 | + } |
2108 | + |
2109 | + void active_changed (Object subscription, ParamSpec pspec) { |
2110 | + var filters = new StringBuilder (); |
2111 | + foreach (var sub in subscriptions) { |
2112 | + if (sub == custom) |
2113 | + continue; |
2114 | + if (sub.uri.has_prefix ("http:") && !sub.active) |
2115 | + filters.append ("http-" + sub.uri.substring (4)); |
2116 | + else if (sub.uri.has_prefix ("file:") && !sub.active) |
2117 | + filters.append ("file-" + sub.uri.substring (4)); |
2118 | + else if (sub.uri.has_prefix ("https:") && !sub.active) |
2119 | + filters.append ("https-" + sub.uri.substring (5)); |
2120 | + else |
2121 | + filters.append (sub.uri); |
2122 | + filters.append_c (';'); |
2123 | + } |
2124 | + |
2125 | + string[] list = (filters.str.slice (0, -1)).split (";"); |
2126 | + keyfile.set_string_list ("settings", "filters", list); |
2127 | + try { |
2128 | + string filename = GLib.Path.build_filename (path, "config"); |
2129 | + FileUtils.set_contents (filename, keyfile.to_data ()); |
2130 | + } catch (Error error) { |
2131 | + warning ("Failed to save settings: %s", error.message); |
2132 | + } |
2133 | + } |
2134 | + |
2135 | + public void add_custom_rule (string rule) { |
2136 | + try { |
2137 | + var file = File.new_for_uri (custom.uri); |
2138 | + file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data); |
2139 | + } catch (Error error) { |
2140 | + warning ("Failed to add custom rule: %s", error.message); |
2141 | + } |
2142 | + } |
2143 | + |
2144 | + /* foreach support */ |
2145 | + public new Subscription? get (uint index) { |
2146 | + return subscriptions.nth_data (index); |
2147 | + } |
2148 | + public uint size { get; private set; } |
2149 | + } |
2150 | +} |
2151 | |
2152 | === added file 'extensions/adblock/element.vala' |
2153 | --- extensions/adblock/element.vala 1970-01-01 00:00:00 +0000 |
2154 | +++ extensions/adblock/element.vala 2014-02-20 20:31:17 +0000 |
2155 | @@ -0,0 +1,36 @@ |
2156 | +/* |
2157 | + Copyright (C) 2014 Christian Dywan <christian@twotoasts.de> |
2158 | + |
2159 | + This library is free software; you can redistribute it and/or |
2160 | + modify it under the terms of the GNU Lesser General Public |
2161 | + License as published by the Free Software Foundation; either |
2162 | + version 2.1 of the License, or (at your option) any later version. |
2163 | + |
2164 | + See the file COPYING for the full license text. |
2165 | +*/ |
2166 | + |
2167 | +namespace Adblock { |
2168 | + public class Element : Feature { |
2169 | + public HashTable<string, string> blockcssprivate; |
2170 | + bool debug_element; |
2171 | + |
2172 | + public Element () { |
2173 | + base (); |
2174 | + debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? ""); |
2175 | + } |
2176 | + |
2177 | + public override void clear () { |
2178 | + blockcssprivate = new HashTable<string, string> (str_hash, str_equal); |
2179 | + } |
2180 | + |
2181 | + public string? lookup (string domain) { |
2182 | + return blockcssprivate.lookup (domain); |
2183 | + } |
2184 | + |
2185 | + public void insert (string domain, string value) { |
2186 | + if (debug_element) |
2187 | + stdout.printf ("Element to be blocked %s => %s\n", domain, value); |
2188 | + blockcssprivate.insert (domain, value); |
2189 | + } |
2190 | + } |
2191 | +} |
2192 | |
2193 | === added file 'extensions/adblock/extension.vala' |
2194 | --- extensions/adblock/extension.vala 1970-01-01 00:00:00 +0000 |
2195 | +++ extensions/adblock/extension.vala 2014-02-20 20:31:17 +0000 |
2196 | @@ -0,0 +1,545 @@ |
2197 | +/* |
2198 | + Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de> |
2199 | + Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com> |
2200 | + |
2201 | + This library is free software; you can redistribute it and/or |
2202 | + modify it under the terms of the GNU Lesser General Public |
2203 | + License as published by the Free Software Foundation; either |
2204 | + version 2.1 of the License, or (at your option) any later version. |
2205 | + |
2206 | + See the file COPYING for the full license text. |
2207 | +*/ |
2208 | + |
2209 | +namespace Adblock { |
2210 | + public enum Directive { |
2211 | + ALLOW, |
2212 | + BLOCK |
2213 | + } |
2214 | + |
2215 | + public class Extension : Midori.Extension { |
2216 | + Config config; |
2217 | + HashTable<string, Directive?> cache; |
2218 | + |
2219 | +#if HAVE_WEBKIT2 |
2220 | + public Extension (WebKit.WebExtension web_extension) { |
2221 | + init (); |
2222 | + web_extension.page_created.connect (page_created); |
2223 | + } |
2224 | + |
2225 | + void page_created (WebKit.WebPage web_page) { |
2226 | + web_page.send_request.connect (send_request); |
2227 | + } |
2228 | + |
2229 | + bool send_request (WebKit.WebPage web_page, WebKit.URIRequest request, WebKit.URIResponse? redirected_response) { |
2230 | + return request_handled (web_page.uri, request.uri); |
2231 | + } |
2232 | +#else |
2233 | + public Extension () { |
2234 | + GLib.Object (name: _("Advertisement blocker"), |
2235 | + description: _("Block advertisements according to a filter list"), |
2236 | + version: "2.0", |
2237 | + authors: "Christian Dywan <christian@twotoasts.de>"); |
2238 | + activate.connect (extension_activated); |
2239 | + open_preferences.connect (extension_preferences); |
2240 | + } |
2241 | + |
2242 | + void extension_preferences () { |
2243 | + open_dialog (null); |
2244 | + } |
2245 | + |
2246 | + void open_dialog (string? uri) { |
2247 | + var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"), |
2248 | + null, |
2249 | +#if !HAVE_GTK3 |
2250 | + Gtk.DialogFlags.NO_SEPARATOR | |
2251 | +#endif |
2252 | + Gtk.DialogFlags.DESTROY_WITH_PARENT, |
2253 | + Gtk.STOCK_HELP, Gtk.ResponseType.HELP, |
2254 | + Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE); |
2255 | +#if HAVE_GTK3 |
2256 | + dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button"); |
2257 | +#endif |
2258 | + dialog.set_icon_name (Gtk.STOCK_PROPERTIES); |
2259 | + dialog.set_response_sensitive (Gtk.ResponseType.HELP, false); |
2260 | + |
2261 | + var hbox = new Gtk.HBox (false, 0); |
2262 | + (dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12); |
2263 | + var vbox = new Gtk.VBox (false, 0); |
2264 | + hbox.pack_start (vbox, true, true, 4); |
2265 | + var button = new Gtk.Label (null); |
2266 | + string description = """ |
2267 | + Type the address of a preconfigured filter list in the text entry |
2268 | + and click "Add" to add it to the list. |
2269 | + You can find more lists at %s %s. |
2270 | + """.printf ( |
2271 | + "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>", |
2272 | + "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>"); |
2273 | + button.activate_link.connect ((uri)=>{ |
2274 | + var browser = Midori.Browser.get_for_widget (button); |
2275 | + var view = browser.add_uri (uri); |
2276 | + browser.tab = view; |
2277 | + return true; |
2278 | + }); |
2279 | + button.set_markup (description); |
2280 | + button.set_line_wrap (true); |
2281 | + vbox.pack_start (button, false, false, 4); |
2282 | + |
2283 | + var entry = new Gtk.Entry (); |
2284 | + if (uri != null) |
2285 | + entry.set_text (uri); |
2286 | + vbox.pack_start (entry, false, false, 4); |
2287 | + |
2288 | + var liststore = new Gtk.ListStore (1, typeof (Subscription)); |
2289 | + var treeview = new Gtk.TreeView.with_model (liststore); |
2290 | + treeview.set_headers_visible (false); |
2291 | + var column = new Gtk.TreeViewColumn (); |
2292 | + var renderer_toggle = new Gtk.CellRendererToggle (); |
2293 | + column.pack_start (renderer_toggle, false); |
2294 | + column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => { |
2295 | + Subscription sub; |
2296 | + liststore.get (iter, 0, out sub); |
2297 | + renderer.set ("active", sub.active, |
2298 | + "sensitive", !sub.uri.has_suffix ("custom.list")); |
2299 | + }); |
2300 | + renderer_toggle.toggled.connect ((path) => { |
2301 | + Gtk.TreeIter iter; |
2302 | + if (liststore.get_iter_from_string (out iter, path)) { |
2303 | + Subscription sub; |
2304 | + liststore.get (iter, 0, out sub); |
2305 | + sub.active = !sub.active; |
2306 | + } |
2307 | + }); |
2308 | + treeview.append_column (column); |
2309 | + |
2310 | + column = new Gtk.TreeViewColumn (); |
2311 | + var renderer_text = new Gtk.CellRendererText (); |
2312 | + column.pack_start (renderer_text, false); |
2313 | + renderer_text.set ("editable", true); |
2314 | + // TODO: renderer_text.edited.connect |
2315 | + column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => { |
2316 | + Subscription sub; |
2317 | + liststore.get (iter, 0, out sub); |
2318 | + renderer.set ("text", sub.uri); |
2319 | + }); |
2320 | + treeview.append_column (column); |
2321 | + |
2322 | + var scrolled = new Gtk.ScrolledWindow (null, null); |
2323 | + scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); |
2324 | + scrolled.add (treeview); |
2325 | + vbox.pack_start (scrolled); |
2326 | + |
2327 | + foreach (Subscription sub in config) |
2328 | + liststore.insert_with_values (null, 0, 0, sub); |
2329 | + // TODO: row-inserted row-changed row-deleted |
2330 | + // TODO vbox with add/ edit/ remove/ down/ up |
2331 | + |
2332 | + dialog.get_content_area ().show_all (); |
2333 | + |
2334 | + dialog.response.connect ((response)=>{ dialog.destroy (); }); |
2335 | + dialog.show (); |
2336 | + } |
2337 | + |
2338 | + void extension_activated (Midori.App app) { |
2339 | + init (); |
2340 | + foreach (var browser in app.get_browsers ()) |
2341 | + browser_added (browser); |
2342 | + app.add_browser.connect (browser_added); |
2343 | + } |
2344 | + |
2345 | + void browser_added (Midori.Browser browser) { |
2346 | + foreach (var tab in browser.get_tabs ()) |
2347 | + tab_added (tab); |
2348 | + browser.add_tab.connect (tab_added); |
2349 | + } |
2350 | + |
2351 | + void tab_added (Midori.View view) { |
2352 | + view.web_view.resource_request_starting.connect (resource_requested); |
2353 | + view.web_view.navigation_policy_decision_requested.connect (navigation_requested); |
2354 | + view.notify["load-status"].connect ((pspec) => { |
2355 | + if (view.load_status == Midori.LoadStatus.FINISHED) |
2356 | + inject_css (view, view.uri); |
2357 | + }); |
2358 | + view.context_menu.connect (context_menu); |
2359 | + } |
2360 | + |
2361 | + void context_menu (WebKit.HitTestResult hit_test_result, Midori.ContextAction menu) { |
2362 | + string label, uri; |
2363 | + if ((hit_test_result.context & WebKit.HitTestResultContext.IMAGE) != 0) { |
2364 | + label = _("Bl_ock image"); |
2365 | + uri = hit_test_result.image_uri; |
2366 | + } else if ((hit_test_result.context & WebKit.HitTestResultContext.LINK) != 0) { |
2367 | + label = _("Bl_ock link"); |
2368 | + uri = hit_test_result.link_uri; |
2369 | + } else |
2370 | + return; |
2371 | + var action = new Gtk.Action ("BlockElement", label, null, null); |
2372 | + action.activate.connect ((action) => { |
2373 | + edit_rule_dialog (uri); |
2374 | + }); |
2375 | + menu.add (action); |
2376 | + } |
2377 | + |
2378 | + void edit_rule_dialog (string uri) { |
2379 | + var dialog = new Gtk.Dialog.with_buttons (_("Edit rule"), |
2380 | + null, |
2381 | +#if !HAVE_GTK3 |
2382 | + Gtk.DialogFlags.NO_SEPARATOR | |
2383 | +#endif |
2384 | + Gtk.DialogFlags.DESTROY_WITH_PARENT, |
2385 | + Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, |
2386 | + Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT); |
2387 | + dialog.set_icon_name (Gtk.STOCK_ADD); |
2388 | + dialog.resizable = false; |
2389 | + |
2390 | + var hbox = new Gtk.HBox (false, 8); |
2391 | + var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL); |
2392 | + hbox.border_width = 5; |
2393 | + var label = new Gtk.Label.with_mnemonic (_("_Rule:")); |
2394 | + sizegroup.add_widget (label); |
2395 | + hbox.pack_start (label, false, false, 0); |
2396 | + (dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0); |
2397 | + |
2398 | + var entry = new Gtk.Entry (); |
2399 | + sizegroup.add_widget (entry); |
2400 | + entry.activates_default = true; |
2401 | + entry.set_text (uri); |
2402 | + hbox.pack_start (entry, true, true, 0); |
2403 | + |
2404 | + dialog.get_content_area ().show_all (); |
2405 | + |
2406 | + dialog.set_default_response (Gtk.ResponseType.ACCEPT); |
2407 | + if (dialog.run () != Gtk.ResponseType.ACCEPT) |
2408 | + return; |
2409 | + |
2410 | + string new_rule = entry.get_text (); |
2411 | + dialog.destroy (); |
2412 | + config.add_custom_rule (new_rule); |
2413 | + } |
2414 | + |
2415 | + |
2416 | + void resource_requested (WebKit.WebView web_view, WebKit.WebFrame frame, |
2417 | + WebKit.WebResource resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) { |
2418 | + |
2419 | + if (request_handled (web_view.uri, request.uri)) |
2420 | + request.set_uri ("about:blank"); |
2421 | + } |
2422 | + |
2423 | + bool navigation_requested (WebKit.WebFrame frame, WebKit.NetworkRequest request, |
2424 | + WebKit.WebNavigationAction action, WebKit.WebPolicyDecision decision) { |
2425 | + |
2426 | + string uri = request.uri; |
2427 | + if (uri.has_prefix ("abp:")) { |
2428 | + uri = uri.replace ("abp://", "abp:"); |
2429 | + if (uri.has_prefix ("abp:subscribe?location=")) { |
2430 | + /* abp://subscripe?location=http://example.com&title=foo */ |
2431 | + string[] parts = uri.substring (23, -1).split ("&", 2); |
2432 | + decision.ignore (); |
2433 | + open_dialog (parts[0]); |
2434 | + return true; |
2435 | + } |
2436 | + } |
2437 | + return false; |
2438 | + } |
2439 | + |
2440 | + void inject_css (Midori.View view, string page_uri) { |
2441 | + /* Don't block ads on internal pages */ |
2442 | + if (!Midori.URI.is_http (page_uri)) |
2443 | + return; |
2444 | + string domain = Midori.URI.parse_hostname (page_uri, null); |
2445 | + string[] subdomains = domain.split ("."); |
2446 | + if (subdomains == null) |
2447 | + return; |
2448 | + int cnt = subdomains.length - 1; |
2449 | + var subdomain = new StringBuilder (subdomains[cnt]); |
2450 | + subdomain.prepend_c ('.'); |
2451 | + cnt--; |
2452 | + var code = new StringBuilder (); |
2453 | + bool debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? ""); |
2454 | + string hider_css; |
2455 | + |
2456 | + /* Hide elements that were blocked, otherwise we will get "broken image" icon */ |
2457 | + cache.foreach ((key, val) => { |
2458 | + if (val == Adblock.Directive.BLOCK) |
2459 | + code.append ("img[src*=\"%s\"] , iframe[src*=\"%s\"] , ".printf (key, key)); |
2460 | + }); |
2461 | + if (debug_element) |
2462 | + hider_css = " { background-color: red; border: 4px solid green; }"; |
2463 | + else |
2464 | + hider_css = " { visiblility: hidden; width: 0; height: 0; }"; |
2465 | + |
2466 | + code.truncate (code.len -3); |
2467 | + code.append (hider_css); |
2468 | + if (debug_element) |
2469 | + stdout.printf ("hider css: %s\n", code.str); |
2470 | + view.inject_stylesheet (code.str); |
2471 | + |
2472 | + code.erase (); |
2473 | + int blockscnt = 0; |
2474 | + while (cnt >= 0) { |
2475 | + subdomain.prepend (subdomains[cnt]); |
2476 | + string? style = null; |
2477 | + foreach (Subscription sub in config) { |
2478 | + foreach (var feature in sub) { |
2479 | + if (feature is Adblock.Element) { |
2480 | + style = (feature as Adblock.Element).lookup (subdomain.str); |
2481 | + break; |
2482 | + } |
2483 | + } |
2484 | + } |
2485 | + if (style != null) { |
2486 | + code.append (style); |
2487 | + code.append_c (','); |
2488 | + blockscnt++; |
2489 | + } |
2490 | + subdomain.prepend_c ('.'); |
2491 | + cnt--; |
2492 | + } |
2493 | + if (blockscnt == 0) |
2494 | + return; |
2495 | + code.truncate (code.len - 1); |
2496 | + |
2497 | + if (debug_element) |
2498 | + hider_css = " { background-color: red !important; border: 4px solid green !important; }"; |
2499 | + else |
2500 | + hider_css = " { display: none !important }"; |
2501 | + |
2502 | + code.append (hider_css); |
2503 | + view.inject_stylesheet (code.str); |
2504 | + if (debug_element) |
2505 | + stdout.printf ("css: %s\n", code.str); |
2506 | + } |
2507 | +#endif |
2508 | + |
2509 | + internal void init () { |
2510 | + debug ("Adblock2"); |
2511 | + |
2512 | + string config_dir = Midori.Paths.get_extension_config_dir ("libadblock.so"); |
2513 | + config = new Config (config_dir); |
2514 | + reload_rules (); |
2515 | + } |
2516 | + |
2517 | + void reload_rules () { |
2518 | + cache = new HashTable<string, Directive?> (str_hash, str_equal); |
2519 | + foreach (Subscription sub in config) { |
2520 | + try { |
2521 | + sub.parse (); |
2522 | + } catch (GLib.Error error) { |
2523 | + warning ("Error parsing %s: %s", sub.uri, error.message); |
2524 | + } |
2525 | + } |
2526 | + } |
2527 | + |
2528 | + bool request_handled (string page_uri, string request_uri) { |
2529 | + /* Always allow the main page */ |
2530 | + if (request_uri == page_uri) |
2531 | + return false; |
2532 | + |
2533 | + Directive? directive = cache.lookup (request_uri); |
2534 | + if (directive == null) { |
2535 | + foreach (Subscription sub in config) { |
2536 | + directive = sub.get_directive (request_uri, page_uri); |
2537 | + if (directive != null) |
2538 | + break; |
2539 | + } |
2540 | + if (directive == null) |
2541 | + directive = Directive.ALLOW; |
2542 | + cache.insert (request_uri, directive); |
2543 | + } |
2544 | + return directive == Directive.BLOCK; |
2545 | + } |
2546 | + } |
2547 | + |
2548 | + static void debug (string format, ...) { |
2549 | + bool debug_match = "adblock:match" in (Environment.get_variable ("MIDORI_DEBUG") ?? ""); |
2550 | + if (!debug_match) |
2551 | + return; |
2552 | + |
2553 | + var args = va_list (); |
2554 | + stdout.vprintf (format + "\n", args); |
2555 | + } |
2556 | + |
2557 | + internal static string? fixup_regex (string prefix, string? src) { |
2558 | + if (src == null) |
2559 | + return null; |
2560 | + |
2561 | + var fixed = new StringBuilder (); |
2562 | + fixed.append(prefix); |
2563 | + |
2564 | + uint i = 0; |
2565 | + if (src[0] == '*') |
2566 | + i++; |
2567 | + uint l = src.length; |
2568 | + while (i < l) { |
2569 | + char c = src[i]; |
2570 | + switch (c) { |
2571 | + case '*': |
2572 | + fixed.append (".*"); break; |
2573 | + case '|': |
2574 | + case '^': |
2575 | + case '+': |
2576 | + break; |
2577 | + case '?': |
2578 | + case '[': |
2579 | + case ']': |
2580 | + fixed.append_printf ("\\%c", c); break; |
2581 | + default: |
2582 | + fixed.append_c (c); break; |
2583 | + } |
2584 | + i++; |
2585 | + } |
2586 | + return fixed.str; |
2587 | + } |
2588 | +} |
2589 | + |
2590 | +#if HAVE_WEBKIT2 |
2591 | +Adblock.Extension? filter; |
2592 | +public static void webkit_web_extension_initialize (WebKit.WebExtension web_extension) { |
2593 | + filter = new Adblock.Extension (web_extension); |
2594 | +} |
2595 | +#else |
2596 | +public Midori.Extension extension_init () { |
2597 | + return new Adblock.Extension (); |
2598 | +} |
2599 | +#endif |
2600 | + |
2601 | +#if !HAVE_WEBKIT2 |
2602 | +struct TestCaseLine { |
2603 | + public string line; |
2604 | + public string fixed; |
2605 | +} |
2606 | + |
2607 | +const TestCaseLine[] lines = { |
2608 | + { null, null }, |
2609 | + { "!", "!" }, |
2610 | + { "@@", "@@" }, |
2611 | + { "##", "##" }, |
2612 | + { "[", "\\[" }, |
2613 | + { "+advert/", "advert/" }, |
2614 | + { "*foo", "foo" }, |
2615 | + { "f*oo", "f.*oo" }, |
2616 | + { "?foo", "\\?foo" }, |
2617 | + { "foo?", "foo\\?" }, |
2618 | + { ".*foo/bar", "..*foo/bar" }, |
2619 | + { "http://bla.blub/*", "http://bla.blub/.*" }, |
2620 | + { "bag?r[]=*cpa", "bag\\?r\\[\\]=.*cpa" }, |
2621 | + { "(facebookLike,", "(facebookLike," }, |
2622 | +}; |
2623 | + |
2624 | +void test_adblock_fixup_regexp () { |
2625 | + foreach (var line in lines) { |
2626 | + Katze.assert_str_equal (line.line, Adblock.fixup_regex ("", line.line), line.fixed); |
2627 | + } |
2628 | +} |
2629 | + |
2630 | +struct TestCasePattern { |
2631 | + public string uri; |
2632 | + public Adblock.Directive directive; |
2633 | +} |
2634 | + |
2635 | +const TestCasePattern[] patterns = { |
2636 | + { "http://www.engadget.com/_uac/adpage.html", Adblock.Directive.BLOCK }, |
2637 | + { "http://test.dom/test?var=1", Adblock.Directive.BLOCK }, |
2638 | + { "http://ads.foo.bar/teddy", Adblock.Directive.BLOCK }, |
2639 | + { "http://ads.fuu.bar/teddy", Adblock.Directive.ALLOW }, |
2640 | + { "https://ads.bogus.name/blub", Adblock.Directive.BLOCK }, |
2641 | + // FIXME { "http://ads.bla.blub/kitty", Adblock.Directive.BLOCK }, |
2642 | + // FIXME { "http://ads.blub.boing/soda", Adblock.Directive.BLOCK }, |
2643 | + { "http://ads.foo.boing/beer", Adblock.Directive.ALLOW }, |
2644 | + { "https://testsub.engine.adct.ru/test?id=1", Adblock.Directive.BLOCK }, |
2645 | + { "http://test.ltd/addyn/test/test?var=adtech;&var2=1", Adblock.Directive.BLOCK }, |
2646 | + { "http://add.doubleclick.net/pfadx/aaaa.mtvi", Adblock.Directive.BLOCK }, |
2647 | + { "http://add.doubleclick.net/pfadx/aaaa.mtv", Adblock.Directive.ALLOW }, |
2648 | + { "http://objects.tremormedia.com/embed/xml/list.xml?r=", Adblock.Directive.BLOCK }, |
2649 | + { "http://qq.videostrip.c/sub/admatcherclient.php", Adblock.Directive.ALLOW }, |
2650 | + { "http://qq.videostrip.com/sub/admatcherclient.php", Adblock.Directive.BLOCK }, |
2651 | + { "http://qq.videostrip.com/sub/admatcherclient.php", Adblock.Directive.BLOCK }, |
2652 | + { "http://br.gcl.ru/cgi-bin/br/test", Adblock.Directive.BLOCK }, |
2653 | + { "https://bugs.webkit.org/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&long_desc_type=substring&long_desc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&keywords_type=allwords&keywords=&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailassigned_to1=1&emailtype1=substring&email1=&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailtype2=substring&email2=&bugidtype=include&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&query_based_on=gtkport&field0-0-0=keywords&type0-0-0=anywordssubstr&value0-0-0=Gtk%20Cairo%20soup&field0-0-1=short_desc&type0-0-1=anywordssubstr&value0-0-1=Gtk%20Cairo%20soup%20autoconf%20automake%20autotool&field0-0-2=component&type0-0-2=equals&value0-0-2=WebKit%20Gtk", Adblock.Directive.ALLOW }, |
2654 | + { "http://www.engadget.com/2009/09/24/google-hits-android-rom-modder-with-a-cease-and-desist-letter/", Adblock.Directive.ALLOW }, |
2655 | + { "http://karibik-invest.com/es/bienes_raices/search.php?sqT=19&sqN=&sqMp=&sqL=0&qR=1&sqMb=&searchMode=1&action=B%FAsqueda", Adblock.Directive.ALLOW }, |
2656 | + { "http://google.com", Adblock.Directive.ALLOW }, |
2657 | +}; |
2658 | + |
2659 | +string pretty_directive (Adblock.Directive? directive) { |
2660 | + if (directive == null) |
2661 | + return "none"; |
2662 | + return directive.to_string (); |
2663 | +} |
2664 | + |
2665 | +void test_adblock_pattern () { |
2666 | + string path = Midori.Paths.get_res_filename ("adblock.list"); |
2667 | + string uri; |
2668 | + try { |
2669 | + uri = Filename.to_uri (path, null); |
2670 | + } catch (Error error) { |
2671 | + GLib.error (error.message); |
2672 | + } |
2673 | + var sub = new Adblock.Subscription (uri); |
2674 | + try { |
2675 | + sub.parse (); |
2676 | + } catch (Error error) { |
2677 | + GLib.error (error.message); |
2678 | + } |
2679 | + foreach (var pattern in patterns) { |
2680 | + Adblock.Directive? directive = sub.get_directive (pattern.uri, ""); |
2681 | + if (directive == null) |
2682 | + directive = Adblock.Directive.ALLOW; |
2683 | + if (directive != pattern.directive) { |
2684 | + error ("%s expected for %s but got %s", |
2685 | + pretty_directive (pattern.directive), pattern.uri, pretty_directive (directive)); |
2686 | + } |
2687 | + } |
2688 | +} |
2689 | + |
2690 | +struct TestUpdateExample { |
2691 | + public string content; |
2692 | + public bool result; |
2693 | +} |
2694 | + |
2695 | + const TestUpdateExample[] examples = { |
2696 | + { "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true }, |
2697 | + { "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true }, |
2698 | + { "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false }, |
2699 | + { "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false }, |
2700 | + { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true }, |
2701 | + { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true }, |
2702 | + { "[Adblock]\n ! dummy, i dont have any dates\n", false }, |
2703 | + }; |
2704 | + |
2705 | +void test_subscription_update () { |
2706 | + string uri; |
2707 | + FileIOStream iostream; |
2708 | + File file; |
2709 | + try { |
2710 | + file = File.new_tmp ("midori_adblock_update_test_XXXXXX", out iostream); |
2711 | + uri = file.get_uri (); |
2712 | + } catch (Error error) { |
2713 | + GLib.error (error.message); |
2714 | + } |
2715 | + var sub = new Adblock.Subscription (uri); |
2716 | + var updater = new Adblock.Updater (); |
2717 | + sub.add_feature (updater); |
2718 | + |
2719 | + foreach (var example in examples) { |
2720 | + try { |
2721 | + file.replace_contents (example.content.data, null, false, FileCreateFlags.NONE, null); |
2722 | + updater.last_mod_meta = null; |
2723 | + updater.expires_meta = null; |
2724 | + sub.parse (); |
2725 | + } catch (Error error) { |
2726 | + GLib.error (error.message); |
2727 | + } |
2728 | + if (example.result == true) |
2729 | + assert (updater.needs_updating()); |
2730 | + else |
2731 | + assert (!updater.needs_updating()); |
2732 | + } |
2733 | +} |
2734 | + |
2735 | +public void extension_test () { |
2736 | + Test.add_func ("/extensions/adblock2/parse", test_adblock_fixup_regexp); |
2737 | + Test.add_func ("/extensions/adblock2/pattern", test_adblock_pattern); |
2738 | + Test.add_func ("/extensions/adblock2/update", test_subscription_update); |
2739 | +} |
2740 | +#endif |
2741 | + |
2742 | |
2743 | === added file 'extensions/adblock/filter.vala' |
2744 | --- extensions/adblock/filter.vala 1970-01-01 00:00:00 +0000 |
2745 | +++ extensions/adblock/filter.vala 2014-02-20 20:31:17 +0000 |
2746 | @@ -0,0 +1,52 @@ |
2747 | +/* |
2748 | + Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de> |
2749 | + Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com> |
2750 | + |
2751 | + This library is free software; you can redistribute it and/or |
2752 | + modify it under the terms of the GNU Lesser General Public |
2753 | + License as published by the Free Software Foundation; either |
2754 | + version 2.1 of the License, or (at your option) any later version. |
2755 | + |
2756 | + See the file COPYING for the full license text. |
2757 | +*/ |
2758 | + |
2759 | +namespace Adblock { |
2760 | + public abstract class Filter : Feature { |
2761 | + Options optslist; |
2762 | + protected HashTable<string, Regex?> rules; |
2763 | + |
2764 | + public virtual void insert (string sig, Regex regex) { |
2765 | + rules.insert (sig, regex); |
2766 | + } |
2767 | + |
2768 | + public virtual Regex? lookup (string sig) { |
2769 | + return rules.lookup (sig); |
2770 | + } |
2771 | + |
2772 | + public virtual uint size () { |
2773 | + return rules.size (); |
2774 | + } |
2775 | + |
2776 | + protected Filter (Options options) { |
2777 | + optslist = options; |
2778 | + clear (); |
2779 | + } |
2780 | + |
2781 | + public override void clear () { |
2782 | + rules = new HashTable<string, Regex> (str_hash, str_equal); |
2783 | + } |
2784 | + |
2785 | + protected bool check_rule (Regex regex, string pattern, string request_uri, string page_uri) throws Error { |
2786 | + if (!regex.match_full (request_uri)) |
2787 | + return false; |
2788 | + |
2789 | + var opts = optslist.lookup (pattern); |
2790 | + if (opts != null && Regex.match_simple (",third-party", opts, |
2791 | + RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY)) |
2792 | + if (page_uri != null && regex.match_full (page_uri)) |
2793 | + return false; |
2794 | + debug ("blocked by pattern regexp=%s -- %s", regex.get_pattern (), request_uri); |
2795 | + return true; |
2796 | + } |
2797 | + } |
2798 | +} |
2799 | |
2800 | === added file 'extensions/adblock/keys.vala' |
2801 | --- extensions/adblock/keys.vala 1970-01-01 00:00:00 +0000 |
2802 | +++ extensions/adblock/keys.vala 2014-02-20 20:31:17 +0000 |
2803 | @@ -0,0 +1,47 @@ |
2804 | +/* |
2805 | + Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de> |
2806 | + Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com> |
2807 | + |
2808 | + This library is free software; you can redistribute it and/or |
2809 | + modify it under the terms of the GNU Lesser General Public |
2810 | + License as published by the Free Software Foundation; either |
2811 | + version 2.1 of the License, or (at your option) any later version. |
2812 | + |
2813 | + See the file COPYING for the full license text. |
2814 | +*/ |
2815 | + |
2816 | +namespace Adblock { |
2817 | + public class Keys : Filter { |
2818 | + List<Regex> blacklist; |
2819 | + |
2820 | + public Keys (Options options) { |
2821 | + base (options); |
2822 | + } |
2823 | + |
2824 | + public override void clear () { |
2825 | + base.clear (); |
2826 | + blacklist = new List<Regex> (); |
2827 | + } |
2828 | + |
2829 | + public override Directive? match (string request_uri, string page_uri) throws Error { |
2830 | + string? uri = fixup_regex ("", request_uri); |
2831 | + if (uri == null) |
2832 | + return null; |
2833 | + |
2834 | + int signature_size = 8; |
2835 | + int pos, l = uri.length; |
2836 | + for (pos = l - signature_size; pos >= 0; pos--) { |
2837 | + string signature = uri.offset (pos).ndup (signature_size); |
2838 | + var regex = rules.lookup (signature); |
2839 | + if (regex == null || blacklist.find (regex) != null) |
2840 | + continue; |
2841 | + |
2842 | + if (check_rule (regex, uri, request_uri, page_uri)) |
2843 | + return Directive.BLOCK; |
2844 | + blacklist.prepend (regex); |
2845 | + } |
2846 | + |
2847 | + return null; |
2848 | + } |
2849 | + } |
2850 | +} |
2851 | |
2852 | === added file 'extensions/adblock/options.vala' |
2853 | --- extensions/adblock/options.vala 1970-01-01 00:00:00 +0000 |
2854 | +++ extensions/adblock/options.vala 2014-02-20 20:31:17 +0000 |
2855 | @@ -0,0 +1,32 @@ |
2856 | +/* |
2857 | + Copyright (C) 2014 Christian Dywan <christian@twotoasts.de> |
2858 | + |
2859 | + This library is free software; you can redistribute it and/or |
2860 | + modify it under the terms of the GNU Lesser General Public |
2861 | + License as published by the Free Software Foundation; either |
2862 | + version 2.1 of the License, or (at your option) any later version. |
2863 | + |
2864 | + See the file COPYING for the full license text. |
2865 | +*/ |
2866 | + |
2867 | +namespace Adblock { |
2868 | + public class Options : GLib.Object { |
2869 | + HashTable<string, string?> optslist; |
2870 | + |
2871 | + public Options () { |
2872 | + clear (); |
2873 | + } |
2874 | + |
2875 | + public void insert (string sig, string? opts) { |
2876 | + optslist.insert (sig, opts); |
2877 | + } |
2878 | + |
2879 | + public string? lookup (string sig) { |
2880 | + return optslist.lookup (sig); |
2881 | + } |
2882 | + |
2883 | + public void clear () { |
2884 | + optslist = new HashTable<string, string?> (str_hash, str_equal); |
2885 | + } |
2886 | + } |
2887 | +} |
2888 | |
2889 | === added file 'extensions/adblock/pattern.vala' |
2890 | --- extensions/adblock/pattern.vala 1970-01-01 00:00:00 +0000 |
2891 | +++ extensions/adblock/pattern.vala 2014-02-20 20:31:17 +0000 |
2892 | @@ -0,0 +1,26 @@ |
2893 | +/* |
2894 | + Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de> |
2895 | + Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com> |
2896 | + |
2897 | + This library is free software; you can redistribute it and/or |
2898 | + modify it under the terms of the GNU Lesser General Public |
2899 | + License as published by the Free Software Foundation; either |
2900 | + version 2.1 of the License, or (at your option) any later version. |
2901 | + |
2902 | + See the file COPYING for the full license text. |
2903 | +*/ |
2904 | + |
2905 | +namespace Adblock { |
2906 | + public class Pattern : Filter { |
2907 | + public Pattern (Options options) { |
2908 | + base (options); |
2909 | + } |
2910 | + |
2911 | + public override Directive? match (string request_uri, string page_uri) throws Error { |
2912 | + foreach (var patt in rules.get_keys ()) |
2913 | + if (check_rule (rules.lookup (patt), patt, request_uri, page_uri)) |
2914 | + return Directive.BLOCK; |
2915 | + return null; |
2916 | + } |
2917 | + } |
2918 | +} |
2919 | |
2920 | === added file 'extensions/adblock/subscriptions.vala' |
2921 | --- extensions/adblock/subscriptions.vala 1970-01-01 00:00:00 +0000 |
2922 | +++ extensions/adblock/subscriptions.vala 2014-02-20 20:31:17 +0000 |
2923 | @@ -0,0 +1,328 @@ |
2924 | +/* |
2925 | + Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de> |
2926 | + Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com> |
2927 | + |
2928 | + This library is free software; you can redistribute it and/or |
2929 | + modify it under the terms of the GNU Lesser General Public |
2930 | + License as published by the Free Software Foundation; either |
2931 | + version 2.1 of the License, or (at your option) any later version. |
2932 | + |
2933 | + See the file COPYING for the full license text. |
2934 | +*/ |
2935 | + |
2936 | +namespace Adblock { |
2937 | + public abstract class Feature : GLib.Object { |
2938 | + public virtual bool header (string key, string value) { |
2939 | + return false; |
2940 | + } |
2941 | + public virtual Directive? match (string request_uri, string page_uri) throws Error { |
2942 | + return null; |
2943 | + } |
2944 | + public virtual void clear () { |
2945 | + } |
2946 | + } |
2947 | + |
2948 | + public class Subscription : GLib.Object { |
2949 | + public string? path; |
2950 | + public string uri { get; set; default = null; } |
2951 | + public bool active { get; set; default = true; } |
2952 | + List<Feature> features; |
2953 | + public Pattern pattern; |
2954 | + public Keys keys; |
2955 | + public Options optslist; |
2956 | + public Whitelist whitelist; |
2957 | + public Element element; |
2958 | + WebKit.Download? download; |
2959 | + |
2960 | + public Subscription (string uri) { |
2961 | + this.uri = uri; |
2962 | + |
2963 | + this.optslist = new Options (); |
2964 | + this.whitelist = new Whitelist (optslist); |
2965 | + add_feature (this.whitelist); |
2966 | + this.keys = new Keys (optslist); |
2967 | + add_feature (this.keys); |
2968 | + this.pattern = new Pattern (optslist); |
2969 | + add_feature (this.pattern); |
2970 | + this.element = new Element (); |
2971 | + add_feature (this.element); |
2972 | + clear (); |
2973 | + } |
2974 | + |
2975 | + public void add_feature (Feature feature) { |
2976 | + features.append (feature); |
2977 | + size++; |
2978 | + } |
2979 | + |
2980 | + /* foreach support */ |
2981 | + public new Feature? get (uint index) { |
2982 | + return features.nth_data (index); |
2983 | + } |
2984 | + public uint size { get; private set; } |
2985 | + |
2986 | + public void clear () { |
2987 | + foreach (var feature in features) |
2988 | + feature.clear (); |
2989 | + optslist.clear (); |
2990 | + } |
2991 | + |
2992 | + internal void parse_line (string? line) throws Error { |
2993 | + if (line.has_prefix ("@@")) { |
2994 | + if (line.contains("$") && line.contains ("domain")) |
2995 | + return; |
2996 | + if (line.has_prefix ("@@||")) |
2997 | + add_url_pattern ("^", "whitelist", line.offset (4)); |
2998 | + else if (line.has_prefix ("@@|")) |
2999 | + add_url_pattern ("^", "whitelist", line.offset (3)); |
3000 | + else |
3001 | + add_url_pattern ("", "whitelist", line.offset (2)); |
3002 | + return; |
3003 | + } |
3004 | + /* TODO: [include] [exclude] */ |
3005 | + if (line[0] == '[') |
3006 | + return; |
3007 | + |
3008 | + /* CSS block hider */ |
3009 | + if (line.has_prefix ("##")) { |
3010 | + /* TODO */ |
3011 | + return; |
3012 | + } |
3013 | + if (line[0] == '#') |
3014 | + return; |
3015 | + |
3016 | + /* Per domain CSS hider rule */ |
3017 | + if ("##" in line) { |
3018 | + frame_add_private (line, "##"); |
3019 | + return; |
3020 | + } |
3021 | + if ("#" in line) { |
3022 | + frame_add_private (line, "#"); |
3023 | + return; |
3024 | + } |
3025 | + |
3026 | + /* URL blocker rule */ |
3027 | + if (line.has_prefix ("|")) { |
3028 | + /* TODO: handle options and domains excludes */ |
3029 | + if (line.contains("$")) |
3030 | + return; |
3031 | + |
3032 | + if (line.has_prefix ("||")) |
3033 | + add_url_pattern ("", "fulluri", line.offset (2)); |
3034 | + else |
3035 | + add_url_pattern ("^", "fulluri", line.offset (1)); |
3036 | + return /* add_url_pattern */; |
3037 | + } |
3038 | + |
3039 | + add_url_pattern ("", "uri", line); |
3040 | + return /* add_url_pattern */; |
3041 | + } |
3042 | + |
3043 | + void frame_add_private (string line, string sep) { |
3044 | + string[] data = line.split (sep, 2); |
3045 | + if (!(data[1] != null && data[1] != "") |
3046 | + || data[1].chr (-1, '\'') != null |
3047 | + || (data[1].chr (-1, ':') != null |
3048 | + && !Regex.match_simple (".*\\[.*:.*\\].*", data[1], |
3049 | + RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))) { |
3050 | + return; |
3051 | + } |
3052 | + |
3053 | + if (data[0].chr (-1, ',') != null) { |
3054 | + string[] domains = data[0].split (",", -1); |
3055 | + |
3056 | + foreach (string domain in domains) { |
3057 | + /* Ignore Firefox-specific option */ |
3058 | + if (domain == "~pregecko2") |
3059 | + continue; |
3060 | + /* FIXME: ~ should negate match */ |
3061 | + if (domain[0] == '~') |
3062 | + domain = domain.substring (1, -1); |
3063 | + update_css_hash (domain.strip (), data[1]); |
3064 | + } |
3065 | + } |
3066 | + else { |
3067 | + update_css_hash (data[0], data[1]); |
3068 | + } |
3069 | + } |
3070 | + |
3071 | + void update_css_hash (string domain, string value) { |
3072 | + string? olddata = element.lookup (domain); |
3073 | + if (olddata != null) { |
3074 | + string newdata = olddata + " , " + value; |
3075 | + element.insert (domain, newdata); |
3076 | + } else { |
3077 | + element.insert (domain, value); |
3078 | + } |
3079 | + } |
3080 | + |
3081 | + void add_url_pattern (string prefix, string type, string line) throws Error { |
3082 | + string[]? data = line.split ("$", 2); |
3083 | + if (data == null || data[0] == null) |
3084 | + return; |
3085 | + |
3086 | + string patt, opts; |
3087 | + patt = data[0]; |
3088 | + opts = type; |
3089 | + |
3090 | + if (data[1] != null) |
3091 | + opts = type + "," + data[1]; |
3092 | + |
3093 | + if (Regex.match_simple ("subdocument", opts, |
3094 | + RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY)) |
3095 | + return; |
3096 | + |
3097 | + string format_patt = fixup_regex (prefix, patt); |
3098 | + debug ("got: %s opts %s", format_patt, opts); |
3099 | + compile_regexp (format_patt, opts); |
3100 | + /* return format_patt */ |
3101 | + } |
3102 | + |
3103 | + bool compile_regexp (string? patt, string opts) throws Error { |
3104 | + if (patt == null) |
3105 | + return false; |
3106 | + try { |
3107 | + var regex = new Regex (patt, RegexCompileFlags.OPTIMIZE, RegexMatchFlags.NOTEMPTY); |
3108 | + /* is pattern is already a regex? */ |
3109 | + if (Regex.match_simple ("^/.*[\\^\\$\\*].*/$", patt, |
3110 | + RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY) |
3111 | + || opts != null && opts.contains ("whitelist")) { |
3112 | + debug ("patt: %s", patt); |
3113 | + if (opts.contains ("whitelist")) |
3114 | + this.whitelist.insert (patt, regex); |
3115 | + else |
3116 | + this.pattern.insert (patt, regex); |
3117 | + this.optslist.insert (patt, opts); |
3118 | + return false; |
3119 | + } else { /* nope, no regex */ |
3120 | + int pos = 0, len; |
3121 | + int signature_size = 8; |
3122 | + string sig; |
3123 | + len = patt.length; |
3124 | + |
3125 | + /* chop up pattern into substrings for faster matching */ |
3126 | + for (pos = len - signature_size; pos>=0; pos--) |
3127 | + { |
3128 | + sig = patt.offset (pos).ndup (signature_size); |
3129 | + /* we don't have a * nor \\, does not look like regex, save chunk as "key" */ |
3130 | + if (!Regex.match_simple ("[\\*]", sig, RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY) && keys.lookup (sig) == null) { |
3131 | + this.keys.insert (sig, regex); |
3132 | + this.optslist.insert (sig, opts); |
3133 | + } else { |
3134 | + /* starts with * or \\ - save as regex */ |
3135 | + if ((sig.has_prefix ("*") || sig.has_prefix("\\")) && this.pattern.lookup (sig) == null) { |
3136 | + this.pattern.insert (sig, regex); |
3137 | + this.optslist.insert (sig, opts); |
3138 | + } |
3139 | + } |
3140 | + } |
3141 | + } |
3142 | + return false; |
3143 | + } |
3144 | + catch (Error error) { |
3145 | + warning ("Adblock compile regexp: %s", error.message); |
3146 | + return true; |
3147 | + } |
3148 | + } |
3149 | + |
3150 | + public void parse_header (string header) throws Error { |
3151 | + /* Headers come in two forms |
3152 | + ! Foo: Bar |
3153 | + ! Some freeform text |
3154 | + */ |
3155 | + string key = header; |
3156 | + string value = ""; |
3157 | + if (header.contains (":")) { |
3158 | + string[] parts = header.split (":", 2); |
3159 | + if (parts[0] != null) { |
3160 | + key = parts[0].substring (2, -1); |
3161 | + value = parts[1].substring (1, -1); |
3162 | + } |
3163 | + } |
3164 | + debug ("Header '%s' says '%s'", key, value); |
3165 | + foreach (var feature in features) { |
3166 | + if (feature.header (key, value)) |
3167 | + break; |
3168 | + } |
3169 | + } |
3170 | + |
3171 | + void download_status (ParamSpec pspec) { |
3172 | + if (download.get_status () != WebKit.DownloadStatus.FINISHED) |
3173 | + return; |
3174 | + |
3175 | + download = null; |
3176 | + try { |
3177 | + parse (); |
3178 | + } catch (Error error) { |
3179 | + warning ("Error parsing %s: %s", uri, error.message); |
3180 | + } |
3181 | + } |
3182 | + |
3183 | + public void parse () throws Error |
3184 | + { |
3185 | + if (!active) |
3186 | + return; |
3187 | + |
3188 | + debug ("Parsing %s (%s)", uri, path); |
3189 | + |
3190 | + clear (); |
3191 | + |
3192 | + if (uri.has_prefix ("file://")) |
3193 | + path = Filename.from_uri (uri); |
3194 | + else { |
3195 | + string cache_dir = GLib.Path.build_filename (GLib.Environment.get_home_dir (), ".cache", "midori", "adblock"); |
3196 | + Midori.Paths.mkdir_with_parents (cache_dir); |
3197 | + string filename = Checksum.compute_for_string (ChecksumType.MD5, this.uri, -1); |
3198 | + path = GLib.Path.build_filename (cache_dir, filename); |
3199 | + } |
3200 | + |
3201 | + File filter_file = File.new_for_path (path); |
3202 | + DataInputStream stream; |
3203 | + try { |
3204 | + stream = new DataInputStream (filter_file.read ()); |
3205 | + } catch (IOError.NOT_FOUND exist_error) { |
3206 | +#if HAVE_WEBKIT2 |
3207 | + /* TODO */ |
3208 | +#else |
3209 | + if (download != null) |
3210 | + return; |
3211 | + |
3212 | + download = new WebKit.Download (new WebKit.NetworkRequest (uri)); |
3213 | + download.destination_uri = Filename.to_uri (path, null); |
3214 | + download.notify["status"].connect (download_status); |
3215 | + debug ("Fetching %s to %s now", uri, download.destination_uri); |
3216 | + download.start (); |
3217 | +#endif |
3218 | + return; |
3219 | + } |
3220 | + |
3221 | + string? line; |
3222 | + while ((line = stream.read_line (null)) != null) { |
3223 | + if (line == null) |
3224 | + continue; |
3225 | + string chomped = line.chomp (); |
3226 | + if (chomped == "") |
3227 | + continue; |
3228 | + if (line[0] == '!') |
3229 | + parse_header (chomped); |
3230 | + else |
3231 | + parse_line (chomped); |
3232 | + } |
3233 | + } |
3234 | + |
3235 | + public Directive? get_directive (string request_uri, string page_uri) { |
3236 | + try { |
3237 | + foreach (var feature in features) { |
3238 | + Directive? directive = feature.match (request_uri, page_uri); |
3239 | + if (directive != null) { |
3240 | + debug ("%s gave %s for %s (%s)\n", |
3241 | + feature.get_type ().name (), directive.to_string (), request_uri, page_uri); |
3242 | + return directive; |
3243 | + } |
3244 | + } |
3245 | + } catch (Error error) { |
3246 | + warning ("Adblock match error: %s\n", error.message); |
3247 | + } |
3248 | + return null; |
3249 | + } |
3250 | + } |
3251 | +} |
3252 | |
3253 | === added file 'extensions/adblock/updater.vala' |
3254 | --- extensions/adblock/updater.vala 1970-01-01 00:00:00 +0000 |
3255 | +++ extensions/adblock/updater.vala 2014-02-20 20:31:17 +0000 |
3256 | @@ -0,0 +1,139 @@ |
3257 | +/* |
3258 | + Copyright (C) 2014 Paweł Forysiuk <tuxator@o2.pl> |
3259 | + Copyright (C) 2014 Christian Dywan <christian@twotoasts.de> |
3260 | + |
3261 | + This library is free software; you can redistribute it and/or |
3262 | + modify it under the terms of the GNU Lesser General Public |
3263 | + License as published by the Free Software Foundation; either |
3264 | + version 2.1 of the License, or (at your option) any later version. |
3265 | + |
3266 | + See the file COPYING for the full license text. |
3267 | +*/ |
3268 | + |
3269 | +namespace Adblock { |
3270 | + public class Updater : Feature { |
3271 | + public string expires_meta { get; set; default = null; } |
3272 | + public string last_mod_meta { get; set; default = null; } |
3273 | + public int64 update_tstamp { get; set; default = 0; } |
3274 | + public int64 last_mod_tstamp { get; set; default = 0; } |
3275 | + public int64 last_check_tstamp { get; set; default = 0; } |
3276 | + |
3277 | + public Updater () { |
3278 | + } |
3279 | + |
3280 | + public override bool header (string key, string value) { |
3281 | + if (key.has_prefix ("Last mod") || key == "Updated") { |
3282 | + last_mod_meta = value; |
3283 | + return true; |
3284 | + } else if (key == "Expires") { |
3285 | + /* ! Expires: 5 days (update frequency) */ |
3286 | + expires_meta = value; |
3287 | + return true; |
3288 | + } else if (key.has_prefix ("! This list expires after")) { |
3289 | + /* ! This list expires after 14 days */ |
3290 | + expires_meta = key.substring (26, -1); |
3291 | + return true; |
3292 | + } |
3293 | + return false; |
3294 | + } |
3295 | + |
3296 | + int get_month_from_string (string? month) { |
3297 | + if (month == null) |
3298 | + return 0; |
3299 | + |
3300 | + string[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; |
3301 | + for (int i = 0; i<= months.length; i++) |
3302 | + { |
3303 | + if (month.has_prefix (months[i])) |
3304 | + return i+1; |
3305 | + } |
3306 | + return 0; |
3307 | + } |
3308 | + |
3309 | + public bool needs_updating () { |
3310 | + DateTime now = new DateTime.now_local (); |
3311 | + DateTime expire_date = null; |
3312 | + DateTime last_mod_date = null; |
3313 | + string? last_mod = last_mod_meta; |
3314 | + string? expires = expires_meta; |
3315 | + |
3316 | + /* We have "last modification" metadata */ |
3317 | + if (last_mod != null) { |
3318 | + int h = 0, min = 0, d, m, y; |
3319 | + /* Date in a form of: 20.08.2012 12:34 */ |
3320 | + if (last_mod.contains (".") || last_mod.contains("-")) { |
3321 | + string[] parts = last_mod.split (" ", 2); |
3322 | + string[] date_parts; |
3323 | + string split_char = " "; |
3324 | + |
3325 | + /* contains time part ? */ |
3326 | + if (parts[1] != "" && parts[1].contains (":")) { |
3327 | + string[] time_parts = parts[1].split (":", 2); |
3328 | + h = int.parse(time_parts[0]); |
3329 | + min = int.parse(time_parts[1]); |
3330 | + } |
3331 | + |
3332 | + /* check if dot or dash was used as a delimiter */ |
3333 | + if (parts[0].contains (".")) |
3334 | + split_char = "."; |
3335 | + else if (parts[0].contains ("-")) |
3336 | + split_char = "-"; |
3337 | + |
3338 | + date_parts = parts[0].split (split_char, 3); |
3339 | + m = int.parse(date_parts[1]); |
3340 | + if (date_parts[2].length == 4) { |
3341 | + y = int.parse(date_parts[2]); |
3342 | + d = int.parse(date_parts[0]); |
3343 | + } else { |
3344 | + y = int.parse(date_parts[0]); |
3345 | + d = int.parse(date_parts[2]); |
3346 | + } |
3347 | + } else { /* Date in a form of: 20 Mar 2012 12:34 */ |
3348 | + string[] parts = last_mod.split (" ", 4); |
3349 | + /* contains time part ? */ |
3350 | + if (parts[3] != null && parts[3].contains (":")) { |
3351 | + string[] time_parts = parts[3].split (":", 2); |
3352 | + h = int.parse(time_parts[0]); |
3353 | + min = int.parse(time_parts[1]); |
3354 | + } |
3355 | + |
3356 | + m = get_month_from_string (parts[1]); |
3357 | + if (parts[2].length == 4) { |
3358 | + y = int.parse(parts[2]); |
3359 | + d = int.parse(parts[0]); |
3360 | + } else { |
3361 | + y = int.parse(parts[0]); |
3362 | + d = int.parse(parts[2]); |
3363 | + } |
3364 | + } |
3365 | + |
3366 | + last_mod_date = new DateTime.local (y, m, d, h, min, 0.0); |
3367 | + } |
3368 | + |
3369 | + if (last_mod_date == null) |
3370 | + last_mod_date = now; |
3371 | + |
3372 | + /* We have "expires" metadata */ |
3373 | + if (expires != null) { |
3374 | + if (expires.contains ("days")) { |
3375 | + string[] parts = expires.split (" "); |
3376 | + expire_date = last_mod_date.add_days (int.parse (parts[0])); |
3377 | + } else if (expires.contains ("hours")) { |
3378 | + string[] parts = expires.split (" "); |
3379 | + expire_date = last_mod_date.add_hours (int.parse (parts[0])); |
3380 | + } |
3381 | + } else { |
3382 | + /* No expire metadata found, assume x days */ |
3383 | + int days_to_expire = 7; |
3384 | + expire_date = last_mod_date.add_days (days_to_expire); |
3385 | + } |
3386 | + |
3387 | + last_mod_tstamp = last_mod_date.to_unix (); |
3388 | + last_check_tstamp = now.to_unix (); |
3389 | + update_tstamp = expire_date.to_unix (); |
3390 | + |
3391 | + /* Check if we are past expire date */ |
3392 | + return now.compare (expire_date) == 1; |
3393 | + } |
3394 | + } |
3395 | +} |
3396 | |
3397 | === added file 'extensions/adblock/whitelist.vala' |
3398 | --- extensions/adblock/whitelist.vala 1970-01-01 00:00:00 +0000 |
3399 | +++ extensions/adblock/whitelist.vala 2014-02-20 20:31:17 +0000 |
3400 | @@ -0,0 +1,30 @@ |
3401 | +/* |
3402 | + Copyright (C) 2014 Christian Dywan <christian@twotoasts.de> |
3403 | + Copyright (C) 2014 Paweł Forysiuk <tuxator@o2.pl> |
3404 | + |
3405 | + This library is free software; you can redistribute it and/or |
3406 | + modify it under the terms of the GNU Lesser General Public |
3407 | + License as published by the Free Software Foundation; either |
3408 | + version 2.1 of the License, or (at your option) any later version. |
3409 | + |
3410 | + See the file COPYING for the full license text. |
3411 | +*/ |
3412 | + |
3413 | +namespace Adblock { |
3414 | + public class Whitelist : Filter { |
3415 | + public Whitelist (Options options) { |
3416 | + base (options); |
3417 | + } |
3418 | + |
3419 | + public override Directive? match (string request_uri, string page_uri) throws Error { |
3420 | + foreach (var white in rules.get_keys ()) { |
3421 | + var regex = rules.lookup (white); |
3422 | + if (!regex.match_full (request_uri)) |
3423 | + return null; |
3424 | + if (Regex.match_simple (regex.get_pattern (), request_uri, RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY)) |
3425 | + return Directive.ALLOW; |
3426 | + } |
3427 | + return null; |
3428 | + } |
3429 | + } |
3430 | +} |
3431 | |
3432 | === modified file 'katze/midori-paths.vala' |
3433 | --- katze/midori-paths.vala 2013-11-22 18:14:18 +0000 |
3434 | +++ katze/midori-paths.vala 2014-02-20 20:31:17 +0000 |
3435 | @@ -143,6 +143,7 @@ |
3436 | #if HAVE_WEBKIT2 |
3437 | WebKit.WebContext.get_default ().set_disk_cache_directory ( |
3438 | Path.build_path (Path.DIR_SEPARATOR_S, cache_dir, "web")); |
3439 | + |
3440 | var cookie_manager = WebKit.WebContext.get_default ().get_cookie_manager (); |
3441 | cookie_manager.set_persistent_storage (Path.build_filename (config, "cookies.db"), |
3442 | WebKit.CookiePersistentStorage.SQLITE); |
3443 | |
3444 | === modified file 'midori/midori-app.c' |
3445 | --- midori/midori-app.c 2014-01-12 18:14:15 +0000 |
3446 | +++ midori/midori-app.c 2014-02-20 20:31:17 +0000 |
3447 | @@ -1256,7 +1256,7 @@ |
3448 | midori_debug (const gchar* token) |
3449 | { |
3450 | static const gchar* debug_token = NULL; |
3451 | - const gchar* debug_tokens = "adblock:match adblock:time startup headers body referer cookies paths hsts unarmed bookmarks mouse app "; |
3452 | + const gchar* debug_tokens = "adblock:match adblock:time adblock:element startup headers body referer cookies paths hsts unarmed bookmarks mouse app "; |
3453 | if (debug_token == NULL) |
3454 | { |
3455 | gchar* found_token; |
3456 | |
3457 | === modified file 'midori/midori.vapi' |
3458 | --- midori/midori.vapi 2014-01-26 11:33:02 +0000 |
3459 | +++ midori/midori.vapi 2014-02-20 20:31:17 +0000 |
3460 | @@ -135,6 +135,7 @@ |
3461 | public void install_boolean (string name, bool default_value); |
3462 | public void install_integer (string name, int default_value); |
3463 | public void install_string (string name, string default_value); |
3464 | + public void install_string_list (string name, string[]? default_value); |
3465 | |
3466 | public bool get_boolean (string name); |
3467 | public int get_integer (string name); |
3468 | |
3469 | === modified file 'midori/webkit2gtk-3.0.vapi' |
3470 | --- midori/webkit2gtk-3.0.vapi 2013-04-08 18:35:37 +0000 |
3471 | +++ midori/webkit2gtk-3.0.vapi 2014-02-20 20:31:17 +0000 |
3472 | @@ -552,6 +552,22 @@ |
3473 | [CCode (has_construct_function = false)] |
3474 | protected WebViewBase (); |
3475 | } |
3476 | + [CCode (cheader_filename = "webkit2/webkit-web-extension.h", type_id = "webkit_web_extension_get_type ()")] |
3477 | + public class WebExtension : GLib.Object { |
3478 | + [CCode (has_construct_function = false)] |
3479 | + protected WebExtension (); |
3480 | + public WebKit.WebPage get_page (uint64 page_id); |
3481 | + public signal void page_created (WebKit.WebPage web_page); |
3482 | + } |
3483 | + [CCode (cheader_filename = "webkit2/webkit-web-extension.h", type_id = "webkit_web_extension_get_type ()")] |
3484 | + public class WebPage : GLib.Object { |
3485 | + [CCode (has_construct_function = false)] |
3486 | + protected WebPage (); |
3487 | + public uint64 get_id (); |
3488 | + public string uri { get; } |
3489 | + public signal bool send_request (WebKit.URIRequest request, WebKit.URIResponse? redirected_response); |
3490 | + public signal void document_loaded (); |
3491 | + } |
3492 | [CCode (cheader_filename = "webkit2/webkit2.h", type_id = "webkit_window_properties_get_type ()")] |
3493 | public class WindowProperties : GLib.Object { |
3494 | [CCode (has_construct_function = false)] |
3495 | |
3496 | === modified file 'po/POTFILES.in' |
3497 | --- po/POTFILES.in 2013-12-13 17:34:40 +0000 |
3498 | +++ po/POTFILES.in 2014-02-20 20:31:17 +0000 |
3499 | @@ -36,7 +36,6 @@ |
3500 | katze/katze-preferences.c |
3501 | katze/midori-uri.vala |
3502 | katze/midori-paths.vala |
3503 | -extensions/adblock.c |
3504 | extensions/addons.c |
3505 | extensions/colorful-tabs.c |
3506 | extensions/cookie-manager/cookie-manager.c |
3507 | @@ -92,3 +91,13 @@ |
3508 | extensions/tabby.vala |
3509 | extensions/flummi.vala |
3510 | extensions/notes.vala |
3511 | +extensions/adblock/extension.vala |
3512 | +extensions/adblock/filter.vala |
3513 | +extensions/adblock/keys.vala |
3514 | +extensions/adblock/pattern.vala |
3515 | +extensions/adblock/options.vala |
3516 | +extensions/adblock/whitelist.vala |
3517 | +extensions/adblock/subscriptions.vala |
3518 | +extensions/adblock/config.vala |
3519 | +extensions/adblock/updater.vala |
3520 | +extensions/adblock/element.vala |