Lifeblog Posting Protocol Example

As you may have seen by the couple of test posts on this website, I’ve managed to get Nokia’s Lifeblog application posting entries to my blog.

Lifeblog uses a flavour of the Atom protocol to handle web posting. Nokia have also very kindly posted the Lifeblog posting protocol specification, which details how Lifeblog works. However, there are some differences I’ve found between the spec, and how Lifeblog 1.5 actually works. Here I’ll document what I’ve done to get posting working.

Currently Nokia only claim that Typepad enabled blogs are supported, but now my homebrew blog supports live posting too.

Lifeblog works in two stages…

  1. Getting a list of supported blogs
  2. Posting to the users preferred blog

Let’s have a look at how the application works in practice.

Firstly you have to enter where Lifeblog can find the list of supported blogs into it’s web settings menu. When you first attempt to post a blog entry from your phone, this address will be called to retrieve the list of supported blogs.

It will send a WSSE header for authentication. I have explained how to implement WSSE in Perl before so won’t go through it again. Once validated the list of supported blogs needs to look something like this.

<?xml version="1.0"?><feed xmlns="http://purl.org/atom/ns#"><link type="application/x.atom+xml" rel="service.post" href="http://www.robertprice.co.uk/cgi-bin/patom.pl" title="robertprice"/><link type="application/x.atom+xml" rel="service.feed" href="http://www.robertprice.co.uk/cgi-bin/fatom.pl" title="robertprice"/><link type="application/x.atom+xml" rel="service.upload" href="http://www.robertprice.co.uk/cgi-bin/uatom.pl" title="robertprice"/><link type="application/x.atom+xml" rel="service.categories" href="http://www.robertprice.co.uk/cgi-bin/catom.pl" title="robertprice"/><link type="text/html" rel="alternate" href="http://www.robertprice.co.uk/" title="robertprice"/></feed>

I return this with the MIME type of text/plain, though the spec says it should be application/atom+xml. I’ve not had it fail on me doing this. It is important to note however is that when I inserted linefeeds to make the XML slightly more legible it failed. So I’d recommend not having any linefeeds in the XML to ensure it works correctly.

Notice how all the entries have the same title, robertprice. This is how Atom knows all the different links are related to the same blog.

If the WSSE authentication fails, just return a 401 Unauthorized error. In Perl, you can get a CGI script to do this using the following code.

print "Status: 401 Unauthorizedrn";
print "Content-type: text/plain; charset=utf-8rnrn";
print "Unauthorisedrn"

Now Lifeblog tries to send the actual entry to the atom scripts. Entries are in two parts, so your scripts need to keep track of the session being used. This is done by use of the id element that you send back to with your reply to the first message. Lifeblog then makes sure the second part returns this id, allowing you to keep state.

For example, here’s a test entry of me trying to post a note to my blog.

Lifeblog sends the first batch of XML…

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:typepad="http://sixapart.com/atom/typepad#">
<title>mt_20050219_215424343</title>
<typepad:standalone>1</typepad:standalone>
<dc:type>Text</dc:type>
<dc:format>Note</dc:format>
<content type="text/plain" mode="escaped">Just a quick test note</content>
<summary>Sat 19/02/2005 12:05 Text note</summary>
</entry>

We reply with a bit of XML, and 201 Created HTTP response, assuming the WSSE authenticates. In Perl we can do something like this…

print "Status: 201 Createdrn";
print "Content-type: application/atom+xml; charset=utf-8rn";
print "rn";
print q{<?xml version="1.0"?>};
print q{<entry xmlns="http://purl.org/atom/ns#">};
print q{<title>blog entry</title>};
print q{<summary>blog entry</summary>};
print qq{<issued>$issued</issued>};
print q{<link type="text/html" rel="alternative" href="http://www.robertprice.co.uk/" title="HTML"/>};
print qq{<id>$tag</id>};
print q{</entry>};
print "rn";

The id is returned in the variable $tag. Mark Pilgrim has an excellent article on his site about what makes a good atom id tag. In this example we can use a simple bit of Perl code like this…

my $time = time;
my ($sec, $min, $hour, $day, $month, $year) = (localtime(time))[0,1,2,3,4,5];
$year += 1900;
$month += 1;
my $tag = "tag:robertprice.co.uk,$year-$month-$day:/lifeblog/$time";

Now lifeblog sends the second part of the data. In our example, it looks like this.

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:typepad="http://sixapart.com/atom/typepad#">
<title>Lifeblog post</title>
<created>2005-2-20T0:13:15Z</created>
<content type="text/plain" mode="escaped"></content>
<link rel="related" type="text/plain" href="tag:robertprice.co.uk,post-2:test-123"/>
</entry>

We reply with a confirmation XML message, again with the 201 Created HTTP status code.

print "Status: 201 Createdrn";
print "Content-type: application/atom+xml; charset=utf-8rn";
print "rn";
print q{<?xml version="1.0"?>};
print q{<entry xmlns="http://purl.org/atom/ns#">};
print qq{<title>$title</title>};
print qq{<summary>$summary</summary>};
print qq{<issued>$issued</issued>};
print q{<link type="text/html" rel="alternative" href="http://www.robertprice.co.uk/" title="HTML"/>};
print qq{<id>$tag</id>};
print q{</entry>};
print "rn";

In this case, I’m assuming the values for title, summary and id have been extracted from the XML sent by Lifeblog. I’m also returning an issued date, this can be generated using a little bit of Perl, for example…


my $issued = sprintf("%04d-%02d-%02dT%02d:%02d:02dZ",$year, $month, $day, $hour, $min, $sec);

It’s trivial to extract the XML, in my case, I just used XPath expressions to get the relevant data.

Let me just go over the difference between the two XML postings made by Lifeblog.

In the first part it sends over the item we want to post. In this case it’s the note. The content is in the <content> tag, and lifeblog also adds some Dublin core metadata telling us the type is Text and the format is Note. If we were sending an image, the metadata would be missing, and the content would be in base64 with the MIME type of image/jpeg.

The second part contains the lifeblog data. Here we have the title, created date and any other data we added on lifeblog. If we had entered some body text, it would have appeared here in the <content> tag. It also contains the <link> tag that contains the id tag of the first part so our script can rebuild the two XML items.

Now you should have enough information to make the glue for Lifeblog to link into your own blogging system.

I’m just using the trial version of Nokia Lifeblog 1.5 on my Nokia 7610 phone, and it’s working just fine for me! Well done Nokia for producing such a useful bit of software.