Wednesday 13 April 2011

DjView4 plug-in in Mac OS X Firefox? Only under X11.

It was easy to find open tools to make decent-quality e-books from my scans of a classic text. Scan Tailor and djvubind are a killer combination for producing an e-book with positional, searchable OCR in no time. The URI and the web browser are central to what I'm doing at the moment, and DjVu is no use unless a single click on a URI will render it directly in the browser, jumping straight to the fragment of interest.

I'm deeply invested in Firefox and OS X and while the LizardTech plug-in lives on at Celartem and works on Firefox 3.6.x, it's closed-source, its toolbar buttons don't work and I'm not sure it'll survive for long. DjView 4 includes an open NPAPI plug-in which works on Linux but isn't available for OS X, so I thought I'd have a go at getting the DjView 4 plug-in working on OS X in case it was something trivial. It wasn't, and I thought I'd document why in case anyone else wasted their time wondering/speculating before (or after) the resulting patch gets accepted.

My first misdirected effort was to simply build the DjView 4.7 tree outside MacPorts and copy the resulting nsdejavu.so into the ~/Library/Internet Plug-Ins/ directory, hoping simple shared objects still worked like the good ol' Linux days. They don't; on OS X, plug-ins must be "loadable bundles" (well, technically a "loadable module" bundle within a loadable bundle, which also happens to be a package — and let's not even get started on flat packages).

My second misdirected effort was to simply copy the built nsdejavu.so into an existing plug-in bundle (updating CFBundleExecutable). The plug-in ceased to appear in about:plugins. I guess if Firefox cared to check, the stuff returned by NP_GetValue() would be inconsistent with the Info.plist, but that seemed unlikely.

My third effort was to build the plug-in as a bundle, using the OS X template plug-in from the new NPAPI SDK. After altering the HEADER_SEARCH_PATHS to include config.h, the MacPorts glib headers and the NPAPI headers and fixing a bunch of trivial compile errors, I hit a wall at:

DjVuPlugin.c: In function 'NPP_SetWindow':
DjVuPlugin.c:2484: error: 'NPWindow' has no member named 'ws_info'


NPWindow only has ws_info if you're compiling for Mozilla on non-OSX UNIX. Reading a little more closely, it turns out the plug-in runs DjView in a separate process, relying on Firefox support for either Xt or XEmbed to render the document, neither of which are supported on the normal Cocoa-based Firefox most OS X users use. (While it's possible to run X11-based Firefox on OS X and probably possible to build an Qt-on-X11 variant of DjView that would support the -netscape flag, it's not worth it.)

So we're stuck with Celartem's plug-in for now.

Saturday 5 March 2011

Predictable path for single result in SpotlightFS

As part of a slightly bigger project, I'm putting together a simple system to look up select files or directories by a unique ID I've assigned them previously which I can use to reliably refer to them forever, without depending on their contents (which rules out checksums) or their paths (which rules out symlinks), and which survives (careful) moves across filesystems/disks (which rules out inode numbers), or even of changing my UNIX flavour (which rules out hard dependence on OS X). It'll be rather like URL shorteners (except some have disappeared!) or Carbon's Alias Manager.

I'm using Spotlight to index the UID and SpotlightFS to make access a simple matter of accessing a URL such as file:///byUID/o9e8 (making Spotlight an implementation detail that can change later). I can formulate a Spotlight query that'll return exactly one result — the one with the corresponding UID — but SpotlightFS will always give you a directory of symlinks whose names encode the true paths to results (link targets), meaning they vary based on the path. An example query (not using my UID stuff) with one result:


me$ ls -la /Volumes/SpotlightFS/SmarterFolder/exec_separate_vm/
.
..
:Users:me:JavaSnoop.properties -> /Users/me/JavaSnoop.properties


What I want is a predictable symlink that works when there's exactly one result, linking to that single result. The only way I can see to do this and keep file:///byUID/ URLs working is to patch SpotlightFS. The resulting patch (to version 2.0.3,2, ready to be dropped into /opt/local/var/macports/sources/rsync.macports.org/release/ports/fuse/spotlightfs/files/) allows me to do this:


me$ ls -la /Volumes/SpotlightFS/SmarterFolder/exec_separate_vm/the-only-result
/Volumes/SpotlightFS/SmarterFolder/exec_separate_vm/the-only-result
->
/Users/me/JavaSnoop.properties


It doesn't show up in the directory listing of a SmarterFolder query — if you don't stat() it, you'll never know it's there (which could be considered a feature). If there are zero results or multiple results, it'll provide the same symlink with an explanatory message as the target:


me$ ls -la /Volumes/SpotlightFS/SmarterFolder/arloegu098pgm/the-only-result
/Volumes/SpotlightFS/SmarterFolder/arloegu098pgm/the-only-result
->
error-there-must-be-exactly-one-result

Saturday 19 February 2011

Make OS X forget incorrect Wi-Fi details

Every time I connect to one particular wireless network I get prompted with an "Authenticating to network" dialog box:



The "Please enter your name" always contains a username I once entered by mistake; even though I now enter the correct one every time, the mistake gets remembered. "Please enter your password" is always blank, which is annoying because I'd expect it to be stored in the Keychain. Deleting all related Keychain entries for the network made no difference.

A quick ferret through ~/Library/ turns up a likely suspect:


me$ grep -r 'incorrect-username@domain' ~/Library/
Binary file Library/Preferences/com.apple.eap.profiles.plist matches


Opening that file up with Property List Editor and correcting all instances of incorrect-username@domain fixed the problems (including prompting for a password).

Sunday 13 February 2011

Root shell in Mac OS X Terminal.app using system dialog

I run OS X as a standard user and often need to do admin-y things. I like OS X's use of sudo but feel uneasy about typing an administrator password into a terminal even with "Secure Keyboard Entry" on. Supposing somebody exploited some browser hole and got access as me, it seems to me it's just a little easier to alias sudo to something evil than it is to fake the system authentication dialog (the one provided by SecurityAgent). Using the SecurityAgent dialog has its problems (I'd really like to see the show-picture-chosen-by-user approach that online banking uses) but as I always trigger it explicitly it'd require a human to poke around. So, using a variant of the technique from Mac OS X Hints:

osascript -e \
"do shell script \
\"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal\" \
with administrator privileges" &>/dev/null &

This'll prompt for admin login details using the system authentication dialog (SecurityAgent) then start a new Terminal.app with a root shell inside. Conveniently enough it also turns the menu bar blue restores the default menu bar transparency whenever the terminal has focus, making it easy to tell which you're using even if you don't set a special background colour.

UPDATE : two minor disadvantages to this approach if you (constantly) have a Terminal running as a normal user:

  • if you quit the normal user Terminal, you can't restart it from the dock; seems the dock sees the root one and assumes your click is redundant

  • if you use OpenTerminalHere then it opens the new terminal in the root Terminal — which I guess means "Secure Keyboard Entry" prevents only eavesdropping and not stuffing commands/keys into it

Template Toolkit, tpage, redirect and OUTPUT_PATH

It seems you can't use [% FILTER redirect(...) %] from tpage:

me$ cat foo.tt
[% FILTER redirect("foo.html") %]
blah
[% END %]
me$ tpage foo.tt
redirect error - OUTPUT_PATH is not set
me$

Somebody submitted a very simple patch against tpage, which is one way to solve it. Another is to write a very short Perl script that uses the programmatic interface and use it to replace tpage. In case you can't or don't want to do either of those, though, here's a third, quick-and-dirty recipe which you can just cut and paste and still use with tpage:

me$ cat foo2.tt
[% PERL %]
$context->CONFIG->{OUTPUT_PATH} = ".";
[% END # perl %]
[% FILTER redirect("foo.html") %]
blah
[% END %]
me$ tpage --eval_perl foo.tt

me$ cat foo.html

blah
me$

Thursday 10 February 2011

MacPorts MacFUSE/bindfs and "Operation not permitted"

I couldn't get MacPorts MacFUSE + bindfs to work out of the box; it worked fine as an admin user but not as a normal user:


$ mkdir foo bar
$ bindfs foo bar
mount_fusefs: failed to mount /Users/me/foo /Users/me/bar@/dev/fuse0:
Operation not permitted


I'm not the only one of course, but I don't see any solution described. The device has lenient permissions:


$ ls -la /dev/fuse*
crw-rw-rw- 1 root operator 31, 0 2011-02-10 12:06 /dev/fuse0


Quick fix: tell MacFUSE to treat everyone as a (MacFUSE) administrator:


$ id
uid=501(me) gid=20(staff) groups=20(staff),
[...]
$ sudo sysctl -w macfuse.tunables.admin_group=20
macfuse.tunables.admin_group: 80 -> 20


I'm sure this isn't meant to be necessary and it has security implications (I've not thought it through properly but the docs say if someone could already run code as a normal user and used allow_other then they'd at least be able to hang some system processes that walked the mount) but it gets the job done.

GnuPlot "all points y value undefined" with "set timefmt %s"

Try as I might, I couldn't get MacPorts GnuPlot ("4.2 patchlevel 5") to restrict the range of some time data I was plotting (as usual, I'm not the only one):

gnuplot> set xdata time
gnuplot> set timefmt "%s"
gnuplot> set xrange ["1297212300":"1297256400"]
gnuplot> plot "battery_graph_Feb09-002305_to_Feb09-130805.csv" \
using 2:3 with lines
^
all points y value undefined!

gnuplot> show xrange

set xdata time
set xrange [ "" : "" ] noreverse nowriteback

gnuplot> set xrange ["1297212300":"1297256400"]
gnuplot> show xrange

set xdata time
set xrange [ "" : "" ] noreverse nowriteback



Solution: roll the latest version from source (Version 4.4 patchlevel 2; I couldn't work out what version fixed it from the ChangeLog and don't care to binary-chop it). Downside: can't quite work out how to enable AquaTerm support.