sindro.me

feeling bold on the internet

about

While happily installing prerequisites to build an app on Solaris 11, i enjoyed having Mercurial already installed in the base system.. except for a BIG issue: digest authentication was broken. I tcpdump’ed the traffic exchanged between the mercurial client and the CGI server and I saw that no Authorization header was sent, and obviously the server refused to serve the hg repository.

Before reinstalling python, maybe from source and replacing the default installation or having side by side two different versions, with consequent nuisances and dirt around the system, I tried a very very small patch to urllib2.py that… amusingly enough, fixed my problem:

--- urllib2.py~ Fri Jan 25 02:35:59 2008
+++ urllib2.py  Fri Jan 25 03:27:52 2008
@@ -815,7 +815,7 @@
             auth_val = 'Digest %s' % auth
             if req.headers.get(self.auth_header, None) == auth_val:
                 return None
-            req.add_unredirected_header(self.auth_header, auth_val)
+            req.add_header(self.auth_header, auth_val)
             resp = self.parent.open(req)
             return resp

I’m no fscking python expert (but the language is interesting), so don’t ask me WHY it works, i simply followed the add_header comment that said “this method is useful for adding authentication headers” and replaced the unredirected_header method with the former. I really don’t know why with Python2.5’s urllib2 “everything works” even with that method, something must be broken somewhere else. A diff between the two urllibs gave me nothing, I really should learn Python one day or another.

I also found no information by googling keywords such as «solaris “http {authorization,authentication}” {urllib2,python} {broken,not working} mercurial» (shell interpolation intended), so I hope this post will be useful to someone ;).

Active Gibberish

- 3 mins read

UPDATE: you don’t need this code, because starting from the 2.2 version of Rails, localization support is built-in.

Localization for Active Record error messages

Today i had to answer to one of the questions every non-english Rails developer stumbles upon now or after.. how to localize AR error messages for pleasant appearance to a non-english customer ;).

First off, thanks to defunkt’s excellent gibberish plugin and to the way AR validation errors are exposed, the task was accomplished in an easy and clean manner, without messing too much with AR’s internals.

I started by translating every default AR error message, with this translation file located in lang/it.yml:

# Active Record errors
#
ar_accepted:     "deve essere accettato" 
ar_not_a_number: "non è un numero" 
ar_blank:        "è un campo obbligatorio" 
ar_empty:        "è un campo obbligatorio" 
ar_inclusion:    "non è nella lista dei valori validi" 
ar_too_long:     "è troppo lungo (massimo %d caratteri)" 
ar_exclusion:    "è riservato" 
ar_too_short:    "è troppo corto (minimo %d caratteri)" 
ar_invalid:      "non è valido" 
ar_wrong_length: "è errato, dovrebbe essere di %d caratteri" 
ar_confirmation: "non corrisponde" 
ar_taken:        "esiste già" 
# This one is not a default key, but I use it in my validations
ar_greater_zero: "deve essere maggiore di zero" 

and four lines in config/environment.rb:

Gibberish.current_language = :it
ActiveRecord::Errors.default_error_messages =
  ActiveRecord::Errors.default_error_messages.inject({}) {|h, (key, string)|
    h.update(key => string["ar_#{key}".intern]) # <em>Gibberish magic</em>
}

The first one simply sets Italian (:it) as the default language, the inject builds a new error_messages hash using Gibberish to translate the default ones. I named every AR error key in my translation file with an “ar_” prefix, in order to avoid possible future key clashes. Finally, AR array is overwritten with the new one freshly built.

Five minutes ago, I overwritten the super-shining-new CSS stylesheet that implements the current color scheme, because i wanted to restore the original one and put it in a new theme for this site, so that people who enjoyed the old theme could continue to use it. But, as the most kiddiest system administrator, i uncompressed the original files from the backup archive OVER the current ones..

Safari to the rescue! Every cached item by safari is stored into a SQlite3 database located in ~/Library/Caches/com.apple.Safari, let’s inspect how it is structured:

 13:54:42 vjt@voyager:~/Library/Caches/com.apple.Safari$ sqlite3 Cache.db 
SQLite version 3.5.1
Enter ".help" for instructions

sqlite> .tables
cfurl_cache_blob_data       cfurl_cache_schema_version
cfurl_cache_response      

sqlite> .schema cfurl_cache_response 
CREATE TABLE cfurl_cache_response(
  entry_ID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  version INTEGER,
  hash_value INTEGER,
  storage_policy INTEGER,
  request_key TEXT UNIQUE,
  time_stamp NOT NULL DEFAULT CURRENT_TIMESTAMP);

sqlite> .schema cfurl_cache_blob_data
CREATE TABLE cfurl_cache_blob_data(
  entry_ID INTEGER PRIMARY KEY,
  response_object BLOB,
  request_object BLOB,
  receiver_data BLOB,
  proto_props BLOB,
  user_info BLOB);

sqlite> select * from cfurl_cache_response limit 3;
1|0|1897220634|0|http://..../|2008-01-19 11:10:33
2|0|-662909776|0|http://..../|2008-01-19 11:10:33

Wow. Impressive. That’s why i love Apple products, because they are so well structured that you can freely inspect them and use them and their resources for every unplanned task you could have to complete.. even to fix your own mistakes ;). And it’s also intriguing, because you have to scratch your own itch and find the solution while exploring a beautifully constructed software product.

To make a long story short, every cached URL is stored into the request_key field of the cfurl_cache_response table, while in the receiver_data field of the cfurl_cache_blob_data there is the actual cached data. Now we can look for the overwritten bbs theme CSS stylesheet:

Today's row

- 1 min read
05:01:24 vjt@voyager:~/Antani/trunk$ replace(){ sed -e "s|$1|$2|g" 
< $3 > ${3}X; mv ${3}X $3; }; egrep -r 'XP_[A-Z_]+[[:space:]]+-?[[
:digit:]]' Headers |ruby -ne "f,m=scan(/(.+):.+(XP_[\w_]+)/).first
;puts '%s %s %s' % [ f, m, 'kXP'<<m.scan(/(_[A-Z])([A-Z]+)/).map {
|a,b| a[1..1]<<b. downcase }.join ]" | while read hdr from to; do
replace $from $to $hdr; for src in `grep -rl $from Sources`; do
replace $from $to $src; done; done
  • You must have PTH installed, and maybe other libs.
  • This was tested on SCO_SV os507 3.2 5.0.7 i386

If you have UDK, run:

$ CFLAGS='-I/usr/local/include -belf' LDFLAGS='-L/usr/local/lib' \
  ./configure --with-threads --with-pth --disable-shared --disable-ipv6
  • Add /usr/local/include to BASECFLAGS in Makefile (autocrap sucks).
  • Patch Modules/ctypes/_ctypes_test.c by putting an #ifdef HAVE_LONG_LONG around functions that use PY_LONG_LONG (hints: lines 384 and 318).
  • Patch Objects/longobject.c and on line 817 put the IS_LITTLE_ENDIAN macro before the #ifdef HAVE_LONG_LONG block, and put _PyLong_FromSsize_t and _PyLong_FromSize_t after the HAVE_LONG_LONG block.

If you have GCC, run:

$ CFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib'            \
  ./configure --with-threads --with-pth --disable-shared --disable-ipv6

Either with UDK or GCC:

  • Edit pyconfig.h and comment out the socklen_t define
  • Edit Modules/socketmodule.c and on line 226 add || defined(SCO5) in order to define INET_ADDRSTRLEN.
  • Run make (or gmake if you wish)
  • You will be left without _curses.so, _curses_panel.so, _locale.so and readline.so if using GCC and also pyexpat, elementtree and sha512 if using UDK.
      __   ____  __ __  ____     __
      \ \ / /  \/  |  \/  \ \   / /
       \ V /| |\/| | |\/| |\ \ / / 
        | | | |  | | |  | | \ V /_ 
        |_| |_|  |_|_|  |_|  \_/(_)
[vjt@os507 ~/Python-2.5.1-vjt] $ python
Python 2.5.1 (r251:31337, Sep 13 2007, 22:40:33) 
[GCC 4.2.1] on sco_sv3
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> 
[vjt@os507 ~] $ hg clone http://code.wuhrer.thc/hg/Antani
destination directory: Antani
http authorization required

!! YAY! :D

When Sysadmins Ruled the Earth

- 1 min read

Novel logo

A really, really, really NERD novel by Cory Doctorow that tells about a bunch of sysadmins that strive to keep the good ol’ Net online after a catastrophic event that brought the entire world to its knees. They fight with scarce power and food supplies and communicate over the Usenet… using the good old alt. hierarchy.

Vote: 10+ for the geekiest thing I’ve ever read. It’s really worth the hour needed to read it completely. Enjoy it

Ingredients: Debian, Netatalk, Avahi, some trickery.

Step 1: Recompile Netatalk with SSL Support

Recompile Netatalk with SSL Support.

You can safely ignore the “.passwd” stuff, because afpd uses PAM for user authentication.

Hint: Disable the atalk protocol handlers in /etc/default/netatalk for a faster startup:

# Set which daemons to run (papd is dependent upon atalkd):
ATALKD_RUN=no        # appletalk protocol
PAPD_RUN=no          # printer sharing daemon (printers are soooo '90s)
CNID_METAD_RUN=yes   # don't remember but is needed, rtfm!
AFPD_RUN=yes         # you will always need this
TIMELORD_RUN=no      # my time lord's name is <a href="http://openntpd.org">openntpd</a>
A2BOOT_RUN=no        # boot? nah! :P

Step 2: Create a share for time machine backup data, by adding e.g.

# path         name           perms     charset
/some/where/tm "Time Machine" allow:vjt volcharset:"UTF8" 

into /etc/netatalk/AppleVolumes.default.

Step 3: Let the AFPD server show up in finder

Download the avahi service file, put it into /etc/avahi/services and reload avahi with /etc/init.d/avahi-daemon reload (sorry, original links are broken).

Step 4: Set Up Time Machine Backup

You need two files on your afp network share: .com.apple.timemachine.supported and a dot-file named with your en0 MAC address. To create it, the easier way is to attach an USB/Firewire disk, rename it with the name of the intended network share (specified into the AppleVolumes file) and enable time machine on it.

Then, copy over the .00… file on the external disk into your home dir, eject the disk, mount the network share from the finder and copy the file there.

Finally, touch .com.apple.timemachine.supported onto the network share, and re-open time machine preferences: the size of your backup volume should be equal to the network share size :).