diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c3077cb --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: true +AlignOperands: true +AlignTrailingComments: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AlwaysBreakAfterReturnType: AllDefinitions +BinPackArguments: false +BinPackParameters: false +BreakBeforeBraces: Allman +SpaceBeforeParens: Never +IncludeBlocks: Regroup +ReflowComments: false +SortIncludes: true +UseTab: ForIndentation +IndentWidth: 2 +TabWidth: 2 +ColumnLimit: 100 + +NamespaceIndentation: All diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cd52797 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# https://EditorConfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = tab +indent_size = tab +max_line_length = 100 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f3b962 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/badwolf +/locale diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6753a20 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ +image: archlinux/base:latest + +before_script: + - pacman -Syu --noconfirm make clang + +stages: + - lint + - test + - analysis + +test: + stage: test + script: + - pacman -Syu --noconfirm webkit2gtk pkg-config gettext gcc + - make CC=gcc test + - make clean + - make CC=clang test + - make install + +format: + stage: lint + script: + - pacman -Syu --noconfirm git + - make format + - git diff --exit-code + +scan-build: + stage: analysis + script: + - pacman -Syu --noconfirm webkit2gtk pkg-config gettext gcc + - scan-build --use-cc=gcc -o scan-build-gcc make + - make clean + - scan-build --use-cc=clang -o scan-build-clang make + artifacts: + paths: + - scan-build-gcc/* + - scan-build-clang/* diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..6ba3f5f --- /dev/null +++ b/COPYING @@ -0,0 +1,26 @@ +Copyright (c) 2019-2020, Badwolf Authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/KnowledgeBase.md b/KnowledgeBase.md new file mode 100644 index 0000000..f5f9f9a --- /dev/null +++ b/KnowledgeBase.md @@ -0,0 +1,4 @@ +- Similar WebKitGTK+ browser: https://www.uninformativ.de/git/lariza/ +- WebKit2 Extensions Tutorial: https://blogs.igalia.com/carlosgc/2013/09/10/webkit2gtk-web-process-extensions/ +- https://trac.torproject.org/projects/tor/wiki/doc/ImportantGoogleChromeBugs / https://trac.torproject.org/projects/tor/ticket/1925 +- https://webkit.org/blog/3476/content-blockers-first-look/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2ba7432 --- /dev/null +++ b/Makefile @@ -0,0 +1,100 @@ +# POSIX-ish Makefile with extensions common to *BSD and GNU such as: +# - Usage of backticks for shell evaluation +# - Usage of ?= for defining variables when not already defined +# - Usage of += for appending to a variable + +PACKAGE = Badwolf +VERSION = 1.0.3 +VERSION_FULL = $(VERSION)`./version.sh` + +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +MANDIR ?= $(PREFIX)/share/man +DOCDIR ?= $(PREFIX)/share/doc/badwolf-$(VERSION) +DATADIR ?= $(PREFIX)/share/badwolf +APPSDIR ?= $(PREFIX)/share/applications + +CC ?= cc +CFLAGS ?= -g -Wall -Wextra -Wconversion -Wsign-conversion -O2 +DBG ?= +PKGCONFIG ?= pkg-config +MSGFMT ?= msgfmt +INKSCAPE ?= inkscape + +# for i in 24 32 48 64 128 256; do echo icons/hicolor/${i}x${i}/apps/badwolf.png; done | tr '\n' ' ' +ICON_SIZES = icons/hicolor/24x24/apps/badwolf.png icons/hicolor/32x32/apps/badwolf.png icons/hicolor/48x48/apps/badwolf.png icons/hicolor/64x64/apps/badwolf.png icons/hicolor/128x128/apps/badwolf.png icons/hicolor/256x256/apps/badwolf.png + +DEPS = gtk+-3.0 webkit2gtk-4.0 libsoup-2.4 +SRCS = uri.c uri_test.c keybindings.c downloads.c badwolf.c +OBJS = uri.o keybindings.o downloads.o badwolf.o +OBJS_test = uri_test.o +EXE = lrrh +EXE_test = uri_test +TRANS = fr.mo pt_BR.mo tr.mo +DOCS = usr.bin.badwolf README.md KnowledgeBase.md interface.txt + +CDEPS = -DDATADIR=\"$(DATADIR)\" -DPACKAGE=\"$(PACKAGE)\" -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION_FULL)\" +CDEPS += `$(PKGCONFIG) --cflags $(DEPS)` +LIBS = `$(PKGCONFIG) --libs $(DEPS)` + +all: $(EXE) $(TRANS) po/messages.pot + +icons: $(ICON_SIZES) + +icons/hicolor/scalable/apps/badwolf.svg: badwolf.svg + mkdir -p icons/hicolor/scalable/apps + scour --no-line-breaks --enable-id-stripping --remove-metadata $< $@ + +icons/hicolor/%/apps/badwolf.png: icons/hicolor/scalable/apps/badwolf.svg + mkdir -p `dirname $@` + $(INKSCAPE) `echo $@ | cut -d/ -f3 | ./icons_size.sh` $< -o $@ + +po/messages.pot: $(SRCS) + xgettext --keyword=_ --language=C --from-code=UTF-8 -o $@ --add-comments --sort-output --copyright-holder="Badwolf Authors " --package-name="$(PACKAGE)" --package-version="$(VERSION_FULL)" --msgid-bugs-address="contact+badwolf-msgid@hacktivis.me" $(SRCS) + +po/%.po: po/messages.pot + msgmerge --update --backup=off $@ $< + +${TRANS}: po/${@:.mo=.po} + mkdir -p locale/${@:.mo=}/LC_MESSAGES + $(MSGFMT) -o locale/${@:.mo=}/LC_MESSAGES/$(PACKAGE).mo po/${@:.mo=.po} + +lrrh: $(OBJS) + $(CC) -std=c11 -o $@ $(OBJS) $(LDFLAGS) $(LIBS) + +.c: + $(CC) -std=c11 $(CFLAGS) $(CDEPS) $(LDFLAGS) $(LIBS) -o $@ $< + +.c.o: + $(CC) -std=c11 $(CFLAGS) $(CDEPS) -c -o $@ $< + +uri_test: uri.o uri_test.o + $(CC) -std=c11 -o $@ uri.o uri_test.o $(LDFLAGS) $(LIBS) + $(DBG) ./$@ + +.PHONY: test +test: $(EXE_test) + +.PHONY: install +install: all + mkdir -p $(DESTDIR)$(BINDIR) + cp -p badwolf $(DESTDIR)$(BINDIR)/ + mkdir -p $(DESTDIR)$(MANDIR)/man1 + cp -p badwolf.1 $(DESTDIR)$(MANDIR)/man1/ + mkdir -p $(DESTDIR)$(DATADIR)/locale + cp -r locale/ $(DESTDIR)$(DATADIR)/ + cp interface.css $(DESTDIR)$(DATADIR)/ + mkdir -p $(DESTDIR)$(APPSDIR) + cp -p badwolf.desktop $(DESTDIR)$(APPSDIR)/ + mkdir -p $(DESTDIR)$(DOCDIR) + cp -p $(DOCS) $(DESTDIR)$(DOCDIR)/ + mkdir -p $(DESTDIR)$(PREFIX)/share + cp -r icons $(DESTDIR)$(PREFIX)/share/ + @printf '\nNote: An example AppArmor profile has been installed at '$(DOCDIR)/usr.bin.badwolf'\n' + +.PHONY: clean +clean: + rm -fr locale $(OBJS) $(OBJS_test) $(EXE) $(EXE_test) + +format: *.c *.h + clang-format -style=file -assume-filename=.clang-format -i *.c *.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e868eb --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# BadWolf +Minimalist and privacy-oriented WebKitGTK+ browser. + +Homepage: + +``` +Copyright © 2019-2020 Badwolf Authors +SPDX-License-Identifier: BSD-3-Clause +``` + +The name is a reference to BBC’s Doctor Who Tv serie, I took it simply because I wanted to have a specie in the name, like some other web browsers do, but doesn’t go into the “gentle” zone. + +## Differencies +Comparing from other small WebKit browsers for unixes found in the wild: + +- Independent of environment, should just work if GTK and WebKitGTK does +- Storing data should be: + - explicit and optionnal (ie. Applying preferences doesn't imply Saving to disk) + - not queryabe by WebKit (so the web can't use it) + - done in a standard format (like XBEL for bookmarks) +- Static UI, no element should be added at runtime, this is to avoid potential tracking via viewport changes +- Small codebase, should be possible to read and understand it completely over an afternoon. +- Does not use modal editing (from vi) as that was designed for editing, not browsing +- UTF-8 encoding by default + +Motivation from other clients + +## Contributing +### Translations +You need to have gettext installed. If you want a GUI, poedit exists and Weblate is a good web platform that I might consider hosting at some point. + +- Syncing POT file with the source code: ``make po/messages.pot`` +- Syncing PO file with the POT file: ``make po/de.po`` +- Initialising a new PO file (example for German, `de_DE`): ``msginit -l de_DE -i po/messages.pot -o po/de.po`` + +## Contacts / Discussions +- IRC: `#badwolf-browser` on FreeNode +- Matrix (bridge): + +## Repositories +### git +- Main: , +- Mirror: , this one can also be used if you prefer tickets/PRs over emails + +### release assets +- Main: +- Mirror: + +- `*.tar.*` files are tarballs archives to be extracted with a program like `tar(1)`, GNU tar and LibArchive bsdtar are known to work. +- `*.sig` files are OpenPGP signatures done with my [key](https://hacktivis.me/key.asc)(`DDC9 237C 14CF 6F4D D847 F6B3 90D9 3ACC FEFF 61AE`). +- `*.sign` files are minisign (OpenBSD `signify(1)` compatible) signatures, they key used for it can be found at as well as other places (feel free to ping me to get it) + +## Manual Installation +Dependencies are: +- C11 Compiler (such as clang or gcc) +- [WebKitGTK](https://webkitgtk.org/), only the latest stable is supported +- POSIX make with extension for shell in variables (works with GNU, {Net,Free,Open}BSD) +- A pkg-config implementation (pkgconf is recommended) +- (optionnal) gettext implementation (such as GNU Gettext) + +Compilation is done with `make`, install with `make install` (`DESTDIR` and `PREFIX` environment variables are supported, amongs other common ones). An example AppArmor profile is provided at `usr.bin.badwolf`, please do some long runtime checks before shipping it or a modified version, help can be provided but with no support. + +You'll also need inkscape (command line only) if you want to regenerate the icons, for example after modifying them or adding a new size. These aren't needed for normal installation as it is bundled. + +## Notes +Most of the privacy/security stuff will be done with patches against WebKit as quite a lot isn’t into [WebKitSettings](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html) and with generic WebKit extensions that should be resuseable. diff --git a/badwolf.1 b/badwolf.1 new file mode 100644 index 0000000..d1bda3e --- /dev/null +++ b/badwolf.1 @@ -0,0 +1,110 @@ +./" BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +./" Copyright © 2019-2020 Badwolf Authors +./" SPDX-License-Identifier: BSD-3-Clause +.Dd 2019-10-31 +.Dt BADWOLF 1 +.Sh NAME +.Nm badwolf +.Nd minimalist and privacy-oriented WebkitGTK browser +.Sh SYNOPSIS +.Nm +.Op Ar webkit/gtk options +.Op Ar URLs or paths +.Sh DESCRIPTION +.Nm +is a minimalist browser that cares about privacy, it is based on WebKitGTK and thus also accepts WebKitGTK (and dependencies) flags and environment variables, unfortunately there doesn't seems to be manpages for theses. +.Pp +Runtime configuration specific to +.Nm +will probably get added at a later release. +.Sh KEYBINDINGS +The following section lists the keybinding by their action, each item is described by the widget the focus is on or +.Aq any +if it works for the whole window, followed by the keybind it grabs. +.Bl -tag -width Ds +.It webview Ctrl-Scroll +Zooms the webpage in/out. +.It webview Ctrl-0 +Resets webpage zoom to 100%. +.It any Ctrl-t +Creates a new tab (in a new session, similar as pressing the button) +.It browser Ctrl-F4, browser Alt-d +Closes the current tab +.It browser Ctrl-f +Focuses on the search entry +.It browser Ctrl-l +Focuses on the location(URL) entry +.It browser Ctrl-Shift-r / Ctrl-r, browser F5 +Reloads the content in the current tab (with/without clearing cache) +.It browser Escape +Stops loading the content in the current tab +.It browser F7 +Toggles caret browsing. +.It browser F12 +Opens the web inspector. +.It browser Ctrl-[ / Ctrl-] +Go back/forward in current tab’s history +.It browser Ctrl-p +Print the current page. (spawns a dialog) +.It any Alt-Left / Alt-Right +Go to the previous/next tab +.It any F1 +Shows the about dialog +.It any Alt-n +Where n is any numeric-row key. +Go to the n-th tab, 0 goes to the last one. +.El +.Ss DEFAULT ONES +Here is a incomplete list of the default Webkit/GTK keybindings: +.Bl -tag -width Ds +.It any Ctrl-PageUp / Ctrl-PageDown +Go to the previous/next tab +.It search Ctrl-g / Ctrl-Shift-g +When the search box is focused it goes to the Next/Previous search term. +.It search Escape +Cancels current search +.El +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev BADWOLF_L10N +A colon-separated list in the form lang_COUNTRY where lang is in ISO-639 and COUNTRY in ISO-3166. +For example +.Ic BADWOLF_L10N="en_GB:fr_FR:de_DE" . +When this variable isn't set, spelling isn't activated. +A more generic variable name is also intended to be used in the future. +.El +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa ${XDG_DATA_HOME:-$HOME/.local/share}/badwolf/webkit-web-extensions/ +Directory containing the +.Lk https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebExtension.html WebKitWebExtensions +to be loaded into +.Nm . Note: They aren't the JavaScript-based Web-Extensions supported by Firefox or Chrome, but native code in shared objects using the WebKitGTK API. +.Pp +Examples of useful extensions may be found at: +.Bl -item -compact +.Lk https://hacktivis.me/git/badwolf-extensions +.Lk https://github.com/jun7/wyebadblock +.El +.It Pa ${DATADIR:-/usr/local/share}/badwolf/interface.css +.It Pa ${XDG_DATA_HOME:-$HOME/.local/share}/badwolf/interface.css +CSS files (respectively system and user-level) for styling badwolf interface. +See +.Lk https://developer.gnome.org/gtk3/stable/chap-css-properties.html +for the properties being available. +.Pp +For testing your styles I would recommend using the +.Ev GTK_DEBUG=interactive +environment variable on launching +.Nm +and going to the CSS tab. +.El +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact+badwolf@hacktivis.me +.Sh BUGS +You can submit contributions or tickets to +.Lk https://gitlab.com/lanodan/badwolf +or +.Mt contact+badwolf@hacktivis.me , +with +.Xr git-send-email 1 for patches. diff --git a/badwolf.c b/badwolf.c new file mode 100644 index 0000000..c6f7fc2 --- /dev/null +++ b/badwolf.c @@ -0,0 +1,1005 @@ +// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +// Copyright © 2019-2020 Badwolf Authors +// SPDX-License-Identifier: BSD-3-Clause + +#include "badwolf.h" + +#include "config.h" +#include "downloads.h" +#include "keybindings.h" +#include "uri.h" + +#include /* _() and other internationalization/localization helpers */ +#include /* soup* */ +#include /* LC_* */ +#include /* perror(), fprintf() */ +#include /* malloc() */ +#include /* access() */ + +gchar *web_extensions_directory; +const gchar *homepage = "https://hacktivis.me/projects/badwolf"; +const gchar *version = VERSION; + +static gboolean WebViewCb_close(WebKitWebView *webView, gpointer user_data); +static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView, + WebKitWebProcessTerminationReason reason, + gpointer user_data); +static gboolean +WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean +WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean +WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); +static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView, + GParamSpec *pspec, + gpointer user_data); +static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView, + WebKitHitTestResult *hit, + guint modifiers, + gpointer user_data); +static WebKitWebView *WebViewCb_create(WebKitWebView *related_web_view, + WebKitNavigationAction *navigation_action, + gpointer user_data); +static gboolean WebViewCb_permission_request(WebKitWebView *web_view, + WebKitPermissionRequest *request, + gpointer user_data); +static gboolean WebViewCb_decide_policy(WebKitWebView *web_view, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType decision_type, + gpointer user_data); +static void +WebViewCb_load_changed(WebKitWebView *webView, WebKitLoadEvent load_event, gpointer user_data); +static void web_contextCb_download_started(WebKitWebContext *web_context, + WebKitDownload *download, + gpointer user_data); +static gboolean locationCb_activate(GtkEntry *location, gpointer user_data); +static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data); +static gboolean auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data); +static void backCb_clicked(GtkButton *back, gpointer user_data); +static void forwardCb_clicked(GtkButton *forward, gpointer user_data); +static void printCb_clicked(GtkButton *forward, gpointer user_data); +static gboolean SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data); +static gboolean SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data); +static gboolean SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data); +static gboolean SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data); +static void new_tabCb_clicked(GtkButton *new_tab, gpointer user_data); +static void closeCb_clicked(GtkButton *close, gpointer user_data); +static void +notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data); + +static gboolean +WebViewCb_close(WebKitWebView *webView, gpointer user_data) +{ + (void)webView; + struct Client *browser = (struct Client *)user_data; + + gtk_widget_destroy(browser->box); + + free(browser); + + return TRUE; +} + +static gboolean +WebViewCb_web_process_terminated(WebKitWebView *webView, + WebKitWebProcessTerminationReason reason, + gpointer user_data) +{ + (void)webView; + struct Client *browser = (struct Client *)user_data; + + switch(reason) + { + case WEBKIT_WEB_PROCESS_CRASHED: + fprintf(stderr, "%s", _("the web process crashed.\n")); + webView_tab_label_change(browser, _("Crashed")); + break; + case WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT: + fprintf(stderr, "%s", _("the web process exceeded the memory limit.\n")); + webView_tab_label_change(browser, _("Out of Memory")); + break; + default: + fprintf(stderr, "%s", _("the web process terminated for an unknown reason.\n")); + webView_tab_label_change(browser, _("Unknown Crash")); + } + + return FALSE; +} + +static gboolean +WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) +{ + (void)webView; + (void)pspec; + const gchar *location_uri; + struct Client *browser = (struct Client *)user_data; + + location_uri = webkit_web_view_get_uri(browser->webView); + + gtk_entry_set_text(GTK_ENTRY(browser->location), location_uri); + + if(webkit_uri_for_display(location_uri) != location_uri) + gtk_widget_set_tooltip_text(browser->location, webkit_uri_for_display(location_uri)); + else + gtk_widget_set_has_tooltip(browser->location, false); + + return TRUE; +} + +GtkWidget * +badwolf_new_tab_box(const gchar *title, struct Client *browser) +{ + (void)browser; + GtkWidget *tab_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(tab_box, "browser__tabbox"); + GtkWidget *close = + gtk_button_new_from_icon_name("window-close-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_widget_set_name(close, "browser__tabbox__close"); + GtkWidget *label = gtk_label_new(title); + gtk_widget_set_name(label, "browser__tabbox__label"); + GtkWidget *playing = + gtk_image_new_from_icon_name("audio-volume-high-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(playing, "browser__tabbox__playing"); + +#ifdef BADWOLF_TAB_BOX_WIDTH + gtk_widget_set_size_request(label, BADWOLF_TAB_BOX_WIDTH, -1); +#endif +#ifdef BADWOLF_TAB_LABEL_CHARWIDTH + gtk_label_set_width_chars(GTK_LABEL(label), BADWOLF_TAB_LABEL_CHARWIDTH); +#endif + gtk_widget_set_hexpand(tab_box, BADWOLF_TAB_HEXPAND); + + gtk_label_set_ellipsize(GTK_LABEL(label), BADWOLF_TAB_LABEL_ELLIPSIZE); + gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE); + + gtk_box_pack_start(GTK_BOX(tab_box), playing, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(tab_box), label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(tab_box), close, FALSE, FALSE, 0); + + gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE); + + g_signal_connect(close, "clicked", G_CALLBACK(closeCb_clicked), browser); + + gtk_widget_set_tooltip_text(tab_box, title); + + gtk_widget_show_all(tab_box); + gtk_widget_set_visible(playing, webkit_web_view_is_playing_audio(browser->webView)); + + return tab_box; +} + +static gboolean +WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) +{ + (void)webView; + (void)pspec; + struct Client *browser = (struct Client *)user_data; + + webView_tab_label_change(browser, NULL); + + return TRUE; +} + +static gboolean +WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) +{ + (void)webView; + (void)pspec; + struct Client *browser = (struct Client *)user_data; + + webView_tab_label_change(browser, NULL); + + return TRUE; +} + +void +webView_tab_label_change(struct Client *browser, const gchar *title) +{ + GtkWidget *notebook = browser->window->notebook; + +#define title_IS_EMPTY (title == NULL) || strnlen(title, 2) == 0 + + if(title_IS_EMPTY) title = webkit_web_view_get_title(browser->webView); + if(title_IS_EMPTY) title = webkit_web_view_get_uri(browser->webView); + if(title_IS_EMPTY) title = "BadWolf"; + + gtk_notebook_set_tab_label( + GTK_NOTEBOOK(notebook), browser->box, badwolf_new_tab_box(title, browser)); + gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title); + + // Set the window title if the title change was on the current tab + if(gtk_notebook_page_num(GTK_NOTEBOOK(notebook), browser->box) == + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))) + gtk_window_set_title(GTK_WINDOW(browser->window->main_window), title); +} + +static gboolean +WebViewCb_notify__estimated_load_progress(WebKitWebView *webView, + GParamSpec *pspec, + gpointer user_data) +{ + (void)webView; + (void)pspec; + struct Client *browser = (struct Client *)user_data; + gdouble progress; + + progress = webkit_web_view_get_estimated_load_progress(browser->webView); + + if(progress >= 1) progress = 0; + + gtk_entry_set_progress_fraction(GTK_ENTRY(browser->location), progress); + + return TRUE; +} + +static gboolean +WebViewCb_mouse_target_changed(WebKitWebView *webView, + WebKitHitTestResult *hit, + guint modifiers, + gpointer user_data) +{ + (void)webView; + (void)modifiers; + struct Client *browser = (struct Client *)user_data; + + if(webkit_hit_test_result_context_is_link(hit)) + { + const gchar *link_uri = webkit_hit_test_result_get_link_uri(hit); + + gtk_label_set_text(GTK_LABEL(browser->statuslabel), webkit_uri_for_display(link_uri)); + } + else + gtk_label_set_text(GTK_LABEL(browser->statuslabel), NULL); + + return FALSE; +} + +static gboolean +WebViewCb_scroll_event(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + (void)widget; + struct Client *browser = (struct Client *)data; + gdouble delta_x, delta_y; + gdouble zoom; + + if(((GdkEventScroll *)event)->state & GDK_CONTROL_MASK) + { + gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); + zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView)); + zoom -= delta_y * 0.1; + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom); + return TRUE; + } + + return FALSE; +} + +static WebKitWebView * +WebViewCb_create(WebKitWebView *related_web_view, + WebKitNavigationAction *navigation_action, + gpointer user_data) +{ + (void)navigation_action; + struct Window *window = (struct Window *)user_data; + struct Client *browser = new_browser(window, NULL, related_web_view); + + if(badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, FALSE) < 0) + return NULL; + else + return browser->webView; +} + +static gboolean +WebViewCb_permission_request(WebKitWebView *web_view, + WebKitPermissionRequest *request, + gpointer user_data) +{ + (void)web_view; + (void)user_data; + + webkit_permission_request_deny(request); + + return TRUE; /* Stop other handlers */ +} + +static gboolean +WebViewCb_decide_policy(WebKitWebView *web_view, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType decision_type, + gpointer user_data) +{ + WebKitResponsePolicyDecision *r; + (void)web_view; + (void)user_data; + + switch(decision_type) + { + case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: + r = WEBKIT_RESPONSE_POLICY_DECISION(decision); + if(!webkit_response_policy_decision_is_mime_type_supported(r)) + webkit_policy_decision_download(decision); + else + webkit_policy_decision_use(decision); + break; + default: + /* Use whatever default there is. */ + return FALSE; + } + + return TRUE; +} + +static void +WebViewCb_load_changed(WebKitWebView *webView, WebKitLoadEvent load_event, gpointer user_data) +{ + (void)webView; + (void)load_event; + struct Client *browser = (struct Client *)user_data; + + gtk_widget_set_sensitive(browser->back, webkit_web_view_can_go_back(browser->webView)); + gtk_widget_set_sensitive(browser->forward, webkit_web_view_can_go_forward(browser->webView)); +} + +static char * +detail_tls_certificate_flags(GTlsCertificateFlags tls_errors) +{ + GString *errors = g_string_new(NULL); + + g_string_append_printf(errors, + _("Couldn't verify the TLS certificate to ensure a better security of the " + "connection. You might want to verify your machine and network.\n\n")); + + if(tls_errors & G_TLS_CERTIFICATE_UNKNOWN_CA) + g_string_append_printf(errors, _("Error: The X509 Certificate Authority is unknown.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_BAD_IDENTITY) + g_string_append(errors, _("Error: The given identity doesn't match the expected one.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) + g_string_append(errors, + _("Error: The certificate isn't valid yet. Check your system's clock.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_EXPIRED) + g_string_append(errors, _("Error: The certificate has expired. Check your system's clock.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_REVOKED) + g_string_append(errors, _("Error: The certificate has been revoked.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_INSECURE) + g_string_append(errors, _("Error: The certificate is considered to be insecure.\n")); + + if(tls_errors & G_TLS_CERTIFICATE_GENERIC_ERROR) + g_string_append(errors, _("Error: Some unknown error occurred validating the certificate.\n")); + + return g_string_free(errors, FALSE); +} + +static gboolean +WebViewCb_load_failed_with_tls_errors(WebKitWebView *web_view, + gchar *failing_text, + GTlsCertificate *certificate, + GTlsCertificateFlags errors, + gpointer user_data) +{ + (void)web_view; + (void)certificate; + (void)errors; + struct Client *browser = (struct Client *)user_data; + gchar *error_details = detail_tls_certificate_flags(errors); + gint dialog_response; + SoupURI *failing_uri = soup_uri_new(failing_text); + + GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(browser->window->main_window), + GTK_DIALOG_MODAL & GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_NONE, + _("TLS Error for %s."), + failing_text); + gtk_dialog_add_buttons( + GTK_DIALOG(dialog), _("Temporarily Add Exception"), 1, _("Continue"), 0, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), 0); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s\n", error_details); + + dialog_response = gtk_dialog_run(GTK_DIALOG(dialog)); + + if(dialog_response == 1) + { + webkit_web_context_allow_tls_certificate_for_host( + webkit_web_view_get_context(browser->webView), certificate, failing_uri->host); + webkit_web_view_reload(browser->webView); + } + + soup_uri_free(failing_uri); + g_free(error_details); + gtk_widget_destroy(dialog); + + return FALSE; /* propagate the event further */ +} + +static void +web_contextCb_download_started(WebKitWebContext *web_context, + WebKitDownload *webkit_download, + gpointer user_data) +{ + (void)web_context; + struct Client *browser = (struct Client *)user_data; + struct Download *download = malloc(sizeof(struct Client)); + + if(download != NULL) + { + download->window = browser->window; + + download_new_entry(webkit_download, download); + + g_signal_connect( + G_OBJECT(webkit_download), "received-data", G_CALLBACK(downloadCb_received_data), download); + g_signal_connect(G_OBJECT(webkit_download), + "created-destination", + G_CALLBACK(downloadCb_created_destination), + download); + g_signal_connect(G_OBJECT(webkit_download), "failed", G_CALLBACK(downloadCb_failed), download); + g_signal_connect( + G_OBJECT(webkit_download), "finished", G_CALLBACK(downloadCb_finished), download); + } + + g_signal_connect(G_OBJECT(webkit_download), + "decide-destination", + G_CALLBACK(downloadCb_decide_destination), + user_data); +} + +static gboolean +locationCb_activate(GtkEntry *location, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_load_uri(browser->webView, + badwolf_ensure_uri_scheme(gtk_entry_get_text(location), TRUE)); + + if(browser != NULL) + gtk_widget_grab_focus (GTK_WIDGET (browser->webView)); + + return TRUE; +} + +static gboolean +javascriptCb_toggled(GtkButton *javascript, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + + WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); + + webkit_settings_set_enable_javascript_markup( + settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(javascript))); + + webkit_web_view_set_settings(browser->webView, settings); + + return TRUE; +} + +static gboolean +auto_load_imagesCb_toggled(GtkButton *auto_load_images, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + + WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); + + webkit_settings_set_auto_load_images( + settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_load_images))); + + webkit_web_view_set_settings(browser->webView, settings); + + return TRUE; +} + +static void +backCb_clicked(GtkButton *back, gpointer user_data) +{ + (void)back; + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_go_back(browser->webView); +} + +static void +forwardCb_clicked(GtkButton *forward, gpointer user_data) +{ + (void)forward; + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_go_forward(browser->webView); +} + +static void +printCb_clicked(GtkButton *print, gpointer user_data) +{ + (void)print; + struct Client *browser = (struct Client *)user_data; + + WebKitPrintOperation *print_operation = webkit_print_operation_new(browser->webView); + + webkit_print_operation_run_dialog(print_operation, GTK_WINDOW(browser->window)); +} + +static gboolean +SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data) +{ + (void)search; + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + + webkit_find_controller_search_next(findController); + + return TRUE; +} + +static gboolean +SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data) +{ + (void)search; + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + + webkit_find_controller_search_previous(findController); + + return TRUE; +} + +static gboolean +SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + const gchar *search_text = gtk_entry_get_text(GTK_ENTRY(search)); + + webkit_find_controller_search(findController, search_text, 0, 0); + + return TRUE; +} + +static gboolean +SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data) +{ + (void)search; + struct Client *browser = (struct Client *)user_data; + WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); + + webkit_find_controller_search_finish(findController); + + return TRUE; +} + +static gboolean +widgetCb_drop_button3_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + (void)widget; + (void)user_data; + + // Button3 being right-click on right-handed mode, left-click on left-handed mode + return ((GdkEventButton *)event)->button == 3; +} + +struct Client * +new_browser(struct Window *window, const gchar *target_url, WebKitWebView *related_web_view) +{ + struct Client *browser = malloc(sizeof(struct Client)); + target_url = badwolf_ensure_uri_scheme(target_url, (related_web_view == NULL)); + char *badwolf_l10n = NULL; + + if(browser == NULL) return NULL; + + browser->window = window; + browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_name(browser->box, "browser__box"); + + browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(browser->toolbar, "browser__toolbar"); + + browser->back = + gtk_button_new_from_icon_name("go-previous-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_widget_set_name(browser->back, "browser__back"); + + browser->forward = gtk_button_new_from_icon_name("go-next-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(browser->forward, "browser__forward"); + + GtkWidget *toolbar_separator = gtk_separator_new(GTK_ORIENTATION_VERTICAL); + + browser->javascript = gtk_toggle_button_new_with_mnemonic(_("_JS")); + gtk_widget_set_name(browser->javascript, "browser__javascript"); + gtk_widget_set_tooltip_text(browser->javascript, _("Toggle javascript")); + gtk_button_set_relief(GTK_BUTTON(browser->javascript), GTK_RELIEF_NONE); + + browser->auto_load_images = gtk_toggle_button_new_with_mnemonic(_("_IMG")); + gtk_widget_set_name(browser->auto_load_images, "browser__load_images"); + gtk_widget_set_tooltip_text(browser->auto_load_images, _("Toggle loading images automatically")); + gtk_button_set_relief(GTK_BUTTON(browser->auto_load_images), GTK_RELIEF_NONE); + + browser->location = gtk_entry_new(); + gtk_widget_set_name(browser->location, "browser__location"); + + GtkWidget *print = + gtk_button_new_from_icon_name("document-print-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(browser->back, "browser__print"); + + browser->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(browser->statusbar, "browser__statusbar"); + browser->search = gtk_search_entry_new(); + gtk_widget_set_name(browser->search, "browser__search"); + browser->statuslabel = gtk_label_new(NULL); + gtk_widget_set_name(browser->statuslabel, "browser__statuslabel"); + + setenv("GTK_THEME", ":light", 0); + + WebKitWebContext *web_context = webkit_web_context_new_ephemeral(); + webkit_web_context_set_sandbox_enabled(web_context, TRUE); + webkit_web_context_set_process_model(web_context, + WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); + + webkit_web_context_set_web_extensions_directory(web_context, web_extensions_directory); + + badwolf_l10n = getenv("BADWOLF_L10N"); + + if(badwolf_l10n != NULL) + { + gchar **languages = g_strsplit(badwolf_l10n, ":", -1); + webkit_web_context_set_spell_checking_languages(web_context, (const gchar *const *)languages); + g_strfreev(languages); + + webkit_web_context_set_spell_checking_enabled(web_context, TRUE); + } + + WebKitSettings *settings = webkit_settings_new_with_settings(BADWOLF_WEBKIT_SETTINGS); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->javascript), + webkit_settings_get_enable_javascript_markup(settings)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->auto_load_images), + webkit_settings_get_auto_load_images(settings)); + + browser->webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", + web_context, + "related-view", + related_web_view, + "settings", + settings, + NULL)); + gtk_widget_set_name(GTK_WIDGET(browser->webView), "browser__webView"); + g_object_unref(web_context); + g_object_unref(settings); + + gtk_box_pack_start( + GTK_BOX(browser->toolbar), GTK_WIDGET(browser->back), FALSE, FALSE, BADWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->forward), + FALSE, + FALSE, + BADWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + toolbar_separator, + FALSE, + FALSE, + BADWOLF_TOOLBAR_SEPARATOR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->javascript), + FALSE, + FALSE, + BADWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->auto_load_images), + FALSE, + FALSE, + BADWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), + GTK_WIDGET(browser->location), + TRUE, + TRUE, + BADWOLF_TOOLBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->toolbar), print, FALSE, FALSE, BADWOLF_TOOLBAR_PADDING); + + gtk_container_set_focus_child(GTK_CONTAINER(browser->box), browser->toolbar); + gtk_container_set_focus_child(GTK_CONTAINER(browser->toolbar), browser->location); + + gtk_box_pack_start( + GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, BADWOLF_BOX_PADDING); + gtk_box_pack_start( + GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, BADWOLF_BOX_PADDING); + + gtk_box_pack_start( + GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, BADWOLF_BOX_PADDING); + + gtk_box_pack_start(GTK_BOX(browser->statusbar), + GTK_WIDGET(browser->search), + FALSE, + FALSE, + BADWOLF_STATUSBAR_PADDING); + gtk_box_pack_start(GTK_BOX(browser->statusbar), + GTK_WIDGET(browser->statuslabel), + FALSE, + FALSE, + BADWOLF_STATUSBAR_PADDING); + + gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START); + + gtk_label_set_single_line_mode(GTK_LABEL(browser->statuslabel), TRUE); + gtk_label_set_ellipsize(GTK_LABEL(browser->statuslabel), BADWOLF_STATUSLABEL_ELLIPSIZE); + + gtk_entry_set_text(GTK_ENTRY(browser->location), target_url); + gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL); + + gtk_entry_set_placeholder_text(GTK_ENTRY(browser->search), _("search in current page")); + + /* signals for back/forward buttons */ + g_signal_connect(browser->back, "clicked", G_CALLBACK(backCb_clicked), browser); + g_signal_connect(browser->forward, "clicked", G_CALLBACK(forwardCb_clicked), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect( + browser->back, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->back, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->forward, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->forward, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + + /* signals for javacript toggle widget */ + g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect( + browser->javascript, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect( + browser->javascript, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + + /* signals for auto_load_images toggle widget */ + g_signal_connect( + browser->auto_load_images, "toggled", G_CALLBACK(auto_load_imagesCb_toggled), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect(browser->auto_load_images, + "button-press-event", + G_CALLBACK(widgetCb_drop_button3_event), + NULL); + g_signal_connect(browser->auto_load_images, + "button-release-event", + G_CALLBACK(widgetCb_drop_button3_event), + NULL); + + /* signals for location entry widget */ + g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser); + + /* signals for print button */ + g_signal_connect(print, "clicked", G_CALLBACK(printCb_clicked), browser); + /* prevents GtkNotebook from spawning it's context-menu */ + g_signal_connect(print, "button-press-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + g_signal_connect(print, "button-release-event", G_CALLBACK(widgetCb_drop_button3_event), NULL); + + /* signals for WebView widget */ + g_signal_connect(browser->webView, + "web-process-terminated", + G_CALLBACK(WebViewCb_web_process_terminated), + browser); + g_signal_connect(browser->webView, "notify::uri", G_CALLBACK(WebViewCb_notify__uri), browser); + g_signal_connect(browser->webView, "notify::title", G_CALLBACK(WebViewCb_notify__title), browser); + g_signal_connect(browser->webView, + "notify::is-playing-audio", + G_CALLBACK(WebViewCb_notify__is__playing__audio), + browser); + g_signal_connect(browser->webView, + "mouse-target-changed", + G_CALLBACK(WebViewCb_mouse_target_changed), + browser); + g_signal_connect(browser->webView, + "notify::estimated-load-progress", + G_CALLBACK(WebViewCb_notify__estimated_load_progress), + browser); + g_signal_connect(browser->webView, "create", G_CALLBACK(WebViewCb_create), window); + g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser); + g_signal_connect( + browser->webView, "key-press-event", G_CALLBACK(WebViewCb_key_press_event), browser); + g_signal_connect(browser->webView, "scroll-event", G_CALLBACK(WebViewCb_scroll_event), browser); + g_signal_connect( + browser->webView, "permission-request", G_CALLBACK(WebViewCb_permission_request), NULL); + g_signal_connect(browser->webView, "decide-policy", G_CALLBACK(WebViewCb_decide_policy), NULL); + g_signal_connect(browser->webView, + "load-failed-with-tls-errors", + G_CALLBACK(WebViewCb_load_failed_with_tls_errors), + browser); + g_signal_connect(browser->webView, "load-changed", G_CALLBACK(WebViewCb_load_changed), browser); + + /* signals for WebView's WebContext */ + g_signal_connect(G_OBJECT(web_context), + "download-started", + G_CALLBACK(web_contextCb_download_started), + browser); + + /* signals for search widget */ + g_signal_connect(browser->search, "next-match", G_CALLBACK(SearchEntryCb_next__match), browser); + g_signal_connect( + browser->search, "previous-match", G_CALLBACK(SearchEntryCb_previous__match), browser); + g_signal_connect( + browser->search, "search-changed", G_CALLBACK(SearchEntryCb_search__changed), browser); + g_signal_connect(browser->search, "stop-search", G_CALLBACK(SearchEntryCb_stop__search), browser); + + /* signals for box container */ + g_signal_connect(browser->box, "key-press-event", G_CALLBACK(boxCb_key_press_event), browser); + + if(related_web_view == NULL) webkit_web_view_load_uri(browser->webView, target_url); + + return browser; +} + +/* badwolf_new_tab: Inserts struct Client *browser in GtkNotebook *notebook + * and optionally switches selected tab to it. + * + * returns: + * 0 : Ran successfully + * -1 : Failed to insert a page for browser->box + * -2 : browser is NULL + */ +int +badwolf_new_tab(GtkNotebook *notebook, struct Client *browser, bool auto_switch) +{ + gint current_page = gtk_notebook_get_current_page(notebook); + gchar *title = _("New tab"); + + if(browser == NULL) return -2; + + gtk_widget_show_all(browser->box); + + if(gtk_notebook_insert_page(notebook, browser->box, NULL, (current_page + 1)) == -1) return -1; + + gtk_notebook_set_tab_reorderable(notebook, browser->box, TRUE); + gtk_notebook_set_tab_label(notebook, browser->box, badwolf_new_tab_box(title, browser)); + gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(notebook), browser->box, title); + + gtk_widget_queue_draw(GTK_WIDGET(notebook)); + + if(auto_switch) + { + gtk_notebook_set_current_page(notebook, gtk_notebook_page_num(notebook, browser->box)); + } + + return 0; +} + +static void +new_tabCb_clicked(GtkButton *new_tab, gpointer user_data) +{ + (void)new_tab; + struct Window *window = (struct Window *)user_data; + struct Client *browser = new_browser(window, NULL, NULL); + + badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, TRUE); +} + +static void +closeCb_clicked(GtkButton *close, gpointer user_data) +{ + (void)close; + struct Client *browser = (struct Client *)user_data; + + webkit_web_view_try_close(browser->webView); +} + +static void +notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) +{ + (void)page_num; + struct Window *window = (struct Window *)user_data; + GtkWidget *label = gtk_notebook_get_tab_label(notebook, page); + + // TODO: Maybe find a better way to store the title + gtk_window_set_title(GTK_WINDOW(window->main_window), gtk_widget_get_tooltip_text(label)); +} + +int +main(int argc, char *argv[]) +{ + struct Window *window = &(struct Window){NULL, NULL, NULL, NULL}; + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, DATADIR "/locale"); + bind_textdomain_codeset(PACKAGE, "UTF-8"); + textdomain(PACKAGE); + + gtk_init(&argc, &argv); + + fprintf(stderr, _("Running Badwolf version: %s\n"), version); + fprintf(stderr, + _("Buildtime WebKit version: %d.%d.%d\n"), + WEBKIT_MAJOR_VERSION, + WEBKIT_MINOR_VERSION, + WEBKIT_MICRO_VERSION); + fprintf(stderr, + _("Runtime WebKit version: %d.%d.%d\n"), + webkit_get_major_version(), + webkit_get_minor_version(), + webkit_get_micro_version()); + + web_extensions_directory = + g_build_filename(g_get_user_data_dir(), "badwolf", "webkit-web-extension", NULL); + fprintf(stderr, _("webkit-web-extension directory set to: %s\n"), web_extensions_directory); + + window->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + window->notebook = gtk_notebook_new(); + window->new_tab = gtk_button_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + window->downloads_tab = badwolf_downloads_tab_new(); + + gtk_window_set_default_size( + GTK_WINDOW(window->main_window), BADWOLF_DEFAULT_WIDTH, BADWOLF_DEFAULT_HEIGHT); + gtk_window_set_role(GTK_WINDOW(window->main_window), "browser"); + gtk_window_set_icon_name(GTK_WINDOW(window->main_window), "badwolf"); + + gchar *provider_path_app = g_build_filename(DATADIR, "interface.css", NULL); + if(access(provider_path_app, R_OK) == 0) + { + GtkCssProvider *css_provider_app = gtk_css_provider_new(); + gtk_css_provider_load_from_path(css_provider_app, provider_path_app, NULL); + gtk_style_context_add_provider_for_screen( + gtk_widget_get_screen(GTK_WIDGET(window->main_window)), + GTK_STYLE_PROVIDER(css_provider_app), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + g_free(provider_path_app); + + gchar *provider_path_user = + g_build_filename(g_get_user_data_dir(), "badwolf", "interface.css", NULL); + if(access(provider_path_user, R_OK) == 0) + { + GtkCssProvider *css_provider_user = gtk_css_provider_new(); + gtk_css_provider_load_from_path(css_provider_user, provider_path_user, NULL); + gtk_style_context_add_provider_for_screen( + gtk_widget_get_screen(GTK_WIDGET(window->main_window)), + GTK_STYLE_PROVIDER(css_provider_user), + GTK_STYLE_PROVIDER_PRIORITY_USER); + } + g_free(provider_path_user); + + gtk_widget_set_tooltip_text(window->new_tab, _("Open new tab")); + + gtk_notebook_set_action_widget(GTK_NOTEBOOK(window->notebook), window->new_tab, GTK_PACK_END); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(window->notebook), BADWOLF_TAB_POSITION); + gtk_notebook_popup_enable(GTK_NOTEBOOK(window->notebook)); + + gtk_container_add(GTK_CONTAINER(window->main_window), window->notebook); + gtk_widget_queue_draw(window->notebook); + + badwolf_downloads_tab_attach(window); + + g_signal_connect( + window->main_window, "key-press-event", G_CALLBACK(main_windowCb_key_press_event), window); + + g_signal_connect(window->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + g_signal_connect(window->new_tab, "clicked", G_CALLBACK(new_tabCb_clicked), window); + g_signal_connect(window->notebook, "switch-page", G_CALLBACK(notebookCb_switch__page), window); + + gtk_widget_show(window->new_tab); + gtk_widget_show_all(window->main_window); + + struct Client *browser = NULL; + if(argc == 1) + { + browser = new_browser(window, NULL, NULL); + badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, FALSE); + } + else + { + for(int i = 1; i < argc; ++i) + { + browser = new_browser(window, argv[i], NULL); + badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser, FALSE); + } + } + + gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), 1); + + if(browser != NULL) + gtk_widget_grab_focus (GTK_WIDGET (browser->webView)); + + gtk_main(); + +#if 0 + /* TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. */ + _("ø"); +#endif + + return 0; +} diff --git a/badwolf.desktop b/badwolf.desktop new file mode 100644 index 0000000..5bf865b --- /dev/null +++ b/badwolf.desktop @@ -0,0 +1,15 @@ +# https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/ +[Desktop Entry] +Version=1.1 +Type=Application +Categories=Network;WebBrowser +Comment=A minimalist and privacy-oriented web browser +Comment[fr]=Un navigateur web minimaliste et orienté vers le respect de la vie privée +Exec=badwolf %U +GenericName=Web Browser +GenericName[fr]=Navigateur Web +Icon=badwolf +Keywords=Internet;WWW;Browser;Web;WebKit;WebKitGTK +Keywords[fr]=Internet;WWW;Browser;Web;Surfer;Navigateur;WebKit;WebKitGTK +MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https +Name=Badwolf diff --git a/badwolf.h b/badwolf.h new file mode 100644 index 0000000..3d9b7f4 --- /dev/null +++ b/badwolf.h @@ -0,0 +1,43 @@ +#ifndef BADWOLF_H_INCLUDED +#define BADWOLF_H_INCLUDED + +#include +#include + +extern const gchar *homepage; +extern const gchar *version; + +struct Window +{ + GtkWidget *main_window; + GtkWidget *notebook; + GtkWidget *new_tab; + GtkWidget *downloads_tab; +}; + +struct Client +{ + GtkWidget *box; + + GtkWidget *toolbar; + GtkWidget *back; + GtkWidget *forward; + GtkWidget *javascript; + GtkWidget *auto_load_images; + GtkWidget *location; + + WebKitWebView *webView; + struct Window *window; + + GtkWidget *statusbar; + GtkWidget *statuslabel; + GtkWidget *search; +}; + +GtkWidget *badwolf_new_tab_box(const gchar *title, struct Client *browser); +void webView_tab_label_change(struct Client *browser, const gchar *title); +struct Client * +new_browser(struct Window *window, const gchar *target_url, WebKitWebView *related_web_view); +int badwolf_new_tab(GtkNotebook *notebook, struct Client *browser, bool auto_switch); +gint badwolf_get_tab_position(GtkContainer *notebook, GtkWidget *child); +#endif /* BADWOLF_H_INCLUDED */ diff --git a/badwolf.inkscape.svg b/badwolf.inkscape.svg new file mode 100644 index 0000000..de73138 --- /dev/null +++ b/badwolf.inkscape.svg @@ -0,0 +1,164 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/badwolf.svg b/badwolf.svg new file mode 100644 index 0000000..d2ff8e2 --- /dev/null +++ b/badwolf.svg @@ -0,0 +1,134 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config.h b/config.h new file mode 100644 index 0000000..8426ce3 --- /dev/null +++ b/config.h @@ -0,0 +1,107 @@ +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED +/* BADWOLF_TAB_POSITION: Position of the tab listing, can be one of: + * - GTK_POS_TOP + * - GTK_POS_BOTTOM + * - GTK_POS_RIGHT + * - GTK_POS_LEFT + * + * See https://developer.gnome.org/gtk3/stable/gtk3-Standard-Enumerations.html#GtkPositionType + */ +#define BADWOLF_TAB_POSITION GTK_POS_TOP + +/* BADWOLF_TAB_LABEL_CHARWIDTH: Amount of characters the tab label text fits + * Quite conflicts with BADWOLF_TAB_BOX_WIDTH, recommended to only define one + */ +#define BADWOLF_TAB_LABEL_CHARWIDTH 26 + +/* BADWOLF_TAB_BOX_WIDTH: Requested width (in pixels) for the whole tab + * Quite conflicts with BADWOLF_TAB_LABEL_CHARWIDTH, recommended to only define one + */ +//#define BADWOLF_TAB_BOX_WIDTH 120 + +// BADWOLF_TAB_HEXPAND: Should the tab try to fill the available horizontal space? +#define BADWOLF_TAB_HEXPAND FALSE + +/* BADWOLF_TAB_LABEL_ELLIPSIZE: pango ellipsize mode of the tab label text, can be one of: + * - PANGO_ELLIPSIZE_NONE + * - PANGO_ELLIPSIZE_START + * - PANGO_ELLIPSIZE_MIDDLE + * - PANGO_ELLIPSIZE_END + * + * See https://developer.gnome.org/pango/stable/pango-Layout-Objects.html#PangoEllipsizeMode + */ +#define BADWOLF_TAB_LABEL_ELLIPSIZE PANGO_ELLIPSIZE_MIDDLE + +// BADWOLF_BOX_PADDING: Amount of padding between browser’s box (tab child) elements +#define BADWOLF_BOX_PADDING 0 + +// BADWOLF_TOOLBAR_PADDING: Amount of padding between toolbar elements +#define BADWOLF_TOOLBAR_PADDING 0 + +// BADWOLF_TOOLBAR_PADDING: Amount of padding between toolbar elements +#define BADWOLF_TOOLBAR_SEPARATOR_PADDING 4 + +// BADWOLF_STATUSBAR_PADDING: Amount of padding between statusbar elements +#define BADWOLF_STATUSBAR_PADDING 0 + +// BADWOLF_DOWNLOAD_PADDING: Amount of padding between download list row-elements +#define BADWOLF_DOWNLOAD_PADDING 5 + +/* BADWOLF_DEFAULT_WIDTH / BADWOLF_DEFAULT_HEIGHT: + * Used to define the default width/height of the window, + * useful for floating Window Managers, probably useless in tiling ones + * + * See https://developer.gnome.org/gtk3/stable/GtkWindow.html#gtk-window-set-default-size + */ +#define BADWOLF_DEFAULT_WIDTH 800 +#define BADWOLF_DEFAULT_HEIGHT 600 + +/* BADWOLF_WEBKIT_SETTINGS: + * Used when creating a new view with webkit_settings_new_with_settings the usage is: + * setting-name, setting-value, setting-name, …, NULL + * + * See: https://webkitgtk.org/reference/webkit2gtk/stable/WebKitSettings.html + */ +// clang-format off +#define BADWOLF_WEBKIT_SETTINGS \ + "default-charset", "utf-8", \ + "enable-accelerated-2d-canvas", FALSE, \ + "enable-caret-browsing", FALSE, \ + "enable-developer-extras", TRUE, \ + "enable-dns-prefetching", FALSE, \ + "enable-hyperlink-auditing", FALSE, \ + "enable-java", FALSE, \ + "enable-javascript-markup", FALSE, \ + "enable-javascript", TRUE, \ + "enable-plugins", FALSE, \ + "javascript-can-access-clipboard", FALSE, \ + "javascript-can-open-windows-automatically", FALSE, \ + "media-playback-requires-user-gesture", TRUE, \ + "minimum-font-size", 9, \ + "allow-top-navigation-to-data-urls", FALSE, \ + NULL +// clang-format on + +/* BADWOLF_STATUSLABEL_ELLIPSIZE: pango ellipsize mode of the status bar label text, can be one of: + * - PANGO_ELLIPSIZE_NONE + * - PANGO_ELLIPSIZE_START + * - PANGO_ELLIPSIZE_MIDDLE + * - PANGO_ELLIPSIZE_END + * + * See https://developer.gnome.org/pango/stable/pango-Layout-Objects.html#PangoEllipsizeMode + */ +#define BADWOLF_STATUSLABEL_ELLIPSIZE PANGO_ELLIPSIZE_MIDDLE + +/* BADWOLF_DOWNLOAD_FILE_PATH_ELLIPSIZE: pango ellipsize mode of the download destination path, + * can be one of: + * - PANGO_ELLIPSIZE_NONE + * - PANGO_ELLIPSIZE_START + * - PANGO_ELLIPSIZE_MIDDLE + * - PANGO_ELLIPSIZE_END + * + * See https://developer.gnome.org/pango/stable/pango-Layout-Objects.html#PangoEllipsizeMode + */ +#define BADWOLF_DOWNLOAD_FILE_PATH_ELLIPSIZE PANGO_ELLIPSIZE_MIDDLE + +#endif /* CONFIG_H_INCLUDED */ diff --git a/decisions.md b/decisions.md new file mode 100644 index 0000000..cc8ab5c --- /dev/null +++ b/decisions.md @@ -0,0 +1,44 @@ +# Decisions taken over the project lifetime +## What is this? +See +Bascially, the idea is to have a file in your projects repo, where you record +the decisions you make over the course of the project's lifetime. +Rewriting history will not be done but Post-Scriptum notices will. + +## 2020-02-04 & 2020-02-09 : Use BSD extensions in the Makefile +The BSD extensions += and ?= for variable extension is used to avoid having to explicitely pass a list of variables to make, thus making build recipies simpler. +They are also compatible with GNU make so portability shouldn't have significantly dropped. +Which also meant dropping `.POSIX` into the file so `CC` wouldn't be set by default to `c99` (which isn't what I want, the codebase is in c11). + +## 2020-01-30 02:59 : Make window declaration static +This allows to avoid a quite useless memory allocation that would only be freed at the end of the program. + +## 2020-01-22 15:30 : Update copyright to Badwolf Authors +I don't want copyright assignment to me or any organisation, it should belong to every contributor, meaning that any copyright change will need to be accepted by them or their contribution would need to be replaced if need to be. + +## 2020-01-08 08:42:00Z : Start of the decisions.md file +From memory and git log: + +- Changelog is done when preparing for a release, it is copied into the git tag description. This means that there isn't a central file and isn't a changelog entry added with commits. +- OpenPGP is deprecated in favor of signify, reasons being security and portability. +- Prefer using lower and more standard/ubiquitous layers for implementations, one example is the about information with printf and Gtk Dialog rather than a custom scheme in WebKitGTK. + - using GLib instead of POSIX was done at first but the quality/behaviour of GLib (like `g_malloc()` calling `abort()` on errors) reverted this decision. +- WebKitGTK was picked because it is the only WebKit port which is maintained enough without a bus-factor (consider giving QtWebKit a hand too?). +- The `.clang-format` file was copied from the one living in my home directory: + - function declaration is at the start of the line to allow jumping to it with a simple editor + - braces on their own lines to allow for comments space + - "tabs for indentation and space for alignement" style is used for flexibility +- mdoc was picked over other formats for the manpage, as it is implemented in mandoc and GNU groff, much simpler to read/write and transform to other formats (like HTML or Markdown). +- Markdown was picked over other formats for informal documentation, it is a simple format supported by most forges, a format which is easier to write and parse could be considered +- Proprietary systems are not supported for their lack of auditability and transparency. +- C was picked because: + 1. I wanted to avoid being limited by binding completeness or model + 2. I think it is a good language which is fit for this purpose provided you are careful enough (such as using LLVM to it's full potential for checks) + 3. It is portable and doesn't introduces more dependencies than is already needed +- POSIX was picked for it's portability (only known one where it's absent is Windows but it's not going to be supported) +- Writing a portable (almost POSIX-only) Makefile with `pkg-config(1)` was picked over other build systems which tend to be over-complicated and rely on only one implementation. +- Originally the URL entry accepted only valid URLs, this was changed to a bit of heuristics for fixing it when it was entered by the user. +- No search entry is provided, this is to encourage users to avoid search engines as much as possible, support for SmartBookmarks with keywords (similar to DuckDuckGo/Searx !bangs) will be added after support for suggestions via bookmarks +- Bookmarks are first-class citizens as they are data explicitely controlled by the user +- No browsing history is provided, this is to encourage to use and improve bookmarks, it will be added later on with data retention limits, ability to wipe it and non-reachable from WebKit (and so the Wild Web). +- Javascript is turned off by default with a checkbox to enable it temporarly, Permissions Requests (microphone, geolocation, …) are all denied, something similar to uMatrix and NoScript will eventually be done to fix this diff --git a/downloads.c b/downloads.c new file mode 100644 index 0000000..307af15 --- /dev/null +++ b/downloads.c @@ -0,0 +1,222 @@ +// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +// Copyright © 2019 Haelwenn (lanodan) Monnier +// SPDX-License-Identifier: BSD-3-Clause + +#include "downloads.h" + +#include "badwolf.h" +#include "config.h" + +#include /* _() and other internationalization/localization helpers */ + +static void +download_stop_iconCb_clicked(GtkButton *stop_icon, gpointer user_data) +{ + (void)stop_icon; + WebKitDownload *webkit_download = (WebKitDownload *)user_data; + + webkit_download_cancel(webkit_download); +} + +void +download_format_elapsed(char *formatted, size_t formatted_size, char *format, int total) +{ + snprintf(formatted, + formatted_size, + format, + total / 3600, /* hours */ + (total % 3600) / 60, /* minutes */ + total % 60); /* seconds */ +} + +void +download_new_entry(WebKitDownload *webkit_download, struct Download *download) +{ + download->error = 0; + download->container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, BADWOLF_DOWNLOAD_PADDING); + download->progress = gtk_progress_bar_new(); + download->file_path = gtk_label_new(NULL); + download->status = gtk_label_new(_("Download starting…")); + download->icon = + gtk_image_new_from_icon_name("network-idle-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + download->stop_icon = gtk_button_new_from_icon_name("process-stop", GTK_ICON_SIZE_SMALL_TOOLBAR); + + gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(download->progress), TRUE); + gtk_label_set_ellipsize(GTK_LABEL(download->file_path), BADWOLF_DOWNLOAD_FILE_PATH_ELLIPSIZE); + + g_signal_connect( + download->stop_icon, "clicked", G_CALLBACK(download_stop_iconCb_clicked), webkit_download); + + gtk_box_pack_start(GTK_BOX(download->container), download->icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(download->container), download->progress, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(download->container), download->stop_icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(download->container), download->status, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(download->container), download->file_path, FALSE, FALSE, 0); + + gtk_list_box_insert(GTK_LIST_BOX(download->window->downloads_tab), download->container, -1); + + gtk_widget_show_all(download->container); +} + +void +downloadCb_created_destination(WebKitDownload *webkit_download, + gchar *destination, + gpointer user_data) +{ + (void)webkit_download; + char *markup; + struct Download *download = (struct Download *)user_data; + + markup = g_markup_printf_escaped( + "%s", destination, webkit_uri_for_display(destination)); + + gtk_label_set_markup(GTK_LABEL(download->file_path), markup); + g_free(markup); +} + +gboolean +downloadCb_decide_destination(WebKitDownload *webkit_download, + gchar *suggested_filename, + gpointer user_data) +{ + struct Client *browser = (struct Client *)user_data; + gint chooser_response; + GtkWindow *parent_window = GTK_WINDOW(browser->window->main_window); + + GtkFileChooserNative *file_dialog = + gtk_file_chooser_native_new(NULL, parent_window, GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL); + GtkFileChooser *file_chooser = GTK_FILE_CHOOSER(file_dialog); + + gtk_file_chooser_set_current_name(file_chooser, suggested_filename); + gtk_file_chooser_set_do_overwrite_confirmation(file_chooser, TRUE); + webkit_download_set_allow_overwrite(webkit_download, TRUE); + + chooser_response = gtk_native_dialog_run(GTK_NATIVE_DIALOG(file_dialog)); + + if(chooser_response == GTK_RESPONSE_ACCEPT) + webkit_download_set_destination(webkit_download, gtk_file_chooser_get_uri(file_chooser)); + else + webkit_download_cancel(webkit_download); + + g_object_unref(file_dialog); + + return FALSE; /* Let it propagate */ +} + +void +downloadCb_failed(WebKitDownload *webkit_download, GError *error, gpointer user_data) +{ + struct Download *download = (struct Download *)user_data; + char formatted[BUFSIZ]; + int total = (int)webkit_download_get_elapsed_time(webkit_download); + char *format; + + download->error = error; + + if(g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER)) + format = _("%02i:%02i:%02i Download cancelled"); + else + format = _("%02i:%02i:%02i Download error"); + + download_format_elapsed(formatted, sizeof(formatted), format, total); + + gtk_label_set_text(GTK_LABEL(download->status), formatted); + + gtk_widget_destroy(download->stop_icon); + + gtk_image_set_from_icon_name( + GTK_IMAGE(download->icon), "network-error-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); +} + +void +downloadCb_finished(WebKitDownload *webkit_download, gpointer user_data) +{ + struct Download *download = (struct Download *)user_data; + char formatted[BUFSIZ]; + int total = (int)webkit_download_get_elapsed_time(webkit_download); + + gchar *format_size = g_format_size(webkit_download_get_received_data_length(webkit_download)); + + download_format_elapsed( + formatted, sizeof(formatted), _("%02i:%02i:%02i Download finished"), total); + + gtk_widget_destroy(download->stop_icon); + + if(download->error == 0) + { + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(download->progress), 1); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(download->progress), format_size); + gtk_label_set_text(GTK_LABEL(download->status), formatted); + gtk_image_set_from_icon_name( + GTK_IMAGE(download->icon), "network-idle-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + } + + // TODO: Send notification +} + +void +downloadCb_received_data(WebKitDownload *webkit_download, guint64 data_lenght, gpointer user_data) +{ + (void)data_lenght; + struct Download *download = (struct Download *)user_data; + char formatted[BUFSIZ]; + int total = (int)webkit_download_get_elapsed_time(webkit_download); + + gchar *format_size = g_format_size(webkit_download_get_received_data_length(webkit_download)); + + download_format_elapsed(formatted, sizeof(formatted), _("%02i:%02i:%02i Downloading…"), total); + + gtk_image_set_from_icon_name( + GTK_IMAGE(download->icon), "network-receive-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_label_set_text(GTK_LABEL(download->status), formatted); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(download->progress), + webkit_download_get_estimated_progress(webkit_download)); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(download->progress), format_size); + + g_free(format_size); +} + +GtkWidget * +badwolf_downloads_tab_new() +{ + return gtk_list_box_new(); +} + +void +badwolf_downloads_tab_attach(struct Window *window) +{ + GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_name(scrolled_window, "browser__scrollwin_downloads"); + gtk_container_add(GTK_CONTAINER(scrolled_window), window->downloads_tab); + gtk_notebook_insert_page(GTK_NOTEBOOK(window->notebook), scrolled_window, NULL, 0); + + gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(window->notebook), scrolled_window, TRUE); + + GtkWidget *tab_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(tab_box, "browser__tabbox"); + GtkWidget *icon = gtk_image_new_from_icon_name("emblem-downloads", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_set_name(icon, "browser__tabbox__icon"); + GtkWidget *label = gtk_label_new(_("Downloads")); + gtk_widget_set_name(label, "browser__tabbox__label"); + +#ifdef BADWOLF_TAB_BOX_WIDTH + gtk_widget_set_size_request(label, BADWOLF_TAB_BOX_WIDTH, -1); +#endif +#ifdef BADWOLF_TAB_LABEL_CHARWIDTH + gtk_label_set_width_chars(GTK_LABEL(label), BADWOLF_TAB_LABEL_CHARWIDTH); +#endif + gtk_widget_set_hexpand(tab_box, BADWOLF_TAB_HEXPAND); + + gtk_label_set_ellipsize(GTK_LABEL(label), BADWOLF_TAB_LABEL_ELLIPSIZE); + gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE); + + gtk_box_pack_start(GTK_BOX(tab_box), icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(tab_box), label, TRUE, TRUE, 0); + + gtk_widget_set_tooltip_text(tab_box, _("LRRH Downloads")); + gtk_notebook_set_tab_label(GTK_NOTEBOOK(window->notebook), scrolled_window, tab_box); + gtk_notebook_set_menu_label_text( + GTK_NOTEBOOK(window->notebook), scrolled_window, _("LRRH Downloads")); + + gtk_widget_show_all(tab_box); +} diff --git a/downloads.h b/downloads.h new file mode 100644 index 0000000..fa5513e --- /dev/null +++ b/downloads.h @@ -0,0 +1,28 @@ +#include "badwolf.h" + +#include + +struct Download +{ + struct Window *window; + + GtkWidget *container; + GtkWidget *icon; + GtkWidget *stop_icon; + GtkWidget *file_path; + GtkWidget *progress; + GtkWidget *status; + GError *error; +}; + +void download_new_entry(WebKitDownload *webkit_download, struct Download *download); +void +downloadCb_created_destination(WebKitDownload *download, gchar *destination, gpointer user_data); +gboolean downloadCb_decide_destination(WebKitDownload *download, + gchar *suggested_filename, + gpointer user_data); +void downloadCb_failed(WebKitDownload *webkit_download, GError *error, gpointer user_data); +void downloadCb_finished(WebKitDownload *download, gpointer user_data); +void downloadCb_received_data(WebKitDownload *download, guint64 data_lenght, gpointer user_data); +GtkWidget *badwolf_downloads_tab_new(); +void badwolf_downloads_tab_attach(struct Window *window); diff --git a/icons/hicolor/128x128/apps/badwolf.png b/icons/hicolor/128x128/apps/badwolf.png new file mode 100644 index 0000000..0fa27e1 Binary files /dev/null and b/icons/hicolor/128x128/apps/badwolf.png differ diff --git a/icons/hicolor/24x24/apps/badwolf.png b/icons/hicolor/24x24/apps/badwolf.png new file mode 100644 index 0000000..9befcf3 Binary files /dev/null and b/icons/hicolor/24x24/apps/badwolf.png differ diff --git a/icons/hicolor/256x256/apps/badwolf.png b/icons/hicolor/256x256/apps/badwolf.png new file mode 100644 index 0000000..f8e1f14 Binary files /dev/null and b/icons/hicolor/256x256/apps/badwolf.png differ diff --git a/icons/hicolor/32x32/apps/badwolf.png b/icons/hicolor/32x32/apps/badwolf.png new file mode 100644 index 0000000..c88da40 Binary files /dev/null and b/icons/hicolor/32x32/apps/badwolf.png differ diff --git a/icons/hicolor/48x48/apps/badwolf.png b/icons/hicolor/48x48/apps/badwolf.png new file mode 100644 index 0000000..8cd38e3 Binary files /dev/null and b/icons/hicolor/48x48/apps/badwolf.png differ diff --git a/icons/hicolor/64x64/apps/badwolf.png b/icons/hicolor/64x64/apps/badwolf.png new file mode 100644 index 0000000..f15a58a Binary files /dev/null and b/icons/hicolor/64x64/apps/badwolf.png differ diff --git a/icons/hicolor/scalable/apps/badwolf.svg b/icons/hicolor/scalable/apps/badwolf.svg new file mode 100644 index 0000000..7d2500d --- /dev/null +++ b/icons/hicolor/scalable/apps/badwolf.svg @@ -0,0 +1,2 @@ + + diff --git a/icons_size.sh b/icons_size.sh new file mode 100755 index 0000000..29adf3a --- /dev/null +++ b/icons_size.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# Transforms an icon size from the Icon Theme Specification to rsvg-convert(1) options +#sed -r -e 's;^([^x]*)x([^@]*)$;-w \1 -h \2;' -e 's;^([^x]*)x([^@]*)@(.*)$;-w \1 -h \2 -z \3;' +sed -r 's;^([^x]*)x([^@]*)$;-w \1 -h \2;' diff --git a/interface.css b/interface.css new file mode 100644 index 0000000..d7272bf --- /dev/null +++ b/interface.css @@ -0,0 +1,2 @@ +#browser__location, #browser__statuslabel { font-family: monospace; } +button:checked label { font-weight: bold; } diff --git a/interface.txt b/interface.txt new file mode 100644 index 0000000..76d9526 --- /dev/null +++ b/interface.txt @@ -0,0 +1,43 @@ +# Badwolf +- A tablist with a new tab button at the end, right-clicking on the tablist gives you the list in a context-menu format +- Each tab contains a Browser View or the Downloads View + +## Tab Labels +- An icon to indicate if a media is playing +- The title of the web view +- A close button + +## Browser View + +The Browser View contains the following items: +- Toolbar +- WebKit WebView +- Status Bar + +### Toolbar +- Backward navigation button followed by a smaller forward navigation button +- Separator +- Javascript-markup toggler marked "JS" followed by an image-loading toggler marked "IMG" +- Potentially some future elements +- Location entry linked to the WebKit WebView, with integrated progress indication also linked to the WebKit WebView +- Potentially some future elements +- Print button +- Potentially some future elements + +The element focused by default is the location entry. + +### Status bar +- Search entry linked to the WebKit WebView +- Potentially some future elements +- Label showing where the mouse is pointing +- Potentially some future elements + +## Downloads View + +The download view is a table contains the following items on each row corresponding to each download: +- Icon linked to the network status +- Progress bar, with integrated received-size indication +- (when downloading) Cancelling button +- Label about the current status +- Link to download destination +- Potentially some future elements diff --git a/keybindings.c b/keybindings.c new file mode 100644 index 0000000..4ccdc33 --- /dev/null +++ b/keybindings.c @@ -0,0 +1,260 @@ +// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +// Copyright © 2019-2020 Badwolf Authors +// SPDX-License-Identifier: BSD-3-Clause + +#include "keybindings.h" + +#include "badwolf.h" + +#include /* _() */ + +static gboolean +about_dialogCb_activate_link(GtkAboutDialog *about_dialog, gchar *uri, gpointer user_data) +{ + (void)about_dialog; + struct Window *window = (struct Window *)user_data; + + badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(window, uri, NULL), FALSE); + + gtk_widget_destroy(GTK_WIDGET(about_dialog)); + + return TRUE; +} + +static void +badwolf_about_dialog(GtkWindow *main_window, gpointer user_data) +{ + struct Window *window = (struct Window *)user_data; + GtkWidget *about_dialog = gtk_about_dialog_new(); + + char *comments = NULL; + + comments = g_strdup_printf(_("Minimalist and privacy-oriented WebKitGTK+ browser\n" + "Runtime WebKit version: %d.%d.%d"), + webkit_get_major_version(), + webkit_get_minor_version(), + webkit_get_micro_version()); + + gtk_window_set_transient_for(GTK_WINDOW(about_dialog), main_window); + gtk_window_set_destroy_with_parent(GTK_WINDOW(about_dialog), TRUE); + + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about_dialog), + "SPDX-License-Identifier: BSD-3-Clause"); + gtk_about_dialog_set_copyright( + GTK_ABOUT_DIALOG(about_dialog), + "2019-2020 Badwolf Authors "); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about_dialog), homepage); + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about_dialog), comments); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about_dialog), version); + gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(about_dialog), "badwolf"); + + g_signal_connect(about_dialog, "activate-link", G_CALLBACK(about_dialogCb_activate_link), window); + + (void)gtk_dialog_run(GTK_DIALOG(about_dialog)); + gtk_widget_destroy(about_dialog); +} + +static void +toggle_caret_browsing(WebKitWebView *webView) +{ + WebKitSettings *settings = webkit_web_view_get_settings(webView); + + webkit_settings_set_enable_caret_browsing(settings, + !webkit_settings_get_enable_caret_browsing(settings)); + + webkit_web_view_set_settings(webView, settings); +} + +/* + * Goto the next tab in notebook. If current is the last one, goto the second tab! + */ +static void +goto_next_tab(GtkNotebook *notebook) +{ + gint npages = gtk_notebook_get_n_pages(notebook); + gint curr = gtk_notebook_get_current_page(notebook); + if (curr+1 == npages) + gtk_notebook_set_current_page(notebook, 1); + else gtk_notebook_next_page(notebook); +} + +/* commonCb_key_press_event: Global callback for keybindings + * + * These shortcuts should be avoided as much as possible: + * - Single key shortcuts (ie. backspace and space) + * - Triple key shortcuts (except for Ctrl+Shift) + * - Unix Terminal shortcuts (specially Ctrl-W) + * + * loosely follows https://developer.gnome.org/hig/stable/keyboard-input.html + */ +gboolean +commonCb_key_press_event(struct Window *window, GdkEvent *event, struct Client *browser) +{ + GtkNotebook *notebook = GTK_NOTEBOOK(window->notebook); + struct Client *nbrowser = NULL; + gdouble zoom = 0; + + if(((GdkEventKey *)event)->state & GDK_CONTROL_MASK) + { + if(browser != NULL) + { + switch(((GdkEventKey *)event)->keyval) + { + case GDK_KEY_F4: webkit_web_view_try_close(browser->webView); return TRUE; + case GDK_KEY_r: + if(((GdkEventKey *)event)->state & GDK_SHIFT_MASK) + webkit_web_view_reload_bypass_cache(browser->webView); + else + webkit_web_view_reload(browser->webView); + + return TRUE; + case GDK_KEY_f: gtk_widget_grab_focus(browser->search); return TRUE; + case GDK_KEY_l: gtk_widget_grab_focus(browser->location); return TRUE; + case GDK_KEY_0: + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), 1); + return TRUE; + case GDK_KEY_p: + webkit_print_operation_run_dialog(webkit_print_operation_new(browser->webView), + GTK_WINDOW(browser->window)); + return TRUE; + case GDK_KEY_i: + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->auto_load_images), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(browser->auto_load_images))); + webkit_web_view_reload(browser->webView); + return TRUE; + case GDK_KEY_j: + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(browser->javascript), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(browser->javascript))); + webkit_web_view_reload(browser->webView); + return TRUE; + case GDK_KEY_q: + gtk_main_quit(); + return TRUE; + case GDK_KEY_n: + nbrowser = new_browser(window, + gtk_label_get_text(GTK_LABEL(browser->statuslabel)), + NULL); + if (nbrowser != NULL) + { + badwolf_new_tab(GTK_NOTEBOOK(window->notebook), nbrowser, FALSE); + gint npages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(window->notebook)); + gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), npages-1); + } + return TRUE; + case GDK_KEY_w: + webkit_web_view_try_close(browser->webView); + return TRUE; + case GDK_KEY_plus: + zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView)); + zoom += zoom * 0.1; + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom); + return TRUE; + case GDK_KEY_minus: + zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView)); + zoom += zoom * -0.1; + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom); + return TRUE; + case GDK_KEY_Tab: + goto_next_tab(notebook); + return TRUE; + } + } + else + { + switch(((GdkEventKey *)event)->keyval) + { + case GDK_KEY_Page_Down: gtk_notebook_next_page(notebook); return TRUE; + case GDK_KEY_Page_Up: gtk_notebook_prev_page(notebook); return TRUE; + case GDK_KEY_t: badwolf_new_tab(notebook, new_browser(window, NULL, NULL), TRUE); return TRUE; + + case GDK_KEY_q: + gtk_main_quit(); + return TRUE; + + case GDK_KEY_Tab: + /*npages = gtk_notebook_get_n_pages(notebook); + curr = gtk_notebook_get_current_page(notebook); + if (curr+1 == npages) + gtk_notebook_set_current_page(notebook, 1); + else gtk_notebook_next_page(notebook);*/ + goto_next_tab(notebook); + return TRUE; + } + } + } + + if((((GdkEventKey *)event)->state & GDK_MOD1_MASK)) + { + if(browser != NULL) + { + switch(((GdkEventKey *)event)->keyval) + { + case GDK_KEY_Left: + webkit_web_view_go_back(browser->webView); + return TRUE; + case GDK_KEY_Right: + webkit_web_view_go_forward(browser->webView); + return TRUE; + } + } + + if((((GdkEventKey *)event)->keyval >= GDK_KEY_0) && + (((GdkEventKey *)event)->keyval <= GDK_KEY_9)) + gtk_notebook_set_current_page(notebook, (gint)(((GdkEventKey *)event)->keyval - GDK_KEY_1)); + } + + if(browser != NULL) + { + switch(((GdkEventKey *)event)->keyval) + { + case GDK_KEY_F5: webkit_web_view_reload(browser->webView); return TRUE; + case GDK_KEY_Escape: webkit_web_view_stop_loading(browser->webView); return TRUE; + case GDK_KEY_F7: toggle_caret_browsing(browser->webView); return TRUE; + case GDK_KEY_F12: + webkit_web_inspector_show(webkit_web_view_get_inspector(browser->webView)); + return TRUE; + } + } + else + { + switch(((GdkEventKey *)event)->keyval) + { + case GDK_KEY_F1: badwolf_about_dialog(GTK_WINDOW(window->main_window), window); return TRUE; + } + } + + return FALSE; +} + +gboolean +WebViewCb_key_press_event(WebKitWebView *webView, GdkEvent *event, gpointer user_data) +{ + (void)webView; + struct Client *browser = (struct Client *)user_data; + + if(commonCb_key_press_event(browser->window, event, browser)) return TRUE; + + return FALSE; +} + +gboolean +boxCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + (void)widget; + struct Client *browser = (struct Client *)user_data; + + if(commonCb_key_press_event(browser->window, event, browser)) return TRUE; + + return FALSE; +} + +gboolean +main_windowCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + (void)widget; + struct Window *window = (struct Window *)user_data; + + if(commonCb_key_press_event(window, event, NULL)) return TRUE; + + return FALSE; +} diff --git a/keybindings.h b/keybindings.h new file mode 100644 index 0000000..e7d1d6b --- /dev/null +++ b/keybindings.h @@ -0,0 +1,12 @@ +#ifndef KEYBINDINGS_H_INCLUDED +#define KEYBINDINGS_H_INCLUDED +#include "badwolf.h" + +#include +#include + +gboolean boxCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data); +gboolean commonCb_key_press_event(struct Window *window, GdkEvent *event, struct Client *browser); +gboolean main_windowCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data); +gboolean WebViewCb_key_press_event(WebKitWebView *webView, GdkEvent *event, gpointer user_data); +#endif /* KEYBINDINGS_H_INCLUDED */ diff --git a/mo/.keep b/mo/.keep new file mode 100644 index 0000000..e69de29 diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..d41f044 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,187 @@ +# BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +# Copyright (C) 2019-2020 Badwolf Authors +# This file is distributed under the same license as the Badwolf package. +# Haelwenn (lanodan) Monnier , 2019 +# +msgid "" +msgstr "" +"Project-Id-Version: Badwolf 0.3.0+gd88f2e7\n" +"Report-Msgid-Bugs-To: contact+badwolf-msgid@hacktivis.me\n" +"POT-Creation-Date: 2020-05-15 06:00+0200\n" +"PO-Revision-Date: 2019-12-22 00:57+0100\n" +"Last-Translator: Haelwenn (lanodan) Monnier \n" +"Language-Team: French\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: downloads.c:117 +#, fuzzy, c-format +msgid "%02i:%02i:%02i Download cancelled" +msgstr "%02i:%02i:%02i Téléchargement annulé" + +#: downloads.c:119 +#, fuzzy, c-format +msgid "%02i:%02i:%02i Download error" +msgstr "%02i:%02i:%02i Erreur du téléchargement" + +#: downloads.c:141 +#, fuzzy, c-format +msgid "%02i:%02i:%02i Download finished" +msgstr "%02i:%02i:%02i Téléchargement finit" + +#: downloads.c:167 +#, c-format +msgid "%02i:%02i:%02i Downloading…" +msgstr "%02i:%02i:%02i Téléchargement en cours…" + +#: downloads.c:195 downloads.c:212 downloads.c:215 +msgid "Badwolf Downloads" +msgstr "Téléchargements Badwolf" + +#: badwolf.c:861 +#, c-format +msgid "Buildtime WebKit version: %d.%d.%d\n" +msgstr "Version WebKit à la compilation: %d.%d.%d\n" + +#: badwolf.c:391 +msgid "Continue" +msgstr "Continuer" + +#: badwolf.c:341 +msgid "" +"Couldn't verify the TLS certificate to ensure a better security of the " +"connection. You might want to verify your machine and network.\n" +"\n" +msgstr "" +"Impossibilité de vérifier le certificat TLS pour assurer une meilleure " +"sécurité de la connection. Pensez potentiellement à vérifier votre machine " +"et son réseau.\n" +"\n" + +#: badwolf.c:98 +msgid "Crashed" +msgstr "Crash" + +#: downloads.c:39 +msgid "Download starting…" +msgstr "Démarrage du téléchargement" + +#: badwolf.c:364 +msgid "Error: Some unknown error occurred validating the certificate.\n" +msgstr "" +"Erreur : Une erreur inconnue est apparue pendant la validation du " +"certificat.\n" + +#: badwolf.c:345 +msgid "Error: The X509 Certificate Authority is unknown.\n" +msgstr "Erreur : L'autorité de certification (CA X509) est inconnue.\n" + +#: badwolf.c:358 +msgid "Error: The certificate has been revoked.\n" +msgstr "Erreur : Le certificat à été révoqué.\n" + +#: badwolf.c:355 +msgid "Error: The certificate has expired. Check your system's clock.\n" +msgstr "Erreur : Le certificat a expiré.\n" + +#: badwolf.c:361 +msgid "Error: The certificate is considered to be insecure.\n" +msgstr "Erreur : Le certificat est considéré comme non-sécurisé.\n" + +#: badwolf.c:352 +msgid "Error: The certificate isn't valid yet. Check your system's clock.\n" +msgstr "Erreur : Le certificat n'est pas encore valide.\n" + +#: badwolf.c:348 +msgid "Error: The given identity doesn't match the expected one.\n" +msgstr "Erreur : L'identité ne correspond pas à celle attendue.\n" + +#: keybindings.c:32 +#, c-format +msgid "" +"Minimalist and privacy-oriented WebKitGTK+ browser\n" +"Runtime WebKit version: %d.%d.%d" +msgstr "" +"Navigateur WebKitGTK+ minimaliste et orienté vie privée\n" +"Version WebKit au lancement: %d.%d.%d" + +#: badwolf.c:790 +msgid "New tab" +msgstr "Nouvel onglet" + +#: badwolf.c:910 +msgid "Open new tab" +msgstr "Ouvrir un nouvel onglet" + +#: badwolf.c:102 +msgid "Out of Memory" +msgstr "Dépassement Mémoire" + +#: badwolf.c:859 +#, c-format +msgid "Running Badwolf version: %s\n" +msgstr "Version de Badwolf: %s\n" + +#: badwolf.c:866 +#, c-format +msgid "Runtime WebKit version: %d.%d.%d\n" +msgstr "Version WebKit au lancement: %d.%d.%d\n" + +#: badwolf.c:388 +#, c-format +msgid "TLS Error for %s." +msgstr "Erreur TLS pour %s." + +#: badwolf.c:391 +#, fuzzy +msgid "Temporarily Add Exception" +msgstr "Ajouter Temporairement une Exception" + +#: badwolf.c:588 +msgid "Toggle javascript" +msgstr "Activer/Désactiver javascript" + +#: badwolf.c:593 +msgid "Toggle loading images automatically" +msgstr "" + +#: badwolf.c:106 +msgid "Unknown Crash" +msgstr "Crash inconnu" + +#: badwolf.c:591 +msgid "_IMG" +msgstr "_IMG" + +#: badwolf.c:586 +msgid "_JS" +msgstr "_JS" + +#: badwolf.c:706 +msgid "search in current page" +msgstr "recherche dans la page courante" + +#: badwolf.c:97 +msgid "the web process crashed.\n" +msgstr "le processus web a cessé de fonctionner.\n" + +#: badwolf.c:101 +msgid "the web process exceeded the memory limit.\n" +msgstr "le processus web a dépassé la limitation de mémoire.\n" + +#: badwolf.c:105 +msgid "the web process terminated for an unknown reason.\n" +msgstr "le processus web s’est interrompu pour une raison inconnue.\n" + +#: badwolf.c:873 +#, c-format +msgid "webkit-web-extension directory set to: %s\n" +msgstr "Répertoire webkit-web-extension configuré à %s\n" + +#. TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. +#: badwolf.c:944 +msgid "ø" +msgstr "" diff --git a/po/messages.pot b/po/messages.pot new file mode 100644 index 0000000..35c7666 --- /dev/null +++ b/po/messages.pot @@ -0,0 +1,182 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Badwolf Authors +# This file is distributed under the same license as the Badwolf package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Badwolf 1.0.3\n" +"Report-Msgid-Bugs-To: contact+badwolf-msgid@hacktivis.me\n" +"POT-Creation-Date: 2021-01-17 11:22-0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: downloads.c:117 +#, c-format +msgid "%02i:%02i:%02i Download cancelled" +msgstr "" + +#: downloads.c:119 +#, c-format +msgid "%02i:%02i:%02i Download error" +msgstr "" + +#: downloads.c:141 +#, c-format +msgid "%02i:%02i:%02i Download finished" +msgstr "" + +#: downloads.c:167 +#, c-format +msgid "%02i:%02i:%02i Downloading…" +msgstr "" + +#: badwolf.c:906 +#, c-format +msgid "Buildtime WebKit version: %d.%d.%d\n" +msgstr "" + +#: badwolf.c:399 +msgid "Continue" +msgstr "" + +#: badwolf.c:349 +msgid "" +"Couldn't verify the TLS certificate to ensure a better security of the " +"connection. You might want to verify your machine and network.\n" +"\n" +msgstr "" + +#: badwolf.c:95 +msgid "Crashed" +msgstr "" + +#: downloads.c:39 +msgid "Download starting…" +msgstr "" + +#: downloads.c:199 +msgid "Downloads" +msgstr "" + +#: badwolf.c:372 +msgid "Error: Some unknown error occurred validating the certificate.\n" +msgstr "" + +#: badwolf.c:353 +msgid "Error: The X509 Certificate Authority is unknown.\n" +msgstr "" + +#: badwolf.c:366 +msgid "Error: The certificate has been revoked.\n" +msgstr "" + +#: badwolf.c:363 +msgid "Error: The certificate has expired. Check your system's clock.\n" +msgstr "" + +#: badwolf.c:369 +msgid "Error: The certificate is considered to be insecure.\n" +msgstr "" + +#: badwolf.c:360 +msgid "Error: The certificate isn't valid yet. Check your system's clock.\n" +msgstr "" + +#: badwolf.c:356 +msgid "Error: The given identity doesn't match the expected one.\n" +msgstr "" + +#: downloads.c:216 downloads.c:219 +msgid "LRRH Downloads" +msgstr "" + +#: keybindings.c:32 +#, c-format +msgid "" +"Minimalist and privacy-oriented WebKitGTK+ browser\n" +"Runtime WebKit version: %d.%d.%d" +msgstr "" + +#: badwolf.c:841 +msgid "New tab" +msgstr "" + +#: badwolf.c:955 +msgid "Open new tab" +msgstr "" + +#: badwolf.c:99 +msgid "Out of Memory" +msgstr "" + +#: badwolf.c:904 +#, c-format +msgid "Running Badwolf version: %s\n" +msgstr "" + +#: badwolf.c:911 +#, c-format +msgid "Runtime WebKit version: %d.%d.%d\n" +msgstr "" + +#: badwolf.c:396 +#, c-format +msgid "TLS Error for %s." +msgstr "" + +#: badwolf.c:399 +msgid "Temporarily Add Exception" +msgstr "" + +#: badwolf.c:609 +msgid "Toggle javascript" +msgstr "" + +#: badwolf.c:614 +msgid "Toggle loading images automatically" +msgstr "" + +#: badwolf.c:103 +msgid "Unknown Crash" +msgstr "" + +#: badwolf.c:612 +msgid "_IMG" +msgstr "" + +#: badwolf.c:607 +msgid "_JS" +msgstr "" + +#: badwolf.c:729 +msgid "search in current page" +msgstr "" + +#: badwolf.c:94 +msgid "the web process crashed.\n" +msgstr "" + +#: badwolf.c:98 +msgid "the web process exceeded the memory limit.\n" +msgstr "" + +#: badwolf.c:102 +msgid "the web process terminated for an unknown reason.\n" +msgstr "" + +#: badwolf.c:918 +#, c-format +msgid "webkit-web-extension directory set to: %s\n" +msgstr "" + +#. TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. +#: badwolf.c:1001 +msgid "ø" +msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po new file mode 100644 index 0000000..9f54980 --- /dev/null +++ b/po/pt_BR.po @@ -0,0 +1,185 @@ +# BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +# Copyright (C) 2020 Badwolf Authors +# This file is distributed under the same license as the Badwolf package. +# +msgid "" +msgstr "" +"Project-Id-Version: Badwolf 0.4.0+g607300e\n" +"Report-Msgid-Bugs-To: contact+badwolf-msgid@hacktivis.me\n" +"POT-Creation-Date: 2020-05-18 03:06+0200\n" +"PO-Revision-Date: 2020-05-17 20:18-0300\n" +"Last-Translator: Pedro Lucas Porcellis \n" +"Language-Team: Brazilian Portuguese\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: downloads.c:117 +#, c-format +msgid "%02i:%02i:%02i Download cancelled" +msgstr "%02i:%02i:%02i Transferência Cancelada" + +#: downloads.c:119 +#, c-format +msgid "%02i:%02i:%02i Download error" +msgstr "%02i:%02i:%02i Erro durante a transferência" + +#: downloads.c:141 +#, c-format +msgid "%02i:%02i:%02i Download finished" +msgstr "%02i:%02i:%02i Transferência Concluída" + +#: downloads.c:167 +#, c-format +msgid "%02i:%02i:%02i Downloading…" +msgstr "%02i:%02i:%02i Baixando…" + +#: downloads.c:195 downloads.c:212 downloads.c:215 +msgid "Badwolf Downloads" +msgstr "Transferências do Badwolf" + +#: badwolf.c:874 +#, c-format +msgid "Buildtime WebKit version: %d.%d.%d\n" +msgstr "Versão do WebKit %d.%d.%d\n" + +#: badwolf.c:403 +msgid "Continue" +msgstr "Continuar" + +#: badwolf.c:353 +msgid "" +"Couldn't verify the TLS certificate to ensure a better security of the " +"connection. You might want to verify your machine and network.\n" +"\n" +msgstr "" +"Não foi possível verificar o certificado TLS para garantir uma melhor " +"segurança da conexão. Talvez você deva verificar a sua máquina e rede.\n" +"\n" + +#: badwolf.c:99 +msgid "Crashed" +msgstr "Erro" + +#: downloads.c:39 +msgid "Download starting…" +msgstr "Transferência Iniciada…" + +#: badwolf.c:376 +msgid "Error: Some unknown error occurred validating the certificate.\n" +msgstr "" +"Erro: Algum erro desconhecido ocorreu enquanto validava o certificado.\n" + +#: badwolf.c:357 +msgid "Error: The X509 Certificate Authority is unknown.\n" +msgstr "Erro: A Autoridade de Certificados X509 é desconhecida.\n" + +#: badwolf.c:370 +msgid "Error: The certificate has been revoked.\n" +msgstr "Erro: O certificado foi revogado.\n" + +#: badwolf.c:367 +msgid "Error: The certificate has expired. Check your system's clock.\n" +msgstr "" +"Erro: O certificado foi expirado. Verifique o relógio do seu sistema.\n" + +#: badwolf.c:373 +msgid "Error: The certificate is considered to be insecure.\n" +msgstr "Erro: O certificado é considerado inseguro.\n" + +#: badwolf.c:364 +msgid "Error: The certificate isn't valid yet. Check your system's clock.\n" +msgstr "" +"Erro: O certificado não é válido ainda. Verifique o relógio do seu sistema.\n" + +#: badwolf.c:360 +msgid "Error: The given identity doesn't match the expected one.\n" +msgstr "Erro: A identidade não confere com a esperada.\n" + +#: keybindings.c:32 +#, c-format +msgid "" +"Minimalist and privacy-oriented WebKitGTK+ browser\n" +"Runtime WebKit version: %d.%d.%d" +msgstr "" +"Navegador orientado pela privacidade e minimalismo\n" +"Versão do WebKit: %d.%d.%d" + +#: badwolf.c:803 +msgid "New tab" +msgstr "Nova aba" + +#: badwolf.c:923 +msgid "Open new tab" +msgstr "Abrir uma Nova aba" + +#: badwolf.c:103 +msgid "Out of Memory" +msgstr "Sem memória" + +#: badwolf.c:872 +#, c-format +msgid "Running Badwolf version: %s\n" +msgstr "Rodando versão %s do Badwolf\n" + +#: badwolf.c:879 +#, c-format +msgid "Runtime WebKit version: %d.%d.%d\n" +msgstr "Versão do WebKit: %d.%d.%d\n" + +#: badwolf.c:400 +#, c-format +msgid "TLS Error for %s." +msgstr "Erro TLS para %s." + +#: badwolf.c:403 +msgid "Temporarily Add Exception" +msgstr "Adicionar exceção temporária" + +#: badwolf.c:600 +msgid "Toggle javascript" +msgstr "Habilitar Javascript" + +#: badwolf.c:605 +msgid "Toggle loading images automatically" +msgstr "Habilitar carregamento de imagens automático" + +#: badwolf.c:107 +msgid "Unknown Crash" +msgstr "Erro desconhecido" + +#: badwolf.c:603 +msgid "_IMG" +msgstr "_IMG" + +#: badwolf.c:598 +msgid "_JS" +msgstr "_JS" + +#: badwolf.c:718 +msgid "search in current page" +msgstr "buscar na página atual" + +#: badwolf.c:98 +msgid "the web process crashed.\n" +msgstr "o processo web travou.\n" + +#: badwolf.c:102 +msgid "the web process exceeded the memory limit.\n" +msgstr "o processo web excedeu o limite de memória.\n" + +#: badwolf.c:106 +msgid "the web process terminated for an unknown reason.\n" +msgstr "o processo web terminou por uma razão desconhecida.\n" + +#: badwolf.c:886 +#, c-format +msgid "webkit-web-extension directory set to: %s\n" +msgstr "diretório de extensões configurado para: %s\n" + +#. TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. +#: badwolf.c:957 +msgid "ø" +msgstr "" diff --git a/po/tr.po b/po/tr.po new file mode 100644 index 0000000..040c48c --- /dev/null +++ b/po/tr.po @@ -0,0 +1,183 @@ +# Turkish translations for Badwolf package. +# Copyright (C) 2020 Badwolf Authors +# This file is distributed under the same license as the Badwolf package. +# Oğuz Ersen , 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: Badwolf 1.0.2+g17b9802.develop\n" +"Report-Msgid-Bugs-To: contact+badwolf-msgid@hacktivis.me\n" +"POT-Creation-Date: 2020-07-11 11:35+0200\n" +"PO-Revision-Date: 2020-07-17 23:50+0300\n" +"Last-Translator: Oğuz Ersen \n" +"Language-Team: Turkish\n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: downloads.c:117 +#, c-format +msgid "%02i:%02i:%02i Download cancelled" +msgstr "%02i:%02i:%02i İndirme iptal edildi" + +#: downloads.c:119 +#, c-format +msgid "%02i:%02i:%02i Download error" +msgstr "%02i:%02i:%02i İndirme hatası" + +#: downloads.c:141 +#, c-format +msgid "%02i:%02i:%02i Download finished" +msgstr "%02i:%02i:%02i İndirme tamamlandı" + +#: downloads.c:167 +#, c-format +msgid "%02i:%02i:%02i Downloading…" +msgstr "%02i:%02i:%02i İndiriliyor…" + +#: downloads.c:199 downloads.c:216 downloads.c:219 +msgid "Badwolf Downloads" +msgstr "Badwolf İndirmeleri" + +#: badwolf.c:919 +#, c-format +msgid "Buildtime WebKit version: %d.%d.%d\n" +msgstr "Oluşturma zamanı WebKit sürümü: %d.%d.%d\n" + +#: badwolf.c:416 +msgid "Continue" +msgstr "Devam Et" + +#: badwolf.c:366 +msgid "" +"Couldn't verify the TLS certificate to ensure a better security of the " +"connection. You might want to verify your machine and network.\n" +"\n" +msgstr "" +"Bağlantının güvenliğini daha iyi sağlamak için TLS sertifikası " +"doğrulanamadı. Makinenizi ve ağınızı doğrulamak isteyebilirsiniz.\n" +"\n" + +#: badwolf.c:99 +msgid "Crashed" +msgstr "Çöktü" + +#: downloads.c:39 +msgid "Download starting…" +msgstr "İndirme başlatılıyor…" + +#: badwolf.c:389 +msgid "Error: Some unknown error occurred validating the certificate.\n" +msgstr "Hata: Sertifika doğrulanırken bilinmeyen bir hata oluştu.\n" + +#: badwolf.c:370 +msgid "Error: The X509 Certificate Authority is unknown.\n" +msgstr "Hata: X509 Sertifika Yetkilisi bilinmiyor.\n" + +#: badwolf.c:383 +msgid "Error: The certificate has been revoked.\n" +msgstr "Hata: Sertifika yürürlükten kaldırıldı.\n" + +#: badwolf.c:380 +msgid "Error: The certificate has expired. Check your system's clock.\n" +msgstr "Hata: Sertifikanın süresi doldu. Sisteminizin saatini gözden geçirin.\n" + +#: badwolf.c:386 +msgid "Error: The certificate is considered to be insecure.\n" +msgstr "Hata: Sertifikanın güvensiz olduğu kabul ediliyor.\n" + +#: badwolf.c:377 +msgid "Error: The certificate isn't valid yet. Check your system's clock.\n" +msgstr "Hata: Sertifika henüz geçerli değil. Sisteminizin saatini gözden geçirin.\n" + +#: badwolf.c:373 +msgid "Error: The given identity doesn't match the expected one.\n" +msgstr "Hata: Verilen kimlik beklenen ile eşleşmiyor.\n" + +#: keybindings.c:32 +#, c-format +msgid "" +"Minimalist and privacy-oriented WebKitGTK+ browser\n" +"Runtime WebKit version: %d.%d.%d" +msgstr "" +"Sadelik ve gizlilik odaklı WebKitGTK+ tarayıcısı\n" +"Çalışma zamanı WebKit sürümü: %d.%d.%d" + +#: badwolf.c:854 +msgid "New tab" +msgstr "Yeni sekme" + +#: badwolf.c:968 +msgid "Open new tab" +msgstr "Yeni sekme aç" + +#: badwolf.c:103 +msgid "Out of Memory" +msgstr "Yetersiz Bellek" + +#: badwolf.c:917 +#, c-format +msgid "Running Badwolf version: %s\n" +msgstr "Çalışan Badwolf sürümü: %s\n" + +#: badwolf.c:924 +#, c-format +msgid "Runtime WebKit version: %d.%d.%d\n" +msgstr "Çalışma zamanı WebKit sürümü: %d.%d.%d\n" + +#: badwolf.c:413 +#, c-format +msgid "TLS Error for %s." +msgstr "%s için TLS Hatası." + +#: badwolf.c:416 +msgid "Temporarily Add Exception" +msgstr "Geçici Olarak İstisna Ekle" + +#: badwolf.c:624 +msgid "Toggle javascript" +msgstr "Javascript'i aç/kapat" + +#: badwolf.c:629 +msgid "Toggle loading images automatically" +msgstr "Resimleri otomatik yüklemeyi aç/kapat" + +#: badwolf.c:107 +msgid "Unknown Crash" +msgstr "Bilinmeyen Çökme" + +#: badwolf.c:627 +msgid "_IMG" +msgstr "_RSM" + +#: badwolf.c:622 +msgid "_JS" +msgstr "_JS" + +#: badwolf.c:742 +msgid "search in current page" +msgstr "geçerli sayfada ara" + +#: badwolf.c:98 +msgid "the web process crashed.\n" +msgstr "web işlemi çöktü.\n" + +#: badwolf.c:102 +msgid "the web process exceeded the memory limit.\n" +msgstr "web işlemi bellek sınırını aştı.\n" + +#: badwolf.c:106 +msgid "the web process terminated for an unknown reason.\n" +msgstr "web işlemi bilinmeyen bir nedenle sona erdi.\n" + +#: badwolf.c:931 +#, c-format +msgid "webkit-web-extension directory set to: %s\n" +msgstr "webkit-web-extension dizini şuna ayarlandı: %s\n" + +#. TRANSLATOR Ignore this entry. Done for forcing Unicode in xgettext. +#: badwolf.c:1002 +msgid "ø" +msgstr "" diff --git a/uri.c b/uri.c new file mode 100644 index 0000000..912bf86 --- /dev/null +++ b/uri.c @@ -0,0 +1,40 @@ +// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +// Copyright © 2019-2020 Badwolf Authors +// SPDX-License-Identifier: BSD-3-Clause + +#include "uri.h" + +#include /* g_strcmp0(), g_uri_parse_scheme(), g_strdup_printf */ +#include /* realpath(), free() */ +#include /* access() */ + +const gchar * +badwolf_ensure_uri_scheme(const gchar *text, gboolean try_file) +{ + const gchar *fallback = "about:blank"; + char *path = NULL; + + if(g_strcmp0(text, "") <= 0) return fallback; + + if(g_uri_parse_scheme(text)) return text; + + if(try_file) + { + path = realpath(text, NULL); + gchar *f = NULL; + + if(path != NULL) + { + if(access(path, R_OK) == 0) + { + f = g_strdup_printf("file://%s", path); + } + + free(path); + + return f; + } + } + + return g_strdup_printf("http://%s", text); +} diff --git a/uri.h b/uri.h new file mode 100644 index 0000000..cef50b4 --- /dev/null +++ b/uri.h @@ -0,0 +1,17 @@ +#ifndef URI_H_INCLUDED +#define URI_H_INCLUDED +#include + +/* badwolf_ensure_uri_scheme: tries to add a scheme on a pseudo-URL missing a scheme + * - gchar text: pseudo-URL missing a scheme + * - gboolean try_file: when TRUE check try first if it can be a file:// path + * + * When `text` isn't exploitable (ie. NULL), returns "about:blank", + * when the URL seems to be valid, return it, + * if try_file is TRUE, check if it can be file:// path, + * some other checks might be added. + * In the end use the fallback (`http://` for now, might get configuration), + * might get some safeguard. + */ +const gchar *badwolf_ensure_uri_scheme(const gchar *text, gboolean try_file); +#endif /* URI_H_INCLUDED */ diff --git a/uri_test.c b/uri_test.c new file mode 100644 index 0000000..a153817 --- /dev/null +++ b/uri_test.c @@ -0,0 +1,64 @@ +// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +// Copyright © 2019-2020 Badwolf Authors +// SPDX-License-Identifier: BSD-3-Clause + +#include "uri.h" + +#include +#include + +static void +badwolf_ensure_uri_scheme_test(void) +{ + const gchar *fallback = "about:blank"; + + struct + { + const gchar *expect; + const gchar *text; + gboolean try_file; + } cases[] = { + // + {"http://uri.c", "http://uri.c", FALSE}, + {"http://uri.c", "http://uri.c", TRUE}, + {"file:///dev/null", "file:///dev/null", FALSE}, + {"file:///dev/null", "file:///dev/null", TRUE}, + {fallback, NULL, FALSE}, + {fallback, NULL, TRUE}, + {fallback, "", FALSE}, + {fallback, "", TRUE}, + {"http:///dev/null", "/dev/null", FALSE}, + {"file:///dev/null", "/dev/null", TRUE}, + {"http:///usr/../dev/null", "/usr/../dev/null", FALSE}, + {"file:///dev/null", "/usr/../dev/null", TRUE}, + {"http://example.org", "example.org", FALSE}, + {"http://example.org", "example.org", TRUE}, + {"http://", "http://", FALSE}, + {"http://", "http://", TRUE}, + {"http://badwolf.c", "badwolf.c", FALSE} // + }; + + for(size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) + { + g_info("badwolf_ensure_uri_scheme(\"%s\", %s)", + cases[i].text, + cases[i].try_file ? "TRUE" : "FALSE"); + + const gchar *got = badwolf_ensure_uri_scheme(cases[i].text, cases[i].try_file); + + if(g_strcmp0(got, cases[i].expect) != 0) + { + g_error("expected: \"%s\", got: \"%s\"", cases[i].expect, got); + } + } +} + +int +main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/badwolf_ensure_uri_scheme/test", badwolf_ensure_uri_scheme_test); + + return g_test_run(); +} diff --git a/usr.bin.badwolf b/usr.bin.badwolf new file mode 100644 index 0000000..0ebce5d --- /dev/null +++ b/usr.bin.badwolf @@ -0,0 +1,81 @@ +# BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser +# Copyright © 2019-2020 Badwolf Authors +# SPDX-License-Identifier: BSD-3-Clause +# +# Made on Gentoo Linux with PREFIX=/usr +#include + +/usr/bin/badwolf { + #include + #include + #include + #include + #include + + /usr/bin/badwolf mr, + /usr/bin/bwrap Cx, + /usr/libexec/webkit2gtk-4.0/WebKitNetworkProcess Cx, + /usr/libexec/webkit2gtk-4.0/WebKitWebProcess Cx, + + owner @{PROC}/@{pid}/cmdline r, + owner @{PROC}/@{pid}/fd/ r, + + owner @{HOME}/.local/share/badwolf/ r, + owner @{HOME}/.local/share/badwolf/** r, + + deny @{HOME}/.local/share/webkitgtk/** rwmlk, + + / r, + /** r, + + #include + + profile /usr/libexec/webkit2gtk-4.0/WebKitNetworkProcess { + #include + #include + #include + #include + + network inet stream, + network inet6 stream, + + /usr/libexec/webkit2gtk-4.0/WebKitNetworkProcess mr, + /** r, + owner /** w, + } + + profile /usr/libexec/webkit2gtk-4.0/WebKitWebProcess { + #include + #include + #include + #include + #include + #include + #include + #include + + /usr/libexec/webkit2gtk-4.0/WebKitWebProcess mr, + + owner @{PROC}/@{pid}/cmdline r, + owner @{PROC}/@{pid}/fd/ r, + + /etc/passwd r, + /etc/group r, + /etc/nsswitch.conf r, + /dev/ r, + + owner @{HOME}/.local/share/badwolf/webkit-web-extension/ r, + owner @{HOME}/.local/share/badwolf/webkit-web-extension/** mr, + } + + profile /usr/bin/bwrap { + #include + + deny capability sys_admin, + + /usr/bin/bwrap mr, + @{PROC}/sys/kernel/overflowuid r, + @{PROC}/sys/kernel/overflowgid r, + owner @{PROC}/@{pid}/fd/ r, + } +} diff --git a/version.sh b/version.sh new file mode 100755 index 0000000..0c9d3d6 --- /dev/null +++ b/version.sh @@ -0,0 +1,9 @@ +#!/bin/sh +hash=$(git --git-dir="$(dirname $0)/.git" rev-parse --short HEAD 2>/dev/null) +branch=$(git --git-dir="$(dirname $0)/.git" rev-parse --abbrev-ref HEAD | sed -r 's/.*[^0-9A-Za-z-]([0-9A-Za-z-]*)$/\1/g') + +if [ -n "$hash" ] || [ -n "$branch" ] +then + printf '+g%s.%s' "$hash" "$branch" +fi +echo