diff options
author | Brendan Taylor <whateley@gmail.com> | 2010-12-03 22:04:24 -0700 |
---|---|---|
committer | Brendan Taylor <whateley@gmail.com> | 2010-12-03 22:04:24 -0700 |
commit | 356c565e78406e6148311d4ecb99ec2e9010e5f9 (patch) | |
tree | 1b78d6d478e994615376be9c3b04eb33a77c5b71 /examples | |
parent | b8b5e56ba3a764cb162160bc5a326acfdb5ad6e6 (diff) | |
parent | 55bd9f83f2aacd11a14ca46a98a85584669c6c13 (diff) |
Merge branch 'experimental' into dev/per-site-settings
Diffstat (limited to 'examples')
26 files changed, 1089 insertions, 385 deletions
diff --git a/examples/config/config b/examples/config/config index b6d9fe3..48aacfc 100644 --- a/examples/config/config +++ b/examples/config/config @@ -3,8 +3,10 @@ # === Core settings ========================================================== -# Install location prefix. -set prefix = /usr/local +# common directory locations +set prefix = @(echo $PREFIX)@ +set data_home = @(echo $XDG_DATA_HOME)@ +set cache_home = @(echo $XDG_CACHE_HOME)@ # Interface paths. set fifo_dir = /tmp @@ -34,28 +36,26 @@ set set_mode = set mode = set set_status = set status_message = # Spawn path shortcuts. In spawn the first dir+path match is used in "dir1:dir2:dir3:executable" -set scripts_dir = $XDG_DATA_HOME/uzbl:@prefix/share/uzbl/examples/data:scripts +set scripts_dir = @data_home/uzbl:@prefix/share/uzbl/examples/data:scripts # === Hardcoded handlers ===================================================== # These handlers can't be moved to the new event system yet as we don't # 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 cookie_handler = talk_to_socket @cache_home/uzbl/cookie_daemon_socket set scheme_handler = sync_spawn @scripts_dir/scheme.py set authentication_handler = sync_spawn @scripts_dir/auth.py +set download_handler = sync_spawn @scripts_dir/download.sh # === Dynamic event handlers ================================================= # Open link in new window -@on_event NEW_WINDOW sh 'uzbl-browser -u "$8"' %r +@on_event NEW_WINDOW sh 'uzbl-browser ${8:+-u "$8"}' %r # Open in current window #@on_event NEW_WINDOW uri %s # Open in new tab #@on_event NEW_WINDOW event NEW_TAB %s -# Download handler -@on_event DOWNLOAD_REQUEST spawn @scripts_dir/download.sh %s \@proxy_url - # Load start handler @on_event LOAD_START @set_status <span foreground="khaki">wait</span> # Reset the keycmd on navigation @@ -64,6 +64,9 @@ set authentication_handler = sync_spawn @scripts_dir/auth.py # Load commit handlers @on_event LOAD_COMMIT @set_status <span foreground="green">recv</span> +# Userscript support. Add all scripts to $XDG_DATA_HOME/uzbl/userscripts +#@on_event LOAD_COMMIT spawn @scripts_dir/userscripts.sh + # Load finish handlers @on_event LOAD_FINISH @set_status <span foreground="gold">done</span> @on_event LOAD_FINISH spawn @scripts_dir/history.sh @@ -76,6 +79,9 @@ set authentication_handler = sync_spawn @scripts_dir/auth.py # Example CONFIG_CHANGED event handler #@on_event CONFIG_CHANGED print Config changed: %1 = %2 +# Scroll percentage calculation +@on_event SCROLL_VERT set scroll_message = \@<(function(){var a='%1'.split(' ');var p='--';if(a[2]!=a[1]){p=(a[0]/(a[2]-a[3]));p=Math.round(10000*p)/100;};return p+'%';})()>\@ + # === Behaviour and appearance =============================================== set show_status = 1 @@ -98,7 +104,9 @@ set name_section = <span foreground="khaki">\@[\@NAME]\@</span> set status_section = <span foreground="orange">\@status_message</span> set selected_section = <span foreground="#606060">\@[\@SELECTED_URI]\@</span> -set status_format = <span font_family="monospace">@mode_section @keycmd_section @progress_section @uri_section @name_section @status_section @scroll_section @selected_section</span> +set download_section = <span foreground="white">\@downloads</span> + +set status_format = <span font_family="monospace">@mode_section @keycmd_section @progress_section @uri_section @name_section @status_section @scroll_section @selected_section @download_section</span> set title_format_long = \@keycmd_prompt \@raw_modcmd \@raw_keycmd \@TITLE - Uzbl browser <\@NAME> \@SELECTED_URI @@ -121,6 +129,7 @@ set useragent = Uzbl (Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}.@{WEBKIT_MI @modmap <Control> <Ctrl> @modmap <ISO_Left_Tab> <Shift-Tab> @modmap <space> <Space> +@modmap <KP_Enter> <Enter> #modkey_addition <Key1> <Key2> <Result> @modkey_addition <Shift> <Ctrl> <Meta> @@ -130,6 +139,7 @@ set useragent = Uzbl (Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}.@{WEBKIT_MI #ignore_key <glob> @ignore_key <ISO_*> @ignore_key <Shift> +@ignore_key <Multi_key> # --- Bind aliases ----------------------------------------------------------- @@ -173,7 +183,7 @@ set ebind = @mode_bind global,-insert # --- Mouse bindings --------------------------------------------------------- # Middle click open in new window -@bind <Button2> = sh 'if [ "$8" ]; then uzbl-browser -u "$8"; else echo "uri $(xclip -o | sed s/\\\@/%40/g)" > $4; fi' \@SELECTED_URI +@bind <Button2> = sh 'if [ "$8" ]; then uzbl-browser -u "$8"; else echo "uri $(xclip -o | sed s/\\\@/%40/g)" > "$UZBL_FIFO"; fi' \@SELECTED_URI # --- Keyboard bindings ------------------------------------------------------ @@ -181,6 +191,9 @@ set ebind = @mode_bind global,-insert # a colon. @cbind :_ = %s +# open a new window or a new tab (see the on_event NEW_WINDOW settings above) +@cbind w = event NEW_WINDOW + # Page movement binds @cbind j = scroll vertical 20 @cbind k = scroll vertical -20 @@ -188,8 +201,12 @@ set ebind = @mode_bind global,-insert @cbind l = scroll horizontal 20 @cbind <Page_Up> = scroll vertical -100% @cbind <Page_Down> = scroll vertical 100% +@cbind <Ctrl>f = scroll vertical 100% +@cbind <Ctrl>b = scroll vertical -100% @cbind << = scroll vertical begin @cbind >> = scroll vertical end +@cbind <Home> = scroll vertical begin +@cbind <End> = scroll vertical end @cbind ^ = scroll horizontal begin @cbind $ = scroll horizontal end @cbind <Space> = scroll vertical end @@ -230,14 +247,14 @@ set ebind = @mode_bind global,-insert # Exit binding @cbind ZZ = exit # Dump config to stdout -@cbind !dump = sh "echo dump_config > $4" +@cbind !dump = sh 'echo dump_config > "$UZBL_FIFO"' # Reload all variables in the config -@cbind !reload = sh "sed '/^# === Post-load misc commands/,$d' $1 | grep '^set ' > $4" +@cbind !reload = sh "sed '/^# === Post-load misc commands/,$d' \"$UZBL_CONFIG\" | grep '^set ' > \"$UZBL_FIFO\"" # Use socat to directly inject commands into uzbl-core and view events # raised by uzbl-core: -@cbind <Ctrl><Alt>t = sh 'xterm -e "socat unix-connect:$5 -"' -#@cbind <Ctrl><Alt>t = sh 'urxvt -e socat unix-connect:$5 -' +@cbind <Ctrl><Alt>t = sh "xterm -e 'socat unix-connect:\"$UZBL_SOCKET\" -'" +#@cbind <Ctrl><Alt>t = sh "urxvt -e 'socat unix-connect:\"$UZBL_SOCKET\" -'" # Uri opening prompts @cbind o<uri:>_ = uri %s @@ -252,20 +269,24 @@ set ebind = @mode_bind global,-insert @cbind gh = uri http://www.uzbl.org # Yanking & pasting binds -@cbind yu = sh 'echo -n $6 | xclip' -@cbind yy = sh 'echo -n $7 | xclip' +@cbind yu = sh 'echo -n "$UZBL_URI" | xclip' +@cbind yU = sh 'echo -n $8 | xclip' \@SELECTED_URI +@cbind yy = sh 'echo -n "$UZBL_TITLE" | xclip' +@cbind yY = sh 'echo -n $8 | xclip' \@SELECTED_URI +# Clone current window +@cbind c = sh 'uzbl-browser -u "$UZBL_URI"' # Go the page from primary selection -@cbind p = sh 'echo "uri `xclip -selection primary -o | sed s/\\\@/%40/g`" > $4' +@cbind p = sh 'echo "uri `xclip -selection primary -o | sed s/\\\@/%40/g`" > "$UZBL_FIFO"' # Go to the page in clipboard -@cbind P = sh 'echo "uri `xclip -selection clipboard -o | sed s/\\\@/%40/g`" > $4' +@cbind P = sh 'echo "uri `xclip -selection clipboard -o | sed s/\\\@/%40/g`" > "$UZBL_FIFO"' # Start a new uzbl instance from the page in primary selection -@cbind 'p = sh 'exec uzbl-browser --uri $(xclip -o)' +@cbind 'p = sh 'exec uzbl-browser --uri "$(xclip -o)"' # paste primary selection into keycmd at the cursor position -@bind <Shift-Insert> = sh 'echo "event INJECT_KEYCMD `xclip -o | sed s/\\\@/%40/g`" > $4' +@bind <Shift-Insert> = sh 'echo "event INJECT_KEYCMD `xclip -o | sed s/\\\@/%40/g`" > "$UZBL_FIFO"' # Bookmark inserting binds -@cbind <Ctrl>b<tags:>_ = sh 'echo -e "$6 %s" >> $XDG_DATA_HOME/uzbl/bookmarks' +@cbind <Ctrl>b<tags:>_ = sh 'echo `printf "$UZBL_URI %s"` >> "$XDG_DATA_HOME"/uzbl/bookmarks' # Or use a script to insert a bookmark. @cbind B = spawn @scripts_dir/insert_bookmark.sh @@ -279,7 +300,8 @@ 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 '@follow_hint_keys %s' +@cbind fl* = spawn @scripts_dir/follow.sh "%s" +@cbind gi = spawn @scripts_dir/go_input.sh # Form filler binds # This script allows you to configure (per domain) values to fill in form @@ -299,7 +321,12 @@ set formfiller = spawn @scripts_dir/formfiller.sh @cbind gN = event NEW_TAB_NEXT @cbind go<uri:>_ = event NEW_TAB %s @cbind gO<uri:>_ = event NEW_TAB_NEXT %s -@cbind gY = sh 'echo "event NEW_TAB `xclip -selection primary -o | sed s/\\\@/%40/g`" > $4' +@cbind gy = sh 'echo "event NEW_TAB `xclip -selection primary -o | sed s/\\\@/%40/g`" > "$UZBL_FIFO"' +@cbind gY = sh 'echo "event NEW_TAB_NEXT `xclip -selection primary -o | sed s/\\\@/%40/g`" > "$UZBL_FIFO"' + +# Clone current tab +@cbind gd = sh 'echo "event NEW_TAB $UZBL_URI" > "$UZBL_FIFO"' +@cbind gD = sh 'echo "event NEW_TAB_NEXT $UZBL_URI" > "$UZBL_FIFO"' # Closing / resting @cbind gC = exit @@ -353,13 +380,13 @@ set stack = @mode_config stack @insert modcmd_updates = 0 # Multi-stage-binding mode config. -@stack keycmd_events = 1 -@stack modcmd_updates = 1 -@stack forward_keys = 0 @stack keycmd_style = foreground="red" -@stack prompt_style = foreground="#888" weight="light" @stack status_background = #202020 @stack mode_indicator = Bnd +@stack prompt_style = foreground="#888" weight="light" +@stack keycmd_events = 1 +@stack modcmd_updates = 1 +@stack forward_keys = 0 set default_mode = command diff --git a/examples/config/cookies b/examples/config/cookies deleted file mode 100644 index 9b7374a..0000000 --- a/examples/config/cookies +++ /dev/null @@ -1,22 +0,0 @@ -# This file demonstrates how one *could* manage his cookies. this file is used by the example cookie handler script. -# stick to this format. -# trusted -> always store what we get, send what we have (TODO: by default, or when requested?) -# deny -> deny storing + sending - -# if you don't like to edit this file manually, you could even write a script that adds/removes entries using sed, and call the script from uzbl with a keybind... - - -TRUSTED -bbs.archlinux.org -archlinux.org -linux.com - - - - -DENY -www.icanhascheezburger.com - - - -# rest -> ask
\ No newline at end of file diff --git a/examples/data/plugins/downloads.py b/examples/data/plugins/downloads.py new file mode 100644 index 0000000..7bf32d7 --- /dev/null +++ b/examples/data/plugins/downloads.py @@ -0,0 +1,69 @@ +# this plugin does a very simple display of download progress. to use it, add +# @downloads to your status_format. + +import os +ACTIVE_DOWNLOADS = {} + +# after a download's status has changed this is called to update the status bar +def update_download_section(uzbl): + global ACTIVE_DOWNLOADS + + if len(ACTIVE_DOWNLOADS): + # add a newline before we list downloads + result = ' downloads:' + for path in ACTIVE_DOWNLOADS: + # add each download + fn = os.path.basename(path) + progress, = ACTIVE_DOWNLOADS[path] + + dl = " %s (%d%%)" % (fn, progress * 100) + + # replace entities to make sure we don't break our markup + # (this could be done with an @[]@ expansion in uzbl, but then we + # can't use the above to make a new line) + dl = dl.replace("&", "&").replace("<", "<") + result += dl + else: + result = '' + + # and the result gets saved to an uzbl variable that can be used in + # status_format + if uzbl.config.get('downloads', '') != result: + uzbl.config['downloads'] = result + +def download_started(uzbl, destination_path): + # add to the list of active downloads + global ACTIVE_DOWNLOADS + ACTIVE_DOWNLOADS[destination_path] = (0.0,) + + # update the progress + update_download_section(uzbl) + +def download_progress(uzbl, args): + # parse the arguments + s = args.rindex(' ') + destination_path = args[:s] + progress = float(args[s+1:]) + + # update the progress + global ACTIVE_DOWNLOADS + ACTIVE_DOWNLOADS[destination_path] = (progress,) + + # update the status bar variable + update_download_section(uzbl) + +def download_complete(uzbl, destination_path): + # remove from the list of active downloads + global ACTIVE_DOWNLOADS + del ACTIVE_DOWNLOADS[destination_path] + + # update the status bar variable + update_download_section(uzbl) + +# plugin init hook +def init(uzbl): + connect_dict(uzbl, { + 'DOWNLOAD_STARTED': download_started, + 'DOWNLOAD_PROGRESS': download_progress, + 'DOWNLOAD_COMPLETE': download_complete, + }) diff --git a/examples/data/scripts/auth.py b/examples/data/scripts/auth.py index 4feb90b..9c1b4fc 100755 --- a/examples/data/scripts/auth.py +++ b/examples/data/scripts/auth.py @@ -4,50 +4,50 @@ import gtk import sys def responseToDialog(entry, dialog, response): - dialog.response(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)) + 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 = 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) + login.connect("activate", responseToDialog, dialog, gtk.RESPONSE_OK) + password.connect("activate", responseToDialog, dialog, gtk.RESPONSE_OK) - hbox = gtk.HBox(); + hbox = gtk.HBox(); - vbox_entries = gtk.VBox(); - vbox_labels = gtk.VBox(); + 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_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) + 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.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() + dialog.vbox.pack_start(hbox) + dialog.show_all() + rv = dialog.run() - output = login.get_text() + "\n" + password.get_text() - dialog.destroy() - return rv, output + 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) + 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/download.sh b/examples/data/scripts/download.sh index f6d34e9..df7a571 100755 --- a/examples/data/scripts/download.sh +++ b/examples/data/scripts/download.sh @@ -1,22 +1,26 @@ #!/bin/sh -# just an example of how you could handle your downloads -# try some pattern matching on the uri to determine what we should do +# +# uzbl's example configuration sets this script up as its download_handler. +# when uzbl starts a download it runs this script. +# if the script prints a file path to stdout, uzbl will save the download to +# that path. +# if nothing is printed to stdout, the download will be cancelled. -# Some sites block the default wget --user-agent.. -GET="wget --user-agent=Firefox --content-disposition --load-cookies=$XDG_DATA_HOME/uzbl/cookies.txt" +shift 7 +. $UZBL_UTIL_DIR/uzbl-dir.sh -dest="$HOME" -url="$8" +# the URL that is being downloaded +uri=$1 -http_proxy="$9" -export http_proxy +# a filename suggested by the server or based on the URL +suggested_filename=${2:-$(echo "$uri" | sed 's/\W/-/g')} -test "x$url" = "x" && { echo "you must supply a url! ($url)"; exit 1; } +# the mimetype of the file being downloaded +content_type=$3 -# only changes the dir for the $get sub process -if echo "$url" | grep -E '.*\.torrent' >/dev/null; -then - ( cd "$dest"; $GET "$url") -else - ( cd "$dest"; $GET "$url") -fi +# the size of the downloaded file in bytes. this is not always accurate, since +# the server might not have sent a size with its response headers. +total_size=$4 + +# just save the file to the default directory with the suggested name +echo $UZBL_DOWNLOAD_DIR/$suggested_filename diff --git a/examples/data/scripts/follow.js b/examples/data/scripts/follow.js index a42447c..d995696 100644 --- a/examples/data/scripts/follow.js +++ b/examples/data/scripts/follow.js @@ -119,31 +119,37 @@ function generateHint(el, label) { // hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px,-5px)'; return hint; } -//Here we choose what to do with an element if we -//want to "follow" it. On form elements we "select" -//or pass the focus, on links we try to perform a click, -//but at least set the href of the link. (needs some improvements) + +// Here we choose what to do with an element that the user has selected. +// Form elements get selected and/or focussed, and links and buttons are +// clicked. This function returns "XXXRESET_MODEXXX" to indicate that uzbl +// should be reset to command mode with an empty keycmd, or +// "XXX_EMIT_FORM_ACTIVEXXX" to indicate that uzbl should be set to insert mode. function clickElem(item) { removeAllHints(); if (item) { var name = item.tagName; - if (name == 'A') { + if (name == 'BUTTON') { item.click(); - window.location = item.href; + return "XXXRESET_MODEXXX"; } else if (name == 'INPUT') { - var type = item.getAttribute('type').toUpperCase(); - if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') { + var type = item.type.toUpperCase(); + if (type == 'TEXT' || type == 'SEARCH' || type == 'PASSWORD') { item.focus(); item.select(); + return "XXXEMIT_FORM_ACTIVEXXX"; } else { item.click(); + return "XXXRESET_MODEXXX"; } } else if (name == 'TEXTAREA' || name == 'SELECT') { item.focus(); item.select(); + return "XXXEMIT_FORM_ACTIVEXXX"; } else { item.click(); window.location = item.href; + return "XXXRESET_MODEXXX"; } } } @@ -166,7 +172,7 @@ function addFormElems() { for (var f = 0; f < forms.length; f++) { for (var e = 0; e < forms[f].elements.length; e++) { var el = forms[f].elements[e]; - if (el && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) { + if (el && ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) { res[0].push(el); } } @@ -193,38 +199,38 @@ function reDrawHints(elems, chars) { // pass: number of keys // returns: key length function labelLength(n) { - var oldn = n; - var keylen = 0; - if(n < 2) { - return 1; - } - n -= 1; // our highest key will be n-1 - while(n) { - keylen += 1; - n = Math.floor(n / charset.length); - } - return keylen; + var oldn = n; + var keylen = 0; + if(n < 2) { + return 1; + } + n -= 1; // our highest key will be n-1 + while(n) { + keylen += 1; + n = Math.floor(n / charset.length); + } + return keylen; } // pass: number // returns: label function intToLabel(n) { - var label = ''; - do { - label = charset.charAt(n % charset.length) + label; - n = Math.floor(n / charset.length); - } while(n); - return label; + var label = ''; + do { + label = charset.charAt(n % charset.length) + label; + n = Math.floor(n / charset.length); + } while(n); + return label; } // pass: label // returns: number function labelToInt(label) { - var n = 0; - var i; - for(i = 0; i < label.length; ++i) { - n *= charset.length; - n += charset.indexOf(label[i]); - } - return n; + var n = 0; + var i; + for(i = 0; i < label.length; ++i) { + n *= charset.length; + n += charset.indexOf(label[i]); + } + return n; } //Put it all together function followLinks(follow) { @@ -242,7 +248,7 @@ function followLinks(follow) { var oldDiv = doc.getElementById(uzbldivid); var leftover = [[], []]; if (s.length == len && linknr < elems[0].length && linknr >= 0) { - clickElem(elems[0][linknr]); + return clickElem(elems[0][linknr]); } else { for (var j = 0; j < elems[0].length; j++) { var b = true; diff --git a/examples/data/scripts/follow.sh b/examples/data/scripts/follow.sh new file mode 100755 index 0000000..2d666a2 --- /dev/null +++ b/examples/data/scripts/follow.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# This script is just a wrapper around follow.js that lets us change uzbl's mode +# after a link is selected. + +shift 7 + +# if socat is installed then we can change Uzbl's input mode once a link is +# selected; otherwise we just select a link. +if ! which socat >/dev/null 2>&1; then + echo 'script @scripts_dir/follow.js "@{follow_hint_keys} '$1'"' > "$UZBL_FIFO" + exit +fi + +result=$(echo 'script @scripts_dir/follow.js "@{follow_hint_keys} '$1'"' | socat - unix-connect:"$UZBL_SOCKET") +case $result in + *XXXEMIT_FORM_ACTIVEXXX*) + # a form element was selected + echo 'event FORM_ACTIVE' > "$UZBL_FIFO" ;; + *XXXRESET_MODEXXX*) + # a link was selected, reset uzbl's input mode + echo 'set mode=' > "$UZBL_FIFO" ;; +esac diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 69eca17..6e04573 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -2,12 +2,12 @@ # # Enhanced html form (eg for logins) filler (and manager) for uzbl. # -# uses settings files like: $keydir/<domain> +# uses settings files like: $UZBL_FORMS_DIR/<domain> # files contain lines like: !profile=<profile_name> # <fieldname>(fieldtype): <value> -# profile_name should be replaced with a name that will tell sth about that +# profile_name should be replaced with a name that will tell sth about that # profile -# fieldtype can be checkbox, text or password, textarea - only for information +# fieldtype can be checkbox, text or password, textarea - only for information # pupropse (auto-generated) - don't change that # # Texteares: for textareas edited text can be now splitted into more lines. @@ -37,62 +37,32 @@ # something else (or empty): if file not available: new, otherwise load. # -# config dmenu colors and prompt -NB="#0f0f0f" -NF="#4e7093" -SB="#003d7c" -SF="#3a9bff" +DMENU_ARGS="-i" +DMENU_SCHEMA="formfiller" +DMENU_LINES="3" +DMENU_PROMPT="Choose profile" +DMENU_OPTIONS="vertical resize" -if [ "`dmenu --help 2>&1| grep lines`x" != "x" ] -then - LINES=" -l 3 " -else - LINES="" -fi +. $UZBL_UTIL_DIR/dmenu.sh +. $UZBL_UTIL_DIR/editor.sh +. $UZBL_UTIL_DIR/uzbl-dir.sh -PROMPT="Choose profile" +RAND=$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -c 1-5) MODELINE="> vim:ft=formfiller" -keydir=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/dforms - -[ -d "`dirname $keydir`" ] || exit 1 -[ -d "$keydir" ] || mkdir "$keydir" +[ -d "$(dirname $UZBL_FORMS_DIR)" ] || exit 1 +[ -d $UZBL_FORMS_DIR ] || mkdir $UZBL_FORMS_DIR || exit 1 -editor="${VISUAL}" -if [ -z "${editor}" ]; then - if [ -z "${EDITOR}" ]; then - editor='xterm -e vim' - else - editor="xterm -e ${EDITOR}" - fi -fi +shift 7 -config=$1; -shift -pid=$1; -shift -xid=$1; -shift -fifo=$1; -shift -socket=$1; -shift -url=$1; -shift -title=$1; -shift action=$1 -[ -d $keydir ] || mkdir $keydir || exit 1 - -domain=$(echo $url | sed 's/\(http\|https\):\/\/\([^\/]\+\)\/.*/\2/') +domain=$(echo $UZBL_URI | sed 's/\(http\|https\):\/\/\([^\/]\+\)\/.*/\2/') -if [ "$action" != 'edit' -a "$action" != 'new' -a "$action" != 'load' -a "$action" != 'add' -a "$action" != 'once' ] -then +if [ "$action" != 'edit' -a "$action" != 'new' -a "$action" != 'load' -a "$action" != 'add' -a "$action" != 'once' ]; then action="new" - [ -e "$keydir/$domain" ] && action="load" -elif [ "$action" = 'edit' ] && [ ! -e "$keydir/$domain" ] -then + [ -e "$UZBL_FORMS_DIR/$domain" ] && action="load" +elif [ "$action" = 'edit' ] && [ ! -e "$UZBL_FORMS_DIR/$domain" ]; then action="new" fi @@ -152,41 +122,38 @@ insertFunction="function insert(fname, ftype, fvalue, fchecked) { \ } \ }; " -if [ "$action" = 'load' ] -then - [ -e $keydir/$domain ] || exit 2 - if [ `cat $keydir/$domain|grep "!profile"|wc -l` -gt 1 ] - then - menu=`cat $keydir/$domain| \ - sed -n 's/^!profile=\([^[:blank:]]\+\)/\1/p'` - option=`echo -e -n "$menu"| dmenu ${LINES} -nb "${NB}" -nf "${NF}" -sb "${SB}" -sf "${SF}" -p "${PROMPT}"` +if [ "$action" = 'load' ]; then + [ -e $UZBL_FORMS_DIR/$domain ] || exit 2 + if [ $(cat $UZBL_FORMS_DIR/$domain | grep "!profile" | wc -l) -gt 1 ]; then + menu=$(cat $UZBL_FORMS_DIR/$domain | \ + sed -n 's/^!profile=\([^[:blank:]]\+\)/\1/p') + option=$(printf "$menu" | $DMENU) fi # Remove comments sed '/^>/d' -i $tmpfile - sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $keydir/$domain - fields=`cat $keydir/$domain | \ + sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $UZBL_FORMS_DIR/$domain + fields=$(cat $UZBL_FORMS_DIR/$domain | \ sed -n "/^!profile=${option}/,/^!profile=/p" | \ sed '/^!profile=/d' | \ sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \ sed 's/^\(.\+\)$/<{br}>\1/' | \ tr -d '\n' | \ - sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g'` + sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g') printf '%s\n' "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ - sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo + sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' | socat - unix-connect:$UZBL_SOCKET printf '%s\n' "${fields}" | \ sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ - sed -e 's/@/\\@/g' > $fifo -elif [ "$action" = "once" ] -then - tmpfile=`mktemp` + sed -e 's/@/\\@/g' | socat - unix-connect:$UZBL_SOCKET +elif [ "$action" = "once" ]; then + tmpfile=$(mktemp) printf 'js %s dump(); \n' "$dumpFunction" | \ - socat - unix-connect:$socket | \ + socat - unix-connect:$UZBL_SOCKET | \ sed -n '/^[^(]\+([^)]\+):/p' > $tmpfile echo "$MODELINE" >> $tmpfile - ${editor} $tmpfile + $UZBL_EDITOR $tmpfile [ -e $tmpfile ] || exit 2 @@ -194,23 +161,22 @@ then sed '/^>/d' -i $tmpfile sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile - fields=`cat $tmpfile | \ + fields=$(cat $tmpfile | \ sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \ sed 's/^\(.\+\)$/<{br}>\1/' | \ tr -d '\n' | \ - sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g'` + sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g') printf '%s\n' "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ - sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo + sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' | socat - unix-connect:$UZBL_SOCKET printf '%s\n' "${fields}" | \ sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ - sed -e 's/@/\\@/g' > $fifo + sed -e 's/@/\\@/g' | socat - unix-connect:$UZBL_SOCKET rm -f $tmpfile else - if [ "$action" = 'new' -o "$action" = 'add' ] - then - [ "$action" = 'new' ] && echo "$MODELINE" > $keydir/$domain - echo "!profile=NAME_THIS_PROFILE$RANDOM" >> $keydir/$domain + if [ "$action" = 'new' -o "$action" = 'add' ]; then + [ "$action" = 'new' ] && echo "$MODELINE" > $UZBL_FORMS_DIR/$domain + echo "!profile=NAME_THIS_PROFILE$RAND" >> $UZBL_FORMS_DIR/$domain # # 2. and 3. line (tr -d and sed) are because, on gmail login for example, # <input > tag is splited into lines @@ -228,11 +194,11 @@ else # passwd(password): # printf 'js %s dump(); \n' "$dumpFunction" | \ - socat - unix-connect:$socket | \ - sed -n '/^[^(]\+([^)]\+):/p' >> $keydir/$domain + socat - unix-connect:$UZBL_SOCKET | \ + sed -n '/^[^(]\+([^)]\+):/p' >> $UZBL_FORMS_DIR/$domain fi - [ -e "$keydir/$domain" ] || exit 3 #this should never happen, but you never know. - $editor "$keydir/$domain" #TODO: if user aborts save in editor, the file is already overwritten + [ -e "$UZBL_FORMS_DIR/$domain" ] || exit 3 #this should never happen, but you never know. + $UZBL_EDITOR "$UZBL_FORMS_DIR/$domain" #TODO: if user aborts save in editor, the file is already overwritten fi # vim:fileencoding=utf-8:sw=4 diff --git a/examples/data/scripts/go_input.js b/examples/data/scripts/go_input.js new file mode 100644 index 0000000..557671f --- /dev/null +++ b/examples/data/scripts/go_input.js @@ -0,0 +1,27 @@ +var elements = document.querySelectorAll("textarea, input" + [ + ":not([type='button'])", + ":not([type='checkbox'])", + ":not([type='hidden'])", + ":not([type='image'])", + ":not([type='radio'])", + ":not([type='reset'])", + ":not([type='submit'])"].join("")); +function gi() { + if (elements) { + var el, i = 0; + while((el = elements[i++])) { + var style=getComputedStyle(el, null); + if (style.display !== 'none' && style.visibility === 'visible') { + if (el.type === "file") { + el.click(); + } + else { + el.focus(); + } + return "XXXEMIT_FORM_ACTIVEXXX"; + } + } + } +} + +gi(); diff --git a/examples/data/scripts/go_input.sh b/examples/data/scripts/go_input.sh new file mode 100755 index 0000000..ace0e79 --- /dev/null +++ b/examples/data/scripts/go_input.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +case $(echo 'script @scripts_dir/go_input.js' | socat - unix-connect:"$UZBL_SOCKET") in + *XXXEMIT_FORM_ACTIVEXXX*) echo 'event FORM_ACTIVE' > "$UZBL_FIFO" ;; +esac diff --git a/examples/data/scripts/history.sh b/examples/data/scripts/history.sh index 7c83aa6..266d65d 100755 --- a/examples/data/scripts/history.sh +++ b/examples/data/scripts/history.sh @@ -1,5 +1,7 @@ #!/bin/sh -file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/history -[ -d `dirname $file` ] || exit 1 -echo `date +'%Y-%m-%d %H:%M:%S'`" $6 $7" >> $file +. $UZBL_UTIL_DIR/uzbl-dir.sh + +[ -w "$UZBL_HISTORY_FILE" ] || [ ! -a "$UZBL_HISTORY_FILE" ] || exit 1 + +echo $(date +'%Y-%m-%d %H:%M:%S')" $UZBL_URI $UZBL_TITLE" >> $UZBL_HISTORY_FILE diff --git a/examples/data/scripts/insert_bookmark.sh b/examples/data/scripts/insert_bookmark.sh index d0ec84e..f67e67a 100755 --- a/examples/data/scripts/insert_bookmark.sh +++ b/examples/data/scripts/insert_bookmark.sh @@ -1,18 +1,15 @@ #!/bin/sh -[ -d "${XDG_DATA_HOME:-$HOME/.local/share}/uzbl" ] || exit 1 -file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/bookmarks +. "$UZBL_UTIL_DIR"/uzbl-dir.sh -which zenity &>/dev/null || exit 2 -url=$6 -# replace tabs, they are pointless in titles and we want to use tabs as delimiter. -title=$(echo "$7" | sed 's/\t/ /') -entry=`zenity --entry --text="Add bookmark. add tags after the '\t', separated by spaces" --entry-text="$url $title\t"` +[ -d "$UZBL_DATA_DIR" ] || exit 1 +[ -w "$UZBL_BOOKMARKS_FILE" ] || [ ! -a "$UZBL_BOOKMARKS_FILE" ] || exit 1 + +which zenity >/dev/null 2>&1 || exit 2 + +tags=$(zenity --entry --text="Enter space-separated tags for bookmark $UZBL_URI:") exitstatus=$? -if [ $exitstatus -ne 0 ]; then exit $exitstatus; fi -url=`echo $entry | awk '{print $1}'` +[ $exitstatus -eq 0 ] || exit $exitstatus # TODO: check if already exists, if so, and tags are different: ask if you want to replace tags -echo "$entry" >/dev/null #for some reason we need this.. don't ask me why -echo -e "$entry" >> $file -true +echo "$UZBL_URI $tags" >> "$UZBL_BOOKMARKS_FILE" diff --git a/examples/data/scripts/instance-select-wmii.sh b/examples/data/scripts/instance-select-wmii.sh index fdd27e6..19d04e8 100755 --- a/examples/data/scripts/instance-select-wmii.sh +++ b/examples/data/scripts/instance-select-wmii.sh @@ -11,44 +11,38 @@ # See http://www.uzbl.org/wiki/wmii for more info # $1 must be one of 'list', 'next', 'prev' -COLORS=" -nb #303030 -nf khaki -sb #CCFFAA -sf #303030" +DMENU_SCHEME="wmii" -if dmenu --help 2>&1 | grep -q '\[-rs\] \[-ni\] \[-nl\] \[-xs\]' -then - DMENU="dmenu -i -xs -rs -l 10" # vertical patch -else - DMENU="dmenu -i" -fi +. $UZBL_UTIL_DIR/dmenu.sh -if [ "$1" == 'list' ] -then - list= - # get window id's of uzbl clients. we could also get the label in one shot but it's pretty tricky - for i in $(wmiir read /tag/sel/index | grep uzbl |cut -d ' ' -f2) - do - label=$(wmiir read /client/$i/label) - list="$list$i : $label\n" - done - window=$(echo -e "$list" | $DMENU $COLORS | cut -d ' ' -f1) - wmiir xwrite /tag/sel/ctl "select client $window" -elif [ "$1" == 'next' ] -then - current=$(wmiir read /client/sel/ctl | head -n 1) - # find the next uzbl window and focus it - next=$(wmiir read /tag/sel/index | grep -A 10000 " $current " | grep -m 1 uzbl | cut -d ' ' -f2) - if [ x"$next" != "x" ] - then - wmiir xwrite /tag/sel/ctl "select client $next" - fi -elif [ "$1" == 'prev' ] -then - current=$(wmiir read /client/sel/ctl | head -n 1) - prev=$(wmiir read /tag/sel/index | grep -B 10000 " $current " | tac | grep -m 1 uzbl | cut -d ' ' -f2) - if [ x"$prev" != "x" ] - then - wmiir xwrite /tag/sel/ctl "select client $prev" - fi -else - echo "\$1 not valid" >&2 - exit 2 -fi +case "$1" in + "list" ) + list= + # get window id's of uzbl clients. we could also get the label in one shot but it's pretty tricky + for i in $(wmiir read /tag/sel/index | grep uzbl |cut -d ' ' -f2); do + label=$(wmiir read /client/$i/label) + list="$list$i : $label\n" + done + window=$(printf "$list\n" | $DMENU | cut -d ' ' -f1) + wmiir xwrite /tag/sel/ctl "select client $window" + ;; + "next" ) + current=$(wmiir read /client/sel/ctl | head -n 1) + # find the next uzbl window and focus it + next=$(wmiir read /tag/sel/index | grep -A 10000 " $current " | grep -m 1 uzbl | cut -d ' ' -f2) + if [ -n "$next" ]; then + wmiir xwrite /tag/sel/ctl "select client $next" + fi + ;; + "prev" ) + current=$(wmiir read /client/sel/ctl | head -n 1) + prev=$(wmiir read /tag/sel/index | grep -B 10000 " $current " | tac | grep -m 1 uzbl | cut -d ' ' -f2) + if [ -n "$prev" ]; then + wmiir xwrite /tag/sel/ctl "select client $prev" + fi + ;; + * ) + echo "$1 not valid" >&2 + exit 2 + ;; +esac diff --git a/examples/data/scripts/load_url_from_bookmarks.sh b/examples/data/scripts/load_url_from_bookmarks.sh index 1e9f9e7..a5d9586 100755 --- a/examples/data/scripts/load_url_from_bookmarks.sh +++ b/examples/data/scripts/load_url_from_bookmarks.sh @@ -1,20 +1,22 @@ -#!/bin/bash +#!/bin/sh #NOTE: it's the job of the script that inserts bookmarks to make sure there are no dupes. -file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/bookmarks -[ -r "$file" ] || exit -COLORS=" -nb #303030 -nf khaki -sb #CCFFAA -sf #303030" -if dmenu --help 2>&1 | grep -q '\[-rs\] \[-ni\] \[-nl\] \[-xs\]' -then - DMENU="dmenu -i -xs -rs -l 10" # vertical patch - # show tags as well - goto=`$DMENU $COLORS < $file | awk '{print $1}'` +DMENU_SCHEME="bookmarks" +DMENU_OPTIONS="xmms vertical resize" + +. "$UZBL_UTIL_DIR"/dmenu.sh +. "$UZBL_UTIL_DIR"/uzbl-dir.sh + +[ -r "$UZBL_BOOKMARKS_FILE" ] || exit 1 + +if [ -z "$DMENU_HAS_VERTICAL" ]; then + # because they are all after each other, just show the url, not their tags. + goto=$(awk '{print $1}' "$UZBL_BOOKMARKS_FILE" | $DMENU) else - DMENU="dmenu -i" - # because they are all after each other, just show the url, not their tags. - goto=`awk '{print $1}' $file | $DMENU $COLORS` + # show tags as well + goto=$($DMENU < "$UZBL_BOOKMARKS_FILE" | awk '{print $1}') fi -#[ -n "$goto" ] && echo "uri $goto" > $4 -[ -n "$goto" ] && echo "uri $goto" | socat - unix-connect:$5 +[ -n "$goto" ] && echo "uri $goto" > "$UZBL_FIFO" +#[ -n "$goto" ] && echo "uri $goto" | socat - unix-connect:"$UZBL_SOCKET" diff --git a/examples/data/scripts/load_url_from_history.sh b/examples/data/scripts/load_url_from_history.sh index 62e02ac..59ad492 100755 --- a/examples/data/scripts/load_url_from_history.sh +++ b/examples/data/scripts/load_url_from_history.sh @@ -1,24 +1,23 @@ #!/bin/sh -history_file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/history -[ -r "$history_file" ] || exit 1 +DMENU_SCHEME="history" +DMENU_OPTIONS="xmms vertical resize" + +. "$UZBL_UTIL_DIR"/dmenu.sh +. "$UZBL_UTIL_DIR"/uzbl-dir.sh + +[ -r "$UZBL_HISTORY_FILE" ] || exit 1 # choose from all entries, sorted and uniqued -# goto=`awk '{print $3}' $history_file | sort -u | dmenu -i` -COLORS=" -nb #303030 -nf khaki -sb #CCFFAA -sf #303030" -if dmenu --help 2>&1 | grep -q '\[-rs\] \[-ni\] \[-nl\] \[-xs\]'; -then - DMENU="dmenu -i -xs -rs -l 10" # vertical patch - # choose an item in reverse order, showing also the date and page titles - # pick the last field from the first 3 fields. this way you can pick a url (prefixed with date & time) or type just a new url. - goto=`tac $history_file | $DMENU $COLORS | cut -d ' ' -f -3 | awk '{print $NF}'` +# goto=$(awk '{print $3}' $history_file | sort -u | dmenu -i) +if [ -z "$DMENU_HAS_VERTICAL" ]; then + current=$(tail -n 1 "$UZBL_HISTORY_FILE" | awk '{print $3}'); + goto=$( (echo $current; awk '{print $3}' "$UZBL_HISTORY_FILE" | grep -v "^$current\$" | sort -u) | $DMENU) else - DMENU="dmenu -i" - # choose from all entries (no date or title), the first one being current url, and after that all others, sorted and uniqued, in ascending order - current=`tail -n 1 $history_file | awk '{print $3}'`; - goto=`(echo $current; awk '{print $3}' $history_file | grep -v "^$current\$" \ - | sort -u) | $DMENU $COLORS` + # choose an item in reverse order, showing also the date and page titles + # pick the last field from the first 3 fields. this way you can pick a url (prefixed with date & time) or type just a new url. + goto=$(tac "$UZBL_HISTORY_FILE" | $DMENU | cut -d ' ' -f -3 | awk '{print $NF}') fi -[ -n "$goto" ] && echo "uri $goto" > $4 -#[ -n "$goto" ] && echo "uri $goto" | socat - unix-connect:$5 +[ -n "$goto" ] && echo "uri $goto" > "$UZBL_FIFO" +#[ -n "$goto" ] && echo "uri $goto" | socat - unix-connect:"$UZBL_SOCKET" diff --git a/examples/data/scripts/session.sh b/examples/data/scripts/session.sh index 1059b5e..36e0c19 100755 --- a/examples/data/scripts/session.sh +++ b/examples/data/scripts/session.sh @@ -1,62 +1,84 @@ #!/bin/sh +# +# Very simple session manager for uzbl-browser. +# To use, add a line like 'bind quit = spawn @scripts_dir/session.sh' to your +# config. This binding will exit every instance of uzbl and store the URLs they +# had open in $UZBL_SESSION_FILE. +# +# When a session file exists this script can be run with no arguments (or the +# argument "launch") to start an instance of uzbl-browser for every stored url. +# +# If no session file exists (or if called with "endsession" as the first +# argument), this script looks for instances of uzbl that have fifos in +# $UZBL_FIFO_DIR and instructs each of them to store its current url in +# $UZBL_SESSION_FILE and terminate. +# +# "endinstance" is used internally and doesn't need to be called manually. -# Very simple session manager for uzbl-browser. When called with "endsession" as the -# argument, it'll backup $sessionfile, look for fifos in $fifodir and -# instruct each of them to store their current url in $sessionfile and -# terminate themselves. Run with "launch" as the argument and an instance of -# uzbl-browser will be launched for each stored url. "endinstance" is used internally -# and doesn't need to be called manually at any point. -# Add a line like 'bind quit = /path/to/session.sh endsession' to your config - -[ -d ${XDG_DATA_HOME:-$HOME/.local/share}/uzbl ] || exit 1 -scriptfile=$0 # this script -sessionfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/browser-session # the file in which the "session" (i.e. urls) are stored -configfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/config # uzbl configuration file -UZBL="uzbl-browser -c $configfile" # add custom flags and whatever here. - -fifodir=/tmp # remember to change this if you instructed uzbl to put its fifos elsewhere -thisfifo="$4" -act="$8" -url="$6" - -if [ "$act." = "." ]; then - act="$1" +if [ -z "$UZBL_UTIL_DIR" ]; then + # we're being run standalone, we have to figure out where $UZBL_UTIL_DIR is + # using the same logic as uzbl-browser does. + UZBL_UTIL_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/scripts/util + if ! [ -d "$UZBL_UTIL_DIR" ]; then + PREFIX=$(grep '^PREFIX' "$(which uzbl-browser)" | sed 's/.*=//') + UZBL_UTIL_DIR=$PREFIX/share/uzbl/examples/data/scripts/util + fi +fi + +. "$UZBL_UTIL_DIR"/uzbl-dir.sh +[ -d "$UZBL_DATA_DIR" ] || exit 1 + +UZBL="uzbl-browser -c $UZBL_CONFIG_FILE" # add custom flags and whatever here. + +if [ $# -gt 1 ]; then + # this script is being run from uzbl, rather than standalone + # discard the uzbl arguments + shift 7 fi +scriptfile=$(readlink -f $0) # this script +act="$1" + +if [ -z "$act" ]; then + [ -f "$UZBL_SESSION_FILE" ] && act="launch" || act="endsession" +fi case $act in - "launch" ) - urls=`cat $sessionfile` - if [ "$urls." = "." ]; then - $UZBL - else - for url in $urls; do - $UZBL --uri "$url" & - done - fi - exit 0 - ;; + "launch" ) + urls=$(cat "$UZBL_SESSION_FILE") + if [ -z "$urls" ]; then + $UZBL + else + for url in $urls; do + $UZBL --uri "$url" & + done + mv "$UZBL_SESSION_FILE" "$UZBL_SESSION_FILE~" + fi + ;; - "endinstance" ) - if [ "$url" != "(null)" ]; then - echo "$url" >> $sessionfile; - fi - echo "exit" > "$thisfifo" - ;; - - "endsession" ) - mv "$sessionfile" "$sessionfile~" - for fifo in $fifodir/uzbl_fifo_*; do - if [ "$fifo" != "$thisfifo" ]; then - echo "spawn $scriptfile endinstance" > "$fifo" - fi - done - echo "spawn $scriptfile endinstance" > "$thisfifo" - ;; - - * ) echo "session manager: bad action" - echo "Usage: $scriptfile [COMMAND] where commands are:" - echo " launch - Restore a saved session or start a new one" - echo " endsession - Quit the running session. Must be called from uzbl" - ;; + "endinstance" ) + if [ -z "$UZBL_FIFO" ]; then + echo "session manager: endinstance must be called from uzbl" + exit 1 + fi + [ "$UZBL_URI" != "(null)" ] && echo "$UZBL_URI" >> "$UZBL_SESSION_FILE" + echo exit > "$UZBL_FIFO" + ;; + + "endsession" ) + for fifo in "$UZBL_FIFO_DIR"/uzbl_fifo_*; do + if [ "$fifo" != "$UZBL_FIFO" ]; then + echo "spawn $scriptfile endinstance" > "$fifo" + fi + done + [ -z "$UZBL_FIFO" ] || echo "spawn $scriptfile endinstance" > "$UZBL_FIFO" + ;; + + * ) + echo "session manager: bad action" + echo "Usage: $scriptfile [COMMAND] where commands are:" + echo " launch - Restore a saved session or start a new one" + echo " endinstance - Quit the current instance. Must be called from uzbl" + echo " endsession - Quit the running session." + ;; esac diff --git a/examples/data/scripts/userscript.sh b/examples/data/scripts/userscript.sh new file mode 100755 index 0000000..fd95fdc --- /dev/null +++ b/examples/data/scripts/userscript.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +if [ $# = "3" ] +then + UZBL_FIFO=$1 + UZBL_URI=$2 + SCRIPT=$3 +else + SCRIPT=$8 +fi + +# Extract metadata chunk +META="`sed -ne '/^\s*\/\/\s*==UserScript==\s*$/,/^\s*\/\/\s*==\/UserScript==\s*$/p' "$SCRIPT"`" +SHOULD_RUN=false # Assume this script will not be included +# Loop over all include rules +for INCLUDE in `echo "$META" | grep "^\s*\/\/\s*@include"`; do + # Munge into grep pattern + INCLUDE="`echo "$INCLUDE" | sed -e 's/^\s*\/\/\s*@include\s*//' -e 's/\./\\\\./g' -e 's/\*/.*/g' -e 's/[\r\n]//g'`" + if echo "$UZBL_URI" | grep -x "$INCLUDE"; then + SHOULD_RUN=true + break + fi +done + +# Loop over all exclude rules +for EXCLUDE in `echo "$META" | grep "^\s*\/\/\s*@exclude"`; do + # Munge into grep pattern + EXCLUDE="`echo "$EXCLUDE" | sed -e 's/^\s*\/\/\s*@exclude\s*//' -e 's/\./\\\\./g' -e 's/\*/.*/g' -e 's/[\r\n]//g'`" + if echo "$UZBL_URI" | grep -x "$EXCLUDE"; then + SHOULD_RUN=false + break + fi +done + +# Run the script +if [ $SHOULD_RUN = true ]; then + echo "script '$SCRIPT'" > "$UZBL_FIFO" +fi diff --git a/examples/data/scripts/userscripts.sh b/examples/data/scripts/userscripts.sh new file mode 100755 index 0000000..4f76c90 --- /dev/null +++ b/examples/data/scripts/userscripts.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +scripts_dir="$XDG_DATA_HOME/uzbl/userscripts" + +for SCRIPT in $(grep -rlx "\s*//\s*==UserScript==\s*" "$scripts_dir") +do + $XDG_DATA_HOME/uzbl/scripts/userscript.sh "$UZBL_FIFO" "$UZBL_URI" "$SCRIPT" +done diff --git a/examples/data/scripts/util/dmenu.sh b/examples/data/scripts/util/dmenu.sh new file mode 100644 index 0000000..da61cae --- /dev/null +++ b/examples/data/scripts/util/dmenu.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# dmenu setup + +case "$DMENU_SCHEME" in + # wmii + "wmii" ) + NB="#303030" + NF="khaki" + SB="#ccffaa" + SF="#303030" + ;; + # Formfiller + "formfiller" ) + NB="#0f0f0f" + NF="#4e7093" + SB="#003d7c" + SF="#3a9bff" + ;; + # Bookmarks + "bookmarks" ) + NB="#303030" + NF="khaki" + SB="#ccffaa" + SF="#303030" + ;; + # History + "history" ) + NB="#303030" + NF="khaki" + SB="#ccffaa" + SF="#303030" + ;; + # Default + * ) + NB="#303030" + NF="khaki" + SB="#ccffaa" + SF="#303030" + ;; +esac + +DMENU_COLORS="-nb $NB -nf $NF -sb $SB -sf $SF" + +# Default arguments +if [ -z "$DMENU_ARGS" ]; then + DMENU_ARGS="-i" +fi + +# Set the font if wanted +if [ -n "$DMENU_FONT" ]; then + DMENU_ARGS="$DMENU_ARGS -fn $DMENU_FONT" +fi + +# Set the prompt if wanted +if [ -n "$DMENU_PROMPT" ]; then + DMENU_ARGS="$DMENU_ARGS -p $DMENU_PROMPT" +fi + +# Detect the xmms patch +if dmenu --help 2>&1 | grep -q '\[-xs\]'; then + DMENU_XMMS_ARGS="-xs" + DMENU_HAS_XMMS=1 + + if echo $DMENU_OPTIONS | grep -q -w 'xmms'; then + DMENU_ARGS="$DMENU_ARGS $DMENU_XMMS_ARGS" + fi +fi + +# Detect the vertical patch +if dmenu --help 2>&1 | grep -q '\[-l <\?lines>\?\]'; then + # Default to 10 lines + if [ -z "$DMENU_LINES" ]; then + DMENU_LINES=10 + fi + + DMENU_VERTICAL_ARGS="-l $DMENU_LINES" + DMENU_HAS_VERTICAL=1 + + # Detect the resize patch + if dmenu --help 2>&1 | grep -q '\[-rs\]'; then + DMENU_RESIZE_ARGS="-rs" + DMENU_HAS_RESIZE=1 + fi + + if echo $DMENU_OPTIONS | grep -q -w 'vertical'; then + DMENU_ARGS="$DMENU_ARGS $DMENU_VERTICAL_ARGS" + + if echo $DMENU_OPTIONS | grep -q -w 'resize'; then + DMENU_ARGS="$DMENU_ARGS $DMENU_RESIZE_ARGS" + fi + fi +fi + +# Detect placement patch +if dmenu --help 2>&1 | grep -q '\[-x <xoffset>\]'; then + DMENU_PLACE_X="-x" + DMENU_PLACE_Y="-y" + DMENU_PLACE_WIDTH="-w" + DMENU_PLACE_HEIGHT="-h" + DMENU_HAS_PLACEMENT=1 +fi + +DMENU="dmenu $DMENU_ARGS $DMENU_COLORS" diff --git a/examples/data/scripts/util/editor.sh b/examples/data/scripts/util/editor.sh new file mode 100644 index 0000000..1969769 --- /dev/null +++ b/examples/data/scripts/util/editor.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Editor selection + +if [ -z "$VTERM" ]; then + VTERM="xterm" +fi + +UZBL_EDITOR="$VISUAL" +if [ -z "$UZBL_EDITOR" ]; then + if [ -z "$EDITOR" ]; then + UZBL_EDITOR="$VTERM -e vim" + else + UZBL_EDITOR="$VTERM -e $EDITOR" + fi +fi diff --git a/examples/data/scripts/util/uzbl-dir.sh b/examples/data/scripts/util/uzbl-dir.sh new file mode 100644 index 0000000..bb56954 --- /dev/null +++ b/examples/data/scripts/util/uzbl-dir.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Common directories and files used in scripts + +# Common things first +UZBL_DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl +UZBL_CONFIG_DIR=${XDG_CONFIG_DIR:-$HOME/.config}/uzbl +UZBL_FIFO_DIR=/tmp +UZBL_SOCKET_DIR=/tmp + +# Directories +UZBL_DOWNLOAD_DIR=${XDG_DOWNLOAD_DIR:-$HOME} +UZBL_FORMS_DIR=$UZBL_DATA_DIR/dforms + +# Data files +UZBL_CONFIG_FILE=$UZBL_CONFIG_DIR/config +UZBL_COOKIE_FILE=$UZBL_DATA_DIR/cookies.txt +UZBL_BOOKMARKS_FILE=$UZBL_DATA_DIR/bookmarks +UZBL_HISTORY_FILE=$UZBL_DATA_DIR/history +UZBL_SESSION_FILE=$UZBL_DATA_DIR/browser-session diff --git a/examples/data/scripts/util/uzbl-window.sh b/examples/data/scripts/util/uzbl-window.sh new file mode 100644 index 0000000..a7e92eb --- /dev/null +++ b/examples/data/scripts/util/uzbl-window.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# uzbl window detection + +UZBL_WIN_POS=$(xwininfo -id $UZBL_XID | \ + sed -ne 's/Corners:[ ]*[+-]\([0-9]*\)[+-]\([0-9]*\).*$/\1 \2/p') +UZBL_WIN_SIZE=$(xwininfo -id $UZBL_XID | \ + sed -ne 's/-geometry[ ]*\([0-9]*\)x\([0-9]*\).*$/\1 \2/p') +UZBL_WIN_POS_X=$(echo $UZBL_WIN_POS | cut -d\ -f1) +UZBL_WIN_POS_Y=$(echo $UZBL_WIN_POS | cut -d\ -f2) +UZBL_WIN_WIDTH=$(echo $UZBL_WIN_SIZE | cut -d\ -f1) +UZBL_WIN_HEIGHT=$(echo $UZBL_WIN_SIZE | cut -d\ -f2) diff --git a/examples/data/scripts/uzbl-cookie-daemon b/examples/data/scripts/uzbl-cookie-daemon index ed88de4..0b9bef9 100755 --- a/examples/data/scripts/uzbl-cookie-daemon +++ b/examples/data/scripts/uzbl-cookie-daemon @@ -436,18 +436,26 @@ class CookieMonster: daemon_timeout = config['daemon_timeout'] echo("listening on %r" % config['cookie_socket']) + connections = [] + while self._running: # This line tells the socket how many pending incoming connections # to enqueue at once. Raising this number may or may not increase # performance. self.server_socket.listen(1) - if bool(select.select([self.server_socket], [], [], 1)[0]): - client_socket, _ = self.server_socket.accept() - self.handle_request(client_socket) + r, w, x = select.select([self.server_socket]+connections, [], [], 1) + + for socket in r: + if self.server_socket == socket: + client_socket, _ = socket.accept() + connections.append(client_socket) + else: + if not self.handle_request(socket): + # connection was closed, forget about the client socket + connections.remove(socket) + self.last_request = time.time() - client_socket.close() - continue if daemon_timeout: # Checks if the daemon has been idling for too long. @@ -462,7 +470,7 @@ class CookieMonster: # Receive cookie request from client. data = client_socket.recv(8192) if not data: - return + return False # Cookie argument list in packet is null separated. argv = data.split("\0") @@ -471,17 +479,17 @@ class CookieMonster: # Catch the EXIT command sent to kill running daemons. if action == "EXIT": self._running = False - return + return False # Catch whitelist RELOAD command. elif action == "RELOAD": self.reload_whitelist() - return + return True # Return if command unknown. elif action not in ['GET', 'PUT']: error("unknown command %r." % argv) - return + return True # Determine whether or not to print cookie data to terminal. print_cookie = (config['verbose'] and not config['daemon_mode']) @@ -499,13 +507,14 @@ class CookieMonster: req = urllib2.Request(uri) if action == "GET": - self.jar.add_cookie_header(req) - if req.has_header('Cookie'): - cookie = req.get_header('Cookie') - client_socket.send(cookie) - if print_cookie: - print cookie - + self.jar._policy._now = self._now = int(time.time()) + cookies = self.jar._cookies_for_request(req) + attrs = self.jar._cookie_attrs(cookies) + if attrs: + cookie = "; ".join(attrs) + client_socket.send(cookie) + if print_cookie: + print cookie else: client_socket.send("\0") @@ -515,10 +524,13 @@ class CookieMonster: print cookie self.put_cookie(req, cookie) + client_socket.send("\0") if print_cookie: print + return True + def put_cookie(self, req, cookie=None): '''Put a cookie in the cookie jar.''' diff --git a/examples/data/scripts/uzbl-event-manager b/examples/data/scripts/uzbl-event-manager index ab13fbb..8ad3af7 100755 --- a/examples/data/scripts/uzbl-event-manager +++ b/examples/data/scripts/uzbl-event-manager @@ -825,7 +825,7 @@ if __name__ == "__main__": add('-n', '--no-daemon', dest='daemon_mode', action='store_false', default=True, - help='daemonize the process') + help='do not daemonize the process') add('-a', '--auto-close', dest='auto_close', action='store_true', default=False, diff --git a/examples/data/scripts/uzbl-tabbed b/examples/data/scripts/uzbl-tabbed index 5bf802a..0086c04 100755 --- a/examples/data/scripts/uzbl-tabbed +++ b/examples/data/scripts/uzbl-tabbed @@ -158,7 +158,7 @@ import atexit import types from gobject import io_add_watch, source_remove, timeout_add, IO_IN, IO_HUP -from signal import signal, SIGTERM, SIGINT, SIGCHLD +from signal import signal, SIGTERM, SIGINT from optparse import OptionParser, OptionGroup from traceback import print_exc @@ -471,8 +471,9 @@ class UzblInstance: elif var == "gtk_tab_pos": self.parent.update_gtk_tab_pos() elif var == "status_background": - col = gtk.gdk.color_parse(config['status_background']) - self.parent.ebox.modify_bg(gtk.STATE_NORMAL, col) + if config['status_background'].strip(): + col = gtk.gdk.color_parse(config['status_background']) + self.parent.ebox.modify_bg(gtk.STATE_NORMAL, col) elif var == "tab_titles" or var == "tab_indexes": for tab in self.parent.notebook: self.parent.tabs[tab].title_changed(True) @@ -536,7 +537,6 @@ class UzblTabbed: self._timers = {} self._buffer = "" self._killed = False - self._processes = [] # A list of the recently closed tabs self._closed = [] @@ -656,9 +656,6 @@ class UzblTabbed: # Catch keyboard interrupts signal(SIGINT, lambda signum, stack_frame: self.terminate(SIGINT)) - # Catch SIGCHLD - signal(SIGCHLD, lambda signum, stack_frame: self.join_children()) - try: gtk.main() @@ -678,15 +675,6 @@ class UzblTabbed: raise - - def join_children(self): - '''Find and remove zombie children processes.''' - - for p in self._processes: - if p.poll() is not None: - self._processes.remove(p) - - def terminate(self, termsig=None): '''Handle termination signals and exit safely and cleanly.''' @@ -847,6 +835,8 @@ class UzblTabbed: # Commands ( [] = optional, {} = required ) # new [uri] # open new tab and head to optional uri. + # newbg [uri] + # open a new tab in the background # close [tab-num] # close current tab or close via tab id. # next [n-tabs] @@ -875,6 +865,12 @@ class UzblTabbed: else: self.new_tab() + elif cmd[0] == "newbg": + if len(cmd) == 2: + self.new_tab(cmd[1], switch=False) + else: + self.new_tab(switch=False) + elif cmd[0] == "newfromclip": uri = subprocess.Popen(['xclip','-selection','clipboard','-o'],\ stdout=subprocess.PIPE).communicate()[0] @@ -1009,7 +1005,7 @@ class UzblTabbed: cmd = ['uzbl-browser', '-n', name, '-s', str(sid), '--connect-socket', self.socket_path, '--uri', uri] - self._processes += [subprocess.Popen(cmd)] # TODO: do i need close_fds=True ? + gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH) uzbl = UzblInstance(self, tab, name, uri, title, switch) SocketClient.instances_queue[name] = uzbl diff --git a/examples/uzbl-cookie-manager.c b/examples/uzbl-cookie-manager.c new file mode 100644 index 0000000..70addf3 --- /dev/null +++ b/examples/uzbl-cookie-manager.c @@ -0,0 +1,381 @@ +#define _POSIX_SOURCE + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/select.h> +#include <sys/unistd.h> + +#include <sys/stat.h> +#include <sys/file.h> +#include <fcntl.h> +#include <stdlib.h> + +#include <libsoup/soup-cookie.h> +#include <libsoup/soup-cookie-jar-text.h> +#include <libsoup/soup-uri.h> + +#include "../src/util.h" + +extern const XDG_Var XDG[]; + +int verbose = 0; + +#define SOCK_BACKLOG 10 +#define MAX_COOKIE_LENGTH 4096 + +char cookie_buffer[MAX_COOKIE_LENGTH]; + +int setup_socket(const char *cookied_socket_path) { + /* delete the cookie socket if it was left behind on a previous run */ + unlink(cookied_socket_path); + + int socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + + if(socket_fd < 0) { + fprintf(stderr, "socket failed (%s)\n", strerror(errno)); + return -1; + } + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, cookied_socket_path); + + if(bind(socket_fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) { + fprintf(stderr, "bind failed (%s)\n", strerror(errno)); + return -1; + } + + if(listen(socket_fd, SOCK_BACKLOG) < 0) { + fprintf(stderr, "listen failed (%s)\n", strerror(errno)); + return -1; + } + + return socket_fd; +} + +const char *whitelist_path = NULL; +GPtrArray *whitelisted_hosts = NULL; +time_t whitelist_update_time = 0; + +void whitelist_line_cb(const gchar* line, void *user_data) { + (void) user_data; + + gchar *norm_host; + + const gchar *p = line; + while(isspace(*p)) + p++; + + if(p[0] == '#' || !p[0]) /* ignore comments and blank lines */ + return; + + if(p[0] == '.') + norm_host = g_strdup(p); + else + norm_host = g_strconcat(".", p, NULL); + + g_ptr_array_add(whitelisted_hosts, g_strchomp(norm_host)); +} + +gboolean load_whitelist(const char *whitelist_path) { + if(!file_exists(whitelist_path)) + return FALSE; + + /* check if the whitelist file was updated */ + struct stat f; + if(stat(whitelist_path, &f) < 0) + return FALSE; + + if(whitelisted_hosts == NULL) + whitelisted_hosts = g_ptr_array_new(); + + if(f.st_mtime > whitelist_update_time) { + /* the file was updated, reload the whitelist */ + if(verbose) puts("reloading whitelist"); + while(whitelisted_hosts->len > 0) { + g_free(g_ptr_array_index(whitelisted_hosts, 0)); + g_ptr_array_remove_index_fast(whitelisted_hosts, 0); + } + for_each_line_in_file(whitelist_path, whitelist_line_cb, NULL); + whitelist_update_time = f.st_mtime; + } + + return TRUE; +} + +gboolean should_save_cookie(const char *host) { + if(!load_whitelist(whitelist_path)) + return TRUE; /* some error with the file, assume no whitelist */ + + /* we normalize the hostname so it has a . in front like the whitelist entries */ + gchar *test_host = (host[0] == '.') ? g_strdup(host) : g_strconcat(".", host, NULL); + int hl = strlen(test_host); + + /* test against each entry in the whitelist */ + gboolean result = FALSE; + guint i; + for(i = 0; i < whitelisted_hosts->len; i++) { + /* a match means the host ends with (or is equal to) the whitelist entry */ + const gchar *entry = g_ptr_array_index(whitelisted_hosts, i); + int el = strlen(entry); + result = (el <= hl) && !strcmp(test_host + (hl - el), entry); + + if(result) + break; + } + + g_free(test_host); + + return result; +} + +void handle_request(SoupCookieJar *j, const char *buff, int len, int fd) { + const char *command = buff; + + const char *scheme = command + strlen(command) + 1; + if((scheme - buff) > len) { + fprintf(stderr, "got malformed or partial request\n"); + return; + } + + const char *host = scheme + strlen(scheme) + 1; + if((host - buff) > len) { + fprintf(stderr, "got malformed or partial request\n"); + return; + } + + const char *path = host + strlen(host) + 1; + if((path - buff) > len) { + fprintf(stderr, "got malformed or partial request\n"); + return; + } + + /* glue the parts back together into a SoupURI */ + char *u = g_strconcat(scheme, "://", host, path, NULL); + if(verbose) printf("%s %s\n", command, u); + SoupURI *uri = soup_uri_new(u); + g_free(u); + + if(!strcmp(command, "GET")) { + char *result = soup_cookie_jar_get_cookies(j, uri, TRUE); + if(result) { + if(verbose) puts(result); + if(write(fd, result, strlen(result)+1) < 0) + fprintf(stderr, "write failed (%s)", strerror(errno)); + + g_free(result); + } else { + if(verbose) puts("-"); + if(write(fd, "", 1) < 0) + fprintf(stderr, "write failed (%s)", strerror(errno)); + } + } else if(!strcmp(command, "PUT")) { + const char *name_and_val = path + strlen(path) + 1; + if((name_and_val - buff) > len) { + fprintf(stderr, "got malformed or partial request\n"); + return; + } + + if(verbose) puts(name_and_val); + + if(should_save_cookie(host)) { + char *eql = strchr(name_and_val, '='); + eql[0] = 0; + + const char *name = name_and_val; + const char *value = eql + 1; + + SoupCookie *cookie = soup_cookie_new(name, value, host, path, SOUP_COOKIE_MAX_AGE_ONE_YEAR); + + soup_cookie_jar_add_cookie(j, cookie); + } else if(verbose) + puts("no, blacklisted."); + + if(write(fd, "", 1) < 0) + fprintf(stderr, "write failed (%s)", strerror(errno)); + } + + soup_uri_free(uri); +} + +void +wait_for_things_to_happen_and_then_do_things(SoupCookieJar* j, int cookie_socket) { + GArray *connections = g_array_new (FALSE, FALSE, sizeof (int)); + + while(1) { + unsigned int i; + int r; + fd_set fs; + + int maxfd = cookie_socket; + FD_ZERO(&fs); + FD_SET(maxfd, &fs); + + for(i = 0; i < connections->len; i++) { + int fd = g_array_index(connections, int, i); + if(fd > maxfd) maxfd = fd; + FD_SET(fd, &fs); + } + + r = select(maxfd+1, &fs, NULL, NULL, NULL); + if(r < 0) { + fprintf(stderr, "select failed (%s)\n", strerror(errno)); + continue; + } + + if(FD_ISSET(cookie_socket, &fs)) { + /* handle new connection */ + int fd = accept(cookie_socket, NULL, NULL); + g_array_append_val(connections, fd); + if(verbose) puts("got connection."); + } + + for(i = 0; i < connections->len; i++) { + /* handle activity on a connection */ + int fd = g_array_index(connections, int, i); + if(FD_ISSET(fd, &fs)) { + r = read(fd, cookie_buffer, MAX_COOKIE_LENGTH); + if(r < 0) { + fprintf(stderr, "read failed (%s)\n", strerror(errno)); + continue; + } else if(r == 0) { + if(verbose) puts("client hung up."); + g_array_remove_index(connections, i); + i--; /* other elements in the array are moved down to fill the gap */ + continue; + } + cookie_buffer[r] = 0; + + handle_request(j, cookie_buffer, r, fd); + } + } + } +} + +void usage(const char *progname) { + printf("%s [-s socket-path] [-f cookies.txt] [-w whitelist-file] [-n] [-v]\n", progname); + puts("\t-n\tdon't daemonise the process"); + puts("\t-v\tbe verbose"); +} + +void daemonise() { + int r = fork(); + + if(r < 0) { + fprintf(stderr, "fork failed (%s)", strerror(errno)); + exit(1); + } else if (r > 0) { + /* this is the parent, which has done its job */ + exit(0); + } + + if(setsid() < 0) { + fprintf(stderr, "setsid failed (%s)", strerror(errno)); + exit(1); + } +} + +const char *pid_file_path = NULL; +const char *cookied_socket_path = NULL; + +void cleanup_after_signal(int signal) { + (void) signal; + unlink(pid_file_path); + unlink(cookied_socket_path); + exit(0); +} + +int main(int argc, char *argv[]) { + int i; + + const char *cookies_txt_path = NULL; + gboolean foreground = FALSE; + + for(i = 1; i < argc && argv[i][0] == '-'; i++) { + switch(argv[i][1]) { + case 's': + cookied_socket_path = argv[++i]; + break; + case 'f': + cookies_txt_path = argv[++i]; + break; + case 'w': + whitelist_path = argv[++i]; + break; + case 'n': + foreground = TRUE; + break; + case 'v': + verbose = 1; + break; + default: + usage(argv[0]); + return 1; + } + } + + if(!foreground) + daemonise(); + + if(!cookies_txt_path) + cookies_txt_path = g_strconcat(get_xdg_var(XDG[1]), "/uzbl/cookies.txt", NULL); + + if(!cookied_socket_path) + cookied_socket_path = g_strconcat(get_xdg_var(XDG[2]), "/uzbl/cookie_daemon_socket", NULL); + + if(!whitelist_path) + whitelist_path = g_strconcat(get_xdg_var(XDG[0]), "/uzbl/cookie_whitelist", NULL); + + /* write out and lock the pid file. + * this ensures that only one uzbl-cookie-manager is running per-socket. + * (we should probably also lock the cookies.txt to prevent accidents...) */ + pid_file_path = g_strconcat(cookied_socket_path, ".pid", NULL); + int lockfd = open(pid_file_path, O_RDWR|O_CREAT, 0600); + if(lockfd < 0) { + fprintf(stderr, "couldn't open pid file %s (%s)\n", pid_file_path, strerror(errno)); + return 1; + } + + if(flock(lockfd, LOCK_EX|LOCK_NB) < 0) { + fprintf(stderr, "couldn't lock pid file %s (%s)\n", pid_file_path, strerror(errno)); + fprintf(stderr, "uzbl-cookie-manager is probably already running\n"); + return 1; + } + + gchar* pids = g_strdup_printf("%d\n", getpid()); + write(lockfd, pids, strlen(pids)); + g_free(pids); + + struct sigaction sa; + sa.sa_handler = cleanup_after_signal; + if(sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) { + fprintf(stderr, "sigaction failed (%s)\n", strerror(errno)); + return 1; + } + + if(!foreground) { + /* close STDIO */ + close(0); + close(1); + close(2); + } + + g_type_init(); + + SoupCookieJar *j = soup_cookie_jar_text_new(cookies_txt_path, FALSE); + + int cookie_socket = setup_socket(cookied_socket_path); + if(cookie_socket < 0) + return 1; + + wait_for_things_to_happen_and_then_do_things(j, cookie_socket); + + return 0; +} |