Archive for the 'Howtos' Category

Editing a PDF (sorta) on Ubuntu

Monday, March 31st, 2008

I have more than once had to fill in a paper form for people in America. Obviously, doing such things is a huge pain, because actual paper mail — with stamps — isn’t just so last century, it’s the century before that. You know, the one with robber barons and Lord Kelvin and workhouses. Put a stop to it. Email. Email, I tell you. It’s gonna catch on, just you wait.

Anyway, a fair few American corporations (and, wouldjabelieveit, the American government too) provide PDF versions of their forms so you can download them and print them out at your leisure. They’re still designed to be printed and written on, though — they’re not officially PDF “forms” in the sense that the PDF reader can give you textboxes and an FDF file to fill them in, they’re just PDF documents. Nonetheless, I find myself wanting to be able to open them up in OpenOffice.org and edit them.

Now, you can’t do that. (Well, you might be able to, but it’s hard.) All I actually need to do is overtype them. So what I do is:

  1. Convert the PDF to an EPS file with pdftops: pdftops -eps AmericanGovernmentForm.pdf (the pdftops program is in the poppler-utils package)
  2. Create a new empty document in OpenOffice.org and drag the new .eps file onto it, which will embed it as an image
  3. Use Insert | Frame to draw a text frame on top of the new image (set it to have no border; set the background colour to anything other than No Fill and transparency to 100%)
  4. Overtype onto the form in the text frame
  5. Export as PDF to get a PDF version with your text in
  6. Email it to the government

No stamps. Their evil must be stopped.

Overriding a single field in the Django admin, using newforms-admin

Friday, March 28th, 2008

Django has been gradually changing the way their automatically-created admin system works to use the newforms-admin code, which makes lots of cool new things possible. However, because newforms-admin is rather new (ha!), it’s not brilliantly documented. One of the things I wanted to do today was to make one field use a custom field-editing widget that I’d created, rather than Django’s default textbox, in the Django admin system. You do that like this.

In newforms-admin, you specify admin options for a model by creating an extra ModelAdmin class for it:

class Vehicle(models.Model):
  colour = models.CharField()
  name = models.CharField()

class VehicleAdmin(admin.ModelAdmin):
  search_fields = ["name", "colour"]

admin.site.register(Vehicle, VehicleAdmin)

Imagine that we wanted to build a custom widget to allow people to choose a colour by clicking on a colour swatch. To do this, you need to actually create your custom widget. So, in a file custom_widgets.py, you create your widget. The easiest way to do this is to subclass one of the existing widgets (TextInput is a good one here, because that’s a normal textbox, which is what gets used by default for CharFields) and then change its render method:

import django.newforms as forms
from string import Template
from django.utils.safestring import mark_safe

class ColourChooserWidget(forms.TextInput):
  def render(self, name, value, attrs=None):
    tpl = Template(u"""<h1>There would be a colour widget here, for value $colour</h1>""")
    return mark_safe(tpl.substitute(colour=value))

There are a few interesting wrinkles in there.

First, overriding render() changes the HTML that your widget prints when asked to display itself by the admin system. (I haven’t actually implemented the widget there; left as an exercise for the reader, that bit.)

Second, you need to call mark_safe() on the HTML you return, otherwise the admin will escape it.

Third, all input to mark_safe() must be Unicode, hence the u""" at the beginning of the string. The parameter value already is Unicode, but any strings you provide must also be explicitly Unicode strings; otherwise, mark_safe() fails silently — the string will be escaped.

Fourth, you don’t have to use string.Template, but it’s pretty convenient.

Once you’ve created your custom widget, you have to hook it up to your model. In our example, we need to change VehicleAdmin:

from custom_widgets import ColourChooserWidget

...

class VehicleAdmin(admin.ModelAdmin):
  search_fields = ["name", "colour"]
  def formfield_for_dbfield(self, db_field, **kwargs):
    if db_field.name == ‘colour’:
      kwargs['widget'] = ColourChooserWidget
    return super(ArticleOptions,self).formfield_for_dbfield(db_field,**kwargs)

The formfield_for_dbfield() function gets called for each of the fields in your model; for the one we care about (colour in this example), override widget in the kwargs and then carry on with the rest of the function, and that hooks it up.

That should be it; now, in the Django admin system, your colour field will use your ColourChooserWidget for editing.

Making the arrow keys work in VLC

Saturday, January 26th, 2008

One of my big flaws with the VLC media player is that you can’t use the arrow keys to skip backwards and forwards in a video. It turns out that this is because the left and right arrow keys are already configured to mean “navigate around a DVD menu” and you can’t configure a key twice. So, if you need to use VLC for something, and you want the arrow keys to work, go into the settings and set the DVD navigate options to something other than Left and Right. Then configure “jump forwards” and “jump backwards” to Right and Left, and then it’ll work. God knows why it has to be this way, but at least if you do that it works.

Negative numbers in the Google Chart API

Saturday, December 8th, 2007

Google’s new Chart API is a useful little thing that returns a PNG of a chart based on the URL you feed it, so you can create graphs like this:

a simple example line graph

by just specifying the <img> src attribute as http://chart.apis.google.com/chart?cht=lc&chs=200×125&chd=s:helloWorld&chxt=x,y&chxl=0:|0|10|20

However, as Marty Alchin rightfully complains about, it doesn’t handle negative numbers at all. Obviously Google, being the internet success story that it is, never has any numbers for anything that dip below zero, but not everyone’s so lucky. However, there are ways to handle negative numbers in the Google Chart API.

Sort of, anyway. This is a bodge. Hold your nose and dive in. I’m sure Google will forcibly inject clue into their charting engine at some point, but until then you can sorta-kinda get around the problem like this.

a simple line graph with negative numbers

For line graphs with negative numbers, you need to do two things. First, lie about the values on the y axis (so you display on the graph that the y axis goes between -10 and 10, in the above example, even though it actually goes between 0 and 20). You’ll obviously need to transform your numbers appropriately, so a data series [-10, -5, 0, 5, 10] should be fed to the graphing engine as [0, 5, 10, 15, 20]. The second thing to do is to draw a horizontal line at 0 on the y axis; if we had real negative number support then that’s where the x axis should be, and so the extra drawn line “stands in” for it. That’s easy enough to do, using the grid lines chg parameter; just make the grid only exist horizontally, and have the grid divide the graph into two 50% parts, with chg=0,50,1,0. The four parameters 0, 50, 1, 0 mean “don’t draw vertical grid lines (0)”, “draw a horizontal grid line every 50% of the y axis (50)”, and “make the line solid with no gaps (1,0)”.

It’s also possible for bar graphs, although it requires a small amount more ingenuity.

a bar graph with negative numbers

This graph has negatives, right? Well, how it’s done might be clearer if you see this graph:

a bar graph without negative numbers, with the parts 'below the line' coloured in

Yep, you just stack up the bars, and make the bottom bit of the stack be transparent (note that in the first graph we have chco=00000000,ff0000,0000ff, which specifies colours for each data series; the first item in there is 00000000, which is in format RRGGBBAA, meaning 0% red, 0% green, 0% blue, and 0% visible. Actually, it could have been ffffff00, or deaded00, or anything; all colours are the same when they have 0 opacity! The second graph is exactly the same, except now the transparent bars are shown in green so you can see how it’s done.

You’ll note that all these graphs still have the “real” x axis (the line at the bottom of the graph) still visible. This is because there’s no way to turn it off, which is unfortunate both for this fake-the-negatives approach and because you can’t do decent sparkline graphs if you have to display the axes.

Both of the tricks above are horrible fudges which only need to exist until the Google Chart people rediscover the minus sign on their keyboards, which I’m sure is already in their bugtracker somewhere. If it’s not, then here, Google, take some of these: - - - - - - - - - - - - - - - - - - - -. Hope you find them useful.

Showing source in Firefox

Wednesday, November 28th, 2007

Annoyed that Firefox pops up the source of a page in a separate window when you View Source? Annoyed that Epiphany shows the source of a page in Gedit? Help is at hand! Simply drag the following link to your bookmarklet bar:

Toggle source

Clicking it will replace the page you’re viewing with that page’s source, nicely syntax-highlighted, in the same browser tab. Clicking it again will go back to the page. That should keep Sean Middleditch happy and stop complaints about Epiphany using Gedit as an HTML viewer.

(Warning: I don’t know what happens if you do this on a page where you’ve just posted some data, like if you’ve just placed an Amazon order or something. Don’t do that.)

Wordpress through Subversion

Friday, November 23rd, 2007

I have to say, I’m liking Wordpress’s install-with-subversion documentation. This will make it a lot easier to upgrade…

(update, 11.05: well, theoretically…)

(update, 11.12: you have to remember to upgrade the database once you’ve installed)

In short, making your Wordpress installation be a Subversion checkout seems like a great idea to me. It works easily (the WP people have instructions on how to convert your existing WP installation into a checkout of Subversion), they’re careful about tagging each new release (so you can upgrade just when releases happen; you don’t have to track the unstable bleeding-edge of Wordpress), and when a new release happens, upgrading is as easy as svn switch http://svn.automattic.com/wordpress/tags/NEW_VERSION/ followed by visiting http://server/wordpress/wp-admin/upgrade.php, and upgrades don’t come much easier than that, if you’re a techie like me. Good work, Wordpress team.

A “presenter view” for OpenOffice presentations on Linux

Thursday, November 22nd, 2007

Earlier this week I was at @media Ajax, and it was great — more in an upcoming post. First, though, a quick bit of scripting I did. You see, it’s become apparent to me that I need a “presenter view” when I’m presenting; I have my laptop running dualscreen, so that the view I get on the laptop screen is not the view that appears on the projector. I need this, because I’m not Derek Featherstone; I can’t just see the slides and remember everything that I need to say. My slides are often just a single word, or a picture, and I can’t remember the thread of what I’m aiming at without notes.* So, I used to have notes on a bit of paper. The way I create presentations is:

  1. Write out, longhand, everything I intend to say, word-for-word, like it’s an essay or a playscript
  2. Go through my longhand script and identify all the slides I want to put in to bolster the points
  3. Spend some time with Flickr’s Creative Commons image search finding the images that I want for slides
  4. Put together a presentation
  5. Make notes for each slide expressing the bullet points of what I want to say, with notes to myself to remind me of any particular phrases I want to specifically remember
  6. Throw away the longhand script

The final step, after that lot, used to be “write out all of the notes in big writing on bits of A4 paper”. Then it occurred to me: why not use the presenter view to put my notes on? I used S5, Eric Meyer’s in-browser HTML/CSS/JavaScript based presentation system, for a long time to make my talks, and it has a presenter view. However, I got increasingly hacked off with it being slow and annoying, and so I resolved to use OpenOffice for presentations; it’s just easier. OpenOffice, though, doesn’t have a presenter view (there’s a spec for it, but it doesn’t yet exist). After a bit of casting about, I discovered that (a) OpenOffice will export presentations to PDF, and (b) Evince, the Gnome document viewer, has a presentation mode. So, thought I, I could use that instead. OpenOffice allows you to export the notes pages too into your PDF: if you’ve got three slides in your presentation*, each with notes, and you elect to include notes pages in the PDF export, then you get six pages in the resultant PDF — the first three are just the slides, and the second three are the slides at the top of the page with the notes below.
So the idea appeared in my head: why not open the PDF twice, put PDF-1 on page 1 and display it on the projector, and PDF-2 on page 4 (the first notes page) and display it on my screen? Then just get them to advance in lockstep — when I advance the projector PDF by one page, it advances the notes view on my laptop screen by one page as well — and lo, I have a presenter view.
Those of you with (a) modern versions of PowerPoint or (b) whatever the Mac presentation thing is called are doubtless laughing your heads off right now, and rightfully so. I can’t wait for the OpenOffice people to make this happen properly.

Anyway, to do the advancing, you need a little script. This little script uses dogtail to provide the presenter view, and I used it on Monday and it worked perfectly. Be warned: this is not a proper solution. It’s a crappy hack, and if it de-bones your cat and deletes everything on your hard drive, don’t come crying to me. Almost certainly requires Linux, requires dogtail (and accessibility mode turned on in Gnome (NFI whether KDE has the same features, but it probably does; if you use something other than Gnome or KDE then you’re on your own, you freak)), requires Python and PyGtk, requires Evince (which should be called “Document Viewer”), requires a PDF (that has been exported from OpenOffice.org Impress with notes page included) on the command line, requires a laptop that can do dualhead (mine seems to set up the two screens as One Big Screen, so I don’t know what happens if you have something that actually does it properly), probably requires the phase of the moon to be right as well. Enjoy.

Evince presenter view

import gtk, sys, random, shutil, os, time
from dogtail.tree import root

pdf = sys.argv[1]
# copy pdf to /tmp
pdfcopyname = “pdf.%s.pdf” % random.random()
pdfcopy = “/tmp/” + pdfcopyname
shutil.copy(pdf, pdfcopy)

# start two evinces
os.system(”evince –presentation –page-label=1 %s &” % pdf)
os.system(”evince %s &” % pdfcopy)

# find them with dogtail
time.sleep(2)
from dogtail.tree import root
evince = root.application(”evince”)
pdfw = evince.window(os.path.split(pdf)[1])
pdfcopyw = evince.window(pdfcopyname)
halfpages = int(pdfw.child(roleName=”tool bar”).child(roleName=”label”).text.split()[1]) / 2
pdfpageel = pdfw.child(roleName=”tool bar”).child(roleName=”text”)
pdfcopypageel = pdfcopyw.child(roleName=”tool bar”).child(roleName=”text”)

def ping():
  global pdfw, pdfcopyw, halfpages, pdfpageel, pdfcopypageel
  try:
    pdfpage = int(pdfpageel.text)
    pdfcopypage = int(pdfcopypageel.text)
  except ValueError:
    # pdf window has been destroyed, probably
    raise “die”
  if (pdfpage + halfpages) != pdfcopypage:
    pdfcopypage = pdfpage + halfpages
    pdfcopypageel.text = str(pdfcopypage)
    pdfcopypageel.doAction(”activate”)

  return True

pingobj = gtk.timeout_add(500, ping)
try:
  gtk.main()
except:
  os.unlink(pdfcopy)

DOMContentLoaded for IE, Safari, everything, without document.write

Wednesday, September 26th, 2007

We have now had, for a while, a way of emulating the “DOMContentLoaded” event across all the main browsers. For those of you who don’t know what I’m talking about, a web browser fires an event, “load”, when a page finishes loading, and JavaScript can attach code to that event which will then run once the page has loaded. However, if there are big images or other slow-loading things in the page then the code gets delayed until after they’ve finished loading too; this is annoying, because most DOM scripting doesn’t actually need the images to be loaded, and so the delay is unnecessary. Opera and Mozilla fire a “DOMContentLoaded” event which runs after the page has loaded but before all the slow images and applets and Flash movies need to load, which is perfect. IE and Safari don’t fire such an event, but it’s been possible to do IE and Safari-specific things to make it happen. (In Safari you check document.readyState, in IE you document.write a <script> element with defer set. See Dean’s writeup.) If you’re using a JavaScript library of some sort (jQuery, Prototype, Mootools, whatever) then all this complexity gets wrapped up for you and you don’t have to worry about it, but if you’re writing stand-alone scripts (like my sorttable), which don’t depend on a library, then this is more of a problem; you have to include a big swathe of boilerplate code to emulate the DOMContentLoaded event.

Recently, Hedger Wang provided an alternative approach for IE that doesn’t rely on document.write, which is rather neat. So, it should be possible to combine the existing Safari method, Opera and Mozilla’s DOMContentLoaded support, and Hedger’s IE approach into a short bit of boilerplate code that can be dropped into your standalone script. And indeed it is. See a simple demo, and more importantly, here is the short version of the code:

(function(i) {var u =navigator.userAgent;var e=/*@cc_on!@*/false; var st =
setTimeout;if(/webkit/i.test(u)){st(function(){var dr=document.readyState;
if(dr=="loaded"||dr=="complete"){i()}else{st(arguments.callee,10);}},10);}
else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){
document.addEventListener("DOMContentLoaded",i,false); } else if(e){     (
function(){var t=document.createElement('doc:rdy');try{t.doScroll('left');
i();t=null;}catch(e){st(arguments.callee,0);}})();}else{window.onload=i;}})(init);

The init on the last line is the name of your “init” function; the one that you want to call on page load. (It could be an inline function if required.) Simply copy those 7 lines into your standalone script and change the name of the init function to be your init, and you’re good to go on all browsers. Remember that if you’re already using a JavaScript library then you don’t need this, but if you’re not then it’s simpler than the current swathes of code, and less heavy than depending on a library just to get access to cross-browser onload event handling.

Tracking bugs for sorttable

Friday, September 14th, 2007

I get lots of people mailing me for help with things I’ve written, and sorttable is way up at the top of the list. People find it useful, which is good, and they mail me to say thanks. I like that. On the other hand, they also mail me to say that there are a few things it doesn’t do. Now, they’re right; there are a couple of bugs that slipped through, and more importantly there are a couple of things that never crossed my mind until people started asking whether sorttable could handle them — loading tables with Ajax, tables with zebra stripes, that sort of thing. I’m now at the stage where in my “sorttable unprocessed” folder I’ve got over 150 emails, and an awful lot of them are about the same things.

So, a bug tracker seemed like a good idea. However, I don’t want one where everyone can submit bugs into it. I get little enough time to hack on projects as it is, without having to triage bugs as well. So, I wanted something which was basically read-only; a place where I could point people to say “look, here, the problem you have identified is a known issue, and I am planning on fixing it, and you can subscribe to this and get notified when I do fix it”.

So: the sorttable bug tracker.

It’s shamelessly borrowed from the PuTTY wishlist in almost all particulars, right down to the differing levels of priority. Simon Tatham actually pointed me, when I asked, at the code they use to run that wishlist. I had a couple of extra wrinkles I wanted to add, though, and their code is in Perl and I’m not much cop at Perl, so I built my own very similar thing. You can read my spec for a read-only bug tracker if you like; essentially, the bugs themselves are in a (secured) folder in my Subversion repository as RFC822-style files, and then there’s a little collection of scripts which know how to build a static HTML collection of bugs for the project. I’d like to make the scripts available so other people can do the same thing, but they’ve got passwords hardcoded into them (because the bug area in svn is secured, because there are bugs in projects that I can’t make public). If I can think of a way of making the code available, I’ll do it, but it’s relatively trivial anyway.

At the moment I’m only tracking sorttable bugs in here, but I’m gradually going to bring all my other projects that are me-develop-you-use things into it. It’s not suitable for something like Jackfield, which will eventually develop more of a community around it (and will therefore be a lot more collaborative); sorttable isn’t a big enough deal to be a community-maintained project, but I do need somewhere I can point people at when they say “why doesn’t it handle Ajax-created tables?”, and now I have that somewhere.

Your Honey Pot Caught A Harvester!

Monday, May 7th, 2007

---------------------------- cut ----------------------------

Stuart –

Regardless of how the rest of your day goes, here’s something to be happy
about — today a honey pot you installed successfully identified a
previously unknown email harvester (IP: 61.183.167.138). The
harvester was caught by your honey pot installed at:

www.kryogenix.org

Don’t forget to tell your friends you made the Internet a little better
today. You can refer them to Project Honey Pot.

---------------------------- cut ----------------------------

Project Honeypot is a system designed to catch email spammers. Basically, you sign up for their site and they give you one PHP (or ASP, or Cold Fusion, or half a dozen other things) file. You drop it somewhere in your webspace and throw in a couple of links to it (hidden on your own pages). The idea here is that ordinary people don’t follow the links, but spammer screen-scraping robots do. On the page is an email address and what looks like a weblog comment form, randomly generated, but tied to your Project Honeypot account. If any email arrives at that email address, which is run by Project Honeypot, or comments are posted through the comment form, then they know it’s spam (because only robots will scrape the email address and then mail it). Whether it’s a large help in the great fight against spam or not I don’t know, but it’s pretty easy to set up and once it is set up you don’t have to do anything. And as you can see from above, I’ve already identified a spammer that they didn’t know about.

You can see my honey pot at http://www.kryogenix.org/random/hpot/pedatetightknit.php if you want to take a look. Just looking at it confers no penalty. The comment form on it is hidden with CSS by default, so it might not be visible when you look. Don’t mail any email addresses you find on the page, though!

Signing up for your own honeypot is pretty easy (although I wish they’d use OpenID for accounts); I’m supposed to refer you, but there’s no “affiliate accounts” or similar. Go get one, and we’ll all try and help.

fspot2lj - export F-Spot photos to your LiveJournal photo hosting

Saturday, April 28th, 2007

fspot2lj is for exporting your F-Spot photos to your LiveJournal photo hosting at pics.livejournal.com. It’s not yet finished, but it works well enough to have exported all my photos and resize them all to 800×600 at the same time. It pays attention to F-Spot tags and creates LiveJournal galleries from them.

It uses my Fotobilder Python bindings (which come pckaged with it). You’ll also need Python, PyGtk, the Python SQLite bindings (which come with recent Pythons), the Python Imaging Library (called python-imaging under Ubuntu), and obviously you’ll need some photos in F-Spot.

You can check it out from Subversion with svn checkout http://svn.kryogenix.org/svn/fspot2lj/trunk/ or download it. Bug reports and complaints in the comments here for now. And yes, I know it’s a bit slow at checking the uploads.

FotoBilder LiveJournal photo gallery Python bindings

Saturday, April 28th, 2007

In order to move my photo gallery to LiveJournal, I needed to import lots of photos. This is doable one-at-a-time on the web, but like all good web apps, FotoBilder (which is the software that runs pics.livejournal.com) has an API which you can work with. There don’t seem to be any Python bindings, though, so I’ve written some. Go get fotobilder.py. Note that it’s not a complete implementation; I’ve only provided the stuff I need (UploadPic, UploadPrepare, a few other things). There are some niceties about it, like that you never have to worry about handling challenges because it does all that for you. Nonetheless, it seems to work for what I needed it for.

You’ll also need to get ProgressReportingHTTPConnection.py. This is a modification of the HTTPConnection object in Python’s httplib, which is part of the standard library, so that it can report progress on uploads. If you need to use httplib to send data to a remote server and you want a progress bar, using ProgressReportingHTTPConnection.py makes it easy. Simply use it like httplib.HTTPConnection but pass a function to the constructor as progress_callback; httplib will call your function with every 2KB of upload done.

Update: licenced under the GPL v2.0.

Jon Hicks’ beautiful Google Reader theme

Monday, April 16th, 2007

The great Jon Hicks has put together a beautiful theme for Google Reader. Makes it look so much nicer.

Simple installation instructions for Firefox. First, install the Stylish Firefox extension. Restart Firefox to make it work. Then go to Google Reader and click on the Stylish icon on your status bar, and choose Write Style > For this URL….

Download Jon’s gReader.zip file and open greader.css from the gReader/Firefox + Camino/ folder in it. Copy the text out of it and paste it into the big textbox in the Add Style window. Add a description (I called it “Jon’s Google Reader Stylator”, but do whatever you want). And: pow! Pretty Google Reader! Everyone’s a winner. Nice one, Jon.

Gaim nicer notifications with libnotify on Ubuntu 6.10 Edgy

Sunday, April 8th, 2007

Gaim (now renamed to Pidgin, but the version I’m using isn’t that new) comes with a “guifications” plugin to do “notifications”, those little popup “toast” messages to tell you that someone’s messaged you, someone’s logged on or off, all that sort of thing. However, guifications is ugly and doesn’t look like the rest of my desktop, and there is a proper notification thing for Linux, called libnotify. Some bright spark has written a Gaim plugin to use libnotify for notifications, but it’s not available in Ubuntu edgy. It is, however, in the development release, Ubuntu feisty. Here’s how to install it (and this acts as a very tiny HOWTO for how to install stuff from an Ubuntu release other than the one you’re on).

Basically, this involves building the package from the source code. First, then, you have to tell Ubuntu that it can get source code from Ubuntu feisty. Go System > Administration > Software Sources. In the Third Party tab, click Add, and put in the APT line: textbox:

deb-src http://gb.archive.ubuntu.com/ubuntu/ feisty main universe

Add Source, Close, and Reload when it tells you that the information about available software is out of date.

Ubuntu's Software Sources window helps you configure where software comes from

Next, you need to get the Gaim libnotify package. I’m assuming here that you’ve built packages before; if not, you’ll need to install build-essential from Synaptic (or with sudo aptitude install build-essential in a terminal).

Create a temporary folder to do the build in (Places > Home Folder, then right-click in your home folder window and say Create Folder, and call the new folder tmp).

In a terminal (Applications > Accessories > Terminal), change into your created temporary folder

cd tmp

and install first the things you need to build the package

sudo aptitude install libnotify-dev gaim-dev fakeroot cdbs

(you could also install those three packages from Synaptic if you prefer), and then fetch and automatically build the gaim libnotify package

fakeroot apt-get --build source gaim-libnotify

That will take a little while. Once it’s finished, go back to your Home Folder window and look in the tmp temporary folder you created. There should be a file called gaim-libnotify_0.12-1_i386.deb

Run that file. The Package Installer window will pop up; click Install Package. This will install the new notifications (it may ask for your password to do the installation).

Now, you need to enable the plugin. If Gaim isn’t running, run it (Applications > Internet > Gaim Internet Messenger), and right-click on its notification-area icon; choose Plugins:

In the Plugins window, tick Libnotify popups. Remember to untick Guifications if you were using it before! Close the Plugins window. I then had to quit Gaim (right-click the notification-area icon, Quit) and restart it before the new notifications worked properly. But, lo and behold, nice pretty notifications when someone logs in!

Christian Heilmann sends me a message
Tiny Matt Revell signs off

WebKit browser on Linux

Sunday, April 8th, 2007

Update: the trivial WebKit browser is now included in Ubuntu. Simply install libwebkitgdk0d on Ubuntu gutsy (just click the package name there to install it if you’re running gutsy now) and then run /usr/lib/WebKit/GdkLauncher to get a simple WebKit browser for testing your code.

You too can test WebKit, Apple’s web rendering engine and the thing that makes Safari work, on Linux. The KDE teams have been working hard on making it work inside Qt, the KDE widget set, and indeed it does work! A few simple steps are required.

First, check out the WebKit source code. There is loads of it, so this will take a while. You’ll need Subversion for this.

svn checkout http://svn.webkit.org/repository/webkit/trunk WebKit

That will give you a folder called WebKit. You’ll now need a few requirements; the key one is Qt4. On Ubuntu 6.10 (edgy), get this like so:

sudo aptitude install libqt4-dev

Now build your Qt-based WebKit browser:

QTDIR=/usr/share/qt4/ WebKit/WebKitTools/Scripts/build-webkit

Finally, run it:

WebKit/WebKitBuild/Release/WebKitQt/QtLauncher/QtLauncher about:blank

I created a menu entry to run it (right-click on the Applications menu, say “Edit Menus”). Now I can test stuff in WebKit!

Note that the Subversion version of WebKit is miiiiiiiiles ahead of what Safari is using in released versions of Mac OS X, so don’t think that just because something works in your little WebKit browser it’ll work in Safari. Nonetheless, cool.

IEs4Linux

Sunday, April 8th, 2007

Testing web stuff in Internet Explorer is an unfortunate necessity. It’s a lot easier if you go get IEs4Linux, which with one command installs IE5, IE5.5, and IE6 on Linux using Wine. Nice.

The latest beta version is easier to install (graphical rather than command-line) and also installs IE7 (in a rather hacked weird way); when it goes stable I shall be using it!

Good work Sérgio Luís Lopes Júnior!

Sorttable v2: making your tables even more sortable

Saturday, April 7th, 2007

Sorttable, the JavaScript table sorting library, is pretty much the most popular thing I’ve ever written. (Top hit for “javascript sort table” on Google, too.) Over the four years or so since I first released it, there’s been a steady trickle of thanks, feature requests, and bug fixes to it. I’ve finally got around to releasing a new version of the script that hopefully covers everything that people were looking for. You can get it at the same place as it ever was, sorttable: Make all your tables sortable.

What’s changed in version 2

The most important thing that’s changed is that sorttable now has a much much clever automatic column typer. You still can just drop sorttable in place and not have to do any configuration, but it’s quite a bit cleverer about working out whether your columns are numeric or dates or currency or text. It skips blank lines, it can tell the difference between English and American format dates, it understands numbers with commas in and negative numbers. This is the number one feature request.

Secondly, you can now use custom sort keys for those columns that sorttable can’t work out for itself. See the page for details, but this should allow anyone to make anything work with sorttable v2.

It’s also a bit more compliant with HTML standards. To add a “totals” row you should add the row to a <tfoot> section in your table. Sorttable v1 used a class of “sortbottom” on totals rows; for backwards compatibility this is still supported, but you really should be using <tfoot> instead.

In a similar way, sorttable now uses <thead> as your column headers if you have it. If you do not have it then it will create a <thead> and put the first row of your table in it. This is a backwards incompatible change: to apply style to your table headers, you should now style table.sortable thead, not table.sortable a.sortheader as in v1.

Input elements are now supported in table cells! If your table cells contain input elements then sorttable will use the value in the input element.

Sorttable is now quite a lot faster than it used to be, owing to various little tweaks. It’s especially fast at resorting the table by the column you’ve already sorted by (but reversed), because now it just reverses the table rather than doing the sort.

Stable sorting is supported but commented out in the code (because the stable sort is implemented in JavaScript, not using the native list sorter, and so it’s a lot slower). You can change this by editing one line in sorttable.js if stable sorting is important; see the page for details.

Thanks

Lots of people (really, really lots) have contributed code suggestions, feature requests, bug reports, and compliments about sorttable. Here’s my chance to say thankyou. So: thankyou to…

Eric Chang, Mehmet Kut, Dan Synder, Jeff Bradley, Eric Holstege, Victor Kryukov, Nikola Ivanov, Geoff Clevenger, Mikko Suniala, Scott Reynen, Justin Meyer, Leonardo Kenji Shikida, Ben Ostrowsky, Daniel Berlin, Richard Snazell, Mark James, Eric Rizzo, Garve, Andreas Rehm, Scott Kingdon, Marcus Månsson, Bill Barry, Michael Rumpler, Avi Rogers, Kevin Wu, Espen Hovlands, Petronel Laviniu Malutan, John M. Pierre, opus27, Brendan Bowles, Marty O’Brien, Alex Rubin, Chris Gay, Jack Vanhan, Guy Rosen, John Dawson, Ryan Korczykowski, Carl Forde, Pete K, Glenn Haser, Richard Grassy, Olivier Briat, Bob Hanson, Alex Z, Narayana Rao Kankipati, Rey Hernandez, Eric Moriztz, Marco Valk, Marcus Daniel, David Dockhorn, Angela Weise, Aneel Nazareth, Jeremy Hulford, Thomas K. Weinstock, Edward, Matthew Egbers, Anton Berezin, Moustafa Elqabbany, Justin Williams, Daniel Chace, Inigo Surguy, Vladimir Kornea, John Mund, Pablo, David Golden, Brad Kemper, Brian Schott, Christian Robottom Reis, Justin Haygood, C. David Eagle, Simon Willison, and Dean Edwards.

Phew! All those people have contacted me asking for new features or have sent me patches or have provided code that I was able to reuse in v2 of sorttable. Thankyou: for those of you who wanted sorttable to do something new, I hope you like it.

Belkin Wireless-G USB adapter in Ubuntu 6.10 Edgy

Wednesday, February 14th, 2007

I’ve got a Belkin F5D7050 wireless USB adapter. It didn’t work under Ubuntu. This is because Edgy ships an old fucked version of the driver. If Ubuntu is loading the “rt73usb” driver, and it’s not working for you, that’s why. Download and install the CVS version as per the Ubuntu wiki instructions and it works perfectly. Superb.

Internationalisation

Monday, November 6th, 2006

There’s a useful trick that I first read about from a Microsoft developer, but I can’t remember which. If you want to confirm that your application properly handled international characters, put the following into it:

Iñtërnâtiônàlizætiøn

Lots of non-English characters, but still readable. Since I can never remember what this phrase is, I’m recording it here for my future self.

Fonts and xchat

Friday, October 27th, 2006

Couple of outstanding things:

Are you annoyed by how xchat-gnome in edgy hangs for ages when you connect to a server, because it’s popped up the channel list? Yeah, so am I. Set it to auto-join a channel (doesn’t matter which one, so make one up if you want) in Edit > Preferences > Networks > (network) > Edit > User and Channels and it’ll stop doing it. David Trowbridge and the xchat-gnome people are aware of how annoying this is and they’re doing something about it.

Are you also annoyed by having blurry anti-aliased TTF fonts* in gnome-terminal? The font I really like for terminal windows is ProFont, which looks really, really clear at tiny sizes like 7pt, because it’s a bitmapped font (also called a PCF). Normally, you make a font available to your apps by dropping it in the .fonts folder*, but bitmapped fonts aren’t enabled by default. From a terminal, run sudo dpkg-reconfigure fontconfig-config on edgy (that might be just sudo dpkg-reconfigure fontconfig on other distros or dapper), and go through the questions (choosing the default answers), but tell it that you want to enable bitmapped fonts when it asks. Then download the ProFont (bitmap font) for Linux/UNIX (new version) and put the .pcf files in your .fonts folder in your home directory (which you might have to create). You’ll have to restart any terminals you have open, but then ProFont will be available for you to use as a font (and in all your other apps too).

My home backup system

Wednesday, October 25th, 2006

After having spent the last five years feeling guilty, I now, finally, have my laptop backing up the data I care about to another machine on my network. Here’s how I did it. This is a relatively long and complicated process, but it means that it all happens automatically and by magic, and I don’t ever have to interact with it, which is what I want.

The first component I needed was some backup space: a machine on my network that I could send the backups to. I did look at online backup space (Amazon’s S3 and similar) like all the cool kids, but I just can’t get on with it, and I resent paying because I’m a cheapskate. So, it was to be a box on my network. Now, there are useful NAS machines around, which just get plugged in and automatically export their disc space (normally as a Windows share, with Samba), and I looked at those too (there’s the Terastation, etc, etc). However, I needed an always-on server for another purpose anyway, so I decided to go with a real machine. A machine cobbled together out of the Big Box Of Machine Bits, of course.

Setting up the server

It’s got two disc drives in it, and I divided the first disc into two partitions, one with 1GB and the other with all the rest. Install Ubuntu Linux 6.10 Edgy, server edition, on the 1GB partition. (I actually installed dapper and then upgraded it to edgy, for that bleeding edge greatness; at this writing, edgy is only at RC stage.) After that, we want to take all the remaining space on the machine (one big partition on disc 1, and all of disc 2) and make them one big block of disc space; this is what LVM, the Linux Volume Manager, is for. Note that all this stuff can be done with proper GUI tools, but I don’t have a GUI on this machine because it’s a server and I’m trying to converse disc space. This bit’s also from memory, so be very careful and don’t just slavishly follow it.

# First, make the partition available to LVM, by
# making it a "physical volume". This is LVM-speak for
# "a bit of a disk that I can use"
pvcreate /dev/hda3 # the big partition on the first disc
pvcreate /dev/hdb # and all of the second disc

# Now, create a "volume group". This is LVM-speak for
# "a big block of disc space all managed together"
vgcreate volumegroup /dev/hda3 /dev/hdb

# Next, create a "logical volume". LVM-speak: "something
# that looks like a disc drive, so you can mount it"
# First, find out how big it can be
vgdisplay | grep "Total PE"
  Total PE              11833
# now create the logical volume at that size
lvcreate -l 11833 volumegroup -n logical1

# You now have a device /dev/volumegroup/logical1
# which you can treat as if it were a disc
# Create a dir to put it in
mkdir /space
# and add it to /etc/fstab so it gets mounted. Add the line:
/dev/volumegroup/logical1 /space auto   defaults        0       0

After that complex little bit (again, if you aren’t tight like me, do it with the GUI, it’s easier), you will have a directory /space on the machine with loads of space in it. Install openssh-server and rsync, because we’ll need them later.

Rotating backups

The way I want my backups to work is as follows. Every night, each machine on my network should connect, and send everything that’s changed since yesterday. When I look on the backup server, there should be a folder for each machine, and there should be in there a folder per day. Each folder should look like a complete backup, but if a file hasn’t changed since yesterday it shouldn’t take up any more disc space. So, the folder structure should look, say, like this:

/space
  /stuart
    /2006-10-24
      /folder1
        /file1
        /file2
        /newfile1
      /folder2
        /file3
    /2006-10-23
      /folder1
        /file1
        /file2
      /folder2
        /file3

and the 2006-10-24 folder should have all the files in it but only take up as much space as newfile1. Complicated, but part of the reason I specified this is because I know it’s possible. (The main reason, of course, is that I’m tight and want to save disc space.) Making this happens involves two stages: making a hardlink tree, and using rsync.

The hardlink tree

If you can get over how much this sounds like something out of an Enid Blyton book, it’s a cool technique. I’m not going to explain hardlinks and inodes and things like that here, because there are many other descriptions elsewhere. Suffice to say that, if you have a folder, you can make a duplicate of that folder with cp -al folder newfolder, and that duplicate will look the same and be full of real files but not take up any disc space. My nightly backup therefore needs to do the following:

  1. Copy last night’s backup to a new folder, named for the current date
  2. Change the data in the new folder to look like my laptop, so it’s got all yesterday’s data but with any changes I’ve made today

The issue here is: how do you know what last night’s backup is called? I’ve solved this by making sure there’s a symbolic link called current which always points to the most recent backup. So, the above process actually becomes:

  1. Copy the current folder to a new folder, named for the current date
  2. Change the data in the new folder to look like my laptop, so it’s got all yesterday’s data but with any changes I’ve made today
  3. Change the current link so it points to the newly created most recent backup

The script that does this is stored in /space/begin-backup, made executable with chmod +x /space/begin-backup, and looks like this:

#!/bin/bash

PERSON=$1
BROOT=/space

if [ -z "$PERSON" ]; then
  echo You must pass the name of a backup dir
  exit 1
fi

PDIR=$BROOT/$PERSON/

# If person dir doesn’t exist, create it
if [ ! -d $PDIR ]; then mkdir $PDIR; fi

# If there’s no current dir, create an empty one and link it
if [ ! -d $PDIR/current ]; then
  mkdir $PDIR/first
  ln -s first $PDIR/current
fi

DT=$(date -Iseconds)

# Hardlink-tree the existing recent dir
cp -al “$(readlink -f $PDIR/current)” $PDIR/$DT
# and link current to the new hardlink tree
rm $PDIR/current
ln -s $DT $PDIR/current

We’ll come back to how you run this in a minute.

Rsync

The change the data in the new folder to look like my laptop bit is done with rsync, which is complex but brilliantly clever. In essence, rsync is like copy (or cp), except that it compares the source and the destination and only sends the changes over. On my laptop, I can do

rsync -avz --delete -e ssh \
    /some/folder/to/back/up \
    myserver:/space/stuart/current/

and that will copy /some/folder/to/back/up over to the server. Importantly, if that folder is already in the backup space, in the current folder (because we backed it up yesterday) then it’ll only copy the changes over. This is why we make sure that there’s a folder called current with the contents of last night’s backup!

Exactly how we run this rsync command we’ll come on to in a minute. Patience, Iago.

Choosing what gets backed up

I don’t want to back up everything. I don’t have the space, and to be frank I have a lot of crap lying around on my machine. So I need a very easy way of tagging something for backups. This is a perfect use of emblems; I can “tag” a file or a folder in the file manager with a special “backup” emblem, and that should indicate to my backup process that that file or folder wants to be included in the backup. Ubuntu doesn’t have a backup emblem included by default, but adding one is easy, and explained in the docs. Pick yourself an image (I use this little tape) and add it as an emblem, and then go through your machine and add it to every file or folder that needs backing up. (This will, if applied to a folder, back up everything inside it. If you need it to back up only some of the stuff inside it then you’ll have to not apply it to the folder. Yes, this is awkward, but I don’t need to do that.) Applying emblems is also in the documentation; a quick way if you’re doing this a lot is to pop up the Edit > Backgrounds and Emblems window and just repeatedly drag your new backup emblem to everything.

SSH with no password

One final preparation step: in order that the backup can run without me being around, I need to be able to make an ssh connection from my laptop to the server without entering a password. I’m not going to describe how to do this because there are plenty of guides out there on the web.

Make it so

Now, finally, after lots of setup, it’s time to actually make it all happen. To summarise, then, to do a backup, we need to:

  1. Run, on the server, the copy-last-night’s-backup script
  2. Get the list of all the files with the backup emblem
  3. Use rsync to copy all those files into the new backup folder on the server

To get the list, we can use my findemblem.py script (and you thought I just wrote it for fun!). The final script, dobackup.sh, which actually does the work, just does the above steps, and looks like this:

#!/bin/bash

# Do backups to the rsync server
# You must have already set up a passphraseless ssh key to the ssh server
# so that "ssh servername” just logs you in.

BK=$(dirname $0)
BKNAME=stuart

# First, tell it to clock over the backup
ssh servername /space/begin-backup $BKNAME

# Now, do the backup
python $BK/findemblem.py backup | while read fn; do
  rsync -avzq –delete -e ssh “$fn” \
    servername:/space/$BKNAME/current
done

All that remains now is to schedule this script to run every night, by editing your tasklist with crontab -e and adding the line

40  4  *   *   *     /full/path/to/dobackup.sh

And, lo and behold, you have overnight backups. All done and dusted. Phew.

Find files with an emblem in Gnome

Monday, October 23rd, 2006

With the desktop on Ubuntu Linux (indeed, with any Gnome desktop) it’s possible to add emblems to any file. An emblem is a little reminder, a small icon that appears on top of the file. Think of it as a tag, perhaps, in this Web 2.0 world.

The 'Sam presents' file has a 'money' emblem.

Anyway, I needed a way of establishing which files had a particular emblem from a script. So here’s such a script. Call as follows:

aquarius@giles:~$ python findemblem.py money
/home/aquarius/Sam presents
/home/aquarius/Work/quotation.xml

The actual script is:

import re, glob, sys, urllib, os
from xml.dom import minidom

def grep(pattern, files):
  search = re.compile(pattern).search
  matches = []
  for file in files:
    for index, line in enumerate(open(file)):
      if search(line):
        matches.append(file)
        break
  return matches

def findEmblemedFiles(emblem):
  metafilesWithKeyword = grep(’<keyword ‘,
               glob.glob(os.path.expanduser(’~/.nautilus/metafiles/*’)))
  retfiles = []
  for dirxmlfile in metafilesWithKeyword:
    dom = minidom.parse(dirxmlfile)
    filenodes = [x.parentNode.getAttribute('name')
                 for x in dom.getElementsByTagName('keyword')
                 if x.getAttribute("name") == emblem]
    files = dict([(x,'') for x in filenodes]).keys()
    # turn /home/foo/.nautilus/metafiles/file:%2F%2F%2Fhome%2Ffoo%2FWork.xml
    # into /home/foo/Work/
    dirname = urllib.unquote(os.path.split(dirxmlfile)[1])[7:-4] + ‘/’
    retfiles += [os.path.join(dirname,urllib.unquote(f)) for f in files]
  return retfiles

if __name__ == “__main__”:
  print ‘\n’.join(findEmblemedFiles(sys.argv[1]))

Looking at rawdog

Wednesday, October 11th, 2006

Well, I’ve finally got hacked off with Google Reader (not least because I am sick of Ajax loading screens), and started looking around for my own feed reader. (I did this before, but now I’m going to do it properly.) I don’t like gregarius, after trying it, and I remembered that rawdog popped up in a search I did once because it has a plugin that uses templates from my old weblogging system, Vellum. So, I thought I’d give it a shot. It looks good so far; fulfils the few requirements I have for this sort of thing, which are:

  1. Heavily customisable
  2. Written in Python, so I can actually do the customisation
  3. Doesn’t require a database server

I intend to try and document what I do to Rawdog to get it to be the aggregator that I really want.

First job, though, is to get all my feeds imported into it. I’ve been chatting to Adam Sampson, the rawdog author, about it and a few things that it can do, and something that’s missing is an OPML import. If you’ve got an OPML file, you can import it into rawdog with a (fairly complex) Python and bash one-liner as follows, where feeds.opml is the OPML you’ve exported from your existing aggregator:

for f in $(python -c "from xml.dom import minidom; dom=minidom.parse('feeds.opml‘); print ‘ ‘.join([x.getAttribute('xmlUrl') for x in dom.getElementsByTagName('outline')])”); do rawdog -v -a $f; done

Hitting the Wayback Machine

Sunday, September 24th, 2006

I find myself hitting 404ed pages all the time, and wanting to look the pages up in the Wayback Machine to see what they said when they existed. So, a quick bookmarklet: Get archived version. Drag to your bookmarks bar and click when you hit a 404.

OperationalError: SQL logic error or missing database

Sunday, September 17th, 2006

If you’re getting

OperationalError: SQL logic error or missing database

from your Python web script, first check that your SQLite database is readable and writeable by the user that Apache is running as (www-data if you’re on Debian/Ubuntu) and that the folder that the DB file is in is also readable and writeable by the Apache user. Your webserver needs to be able to write to the file. That is all.

Extremely noddy TortoiseSVN for the Gnome desktop

Tuesday, September 12th, 2006

I use the Nautilus Subversion scripts to manage my Subversion working copies all the time. The big thing that they’re missing is TortoiseSVN’s pretty icons that get displayed on each file so you can tell at a glance whether a file has been changed or not. Well, that sort of thing is also possible, because you can write Nautilus extensions in Python. Grab my svnemblem script and drop it in .nautilus/python-extensions in your home directory. You’ll need to restart Nautilus to make this work (which basically means logging back out and in again) and you’ll need the Python Subversion bindings (package python-subversion in Ubuntu: not pysvn, which is package python-svn) and the Python Nautilus bindings (Ubuntu: python-nautilus). You can test it’s working by running TMPDIR=<some temporary directory, not /tmp> nautilus --no-desktop <path to an SVN working copy dir>.

(thanks to Xalior for help with testing)

Update: there is something of a problem, in that the emblems don’t update if you change anything. This seems to be because once Nautilus has been into a folder once, it doesn’t call the extension when you go in there a second or subsequent times, even if you hit Reload. I don’t know whether this is fixable or not at the moment; I’ll try and look into it. Any suggestions gratefully accepted!

Making .local DNS address lookups work on Ubuntu Dapper

Saturday, September 9th, 2006

I’ve got avahi installed on Ubuntu Dapper, so I assumed that that would make looking up foo.local address lookups work. Lots of sites say “add mdns to the hosts line in /etc/nsswitch.conf“, but it wasn’t working. What is needed is an “mdns plugin” for nsswitch.conf, which is nss-mdns. Installing this (it’s the libnss-mdns package in Ubuntu) made it all work; I can now ping foo.local and it works! Yay!

(not sure why it isn’t there by default…)

Decoding Morse, the computer way

Thursday, September 7th, 2006

Jeremy Keith’s been sent a Morse message and is wondering what it says. Well, I now know what it says, cos I wrote a program to decode it.

First, I went and got the mp3 of the message from Odeo and loaded it into Audacity. (No, not Jokosher; I’m at work, and there’s no Jokosher port for Windows. Audacity is still a free software audio editor, though, so being able to just download it is very handy indeed.) Audacity can read mp3s; I resampled it to 48000Hz (which made the peaks clearer) and made it mono (rather than stereo).

At this point I could theoretically have decoded it by looking at the waveform: as you can see from this Audacity screenshot, that’s clearly the Morse code .... .. .-.-.- (short sound is a dot, long sound is a dash; that decodes to “HI.”)

However, that’d be boring and laborious, and what if the message was five hours long, eh? You wouldn’t want to transcribe that by hand. So, get the computer to do it! I didn’t want to write an mp3 parser, though, and because I was on a Windows box I didn’t have GStreamer available which would have helped with this. So, I exported the sound from Audacity as a WAV file, and then went off and got sox, the Swiss Army knife of audio converters and another open source program. Sox can convert a sound sample to its “data” format, which is a load of lines that look like this:

               0  -6.1035156e-005
  2.2675737e-005  -3.0517578e-005
  4.5351474e-005                0
  6.8027211e-005  -3.0517578e-005
  9.0702948e-005   3.0517578e-005
   0.00011337868  -3.0517578e-005
   0.00013605442                0
   0.00015873016  -3.0517578e-005
    0.0001814059  -3.0517578e-005

where each line represents one sound frame; the first number is “number of seconds since the beginning of the sample”, and the second is “loudness of this frame”. So, that’s the answer! The loudness will be high where there was a sound and low where there wasn’t. I needed to go through the data and find all the loud bits and all the quiet bits; a long quiet bit is a space, a long loud bit is a dash, and a short loud bit is a dot. No problem: quick bit of Python scripting:

fp = open("jezza4.dat")
data = [int(abs(float(x.split()[1])) > 0.01) for x in fp.readlines()[2:]]
fp.close()

# count all the runs
counts = []
current = -1
count = 0
for i in data:
  if i != current:
    counts.append((current,count))
    current = i
    count = 0
  count += 1

# now remove all short runs, which also removes the -1 row!
counts = [x for x in counts if x[1] >= 15]

# and reaggregate everything
counts2 = []
current = -1
count = 0
for i in counts:
  if i[0] != current:
    counts2.append((current,count))
    current = i[0]
    count = 0
  count += i[1]

mystr = “”
for x in counts2:
  if x[0] == 0:
    if x[1] > 350:
      mystr += ” ”
  elif x[0] == 1:
    if x[1] > 500:
      mystr += “-”
    else:
      mystr += “.”
print mystr

and that printed out the Morse code for the message in dots and dashes! Nice and easy. Quick trip over to a Morse code converter to read it, and there we go. Message decoded. Good one, Tom.

Oh, you want to know what it said?

- --- ..- --. .... .-.. ..- -.-. -.- .-.-.- - .-. .- -. ... -.-. .-. .. -... . .. - -.-- --- ..- .-. ... . .-.. ..-. .-.-.- .-- . .-.. .-.. -.. --- -. . - --- -- .- -. - .... --- -. -.-- --..-- - .... --- ..- --. .... .-.-.-

PhpBB spam on the Jokosher forums

Monday, August 21st, 2006

We’ve been having a few bots spamming the Jokosher forums, by registering an account and posting a spam post. To that end, I’ve applied the Easy BotStopper mod, which adds a (simple but apparently quite effective) wrinkle to the signup process. It’s a Club solution, but, as famously said by the desert traveller in the Nike trainers, I don’t have to outrun the lion, I just have to outrun you. Let’s hope it puts enough of a barrier in front of phpbb spambots to keep us ahead of the game for another few months.

If you have any suggestions for ways to stop spam on phpbb installations that don’t involve captchas, I’d be interested in hearing them; we’ve already disabled anonymous posting in addition to the mod above.

Simon Tatham’s Portable Puzzle Collection Launcher

Monday, August 14th, 2006

Simon Tatham has written a collection of little puzzles, which are very useful little things to play with when you’ve got five minutes to spare. However, installing the Debian package (sgt-puzzles) doesn’t add the little games to your Gnome menu. I could just add a submenu and loads of .desktop files, one for each app, but I couldn’t be bothered with that. So, a tiny PyGtk program to launch them. This will only work on Debian/Ubuntu because it looks for the dpkg .list file to build the list of packages!

/usr/local/bin/sgt-index:

#!/usr/bin/python
import gtk, sys, os

try:
  fp = open("/var/lib/dpkg/info/sgt-puzzles.list")
except:
  print "sgt-puzzles not installed!"
  sys.exit()

games = [
  os.path.split(x.strip())[1]
  for x in fp if x.startswith(’/usr/games/’)
]
games.sort()

def startgame(event, gamename):
  os.system(”/usr/games/”+gamename+” &”)
  gtk.main_quit()

win = gtk.Window()
hbox = gtk.HBox()
vbox1 = gtk.VBox()
vbox2 = gtk.VBox()
win.add(hbox)
hbox.add(vbox1)
hbox.add(vbox2)
count = 0
for g in games:
  count += 1
  b = gtk.Button(g)
  b.connect(”clicked”, startgame, g)
  if count % 2 == 1:
    vbox1.add(b)
  else:
    vbox2.add(b)

win.connect(”destroy”, gtk.main_quit)
win.show_all()

gtk.main()

Then just add an item to the menu (right-click the Applications and say Edit Menus).

Lock table is out of available locker entries

Saturday, August 12th, 2006

I managed to shaft my Subversion repository today. No idea how it happened, and fixing it was a bit problematic.

The error was:

svn: bdb: Lock table is out of available locker entries

If I accessed the repos directly through the web (Apache), it just said “couldn’t open the requested svn filesystem”. WebSVN gave the above error, as did using svnlook on the repository itself.

The problem is that the Berkeley DB database* underlying the repository has run out of locks. To fix this, there’s the short-term fix and the long-term fix. You should do both: you have to do the short-term fix first because until you do nothing else works, but then you should really do the long term fix as well; it’ll stop this ever recurring again.

The short term fix

What we have to do is increase the number of lockers in the database. There are two steps to this. First, edit /path/to/your/repos/db/DB_CONFIG. Buried in there there will be some of the following lines:

set_lk_max_locks   2000
set_lk_max_lockers 2000
set_lk_max_objects 2000
set_lg_bsize     262144
set_lg_max      1048576

I’m not sure exactly which to change, so I changed lots of them, as follows:

set_lk_max_locks   3000
set_lk_max_lockers 3000
set_lk_max_objects 3000
set_lg_bsize    2097152
set_lg_max      8388608

However, that alone won’t work. Berkeley DB seems to cache the settings. To fix this, you need to remove the cached environment. In /path/to/your/repos/db/ there will be files called __db.001, __db.002, etc. You need to remove these files. I strongly recommend that you move them somewhere, not delete them. Once you’ve removed them, try re-accessing the repository the way you normally do and it should work. If it didn’t work, move the files back before you try anything. Now do the long-term fix, below.

The long-term fix

The real problem here is that Berkeley DB isn’t very good. The svn people advise you to not use it now in favour of their own format, fsfs. I’m sure that BDB is a great and powerful program, but it does seem to get lots of locking errors and so forth. So, you should convert your repository to fsfs format. Do this like so:

mv /path/to/repos /path/to/repos.old
svnadmin create -fs-type fsfs /path/to/repos
svnadmin dump /path/to/repos.old | svnadmin load /path/to/repos

That renames your existing repository to repos.old and then creates a new fsfs repository where the old one was. Finally, it transfers all your data from the old one to the new one. You can delete the old one once you’ve done this, but it wouldn’t hurt to keep it lying around in case something goes wrong.

Once you’ve done this, everything should continue working, and you’ll never get locking errors again.

Ban that bandwidth slurper

Monday, July 3rd, 2006

Angel, the machine that runs www.kryogenix.org (among other sites) is being rather slow. On investigation of my logs, it appears that that’s quite possibly because it’s being hammered by people. I deleted my log and then waited an hour to see if I was being hit a lot (the log was 200MB! in less than an hour!) and then pulled out the 5 most persistent offenders with

cut -d" " -f1 /var/log/apache2/kryogenix.org-access.log | sort | uniq -c | sort -n | tail -5

which gave me

    324 217.112.126.122
    381 81.133.81.248
    421 82.108.113.14
   2060 213.249.154.101
   2391 81.159.133.111

Those last two are a bit high, I think; two thousand hits in an hour? I mean, I appreciate all you people reading the good word of the Langridge, but I’m not that good a writer. So, they get banned, which is nice and easy. Following Mark Pilgrim’s explanation, I added the following lines to .htaccess:

# all your DoS are belong to us. Ban ban ban.
RewriteCond %{REMOTE_ADDR} ^213.249.154.101$
RewriteRule .* - [F,L]
RewriteCond %{REMOTE_ADDR} ^81.159.133.111$
RewriteRule .* - [F,L]

and…no more hits from those IPs. Beware, people sucking down my bandwidth: I have plenty of room in .htaccess for more of those lines.

Doing a “Show Desktop” using Python’s libwnck bindings

Tuesday, June 13th, 2006

Quick tip: you can do a Show Desktop from a script (from Python, in this case), with:

python -c "import gtk,wnck,gobject; s=wnck.screen_get_default(); s.toggle_showing_desktop(True); gobject.idle_add(gtk.main_quit); gtk.main()"

Subversion in a Windows environment: authenticating against multiple domains

Wednesday, June 7th, 2006

We run Subversion for source control at work, with Apache2, and we use mod_auth_sspi to allow developers to authenticate to Subversion with their Windows username and password. However, we’ve got more than one domain at work, and so we need SSPI to allow you to authenticate with your Windows username and password whichever domain you’re in. The relevant stanza of httpd.conf, to authenticate against multiple NT/2003 domains, looks like this:

<Location /svn>
DAV svn
SVNParentPath e:\svn
#
AuthName "Subversion repositories"
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
</LimitExcept>
AuthAuthoritative Off # multiple authorities
#
AuthType SSPI
SSPIAuth On
SSPIAuthoritative On
SSPIDomain domain_controller_for_first_domain
SSPIOfferBasic On
#
AuthType SSPI
SSPIAuth On
SSPIAuthoritative On
SSPIDomain domain_controller_for_second_domain
SSPIOfferBasic On
#
AuthType SSPI
SSPIAuth On
SSPIAuthoritative On
SSPIDomain domain_controller_for_third_domain
SSPIOfferBasic On
</Location>

Turning on Samba on Fedora Core 4

Tuesday, May 23rd, 2006

I’ve just found configuring Samba shares on Fedora Core 4 very easy (Desktop | System Settings | Server Settings | Samba) except that it didn’t initially work, and I couldn’t work out why. Some googling around led me to a note from Paul Howarth indicating that SELinux blocks Samba sharing by default (very sensibly); you can configure this in Desktop | System Settings | Security Level. Well done, Fedora people. That was the easiest time I’ve ever had setting up Samba.

(Note: I specifically configured a password for a Unix account, and people have to use that username and password when connecting to the share from Windows. It doesn’t use your native Windows username, or authenticate against the domain, because I don’t care about that.)

Distributed backups to friends

Wednesday, April 12th, 2006

It ought to be possible to have a backup system with the following characteristics:

  1. You download a backup client and run it. It asks for a backup group name and a password. It is cross-platform, or at least ported to Linux, Mac OS X, and Windows. It also asks how much space you’re prepared to devote to backups.
  2. You choose directories and files to back up by finding them in your file manager and tagging them as “For backup”.
  3. That’s then all the user interaction that there is.

The way the backup actually works is that:

  • It takes the stuff you want to back up, and creates a big backup file out of it, every night.
  • It breaks the file up into bits, using the PAR stuff from parchive.sourceforge.net. This means that to recover your backup, you need some but not all of the bits, so if some bits get lost it doesn’t matter.
  • It then ships the bits out to other people in your backup group and stores it on their systems, not on yours, giving you off-site backups.

That would make it very easy for a group of people to do mutual backups without having to think very hard about it.

Implementation thoughts

You’d need a server somewhere, to store password details for backup groups and to co-ordinate shipping the data around (since everyone’s likely to be behind a firewall). No-one should ever see or know about this server, though. There is no “sign-up procedure”; to create a new backup group, you just run the client and provide a backup group name and password. That’s all. You don’t have to sign up on the web or explicitly invite anyone into the group; anyone with the username and password can join.

There’s nothing in the above about how to restore from a backup, I know. That needs some kind of UI, but I’m not sure what that should be.

It needs to warn you if there’s not enough space out there on the group to back up all the stuff you’re trying to back up. Some kind of algorithm which demands that if you want to back up N megabytes you have to offer 3N megabytes of space to the group or something.

There should be some rsyncness in it. If not much has changed, it shouldn’t need to send much out to the group. However, this might be complex, because the previous backup is in scattered bits, and you don’t want to do incremental backups because then you need the full backup as well.

Backups must be encrypted, because they’re stored on someone else’s machine. There will probably need to be some kind of UI to provide a passphrase or similar. This also makes rsyncness difficult.

I think this would be a really useful project. The key point, the absolutely critical point, is that the client must be as described above: it just asks you which backup group you’re in and that’s all. No ten pages of options, no need for you to tell it who else is in the group or to maintain a list of who that is or where you want backups to go. If it’s in any way difficult, it won’t get used, and then no-one has backups.

Wish I had time to write this. The big problem that needs solving is how to have the rsyncness in it, so that it only ships changes around rather than a full backup every night. Other than that, it’s all doable, and not all that difficult.

Using Ekiga with SipGate UK

Saturday, March 18th, 2006

A while back I provided instructions on setting up LinPhone to use SipGate UK for Voice Over IP. Now, Ubuntu 6.04 Dapper is out, and comes with the all-new Ekiga softphone! Here’s how to set it up to use SipGate.

  1. Sign up for a SipGate account.
  2. Once you’ve got one, sign into the SipGate website and click “My Account”.
  3. Under “Connection Information”, it will list your SIP-ID (a 7-digit number) and password, which you need for the next steps.
  4. Start Ekiga, and go through the configuration wizard. Don’t sign up for an ekiga.net account.
  5. Say Edit > Accounts, and then Add.
  6. In Account Name, enter Sipgate.
  7. Leave Protocol as SIP.
  8. In Registrar, put sipgate.co.uk.
  9. In User and Password, put your SIP-ID and password from step 3 above and say OK.

That’s it. No port-forwarding required, no complex setup. Well done the Ekiga people! To test your new setup, call sip:613@fwd.pulver.com, which is an echo test.

Run Your Own Web Server Using Linux & Apache

Thursday, December 15th, 2005

My second book, Run Your Own Web Server Using Linux & Apache, is available from SitePoint. If you’re thinking of putting up a web server and you want to do it all with Free Software, and use the industry standard Apache to do it, then you might find the book useful. A collaboration between me and Tony Steidler-Dennison, it should take you through everything you need to know.
Run Your Own Web Server Using Linux & Apache

Foxpose and Optimoz

Friday, December 9th, 2005

Foxpose is a Firefox extension* that shows all your tabs, shrunk, inside the window so you can choose between them. It’s obviously a blatant rip-off of OS X’s Expose, but then that’s a good UI and so using it in other places is a good idea. However, having to find the tiny icon in the Firefox status bar and click on it to do the Foxpose thing is a major pain in the arse; it means that using Foxpose is considerably slower than scanning your tab titles. Yes, there’s a hotkey, but that’s still a pain (not least because you have to focus Firefox first and then press the hotkey and then switch back to the mouse to click on a tab and…bah).

Optimoz is a Firefox extension that allows you to use “mouse gestures”: hold down a button or a hotkey and draw a shape on your Firefox window and something happens, depending on what the shape was. For example, drawing a short line from right to left does the same as pressing the Back button; drawing a squarish lower-case h (go down, up, right, down) goes to the home page, and so on. It’s most useful.

These two can be profitably combined. In Optimoz, set up a “custom” action. You can customise Optimoz by going to Tools > Extensions > Mouse Gestures > Options. From there, click Edit Mappings on the General tab. Add a new mapping with New. In Function type set Custom, in Mapping name write Foxpose, and in Custom JavaScript code write Viamatic.Xpose.Main(); (thanks to Jens Bannmann for help there!). Then click Recognize and draw a mouse gesture in the box that appears (I use a line left-to-right and then right-to-left, so my gesture is coded as RL, but pick whatever you want). OK button all the way out. You can now use your chosen mouse gesture to do the Foxpose thing. Marvellous.

What’s going on

Sunday, December 4th, 2005

Lots of stuff circling over Heathrow. I am pretty much coming to the conclusion that I should write about ideas that I have rather than stashing them away in the black hole that is my projects list, because otherwise they’ll never get done. I don’t like doing that; I’m pretty firmly of the opinion that if you think a thing would be good then you should just write it rather than just writing about it; Ade would say that I do that all the time, because I write specs rather than writing code, but I try fairly hard to not do so. Nonetheless, these ideas should live somewhere other than my head, I think. So some posts should be forthcoming about stuff that’s on my mind.

Before that, though, a few notes on random cool things I know about.

Telewest, my cable provider, have launched a new service called Teleport. It’s very cool indeed. It’s, in fact, the long-promised a la carte TV: you can watch programmes whenever you want, rather than when they’re on. At first, it seemed a bit rubbish: they have a Teleport Films section where you can purchase a film for watching. Now, Telewest already had that with Front Row (now rebranded FilmFlex), but Teleport Films also contains films that aren’t the latest releases; we watched Tango and Cash the other day. After buying a film you can watch it as many times as you like within 24 hours, which is faintly cool. Nonetheless, that’s not all that exciting. We then discovered Teleport TV, which is (again) not all that revolutionary: you can pick TV programmes and watch them, from a pretty limited list. Most of the programmes are rubbish and stuff you wouldn’t want to watch anyway: one exception is Waking the Dead, a BBC murder drama which we’ve started watching and all three series of which are available with Teleport TV. So, again, that’s faintly cool, but the selection’s not up to much. Then we discovered that it has TiVo-like properties of being able to pause and rewind and fast-forward the programmes via the remote control, which was invaluably helpful when Sam and I look at one anotherand say “what did he just say?” or when we want to skip the credits on something. Finally, I discovered Teleport Replay, which has loads of programmes that were on over the last seven days also available for watching (which go away after those 7 days, much like the BBC Radio “Listen Again” service). Including Top Gear, which I shall now never miss again. Well done Telewest. This is a pretty darn good service, although I’d like to see more programmes available for Teleport TV, and the user interface is, as usual for Telewest, dog slow. Nonetheless, nice one.

Jono and I moved all the remaining sites from the old version of our server to the new version. Creating each site was pretty easy. For those unsure about how to use Apache2, a very short HOWTO. To create a site www.example.com, I do it like this (either as root, or using sudo for each command):

mkdir /var/www/example.com
mkdir /var/www/example.com/html

(place all files for the site in /var/www/example.com/html)

chmod -R a+rx /var/www/example.com/html
chown -R username /var/www/example.com

(use username that should own the site in line above)

cd /etc/apache2/sites-available
nano example.com

(place the following in the example.com file you’re editing)

<VirtualHost *>
ServerAdmin email@address.of.admin
DocumentRoot /var/www/example.com/html
ServerName www.example.com

ServerAlias example.com

</VirtualHost>

(now back to the prompt)

cd /etc/apache2/sites-enabled
ln -s ../sites-available/example.com .
/etc/init.d/apache2 restart

(and that site should now be working)

David Morley on the Wolves LUG list is putting together some video tutorials about how to use Linux from a newbie’s perspective. I’d like to see this: I wrote some instructions on how to use vnc2swf for recording a video tutorial (and see the remainder of the thread for corrections). Now, there already is a tool to do this: Gnome’s Istanbul. It’s got a good UI in principle; starting it drops a “record” icon on your panel. Click the icon to start recording, click it again to stop recording. It outputs to Ogg Theora. I think the UI could be improved a bit, though; for one, it should record screencasts and save them as Screencast1, Screencast2, etc, on your desktop, rather than making you specify a name (as the Gnome screenshot tool does). At some point I’d like to hack on it to make that possible, but this is one of those “don’t have enough time” projects. I’d also like to see it be able to output to Flash, as does vnc2swf. However, I don’t want to do that until I’ve confirmed for myself that the GPLFlash project can play such created videos. Being able to just drop Flash stuff on a web page is something of a bonus for stuff like this; I don’t really like in-browser video plugins, mainly because they never seem to work right for me.

We put up our (first) Christmas tree yesterday. Niamh really likes putting up Christmas decorations. It does rather amaze me that it’s come upon us so fast, but, hey, we like Christmas. The second tree will be forthcoming once the new flooring is down in the library, which doesn’t happen until about the 22nd December or something, so bah humbug to that! Anyone want to buy a piano, by the way? We’ve run out of room for it.

Random linkage, from Bloglines’ Keep New. Inci