separate adblock and surf-specific code - surf-adblock - Surf adblock web extension (HTM) git clone git://git.codemadness.org/surf-adblock (DIR) Log (DIR) Files (DIR) Refs (DIR) README (DIR) LICENSE --- (DIR) commit 08e747efa80a44603f80db0fdacb3f63ad210b8e (DIR) parent c4841f33f64cd77b30def9c0b11d1ac9ece7f821 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org> Date: Sun, 4 Jun 2017 14:15:27 +0200 separate adblock and surf-specific code this will be useful later on for writing test-cases also or re-using the code in a daemon. Diffstat: M Makefile | 2 +- M TODO | 2 ++ A adblock.c | 956 +++++++++++++++++++++++++++++++ M surf-adblock.c | 944 +------------------------------ 4 files changed, 973 insertions(+), 931 deletions(-) --- (DIR) diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ include config.mk -SRC = surf-adblock.c +SRC = surf-adblock.c adblock.c OBJ = ${SRC:.c=.lo} all: surf-adblock.la (DIR) diff --git a/TODO b/TODO @@ -1,3 +1,5 @@ +- optimize towupper for fnmatch? check < 128, see musl optimization. + - fix blocking of : ||ads.somesite.com^ - fix tweakers.net popup / rule. (DIR) diff --git a/adblock.c b/adblock.c @@ -0,0 +1,956 @@ +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "adblock.h" + +/* String data / memory pool */ +typedef struct string { + char *data; /* data */ + size_t datasz; /* allocated size */ + size_t len; /* current string length */ +} String; + +struct filterdomain { + char *domain; + int inverse; + struct filterdomain *next; +}; + +struct filterrule { + /* type: match mask, must be atleast 32-bit, see FilterType enum */ + unsigned long block; + int matchbegin; + int matchend; + /* is exception rule: prefix @@ for ABP or #@# for CSS */ + int isexception; + char *css; /* if non-NULL is CSS rule / hide element rule */ + char *uri; + struct filterdomain *domains; + struct filterrule *next; +}; + +enum { + FilterTypeScript = 1 << 0, + FilterTypeImage = 1 << 1, + FilterTypeCSS = 1 << 2, + FilterTypeObject = 1 << 3, + FilterTypeXHR = 1 << 4, + FilterTypeObjectSub = 1 << 5, + FilterTypeSubDoc = 1 << 6, + FilterTypePing = 1 << 7, + FilterTypeDocument = 1 << 8, + FilterTypeElemHide = 1 << 9, + FilterTypeOther = 1 << 10, + FilterTypeGenericHide = 1 << 11, + FilterTypeGenericBlock = 1 << 12, + FilterTypeMatchCase = 1 << 13, +}; + +struct filtertype { + /* `type` must be atleast 32-bit, see FilterType enum */ + unsigned long type; + char *name; + size_t namelen; + int allowinverse; + int allownormal; + int onlyexception; + int (*fn)(struct filterrule *, char *); +}; + +static int parsedomainsoption(struct filterrule *, char *); + +#define STRP(s) s,sizeof(s)-1 + +static struct filtertype filtertypes[] = { + /* NOTE: options with 'type' = 0 are silently ignored and treated as + * requests for now */ + { 0, STRP("collapse"), 1, 1, 0, NULL }, + { FilterTypeDocument, STRP("document"), 1, 0, 1, NULL }, + { 0, STRP("domain"), 0, 1, 0, + /* domain=... */ &parsedomainsoption }, + { 0, STRP("donottrack"), 1, 1, 0, NULL }, + { FilterTypeElemHide, STRP("elemhide"), 0, 0, 1, NULL }, + { 0, STRP("font"), 1, 1, 0, NULL }, + { FilterTypeGenericBlock, STRP("genericblock"), 1, 1, 1, NULL }, + { FilterTypeGenericHide, STRP("generichide"), 1, 1, 1, NULL }, + { FilterTypeImage, STRP("image"), 1, 1, 0, NULL }, + { FilterTypeMatchCase, STRP("match-case"), 1, 1, 0, NULL }, + { 0, STRP("media"), 1, 1, 0, NULL }, + { FilterTypeObject, STRP("object"), 1, 1, 0, NULL }, + { FilterTypeObjectSub, STRP("object-subrequest"), 1, 1, 0, NULL }, + { FilterTypeOther, STRP("other"), 1, 1, 0, NULL }, + { FilterTypePing, STRP("ping"), 1, 1, 0, NULL }, + { 0, STRP("popup"), 1, 1, 0, NULL }, + { FilterTypeScript, STRP("script"), 1, 1, 0, NULL }, + { FilterTypeCSS, STRP("stylesheet"), 1, 1, 0, NULL }, + { FilterTypeSubDoc, STRP("subdocument"), 1, 1, 0, NULL }, + { 0, STRP("third-party"), 1, 1, 0, NULL }, + { FilterTypeXHR, STRP("xmlhttprequest"), 1, 1, 0, NULL }, + /* NOTE: site-key not supported */ +}; + +static String globalcss; +static struct filterrule *rules; + +static void +weprintf(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "surf-adblock: "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void * +wecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + weprintf("calloc: %s\n", strerror(errno)); + + return p; +} + +static char * +westrndup(const char *s, size_t n) +{ + char *p; + + if (!(p = strndup(s, n))) + weprintf("strndup: %s\n", strerror(errno)); + return p; +} + +static char * +westrdup(const char *s) +{ + char *p; + + if (!(p = strdup(s))) + weprintf("strdup: %s\n", strerror(errno)); + + return p; +} + +void +cleanup(void) +{ + struct filterrule *r; + struct filterdomain *d; + + free(globalcss.data); + + for (r = rules; r; r = rules) { + for (d = r->domains; d; d = r->domains) { + free(d->domain); + r->domains = d->next; + free(d); + } + free(r->css); + free(r->uri); + rules = r->next; + free(r); + } +} + +static size_t +string_buffer_realloc(String *s, size_t newsz) +{ + char *tmp; + size_t allocsz; + + for (allocsz = 64; allocsz <= newsz; allocsz *= 2) + ; + if (!(tmp = realloc(s->data, allocsz))) { + weprintf("realloc: %s\n", strerror(errno)); + } else { + s->data = tmp; + s->datasz = allocsz; + } + + return s->datasz; +} + +static size_t +string_append(String *s, const char *data, size_t len) +{ + size_t newlen; + + if (!len) + return len; + + newlen = s->len + len; + /* check if allocation is necesary, don't shrink buffer, + * should be more than datasz ofcourse. */ + if (newlen >= s->datasz) { + if (string_buffer_realloc(s, newlen + 1) <= newlen) + return 0; + } + memcpy(s->data + s->len, data, len); + s->len = newlen; + s->data[s->len] = '\0'; + return len; +} + +#define END 0 +#define UNMATCHABLE -2 +#define BRACKET -3 +#define CARET -4 +#define STAR -5 + +static int +str_next(const char *str, size_t n, size_t *step) +{ + if (!n) { + *step = 0; + return 0; + } + if (str[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, str, n); + if (k<0) { + *step = 1; + return -1; + } + *step = k; + return wc; + } + *step = 1; + + return str[0]; +} + +static int +pat_next(const char *pat, size_t m, size_t *step) +{ + int esc = 0; + + if (!m || !*pat) { + *step = 0; + return END; + } + *step = 1; + if (pat[0]=='\\' && pat[1]) { + *step = 2; + pat++; + esc = 1; + goto escaped; + } + if (pat[0]=='^') + return CARET; + if (pat[0] == '*') + return STAR; +escaped: + if (pat[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, pat, m); + if (k<0) { + *step = 0; + return UNMATCHABLE; + } + *step = k + esc; + return wc; + } + return pat[0]; +} + +static int +casefold(int k) +{ + int c = towupper(k); + return c == k ? towlower(k) : c; +} + +/* match() based on musl-libc fnmatch: + https://git.musl-libc.org/cgit/musl/tree/src/regex/fnmatch.c */ +static int +match(const char *pat, const char *str, int fcase) +{ + size_t m = -1, n = -1; + const char *p, *ptail, *endpat; + const char *s, *stail, *endstr; + size_t pinc, sinc, tailcnt=0; + int c, k, kfold; + + for (;;) { + switch ((c = pat_next(pat, m, &pinc))) { + case UNMATCHABLE: + return 1; + case STAR: + pat++; + m--; + break; + default: + k = str_next(str, n, &sinc); + /* TODO: write a test-case */ + if (c == CARET && (k == '?' || k == '/' || k <= 0)) + return 1; + if (k <= 0) + return (c==END) ? 0 : 1; + str += sinc; + n -= sinc; + kfold = fcase ? casefold(k) : k; + if (k != c && kfold != c) + return 1; + pat+=pinc; + m-=pinc; + continue; + } + break; + } + + /* Compute real pat length if it was initially unknown/-1 */ + m = strnlen(pat, m); + endpat = pat + m; + + /* Find the last * in pat and count chars needed after it */ + for (p=ptail=pat; p<endpat; p+=pinc) { + switch (pat_next(p, endpat-p, &pinc)) { + case UNMATCHABLE: + return 1; + case STAR: + tailcnt=0; + ptail = p+1; + break; + default: + tailcnt++; + break; + } + } + + /* Past this point we need not check for UNMATCHABLE in pat, + * because all of pat has already been parsed once. */ + + /* Compute real str length if it was initially unknown/-1 */ + n = strnlen(str, n); + endstr = str + n; + if (n < tailcnt) return 1; + + /* Find the final tailcnt chars of str, accounting for UTF-8. + * On illegal sequences we may get it wrong, but in that case + * we necessarily have a matching failure anyway. */ + for (s=endstr; s>str && tailcnt; tailcnt--) { + if (s[-1] < 128U || MB_CUR_MAX==1) s--; + else while ((unsigned char)*--s-0x80U<0x40 && s>str); + } + if (tailcnt) return 1; + stail = s; + + /* Check that the pat and str tails match */ + p = ptail; + for (;;) { + c = pat_next(p, endpat-p, &pinc); + p += pinc; + if ((k = str_next(s, endstr-s, &sinc)) <= 0) { + if (c != END) return 1; + break; + } + s += sinc; + kfold = fcase ? casefold(k) : k; + if (k != c && kfold != c) + return 1; + } + + /* We're all done with the tails now, so throw them out */ + endstr = stail; + endpat = ptail; + + /* Match pattern components until there are none left */ + while (pat<endpat) { + p = pat; + s = str; + for (;;) { + c = pat_next(p, endpat-p, &pinc); + p += pinc; + /* Encountering * completes/commits a component */ + if (c == STAR) { + pat = p; + str = s; + break; + } + k = str_next(s, endstr-s, &sinc); + if (!k) + return 1; + kfold = fcase ? casefold(k) : k; + if (k != c && kfold != c) + break; + s += sinc; + } + if (c == STAR) continue; + /* If we failed, advance str, by 1 char if it's a valid + * char, or past all invalid bytes otherwise. */ + k = str_next(str, endstr-str, &sinc); + if (k > 0) str += sinc; + else for (str++; str_next(str, endstr-str, &sinc)<0; str++); + } + + return 0; +} + +/* +domain=... if domain is prefixed with ~, ignore. +multiple domains can be separated with | +*/ +static int +parsedomains(const char *s, int sep, struct filterdomain **head) +{ + struct filterdomain *d, *last = *head = NULL; + char *p; + int inverse; + + do { + inverse = 0; + if (*s == '~') { + inverse = !inverse; + s++; + } + if (!*s || *s == sep) + break; + + if (!(d = wecalloc(1, sizeof(struct filterdomain)))) + return -1; + if ((p = strchr(s, sep))) { /* TODO: should not contain ',' */ + d->domain = westrndup(s, p - s); + s = p + 1; + } else { + d->domain = westrdup(s); + } + if (!d->domain) + return -1; + d->inverse = inverse; + + if (!*head) + *head = last = d; + else + last = last->next = d; + } while (p); + + return (*head != NULL); +} + +static int +parsedomainselement(struct filterrule *f, char *s) +{ + struct filterdomain *d, *last; + + for (last = f->domains; last && last->next; last = last->next) + ; + + if (parsedomains(s, ',', &d) < 0) + return -1; + if (last) + last->next = d; + else + f->domains = d; + + return (d != NULL); +} + +static int +parsedomainsoption(struct filterrule *f, char *s) +{ + struct filterdomain *d, *last; + + for (last = f->domains; last && last->next; last = last->next) + ; + + if (parsedomains(s, '|', &d) < 0) + return -1; + if (last) + last->next = d; + else + f->domains = d; + + return (d != NULL); +} + +static int +filtertype_cmp(const void *a, const void *b) +{ + return strcmp(((struct filtertype *)a)->name, + ((struct filtertype *)b)->name); +} + +/* check if domain is the same domain or a subdomain of `s` */ +static int +matchdomain(const char *s, const char *domain) +{ + size_t l1, l2; + + l1 = strlen(s); + l2 = strlen(domain); + + /* subdomain-specific (longer) or other domain */ + if (l1 > l2) + return 0; + /* subdomain */ + if (l2 > l1 && domain[l2 - l1 - 1] == '.') + return !strcmp(&domain[l2 - l1], s); + + return !strcmp(s, domain); +} + +static int +matchrule(struct filterrule *f, const char *uri, const char *type, + const char *domain) +{ + /* NOTE: order matters, see FilterType enum values */ + struct filterdomain *d; + char pat[1024]; + int r, m; + + r = f->domains ? 0 : 1; + for (d = f->domains; d; d = d->next) { + if (matchdomain(d->domain, domain)) { + if (r && d->inverse) + r = 0; + else if (!r && !d->inverse) + r = 1; + } else if (r && !d->inverse) { + r = 0; + } + } + if (f->css) { + /* DEBUG */ +#if 0 + if (f->isexception) + printf("DEBUG, exception rule, CSS: %s, match? %d\n", + f->css, r); +#endif + return r; + } + +#if 1 + /* skip allow rule, TODO: inverse? */ + if (!r) + return 0; +#endif + +#if 0 + /* DEBUG: test, match if it is a simple pattern */ + char *p; + p = strchr(f->uri, '*'); + if (!p) + p = strchr(f->uri, '^'); + if (!p) { + /* TODO: write a test-case */ + if (f->block & FilterTypeMatchCase) { + if (f->matchbegin) + m = strncmp(uri, f->uri, strlen(f->uri)) == 0; + else if (f->matchend) + m = strlen(f->uri) <= strlen(uri) && + strcmp(&uri[strlen(uri) - strlen(f->uri)], f->uri) == 0; + else + m = strstr(uri, f->uri) ? 1 : 0; + } else { + if (f->matchbegin) + m = strncasecmp(uri, f->uri, strlen(f->uri)) == 0; + else if (f->matchend) + m = strlen(f->uri) <= strlen(uri) && + strcasecmp(&uri[strlen(uri) - strlen(f->uri)], f->uri) == 0; + else + m = strcasestr(uri, f->uri) ? 1 : 0; + } + /*m = r ? !m : m;*/ + return m; + } +#endif + + r = snprintf(pat, sizeof(pat), "%s%s%s", + f->matchbegin ? "" : "*", + f->uri, + f->matchend ? "" : "*"); + if (r == -1 || (size_t)r >= sizeof(pat)) { + fprintf(stderr, "warning: pattern too large, ignoring\n"); + return 0; + } + + m = 0; + if (!match(pat, uri, (f->block & FilterTypeMatchCase) ? 0 : 1)) { +#if 0 + for (; *type; type++) { + for (i = 0; blockstr[i]; i++) { + if (blockstr[i] == *type && + f->block & (1 << i)) + printf("block type '%c'\n", blockstr[i]); + return 1; + } + } + } + + return 0; +#endif + m = 1; + } + /*m = r ? !m : m;*/ + return m; +} + +static int +parserule(struct filterrule *f, char *s) +{ + struct filtertype key, *ft; + int inverse = 0; + char *p, *values; + + if (*s == '!' || (*s == '[' && s[strlen(s) - 1] == ']')) + return 0; /* skip comment or empty line */ + for (; *s && isspace(*s); s++) + ; + if (!*s) + return 0; /* line had only whitespace: skip */ + + memset(f, 0, sizeof(struct filterrule)); + + if ((p = strstr(s, "#@#"))) { + *p = '\0'; + if (parsedomainselement(f, s) < 0) + return -1; + *p = '#'; + if (!(f->css = westrdup(p + 3))) + return -1; + f->isexception = 1; + goto end; /* end of CSS rule */ + } + + /* element hiding rule, NOTE: no wildcards are supported, + "Simplified element hiding syntax" is not supported. */ + if ((p = strstr(s, "##"))) { + *p = '\0'; + if (parsedomainselement(f, s) < 0) + return -1; + *p = '#'; + if (!(f->css = westrdup(p + 2))) + return -1; + goto end; /* end of rule */ + } + + if (!strncmp(s, "@@", 2)) { + f->isexception = 1; + s += 2; + } + if (*s == '|') { + s++; + if (*s == '|') { + f->matchbegin = 1; + s++; + } else { + f->matchend = 1; + } + } + + /* no options, use rest of line as uri. */ + if (!(p = strrchr(s, '$'))) { + if (!(f->uri = westrdup(s))) + return -1; + goto end; + } + + /* has options */ + if (!(f->uri = westrndup(s, p - s))) + return -1; + s = ++p; + + /* blockmask, has options? default: allow all options, case-sensitive + * has no options? default: block all options, case-sensitive */ + f->block = *s ? (unsigned long)FilterTypeMatchCase : ~0UL; + do { + if ((p = strchr(s, ','))) + *p = '\0'; + /* match option */ + inverse = 0; + if (*s == '~') { + inverse = 1; + s++; + } + if ((values = strchr(s, '='))) + *(values) = '\0'; + key.name = s; + + ft = bsearch(&key, &filtertypes, + sizeof(filtertypes) / sizeof(*filtertypes), + sizeof(*filtertypes), filtertype_cmp); + + /* restore NUL-terminator for domain= option */ + if (values) + *(values++) = '='; + + if (ft) { + if (inverse) + f->block &= ~(ft->type); + else + f->block |= ft->type; + if (ft->fn && values) + ft->fn(f, values); + } else { + /* DEBUG */ + fprintf(stderr, "ignored: unknown option: '%s' " + "in rule: %s\n", key.name, f->uri); + } + + /* restore ',' */ + if (p) { + *p = ','; + s = p + 1; + } + } while (p); +end: + + return 1; +} + +#if 0 +static void +debugrule(struct filterrule *r) +{ + printf("\turi: %s\n\tcss: %s\n\tisexception: %d\n\tblockmask: " + "%lu\n===\n", r->uri ? r->uri : "", r->css ? r->css : "", + r->isexception, r->block); +} +#endif + +static int +loadrules(FILE *fp) +{ + struct filterrule f, *r, *rn = NULL; + char *line = NULL; + size_t linesiz = 0; + ssize_t n; + int ret; + + /* TODO: handle ferror() */ + /* load rules */ + while ((n = getline(&line, &linesiz, fp)) > 0) { + if (line[n - 1] == '\n') + line[--n] = '\0'; + if (n > 0 && line[n - 1] == '\r') + line[--n] = '\0'; + + if ((ret = parserule(&f, line) > 0)) { + if (!(r = wecalloc(1, sizeof(struct filterrule)))) + return -1; + if (!rules) + rules = rn = r; + else + rn = rn->next = r; + memcpy(rn, &f, sizeof(struct filterrule)); + } else if (ret < 0) { + return -1; + } + } + return (rules != NULL); +} + +char * +getglobalcss(void) +{ + return globalcss.data; +} + +char * +getdocumentcss(const char *uri) +{ + const char *s; + char domain[256]; + String sitecss; + struct filterrule *r; + size_t len; + + if (!uri) + return NULL; + + if (!(s = strstr(uri, "://"))) + return NULL; + s += sizeof("://") - 1; + len = strcspn(s, "/"); + memcpy(domain, s, len); + domain[len] = '\0'; + + printf("uri: %s\n", uri); + printf("domain: %s\n", domain); + + /* DEBUG: timing */ + struct timespec tp_start, tp_end, tp_diff; + if (clock_gettime(CLOCK_MONOTONIC, &tp_start) == -1) { + fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); + } + + /* site-specific CSS */ + memset(&sitecss, 0, sizeof(sitecss)); + for (r = rules; r; r = r->next) { + if (!r->css || !r->domains || !matchrule(r, "", "", domain)) + continue; + + len = strlen(r->css); + if (string_append(&sitecss, r->css, len) < len) + goto err; + + s = r->isexception ? "{display:initial;}" : "{display:none;}"; + len = strlen(s); + if (string_append(&sitecss, s, len) < len) + goto err; + } +/* printf("sitecss: %s\n", sitecss.data ? sitecss.data : "<empty>");*/ + + /* DEBUG: timing */ + if (clock_gettime(CLOCK_MONOTONIC, &tp_end) == -1) { + fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); + } + + tp_diff.tv_sec = tp_end.tv_sec - tp_start.tv_sec; + tp_diff.tv_nsec = tp_end.tv_nsec - tp_start.tv_nsec; + if (tp_diff.tv_nsec < 0) { + tp_diff.tv_sec--; + tp_diff.tv_nsec += 1000000000L; + } + + printf("timing: %zu sec, %.3f ms\n", + tp_diff.tv_sec, (float)tp_diff.tv_nsec / 1000000.0f); + + if (globalcss.data) + printf("global CSS length in bytes: %zu\n", strlen(globalcss.data)); + if (sitecss.data) + printf("site CSS length in bytes: %zu\n", strlen(sitecss.data)); + + return sitecss.data; + +err: + free(sitecss.data); + return NULL; +} + +int +checkrequest(const char *uri, const char *requri) +{ + char domain[256]; + struct filterrule *r; + const char *s; + size_t len; + int status = 1; + + if (!uri || !strcmp(requri, uri)) + return 1; + + s = strstr(uri, "://") + sizeof("://") - 1; + len = strcspn(s, "/"); + memcpy(domain, s, len); + domain[len] = '\0'; + + /* DEBUG: timing */ + struct timespec tp_start, tp_end, tp_diff; + if (clock_gettime(CLOCK_MONOTONIC, &tp_start) == -1) { + fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); + } + + /* match rules */ + for (r = rules; r; r = r->next) { + if (!r->css && matchrule(r, requri, "csio^", domain)) { + printf("requri: %s\n", requri); + printf("uri: %s\n", uri); + printf("domain: %s\n", domain); + + fprintf(stderr, "blocked: %s, %s\n", domain, requri); + + /* DEBUG: for showing the timing */ + status = 0; + goto end; + /*return 1;*/ + } + } + +end: + /* DEBUG: timing */ + if (clock_gettime(CLOCK_MONOTONIC, &tp_end) == -1) { + fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); + } + + tp_diff.tv_sec = tp_end.tv_sec - tp_start.tv_sec; + tp_diff.tv_nsec = tp_end.tv_nsec - tp_start.tv_nsec; + if (tp_diff.tv_nsec < 0) { + tp_diff.tv_sec--; + tp_diff.tv_nsec += 1000000000L; + } + + printf("%s [%s] timing: %zu sec, %.3f ms\n", + requri, uri, tp_diff.tv_sec, (float)tp_diff.tv_nsec / 1000000.0f); + + return status; +} + +void +init(void) +{ + struct filterrule *r; + FILE *fp; + char filepath[PATH_MAX], *e; + size_t len; + int n; + + if ((e = getenv("SURF_ADBLOCK_FILE"))) { + n = snprintf(filepath, sizeof(filepath), "%s", e); + } else { + if (!(e = getenv("HOME"))) + e = ""; + n = snprintf(filepath, sizeof(filepath), + "%s%s.surf/adblockrules", e, e[0] ? "/" : ""); + } + if (n < 0 || (size_t)n >= sizeof(filepath)) { + weprintf("fatal: rules file path too long"); + return; + } + + if (!(fp = fopen(filepath, "r"))) { + weprintf("fatal: cannot open rules file %s: %s\n", + filepath, strerror(errno)); + return; + } + + n = loadrules(fp); + fclose(fp); + if (n < 1) { + if (n < 0) { + weprintf("fatal: cannot read rules from file %s: %s\n", + filepath, strerror(errno)); + } else { + weprintf("fatal: cannot read any rule from file %s\n", + filepath); + } + return; + } + + /* general CSS rules: all sites */ + for (r = rules; r; r = r->next) { + if (!r->css || r->domains) + continue; + + len = strlen(r->css); + if (string_append(&globalcss, r->css, strlen(r->css)) < len) { + weprintf("cannot load global css selectors " + "in memory\n"); + cleanup(); + return; + } + len = sizeof("{display:none;}") - 1; + if (string_append(&globalcss, "{display:none;}", len) < len) { + weprintf("cannot append css rule " + "to global css selectors\n"); + cleanup(); + return; + } + } +} (DIR) diff --git a/surf-adblock.c b/surf-adblock.c @@ -8,769 +8,30 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <wchar.h> -#include <wctype.h> #include <webkit2/webkit-web-extension.h> #include <webkitdom/webkitdom.h> -/* String data / memory pool */ -typedef struct string { - char *data; /* data */ - size_t datasz; /* allocated size */ - size_t len; /* current string length */ -} String; +#include "adblock.h" typedef struct Page { guint64 id; WebKitWebPage *webpage; - WebKitDOMDOMWindow *view; + /*WebKitDOMDOMWindow *view;*/ struct Page *next; } Page; -struct filterdomain { - char *domain; - int inverse; - struct filterdomain *next; -}; - -struct filterrule { - /* type: match mask, must be atleast 32-bit, see FilterType enum */ - unsigned long block; - int matchbegin; - int matchend; - /* is exception rule: prefix @@ for ABP or #@# for CSS */ - int isexception; - char *css; /* if non-NULL is CSS rule / hide element rule */ - char *uri; - struct filterdomain *domains; - struct filterrule *next; -}; - -enum { - FilterTypeScript = 1 << 0, - FilterTypeImage = 1 << 1, - FilterTypeCSS = 1 << 2, - FilterTypeObject = 1 << 3, - FilterTypeXHR = 1 << 4, - FilterTypeObjectSub = 1 << 5, - FilterTypeSubDoc = 1 << 6, - FilterTypePing = 1 << 7, - FilterTypeDocument = 1 << 8, - FilterTypeElemHide = 1 << 9, - FilterTypeOther = 1 << 10, - FilterTypeGenericHide = 1 << 11, - FilterTypeGenericBlock = 1 << 12, - FilterTypeMatchCase = 1 << 13, -}; - -struct filtertype { - /* `type` must be atleast 32-bit, see FilterType enum */ - unsigned long type; - char *name; - size_t namelen; - int allowinverse; - int allownormal; - int onlyexception; - int (*fn)(struct filterrule *, char *); -}; - -static int parsedomainsoption(struct filterrule *, char *); - -#define STRP(s) s,sizeof(s)-1 - -static struct filtertype filtertypes[] = { - /* NOTE: options with 'type' = 0 are silently ignored and treated as - * requests for now */ - { 0, STRP("collapse"), 1, 1, 0, NULL }, - { FilterTypeDocument, STRP("document"), 1, 0, 1, NULL }, - { 0, STRP("domain"), 0, 1, 0, - /* domain=... */ &parsedomainsoption }, - { 0, STRP("donottrack"), 1, 1, 0, NULL }, - { FilterTypeElemHide, STRP("elemhide"), 0, 0, 1, NULL }, - { 0, STRP("font"), 1, 1, 0, NULL }, - { FilterTypeGenericBlock, STRP("genericblock"), 1, 1, 1, NULL }, - { FilterTypeGenericHide, STRP("generichide"), 1, 1, 1, NULL }, - { FilterTypeImage, STRP("image"), 1, 1, 0, NULL }, - { FilterTypeMatchCase, STRP("match-case"), 1, 1, 0, NULL }, - { 0, STRP("media"), 1, 1, 0, NULL }, - { FilterTypeObject, STRP("object"), 1, 1, 0, NULL }, - { FilterTypeObjectSub, STRP("object-subrequest"), 1, 1, 0, NULL }, - { FilterTypeOther, STRP("other"), 1, 1, 0, NULL }, - { FilterTypePing, STRP("ping"), 1, 1, 0, NULL }, - { 0, STRP("popup"), 1, 1, 0, NULL }, - { FilterTypeScript, STRP("script"), 1, 1, 0, NULL }, - { FilterTypeCSS, STRP("stylesheet"), 1, 1, 0, NULL }, - { FilterTypeSubDoc, STRP("subdocument"), 1, 1, 0, NULL }, - { 0, STRP("third-party"), 1, 1, 0, NULL }, - { FilterTypeXHR, STRP("xmlhttprequest"), 1, 1, 0, NULL }, - /* NOTE: site-key not supported */ -}; - -static String globalcss; static Page *pages; -static struct filterrule *rules; - -static void -cleanup(void) -{ - struct filterrule *r; - struct filterdomain *d; - - free(globalcss.data); - - for (r = rules; r; r = rules) { - for (d = r->domains; d; d = r->domains) { - free(d->domain); - r->domains = d->next; - free(d); - } - free(r->css); - free(r->uri); - rules = r->next; - free(r); - } -} - -static void -weprintf(const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "surf-adblock: "); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -static size_t -string_buffer_realloc(String *s, size_t newsz) -{ - char *tmp; - size_t allocsz; - - for (allocsz = 64; allocsz <= newsz; allocsz *= 2) - ; - if (!(tmp = realloc(s->data, allocsz))) { - weprintf("realloc: %s\n", strerror(errno)); - } else { - s->data = tmp; - s->datasz = allocsz; - } - - return s->datasz; -} - -static size_t -string_append(String *s, const char *data, size_t len) -{ - size_t newlen; - - if (!len) - return len; - - newlen = s->len + len; - /* check if allocation is necesary, don't shrink buffer, - * should be more than datasz ofcourse. */ - if (newlen >= s->datasz) { - if (string_buffer_realloc(s, newlen + 1) <= newlen) - return 0; - } - memcpy(s->data + s->len, data, len); - s->len = newlen; - s->data[s->len] = '\0'; - return len; -} - -static void * -wecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - weprintf("calloc: %s\n", strerror(errno)); - - return p; -} - -static char * -westrndup(const char *s, size_t n) -{ - char *p; - - if (!(p = strndup(s, n))) - weprintf("strndup: %s\n", strerror(errno)); - return p; -} - -static char * -westrdup(const char *s) -{ - char *p; - - if (!(p = strdup(s))) - weprintf("strdup: %s\n", strerror(errno)); - - return p; -} - -#define END 0 -#define UNMATCHABLE -2 -#define BRACKET -3 -#define CARET -4 -#define STAR -5 - -static int -str_next(const char *str, size_t n, size_t *step) -{ - if (!n) { - *step = 0; - return 0; - } - if (str[0] >= 128U) { - wchar_t wc; - int k = mbtowc(&wc, str, n); - if (k<0) { - *step = 1; - return -1; - } - *step = k; - return wc; - } - *step = 1; - - return str[0]; -} - -static int -pat_next(const char *pat, size_t m, size_t *step) -{ - int esc = 0; - - if (!m || !*pat) { - *step = 0; - return END; - } - *step = 1; - if (pat[0]=='\\' && pat[1]) { - *step = 2; - pat++; - esc = 1; - goto escaped; - } - if (pat[0]=='^') - return CARET; - if (pat[0] == '*') - return STAR; -escaped: - if (pat[0] >= 128U) { - wchar_t wc; - int k = mbtowc(&wc, pat, m); - if (k<0) { - *step = 0; - return UNMATCHABLE; - } - *step = k + esc; - return wc; - } - return pat[0]; -} - -static int -casefold(int k) -{ - int c = towupper(k); - return c == k ? towlower(k) : c; -} - -/* match() based on musl-libc fnmatch: - https://git.musl-libc.org/cgit/musl/tree/src/regex/fnmatch.c */ -static int -match(const char *pat, const char *str, int fcase) -{ - size_t m = -1, n = -1; - const char *p, *ptail, *endpat; - const char *s, *stail, *endstr; - size_t pinc, sinc, tailcnt=0; - int c, k, kfold; - - for (;;) { - switch ((c = pat_next(pat, m, &pinc))) { - case UNMATCHABLE: - return 1; - case STAR: - pat++; - m--; - break; - default: - k = str_next(str, n, &sinc); - /* TODO: write a test-case */ - if (c == CARET && (k == '?' || k == '/' || k <= 0)) - return 1; - if (k <= 0) - return (c==END) ? 0 : 1; - str += sinc; - n -= sinc; - kfold = fcase ? casefold(k) : k; - if (k != c && kfold != c) - return 1; - pat+=pinc; - m-=pinc; - continue; - } - break; - } - - /* Compute real pat length if it was initially unknown/-1 */ - m = strnlen(pat, m); - endpat = pat + m; - - /* Find the last * in pat and count chars needed after it */ - for (p=ptail=pat; p<endpat; p+=pinc) { - switch (pat_next(p, endpat-p, &pinc)) { - case UNMATCHABLE: - return 1; - case STAR: - tailcnt=0; - ptail = p+1; - break; - default: - tailcnt++; - break; - } - } - - /* Past this point we need not check for UNMATCHABLE in pat, - * because all of pat has already been parsed once. */ - - /* Compute real str length if it was initially unknown/-1 */ - n = strnlen(str, n); - endstr = str + n; - if (n < tailcnt) return 1; - - /* Find the final tailcnt chars of str, accounting for UTF-8. - * On illegal sequences we may get it wrong, but in that case - * we necessarily have a matching failure anyway. */ - for (s=endstr; s>str && tailcnt; tailcnt--) { - if (s[-1] < 128U || MB_CUR_MAX==1) s--; - else while ((unsigned char)*--s-0x80U<0x40 && s>str); - } - if (tailcnt) return 1; - stail = s; - - /* Check that the pat and str tails match */ - p = ptail; - for (;;) { - c = pat_next(p, endpat-p, &pinc); - p += pinc; - if ((k = str_next(s, endstr-s, &sinc)) <= 0) { - if (c != END) return 1; - break; - } - s += sinc; - kfold = fcase ? casefold(k) : k; - if (k != c && kfold != c) - return 1; - } - - /* We're all done with the tails now, so throw them out */ - endstr = stail; - endpat = ptail; - - /* Match pattern components until there are none left */ - while (pat<endpat) { - p = pat; - s = str; - for (;;) { - c = pat_next(p, endpat-p, &pinc); - p += pinc; - /* Encountering * completes/commits a component */ - if (c == STAR) { - pat = p; - str = s; - break; - } - k = str_next(s, endstr-s, &sinc); - if (!k) - return 1; - kfold = fcase ? casefold(k) : k; - if (k != c && kfold != c) - break; - s += sinc; - } - if (c == STAR) continue; - /* If we failed, advance str, by 1 char if it's a valid - * char, or past all invalid bytes otherwise. */ - k = str_next(str, endstr-str, &sinc); - if (k > 0) str += sinc; - else for (str++; str_next(str, endstr-str, &sinc)<0; str++); - } - - return 0; -} - -/* -domain=... if domain is prefixed with ~, ignore. -multiple domains can be separated with | -*/ -static int -parsedomains(const char *s, int sep, struct filterdomain **head) -{ - struct filterdomain *d, *last = *head = NULL; - char *p; - int inverse; - - do { - inverse = 0; - if (*s == '~') { - inverse = !inverse; - s++; - } - if (!*s || *s == sep) - break; - - if (!(d = wecalloc(1, sizeof(struct filterdomain)))) - return -1; - if ((p = strchr(s, sep))) { /* TODO: should not contain ',' */ - d->domain = westrndup(s, p - s); - s = p + 1; - } else { - d->domain = westrdup(s); - } - if (!d->domain) - return -1; - d->inverse = inverse; - - if (!*head) - *head = last = d; - else - last = last->next = d; - } while (p); - - return (*head != NULL); -} - -static int -parsedomainselement(struct filterrule *f, char *s) -{ - struct filterdomain *d, *last; - - for (last = f->domains; last && last->next; last = last->next) - ; - - if (parsedomains(s, ',', &d) < 0) - return -1; - if (last) - last->next = d; - else - f->domains = d; - - return (d != NULL); -} - -static int -parsedomainsoption(struct filterrule *f, char *s) -{ - struct filterdomain *d, *last; - - for (last = f->domains; last && last->next; last = last->next) - ; - - if (parsedomains(s, '|', &d) < 0) - return -1; - if (last) - last->next = d; - else - f->domains = d; - - return (d != NULL); -} - -static int -filtertype_cmp(const void *a, const void *b) -{ - return strcmp(((struct filtertype *)a)->name, - ((struct filtertype *)b)->name); -} - -/* check if domain is the same domain or a subdomain of `s` */ -static int -matchdomain(const char *s, const char *domain) -{ - size_t l1, l2; - - l1 = strlen(s); - l2 = strlen(domain); - - /* subdomain-specific (longer) or other domain */ - if (l1 > l2) - return 0; - /* subdomain */ - if (l2 > l1 && domain[l2 - l1 - 1] == '.') - return !strcmp(&domain[l2 - l1], s); - - return !strcmp(s, domain); -} - -static int -matchrule(struct filterrule *f, const char *uri, const char *type, - const char *domain) -{ - /* NOTE: order matters, see FilterType enum values */ - struct filterdomain *d; - char pat[1024]; - int r, m; - - r = f->domains ? 0 : 1; - for (d = f->domains; d; d = d->next) { - if (matchdomain(d->domain, domain)) { - if (r && d->inverse) - r = 0; - else if (!r && !d->inverse) - r = 1; - } else if (r && !d->inverse) { - r = 0; - } - } - if (f->css) { - /* DEBUG */ -#if 0 - if (f->isexception) - printf("DEBUG, exception rule, CSS: %s, match? %d\n", - f->css, r); -#endif - return r; - } - -#if 1 - /* skip allow rule, TODO: inverse? */ - if (!r) - return 0; -#endif - -#if 0 - /* DEBUG: test, match if it is a simple pattern */ - char *p; - p = strchr(f->uri, '*'); - if (!p) - p = strchr(f->uri, '^'); - if (!p) { - /* TODO: write a test-case */ - if (f->block & FilterTypeMatchCase) { - if (f->matchbegin) - m = strncmp(uri, f->uri, strlen(f->uri)) == 0; - else if (f->matchend) - m = strlen(f->uri) <= strlen(uri) && - strcmp(&uri[strlen(uri) - strlen(f->uri)], f->uri) == 0; - else - m = strstr(uri, f->uri) ? 1 : 0; - } else { - if (f->matchbegin) - m = strncasecmp(uri, f->uri, strlen(f->uri)) == 0; - else if (f->matchend) - m = strlen(f->uri) <= strlen(uri) && - strcasecmp(&uri[strlen(uri) - strlen(f->uri)], f->uri) == 0; - else - m = strcasestr(uri, f->uri) ? 1 : 0; - } - /*m = r ? !m : m;*/ - return m; - } -#endif - - r = snprintf(pat, sizeof(pat), "%s%s%s", - f->matchbegin ? "" : "*", - f->uri, - f->matchend ? "" : "*"); - if (r == -1 || (size_t)r >= sizeof(pat)) { - fprintf(stderr, "warning: pattern too large, ignoring\n"); - return 0; - } - - m = 0; - if (!match(pat, uri, (f->block & FilterTypeMatchCase) ? 0 : 1)) { -#if 0 - for (; *type; type++) { - for (i = 0; blockstr[i]; i++) { - if (blockstr[i] == *type && - f->block & (1 << i)) - printf("block type '%c'\n", blockstr[i]); - return 1; - } - } - } - - return 0; -#endif - m = 1; - } - /*m = r ? !m : m;*/ - return m; -} - -static int -parserule(struct filterrule *f, char *s) -{ - struct filtertype key, *ft; - int inverse = 0; - char *p, *values; - - if (*s == '!' || (*s == '[' && s[strlen(s) - 1] == ']')) - return 0; /* skip comment or empty line */ - for (; *s && isspace(*s); s++) - ; - if (!*s) - return 0; /* line had only whitespace: skip */ - - memset(f, 0, sizeof(struct filterrule)); - - if ((p = strstr(s, "#@#"))) { - *p = '\0'; - if (parsedomainselement(f, s) < 0) - return -1; - *p = '#'; - if (!(f->css = westrdup(p + 3))) - return -1; - f->isexception = 1; - goto end; /* end of CSS rule */ - } - - /* element hiding rule, NOTE: no wildcards are supported, - "Simplified element hiding syntax" is not supported. */ - if ((p = strstr(s, "##"))) { - *p = '\0'; - if (parsedomainselement(f, s) < 0) - return -1; - *p = '#'; - if (!(f->css = westrdup(p + 2))) - return -1; - goto end; /* end of rule */ - } - - if (!strncmp(s, "@@", 2)) { - f->isexception = 1; - s += 2; - } - if (*s == '|') { - s++; - if (*s == '|') { - f->matchbegin = 1; - s++; - } else { - f->matchend = 1; - } - } - - /* no options, use rest of line as uri. */ - if (!(p = strrchr(s, '$'))) { - if (!(f->uri = westrdup(s))) - return -1; - goto end; - } - - /* has options */ - if (!(f->uri = westrndup(s, p - s))) - return -1; - s = ++p; - - /* blockmask, has options? default: allow all options, case-sensitive - * has no options? default: block all options, case-sensitive */ - f->block = *s ? (unsigned long)FilterTypeMatchCase : ~0UL; - do { - if ((p = strchr(s, ','))) - *p = '\0'; - /* match option */ - inverse = 0; - if (*s == '~') { - inverse = 1; - s++; - } - if ((values = strchr(s, '='))) - *(values) = '\0'; - key.name = s; - - ft = bsearch(&key, &filtertypes, - sizeof(filtertypes) / sizeof(*filtertypes), - sizeof(*filtertypes), filtertype_cmp); - - /* restore NUL-terminator for domain= option */ - if (values) - *(values++) = '='; - - if (ft) { - if (inverse) - f->block &= ~(ft->type); - else - f->block |= ft->type; - if (ft->fn && values) - ft->fn(f, values); - } else { - /* DEBUG */ - fprintf(stderr, "ignored: unknown option: '%s' " - "in rule: %s\n", key.name, f->uri); - } - - /* restore ',' */ - if (p) { - *p = ','; - s = p + 1; - } - } while (p); -end: - - return 1; -} - -#if 0 -static void -debugrule(struct filterrule *r) -{ - printf("\turi: %s\n\tcss: %s\n\tisexception: %d\n\tblockmask: " - "%lu\n===\n", r->uri ? r->uri : "", r->css ? r->css : "", - r->isexception, r->block); -} -#endif - -static int -loadrules(FILE *fp) -{ - struct filterrule f, *r, *rn = NULL; - char *line = NULL; - size_t linesiz = 0; - ssize_t n; - int ret; - - /* TODO: handle ferror() */ - /* load rules */ - while ((n = getline(&line, &linesiz, fp)) > 0) { - if (line[n - 1] == '\n') - line[--n] = '\0'; - if (n > 0 && line[n - 1] == '\r') - line[--n] = '\0'; - - if ((ret = parserule(&f, line) > 0)) { - if (!(r = wecalloc(1, sizeof(struct filterrule)))) - return -1; - if (!rules) - rules = rn = r; - else - rn = rn->next = r; - memcpy(rn, &f, sizeof(struct filterrule)); - } else if (ret < 0) { - return -1; - } - } - return (rules != NULL); -} static Page * newpage(WebKitWebPage *page) { Page *p; - if (!(p = wecalloc(1, sizeof(Page)))) + if (!(p = calloc(1, sizeof(Page)))) { + fprintf(stderr, "surf-adblock: calloc: %s\n", strerror(errno)); return NULL; + } p->next = pages; pages = p; @@ -783,146 +44,32 @@ newpage(WebKitWebPage *page) static void documentloaded(WebKitWebPage *wp, Page *p) { - char domain[256]; WebKitDOMDocument *doc = webkit_web_page_get_dom_document(wp); WebKitDOMHTMLElement *body = webkit_dom_document_get_body(doc); WebKitDOMElement *el; - String sitecss; - struct filterrule *r; - const char *s, *uri = webkit_web_page_get_uri(p->webpage); - size_t len; - - if (!uri) - return; - - s = strstr(uri, "://") + sizeof("://") - 1; - len = strcspn(s, "/"); - memcpy(domain, s, len); - domain[len] = '\0'; - - printf("uri: %s\n", uri); - printf("domain: %s\n", domain); - - /* DEBUG: timing */ - struct timespec tp_start, tp_end, tp_diff; - if (clock_gettime(CLOCK_MONOTONIC, &tp_start) == -1) { - fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); - } - - /* site-specific CSS */ - memset(&sitecss, 0, sizeof(sitecss)); - for (r = rules; r; r = r->next) { - if (!r->css || !r->domains || !matchrule(r, "", "", domain)) - continue; - - len = strlen(r->css); - if (string_append(&sitecss, r->css, len) < len) - return; - - s = r->isexception ? "{display:initial;}" : "{display:none;}"; - len = strlen(s); - if (string_append(&sitecss, s, len) < len) - return; - } -/* printf("sitecss: %s\n", sitecss.data ? sitecss.data : "<empty>");*/ - - /* DEBUG: timing */ - if (clock_gettime(CLOCK_MONOTONIC, &tp_end) == -1) { - fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); - } - - tp_diff.tv_sec = tp_end.tv_sec - tp_start.tv_sec; - tp_diff.tv_nsec = tp_end.tv_nsec - tp_start.tv_nsec; - if (tp_diff.tv_nsec < 0) { - tp_diff.tv_sec--; - tp_diff.tv_nsec += 1000000000L; - } - - printf("timing: %zu sec, %.3f ms\n", - tp_diff.tv_sec, (float)tp_diff.tv_nsec / 1000000.0f); + const char *uri = webkit_web_page_get_uri(p->webpage); + char *css, *globalcss; - if (globalcss.data) - printf("global CSS length in bytes: %zu\n", strlen(globalcss.data)); - if (sitecss.data) - printf("site CSS length in bytes: %zu\n", strlen(sitecss.data)); + /*p->view = webkit_dom_document_get_default_view(doc);*/ - p->view = webkit_dom_document_get_default_view(doc); - - if (globalcss.data) { + if ((globalcss = getglobalcss())) { el = webkit_dom_document_create_element(doc, "style", NULL); webkit_dom_element_set_attribute(el, "type", "text/css", NULL); - webkit_dom_element_set_inner_html(el, globalcss.data, NULL); + webkit_dom_element_set_inner_html(el, globalcss, NULL); webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(el), NULL); } - if (sitecss.data) { + if ((css = getdocumentcss(uri))) { el = webkit_dom_document_create_element(doc, "style", NULL); webkit_dom_element_set_attribute(el, "type", "text/css", NULL); - webkit_dom_element_set_inner_html(el, sitecss.data, NULL); + webkit_dom_element_set_inner_html(el, css, NULL); webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(el), NULL); } - free(sitecss.data); -} - -int -checkrequest(const char *uri, const char *requri) -{ - char domain[256]; - struct filterrule *r; - const char *s; - size_t len; - int status = 1; - - if (!uri || !strcmp(requri, uri)) - return 1; - - s = strstr(uri, "://") + sizeof("://") - 1; - len = strcspn(s, "/"); - memcpy(domain, s, len); - domain[len] = '\0'; - - /* DEBUG: timing */ - struct timespec tp_start, tp_end, tp_diff; - if (clock_gettime(CLOCK_MONOTONIC, &tp_start) == -1) { - fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); - } - - /* match rules */ - for (r = rules; r; r = r->next) { - if (!r->css && matchrule(r, requri, "csio^", domain)) { - printf("requri: %s\n", requri); - printf("uri: %s\n", uri); - printf("domain: %s\n", domain); - - fprintf(stderr, "blocked: %s, %s\n", domain, requri); - - /* DEBUG: for showing the timing */ - status = 0; - goto end; - /*return 1;*/ - } - } - -end: - /* DEBUG: timing */ - if (clock_gettime(CLOCK_MONOTONIC, &tp_end) == -1) { - fprintf(stderr, "clock_gettime: %s\n", strerror(errno)); - } - - tp_diff.tv_sec = tp_end.tv_sec - tp_start.tv_sec; - tp_diff.tv_nsec = tp_end.tv_nsec - tp_start.tv_nsec; - if (tp_diff.tv_nsec < 0) { - tp_diff.tv_sec--; - tp_diff.tv_nsec += 1000000000L; - } - - printf("%s [%s] timing: %zu sec, %.3f ms\n", - requri, uri, tp_diff.tv_sec, (float)tp_diff.tv_nsec / 1000000.0f); - - return status; + free(css); + /* NOTE: globalcss free'd at cleanup() */ } static gboolean @@ -941,69 +88,6 @@ sendrequest(WebKitWebPage *wp, WebKitURIRequest *req, return status; } -void -init(void) -{ - struct filterrule *r; - FILE *fp; - char filepath[PATH_MAX], *e; - size_t len; - int n; - - if ((e = getenv("SURF_ADBLOCK_FILE"))) { - n = snprintf(filepath, sizeof(filepath), "%s", e); - } else { - if (!(e = getenv("HOME"))) - e = ""; - n = snprintf(filepath, sizeof(filepath), - "%s%s.surf/adblockrules", e, e[0] ? "/" : ""); - } - if (n < 0 || (size_t)n >= sizeof(filepath)) { - weprintf("fatal: rules file path too long"); - return; - } - - if (!(fp = fopen(filepath, "r"))) { - weprintf("fatal: cannot open rules file %s: %s\n", - filepath, strerror(errno)); - return; - } - - n = loadrules(fp); - fclose(fp); - if (n < 1) { - if (n < 0) { - weprintf("fatal: cannot read rules from file %s: %s\n", - filepath, strerror(errno)); - } else { - weprintf("fatal: cannot read any rule from file %s\n", - filepath); - } - return; - } - - /* general CSS rules: all sites */ - for (r = rules; r; r = r->next) { - if (!r->css || r->domains) - continue; - - len = strlen(r->css); - if (string_append(&globalcss, r->css, strlen(r->css)) < len) { - weprintf("cannot load global css selectors " - "in memory\n"); - cleanup(); - return; - } - len = sizeof("{display:none;}") - 1; - if (string_append(&globalcss, "{display:none;}", len) < len) { - weprintf("cannot append css rule " - "to global css selectors\n"); - cleanup(); - return; - } - } -} - static void webpagecreated(WebKitWebExtension *e, WebKitWebPage *p, gpointer unused) {