aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rw-r--r--Makefile8
-rw-r--r--README50
-rw-r--r--examples/config/config3
-rwxr-xr-xexamples/data/scripts/auth.py53
-rw-r--r--examples/data/scripts/follow.js7
-rw-r--r--examples/data/scripts/follower.js420
-rw-r--r--src/callbacks.c32
-rw-r--r--src/callbacks.h6
-rw-r--r--src/events.c3
-rw-r--r--src/events.h1
-rw-r--r--src/uzbl-core.c72
-rw-r--r--src/uzbl-core.h9
-rw-r--r--tests/test-expand.c6
14 files changed, 657 insertions, 16 deletions
diff --git a/AUTHORS b/AUTHORS
index 165eae0..b7b6e87 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -37,6 +37,7 @@ In alphabetical order:
Evgeny Grablyk - libsoup settings
Helmut Grohne (helmut) - move void **ptr to union, various fixes
Henri Kemppainen (DuClare) <email is akarinotengoku AT THE DOMAIN OF gmail.com> - many contributions, mostly old handler code
+ Igor Bogomazov - mouse ptr events
Jake Probst <jake.probst@gmail.com> - uzbl_tabbed: multiline tablist
James S Wheaton (uranther) - zoom level, test framework
Jan Kolkmeier (jouz) - scrolling, link following
@@ -50,7 +51,7 @@ In alphabetical order:
Moritz Lenz - small doc fix
Nicolas Pouillard - refactored scroll command
Olivier Schwander - auto file:// prepend
- Paweł Zuzelski (pawelz) - download handler proxy patch
+ Paweł Zuzelski (pawelz) - http auth handler, misc patches
Peter Suschlik - backwards searching
Přemysl Hrubý (anydot) <email is dfenze AT gmail.com> - several C contributions and cleanups
Robert Manea (robm) <email is rob DOT manea AT gmail DOT com> - C code all over the place
diff --git a/Makefile b/Makefile
index 8fe702d..1b77f6f 100644
--- a/Makefile
+++ b/Makefile
@@ -34,9 +34,6 @@ uzbl-core: ${TOBJ} # why doesn't ${OBJ} work?
@echo LINKING object files
@${CC} -o $@ ${OBJ} ${LDFLAGS}
@echo ... done.
- @echo Stripping binary
- @strip $@
- @echo ... done.
uzbl-browser: uzbl-core
@@ -94,6 +91,11 @@ clean:
cd ./tests/; $(MAKE) clean
rm -rf ./sandbox/
+strip:
+ @echo Stripping binary
+ @strip uzbl-core
+ @echo ... done.
+
install: install-uzbl-core install-uzbl-browser install-uzbl-tabbed
install-uzbl-core: all
diff --git a/README b/README
index 0a22b55..7092508 100644
--- a/README
+++ b/README
@@ -513,9 +513,59 @@ The script specific arguments are:
- `$8 URI` of the page to be navigated to
+* authentication handler:
+
+ $8 authentication zone unique identifier
+ $9 domain part of URL that requests authentication
+ $10 authentication realm
+ $11 FALSE if this is the first attempt to authenticate, TRUE otherwise
+
Custom, userdefined scripts (`spawn foo bar`) get first the arguments as
specified in the config and then the above 7 are added at the end.
+### HTTP/BASIC AUTHENTICATION
+
+You can use the authentication_handler variable to denote how http
+authentication should be handled.
+If this variable is:
+
+* not set or empty: use webkit internal auth dialog
+* a valid handler (i.e. {sh,sync}_spawn correct_script), use this handler
+* innvalid handler (spawn, some other command, uses script that does not
+ print anything): skip authentication.
+
+Example:
+
+ set authentication_handler = sync_spawn /patch/to/your/script
+
+Script will be executed on each authentication request.
+It will receive four auth-related parameters:
+
+ $8 authentication zone unique identifier (may be used as 'key')
+ $9 domain part of URL that requests authentication
+ $10 authentication realm
+ $11 FALSE if this is the first attempt to authenticate, TRUE otherwise
+
+Script is expected to print exactly two lines of text on stdout (that means
+its output must contain exactly two '\n' bytes).
+The first line contains username, the second one - password.
+If authentication fails, script will be executed again (with $11 = TRUE).
+Non-interactive scripts should handle this case and do not try to
+authenticate again to avoid loops. If number of '\n' characters in scripts
+output does not equal 2, authentication will fail.
+That means 401 error will be displayed and uzbl won't try to authenticate anymore.
+
+The simplest example of authentication handler script is:
+
+#!/bin/sh
+[ "$11" == "TRUE ] && exit
+echo alice
+echo wonderland
+
+This script tries to authenticate as user alice with password wonderland once
+and never retries authentication.
+See examples for more sofisticated, interactive authentication handler.
+
### JAVASCRIPT HELPER OBJECT DISABLED BECAUSE OF SECURITY LEAK
JavaScript code run from `uzbl` is given a special object in the global
diff --git a/examples/config/config b/examples/config/config
index eb30cc1..4c63fe7 100644
--- a/examples/config/config
+++ b/examples/config/config
@@ -43,6 +43,7 @@ set scripts_dir = $XDG_DATA_HOME/uzbl:@prefix/share/uzbl/examples/data:scrip
# support events that can wait for a response from a script.
set cookie_handler = talk_to_socket $XDG_CACHE_HOME/uzbl/cookie_daemon_socket
set scheme_handler = sync_spawn @scripts_dir/scheme.py
+set authentication_handler = sync_spawn @scripts_dir/auth.py
# Open in the same window.
#set new_window = sh 'echo uri "$8" > $4'
@@ -313,7 +314,7 @@ set follow_hint_keys = 0123456789
#set follow_hint_keys = qwerty
#set follow_hint_keys = asdfghjkl;
#set follow_hint_keys = thsnd-rcgmvwb/;789aefijkopquxyz234
-@cbind fl* = script @scripts_dir/follow.js '%s @{follow_hint_keys}'
+@cbind fl* = script @scripts_dir/follow.js '@follow_hint_keys %s'
# --- Form filler binds ---
# this script allows you to configure (per domain) values to fill in form
diff --git a/examples/data/scripts/auth.py b/examples/data/scripts/auth.py
new file mode 100755
index 0000000..4feb90b
--- /dev/null
+++ b/examples/data/scripts/auth.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+import gtk
+import sys
+
+def responseToDialog(entry, dialog, response):
+ dialog.response(response)
+
+def getText(authInfo, authHost, authRealm):
+ dialog = gtk.MessageDialog(
+ None,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_QUESTION,
+ gtk.BUTTONS_OK_CANCEL,
+ None)
+ dialog.set_markup('%s at %s' % (authRealm, authHost))
+
+ login = gtk.Entry()
+ password = gtk.Entry()
+ password.set_visibility(False)
+
+ login.connect("activate", responseToDialog, dialog, gtk.RESPONSE_OK)
+ password.connect("activate", responseToDialog, dialog, gtk.RESPONSE_OK)
+
+ hbox = gtk.HBox();
+
+ vbox_entries = gtk.VBox();
+ vbox_labels = gtk.VBox();
+
+ vbox_labels.pack_start(gtk.Label("Login:"), False, 5, 5)
+ vbox_labels.pack_end(gtk.Label("Password:"), False, 5, 5)
+
+ vbox_entries.pack_start(login)
+ vbox_entries.pack_end(password)
+
+ dialog.format_secondary_markup("Please enter username and password:")
+ hbox.pack_start(vbox_labels, True, True, 0)
+ hbox.pack_end(vbox_entries, True, True, 0)
+
+ dialog.vbox.pack_start(hbox)
+ dialog.show_all()
+ rv = dialog.run()
+
+ output = login.get_text() + "\n" + password.get_text()
+ dialog.destroy()
+ return rv, output
+
+if __name__ == '__main__':
+ rv, output = getText(sys.argv[8], sys.argv[9], sys.argv[10])
+ if (rv == gtk.RESPONSE_OK):
+ print output;
+ else:
+ exit(1)
diff --git a/examples/data/scripts/follow.js b/examples/data/scripts/follow.js
index 12b3765..afb327f 100644
--- a/examples/data/scripts/follow.js
+++ b/examples/data/scripts/follow.js
@@ -263,8 +263,7 @@ function followLinks(follow) {
}
}
-//Parse input: first argument is user input, second is defined hint keys.
+//Parse input: first argument is follow keys, second is user input.
var args = '%s'.split(' ');
-var charset = args[1];
-
-followLinks(args[0]);
+var charset = args[0];
+followLinks(args[1]);
diff --git a/examples/data/scripts/follower.js b/examples/data/scripts/follower.js
new file mode 100644
index 0000000..604b779
--- /dev/null
+++ b/examples/data/scripts/follower.js
@@ -0,0 +1,420 @@
+// A Link Follower for Uzbl.
+// P.C. Shyamshankar <sykora@lucentbeing.com>
+//
+// WARNING: this script depends on the Uzbl object which is now disabled for
+// WARNING security reasons. So the script currently doesn't work but it's
+// WARNING interesting nonetheless
+//
+// Based extensively (like copy-paste) on the follow_numbers.js and
+// linkfollow.js included with uzbl, but modified to be more customizable and
+// extensible.
+//
+// Usage
+// -----
+//
+// First, you'll need to make sure the script is loaded on each page. This can
+// be done with:
+//
+// @on_event LOAD_COMMIT script /path/to/follower.js
+//
+// Then you can bind it to a key:
+//
+// @bind f* = js follower.follow('%s', matchSpec, handler, hintStyler)
+//
+// where matchSpec, handler and hintStyler are parameters which control the
+// operation of follower. If you don't want to customize any further, you can
+// set these to follower.genericMatchSpec, follower.genericHandler and
+// follower.genericHintStyler respectively.
+//
+// For example,
+//
+// @bind f* = js follower.follow('%s', follower.genericMatchSpec, follower.genericHandler, follower.genericHintStyler)
+// @bind F* = js follower.follow('%s', follower.onlyLinksMatchSpec, follower.newPageHandler, follower.newPageHintStyler)
+//
+// In order to make hints disappear when pressing a key (the Escape key, for
+// example), you can do this:
+//
+// @bind <Escape> = js follower.clearHints()
+//
+// If your Escape is already bound to something like command mode, chain it.
+//
+// Alternatively, you can tell your <Escape> key to emit an event, and handle
+// that instead.
+//
+// @bind <Escape> = event ESCAPE
+// @on_event ESCAPE js follower.clearHints()
+//
+// Customization
+// -------------
+//
+// If however you do want to customize, 3 Aspects of the link follower can be
+// customized with minimal pain or alteration to the existing code base:
+//
+// * What elements are hinted.
+// * The style of the hints displayed.
+// * How the hints are handled.
+//
+// In order to customize behavior, write an alternative, and pass that in to
+// follower.follow invocation. You _will_ have to modify this script, but only
+// locally, it beats having to copy the entire script under a new name and
+// modify.
+//
+// TODO:
+// * Whatever all the other TODOs in the file say.
+// * Find out how to do default arguments in Javascript.
+// * Abstract out the hints into a Hint object, make hintables a list of hint
+// objects instead of two lists.
+
+// Helpers
+String.prototype.lpad = function(padding, length) {
+ var padded = this;
+ while (padded.length < length) {
+ padded = padding + padded;
+ }
+
+ return padded;
+}
+
+function Follower() {
+
+ // Globals
+ var uzblID = 'uzbl-follow'; // ID to apply to each hint.
+ var uzblContainerID = 'uzbl-follow-container'; // ID to apply to the div containing hints.
+
+ // Translation table, used to display something other than numbers as hint
+ // labels. Typically set to the ten keys of the home row.
+ //
+ // Must have exactly 10 elements.
+ //
+ // I haven't parameterized this, to make it customizable. Should I? Do
+ // people really use more than one set of keys at a time?
+ var translation = ["a", "r", "s", "t", "d", "h", "n", "e", "i", "o"];
+
+ // MatchSpecs
+ // These are XPath expressions which indicate which elements will be hinted.
+ // Use multiple expressions for different situations, like hinting only form
+ // elements, or only links, etc.
+ //
+ // TODO: Check that these XPath expressions are correct, and optimize/make
+ // them more elegant. Preferably by someone who actually knows XPath, unlike
+ // me.
+
+ // Vimperator default (copy-pasted, I never used vimperator).
+ this.genericMatchSpec = " //*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href] | //input[not(@type='hidden')] | //a | //area | //iframe | //textarea | //button | //select";
+
+ // Matches only links, suitable for opening in a new instance (I think).
+ this.onlyLinksMatchSpec = " //*[@href] | //a | //area";
+
+ // Follow Handlers
+ // These decide how an element should be 'followed'. The handler is passed
+ // the element in question.
+
+ // Generic Handler, opens links in the same instance, emits the FORM_ACTIVE
+ // event if a form element was chosen. Also clears the keycmd.
+ this.genericHandler = function(node) {
+ if (node) {
+ if (window.itemClicker != undefined) {
+ window.itemClicker(node);
+ } else {
+ var tag = node.tagName.toLowerCase();
+ if (tag == 'a') {
+ node.click();
+ window.location = node.href;
+ } else if (tag == 'input') {
+ var inputType = node.getAttribute('type');
+ if (inputType == undefined)
+ inputType = 'text';
+
+ inputType = inputType.toLowerCase();
+
+ if (inputType == 'text' || inputType == 'file' || inputType == 'password') {
+ node.focus();
+ node.select();
+ } else {
+ node.click();
+ }
+ Uzbl.run("event FORM_ACTIVE");
+ } else if (tag == 'textarea'|| tag == 'select') {
+ node.focus();
+ node.select();
+ Uzbl.run("event FORM_ACTIVE");
+ } else {
+ node.click();
+ if ((node.href != undefined) && node.href)
+ window.location = node.href;
+ }
+ }
+ }
+ Uzbl.run("event SET_KEYCMD");
+ }
+
+ // Handler to open links in a new page. The rest is the same as before.
+ this.newPageHandler = function(node) {
+ if (node) {
+ if (window.itemClicker != undefined) {
+ window.itemClicker(node);
+ } else {
+ var tag = node.tagName.toLowerCase();
+ if (tag == 'a') {
+ node.click();
+ Uzbl.run("@new_window " + node.href);
+ } else if (tag == 'input') {
+ var inputType = node.getAttribute('type');
+ if (inputType == undefined)
+ inputType = 'text';
+
+ inputType = inputType.toLowerCase();
+
+ if (inputType == 'text' || inputType == 'file' || inputType == 'password') {
+ node.focus();
+ node.select();
+ } else {
+ node.click();
+ }
+ Uzbl.run("event FORM_ACTIVE");
+ } else if (tag == 'textarea'|| tag == 'select') {
+ node.focus();
+ node.select();
+ Uzbl.run("event FORM_ACTIVE");
+ } else {
+ node.click();
+ if ((node.href != undefined) && node.href)
+ window.location = node.href;
+ }
+ }
+ }
+ Uzbl.run("event SET_KEYCMD");
+ };
+
+ // Hint styling.
+ // Pretty much any attribute of the hint object can be modified here, but it
+ // was meant to change the styling. Useful to differentiate between hints
+ // with different handlers.
+ //
+ // Hint stylers are applied at the end of hint creation, so that they
+ // override the defaults.
+
+ this.genericHintStyler = function(hint) {
+ hint.style.backgroundColor = '#AAAAAA';
+ hint.style.border = '2px solid #4A6600';
+ hint.style.color = 'black';
+ hint.style.fontSize = '10px';
+ hint.style.fontWeight = 'bold';
+ hint.style.lineHeight = '12px';
+ return hint;
+ };
+
+ this.newPageHintStyler = function(hint) {
+ hint.style.backgroundColor = '#FFCC00';
+ hint.style.border = '2px solid #4A6600';
+ hint.style.color = 'black';
+ hint.style.fontSize = '10px';
+ hint.style.fontWeight = 'bold';
+ hint.style.lineHeight = '12px';
+ return hint;
+ };
+
+ // Beyond lies a jungle of pasta and verbosity.
+
+ // Translate a numeric label using the translation table.
+ function translate(digitLabel, translationTable) {
+ translatedLabel = '';
+ for (var i = 0; i < digitLabel.length; i++) {
+ translatedLabel += translationTable[digitLabel.charAt(i)];
+ }
+
+ return translatedLabel;
+ }
+
+ function computeElementPosition(element) {
+ var up = element.offsetTop;
+ var left = element.offsetLeft;
+ var width = element.offsetWidth;
+ var height = element.offsetHeight;
+
+ while (element.offsetParent) {
+ element = element.offsetParent;
+ up += element.offsetTop;
+ left += element.offsetLeft;
+ }
+
+ return {up: up, left: left, width: width, height: height};
+ }
+
+ // Pretty much copy-pasted from every other link following script.
+ function isInViewport(element) {
+ offset = computeElementPosition(element);
+
+ var up = offset.up;
+ var left = offset.left;
+ var width = offset.width;
+ var height = offset.height;
+
+ return up < window.pageYOffset + window.innerHeight &&
+ left < window.pageXOffset + window.innerWidth &&
+ (up + height) > window.pageYOffset &&
+ (left + width) > window.pageXOffset;
+ }
+
+ function isVisible(element) {
+ if (element == document) {
+ return true;
+ }
+
+ if (!element){
+ return false;
+ }
+
+ if (element.style) {
+ if (element.style.display == 'none' || element.style.visibiilty == 'hidden') {
+ return false;
+ }
+ }
+
+ return isVisible(element.parentNode);
+ }
+
+ function generateHintContainer() {
+ var container = document.getElementById(uzblContainerID);
+ if (container) {
+ container.parentNode.removeChild(container);
+ }
+
+ container = document.createElement('div');
+ container.id = uzblContainerID;
+
+ if (document.body) {
+ document.body.appendChild(container);
+ }
+ return container;
+ }
+
+ // Generate everything that is to be hinted, as per the given matchSpec.
+ // hintables[0] refers to the items, hintables[1] to their labels.
+ function generateHintables(matchSpec) {
+ var hintables = [[], []];
+
+ var itemsFromXPath = document.evaluate(matchSpec, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+ for (var i = 0; i < itemsFromXPath.snapshotLength; ++i) {
+ var element = itemsFromXPath.snapshotItem(i);
+ if (element && isVisible(element) && isInViewport(element)) {
+ hintables[0].push(element);
+ }
+ }
+
+ // Assign labels to each hintable. Can't be combined with the previous
+ // step, because we didn't know how many there were at that time.
+ var hintLength = hintables.length;
+ for (var i = 0; i < hintables[0].length; ++i) {
+ var code = translate(i.toString(), translation);
+ hintables[1].push(code.lpad(translation[0], hintLength));
+ }
+
+ return hintables;
+ }
+
+ // Filter the hintables based on input from the user. Makes the screen less
+ // cluttered after the user has typed some prefix of hint labels.
+ function filterHintables(hintables, target) {
+ var filtered = [[], []];
+
+ var targetPattern = new RegExp("^" + target);
+
+ for (var i = 0; i < hintables[0].length; i++) {
+ if (hintables[1][i].match(targetPattern)) {
+ filtered[0].push(hintables[0][i]);
+ filtered[1].push(hintables[1][i].substring(target.length));
+ }
+ }
+
+ return filtered;
+ }
+
+ // TODO make this use the container variable from main, instead of searching
+ // for it?
+ function clearHints() {
+ var container = document.getElementById(uzblContainerID);
+ if (container) {
+ container.parentNode.removeChild(container);
+ }
+ }
+
+ // So that we can offer this as a separate function.
+ this.clearHints = clearHints;
+
+ function makeHint(node, code, styler) {
+ var position = computeElementPosition(node);
+ var hint = document.createElement('div');
+
+ hint.name = uzblID;
+ hint.innerText = code;
+ hint.style.display = 'inline';
+
+ hint.style.margin = '0px';
+ hint.style.padding = '1px';
+ hint.style.position = 'absolute';
+ hint.style.zIndex = '10000';
+
+ hint.style.left = position.left + 'px';
+ hint.style.top = position.up + 'px';
+
+ var img = node.getElementsByTagName('img');
+ if (img.length > 0) {
+ hint.style.left = position.left + img[0].width / 2 + 'px';
+ }
+
+ hint.style.textDecoration = 'none';
+ hint.style.webkitBorderRadius = '6px';
+ hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px, -5px)';
+
+ hint = styler(hint); // So that custom hint stylers can override the above.
+ return hint;
+ }
+
+
+ function drawHints(container, hintables, styler) {
+ for (var i = 0; i < hintables[0].length; i++) {
+ hint = makeHint(hintables[0][i], hintables[1][i], styler);
+ container.appendChild(hint);
+ }
+
+ if (document.body) {
+ document.body.appendChild(container);
+ }
+ }
+
+ // The main hinting function. I don't know how to do default values to
+ // functions, so all arguments must be specified. Use generics if you must.
+ this.follow = function(target, matchSpec, handler, hintStyler) {
+ var container = generateHintContainer(); // Get a container to hold all hints.
+ var allHintables = generateHintables(matchSpec); // Get all items that can be hinted.
+ hintables = filterHintables(allHintables, target); // Filter them based on current input.
+
+ clearHints(); // Clear existing hints, if any.
+
+ if (hintables[0].length == 0) {
+ // Nothing was hinted, user pressed an unknown key, maybe?
+ // Do nothing.
+ } else if (hintables[0].length == 1) {
+ handler(hintables[0][0]); // Only one hint remains, handle it.
+ } else {
+ drawHints(container, hintables, hintStyler); // Draw whatever hints remain.
+ }
+
+ return;
+ };
+}
+
+// Make on-click links clickable.
+try {
+ HTMLElement.prototype.click = function() {
+ if (typeof this.onclick == 'function') {
+ this.onclick({
+ type: 'click'
+ });
+ }
+ };
+} catch(e) {}
+
+follower = new Follower();
diff --git a/src/callbacks.c b/src/callbacks.c
index 9130f5f..6ec48ea 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -27,6 +27,25 @@ set_proxy_url() {
}
void
+set_authentication_handler() {
+ /* Check if WEBKIT_TYPE_SOUP_AUTH_DIALOG feature is set */
+ GSList *flist = soup_session_get_features (uzbl.net.soup_session, (GType) WEBKIT_TYPE_SOUP_AUTH_DIALOG);
+ guint feature_is_set = g_slist_length(flist);
+ g_slist_free(flist);
+
+ if (uzbl.behave.authentication_handler == NULL || *uzbl.behave.authentication_handler == NULL) {
+ if (!feature_is_set)
+ soup_session_add_feature_by_type
+ (uzbl.net.soup_session, (GType) WEBKIT_TYPE_SOUP_AUTH_DIALOG);
+ } else {
+ if (feature_is_set)
+ soup_session_remove_feature_by_type
+ (uzbl.net.soup_session, (GType) WEBKIT_TYPE_SOUP_AUTH_DIALOG);
+ }
+ return;
+}
+
+void
set_icon() {
if(file_exists(uzbl.gui.icon)) {
if (uzbl.gui.main_window)
@@ -559,6 +578,19 @@ button_release_cb (GtkWidget* window, GdkEventButton* event) {
}
gboolean
+motion_notify_cb(GtkWidget* window, GdkEventMotion* event, gpointer user_data) {
+ (void) window;
+ (void) event;
+ (void) user_data;
+
+ gchar *details;
+ details = g_strdup_printf("%.0lf %.0lf %u", event->x, event->y, event->state);
+ send_event(PTR_MOVE, details, NULL);
+
+ return FALSE;
+}
+
+gboolean
navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
(void) web_view;
(void) frame;
diff --git a/src/callbacks.h b/src/callbacks.h
index 882ffa4..43a9192 100644
--- a/src/callbacks.h
+++ b/src/callbacks.h
@@ -13,6 +13,9 @@ void
set_proxy_url();
void
+set_authentication_handler();
+
+void
set_icon();
void
@@ -167,6 +170,9 @@ gboolean
key_release_cb (GtkWidget* window, GdkEventKey* event);
gboolean
+motion_notify_cb(GtkWidget* window, GdkEventMotion* event, gpointer user_data);
+
+gboolean
navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame,
WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action,
WebKitWebPolicyDecision *policy_decision, gpointer user_data);
diff --git a/src/events.c b/src/events.c
index acb554c..371c8b8 100644
--- a/src/events.c
+++ b/src/events.c
@@ -43,7 +43,8 @@ const char *event_table[LAST_EVENT] = {
"FILE_INCLUDED" ,
"PLUG_CREATED" ,
"COMMAND_ERROR" ,
- "BUILTINS"
+ "BUILTINS" ,
+ "PTR_MOVE"
};
void
diff --git a/src/events.h b/src/events.h
index 7b8f58b..b02a43c 100644
--- a/src/events.h
+++ b/src/events.h
@@ -14,6 +14,7 @@ enum event_type {
LINK_UNHOVER, FORM_ACTIVE, ROOT_ACTIVE,
FOCUS_LOST, FOCUS_GAINED, FILE_INCLUDED,
PLUG_CREATED, COMMAND_ERROR, BUILTINS,
+ PTR_MOVE,
/* must be last entry */
LAST_EVENT
diff --git a/src/uzbl-core.c b/src/uzbl-core.c
index da61093..9168597 100644
--- a/src/uzbl-core.c
+++ b/src/uzbl-core.c
@@ -101,6 +101,7 @@ const struct var_name_to_ptr_t {
{ "forward_keys", PTR_V_INT(uzbl.behave.forward_keys, 1, NULL)},
{ "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
{ "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, NULL)},
+ { "authentication_handler", PTR_V_STR(uzbl.behave.authentication_handler, 1, set_authentication_handler)},
{ "new_window", PTR_V_STR(uzbl.behave.new_window, 1, NULL)},
{ "scheme_handler", PTR_V_STR(uzbl.behave.scheme_handler, 1, NULL)},
{ "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
@@ -2041,6 +2042,7 @@ create_browser () {
"signal::key-release-event", (GCallback)key_release_cb, NULL,
"signal::button-press-event", (GCallback)button_press_cb, NULL,
"signal::button-release-event", (GCallback)button_release_cb, NULL,
+ "signal::motion-notify-event", (GCallback)motion_notify_cb, NULL,
"signal::title-changed", (GCallback)title_change_cb, NULL,
"signal::selection-changed", (GCallback)selection_changed_cb, NULL,
"signal::load-progress-changed", (GCallback)progress_change_cb, NULL,
@@ -2317,6 +2319,63 @@ settings_init () {
init_connect_socket();
g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
+ g_signal_connect(n->soup_session, "authenticate", G_CALLBACK(handle_authentication), NULL);
+}
+
+void handle_authentication (SoupSession *session, SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer user_data) {
+
+ (void) user_data;
+
+ if(uzbl.behave.authentication_handler && *uzbl.behave.authentication_handler != NULL) {
+ gchar *info, *host, *realm;
+ gchar *p;
+
+ soup_session_pause_message(session, msg);
+
+ /* Sanitize strings */
+ info = g_strdup(soup_auth_get_info(auth));
+ host = g_strdup(soup_auth_get_host(auth));
+ realm = g_strdup(soup_auth_get_realm(auth));
+ for (p = info; *p; p++) if (*p == '\'') *p = '\"';
+ for (p = host; *p; p++) if (*p == '\'') *p = '\"';
+ for (p = realm; *p; p++) if (*p == '\'') *p = '\"';
+
+ GString *s = g_string_new ("");
+ g_string_printf(s, "'%s' '%s' '%s' '%s'",
+ info, host, realm, retrying?"TRUE":"FALSE");
+
+ run_handler(uzbl.behave.authentication_handler, s->str);
+
+ if (uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
+ char *username, *password;
+ int number_of_endls=0;
+
+ username = uzbl.comm.sync_stdout;
+
+ for (p = uzbl.comm.sync_stdout; *p; p++) {
+ if (*p == '\n') {
+ *p = '\0';
+ if (++number_of_endls == 1)
+ password = p + 1;
+ }
+ }
+
+ /* If stdout was correct (contains exactly two lines of text) do
+ * authenticate. */
+ if (number_of_endls == 2)
+ soup_auth_authenticate(auth, username, password);
+ }
+
+ if (uzbl.comm.sync_stdout)
+ uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+
+ soup_session_unpause_message(session, msg);
+
+ g_string_free(s, TRUE);
+ g_free(info);
+ g_free(host);
+ g_free(realm);
+ }
}
void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
@@ -2467,9 +2526,9 @@ initialize(int argc, char *argv[]) {
}
event_buffer_timeout(10);
- uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
- uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
- uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
+ uzbl.info.webkit_major = webkit_major_version();
+ uzbl.info.webkit_minor = webkit_minor_version();
+ uzbl.info.webkit_micro = webkit_micro_version();
uzbl.info.arch = ARCH;
uzbl.info.commit = COMMIT;
@@ -2483,6 +2542,13 @@ initialize(int argc, char *argv[]) {
void
load_uri_imp(gchar *uri) {
GString* newuri;
+
+ /* Strip leading whitespaces */
+ while (*uri) {
+ if (!isspace(*uri)) break;
+ uri++;
+ }
+
if (g_strstr_len (uri, 11, "javascript:") != NULL) {
eval_js(uzbl.gui.web_view, uri, NULL);
return;
diff --git a/src/uzbl-core.h b/src/uzbl-core.h
index 70a383c..e05fcdb 100644
--- a/src/uzbl-core.h
+++ b/src/uzbl-core.h
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <string.h>
+#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
@@ -123,6 +124,7 @@ typedef struct {
gchar* socket_dir;
gchar* download_handler;
gchar* cookie_handler;
+ gchar* authentication_handler;
gchar* new_window;
gchar* default_font_family;
gchar* monospace_font_family;
@@ -390,6 +392,13 @@ run_external_js (WebKitWebView * web_view, GArray *argv, GString *result);
void
eval_js(WebKitWebView * web_view, gchar *script, GString *result);
+void
+handle_authentication (SoupSession *session,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ gpointer user_data);
+
void handle_cookies (SoupSession *session,
SoupMessage *msg,
gpointer user_data);
diff --git a/tests/test-expand.c b/tests/test-expand.c
index 4dcd82d..7ea3d61 100644
--- a/tests/test-expand.c
+++ b/tests/test-expand.c
@@ -72,11 +72,11 @@ test_useragent (void) {
void
test_WEBKIT_VERSION (void) {
GString* expected = g_string_new("");
- g_string_append(expected, itos(WEBKIT_MAJOR_VERSION));
+ g_string_append(expected, itos(webkit_major_version()));
g_string_append(expected, " ");
- g_string_append(expected, itos(WEBKIT_MINOR_VERSION));
+ g_string_append(expected, itos(webkit_minor_version()));
g_string_append(expected, " ");
- g_string_append(expected, itos(WEBKIT_MICRO_VERSION));
+ g_string_append(expected, itos(webkit_micro_version()));
g_assert_cmpstr(expand("@WEBKIT_MAJOR @WEBKIT_MINOR @WEBKIT_MICRO", 0), ==, g_string_free(expected, FALSE));
}